/** \file
 * \brief Text Control - Qt Implementation
 *
 * See Copyright Notice in "iup.h"
 */

#include <QLineEdit>
#include <QTextEdit>
#include <QTextCursor>
#include <QTextDocument>
#include <QTextCharFormat>
#include <QTextOption>
#include <QScrollBar>
#include <QDoubleSpinBox>
#include <QWidget>
#include <QString>
#include <QFont>
#include <QColor>
#include <QMimeData>
#include <QUrl>
#include <QCompleter>
#include <QFrame>
#include <QTextList>

#include <cstdlib>
#include <cstdio>
#include <cstring>

extern "C" {
#include "iup.h"
#include "iupcbs.h"
#include "iup_object.h"
#include "iup_attrib.h"
#include "iup_str.h"
#include "iup_image.h"
#include "iup_mask.h"
#include "iup_drvfont.h"
#include "iup_array.h"
#include "iup_text.h"
}

#include "iupqt_drv.h"


/****************************************************************************
 * Custom Widgets with Event Handling
 ****************************************************************************/

/* Forward declarations */
static int qtTextKeyPress(Ihandle* ih, QKeyEvent* evt);

/* Custom QLineEdit with additional functionality */
class IupQtLineEdit : public QLineEdit
{
private:
  Ihandle* ih;
  bool overwrite_mode;

public:
  explicit IupQtLineEdit(Ihandle* ihandle) : QLineEdit(), ih(ihandle), overwrite_mode(false)
  {
    /* Accept drops for DROPFILES_CB */
    setAcceptDrops(true);
  }

  void setIupOverwriteMode(bool mode) { overwrite_mode = mode; }
  bool isIupOverwriteMode() const { return overwrite_mode; }

  /* Override sizeHint to return minimal size (like GTK's gtk_entry_set_width_chars(1))
   * This prevents Qt from using its default larger size hint, allowing IUP's
   * natural size calculation to control the widget size. */
  QSize sizeHint() const override
  {
    QFontMetrics fm(font());
    int h = fm.height() + 2;  /* Minimal height based on font */
    int w = fm.horizontalAdvance('X');  /* 1 character width minimum */
    return QSize(w, h);
  }

protected:
  void keyPressEvent(QKeyEvent* event) override
  {
    /* Handle INSERT key for overwrite mode */
    if (event->key() == Qt::Key_Insert && event->modifiers() == Qt::NoModifier)
    {
      overwrite_mode = !overwrite_mode;
      event->accept();
      return;
    }

    /* Validate input against mask before allowing text change */
    if (!event->text().isEmpty() && event->text().at(0).isPrint())
    {
      int start = cursorPosition();
      int end = start;

      if (hasSelectedText())
      {
        start = selectionStart();
        end = start + selectedText().length();
      }

      /* Call mask validation (ACTION callback handled separately by qtTextKeyPress) */
      int ret = iupEditCallActionCb(ih, NULL, event->text().toUtf8().constData(),
                                     start, end, ih->data->mask, ih->data->nc, 0, 1);
      if (ret == 0)
      {
        /* Mask validation failed - block the key */
        event->accept();
        return;
      }
    }

    /* Handle overwrite mode */
    if (overwrite_mode && !event->text().isEmpty() && !hasSelectedText())
    {
      int pos = cursorPosition();
      if (pos < text().length())
      {
        QString current = text();
        current.remove(pos, 1);
        setText(current);
        setCursorPosition(pos);
      }
    }

    /* Handle text key press event (includes ACTION callback for single-line) */
    if (qtTextKeyPress(ih, event))
    {
      event->accept();
      return;
    }

    QLineEdit::keyPressEvent(event);
  }

  void dragEnterEvent(QDragEnterEvent* event) override
  {
    if (IupGetCallback(ih, "DROPFILES_CB"))
    {
      if (event->mimeData()->hasUrls())
      {
        event->acceptProposedAction();
        return;
      }
    }
    QLineEdit::dragEnterEvent(event);
  }

  void dropEvent(QDropEvent* event) override
  {
    IFnsiii cb = (IFnsiii)IupGetCallback(ih, "DROPFILES_CB");
    if (cb && event->mimeData()->hasUrls())
    {
      QList<QUrl> urls = event->mimeData()->urls();
      int count = urls.size();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
      int x = event->position().x();
      int y = event->position().y();
#else
      int x = event->pos().x();
      int y = event->pos().y();
#endif

      for (int i = 0; i < count; i++)
      {
        QString filePath = urls[i].toLocalFile();
        if (!filePath.isEmpty())
        {
          QByteArray fileArray = filePath.toUtf8();
          if (cb(ih, (char*)fileArray.constData(), count - i - 1, x, y) == IUP_IGNORE)
            break;
        }
      }

      event->acceptProposedAction();
      return;
    }
    QLineEdit::dropEvent(event);
  }
};

/* Custom QSpinBox that supports SPINAUTO=NO mode */
class IupQtSpinBox : public QSpinBox
{
public:
  Ihandle* ih;
  bool spinauto;

  explicit IupQtSpinBox(Ihandle* ihandle) : QSpinBox(), ih(ihandle), spinauto(true) {}

  void setDisplayText(const QString& text)
  {
    lineEdit()->setText(text);
  }

  QString displayText() const
  {
    return lineEdit()->text();
  }

protected:
  QString textFromValue(int value) const override
  {
    if (!spinauto)
    {
      /* When spinauto is off, preserve whatever text is in the line edit */
      return lineEdit()->text();
    }
    return QSpinBox::textFromValue(value);
  }

  int valueFromText(const QString& text) const override
  {
    if (!spinauto)
      return this->value();
    return QSpinBox::valueFromText(text);
  }

  QValidator::State validate(QString& input, int& pos) const override
  {
    if (!spinauto)
      return QValidator::Acceptable;
    return QSpinBox::validate(input, pos);
  }

  void keyPressEvent(QKeyEvent* event) override
  {
    if (ih && qtTextKeyPress(ih, event))
    {
      event->accept();
      return;
    }
    QSpinBox::keyPressEvent(event);
  }
};

/* Custom QTextEdit with additional functionality */
class IupQtTextEdit : public QTextEdit
{
private:
  Ihandle* ih;
  bool overwrite_mode;

public:
  explicit IupQtTextEdit(Ihandle* ihandle) : QTextEdit(), ih(ihandle), overwrite_mode(false)
  {
    setAcceptDrops(true);
    setMouseTracking(true);
  }

  void setIupOverwriteMode(bool mode) { overwrite_mode = mode; }
  bool isIupOverwriteMode() const { return overwrite_mode; }

protected:
  void keyPressEvent(QKeyEvent* event) override
  {
    /* Handle INSERT key for overwrite mode */
    if (event->key() == Qt::Key_Insert && event->modifiers() == Qt::NoModifier)
    {
      overwrite_mode = !overwrite_mode;
      setOverwriteCursor(overwrite_mode);
      event->accept();
      return;
    }

    /* Validate input against mask before allowing text change */
    if (!event->text().isEmpty() && event->text().at(0).isPrint())
    {
      QTextCursor cursor = textCursor();
      int pos = cursor.position();
      int start = pos;
      int end = pos;

      if (cursor.hasSelection())
      {
        int anchor = cursor.anchor();
        start = qMin(pos, anchor);
        end = qMax(pos, anchor);
      }

      /* Call mask validation (ACTION callback handled separately) */
      int ret = iupEditCallActionCb(ih, NULL, event->text().toUtf8().constData(),
                                     start, end, ih->data->mask, ih->data->nc, 0, 1);
      if (ret == 0)
      {
        /* Mask validation failed - block the key */
        event->accept();
        return;
      }
    }

    /* Handle overwrite mode */
    if (overwrite_mode && !event->text().isEmpty())
    {
      QTextCursor cursor = textCursor();
      if (!cursor.hasSelection() && !cursor.atEnd())
      {
        cursor.deleteChar();
        setTextCursor(cursor);
      }
    }

    /* Handle text key press event (includes ACTION callback) */
    if (qtTextKeyPress(ih, event))
    {
      event->accept();
      return;
    }

    QTextEdit::keyPressEvent(event);
  }

