commit 56213bb0b578e7bedd3ab5f7d4f251d4ad32e996
Author: Juergen Spitzmueller <[email protected]>
Date:   Mon Mar 17 16:48:43 2025 +0100

    Implement central model for ColorsCombos
    
    This should hopefully reduce redundant cycles.
---
 src/frontends/qt/ColorsCombo.cpp  | 600 ++++++++++++++++++++++++++++++++------
 src/frontends/qt/ColorsCombo.h    |  52 +++-
 src/frontends/qt/GuiBox.cpp       |   3 -
 src/frontends/qt/GuiBox.h         |   3 -
 src/frontends/qt/GuiCharacter.cpp |   8 -
 src/frontends/qt/GuiCharacter.h   |   2 -
 src/frontends/qt/GuiDocument.cpp  |  21 +-
 src/frontends/qt/GuiTabular.cpp   |   8 -
 src/frontends/qt/GuiTabular.h     |   2 -
 src/frontends/qt/GuiView.cpp      | 117 +++++++-
 src/frontends/qt/GuiView.h        |  16 +
 11 files changed, 688 insertions(+), 144 deletions(-)

diff --git a/src/frontends/qt/ColorsCombo.cpp b/src/frontends/qt/ColorsCombo.cpp
index 734df200f8..5ae8c787d7 100644
--- a/src/frontends/qt/ColorsCombo.cpp
+++ b/src/frontends/qt/ColorsCombo.cpp
@@ -11,141 +11,565 @@
 #include <config.h>
 
 #include "ColorsCombo.h"
-#include "LaTeXColors.h"
+#include "GuiView.h"
 
+#include "support/debug.h"
 #include "support/gettext.h"
+#include "support/lassert.h"
 #include "support/lstrings.h"
+#include "support/qstring_helpers.h"
+
 #include "qt_helpers.h"
 
-#include "support/qstring_helpers.h"
+#include <QAbstractTextDocumentLayout>
+#include <QComboBox>
+#include <QHeaderView>
+#include <QItemDelegate>
+#include <QPainter>
+#include <QSortFilterProxyModel>
+#include <QStandardItemModel>
+#include <QTextFrame>
+
+#include "GuiApplication.h"
 
 #include <QPainter>
