File indexing completed on 2024-12-01 04:37:50

0001 /***************************************************************************
0002  *   SPDX-License-Identifier: GPL-2.0-or-later
0003  *                                                                         *
0004  *   SPDX-FileCopyrightText: 2004-2022 Thomas Fischer <fischer@unix-ag.uni-kl.de>
0005  *                                                                         *
0006  *   This program is free software; you can redistribute it and/or modify  *
0007  *   it under the terms of the GNU General Public License as published by  *
0008  *   the Free Software Foundation; either version 2 of the License, or     *
0009  *   (at your option) any later version.                                   *
0010  *                                                                         *
0011  *   This program is distributed in the hope that it will be useful,       *
0012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0014  *   GNU General Public License for more details.                          *
0015  *                                                                         *
0016  *   You should have received a copy of the GNU General Public License     *
0017  *   along with this program; if not, see <https://www.gnu.org/licenses/>. *
0018  ***************************************************************************/
0019 
0020 #include "colorlabelwidget.h"
0021 
0022 #include <QAbstractItemModel>
0023 #include <QFontMetrics>
0024 #include <QPainter>
0025 #include <QColorDialog>
0026 #include <QSignalBlocker>
0027 
0028 #include <KLocalizedString>
0029 
0030 #include <NotificationHub>
0031 #include <Preferences>
0032 
0033 class ColorLabelComboBoxModel : public QAbstractItemModel
0034 {
0035     Q_OBJECT
0036 
0037 public:
0038     static const QColor NoColor;
0039 
0040     enum ColorLabelComboBoxModelRole {
0041         /// Color of a color-label pair
0042         ColorRole = Qt::UserRole + 1721
0043     };
0044 
0045     struct ColorLabelPair {
0046         QColor color;
0047         QString label;
0048     };
0049 
0050     QColor userColor;
0051 
0052     ColorLabelComboBoxModel(QObject *p = nullptr)
0053             : QAbstractItemModel(p), userColor(NoColor) {
0054         /// nothing
0055     }
0056 
0057     QModelIndex index(int row, int column, const QModelIndex &parent) const override {
0058         return parent == QModelIndex() ? createIndex(row, column) : QModelIndex();
0059     }
0060 
0061     QModelIndex parent(const QModelIndex & = QModelIndex()) const override {
0062         return QModelIndex();
0063     }
0064 
0065     int rowCount(const QModelIndex &parent = QModelIndex()) const override {
0066         return parent == QModelIndex() ? 2 + Preferences::instance().colorCodes().count() : 0;
0067     }
0068 
0069     int columnCount(const QModelIndex &parent = QModelIndex()) const override {
0070         return parent == QModelIndex() ? 1 : 0;
0071     }
0072 
0073     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
0074         if (role == ColorRole) {
0075             const int cci = index.row() - 1;
0076             if (index.row() == 0 || cci < 0 || cci >= Preferences::instance().colorCodes().count())
0077                 return NoColor;
0078             else if (index.row() == rowCount() - 1)
0079                 return userColor;
0080             else
0081                 return QColor(Preferences::instance().colorCodes().at(cci).first);
0082         } else if (role == Qt::FontRole && (index.row() == 0 || index.row() == rowCount() - 1)) {
0083             /// Set first item's text ("No color") and last item's text ("User-defined color") in italics
0084             QFont font;
0085             font.setItalic(true);
0086             return font;
0087         } else if (role == Qt::DecorationRole && index.row() > 0 && (index.row() < rowCount() - 1 || userColor != NoColor)) {
0088             /// For items that have a color to choose, draw a little square in this chosen color
0089             QColor color = data(index, ColorRole).value<QColor>();
0090             return ColorLabelWidget::createSolidIcon(color);
0091         } else if (role == Qt::DisplayRole)
0092             if (index.row() == 0)
0093                 return i18n("No color");
0094             else if (index.row() == rowCount() - 1)
0095                 return i18n("User-defined color");
0096             else
0097                 return Preferences::instance().colorCodes().at(index.row() - 1).second;
0098         else
0099             return QVariant();
0100     }
0101 
0102     QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override {
0103         if (section != 0 || orientation != Qt::Horizontal || role != Qt::DisplayRole)
0104             return QVariant();
0105 
0106         return i18n("Color & Label");
0107     }
0108 
0109     void setColor(const QColor &newColor) {
0110         userColor = newColor;
0111         const QModelIndex idx = index(rowCount() - 1, 0, QModelIndex());
0112         Q_EMIT dataChanged(idx, idx);
0113     }
0114 
0115     void reset() {
0116         beginResetModel();
0117         endResetModel();
0118     }
0119 };
0120 
0121 const QColor ColorLabelComboBoxModel::NoColor = Qt::black;
0122 
0123 
0124 class ColorLabelWidget::ColorLabelWidgetPrivate : private NotificationListener
0125 {
0126 private:
0127     ColorLabelWidget *parent;
0128 
0129 public:
0130     ColorLabelComboBoxModel *model;
0131 
0132     ColorLabelWidgetPrivate(ColorLabelWidget *_parent, ColorLabelComboBoxModel *m)
0133             : parent(_parent), model(m)
0134     {
0135         Q_UNUSED(parent)
0136         NotificationHub::registerNotificationListener(this, NotificationHub::EventConfigurationChanged);
0137     }
0138 
0139     void notificationEvent(int eventId) override {
0140         if (eventId == NotificationHub::EventConfigurationChanged) {
0141             /// Avoid triggering signal when current index is set by the program
0142             const QSignalBlocker blocker(parent);
0143 
0144             const QColor currentColor = parent->currentColor();
0145             model->reset();
0146             model->userColor = ColorLabelComboBoxModel::NoColor;
0147             selectColor(currentColor);
0148         }
0149     }
0150 
0151     int selectColor(const QString &color)
0152     {
0153         return selectColor(QColor(color));
0154     }
0155 
0156     int selectColor(const QColor &color)
0157     {
0158         int rowIndex = 0;
0159         if (color != ColorLabelComboBoxModel::NoColor) {
0160             /// Find row that matches given color
0161             for (rowIndex = 0; rowIndex < model->rowCount(); ++rowIndex)
0162                 if (model->data(model->index(rowIndex, 0, QModelIndex()), ColorLabelComboBoxModel::ColorRole).value<QColor>() == color)
0163                     break;
0164 
0165             if (rowIndex >= model->rowCount()) {
0166                 /// Color was not in the list of known colors, so set user color to given color
0167                 model->userColor = color;
0168                 rowIndex = model->rowCount() - 1;
0169             }
0170         }
0171         return rowIndex;
0172     }
0173 };
0174 
0175 ColorLabelWidget::ColorLabelWidget(QWidget *parent)
0176         : QComboBox(parent), d(new ColorLabelWidgetPrivate(this, new ColorLabelComboBoxModel(this)))
0177 {
0178     setModel(d->model);
0179     connect(this, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ColorLabelWidget::slotCurrentIndexChanged);
0180 }
0181 
0182 ColorLabelWidget::~ColorLabelWidget()
0183 {
0184     delete d;
0185 }
0186 
0187 void ColorLabelWidget::clear()
0188 {
0189     /// Avoid triggering signal when current index is set by the program
0190     const QSignalBlocker blocker(this);
0191 
0192     d->model->userColor = ColorLabelComboBoxModel::NoColor;
0193     setCurrentIndex(0); ///< index 0 should be "no color"
0194 }
0195 
0196 QColor ColorLabelWidget::currentColor() const
0197 {
0198     return d->model->data(d->model->index(currentIndex(), 0, QModelIndex()), ColorLabelComboBoxModel::ColorRole).value<QColor>();
0199 }
0200 
0201 bool ColorLabelWidget::reset(const Value &value)
0202 {
0203     /// Avoid triggering signal when current index is set by the program
0204     const QSignalBlocker blocker(this);
0205 
0206     QSharedPointer<VerbatimText> verbatimText;
0207     int rowIndex = 0;
0208     if (value.count() == 1 && !(verbatimText = value.first().dynamicCast<VerbatimText>()).isNull()) {
0209         /// Create QColor instance based on given textual representation
0210         rowIndex = d->selectColor(verbatimText->text());
0211     }
0212     setCurrentIndex(rowIndex);
0213 
0214     return true;
0215 }
0216 
0217 bool ColorLabelWidget::apply(Value &value) const
0218 {
0219     const QColor color = currentColor();
0220     value.clear();
0221     if (color != ColorLabelComboBoxModel::NoColor)
0222         value.append(QSharedPointer<VerbatimText>(new VerbatimText(color.name())));
0223     return true;
0224 }
0225 
0226 bool ColorLabelWidget::validate(QWidget **, QString &) const
0227 {
0228     return true;
0229 }
0230 
0231 void ColorLabelWidget::setReadOnly(bool isReadOnly)
0232 {
0233     setEnabled(!isReadOnly);
0234 }
0235 
0236 void ColorLabelWidget::slotCurrentIndexChanged(int index)
0237 {
0238     if (index == count() - 1) {
0239         const QColor initialColor = d->model->userColor;
0240         const QColor newColor = QColorDialog::getColor(initialColor, this);
0241         if (newColor.isValid())
0242             d->model->setColor(newColor);
0243     }
0244 
0245     Q_EMIT modified();
0246 }
0247 
0248 QPixmap ColorLabelWidget::createSolidIcon(const QColor &color)
0249 {
0250     QFontMetrics fm = QFontMetrics(QFont());
0251     int h = fm.height() - 4;
0252     QPixmap pm(h, h);
0253     QPainter painter(&pm);
0254     painter.setPen(color);
0255     painter.setBrush(QBrush(color));
0256     painter.drawRect(0, 0, h, h);
0257     return pm;
0258 }
0259 
0260 #include "colorlabelwidget.moc"