  void dragEnterEvent(QDragEnterEvent* event) override
  {
    if (IupGetCallback(ih, "DROPFILES_CB"))
    {
      if (event->mimeData()->hasUrls())
      {
        event->acceptProposedAction();
        return;
      }
    }
    QTextEdit::dragEnterEvent(event);
  }

  void dropEvent(QDropEvent* event) override
  {
    IFnsiii cb = (IFnsiii)IupGetCallback(ih, "DROPFILES_CB");
    if (cb && event->mimeData()->hasUrls())
    {
      QList<QUrl> urls = event->mimeData()->urls();
      int count = urls.size();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
      int x = event->position().x();
      int y = event->position().y();
#else
      int x = event->pos().x();
      int y = event->pos().y();
#endif

      for (int i = 0; i < count; i++)
      {
        QString filePath = urls[i].toLocalFile();
        if (!filePath.isEmpty())
        {
          QByteArray fileArray = filePath.toUtf8();
          if (cb(ih, (char*)fileArray.constData(), count - i - 1, x, y) == IUP_IGNORE)
            break;
        }
      }

      event->acceptProposedAction();
      return;
    }
    QTextEdit::dropEvent(event);
  }

  void mouseReleaseEvent(QMouseEvent* event) override
  {
    if (event->button() == Qt::LeftButton)
    {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
      QString url = anchorAt(event->position().toPoint());
#else
      QString url = anchorAt(event->pos());
#endif
      if (!url.isEmpty())
      {
        QByteArray urlBytes = url.toUtf8();
        IFns cb = (IFns)IupGetCallback(ih, "LINK_CB");
        if (cb)
        {
          int ret = cb(ih, (char*)urlBytes.constData());
          if (ret == IUP_CLOSE)
            IupExitLoop();
          else if (ret == IUP_DEFAULT)
            IupHelp(urlBytes.constData());
        }
        else
          IupHelp(urlBytes.constData());
      }
    }

    QTextEdit::mouseReleaseEvent(event);
  }

  void mouseMoveEvent(QMouseEvent* event) override
  {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    QString url = anchorAt(event->position().toPoint());
#else
    QString url = anchorAt(event->pos());
#endif

    if (!url.isEmpty())
      viewport()->setCursor(Qt::PointingHandCursor);
    else
      viewport()->setCursor(Qt::IBeamCursor);

    QTextEdit::mouseMoveEvent(event);
  }

  void setOverwriteCursor(bool overwrite)
  {
    (void)overwrite;
  }
};

/****************************************************************************
 * Helper Functions
 ****************************************************************************/

extern "C" IUP_SDK_API void iupdrvTextAddSpin(Ihandle* ih, int *w, int h)
{
  static int spin_min_width = -1;

  (void)h;
  (void)ih;

  if (spin_min_width < 0)
  {
    QSpinBox* temp_spin = new QSpinBox();
    temp_spin->setRange(0, 100);

    QSize min_hint = temp_spin->minimumSizeHint();
    spin_min_width = (min_hint.width() > 0) ? min_hint.width() : 130;

    delete temp_spin;
  }

  if (*w < spin_min_width)
    *w = spin_min_width;
}

/* Cached measurements for text widget borders */
static int iupqt_entry_border_x = -1;
static int iupqt_entry_border_y = -1;
static int iupqt_multiline_border_x = -1;
static int iupqt_multiline_border_y = -1;

static void iupqtTextMeasureEntryBorders(void)
{
  if (iupqt_entry_border_x < 0)
  {
    QLineEdit* temp_entry = new QLineEdit();
    temp_entry->setFrame(true);

    /* Use minimumSizeHint which IS content-dependent (uses fm.maxWidth())
       Same pattern as Button: get widget size, subtract content, get borders */
    QSize min_hint = temp_entry->minimumSizeHint();
    QFontMetrics fm(temp_entry->font());

    /* minimumSizeHint uses fm.maxWidth() for width and fm.height() for height */
    int char_width = fm.maxWidth();
    int char_height = fm.height();

    iupqt_entry_border_x = min_hint.width() - char_width;
    iupqt_entry_border_y = min_hint.height() - char_height;

    if (iupqt_entry_border_x < 6) iupqt_entry_border_x = 6;
    if (iupqt_entry_border_y < 6) iupqt_entry_border_y = 6;

    delete temp_entry;
  }
}

static void iupqtTextMeasureMultilineBorders(void)
{
  if (iupqt_multiline_border_x < 0)
  {
    QTextEdit* temp_text = new QTextEdit();
    temp_text->setFrameStyle(QFrame::Panel | QFrame::Sunken);
    temp_text->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    temp_text->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    int frame_width = temp_text->frameWidth();

    int doc_margin = (int)temp_text->document()->documentMargin();

    /* Total border includes frame width on both sides + document margin on both sides */
    iupqt_multiline_border_x = 2 * frame_width + 2 * doc_margin;
    iupqt_multiline_border_y = 2 * frame_width + 2 * doc_margin;

    /* Ensure minimum reasonable border */
    if (iupqt_multiline_border_x < 4) iupqt_multiline_border_x = 4;
    if (iupqt_multiline_border_y < 4) iupqt_multiline_border_y = 4;

    delete temp_text;
  }
}

extern "C" IUP_SDK_API void iupdrvTextAddBorders(Ihandle* ih, int *x, int *y)
{
  if (iupAttribGetBoolean(ih, "_IUP_MULTILINE_TEXT"))
  {
    iupqtTextMeasureMultilineBorders();
    (*x) += iupqt_multiline_border_x;
    (*y) += iupqt_multiline_border_y;
  }
  else
  {
    iupqtTextMeasureEntryBorders();
    (*x) += iupqt_entry_border_x;
    (*y) += iupqt_entry_border_y;
  }
}

extern "C" IUP_SDK_API void iupdrvTextAddExtraPadding(Ihandle* ih, int *w, int *h)
{
  (void)ih;
  (void)w;
  (void)h;
}

extern "C" IUP_SDK_API void iupdrvTextConvertLinColToPos(Ihandle* ih, int lin, int col, int *pos)
{
  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    QTextDocument* doc = text->document();

    lin--; /* IUP starts at 1 */
    col--;

    QTextBlock block = doc->findBlockByLineNumber(lin);
    if (block.isValid())
    {
      int blockLen = block.length() - 1;
      if (col > blockLen)
        col = blockLen;
      if (col < 0)
        col = 0;
      *pos = block.position() + col;
    }
    else
      *pos = 0;
  }
  else
    *pos = col - 1; /* single line, position is column */
}

extern "C" IUP_SDK_API void iupdrvTextConvertPosToLinCol(Ihandle* ih, int pos, int *lin, int *col)
{
  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    QTextDocument* doc = text->document();
    QTextBlock block = doc->findBlock(pos);

    if (block.isValid())
    {
      *lin = block.blockNumber() + 1; /* IUP starts at 1 */
      *col = pos - block.position() + 1;
    }
    else
    {
      *lin = 1;
      *col = 1;
    }
  }
  else
  {
    *lin = 1;
    *col = pos + 1;
  }
}

/****************************************************************************
 * Callbacks
 ****************************************************************************/

static void qtTextValueChanged(Ihandle* ih)
{
  if (ih->data->disable_callbacks)
    return;

  IFn cb = (IFn)IupGetCallback(ih, "VALUECHANGED_CB");
  if (cb)
    cb(ih);
}

static void qtTextCursorPositionChanged(Ihandle* ih)
{
  IFnii cb = (IFnii)IupGetCallback(ih, "CARET_CB");
  if (cb)
  {
    int lin, col, pos;

    if (ih->data->is_multiline)
    {
      IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
      QTextCursor cursor = text->textCursor();
      pos = cursor.position();
    }
    else
    {
      IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
      pos = edit->cursorPosition();
    }

    iupdrvTextConvertPosToLinCol(ih, pos, &lin, &col);
    cb(ih, lin, col);
  }
}