+#include <QListView>
 
 using namespace lyx::support;
 
 namespace lyx {
 namespace frontend {
 
+class ColItemDelegate : public QItemDelegate {
+public:
+       ///
+       explicit ColItemDelegate(ColorsCombo * cc)
+               : QItemDelegate(cc), cc_(cc)
+       {}
+       ///
+       void paint(QPainter * painter, QStyleOptionViewItem const & option,
+               QModelIndex const & index) const override;
+       ///
+       void drawDisplay(QPainter * painter, QStyleOptionViewItem const & opt,
+               const QRect & /*rect*/, const QString & text ) const override;
+       ///
+       QSize sizeHint(QStyleOptionViewItem const & opt,
+               QModelIndex const & index) const override;
+
+private:
+       ///
+       void drawCategoryHeader(QPainter * painter, QStyleOptionViewItem const 
& opt,
+               QString const & category) const;
+       ///
+       QString underlineFilter(QString const & s) const;
+       ///
+       ColorsCombo * cc_;
+};
+
+
+class CCFilterModel : public QSortFilterProxyModel {
+public:
+       ///
+       CCFilterModel(QObject * parent = nullptr)
+               : QSortFilterProxyModel(parent)
+       {}
+};
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// ColorsCombo::Private
+//
+/////////////////////////////////////////////////////////////////////
+
+struct ColorsCombo::Private
+{
+       Private(ColorsCombo * parent) : p(parent),
+               // set the model with five columns
+               // 1st: translated item names
+               // 2nd: raw names
+               // 3rd: category
+               // 4th: availability (bool)
+               model_(guiApp->currentView()->viewColorsModel()),
+               filterModel_(new CCFilterModel(p)),
+               lastSel_(-1),
+               ColItemDelegate_(new ColItemDelegate(parent)),
+               visibleCategories_(0), inShowPopup_(false)
+       {
+               filterModel_->setSourceModel(model_);
+       }
+
+       void resetFilter() { setFilter(QString()); }
+       ///
+       void setFilter(QString const & s);
+       ///
+       void countCategories();
+       ///
+       void toggleRows(QString const data, bool const toggle);
+       ///
+       ColorsCombo * p;
+
+       /** the layout model:
+        * 1st column: translated GUI name,
+        * 2nd column: raw item name,
+        * 3rd column: category,
+        * 4th column: custom color?
+       **/
+       QStandardItemModel * model_;
+       /// the proxy model filtering \c model_
+       CCFilterModel * filterModel_;
+       /// the (model-) index of the last successful selection
+       int lastSel_;
+       /// the character filter
+       QString filter_;
+       ///
+       ColItemDelegate * ColItemDelegate_;
+       ///
+       unsigned visibleCategories_;
+       ///
+       bool inShowPopup_;
+};
+
+
+static QString categoryCC(QAbstractItemModel const & model, int row)
+{
+       return model.data(model.index(row, 2), Qt::DisplayRole).toString();
+}
+
+
+static int headerHeightCC(QStyleOptionViewItem const & opt)
+{
+       return opt.fontMetrics.height();
+}
+
+
+void ColItemDelegate::paint(QPainter * painter, QStyleOptionViewItem const & 
option,
+                          QModelIndex const & index) const
+{
+       QStyleOptionViewItem opt = option;
+
+       // default background
+       painter->fillRect(opt.rect, opt.palette.color(QPalette::Base));
+
+       QString cat = categoryCC(*index.model(), index.row());
+
+       // not the same as in the previous line?
+       if (cc_->d->visibleCategories_ > 0
+           && (index.row() == 0 || cat != categoryCC(*index.model(), 
index.row() - 1))) {
+               painter->save();
+
+               // draw unselected background
+               QStyle::State state = opt.state;
+               opt.state = opt.state & ~QStyle::State_Selected;
+               drawBackground(painter, opt, index);
+               opt.state = state;
+
+               // draw category header
+               drawCategoryHeader(painter, opt,
+                       categoryCC(*index.model(), index.row()));
+
+               // move rect down below header
+               opt.rect.setTop(opt.rect.top() + headerHeightCC(opt));
+
+               painter->restore();
+       }
+
+       QItemDelegate::paint(painter, opt, index);
+}
+
+
+void ColItemDelegate::drawDisplay(QPainter * painter, QStyleOptionViewItem 
const & opt,
+                                const QRect & /*rect*/, const QString & 
text_in) const
+{
+       QString text(text_in);
+       // Mark default color
+       if (cc_->need_default_color_ && cc_->isDefaultColor(text))
+               text = toqstr(bformat(_("%1$s (= Default[[color]])"), 
qstring_to_ucs4(text)));
+
+       QString utext = underlineFilter(text);
+
+       // Draw the rich text.
+       painter->save();
+       QColor col = opt.palette.text().color();
+       if (opt.state & QStyle::State_Selected)
+               col = opt.palette.highlightedText().color();
+       QAbstractTextDocumentLayout::PaintContext context;
+       context.palette.setColor(QPalette::Text, col);
+
+       QTextDocument doc;
+       doc.setDefaultFont(opt.font);
+       doc.setHtml(utext);
+
+       QTextFrameFormat fmt = doc.rootFrame()->frameFormat();
+       fmt.setMargin(0);
+       if (cc_->left_margin_ != -1)
+               fmt.setLeftMargin(cc_->left_margin_);
+               
+       doc.rootFrame()->setFrameFormat(fmt);
+
+       painter->translate(opt.rect.x() + 5,
+               opt.rect.y() + (opt.rect.height() - opt.fontMetrics.height()) / 
2);
+       doc.documentLayout()->draw(painter, context);
+       painter->restore();
+}
+
+
+QSize ColItemDelegate::sizeHint(QStyleOptionViewItem const & opt,
+                              QModelIndex const & index) const
+{
+       QSize size = QItemDelegate::sizeHint(opt, index);
+
+       /// QComboBox uses the first row height to estimate the
+       /// complete popup height during QComboBox::showPopup().
+       /// To avoid scrolling we have to sneak in space for the headers.
+       /// So we tweak this value accordingly. It's not nice, but the
+       /// only possible way it seems.
+       // Add space for the category headers here
+       QString cat = categoryCC(*index.model(), index.row());
+       if (index.row() == 0 || cat != categoryCC(*index.model(), index.row() - 
1)) {
+               size.setHeight(size.height() + headerHeightCC(opt));
+       }
+
+       return size;
+}
+
+
+void ColItemDelegate::drawCategoryHeader(QPainter * painter, 
QStyleOptionViewItem const & opt,
+                                       QString const & category) const
+{
+       // slightly blended color
+       QColor lcol = opt.palette.text().color();
+       lcol.setAlpha(127);
+       painter->setPen(lcol);
+
+       // set 80% scaled, bold font
+       QFont font = opt.font;
+       font.setBold(true);
+       font.setWeight(QFont::Black);
+       font.setPointSize(opt.font.pointSize() * 8 / 10);
+       painter->setFont(font);
+
+       // draw the centered text
+       QFontMetrics fm(font);
+       int w = fm.boundingRect(category).width();
+       int x = opt.rect.x() + (opt.rect.width() - w) / 2;
+       int y = opt.rect.y() + 3 * fm.ascent() / 2;
+       int left = x;
+       int right = x + w;
+       painter->drawText(x, y, category);
+
+       // the vertical position of the line: middle of lower case chars
+       int ymid = y - 1 - fm.xHeight() / 2; // -1 for the baseline
+
+       // draw the horizontal line
+       if (!category.isEmpty()) {
+               painter->drawLine(opt.rect.x(), ymid, left - 1, ymid);
+               painter->drawLine(right + 1, ymid, opt.rect.right(), ymid);
+       } else
+               painter->drawLine(opt.rect.x(), ymid, opt.rect.right(), ymid);
+}
+
+
+QString ColItemDelegate::underlineFilter(QString const & s) const
+{
+       QString const & f = cc_->filter();
+       if (f.isEmpty())
+               return s;
+       QString r(s);
+       QRegularExpression pattern(charFilterRegExpC(f));
+       r.replace(pattern, "<u><b>\\1</b></u>");
+       return r;
+}
+
+
+void ColorsCombo::Private::setFilter(QString const & s)
+{
+       bool enabled = p->view()->updatesEnabled();
+       p->view()->setUpdatesEnabled(false);
+
+       // remember old selection
+       int sel = p->currentIndex();
+       if (sel != -1)
+               lastSel_ = filterModel_->mapToSource(filterModel_->index(sel, 
0)).row();
+
+       filter_ = s;
+
+       // toggle context-dependent entries
+       toggleRows("ignore", p->has_ignore_);
+       toggleRows("inherit", p->has_inherit_);
+       toggleRows("none", p->has_none_ && p->default_color_.isEmpty());
+
+#if QT_VERSION < QT_VERSION_CHECK(5, 12, 0)
+       filterModel_->setFilterRegExp(charFilterRegExp(filter_));
+#else
+       filterModel_->setFilterRegularExpression(charFilterRegExp(filter_));
+#endif
+       countCategories();
+
+       // restore old selection
+       if (lastSel_ != -1) {
+               QModelIndex i = 
filterModel_->mapFromSource(model_->index(lastSel_, 0));
+               if (i.isValid())
+                       p->setCurrentIndex(i.row());
+       }
+
+       // Workaround to resize to content size
+       // FIXME: There must be a better way. The QComboBox::AdjustToContents)
+       //        does not help.
+       if (p->view()->isVisible()) {
+               // call QComboBox::showPopup. But set the inShowPopup_ flag to 
switch on
+               // the hack in the item delegate to make space for the headers.
+               // We do not call our implementation of showPopup because that
+               // would reset the filter again. This is only needed if the 
user clicks
+               // on the QComboBox.
+               LASSERT(!inShowPopup_, /**/);
+               inShowPopup_ = true;
+               p->QComboBox::showPopup();
+               inShowPopup_ = false;
+       }
+
+       p->view()->setUpdatesEnabled(enabled);
+}
+
+
+void ColorsCombo::Private::toggleRows(QString const data, bool const toggle)
+{
+       QList<QStandardItem *> cis = model_->findItems(data, Qt::MatchExactly, 
1);
+       if (cis.empty())
+               return;
+
+       QListView * view = qobject_cast<QListView *>(p->view());
+       QStandardItem * item = cis.front();
+       view->setRowHidden(item->row(), !toggle);
+       if (toggle)
+               item->setFlags(item->flags() & Qt::ItemIsEnabled);
+       else
+               item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
+}
+
+
+void ColorsCombo::Private::countCategories()
+{
+       int n = filterModel_->rowCount();
+       visibleCategories_ = 0;
+       if (n == 0)
+               return;
+
+       QString prevCat = model_->index(0, 2).data().toString();
+
+       // count categories
+       for (int i = 1; i < n; ++i) {
+               QString cat = filterModel_->index(i, 2).data().toString();
+               if (cat != prevCat)
+                       ++visibleCategories_;
+               prevCat = cat;
+       }
+}
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// ColorsCombo
+//
+/////////////////////////////////////////////////////////////////////
+
 
 ColorsCombo::ColorsCombo(QWidget * parent)
-       : CategorizedCombo(parent),
+       : QComboBox(parent),
          has_ignore_(false), has_inherit_(false),
-         has_none_(false), default_value_("none")
+         has_none_(false), default_value_("none"),
+         need_default_color_(false),
+         d(new Private(this))
 {
        setLeftMargin(32);
-       fillComboColor();
        setToolTip(qt_("Type on the list to filter on color names."));
+
+       setSizeAdjustPolicy(QComboBox::AdjustToContents);
+       setMinimumWidth(sizeHint().width());
+       setMaxVisibleItems(100);
+
+       setModel(d->filterModel_);
+       connect(guiApp->currentView(), SIGNAL(colorsModelChanged()),
+               this, SLOT(modelChanged()));
+
+       // for the filtering we have to intercept characters
+       view()->installEventFilter(this);
+       view()->setItemDelegateForColumn(0, d->ColItemDelegate_);
+
+       updateCombo();
 }
 
 
 ColorsCombo::~ColorsCombo()
-{}
+{
+       delete d;
+}
 
 
-void ColorsCombo::setColorIcon(int const i, QString const color)
+void ColorsCombo::showPopup()
 {
-       if (color.isEmpty())
-               return;
+       lastCurrentIndex_ = currentIndex();
+       bool enabled = view()->updatesEnabled();
+       view()->setUpdatesEnabled(false);
+
+       d->resetFilter();
 
-       QPixmap pixmap(32, 20);
-       QColor col(color);
-       pixmap.fill(col);
-       QPainter painter(&pixmap);
-       QRect pixmapRect = QRect(0, 0, 31, 19);
-       painter.drawPixmap(pixmapRect.x(), pixmapRect.y(), pixmap);
-       painter.drawRect(pixmapRect);
+       // call QComboBox::showPopup. But set the inShowPopup_ flag to switch on
+       // the hack in the item delegate to make space for the headers.
+       LASSERT(!d->inShowPopup_, /**/);
+       d->inShowPopup_ = true;
+       QComboBox::showPopup();
+       d->inShowPopup_ = false;
 
-       setItemIcon(i, QIcon(pixmap));
+       view()->setUpdatesEnabled(enabled);
 }
 
 
-void ColorsCombo::addItemSort(QString const & item, QString const & guiname,
-                             QString const & category, QString const color)
+bool ColorsCombo::eventFilter(QObject * o, QEvent * e)
 {
-       QString titem = (item != default_color_)
-                       ? guiname
-                       : toqstr(bformat(_("%1$s (= Default[[color]])"),
-                                        qstring_to_ucs4(guiname)));
+       if (e->type() != QEvent::KeyPress)
+               return QComboBox::eventFilter(o, e);
 
-       QList<QStandardItem *> row;
-       QStandardItem * gui = new QStandardItem(titem);
-       row.append(gui);
-       row.append(new QStandardItem(item));
-       row.append(new QStandardItem(category));
-       row.append(new QStandardItem(true));
-       if (!color.isEmpty())
-               row.append(new QStandardItem(color));
+       QKeyEvent * ke = static_cast<QKeyEvent*>(e);
+       bool modified = (ke->modifiers() == Qt::ControlModifier)
+               || (ke->modifiers() == Qt::AltModifier)
+               || (ke->modifiers() == Qt::MetaModifier);
 
-       // the first entry is easy
-       int const end = model()->rowCount();
-       if (end == 0) {
-               model()->appendRow(row);
-               setColorIcon(0, color);
-               return;
+       switch (ke->key()) {
+       case Qt::Key_Escape:
+               if (!modified && !d->filter_.isEmpty()) {
+                       d->resetFilter();
+                       setCurrentIndex(lastCurrentIndex_);
+                       return true;
+               }
+               break;
+       case Qt::Key_Backspace:
+               if (!modified) {
+                       // cut off one character
+                       d->setFilter(d->filter_.left(d->filter_.length() - 1));
+               }
+               break;
+       default:
+               if (modified || ke->text().isEmpty())
+                       break;
+               // find chars for the filter string
+               QString s;
+               for (int i = 0; i < ke->text().length(); ++i) {
+                       QChar c = ke->text()[i];
+                       if (c.isLetterOrNumber()
+                           || c.isSymbol()
+                           || c.isPunct()
+                           || c.category() == QChar::Separator_Space) {
+                               s += c;
+                       }
+               }
+               if (!s.isEmpty()) {
+                       // append new chars to the filter string
+                       d->setFilter(d->filter_ + s);
+                       return true;
+               }
+               break;
+       }
+
+       return QComboBox::eventFilter(o, e);
+}
+
+
+bool ColorsCombo::set(QString const & item_in, bool const report_missing)
+{
+       d->resetFilter();
+
+       int const curItem = currentIndex();
+       QModelIndex const mindex =
+               d->filterModel_->mapToSource(d->filterModel_->index(curItem, 
1));
+       QString const & currentItem = d->model_->itemFromIndex(mindex)->text();
+       QString const item = (item_in == default_value_) ? "default" : item_in;
+       last_item_ = item;
+       if (item == currentItem) {
+               LYXERR(Debug::GUI, "Already had " << item << " selected.");
+               return true;
        }
 
-       // find category
-       int i = 0;
-       // sort categories alphabetically
-       while (i < end && model()->item(i, 2)->text() != category
-              && model()->item(i, 2)->text() != qt_("Uncategorized"))
-               ++i;
+       QList<QStandardItem *> r = d->model_->findItems(item, Qt::MatchExactly, 
1);
+       if (r.empty()) {
+               if (report_missing)
+                       LYXERR0("Trying to select non existent entry " << item);
+               return false;
+       }
+
+       
setCurrentIndex(d->filterModel_->mapFromSource(r.first()->index()).row());
+       return true;
+}
+
+
+QString ColorsCombo::getData(int row) const
+{
+       int srow = d->filterModel_->mapToSource(d->filterModel_->index(row, 
1)).row();
+       QString const result = d->model_->data(d->model_->index(srow, 1), 
Qt::DisplayRole).toString();
+       return (result == "default") ? default_value_ : result;
+}
 
-       // jump to the end of the category group to sort in order
-       // specified in input
-       while (i < end && model()->item(i, 2)->text() == category)
-               ++i;
 
-       model()->insertRow(i, row);
-       setColorIcon(i, color);
+void ColorsCombo::resetFilter()
+{
+       d->resetFilter();
 }
 
 
-void ColorsCombo::setCustomColors(std::map<std::string, std::string> const & 
custom_colors)
+void ColorsCombo::setLeftMargin(int const i)
 {
-       custom_colors_ = custom_colors;
-       fillComboColor();
+       left_margin_ = i;
 }
 
 
-void ColorsCombo::fillComboColor()
+void ColorsCombo::setDefaultColor(std::string const & col)
+{
+       default_color_ = toqstr(col);
+       need_default_color_ = !col.empty();
+}
+
+
+void ColorsCombo::updateCombo()
+{
+       d->countCategories();
+
+       // needed to recalculate size hint
+       hide();
+       setMinimumWidth(sizeHint().width());
+       show();
+}
+
+
+QString const & ColorsCombo::filter() const
+{
+       return d->filter_;
+}
+
+
+QStandardItemModel * ColorsCombo::model()
+{
+       return d->model_;
+}
+
+
+void ColorsCombo::modelChanged()
+{
+       // restore last setting
+       set(last_item_, false);
+}
+
+
+bool ColorsCombo::isDefaultColor(QString const & guiname)
 {
-       reset();
-       // at first add the general values as required
-       if (has_ignore_)
-               addItemSort(QString("ignore"), qt_("No change"),
-                           QString());
        if (default_color_.isEmpty())
-               addItemSort(default_value_, qt_("Default"),
-                           QString());
-       if (has_none_ && default_value_ != "none")
-               addItemSort("none", qt_("None[[color]]"),
-                           QString());
-       if (has_inherit_)
-               addItemSort(QString("inherit"), qt_("(Without)[[color]]"),
-                           QString());
-       // now custom colors
-       for (auto const & lc : custom_colors_) {
-               QString const lyxname = toqstr(lc.first);
-               QString const guiname = toqstr(lc.first);
-               QString const category = qt_("Custom Colors");
-               QString const color = toqstr(lc.second);
-               addItemSort(lyxname,
-                           guiname,
-                           category,
-                           color);
-       }
-       // then real colors
-       for (auto const & lc : theLaTeXColors().getLaTeXColors()) {
-               QString const lyxname = toqstr(lc.first);
-               QString const guiname = 
toqstr(translateIfPossible(lc.second.guiname()));
-               QString const category = 
toqstr(translateIfPossible(lc.second.category()));
-               QString const color = toqstr(lc.second.hexname());
-               addItemSort(lyxname,
-                           guiname,
-                           category,
-                           color);
-       }
+               return false;
+       QList<QStandardItem *> cis = d->model_->findItems(guiname, 
Qt::MatchExactly, 0);
+       bool const res = d->model_->data(d->model_->index(cis.front()->row(), 
1),
+                                        Qt::DisplayRole).toString() == 
default_color_;
+       if (res)
+               // no need to look further
+               need_default_color_ = false;
+       return res;
 }
 
 
diff --git a/src/frontends/qt/ColorsCombo.h b/src/frontends/qt/ColorsCombo.h
index 71f8f143c7..1c3bd032d6 100644
--- a/src/frontends/qt/ColorsCombo.h
+++ b/src/frontends/qt/ColorsCombo.h
@@ -12,34 +12,35 @@
 #ifndef LYX_COLORSCOMBO_H
 #define LYX_COLORSCOMBO_H
 
-#include "CategorizedCombo.h"
 #include "support/qstring_helpers.h"
 
 #include <QComboBox>
+#include <QStandardItemModel>
 
 
 namespace lyx {
 namespace frontend {
 
-class CCItemDelegate;
+class ColItemDelegate;
 
 /**
  * A combo box with categorization
  */
-class ColorsCombo : public CategorizedCombo
+class ColorsCombo : public QComboBox
 {
        Q_OBJECT
 public:
        ColorsCombo(QWidget * parent);
        ~ColorsCombo();
 
+       /// select an item in the combobox. Returns false if item does not exist
+       bool set(QString const & cc, bool const report_missing = true);
+       /// Reset the combobox filter.
+       void resetFilter();
        /// Update combobox.
        void updateCombo();
-       /// Add item to combo with optional color icon
-       void addItemSort(QString const & item, QString const & guiname,
-                        QString const & category, QString color = QString());
-       /// Set BufferParams to access custom colors
-       void setCustomColors(std::map<std::string, std::string> const & 
custom_colors);
+       ///
+       QString getData(int row) const;
        /// Add "ignore" color entry?
        void hasIgnore(bool const b) { has_ignore_ = b; }
        /// Add "inherit" color entry?
@@ -47,17 +48,28 @@ public:
        /// Add dedicated "none" color entry if default_value_ != "none"
        void hasNone(bool const b) { has_none_ = b; }
        /// Flag a color as default. This will also omit the "none" entry
-       void setDefaultColor(std::string const & col) { default_color_ = 
toqstr(col); }
+       void setDefaultColor(std::string const & col);
        /// Set the value of the default entry. Preset is "none"
        void setDefaultValue(std::string const & val) { default_value_ = 
toqstr(val); }
+       ///
+       bool isDefaultColor(QString const & guiname);
+       ///
+       void showPopup() override;
 
-private:
        ///
-       void setColorIcon(int const i, QString const color);
+       bool eventFilter(QObject * o, QEvent * e) override;
        ///
-       void fillComboColor();
+       QString const & filter() const;
        ///
-       std::map<std::string, std::string> custom_colors_;
+       QStandardItemModel * model();
+       ///
+       void setLeftMargin(int const);
+
+private Q_SLOTS:
+       ///
+       void modelChanged();
+
+private:
        ///
        bool has_ignore_;
        ///
@@ -68,6 +80,20 @@ private:
        QString default_color_;
        ///
        QString default_value_;
+       ///
+       bool need_default_color_;
+       ///
+       friend class ColItemDelegate;
+       ///
+       struct Private;
+       ///
+       Private * const d;
+       ///
+       int lastCurrentIndex_;
+       ///
+       int left_margin_ = -1;
+       ///
+       QString last_item_;
 };
 
 
diff --git a/src/frontends/qt/GuiBox.cpp b/src/frontends/qt/GuiBox.cpp
index b0134c8719..c8c932ed54 100644
--- a/src/frontends/qt/GuiBox.cpp
+++ b/src/frontends/qt/GuiBox.cpp
@@ -223,9 +223,6 @@ void GuiBox::paramsToDialog(Inset const * inset)
 {
        InsetBox const * box = static_cast<InsetBox const *>(inset);
        InsetBoxParams const & params = box->params();
-       custom_colors_cache_ = inset->buffer().masterParams().custom_colors;
-       frameColorCO->setCustomColors(custom_colors_cache_);
-       backgroundColorCO->setCustomColors(custom_colors_cache_);
        QString type = toqstr(params.type);
        if (type == "Framed") {
                pagebreakCB->setChecked(true);
diff --git a/src/frontends/qt/GuiBox.h b/src/frontends/qt/GuiBox.h
index a63d0cae7e..fa4b908502 100644
--- a/src/frontends/qt/GuiBox.h
+++ b/src/frontends/qt/GuiBox.h
@@ -13,7 +13,6 @@
 #ifndef GUIBOX_H
 #define GUIBOX_H
 
-#include "ColorCode.h"
 #include "InsetParamsWidget.h"
 #include "ui_BoxUi.h"
 
@@ -58,8 +57,6 @@ private:
        QStringList ids_spec_;
        ///
        QStringList gui_names_spec_;
-       ///
-       std::map<std::string, std::string> custom_colors_cache_;
 };
 
 } // namespace frontend
diff --git a/src/frontends/qt/GuiCharacter.cpp 
b/src/frontends/qt/GuiCharacter.cpp
index aa6b3a1d25..bdc180d597 100644
--- a/src/frontends/qt/GuiCharacter.cpp
+++ b/src/frontends/qt/GuiCharacter.cpp
@@ -201,8 +201,6 @@ GuiCharacter::GuiCharacter(GuiView & lv)
        fillCombo(langCO, language);
        colorCO->hasIgnore(true);
        colorCO->hasInherit(true);
-       colorCO->setCustomColors(buffer().masterParams().custom_colors);
-       custom_colors_cache_ = buffer().masterParams().custom_colors;
 
        bc().setPolicy(ButtonPolicy::OkApplyCancelAutoReadOnlyPolicy);
        bc().setOK(buttonBox->button(QDialogButtonBox::Ok));
@@ -485,12 +483,6 @@ void GuiCharacter::updateContents()
        if (font_.language() == buffer().params().language)
                font_.setLanguage(reset_language);
 
-       // Update custom colors if needed
-       if (custom_colors_cache_ != buffer().masterParams().custom_colors) {
-               colorCO->setCustomColors(buffer().masterParams().custom_colors);
-               custom_colors_cache_ = buffer().masterParams().custom_colors;
-       }
-
        paramsToDialog(font_);
 
        checkRestoreDefaults();
diff --git a/src/frontends/qt/GuiCharacter.h b/src/frontends/qt/GuiCharacter.h
index e968f7f381..d3345c210f 100644
--- a/src/frontends/qt/GuiCharacter.h
+++ b/src/frontends/qt/GuiCharacter.h
@@ -109,8 +109,6 @@ private:
        bool noun_;
        ///
        bool nospellcheck_;
-       ///
-       std::map<std::string, std::string> custom_colors_cache_;
 
        ///
        QAction * resetdefault_ = new QAction(this);
diff --git a/src/frontends/qt/GuiDocument.cpp b/src/frontends/qt/GuiDocument.cpp
index 30f692804c..e129637447 100644
--- a/src/frontends/qt/GuiDocument.cpp
+++ b/src/frontends/qt/GuiDocument.cpp
@@ -1377,14 +1377,7 @@ GuiDocument::GuiDocument(GuiView & lv)
                this, SLOT(alterCustomColor()));
        connect(colorModule->customColorsTW, 
SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)),
                this, SLOT(toggleCustomColor(QTreeWidgetItem *, int)));
-       
colorModule->textColorCO->setCustomColors(buffer().masterParams().custom_colors);
-       
colorModule->pageColorCO->setCustomColors(buffer().masterParams().custom_colors);
-       
colorModule->noteColorCO->setCustomColors(buffer().masterParams().custom_colors);
-       
colorModule->shadedColorCO->setCustomColors(buffer().masterParams().custom_colors);
-       
colorModule->oddRowColorCO->setCustomColors(buffer().masterParams().custom_colors);
-       
colorModule->evenRowColorCO->setCustomColors(buffer().masterParams().custom_colors);
-       
colorModule->borderColorCO->setCustomColors(buffer().masterParams().custom_colors);
-       colorModule->noteColorCO->setDefaultColor("lightgrey");
+       colorModule->noteColorCO->setDefaultColor("lightgray");
        colorModule->shadedColorCO->setDefaultColor("red");
        colorModule->oddRowColorCO->setDefaultValue("default");
        colorModule->evenRowColorCO->setDefaultValue("default");
@@ -5336,6 +5329,10 @@ void GuiDocument::dispatchParams()
                                table->tabular.updateIndexes();
                }
        }
