File indexing completed on 2025-03-09 04:51:37

0001 /*
0002  * SPDX-FileCopyrightText: 2014 Christian Mollekopf <mollekopf@kolabsys.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
0005  */
0006 
0007 #include "calendardelegate.h"
0008 #include "kohelper.h"
0009 #include "korganizer_debug.h"
0010 
0011 #include <Akonadi/CollectionStatistics>
0012 #include <Akonadi/CollectionUtils>
0013 
0014 #include <QApplication>
0015 #include <QFontDatabase>
0016 #include <QMouseEvent>
0017 #include <QPainter>
0018 #include <QPainterPath>
0019 
0020 StyledCalendarDelegate::StyledCalendarDelegate(QObject *parent)
0021     : QStyledItemDelegate(parent)
0022 {
0023     mIcon.insert(Quickview, QIcon::fromTheme(QStringLiteral("quickview")));
0024 }
0025 
0026 StyledCalendarDelegate::~StyledCalendarDelegate() = default;
0027 
0028 static QRect enableButtonRect(QRect rect, int pos = 1)
0029 {
0030     // 2px border on each side of the icon
0031     static int border = 2;
0032     const int side = rect.height() - (2 * border);
0033     const int offset = side * pos + border * (pos + 1);
0034     return rect.adjusted(rect.width() - (offset + side), border, -offset, -border);
0035 }
0036 
0037 static QStyle *style(const QStyleOptionViewItem &option)
0038 {
0039     QWidget const *widget = nullptr;
0040     if (const auto v3 = qstyleoption_cast<const QStyleOptionViewItem *>(&option)) {
0041         widget = v3->widget;
0042     }
0043     QStyle *style = widget ? widget->style() : QApplication::style();
0044     return style;
0045 }
0046 
0047 static QStyleOptionButton buttonOpt(const QStyleOptionViewItem &opt, const QIcon &icon, const QModelIndex &index, int pos = 1)
0048 {
0049     Q_UNUSED(index)
0050 
0051     QStyleOptionButton option;
0052     option.icon = icon;
0053     const QRect r = opt.rect;
0054     const int h = r.height() - 4;
0055     option.rect = enableButtonRect(r, pos);
0056     option.state = QStyle::State_Active | QStyle::State_Enabled;
0057     option.iconSize = QSize(h, h);
0058     return option;
0059 }
0060 
0061 QList<StyledCalendarDelegate::Action> StyledCalendarDelegate::getActions(const QStyleOptionViewItem &, const QModelIndex &index) const
0062 {
0063     const Akonadi::Collection col = Akonadi::CollectionUtils::fromIndex(index);
0064     // qCDebug(KORGANIZER_LOG) << index.data().toString() << enabled;
0065     const bool isSearchCollection = col.resource().startsWith(QLatin1StringView("akonadi_search_resource"));
0066     const bool isKolabCollection = col.resource().startsWith(QLatin1StringView("akonadi_kolab_resource"));
0067     const bool isTopLevelCollection = (col.parentCollection() == Akonadi::Collection::root());
0068     const bool isToplevelSearchCollection = (isTopLevelCollection && isSearchCollection);
0069     const bool isToplevelKolabCollection = (isTopLevelCollection && isKolabCollection);
0070 
0071     QList<Action> buttons;
0072     if (!isToplevelSearchCollection && !isToplevelKolabCollection) {
0073         buttons << Quickview;
0074     }
0075     if (isSearchCollection && !isToplevelSearchCollection) {
0076         buttons << Total;
0077     }
0078     return buttons;
0079 }
0080 
0081 void StyledCalendarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
0082 {
0083     Q_ASSERT(index.isValid());
0084 
0085     const Akonadi::Collection col = Akonadi::CollectionUtils::fromIndex(index);
0086 
0087     QStyleOptionViewItem opt = option;
0088     opt.font = QFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont));
0089     opt.textElideMode = Qt::ElideLeft;
0090 
0091     initStyleOption(&opt, index);
0092     QStyledItemDelegate::paint(painter, opt, index);
0093 
0094     QStyle *s = style(option);
0095 
0096     // Buttons
0097     {
0098         int i = 1;
0099         const auto lstActions = getActions(option, index);
0100         for (Action action : lstActions) {
0101             if (action != Total) {
0102                 QStyleOptionButton buttonOption = buttonOpt(opt, mIcon.value(action), index, i);
0103                 s->drawControl(QStyle::CE_PushButton, &buttonOption, painter, nullptr);
0104             } else {
0105                 QStyleOptionButton buttonOption = buttonOpt(opt, QPixmap(), index, i);
0106                 buttonOption.features = QStyleOptionButton::Flat;
0107                 buttonOption.rect.setHeight(buttonOption.rect.height() + 4);
0108                 if (col.statistics().count() > 0) {
0109                     buttonOption.text = QString::number(col.statistics().count());
0110                 }
0111                 s->drawControl(QStyle::CE_PushButton, &buttonOption, painter, nullptr);
0112             }
0113             i++;
0114         }
0115     }
0116 
0117     // Color indicator
0118     if (opt.checkState) {
0119         QColor color = KOHelper::resourceColorKnown(col);
0120         if (!color.isValid()) {
0121             color = KOHelper::resourceColor(col);
0122         }
0123         if (color.isValid()) {
0124             painter->save();
0125             painter->setRenderHint(QPainter::Antialiasing);
0126             QPen pen = painter->pen();
0127             pen.setColor(color);
0128             QPainterPath path;
0129             path.addRoundedRect(enableButtonRect(opt.rect, 0), 5, 5);
0130             color.setAlpha(200);
0131             painter->fillPath(path, color);
0132             painter->strokePath(path, pen);
0133             painter->restore();
0134         }
0135     }
0136 }
0137 
0138 bool StyledCalendarDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
0139 {
0140     Q_ASSERT(event);
0141     Q_ASSERT(model);
0142 
0143     // double-click mouse starts the quickview dialog
0144     if (event->type() == QEvent::MouseButtonDblClick) {
0145         Q_EMIT action(index, Quickview);
0146         return true;
0147     }
0148 
0149     int button = -1;
0150     // make sure that we have the right event type
0151     if ((event->type() == QEvent::MouseButtonRelease) || (event->type() == QEvent::MouseButtonPress)) {
0152         auto me = static_cast<QMouseEvent *>(event);
0153 
0154         for (int i = 1; i < 4; i++) {
0155             if (enableButtonRect(option.rect, i).contains(me->pos())) {
0156                 button = i;
0157                 break;
0158             }
0159         }
0160 
0161         if (me->button() != Qt::LeftButton || button < 0) {
0162             return QStyledItemDelegate::editorEvent(event, model, option, index);
0163         }
0164 
0165         if (event->type() == QEvent::MouseButtonPress) {
0166             return true;
0167         }
0168     } else {
0169         return QStyledItemDelegate::editorEvent(event, model, option, index);
0170     }
0171 
0172     Q_ASSERT(button > 0);
0173     QStyleOptionViewItem opt = option;
0174     opt.state |= QStyle::State_MouseOver;
0175 
0176     QList<StyledCalendarDelegate::Action> actions = getActions(opt, index);
0177     if (actions.count() >= button) {
0178         const Action a = actions.at(button - 1);
0179         Q_EMIT action(index, a);
0180         return true;
0181     }
0182     return QStyledItemDelegate::editorEvent(event, model, option, index);
0183 }
0184 
0185 QSize StyledCalendarDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
0186 {
0187     QSize size = QStyledItemDelegate::sizeHint(option, index);
0188     // Without this adjustment toplevel resource folders get a slightly greater height,
0189     // which looks silly and breaks the toolbutton position.
0190     size.setHeight(qApp->style()->pixelMetric(QStyle::PM_SmallIconSize) + 4);
0191     return size;
0192 }
0193 
0194 #include "moc_calendardelegate.cpp"