static int qtTextKeyPress(Ihandle* ih, QKeyEvent* evt)
{
  int ret = iupqtKeyPressEvent((QWidget*)ih->handle, evt, ih);

  if (ret)
  {
    return 1;
  }

  /* Call ACTION callback */
  {
    IFnis cb = (IFnis)IupGetCallback(ih, "ACTION");
    if (cb)
    {
      QString text = evt->text();

      if (!text.isEmpty())
      {
        QChar ch = text.at(0);
        if (ch.isPrint() && ch.unicode() >= 32)
        {
          int iup_key = ch.toLatin1();
          const char* value;
          QString value_str;

          if (ih->data->is_multiline)
          {
            IupQtTextEdit* edit = (IupQtTextEdit*)ih->handle;
            value_str = edit->toPlainText();
          }
          else
          {
            IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
            value_str = edit->text();
          }
          value = value_str.toUtf8().constData();

          int result = cb(ih, iup_key, (char*)value);

          if (result == IUP_IGNORE)
          {
            return 1;
          }
          else if (result == IUP_CLOSE)
          {
            IupExitLoop();
            return 1;
          }
          else if (result != IUP_DEFAULT && result > 0)
          {
            QString replacement = QString(QChar(result));

            ih->data->disable_callbacks = 1;

            if (ih->data->is_multiline)
            {
              IupQtTextEdit* edit = (IupQtTextEdit*)ih->handle;
              QTextCursor cursor = edit->textCursor();
              cursor.insertText(replacement);
            }
            else
            {
              IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
              int pos = edit->cursorPosition();
              QString current = edit->text();
              current.insert(pos, replacement);
              edit->setText(current);
              edit->setCursorPosition(pos + 1);
            }

            ih->data->disable_callbacks = 0;

            return 1;
          }
        }
      }
    }
  }

  return 0;
}

/****************************************************************************
 * Attribute Setters/Getters
 ****************************************************************************/

static int qtTextSetValueAttrib(Ihandle* ih, const char* value)
{
  if (!value) value = "";

  /* Safety check - widget must be mapped before setting value */
  if (!ih->handle)
    return 0;

  ih->data->disable_callbacks = 1;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    text->setPlainText(QString::fromUtf8(value));
  }
  else
  {
    IupQtSpinBox* spinbox = (IupQtSpinBox*)iupAttribGet(ih, "_IUPQT_SPINBOX");
    if (spinbox)
    {
      if (!spinbox->spinauto)
      {
        spinbox->setDisplayText(QString::fromUtf8(value));
      }
      else
      {
        int int_value = 0;
        if (value && *value)
          sscanf(value, "%d", &int_value);
        spinbox->setValue(int_value);
      }
    }
    else
    {
      /* Regular line edit */
      IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
      edit->setText(QString::fromUtf8(value));
    }
  }

  ih->data->disable_callbacks = 0;
  return 0;
}

static char* qtTextGetValueAttrib(Ihandle* ih)
{
  QString value;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    value = text->toPlainText();
  }
  else
  {
    IupQtSpinBox* spinbox = (IupQtSpinBox*)iupAttribGet(ih, "_IUPQT_SPINBOX");
    if (spinbox)
    {
      if (!spinbox->spinauto)
        return iupStrReturnStr(spinbox->displayText().toUtf8().constData());
      else
        return iupStrReturnInt(spinbox->value());
    }
    else
    {
      /* Regular line edit */
      IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
      value = edit->text();
    }
  }

  return iupStrReturnStr(value.toUtf8().constData());
}

static char* qtTextGetLineValueAttrib(Ihandle* ih)
{
  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    QTextCursor cursor = text->textCursor();
    QTextBlock block = cursor.block();
    return iupStrReturnStr(block.text().toUtf8().constData());
  }
  else
    return qtTextGetValueAttrib(ih);
}

static int qtTextSetSelectedTextAttrib(Ihandle* ih, const char* value)
{
  if (!value)
    return 0;

  ih->data->disable_callbacks = 1;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    text->insertPlainText(QString::fromUtf8(value));
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    edit->insert(QString::fromUtf8(value));
  }

  ih->data->disable_callbacks = 0;
  return 0;
}

static char* qtTextGetSelectedTextAttrib(Ihandle* ih)
{
  QString value;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    QTextCursor cursor = text->textCursor();
    value = cursor.selectedText();
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    value = edit->selectedText();
  }

  return iupStrReturnStr(value.toUtf8().constData());
}

static int qtTextSetSelectionAttrib(Ihandle* ih, const char* value)
{
  int start = 1, end = 1;

  if (!value || iupStrEqualNoCase(value, "NONE"))
  {
    if (ih->data->is_multiline)
    {
      IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
      QTextCursor cursor = text->textCursor();
      cursor.clearSelection();
      text->setTextCursor(cursor);
    }
    else
    {
      IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
      edit->deselect();
    }
    return 0;
  }

  if (iupStrEqualNoCase(value, "ALL"))
  {
    if (ih->data->is_multiline)
    {
      IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
      text->selectAll();
    }
    else
    {
      IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
      edit->selectAll();
    }
    return 0;
  }

  if (iupStrToIntInt(value, &start, &end, ':') != 2)
    return 0;

  if (start < 1) start = 1;
  if (end < 1) end = 1;

  start--; /* IUP starts at 1 */
  end--;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    QTextCursor cursor = text->textCursor();
    cursor.setPosition(start);
    cursor.setPosition(end, QTextCursor::KeepAnchor);
    text->setTextCursor(cursor);
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    edit->setSelection(start, end - start);
  }

  return 0;
}

static char* qtTextGetSelectionAttrib(Ihandle* ih)
{
  int start, end;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    QTextCursor cursor = text->textCursor();
    start = cursor.selectionStart();
    end = cursor.selectionEnd();
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    start = edit->selectionStart();
    if (start < 0)
      return nullptr;
    end = start + edit->selectedText().length();
  }

  start++; /* IUP starts at 1 */
  end++;

  return iupStrReturnStrf("%d:%d", start, end);
}

static int qtTextSetSelectionPosAttrib(Ihandle* ih, const char* value)
{
  int start = 0, end = 0;

  if (!value || iupStrEqualNoCase(value, "NONE"))
  {
    if (ih->data->is_multiline)
    {
      IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
      QTextCursor cursor = text->textCursor();
      cursor.clearSelection();
      text->setTextCursor(cursor);
    }
    else
    {
      IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
      edit->deselect();
    }
    return 0;
  }

  if (iupStrEqualNoCase(value, "ALL"))
  {
    if (ih->data->is_multiline)
    {
      IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
      text->selectAll();
    }
    else
    {
      IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
      edit->selectAll();
    }
    return 0;
  }

  if (iupStrToIntInt(value, &start, &end, ':') != 2)
    return 0;

  if (start < 0) start = 0;
  if (end < 0) end = 0;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    QTextCursor cursor = text->textCursor();
    cursor.setPosition(start);
    cursor.setPosition(end, QTextCursor::KeepAnchor);
    text->setTextCursor(cursor);
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    edit->setSelection(start, end - start);
  }

  return 0;
}

static char* qtTextGetSelectionPosAttrib(Ihandle* ih)
{
  int start, end;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    QTextCursor cursor = text->textCursor();
    start = cursor.selectionStart();
    end = cursor.selectionEnd();
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    start = edit->selectionStart();
    if (start < 0)
      return nullptr;
    end = start + edit->selectedText().length();
  }

  return iupStrReturnStrf("%d:%d", start, end);
}

static int qtTextSetCaretAttrib(Ihandle* ih, const char* value)
{
  int lin = 1, col = 1, pos;

  iupStrToIntInt(value, &lin, &col, ',');
  iupdrvTextConvertLinColToPos(ih, lin, col, &pos);

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    QTextCursor cursor = text->textCursor();
    cursor.setPosition(pos);
    text->setTextCursor(cursor);
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    edit->setCursorPosition(pos);
  }

  return 0;
}

static char* qtTextGetCaretAttrib(Ihandle* ih)
{
  int lin, col, pos;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    QTextCursor cursor = text->textCursor();
    pos = cursor.position();
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    pos = edit->cursorPosition();
  }

  iupdrvTextConvertPosToLinCol(ih, pos, &lin, &col);
  return iupStrReturnStrf("%d,%d", lin, col);
}

static int qtTextSetCaretPosAttrib(Ihandle* ih, const char* value)
{
  int pos = 0;

  if (!value)
    return 0;

  iupStrToInt(value, &pos);
  if (pos < 0) pos = 0;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    QTextCursor cursor = text->textCursor();
    cursor.setPosition(pos);
    text->setTextCursor(cursor);
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    edit->setCursorPosition(pos);
  }

  return 0;
}