+
+       // and the custom colors
+       lyxview().updateColorsModel();
+       
        // FIXME LFUN
        // If we used an LFUN, we would not need these two lines:
        BufferView * bv = const_cast<BufferView *>(bufferview());
@@ -5595,14 +5592,6 @@ void GuiDocument::updateCustomColors()
        }
        colorModule->customColorsTW->resizeColumnToContents(0);
 
-       colorModule->textColorCO->setCustomColors(ccs);
-       colorModule->noteColorCO->setCustomColors(ccs);
-       colorModule->pageColorCO->setCustomColors(ccs);
-       colorModule->shadedColorCO->setCustomColors(ccs);
-       colorModule->oddRowColorCO->setCustomColors(ccs);
-       colorModule->evenRowColorCO->setCustomColors(ccs);
-       colorModule->borderColorCO->setCustomColors(ccs);
-
        change_adaptor();
 }
 
diff --git a/src/frontends/qt/GuiTabular.cpp b/src/frontends/qt/GuiTabular.cpp
index ca3b7ca54c..2905fa6ac1 100644
--- a/src/frontends/qt/GuiTabular.cpp
+++ b/src/frontends/qt/GuiTabular.cpp
@@ -913,14 +913,6 @@ void GuiTabular::paramsToDialog(Inset const * inset)
        // Copy Tabular of current inset.
        Tabular const & tabular = itab->tabular;
 
