File indexing completed on 2024-04-28 11:45:25

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 = dynamic_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 = dynamic_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 = dynamic_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 QVector<KateColorItem> &colorItems)
0339 {
0340     for (const KateColorItem &item : colorItems) {
0341         addColorItem(item);
0342     }
0343 }
0344 
0345 QVector<KateColorItem> KateColorTreeWidget::colorItems() const
0346 {
0347     QVector<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 = dynamic_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 = dynamic_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"