static char* qtTextGetCaretPosAttrib(Ihandle* ih)
{
  int pos;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    QTextCursor cursor = text->textCursor();
    pos = cursor.position();
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    pos = edit->cursorPosition();
  }

  return iupStrReturnInt(pos);
}

static int qtTextSetScrollToAttrib(Ihandle* ih, const char* value)
{
  int lin = 1, col = 1, pos;

  if (!value)
    return 0;

  iupStrToIntInt(value, &lin, &col, ',');
  if (lin < 1) lin = 1;
  if (col < 1) col = 1;

  iupdrvTextConvertLinColToPos(ih, lin, col, &pos);

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    QTextCursor cursor = text->textCursor();
    cursor.setPosition(pos);
    text->setTextCursor(cursor);
    text->ensureCursorVisible();
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    edit->setCursorPosition(pos);
  }

  return 0;
}

static int qtTextSetScrollToPosAttrib(Ihandle* ih, const char* value)
{
  int pos = 0;

  if (!value)
    return 0;

  iupStrToInt(value, &pos);
  if (pos < 0) pos = 0;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    QTextCursor cursor = text->textCursor();
    cursor.setPosition(pos);
    text->setTextCursor(cursor);
    text->ensureCursorVisible();
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    edit->setCursorPosition(pos);
  }

  return 0;
}

static int qtTextSetInsertAttrib(Ihandle* ih, const char* value)
{
  if (!ih->handle)
    return 0;
  if (!value)
    return 0;

  ih->data->disable_callbacks = 1;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    text->insertPlainText(QString::fromUtf8(value));
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    edit->insert(QString::fromUtf8(value));
  }

  ih->data->disable_callbacks = 0;
  return 0;
}

static int qtTextSetAppendAttrib(Ihandle* ih, const char* value)
{
  if (!ih->handle)
    return 0;

  ih->data->disable_callbacks = 1;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;

    if (ih->data->append_newline && !text->toPlainText().isEmpty())
      text->append("\n" + QString::fromUtf8(value));
    else
      text->append(QString::fromUtf8(value));

    /* Scroll to the end to make the appended text visible */
    text->moveCursor(QTextCursor::End);
    text->ensureCursorVisible();
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    edit->setText(edit->text() + QString::fromUtf8(value));
  }

  ih->data->disable_callbacks = 0;
  return 0;
}

static int qtTextSetReadOnlyAttrib(Ihandle* ih, const char* value)
{
  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    text->setReadOnly(iupStrBoolean(value));
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    edit->setReadOnly(iupStrBoolean(value));
  }

  return 0;
}

static char* qtTextGetReadOnlyAttrib(Ihandle* ih)
{
  bool readonly;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    readonly = text->isReadOnly();
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    readonly = edit->isReadOnly();
  }

  return iupStrReturnBoolean(readonly);
}

static int qtTextSetNCAttrib(Ihandle* ih, const char* value)
{
  if (!ih->data->is_multiline)
  {
    int max;
    if (iupStrToInt(value, &max))
    {
      IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
      edit->setMaxLength(max);
    }
  }

  return 0;
}

static char* qtTextGetNCAttrib(Ihandle* ih)
{
  if (!ih->data->is_multiline)
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    return iupStrReturnInt(edit->maxLength());
  }

  return nullptr;
}

static int qtTextSetClipboardAttrib(Ihandle* ih, const char* value)
{
  if (!value)
    return 0;

  if (iupStrEqualNoCase(value, "COPY"))
  {
    if (ih->data->is_multiline)
    {
      IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
      text->copy();
    }
    else
    {
      IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
      edit->copy();
    }
  }
  else if (iupStrEqualNoCase(value, "CUT"))
  {
    if (ih->data->is_multiline)
    {
      IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
      text->cut();
    }
    else
    {
      IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
      edit->cut();
    }
  }
  else if (iupStrEqualNoCase(value, "PASTE"))
  {
    if (ih->data->is_multiline)
    {
      IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
      text->paste();
    }
    else
    {
      IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
      edit->paste();
    }
  }
  else if (iupStrEqualNoCase(value, "CLEAR"))
  {
    if (ih->data->is_multiline)
    {
      IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
      QTextCursor cursor = text->textCursor();
      if (cursor.hasSelection())
        cursor.removeSelectedText();
      else
        text->clear();
    }
    else
    {
      IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
      if (edit->hasSelectedText())
        edit->del();
      else
        edit->clear();
    }
  }

  return 0;
}

static int qtTextSetSpinValueAttrib(Ihandle* ih, const char* value)
{
  IupQtSpinBox* spinbox = (IupQtSpinBox*)iupAttribGet(ih, "_IUPQT_SPINBOX");
  if (spinbox)
  {
    int pos;
    if (iupStrToInt(value, &pos))
    {
      ih->data->disable_callbacks = 1;
      spinbox->setValue(pos);
      ih->data->disable_callbacks = 0;
    }
  }
  return 1;
}

static char* qtTextGetSpinValueAttrib(Ihandle* ih)
{
  IupQtSpinBox* spinbox = (IupQtSpinBox*)iupAttribGet(ih, "_IUPQT_SPINBOX");
  if (spinbox)
    return iupStrReturnInt(spinbox->value());
  return NULL;
}

static int qtTextSetPasswordAttrib(Ihandle* ih, const char* value)
{
  if (!ih->data->is_multiline)
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    if (iupStrBoolean(value))
      edit->setEchoMode(QLineEdit::Password);
    else
      edit->setEchoMode(QLineEdit::Normal);
  }

  return 0;
}

static int qtTextSetAlignmentAttrib(Ihandle* ih, const char* value)
{
  Qt::Alignment align = Qt::AlignLeft;

  if (iupStrEqualNoCase(value, "ARIGHT"))
    align = Qt::AlignRight;
  else if (iupStrEqualNoCase(value, "ACENTER"))
    align = Qt::AlignHCenter;
  else /* "ALEFT" */
    align = Qt::AlignLeft;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    text->setAlignment(align);
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    edit->setAlignment(align);
  }

  return 1;
}

static char* qtTextGetCountAttrib(Ihandle* ih)
{
  int count;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    count = text->toPlainText().length();
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    count = edit->text().length();
  }

  return iupStrReturnInt(count);
}

static char* qtTextGetLineCountAttrib(Ihandle* ih)
{
  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    return iupStrReturnInt(text->document()->blockCount());
  }

  return nullptr;
}

static int qtTextSetCueBannerAttrib(Ihandle* ih, const char* value)
{
  if (!ih->data->is_multiline)
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    if (value)
      edit->setPlaceholderText(QString::fromUtf8(value));
    else
      edit->setPlaceholderText(QString());
  }

  return 1;
}

static char* qtTextGetCueBannerAttrib(Ihandle* ih)
{
  if (!ih->data->is_multiline)
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    QString text = edit->placeholderText();
    if (!text.isEmpty())
      return iupStrReturnStr(text.toUtf8().constData());
  }

  return nullptr;
}

static int qtTextSetFilterAttrib(Ihandle* ih, const char* value)
{
  if (!ih->data->is_multiline && value)
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;

    /* Create completer with filter list */
    QStringList filterList;
    QString filterStr = QString::fromUtf8(value);
    QStringList items = filterStr.split(';', Qt::SkipEmptyParts);

    for (const QString& item : items)
      filterList << item.trimmed();

    QCompleter* old_completer = edit->completer();
    QCompleter* completer = new QCompleter(filterList, edit);
    completer->setCaseSensitivity(Qt::CaseInsensitive);
    completer->setCompletionMode(QCompleter::PopupCompletion);
    edit->setCompleter(completer);
    delete old_completer;
  }

  return 1;
}

static int qtTextSetOverwriteAttrib(Ihandle* ih, const char* value)
{
  bool overwrite = iupStrBoolean(value);

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    text->setIupOverwriteMode(overwrite);
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    edit->setIupOverwriteMode(overwrite);
  }

  return 0;
}

static char* qtTextGetOverwriteAttrib(Ihandle* ih)
{
  bool overwrite;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    overwrite = text->isIupOverwriteMode();
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    overwrite = edit->isIupOverwriteMode();
  }

  return iupStrReturnBoolean(overwrite);
}

