1 | /* |
2 | SPDX-FileCopyrightText: 2012-2018 Dominik Haumann <dhaumann@kde.org> |
3 | |
4 | SPDX-License-Identifier: LGPL-2.0-or-later |
5 | */ |
6 | |
7 | #include "katecolortreewidget.h" |
8 | |
9 | #include "katecategorydrawer.h" |
10 | |
11 | #include <QPainter> |
12 | #include <QStyledItemDelegate> |
13 | |
14 | #include <KLocalizedString> |
15 | |
16 | #include <QColorDialog> |
17 | #include <QEvent> |
18 | #include <QKeyEvent> |
19 | #include <qdrawutil.h> |
20 | |
21 | // BEGIN KateColorTreeItem |
22 | class KateColorTreeItem : public QTreeWidgetItem |
23 | { |
24 | public: |
25 | KateColorTreeItem(const KateColorItem &colorItem, QTreeWidgetItem *parent = nullptr) |
26 | : QTreeWidgetItem(parent) |
27 | , m_colorItem(colorItem) |
28 | { |
29 | setText(column: 0, atext: m_colorItem.name); |
30 | if (!colorItem.whatsThis.isEmpty()) { |
31 | setData(column: 1, role: Qt::WhatsThisRole, value: colorItem.whatsThis); |
32 | } |
33 | if (!colorItem.useDefault) { |
34 | setData(column: 2, role: Qt::ToolTipRole, i18n("Use default color from the color theme" )); |
35 | } |
36 | } |
37 | |
38 | QColor color() const |
39 | { |
40 | return m_colorItem.color; |
41 | } |
42 | |
43 | void setColor(const QColor &c) |
44 | { |
45 | m_colorItem.color = c; |
46 | } |
47 | |
48 | QColor defaultColor() const |
49 | { |
50 | return m_colorItem.defaultColor; |
51 | } |
52 | |
53 | bool useDefaultColor() const |
54 | { |
55 | return m_colorItem.useDefault; |
56 | } |
57 | |
58 | void setUseDefaultColor(bool useDefault) |
59 | { |
60 | m_colorItem.useDefault = useDefault; |
61 | QString tooltip = useDefault ? QString() : i18n("Use default color from the color theme" ); |
62 | setData(column: 2, role: Qt::ToolTipRole, value: tooltip); |
63 | } |
64 | |
65 | QString key() const |
66 | { |
67 | return m_colorItem.key; |
68 | } |
69 | |
70 | KateColorItem colorItem() const |
71 | { |
72 | return m_colorItem; |
73 | } |
74 | |
75 | private: |
76 | KateColorItem m_colorItem; |
77 | }; |
78 | // END KateColorTreeItem |
79 | |
80 | // BEGIN KateColorTreeDelegate |
81 | class KateColorTreeDelegate : public QStyledItemDelegate |
82 | { |
83 | public: |
84 | KateColorTreeDelegate(KateColorTreeWidget *widget) |
85 | : QStyledItemDelegate(widget) |
86 | , m_tree(widget) |
87 | { |
88 | } |
89 | |
90 | QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override |
91 | { |
92 | QSize sh = QStyledItemDelegate::sizeHint(option, index); |
93 | if (!index.parent().isValid()) { |
94 | sh.rheight() += 2 * m_categoryDrawer.leftMargin(); |
95 | } else { |
96 | sh.rheight() += m_categoryDrawer.leftMargin(); |
97 | } |
98 | if (index.column() == 0) { |
99 | sh.rwidth() += m_categoryDrawer.leftMargin(); |
100 | } else if (index.column() == 1) { |
101 | sh.rwidth() = 150; |
102 | } else { |
103 | sh.rwidth() += m_categoryDrawer.leftMargin(); |
104 | } |
105 | |
106 | return sh; |
107 | } |
108 | |
109 | QRect fullCategoryRect(const QStyleOptionViewItem &option, const QModelIndex &index) const |
110 | { |
111 | QModelIndex i = index; |
112 | if (i.parent().isValid()) { |
113 | i = i.parent(); |
114 | } |
115 | |
116 | QTreeWidgetItem *item = m_tree->itemFromIndex(index: i); |
117 | QRect r = m_tree->visualItemRect(item); |
118 | |
119 | // adapt width |
120 | r.setLeft(m_categoryDrawer.leftMargin()); |
121 | r.setWidth(m_tree->viewport()->width() - m_categoryDrawer.leftMargin() - m_categoryDrawer.rightMargin()); |
122 | |
123 | // adapt height |
124 | if (item->isExpanded() && item->childCount() > 0) { |
125 | const int childCount = item->childCount(); |
126 | const int h = sizeHint(option, index: index.model()->index(row: 0, column: 0, parent: index)).height(); |
127 | r.setHeight(r.height() + childCount * h); |
128 | } |
129 | |
130 | r.setTop(r.top() + m_categoryDrawer.leftMargin()); |
131 | |
132 | return r; |
133 | } |
134 | |
135 | void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override |
136 | { |
137 | Q_ASSERT(index.isValid()); |
138 | Q_ASSERT(index.column() >= 0 && index.column() <= 2); |
139 | |
140 | // BEGIN: draw toplevel items |
141 | if (!index.parent().isValid()) { |
142 | QStyleOptionViewItem opt(option); |
143 | const QRegion cl = painter->clipRegion(); |
144 | painter->setClipRect(opt.rect); |
145 | opt.rect = fullCategoryRect(option, index); |
146 | m_categoryDrawer.drawCategory(index, sortRole: 0, option: opt, painter); |
147 | painter->setClipRegion(cl); |
148 | return; |
149 | } |
150 | // END: draw toplevel items |
151 | |
152 | // BEGIN: draw background of category for all other items |
153 | { |
154 | QStyleOptionViewItem opt(option); |
155 | opt.rect = fullCategoryRect(option, index); |
156 | const QRegion cl = painter->clipRegion(); |
157 | QRect cr = option.rect; |
158 | if (index.column() == 0) { |
159 | if (m_tree->layoutDirection() == Qt::LeftToRight) { |
160 | cr.setLeft(5); |
161 | } else { |
162 | cr.setRight(opt.rect.right()); |
163 | } |
164 | } |
165 | painter->setClipRect(cr); |
166 | m_categoryDrawer.drawCategory(index, sortRole: 0, option: opt, painter); |
167 | painter->setClipRegion(cl); |
168 | painter->setRenderHint(hint: QPainter::Antialiasing, on: false); |
169 | } |
170 | // END: draw background of category for all other items |
171 | |
172 | // paint the text |
173 | QStyledItemDelegate::paint(painter, option, index); |
174 | if (index.column() == 0) { |
175 | return; |
176 | } |
177 | |
178 | painter->setClipRect(option.rect); |
179 | KateColorTreeItem *item = static_cast<KateColorTreeItem *>(m_tree->itemFromIndex(index)); |
180 | |
181 | // BEGIN: draw color button |
182 | if (index.column() == 1) { |
183 | QColor color = item->useDefaultColor() ? item->defaultColor() : item->color(); |
184 | |
185 | QStyleOptionButton opt; |
186 | opt.rect = option.rect; |
187 | opt.palette = m_tree->palette(); |
188 | |
189 | m_tree->style()->drawControl(element: QStyle::CE_PushButton, opt: &opt, p: painter, w: m_tree); |
190 | opt.rect = m_tree->style()->subElementRect(subElement: QStyle::SE_PushButtonContents, option: &opt, widget: m_tree); |
191 | opt.rect.adjust(dx1: 1, dy1: 1, dx2: -1, dy2: -1); |
192 | painter->fillRect(opt.rect, color); |
193 | |
194 | qDrawShadePanel(p: painter, r: opt.rect, pal: opt.palette, sunken: true, lineWidth: 1, fill: nullptr); |
195 | } |
196 | // END: draw color button |
197 | |
198 | // BEGIN: draw reset icon |
199 | if (index.column() == 2 && !item->useDefaultColor()) { |
200 | // get right pixmap |
201 | const bool enabled = (option.state & QStyle::State_MouseOver || option.state & QStyle::State_HasFocus); |
202 | const QPixmap p = QIcon::fromTheme(QStringLiteral("edit-undo" )).pixmap(w: 16, h: 16, mode: enabled ? QIcon::Normal : QIcon::Disabled); |
203 | |
204 | // compute rect with scaled sizes |
205 | const QRect rect(option.rect.left() + 10, |
206 | option.rect.top() + (option.rect.height() - p.height() / p.devicePixelRatio() + 1) / 2, |
207 | p.width() / p.devicePixelRatio(), |
208 | p.height() / p.devicePixelRatio()); |
209 | painter->drawPixmap(r: rect, pm: p); |
210 | } |
211 | // END: draw reset icon |
212 | } |
213 | |
214 | private: |
215 | KateColorTreeWidget *m_tree; |
216 | KateCategoryDrawer m_categoryDrawer; |
217 | }; |
218 | // END KateColorTreeDelegate |
219 | |
220 | KateColorTreeWidget::KateColorTreeWidget(QWidget *parent) |
221 | : QTreeWidget(parent) |
222 | { |
223 | setItemDelegate(new KateColorTreeDelegate(this)); |
224 | |
225 | QStringList ; |
226 | headers << QString() // i18nc("@title:column the color name", "Color Role") |
227 | << QString() // i18nc("@title:column a color button", "Color") |
228 | << QString(); // i18nc("@title:column use default color", "Reset") |
229 | setHeaderLabels(headers); |
230 | setHeaderHidden(true); |
231 | setRootIsDecorated(false); |
232 | setIndentation(25); |
233 | } |
234 | |
235 | bool KateColorTreeWidget::edit(const QModelIndex &index, EditTrigger trigger, QEvent *event) |
236 | { |
237 | if (m_readOnly) { |
238 | return false; |
239 | } |
240 | |
241 | // accept edit only for color buttons in column 1 and reset in column 2 |
242 | if (!index.parent().isValid() || index.column() < 1) { |
243 | return QTreeWidget::edit(index, trigger, event); |
244 | } |
245 | |
246 | bool accept = false; |
247 | if (event && event->type() == QEvent::KeyPress) { |
248 | QKeyEvent *ke = static_cast<QKeyEvent *>(event); |
249 | accept = (ke->key() == Qt::Key_Space); // allow Space to edit |
250 | } |
251 | |
252 | switch (trigger) { |
253 | case QAbstractItemView::DoubleClicked: |
254 | case QAbstractItemView::SelectedClicked: |
255 | case QAbstractItemView::EditKeyPressed: // = F2 |
256 | accept = true; |
257 | break; |
258 | default: |
259 | break; |
260 | } |
261 | |
262 | if (accept) { |
263 | KateColorTreeItem *item = static_cast<KateColorTreeItem *>(itemFromIndex(index)); |
264 | const QColor color = item->useDefaultColor() ? item->defaultColor() : item->color(); |
265 | |
266 | if (index.column() == 1) { |
267 | const QColor selectedColor = QColorDialog::getColor(initial: color, parent: this, title: QString(), options: QColorDialog::ShowAlphaChannel); |
268 | |
269 | if (selectedColor.isValid()) { |
270 | item->setUseDefaultColor(false); |
271 | item->setColor(selectedColor); |
272 | viewport()->update(); |
273 | Q_EMIT changed(); |
274 | } |
275 | } else if (index.column() == 2 && !item->useDefaultColor()) { |
276 | item->setUseDefaultColor(true); |
277 | viewport()->update(); |
278 | Q_EMIT changed(); |
279 | } |
280 | |
281 | return false; |
282 | } |
283 | return QTreeWidget::edit(index, trigger, event); |
284 | } |
285 | |
286 | void KateColorTreeWidget::drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const |
287 | { |
288 | Q_UNUSED(painter) |
289 | Q_UNUSED(rect) |
290 | Q_UNUSED(index) |
291 | } |
292 | |
293 | void KateColorTreeWidget::selectDefaults() |
294 | { |
295 | bool somethingChanged = false; |
296 | |
297 | // use default colors for all selected items |
298 | for (int a = 0; a < topLevelItemCount(); ++a) { |
299 | QTreeWidgetItem *top = topLevelItem(index: a); |
300 | for (int b = 0; b < top->childCount(); ++b) { |
301 | KateColorTreeItem *it = static_cast<KateColorTreeItem *>(top->child(index: b)); |
302 | Q_ASSERT(it); |
303 | if (!it->useDefaultColor()) { |
304 | it->setUseDefaultColor(true); |
305 | somethingChanged = true; |
306 | } |
307 | } |
308 | } |
309 | |
310 | if (somethingChanged) { |
311 | viewport()->update(); |
312 | Q_EMIT changed(); |
313 | } |
314 | } |
315 | |
316 | void KateColorTreeWidget::addColorItem(const KateColorItem &colorItem) |
317 | { |
318 | QTreeWidgetItem *categoryItem = nullptr; |
319 | for (int i = 0; i < topLevelItemCount(); ++i) { |
320 | if (topLevelItem(index: i)->text(column: 0) == colorItem.category) { |
321 | categoryItem = topLevelItem(index: i); |
322 | break; |
323 | } |
324 | } |
325 | |
326 | if (!categoryItem) { |
327 | categoryItem = new QTreeWidgetItem(); |
328 | categoryItem->setText(column: 0, atext: colorItem.category); |
329 | addTopLevelItem(item: categoryItem); |
330 | expandItem(item: categoryItem); |
331 | } |
332 | |
333 | new KateColorTreeItem(colorItem, categoryItem); |
334 | |
335 | resizeColumnToContents(column: 0); |
336 | } |
337 | |
338 | void KateColorTreeWidget::addColorItems(const QList<KateColorItem> &colorItems) |
339 | { |
340 | for (const KateColorItem &item : colorItems) { |
341 | addColorItem(colorItem: item); |
342 | } |
343 | } |
344 | |
345 | QList<KateColorItem> KateColorTreeWidget::colorItems() const |
346 | { |
347 | QList<KateColorItem> items; |
348 | for (int a = 0; a < topLevelItemCount(); ++a) { |
349 | QTreeWidgetItem *top = topLevelItem(index: a); |
350 | for (int b = 0; b < top->childCount(); ++b) { |
351 | KateColorTreeItem *item = static_cast<KateColorTreeItem *>(top->child(index: b)); |
352 | Q_ASSERT(item); |
353 | items.append(t: item->colorItem()); |
354 | } |
355 | } |
356 | return items; |
357 | } |
358 | |
359 | QColor KateColorTreeWidget::findColor(const QString &key) const |
360 | { |
361 | for (int a = 0; a < topLevelItemCount(); ++a) { |
362 | QTreeWidgetItem *top = topLevelItem(index: a); |
363 | for (int b = 0; b < top->childCount(); ++b) { |
364 | KateColorTreeItem *item = static_cast<KateColorTreeItem *>(top->child(index: b)); |
365 | if (item->key() == key) { |
366 | if (item->useDefaultColor()) { |
367 | return item->defaultColor(); |
368 | } else { |
369 | return item->color(); |
370 | } |
371 | } |
372 | } |
373 | } |
374 | return QColor(); |
375 | } |
376 | |
377 | bool KateColorTreeWidget::readOnly() const |
378 | { |
379 | return m_readOnly; |
380 | } |
381 | |
382 | void KateColorTreeWidget::setReadOnly(bool readOnly) |
383 | { |
384 | m_readOnly = readOnly; |
385 | } |
386 | |
387 | #include "moc_katecolortreewidget.cpp" |
388 | |