1 /***************************************************************************
2  *   Copyright (C) 2004-2018 by Thomas Fischer <fischer@unix-ag.uni-kl.de> *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, see <https://www.gnu.org/licenses/>. *
16  ***************************************************************************/
17 
18 #include "colorlabelwidget.h"
19 
20 #include <QAbstractItemModel>
21 #include <QFontMetrics>
22 #include <QPainter>
23 #include <QColorDialog>
24 
25 #include <KLocalizedString>
26 #include <KSharedConfig>
27 #include <KConfigGroup>
28 
29 #include "notificationhub.h"
30 #include "preferences.h"
31 
32 class ColorLabelComboBoxModel : public QAbstractItemModel, private NotificationListener
33 {
34     Q_OBJECT
35 
36 public:
37     enum ColorLabelComboBoxModelRole {
38         /// Color of a color-label pair
39         ColorRole = Qt::UserRole + 1721
40     };
41 
42     struct ColorLabelPair {
43         QColor color;
44         QString label;
45     };
46 
47     QList<ColorLabelPair> colorLabelPairs;
48     QColor userColor;
49 
50     KSharedConfigPtr config;
51 
ColorLabelComboBoxModel(QObject * p=nullptr)52     ColorLabelComboBoxModel(QObject *p = nullptr)
53             : QAbstractItemModel(p), userColor(Qt::black), config(KSharedConfig::openConfig(QStringLiteral("kbibtexrc"))) {
54         readConfiguration();
55         NotificationHub::registerNotificationListener(this, NotificationHub::EventConfigurationChanged);
56     }
57 
notificationEvent(int eventId)58     void notificationEvent(int eventId) override {
59         if (eventId == NotificationHub::EventConfigurationChanged)
60             readConfiguration();
61     }
62 
readConfiguration()63     void readConfiguration() {
64         KConfigGroup configGroup(config, Preferences::groupColor);
65         QStringList colorCodes = configGroup.readEntry(Preferences::keyColorCodes, Preferences::defaultColorCodes);
66         QStringList colorLabels = configGroup.readEntry(Preferences::keyColorLabels, Preferences::defaultColorLabels);
67 
68         colorLabelPairs.clear();
69         for (QStringList::ConstIterator itc = colorCodes.constBegin(), itl = colorLabels.constBegin(); itc != colorCodes.constEnd() && itl != colorLabels.constEnd(); ++itc, ++itl) {
70             ColorLabelPair clp;
71             clp.color = QColor(*itc);
72             clp.label = i18n((*itl).toUtf8().constData());
73             colorLabelPairs << clp;
74         }
75     }
76 
index(int row,int column,const QModelIndex &) const77     QModelIndex index(int row, int column, const QModelIndex &) const override {
78         return createIndex(row, column);
79     }
80 
parent(const QModelIndex &=QModelIndex ()) const81     QModelIndex parent(const QModelIndex & = QModelIndex()) const override {
82         return QModelIndex();
83     }
84 
rowCount(const QModelIndex & parent=QModelIndex ()) const85     int rowCount(const QModelIndex &parent = QModelIndex()) const override {
86         return parent == QModelIndex() ? 2 + colorLabelPairs.count() : 0;
87     }
88 
columnCount(const QModelIndex &=QModelIndex ()) const89     int columnCount(const QModelIndex & = QModelIndex()) const override {
90         return 1;
91     }
92 
data(const QModelIndex & index,int role=Qt::DisplayRole) const93     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
94         if (role == ColorRole) {
95             if (index.row() == 0)
96                 return QColor(Qt::black);
97             else if (index.row() == rowCount() - 1)
98                 return userColor;
99             else
100                 return colorLabelPairs[index.row() - 1].color;
101         } else if (role == Qt::FontRole && (index.row() == 0 || index.row() == rowCount() - 1)) {
102             QFont font;
103             font.setItalic(true);
104             return font;
105         } else if (role == Qt::DecorationRole && index.row() > 0 && (index.row() < rowCount() - 1 || userColor != Qt::black)) {
106             QColor color = data(index, ColorRole).value<QColor>();
107             return ColorLabelWidget::createSolidIcon(color);
108         } else if (role == Qt::DisplayRole)
109             if (index.row() == 0)
110                 return i18n("No color");
111             else if (index.row() == rowCount() - 1)
112                 return i18n("User-defined color");
113             else
114                 return colorLabelPairs[index.row() - 1].label;
115         else
116             return QVariant();
117     }
118 
headerData(int section,Qt::Orientation orientation,int role=Qt::DisplayRole) const119     QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override {
120         if (section != 0 || orientation != Qt::Horizontal || role != Qt::DisplayRole)
121             return QVariant();
122 
123         return i18n("Color & Label");
124     }
125 
setColor(const QColor & newColor)126     void setColor(const QColor &newColor) {
127         userColor = newColor;
128         const QModelIndex idx = index(rowCount() - 1, 0, QModelIndex());
129         emit dataChanged(idx, idx);
130     }
131 };
132 
133 class ColorLabelWidget::ColorLabelWidgetPrivate
134 {
135 public:
136     ColorLabelComboBoxModel *model;
137 
ColorLabelWidgetPrivate(ColorLabelWidget * parent,ColorLabelComboBoxModel * m)138     ColorLabelWidgetPrivate(ColorLabelWidget *parent, ColorLabelComboBoxModel *m)
139             : model(m)
140     {
141         Q_UNUSED(parent)
142     }
143 };
144 
ColorLabelWidget(QWidget * parent)145 ColorLabelWidget::ColorLabelWidget(QWidget *parent)
146         : KComboBox(false, parent), d(new ColorLabelWidgetPrivate(this, new ColorLabelComboBoxModel(this)))
147 {
148     setModel(d->model);
149     connect(this, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ColorLabelWidget::slotCurrentIndexChanged);
150 }
151 
~ColorLabelWidget()152 ColorLabelWidget::~ColorLabelWidget()
153 {
154     delete d;
155 }
156 
clear()157 void ColorLabelWidget::clear()
158 {
159     setCurrentIndex(0);
160 }
161 
reset(const Value & value)162 bool ColorLabelWidget::reset(const Value &value)
163 {
164     /// Avoid triggering signal when current index is set by the program
165     disconnect(this, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ColorLabelWidget::slotCurrentIndexChanged);
166 
167     QSharedPointer<VerbatimText> verbatimText;
168     if (value.count() == 1 && !(verbatimText = value.first().dynamicCast<VerbatimText>()).isNull()) {
169         int i = 0;
170         const QColor color = QColor(verbatimText->text());
171         for (; i < d->model->rowCount(); ++i)
172             if (d->model->data(d->model->index(i, 0, QModelIndex()), ColorLabelComboBoxModel::ColorRole).value<QColor>() == color)
173                 break;
174 
175         if (i >= d->model->rowCount()) {
176             d->model->userColor = color;
177             i = d->model->rowCount() - 1;
178         }
179         setCurrentIndex(i);
180     } else
181         setCurrentIndex(0);
182 
183     /// Re-enable triggering signal after setting current index
184     connect(this, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ColorLabelWidget::slotCurrentIndexChanged);
185 
186     return true;
187 }
188 
apply(Value & value) const189 bool ColorLabelWidget::apply(Value &value) const
190 {
191     QColor color = d->model->data(d->model->index(currentIndex(), 0, QModelIndex()), ColorLabelComboBoxModel::ColorRole).value<QColor>();
192     value.clear();
193     if (color != Qt::black)
194         value.append(QSharedPointer<VerbatimText>(new VerbatimText(color.name())));
195     return true;
196 }
197 
validate(QWidget **,QString &) const198 bool ColorLabelWidget::validate(QWidget **, QString &) const
199 {
200     return true;
201 }
202 
setReadOnly(bool isReadOnly)203 void ColorLabelWidget::setReadOnly(bool isReadOnly)
204 {
205     setEnabled(!isReadOnly);
206 }
207 
slotCurrentIndexChanged(int index)208 void ColorLabelWidget::slotCurrentIndexChanged(int index)
209 {
210     if (index == count() - 1) {
211         QColor dialogColor = d->model->userColor;
212         if (QColorDialog::getColor(dialogColor, this) == QColorDialog::Accepted)
213             d->model->setColor(dialogColor);
214     }
215 
216     emit modified();
217 }
218 
createSolidIcon(const QColor & color)219 QPixmap ColorLabelWidget::createSolidIcon(const QColor &color)
220 {
221     QFontMetrics fm = QFontMetrics(QFont());
222     int h = fm.height() - 4;
223     QPixmap pm(h, h);
224     QPainter painter(&pm);
225     painter.setPen(color);
226     painter.setBrush(QBrush(color));
227     painter.drawRect(0, 0, h, h);
228     return pm;
229 }
230 
231 #include "colorlabelwidget.moc"
232