static int qtTextSetTabSizeAttrib(Ihandle* ih, const char* value)
{
  if (ih->data->is_multiline)
  {
    int tabsize = 8;
    iupStrToInt(value, &tabsize);

    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;

    /* Calculate tab stop in pixels based on font */
    QFontMetrics metrics(text->font());
    int tabStopWidth = tabsize * metrics.horizontalAdvance(' ');

    text->setTabStopDistance(tabStopWidth);
  }

  return 1;
}

static int qtTextSetBorderAttrib(Ihandle* ih, const char* value)
{
  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    if (iupStrBoolean(value))
      text->setFrameStyle(QFrame::Panel | QFrame::Sunken);
    else
      text->setFrameStyle(QFrame::NoFrame);
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    if (iupStrBoolean(value))
      edit->setFrame(true);
    else
      edit->setFrame(false);
  }

  return 1;
}

static int qtTextSetVisibleColumnsAttrib(Ihandle* ih, const char* value)
{
  if (!ih->data->is_multiline)
  {
    /* Like GTK's gtk_entry_set_width_chars(1), set minimum width to 1 character.
     * VISIBLECOLUMNS is used by IUP's platform-independent code to calculate
     * natural size, but Qt's widget minimum should not constrain IUP's layout. */
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    QFontMetrics metrics(edit->font());
    int min_width = metrics.horizontalAdvance('X');  /* 1 character minimum */
    edit->setMinimumWidth(min_width);
  }

  return 1;
}

static int qtTextSetVisibleLinesAttrib(Ihandle* ih, const char* value)
{
  if (ih->data->is_multiline)
  {
    int lines;
    if (iupStrToInt(value, &lines))
    {
      IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;

      /* Calculate height based on font metrics */
      QFontMetrics metrics(text->font());
      int height = lines * metrics.lineSpacing();
      text->setMinimumHeight(height);
    }
  }

  return 1;
}

static char* qtTextGetScrollVisibleAttrib(Ihandle* ih)
{
  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;

    int horiz_visible = 0, vert_visible = 0;

    QScrollBar* hsb = text->horizontalScrollBar();
    QScrollBar* vsb = text->verticalScrollBar();

    if (hsb && hsb->isVisible())
      horiz_visible = 1;
    if (vsb && vsb->isVisible())
      vert_visible = 1;

    if (horiz_visible && vert_visible)
      return (char*)"YES";
    else if (horiz_visible)
      return (char*)"HORIZONTAL";
    else if (vert_visible)
      return (char*)"VERTICAL";
    else
      return (char*)"NO";
  }

  return nullptr;
}

static char* qtTextGetPasswordAttrib(Ihandle* ih)
{
  if (!ih->data->is_multiline)
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    return iupStrReturnBoolean(edit->echoMode() == QLineEdit::Password);
  }

  return nullptr;
}

static char* qtTextGetBorderAttrib(Ihandle* ih)
{
  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = (IupQtTextEdit*)ih->handle;
    return iupStrReturnBoolean(text->frameStyle() != QFrame::NoFrame);
  }
  else
  {
    IupQtLineEdit* edit = (IupQtLineEdit*)ih->handle;
    return iupStrReturnBoolean(edit->hasFrame());
  }
}

/****************************************************************************
 * Spin Attribute Setters
 ****************************************************************************/

static int qtTextSetSpinMinAttrib(Ihandle* ih, const char* value)
{
  IupQtSpinBox* spin = (IupQtSpinBox*)iupAttribGet(ih, "_IUPQT_SPINBOX");
  if (spin)
  {
    int min;
    if (iupStrToInt(value, &min))
    {
      ih->data->disable_callbacks = 1;
      spin->setMinimum(min);
      ih->data->disable_callbacks = 0;
    }
  }
  return 1;
}

static int qtTextSetSpinMaxAttrib(Ihandle* ih, const char* value)
{
  IupQtSpinBox* spin = (IupQtSpinBox*)iupAttribGet(ih, "_IUPQT_SPINBOX");
  if (spin)
  {
    int max;
    if (iupStrToInt(value, &max))
    {
      ih->data->disable_callbacks = 1;
      spin->setMaximum(max);
      ih->data->disable_callbacks = 0;
    }
  }
  return 1;
}

static int qtTextSetSpinIncAttrib(Ihandle* ih, const char* value)
{
  IupQtSpinBox* spin = (IupQtSpinBox*)iupAttribGet(ih, "_IUPQT_SPINBOX");
  if (spin)
  {
    int inc;
    if (iupStrToInt(value, &inc))
      spin->setSingleStep(inc);
  }
  return 1;
}

/****************************************************************************
 * UnMap Method
 ****************************************************************************/

static void qtTextUnMapMethod(Ihandle* ih)
{
  if (ih->handle)
  {
    QWidget* widget = (QWidget*)ih->handle;

    /* Destroy tooltip if any */
    iupqtTipsDestroy(ih);

    /* Delete the widget - Qt will automatically disconnect signals */
    delete widget;
    ih->handle = nullptr;
  }
}

/****************************************************************************
 * Map Method
 ****************************************************************************/

static int qtTextMapMethod(Ihandle* ih)
{
  QWidget* widget = nullptr;

  if (ih->data->is_multiline)
  {
    IupQtTextEdit* text = new IupQtTextEdit(ih);

    /* Configure multiline text */
    text->setAcceptRichText(false);
    if (iupAttribGetBoolean(ih, "WORDWRAP"))
    {
      text->setLineWrapMode(QTextEdit::WidgetWidth);
      ih->data->sb &= ~IUP_SB_HORIZ;
    }
    else
      text->setLineWrapMode(QTextEdit::NoWrap);
    text->setTabStopDistance(40);

    /* Vertical scrollbar */
    if (ih->data->sb & IUP_SB_VERT)
      text->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    else
      text->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    /* Horizontal scrollbar */
    if (ih->data->sb & IUP_SB_HORIZ)
      text->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    else
      text->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    /* Connect signals */
    QObject::connect(text, &QTextEdit::textChanged, [ih]() {
      qtTextValueChanged(ih);
    });

    QObject::connect(text, &QTextEdit::cursorPositionChanged, [ih]() {
      qtTextCursorPositionChanged(ih);
    });

    widget = text;
  }
  else if (iupAttribGetBoolean(ih, "SPIN"))
  {
    IupQtSpinBox* spin = new IupQtSpinBox(ih);
    iupAttribSet(ih, "_IUPQT_SPINBOX", (char*)spin);

    int min = iupAttribGetInt(ih, "SPINMIN");
    int max = iupAttribGetInt(ih, "SPINMAX");
    int inc = iupAttribGetInt(ih, "SPININC");

    if (max == 0) max = 100;
    if (inc == 0) inc = 1;

    spin->setRange(min, max);
    spin->setSingleStep(inc);
    spin->setWrapping(iupAttribGetBoolean(ih, "SPINWRAP"));

    if (!iupAttribGetBoolean(ih, "SPINAUTO"))
      spin->spinauto = false;

    const char* spinvalue = iupAttribGetStr(ih, "SPINVALUE");
    if (spinvalue)
    {
      int spinval = 0;
      iupStrToInt(spinvalue, &spinval);
      spin->setValue(spinval);
    }

    QObject::connect(spin, QOverload<int>::of(&QSpinBox::valueChanged), [ih, spin](int value) {
      if (ih->data->disable_callbacks)
        return;

      iupAttribSetInt(ih, "SPINVALUE", value);
      qtTextValueChanged(ih);

      IFni spin_cb = (IFni)IupGetCallback(ih, "SPIN_CB");
      if (spin_cb)
        spin_cb(ih, value);
    });

    widget = spin;
  }
  else
  {
    IupQtLineEdit* edit = new IupQtLineEdit(ih);

    /* Connect signals */
    QObject::connect(edit, &QLineEdit::textChanged, [ih]() {
      qtTextValueChanged(ih);
    });

    QObject::connect(edit, &QLineEdit::cursorPositionChanged, [ih]() {
      qtTextCursorPositionChanged(ih);
    });

    widget = edit;
  }

  if (!widget)
    return IUP_ERROR;

  ih->handle = (InativeHandle*)widget;

  iupqtAddToParent(ih);

  if (IupGetCallback(ih, "DROPFILES_CB"))
    iupAttribSet(ih, "DROPFILESTARGET", "YES");

  if (ih->data->formattags)
    iupTextUpdateFormatTags(ih);

  return IUP_NOERROR;
}