-       custom_colors_cache_ = itab->buffer().masterParams().custom_colors;
-       borderColorCO->setCustomColors(custom_colors_cache_);
-       cellColorCO->setCustomColors(custom_colors_cache_);
-       columnColorCO->setCustomColors(custom_colors_cache_);
-       rowColorCO->setCustomColors(custom_colors_cache_);
-       evenRowColorCO->setCustomColors(custom_colors_cache_);
-       oddRowColorCO->setCustomColors(custom_colors_cache_);
-
        BufferView const * bv = guiApp->currentView()->currentBufferView();
        size_t const cell = bv->cursor().idx();
 
diff --git a/src/frontends/qt/GuiTabular.h b/src/frontends/qt/GuiTabular.h
index 733e0edce8..631365e4df 100644
--- a/src/frontends/qt/GuiTabular.h
+++ b/src/frontends/qt/GuiTabular.h
@@ -82,8 +82,6 @@ private:
        docstring decimal_sep_;
        ///
        std::set<std::string> features_;
-       ///
-       std::map<std::string, std::string> custom_colors_cache_;
 };
 
 } // namespace frontend
diff --git a/src/frontends/qt/GuiView.cpp b/src/frontends/qt/GuiView.cpp
index d95ce16f56..6f12a0fed5 100644
--- a/src/frontends/qt/GuiView.cpp
+++ b/src/frontends/qt/GuiView.cpp
@@ -108,6 +108,7 @@
 #include <QShowEvent>
 #include <QSlider>
 #include <QSplitter>
