File indexing completed on 2024-04-28 07:46:40
0001 /* 0002 SPDX-FileCopyrightText: 2012-2018 Dominik Haumann <dhaumann@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "katecolortreewidget.h" 0008 0009 #include "katecategorydrawer.h" 0010 0011 #include <QPainter> 0012 #include <QStyledItemDelegate> 0013 0014 #include <KLocalizedString> 0015 0016 #include <QColorDialog> 0017 #include <QEvent> 0018 #include <QKeyEvent> 0019 #include <qdrawutil.h> 0020 0021 // BEGIN KateColorTreeItem 0022 class KateColorTreeItem : public QTreeWidgetItem 0023 { 0024 public: 0025 KateColorTreeItem(const KateColorItem &colorItem, QTreeWidgetItem *parent = nullptr) 0026 : QTreeWidgetItem(parent) 0027 , m_colorItem(colorItem) 0028 { 0029 setText(0, m_colorItem.name); 0030 if (!colorItem.whatsThis.isEmpty()) { 0031 setData(1, Qt::WhatsThisRole, colorItem.whatsThis); 0032 } 0033 if (!colorItem.useDefault) { 0034 setData(2, Qt::ToolTipRole, i18n("Use default color from the color theme")); 0035 } 0036 } 0037 0038 QColor color() const 0039 { 0040 return m_colorItem.color; 0041 } 0042 0043 void setColor(const QColor &c) 0044 { 0045 m_colorItem.color = c; 0046 } 0047 0048 QColor defaultColor() const 0049 { 0050 return m_colorItem.defaultColor; 0051 } 0052 0053 bool useDefaultColor() const 0054 { 0055 return m_colorItem.useDefault; 0056 } 0057 0058 void setUseDefaultColor(bool useDefault) 0059 { 0060 m_colorItem.useDefault = useDefault; 0061 QString tooltip = useDefault ? QString() : i18n("Use default color from the color theme"); 0062 setData(2, Qt::ToolTipRole, tooltip); 0063 } 0064 0065 QString key() const 0066 { 0067 return m_colorItem.key; 0068 } 0069 0070 KateColorItem colorItem() const 0071 { 0072 return m_colorItem; 0073 } 0074 0075 private: 0076 KateColorItem m_colorItem; 0077 }; 0078 // END KateColorTreeItem 0079 0080 // BEGIN KateColorTreeDelegate 0081 class KateColorTreeDelegate : public QStyledItemDelegate 0082 { 0083 public: 0084 KateColorTreeDelegate(KateColorTreeWidget *widget) 0085 : QStyledItemDelegate(widget) 0086 , m_tree(widget) 0087 { 0088 } 0089 0090 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override 0091 { 0092 QSize sh = QStyledItemDelegate::sizeHint(option, index); 0093 if (!index.parent().isValid()) { 0094 sh.rheight() += 2 * m_categoryDrawer.leftMargin(); 0095 } else { 0096 sh.rheight() += m_categoryDrawer.leftMargin(); 0097 } 0098 if (index.column() == 0) { 0099 sh.rwidth() += m_categoryDrawer.leftMargin(); 0100 } else if (index.column() == 1) { 0101 sh.rwidth() = 150; 0102 } else { 0103 sh.rwidth() += m_categoryDrawer.leftMargin(); 0104 } 0105 0106 return sh; 0107 } 0108 0109 QRect fullCategoryRect(const QStyleOptionViewItem &option, const QModelIndex &index) const 0110 { 0111 QModelIndex i = index; 0112 if (i.parent().isValid()) { 0113 i = i.parent(); 0114 } 0115 0116 QTreeWidgetItem *item = m_tree->itemFromIndex(i); 0117 QRect r = m_tree->visualItemRect(item); 0118 0119 // adapt width 0120 r.setLeft(m_categoryDrawer.leftMargin()); 0121 r.setWidth(m_tree->viewport()->width() - m_categoryDrawer.leftMargin() - m_categoryDrawer.rightMargin()); 0122 0123 // adapt height 0124 if (item->isExpanded() && item->childCount() > 0) { 0125 const int childCount = item->childCount(); 0126 const int h = sizeHint(option, index.model()->index(0, 0, index)).height(); 0127 r.setHeight(r.height() + childCount * h); 0128 } 0129 0130 r.setTop(r.top() + m_categoryDrawer.leftMargin()); 0131 0132 return r; 0133 } 0134 0135 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override 0136 { 0137 Q_ASSERT(index.isValid()); 0138 Q_ASSERT(index.column() >= 0 && index.column() <= 2); 0139 0140 // BEGIN: draw toplevel items 0141 if (!index.parent().isValid()) { 0142 QStyleOptionViewItem opt(option); 0143 const QRegion cl = painter->clipRegion(); 0144 painter->setClipRect(opt.rect); 0145 opt.rect = fullCategoryRect(option, index); 0146 m_categoryDrawer.drawCategory(index, 0, opt, painter); 0147 painter->setClipRegion(cl); 0148 return; 0149 } 0150 // END: draw toplevel items 0151 0152 // BEGIN: draw background of category for all other items 0153 { 0154 QStyleOptionViewItem opt(option); 0155 opt.rect = fullCategoryRect(option, index); 0156 const QRegion cl = painter->clipRegion(); 0157 QRect cr = option.rect; 0158 if (index.column() == 0) { 0159 if (m_tree->layoutDirection() == Qt::LeftToRight) { 0160 cr.setLeft(5); 0161 } else { 0162 cr.setRight(opt.rect.right()); 0163 } 0164 } 0165 painter->setClipRect(cr); 0166 m_categoryDrawer.drawCategory(index, 0, opt, painter); 0167 painter->setClipRegion(cl); 0168 painter->setRenderHint(QPainter::Antialiasing, false); 0169 } 0170 // END: draw background of category for all other items 0171 0172 // paint the text 0173 QStyledItemDelegate::paint(painter, option, index); 0174 if (index.column() == 0) { 0175 return; 0176 } 0177 0178 painter->setClipRect(option.rect); 0179 KateColorTreeItem *item = static_cast<KateColorTreeItem *>(m_tree->itemFromIndex(index)); 0180 0181 // BEGIN: draw color button 0182 if (index.column() == 1) { 0183 QColor color = item->useDefaultColor() ? item->defaultColor() : item->color(); 0184 0185 QStyleOptionButton opt; 0186 opt.rect = option.rect; 0187 opt.palette = m_tree->palette(); 0188 0189 m_tree->style()->drawControl(QStyle::CE_PushButton, &opt, painter, m_tree); 0190 opt.rect = m_tree->style()->subElementRect(QStyle::SE_PushButtonContents, &opt, m_tree); 0191 opt.rect.adjust(1, 1, -1, -1); 0192 painter->fillRect(opt.rect, color); 0193 0194 qDrawShadePanel(painter, opt.rect, opt.palette, true, 1, nullptr); 0195 } 0196 // END: draw color button 0197 0198 // BEGIN: draw reset icon 0199 if (index.column() == 2 && !item->useDefaultColor()) { 0200 // get right pixmap 0201 const bool enabled = (option.state & QStyle::State_MouseOver || option.state & QStyle::State_HasFocus); 0202 const QPixmap p = QIcon::fromTheme(QStringLiteral("edit-undo")).pixmap(16, 16, enabled ? QIcon::Normal : QIcon::Disabled); 0203 0204 // compute rect with scaled sizes 0205 const QRect rect(option.rect.left() + 10, 0206 option.rect.top() + (option.rect.height() - p.height() / p.devicePixelRatio() + 1) / 2, 0207 p.width() / p.devicePixelRatio(), 0208 p.height() / p.devicePixelRatio()); 0209 painter->drawPixmap(rect, p); 0210 } 0211 // END: draw reset icon 0212 } 0213 0214 private: 0215 KateColorTreeWidget *m_tree; 0216 KateCategoryDrawer m_categoryDrawer; 0217 }; 0218 // END KateColorTreeDelegate 0219 0220 KateColorTreeWidget::KateColorTreeWidget(QWidget *parent) 0221 : QTreeWidget(parent) 0222 { 0223 setItemDelegate(new KateColorTreeDelegate(this)); 0224 0225 QStringList headers; 0226 headers << QString() // i18nc("@title:column the color name", "Color Role") 0227 << QString() // i18nc("@title:column a color button", "Color") 0228 << QString(); // i18nc("@title:column use default color", "Reset") 0229 setHeaderLabels(headers); 0230 setHeaderHidden(true); 0231 setRootIsDecorated(false); 0232 setIndentation(25); 0233 } 0234 0235 bool KateColorTreeWidget::edit(const QModelIndex &index, EditTrigger trigger, QEvent *event) 0236 { 0237 if (m_readOnly) { 0238 return false; 0239 } 0240 0241 // accept edit only for color buttons in column 1 and reset in column 2 0242 if (!index.parent().isValid() || index.column() < 1) { 0243 return QTreeWidget::edit(index, trigger, event); 0244 } 0245 0246 bool accept = false; 0247 if (event && event->type() == QEvent::KeyPress) { 0248 QKeyEvent *ke = static_cast<QKeyEvent *>(event); 0249 accept = (ke->key() == Qt::Key_Space); // allow Space to edit 0250 } 0251 0252 switch (trigger) { 0253 case QAbstractItemView::DoubleClicked: 0254 case QAbstractItemView::SelectedClicked: 0255 case QAbstractItemView::EditKeyPressed: // = F2 0256 accept = true; 0257 break; 0258 default: 0259 break; 0260 } 0261 0262 if (accept) { 0263 KateColorTreeItem *item = static_cast<KateColorTreeItem *>(itemFromIndex(index)); 0264 const QColor color = item->useDefaultColor() ? item->defaultColor() : item->color(); 0265 0266 if (index.column() == 1) { 0267 const QColor selectedColor = QColorDialog::getColor(color, this, QString(), QColorDialog::ShowAlphaChannel); 0268 0269 if (selectedColor.isValid()) { 0270 item->setUseDefaultColor(false); 0271 item->setColor(selectedColor); 0272 viewport()->update(); 0273 Q_EMIT changed(); 0274 } 0275 } else if (index.column() == 2 && !item->useDefaultColor()) { 0276 item->setUseDefaultColor(true); 0277 viewport()->update(); 0278 Q_EMIT changed(); 0279 } 0280 0281 return false; 0282 } 0283 return QTreeWidget::edit(index, trigger, event); 0284 } 0285 0286 void KateColorTreeWidget::drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const 0287 { 0288 Q_UNUSED(painter) 0289 Q_UNUSED(rect) 0290 Q_UNUSED(index) 0291 } 0292 0293 void KateColorTreeWidget::selectDefaults() 0294 { 0295 bool somethingChanged = false; 0296 0297 // use default colors for all selected items 0298 for (int a = 0; a < topLevelItemCount(); ++a) { 0299 QTreeWidgetItem *top = topLevelItem(a); 0300 for (int b = 0; b < top->childCount(); ++b) { 0301 KateColorTreeItem *it = static_cast<KateColorTreeItem *>(top->child(b)); 0302 Q_ASSERT(it); 0303 if (!it->useDefaultColor()) { 0304 it->setUseDefaultColor(true); 0305 somethingChanged = true; 0306 } 0307 } 0308 } 0309 0310 if (somethingChanged) { 0311 viewport()->update(); 0312 Q_EMIT changed(); 0313 } 0314 } 0315 0316 void KateColorTreeWidget::addColorItem(const KateColorItem &colorItem) 0317 { 0318 QTreeWidgetItem *categoryItem = nullptr; 0319 for (int i = 0; i < topLevelItemCount(); ++i) { 0320 if (topLevelItem(i)->text(0) == colorItem.category) { 0321 categoryItem = topLevelItem(i); 0322 break; 0323 } 0324 } 0325 0326 if (!categoryItem) { 0327 categoryItem = new QTreeWidgetItem(); 0328 categoryItem->setText(0, colorItem.category); 0329 addTopLevelItem(categoryItem); 0330 expandItem(categoryItem); 0331 } 0332 0333 new KateColorTreeItem(colorItem, categoryItem); 0334 0335 resizeColumnToContents(0); 0336 } 0337 0338 void KateColorTreeWidget::addColorItems(const QList<KateColorItem> &colorItems) 0339 { 0340 for (const KateColorItem &item : colorItems) { 0341 addColorItem(item); 0342 } 0343 } 0344 0345 QList<KateColorItem> KateColorTreeWidget::colorItems() const 0346 { 0347 QList<KateColorItem> items; 0348 for (int a = 0; a < topLevelItemCount(); ++a) { 0349 QTreeWidgetItem *top = topLevelItem(a); 0350 for (int b = 0; b < top->childCount(); ++b) { 0351 KateColorTreeItem *item = static_cast<KateColorTreeItem *>(top->child(b)); 0352 Q_ASSERT(item); 0353 items.append(item->colorItem()); 0354 } 0355 } 0356 return items; 0357 } 0358 0359 QColor KateColorTreeWidget::findColor(const QString &key) const 0360 { 0361 for (int a = 0; a < topLevelItemCount(); ++a) { 0362 QTreeWidgetItem *top = topLevelItem(a); 0363 for (int b = 0; b < top->childCount(); ++b) { 0364 KateColorTreeItem *item = static_cast<KateColorTreeItem *>(top->child(b)); 0365 if (item->key() == key) { 0366 if (item->useDefaultColor()) { 0367 return item->defaultColor(); 0368 } else { 0369 return item->color(); 0370 } 0371 } 0372 } 0373 } 0374 return QColor(); 0375 } 0376 0377 bool KateColorTreeWidget::readOnly() const 0378 { 0379 return m_readOnly; 0380 } 0381 0382 void KateColorTreeWidget::setReadOnly(bool readOnly) 0383 { 0384 m_readOnly = readOnly; 0385 } 0386 0387 #include "moc_katecolortreewidget.cpp"