/****************************************************************************
 * Attribute Setters
 ****************************************************************************/

static int qtTextSetPaddingAttrib(Ihandle* ih, const char* value)
{
  iupStrToIntInt(value, &ih->data->horiz_padding, &ih->data->vert_padding, 'x');

  if (ih->handle)
  {
    if (ih->data->is_multiline)
    {
      QTextEdit* text = (QTextEdit*)ih->handle;
      /* Set document margin for padding */
      text->document()->setDocumentMargin(ih->data->horiz_padding);
      /* Note: Qt's QTextEdit doesn't support separate vertical padding easily */
      ih->data->vert_padding = 0;
    }
    else
    {
      QLineEdit* edit = (QLineEdit*)ih->handle;
      /* Set text margins for padding */
      edit->setTextMargins(ih->data->horiz_padding, ih->data->vert_padding,
                           ih->data->horiz_padding, ih->data->vert_padding);
    }
    return 0;
  }
  else
    return 1; /* store until not mapped, when mapped will be set again */
}

static int qtTextSetRemoveFormattingAttrib(Ihandle* ih, const char* value);

/****************************************************************************
 * Class Initialization
 ****************************************************************************/

extern "C" IUP_SDK_API void iupdrvTextInitClass(Iclass* ic)
{
  /* Driver Dependent Class functions */
  ic->Map = qtTextMapMethod;
  ic->UnMap = qtTextUnMapMethod;

  /* Driver Dependent Attribute functions */

  /* Visual */
  iupClassRegisterAttribute(ic, "BGCOLOR", nullptr, iupdrvBaseSetBgColorAttrib, IUPAF_SAMEASSYSTEM, "TXTBGCOLOR", IUPAF_DEFAULT);
  iupClassRegisterAttribute(ic, "FGCOLOR", nullptr, iupdrvBaseSetFgColorAttrib, IUPAF_SAMEASSYSTEM, "TXTFGCOLOR", IUPAF_DEFAULT);

  /* IupText only */
  iupClassRegisterAttribute(ic, "PADDING", iupTextGetPaddingAttrib, qtTextSetPaddingAttrib, IUPAF_SAMEASSYSTEM, "0x0", IUPAF_NOT_MAPPED);
  iupClassRegisterAttribute(ic, "VALUE", qtTextGetValueAttrib, qtTextSetValueAttrib, nullptr, nullptr, IUPAF_NO_DEFAULTVALUE | IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "LINEVALUE", qtTextGetLineValueAttrib, nullptr, nullptr, nullptr, IUPAF_READONLY | IUPAF_NO_DEFAULTVALUE | IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "SELECTEDTEXT", qtTextGetSelectedTextAttrib, qtTextSetSelectedTextAttrib, nullptr, nullptr, IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "SELECTION", qtTextGetSelectionAttrib, qtTextSetSelectionAttrib, nullptr, nullptr, IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "SELECTIONPOS", qtTextGetSelectionPosAttrib, qtTextSetSelectionPosAttrib, nullptr, nullptr, IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "CARET", qtTextGetCaretAttrib, qtTextSetCaretAttrib, nullptr, nullptr, IUPAF_NO_SAVE|IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "CARETPOS", qtTextGetCaretPosAttrib, qtTextSetCaretPosAttrib, IUPAF_SAMEASSYSTEM, "0", IUPAF_NO_SAVE|IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "INSERT", nullptr, qtTextSetInsertAttrib, nullptr, nullptr, IUPAF_WRITEONLY | IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "APPEND", nullptr, qtTextSetAppendAttrib, nullptr, nullptr, IUPAF_WRITEONLY | IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "READONLY", qtTextGetReadOnlyAttrib, qtTextSetReadOnlyAttrib, nullptr, nullptr, IUPAF_DEFAULT);
  iupClassRegisterAttribute(ic, "NC", qtTextGetNCAttrib, qtTextSetNCAttrib, nullptr, nullptr, IUPAF_NOT_MAPPED);
  iupClassRegisterAttribute(ic, "CLIPBOARD", nullptr, qtTextSetClipboardAttrib, nullptr, nullptr, IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "SCROLLTO", nullptr, qtTextSetScrollToAttrib, nullptr, nullptr, IUPAF_WRITEONLY | IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "SCROLLTOPOS", nullptr, qtTextSetScrollToPosAttrib, nullptr, nullptr, IUPAF_WRITEONLY | IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "SPINMIN", nullptr, qtTextSetSpinMinAttrib, IUPAF_SAMEASSYSTEM, "0", IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "SPINMAX", nullptr, qtTextSetSpinMaxAttrib, IUPAF_SAMEASSYSTEM, "100", IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "SPININC", nullptr, qtTextSetSpinIncAttrib, IUPAF_SAMEASSYSTEM, "1", IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "SPINVALUE", qtTextGetSpinValueAttrib, qtTextSetSpinValueAttrib, IUPAF_SAMEASSYSTEM, "0", IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "COUNT", qtTextGetCountAttrib, nullptr, nullptr, nullptr, IUPAF_READONLY | IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "LINECOUNT", qtTextGetLineCountAttrib, nullptr, nullptr, nullptr, IUPAF_READONLY | IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "ALIGNMENT", nullptr, qtTextSetAlignmentAttrib, IUPAF_SAMEASSYSTEM, "ALEFT", IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "PASSWORD", qtTextGetPasswordAttrib, qtTextSetPasswordAttrib, nullptr, nullptr, IUPAF_NO_INHERIT);

  /* Additional attributes */
  iupClassRegisterAttribute(ic, "CUEBANNER", qtTextGetCueBannerAttrib, qtTextSetCueBannerAttrib, nullptr, nullptr, IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "FILTER", nullptr, qtTextSetFilterAttrib, nullptr, nullptr, IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "OVERWRITE", qtTextGetOverwriteAttrib, qtTextSetOverwriteAttrib, nullptr, nullptr, IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "TABSIZE", nullptr, qtTextSetTabSizeAttrib, IUPAF_SAMEASSYSTEM, "8", IUPAF_DEFAULT);
  iupClassRegisterAttribute(ic, "BORDER", qtTextGetBorderAttrib, qtTextSetBorderAttrib, IUPAF_SAMEASSYSTEM, "YES", IUPAF_DEFAULT);
  iupClassRegisterAttribute(ic, "VISIBLECOLUMNS", nullptr, qtTextSetVisibleColumnsAttrib, IUPAF_SAMEASSYSTEM, "5", IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "VISIBLELINES", nullptr, qtTextSetVisibleLinesAttrib, IUPAF_SAMEASSYSTEM, "1", IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "SCROLLVISIBLE", qtTextGetScrollVisibleAttrib, nullptr, nullptr, nullptr, IUPAF_READONLY|IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "DROPFILESTARGET", nullptr, nullptr, nullptr, nullptr, IUPAF_NO_INHERIT);

  /* Rich text formatting (Qt, GTK and Windows) */
  iupClassRegisterAttribute(ic, "ADDFORMATTAG", nullptr, iupTextSetAddFormatTagAttrib, nullptr, nullptr, IUPAF_IHANDLENAME|IUPAF_NOT_MAPPED|IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "ADDFORMATTAG_HANDLE", nullptr, iupTextSetAddFormatTagHandleAttrib, nullptr, nullptr, IUPAF_IHANDLE | IUPAF_NOT_MAPPED | IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "FORMATTING", iupTextGetFormattingAttrib, iupTextSetFormattingAttrib, nullptr, nullptr, IUPAF_NOT_MAPPED|IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "REMOVEFORMATTING", nullptr, qtTextSetRemoveFormattingAttrib, nullptr, nullptr, IUPAF_WRITEONLY|IUPAF_NO_INHERIT);
}

/* Format tag functions for rich text support */

static bool qtTextParseSelectionPos(const char* selectionpos, int* start, int* end)
{
  if (!selectionpos)
    return false;

  if (sscanf(selectionpos, "%d:%d", start, end) == 2)
    return true;

  return false;
}