+#include <QStandardItemModel>
 #include <QStackedWidget>
 #include <QStatusBar>
 #include <QSvgRenderer>
@@ -569,7 +570,8 @@ GuiView::GuiView(int id)
        : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
          command_execute_(false), minibuffer_focus_(false), 
word_count_enabled_(true),
          char_count_enabled_(true),  char_nb_count_enabled_(false),
-         toolbarsMovable_(false), devel_mode_(false)
+         toolbarsMovable_(false), devel_mode_(false),
+         colors_model_(new QStandardItemModel(0, 4, this))
 {
        connect(this, SIGNAL(bufferViewChanged()),
                this, SLOT(onBufferViewChanged()));
@@ -1211,6 +1213,117 @@ TocModels & GuiView::tocModels()
 }
 
 
+void GuiView::addColorItem(QString const & item, QString const & guiname,
+                          QString const & category, QString const color,
+                          bool const custom, int const r) const
+{
+       QList<QStandardItem *> row;
+       QStandardItem * gui = new QStandardItem(guiname);
+       // add color icon if applicable
+       if (!color.isEmpty()) {
+               QPixmap pixmap(32, 20);
+               QColor col(color);
+               pixmap.fill(col);
+               QPainter painter(&pixmap);
+               QRect pixmapRect = QRect(0, 0, 31, 19);
+               painter.drawPixmap(pixmapRect.x(), pixmapRect.y(), pixmap);
+               painter.drawRect(pixmapRect);
+               gui->setIcon(QIcon(pixmap));
+       }
+       row.append(gui);
+       row.append(new QStandardItem(item));
+       row.append(new QStandardItem(category));
+       row.append(new QStandardItem(custom ? QString("custom") : QString()));
+       if (!color.isEmpty())
+               row.append(new QStandardItem(color));
+
+       // specified row
+       if (r != -1) {
+               colors_model_->insertRow(r, row);
+               return;
+       }
+
+       int const end = colors_model_->rowCount();
+       // first entry
+       if (end == 0) {
+               colors_model_->appendRow(row);
+               return;
+       }
+
+       // find category
+       int i = 0;
+       // sort categories alphabetically
+       while (i < end && colors_model_->item(i, 2)->text() != category)
+               ++i;
+
+       // jump to the end of the category group to sort in order
+       // specified in input
+       while (i < end && colors_model_->item(i, 2)->text() == category)
+               ++i;
+
+       colors_model_->insertRow(i, row);
+}
+
+
+QStandardItemModel * GuiView::viewColorsModel()
+{
+       if (colors_model_->rowCount() > 0)
+               return colors_model_;
+
+       // at first add the general values as required
+       addColorItem("ignore", qt_("No change"));
+       addColorItem("default", qt_("Default"));
+       addColorItem("none", qt_("None[[color]]"));
+       addColorItem("inherit", qt_("(Without)[[color]]"));
+       // then custom colors
+       if (currentBufferView()) {
+               for (auto const & lc : 
currentBufferView()->buffer().masterParams().custom_colors) {
+                       addColorItem(toqstr(lc.first),
+                                    toqstr(lc.first),
+                                    qt_("Custom Colors"),
+                                    toqstr(lc.second),
+                                    true);
+               }
+       }
+       // finally the latex colors
+       for (auto const & lc : theLaTeXColors().getLaTeXColors()) {
+               addColorItem(toqstr(lc.first),
+                            toqstr(translateIfPossible(lc.second.guiname())),
+                            toqstr(translateIfPossible(lc.second.category())),
+                            toqstr(lc.second.hexname()));
+       }
+
+       return colors_model_;
+}
+
+
+void GuiView::updateColorsModel() const
+{
+       bool changed = false;
+       // remove custom colors
+       QList<QStandardItem *> cis = colors_model_->findItems("custom", 
Qt::MatchExactly, 3);
+       for (qsizetype i = 0; i < cis.size(); ++i) {
+               colors_model_->removeRow(cis.at(i)->row());
+               changed = true;
+       }
+       // and add anew
+       if (currentBufferView()) {
+               int r = 4;
+               for (auto const & lc : 
currentBufferView()->buffer().masterParams().custom_colors) {
+                       addColorItem(toqstr(lc.first),
+                                    toqstr(lc.first),
+                                    qt_("Custom Colors"),
+                                    toqstr(lc.second),
+                                    true, r);
+                       ++r;
+                       changed = true;
+               }
+       }
+       if (changed)
+               Q_EMIT colorsModelChanged();
+}
+
+
 void GuiView::setFocus()
 {
        LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
@@ -1600,6 +1713,8 @@ void GuiView::onBufferViewChanged()
        zoom_out_->setEnabled(currentBufferView()
                              && zoom_slider_->value() > 
zoom_slider_->minimum());
        d.stats_update_trigger_ = true;
+       if (!closing_)
+               updateColorsModel();
 }
 
 