static bool qtTextParseSelection(Ihandle* ih, const char* value, int* start, int* end)
{
  int lin_start = 1, col_start = 1, lin_end = 1, col_end = 1;

  if (!value || iupStrEqualNoCase(value, "NONE"))
  {
    *start = 0;
    *end = 0;
    return true;
  }

  if (iupStrEqualNoCase(value, "ALL"))
  {
    QTextEdit* text = (QTextEdit*)ih->handle;
    *start = 0;
    *end = text->toPlainText().length();
    return true;
  }

  if (sscanf(value, "%d,%d:%d,%d", &lin_start, &col_start, &lin_end, &col_end) != 4)
    return false;

  if (lin_start < 1 || col_start < 1 || lin_end < 1 || col_end < 1)
    return false;

  iupdrvTextConvertLinColToPos(ih, lin_start, col_start, start);
  iupdrvTextConvertLinColToPos(ih, lin_end, col_end, end);
  return true;
}

static bool qtTextParseParagraphFormat(Ihandle* formattag, QTextBlockFormat* blockFormat)
{
  int val;
  const char* format;
  bool has_format = false;

  format = iupAttribGet(formattag, "ALIGNMENT");
  if (format)
  {
    if (iupStrEqualNoCase(format, "JUSTIFY"))
      blockFormat->setAlignment(Qt::AlignJustify);
    else if (iupStrEqualNoCase(format, "RIGHT"))
      blockFormat->setAlignment(Qt::AlignRight);
    else if (iupStrEqualNoCase(format, "CENTER"))
      blockFormat->setAlignment(Qt::AlignCenter);
    else
      blockFormat->setAlignment(Qt::AlignLeft);
    has_format = true;
  }

  format = iupAttribGet(formattag, "INDENT");
  if (format && iupStrToInt(format, &val))
  {
    blockFormat->setLeftMargin(val);

    const char* indent_right = iupAttribGet(formattag, "INDENTRIGHT");
    if (indent_right && iupStrToInt(indent_right, &val))
      blockFormat->setRightMargin(val);

    const char* indent_offset = iupAttribGet(formattag, "INDENTOFFSET");
    if (indent_offset && iupStrToInt(indent_offset, &val))
      blockFormat->setTextIndent(val);

    has_format = true;
  }

  format = iupAttribGet(formattag, "SPACEBEFORE");
  if (format && iupStrToInt(format, &val))
  {
    blockFormat->setTopMargin(val);
    has_format = true;
  }

  format = iupAttribGet(formattag, "SPACEAFTER");
  if (format && iupStrToInt(format, &val))
  {
    blockFormat->setBottomMargin(val);
    has_format = true;
  }

  format = iupAttribGet(formattag, "LINESPACING");
  if (format)
  {
    if (iupStrEqualNoCase(format, "SINGLE"))
      blockFormat->setLineHeight(100, QTextBlockFormat::ProportionalHeight);
    else if (iupStrEqualNoCase(format, "ONEHALF"))
      blockFormat->setLineHeight(150, QTextBlockFormat::ProportionalHeight);
    else if (iupStrEqualNoCase(format, "DOUBLE"))
      blockFormat->setLineHeight(200, QTextBlockFormat::ProportionalHeight);
    else if (iupStrToInt(format, &val))
      blockFormat->setLineHeight(val, QTextBlockFormat::FixedHeight);
    has_format = true;
  }

  format = iupAttribGet(formattag, "TABSARRAY");
  if (format)
  {
    QList<QTextOption::Tab> tabs;
    int pos, i = 0;
    char* str;

    while (format)
    {
      str = iupStrDupUntil((const char**)&format, ' ');
      if (!str) break;
      iupStrToInt(str, &pos);
      free(str);

      str = iupStrDupUntil((const char**)&format, ' ');
      if (!str) break;

      QTextOption::TabType tabType = QTextOption::LeftTab;
      if (iupStrEqualNoCase(str, "RIGHT"))
        tabType = QTextOption::RightTab;
      else if (iupStrEqualNoCase(str, "CENTER"))
        tabType = QTextOption::CenterTab;
      else if (iupStrEqualNoCase(str, "DECIMAL"))
        tabType = QTextOption::DelimiterTab;
      free(str);

      tabs.append(QTextOption::Tab(pos, tabType));
      i++;
      if (i == 32) break;
    }

    blockFormat->setTabPositions(tabs);
    has_format = true;
  }

  return has_format;
}

static void qtTextParseCharacterFormat(Ihandle* formattag, QTextCharFormat* charFormat)
{
  int val;
  const char* format;

  format = iupAttribGet(formattag, "FONTFACE");
  if (format)
  {
    const char* mapped_name = iupFontGetPangoName(format);
    if (mapped_name)
      charFormat->setFontFamilies({QString(mapped_name)});
    else
      charFormat->setFontFamilies({QString(format)});
  }

  format = iupAttribGet(formattag, "FONTSIZE");
  if (format && iupStrToInt(format, &val))
  {
    if (val < 0)
      charFormat->setProperty(QTextFormat::FontPixelSize, -val);
    else
      charFormat->setFontPointSize(val);
  }

  format = iupAttribGet(formattag, "FONTSCALE");
  if (format)
  {
    double fval = 0;
    if (iupStrEqualNoCase(format, "XX-SMALL"))
      fval = 0.5787037037037;
    else if (iupStrEqualNoCase(format, "X-SMALL"))
      fval = 0.6444444444444;
    else if (iupStrEqualNoCase(format, "SMALL"))
      fval = 0.8333333333333;
    else if (iupStrEqualNoCase(format, "MEDIUM"))
      fval = 1.0;
    else if (iupStrEqualNoCase(format, "LARGE"))
      fval = 1.2;
    else if (iupStrEqualNoCase(format, "X-LARGE"))
      fval = 1.4399999999999;
    else if (iupStrEqualNoCase(format, "XX-LARGE"))
      fval = 1.728;
    else
      iupStrToDouble(format, &fval);

    if (fval > 0)
    {
      double currentSize = charFormat->fontPointSize();
      if (currentSize > 0)
        charFormat->setFontPointSize(currentSize * fval);
    }
  }

  format = iupAttribGet(formattag, "RISE");
  if (format)
  {
    if (iupStrEqualNoCase(format, "SUPERSCRIPT"))
      charFormat->setVerticalAlignment(QTextCharFormat::AlignSuperScript);
    else if (iupStrEqualNoCase(format, "SUBSCRIPT"))
      charFormat->setVerticalAlignment(QTextCharFormat::AlignSubScript);
    else if (iupStrToInt(format, &val))
    {
      if (val > 0)
        charFormat->setVerticalAlignment(QTextCharFormat::AlignSuperScript);
      else if (val < 0)
        charFormat->setVerticalAlignment(QTextCharFormat::AlignSubScript);
      else
        charFormat->setVerticalAlignment(QTextCharFormat::AlignNormal);
    }
  }

  format = iupAttribGet(formattag, "SMALLCAPS");
  if (format)
  {
    if (iupStrBoolean(format))
      charFormat->setFontCapitalization(QFont::SmallCaps);
    else
      charFormat->setFontCapitalization(QFont::MixedCase);
  }

  format = iupAttribGet(formattag, "ITALIC");
  if (format)
  {
    charFormat->setFontItalic(iupStrBoolean(format));
  }

  format = iupAttribGet(formattag, "STRIKEOUT");
  if (format)
  {
    charFormat->setFontStrikeOut(iupStrBoolean(format));
  }

  format = iupAttribGet(formattag, "PROTECTED");
  if (format)
  {
    if (iupStrBoolean(format))
      charFormat->setObjectType(QTextFormat::UserObject);
  }

  format = iupAttribGet(formattag, "FGCOLOR");
  if (format)
  {
    unsigned char r, g, b;
    if (iupStrToRGB(format, &r, &g, &b))
      charFormat->setForeground(QColor(r, g, b));
  }

  format = iupAttribGet(formattag, "BGCOLOR");
  if (format)
  {
    unsigned char r, g, b;
    if (iupStrToRGB(format, &r, &g, &b))
      charFormat->setBackground(QColor(r, g, b));
  }

  format = iupAttribGet(formattag, "UNDERLINE");
  if (format)
  {
    if (iupStrEqualNoCase(format, "SINGLE"))
      charFormat->setUnderlineStyle(QTextCharFormat::SingleUnderline);
    else if (iupStrEqualNoCase(format, "DOUBLE"))
      charFormat->setUnderlineStyle(QTextCharFormat::WaveUnderline);
    else if (iupStrEqualNoCase(format, "DOTTED"))
      charFormat->setUnderlineStyle(QTextCharFormat::DotLine);
    else
      charFormat->setUnderlineStyle(QTextCharFormat::NoUnderline);
  }

  format = iupAttribGet(formattag, "WEIGHT");
  if (format)
  {
    if (iupStrEqualNoCase(format, "EXTRALIGHT"))
      charFormat->setFontWeight(QFont::ExtraLight);
    else if (iupStrEqualNoCase(format, "LIGHT"))
      charFormat->setFontWeight(QFont::Light);
    else if (iupStrEqualNoCase(format, "SEMIBOLD"))
      charFormat->setFontWeight(QFont::DemiBold);
    else if (iupStrEqualNoCase(format, "BOLD"))
      charFormat->setFontWeight(QFont::Bold);
    else if (iupStrEqualNoCase(format, "EXTRABOLD"))
      charFormat->setFontWeight(QFont::ExtraBold);
    else if (iupStrEqualNoCase(format, "HEAVY"))
      charFormat->setFontWeight(QFont::Black);
    else
      charFormat->setFontWeight(QFont::Normal);
  }

  format = iupAttribGet(formattag, "LINK");
  if (format)
  {
    charFormat->setAnchor(true);
    charFormat->setAnchorHref(QString(format));

    if (!iupAttribGet(formattag, "FGCOLOR"))
      charFormat->setForeground(QColor(0, 0, 255));

    if (!iupAttribGet(formattag, "UNDERLINE"))
      charFormat->setUnderlineStyle(QTextCharFormat::SingleUnderline);
  }
}

static int qtTextSetRemoveFormattingAttrib(Ihandle* ih, const char* value)
{
  if (!ih->data->is_multiline)
    return 0;

  QTextEdit* text = (QTextEdit*)ih->handle;
  if (!text)
    return 0;

  QTextCursor cursor = text->textCursor();

  if (iupStrEqualNoCase(value, "ALL"))
    cursor.select(QTextCursor::Document);
  else if (!cursor.hasSelection())
    return 0;

  cursor.setCharFormat(QTextCharFormat());
  cursor.setBlockFormat(QTextBlockFormat());

  return 0;
}

extern "C" IUP_SDK_API void* iupdrvTextAddFormatTagStartBulk(Ihandle* ih)
{
  if (!ih->data->is_multiline)
    return NULL;

  QTextEdit* text = (QTextEdit*)ih->handle;
  if (!text)
    return NULL;

  bool* undo_enabled = new bool(text->isUndoRedoEnabled());
  text->setUndoRedoEnabled(false);

  return (void*)undo_enabled;
}

extern "C" IUP_SDK_API void iupdrvTextAddFormatTagStopBulk(Ihandle* ih, void* state)
{
  if (!ih->data->is_multiline || !state)
    return;

  QTextEdit* text = (QTextEdit*)ih->handle;
  if (!text)
    return;

  bool* undo_enabled = (bool*)state;
  text->setUndoRedoEnabled(*undo_enabled);
  delete undo_enabled;
}

extern "C" IUP_SDK_API void iupdrvTextAddFormatTag(Ihandle* ih, Ihandle* formattag, int bulk)
{
  (void)bulk;

  if (!ih->data->is_multiline)
    return;

  QTextEdit* text = (QTextEdit*)ih->handle;
  if (!text)
    return;

  int start_pos = 0, end_pos = 0;

  const char* selection = iupAttribGet(formattag, "SELECTION");
  if (selection)
  {
    if (!qtTextParseSelection(ih, selection, &start_pos, &end_pos))
      return;
  }
  else
  {
    const char* selectionpos = iupAttribGet(formattag, "SELECTIONPOS");
    if (selectionpos)
    {
      if (!qtTextParseSelectionPos(selectionpos, &start_pos, &end_pos))
        return;
    }
    else
    {
      QTextCursor cur = text->textCursor();
      start_pos = cur.position();
      end_pos = cur.position();
    }
  }

  QTextCursor cursor = text->textCursor();
  cursor.setPosition(start_pos);
  cursor.setPosition(end_pos, QTextCursor::KeepAnchor);

  {
    const char* image_name = iupAttribGet(formattag, "IMAGE");
    if (image_name)
    {
      QPixmap* pixmap = (QPixmap*)iupImageGetImage(image_name, ih, 0, NULL);
      if (pixmap)
      {
        int img_w, img_h;
        int new_w = 0, new_h = 0;
        const char* attr;

        iupImageGetInfo(image_name, &img_w, &img_h, NULL);

        attr = iupAttribGet(formattag, "WIDTH");
        if (attr) iupStrToInt(attr, &new_w);
        attr = iupAttribGet(formattag, "HEIGHT");
        if (attr) iupStrToInt(attr, &new_h);

        QString resName = QString("iup_image_%1").arg(image_name);
        text->document()->addResource(QTextDocument::ImageResource, QUrl(resName), *pixmap);

        cursor.removeSelectedText();

        QTextImageFormat imgFormat;
        imgFormat.setName(resName);
        if (new_w > 0) imgFormat.setWidth(new_w);
        if (new_h > 0) imgFormat.setHeight(new_h);

        cursor.insertImage(imgFormat);
      }
      return;
    }
  }

  if (iupAttribGet(formattag, "FONTSCALE") && !iupAttribGet(formattag, "FONTSIZE"))
    iupAttribSet(formattag, "FONTSIZE", iupGetFontSizeAttrib(ih));

  QTextCharFormat charFormat;
  qtTextParseCharacterFormat(formattag, &charFormat);
  cursor.mergeCharFormat(charFormat);

  QTextBlockFormat blockFormat;
  if (qtTextParseParagraphFormat(formattag, &blockFormat))
    cursor.mergeBlockFormat(blockFormat);

  const char* numbering = iupAttribGet(formattag, "NUMBERING");
  if (numbering)
  {
    if (iupStrEqualNoCase(numbering, "NONE"))
    {
      QTextList* list = cursor.currentList();
      if (list)
      {
        QTextBlock block = cursor.block();
        list->remove(block);
      }
    }
    else
    {
      QTextListFormat listFormat;

      if (iupStrEqualNoCase(numbering, "BULLET"))
        listFormat.setStyle(QTextListFormat::ListDisc);
      else if (iupStrEqualNoCase(numbering, "ARABIC"))
        listFormat.setStyle(QTextListFormat::ListDecimal);
      else if (iupStrEqualNoCase(numbering, "LCLETTER"))
        listFormat.setStyle(QTextListFormat::ListLowerAlpha);
      else if (iupStrEqualNoCase(numbering, "UCLETTER"))
        listFormat.setStyle(QTextListFormat::ListUpperAlpha);
      else if (iupStrEqualNoCase(numbering, "LCROMAN"))
        listFormat.setStyle(QTextListFormat::ListLowerRoman);
      else if (iupStrEqualNoCase(numbering, "UCROMAN"))
        listFormat.setStyle(QTextListFormat::ListUpperRoman);
      else
        listFormat.setStyle(QTextListFormat::ListDisc);

      const char* style = iupAttribGet(formattag, "NUMBERINGSTYLE");
      if (style)
      {
        if (iupStrEqualNoCase(style, "RIGHTPARENTHESIS"))
        {
          listFormat.setNumberPrefix(QString());
          listFormat.setNumberSuffix(QStringLiteral(")"));
        }
        else if (iupStrEqualNoCase(style, "PARENTHESES"))
        {
          listFormat.setNumberPrefix(QStringLiteral("("));
          listFormat.setNumberSuffix(QStringLiteral(")"));
        }
        else if (iupStrEqualNoCase(style, "PERIOD"))
        {
          listFormat.setNumberPrefix(QString());
          listFormat.setNumberSuffix(QStringLiteral("."));
        }
        else if (iupStrEqualNoCase(style, "NONUMBER"))
        {
          listFormat.setNumberPrefix(QString());
          listFormat.setNumberSuffix(QString());
        }
      }

      const char* numberingtab = iupAttribGet(formattag, "NUMBERINGTAB");
      if (numberingtab)
      {
        int tabval = 0;
        if (iupStrToInt(numberingtab, &tabval))
          listFormat.setIndent(tabval / 8);
      }

      cursor.createList(listFormat);
    }
  }
}