diff --git a/src/frontends/qt/GuiView.h b/src/frontends/qt/GuiView.h
index a202279b20..4fd713308b 100644
--- a/src/frontends/qt/GuiView.h
+++ b/src/frontends/qt/GuiView.h
@@ -22,6 +22,7 @@
 #include <QMenu>
 #include <QSvgWidget>
 
+class QStandardItemModel;
 class QCloseEvent;
 class QDragEnterEvent;
 class QDropEvent;
@@ -190,6 +191,16 @@ public:
        ///
        TocModels & tocModels();
 
+       ///
+       void addColorItem(QString const & item, QString const & guiname,
+                         QString const & category = QString(),
+                         QString const color = QString(),
+                         bool custom = false, int row = -1) const;
+       ///
+       QStandardItemModel * viewColorsModel();
+       ///
+       void updateColorsModel() const;
+
        /// called on timeout
        void autoSave();
 
@@ -245,6 +256,8 @@ Q_SIGNALS:
        void changeTrackingToggled(bool);
        ///
        void dockWidgetVisibilityChanged();
+       ///
+       void colorsModelChanged() const;
 
 public Q_SLOTS:
        ///
@@ -575,6 +588,9 @@ private:
 
        // initial zoom for pinch gesture
        int initialZoom_;
+
+       ///
+       QStandardItemModel * colors_model_;
 };
 
 
-- 
lyx-cvs mailing list
[email protected]
https://lists.lyx.org/mailman/listinfo/lyx-cvs

Reply via email to