File indexing completed on 2025-01-05 04:59:46

0001 /*
0002  * SPDX-FileCopyrightText: 2014-2016 Kevin Ottens <ervin@kde.org>
0003  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004  */
0005 
0006 
0007 #include "itemdelegate.h"
0008 
0009 #include <QApplication>
0010 #include <QPainter>
0011 #include <QStyleOptionViewItem>
0012 
0013 #include <KLocalizedString>
0014 
0015 #include "domain/task.h"
0016 #include "presentation/querytreemodelbase.h"
0017 #include "utils/datetime.h"
0018 
0019 using namespace Widgets;
0020 
0021 ItemDelegate::ItemDelegate(QObject *parent)
0022     : QStyledItemDelegate(parent)
0023 {
0024 }
0025 
0026 QSize ItemDelegate::sizeHint(const QStyleOptionViewItem &option,
0027                              const QModelIndex &index) const
0028 {
0029     QStyleOptionViewItem opt = option;
0030     initStyleOption(&opt, index);
0031     // Make sure they all get the height needed for a check indicator
0032     opt.features = QStyleOptionViewItem::HasCheckIndicator;
0033     // and for a date on the right
0034     opt.text += ' ' + QLocale().dateFormat(QLocale::ShortFormat).toUpper() + ' ';
0035     QSize sz = QStyledItemDelegate::sizeHint(opt, index);
0036     const auto projectInfo = index.data(Presentation::QueryTreeModelBase::ProjectRole);
0037     const auto dataSourceInfo = index.data(Presentation::QueryTreeModelBase::DataSourceRole);
0038     const auto contextListInfo = index.data(Presentation::QueryTreeModelBase::ContextListRole);
0039     const auto hasAdditionalInfo = projectInfo.isValid() || dataSourceInfo.isValid() || contextListInfo.isValid();
0040     if (hasAdditionalInfo)
0041         sz.rheight() += opt.fontMetrics.height();
0042     return sz;
0043 }
0044 
0045 void ItemDelegate::paint(QPainter *painter,
0046                          const QStyleOptionViewItem &option,
0047                          const QModelIndex &index) const
0048 {
0049     const auto data = index.data(Presentation::QueryTreeModelBase::ObjectRole);
0050     auto task = data.value<Domain::Task::Ptr>();
0051 
0052     auto opt = QStyleOptionViewItem(option);
0053     initStyleOption(&opt, index);
0054     const auto widget = opt.widget;
0055     const auto style = widget ? widget->style() : QApplication::style();
0056 
0057     const auto isDone = task ? task->isDone() : false;
0058     const auto isEnabled = (opt.state & QStyle::State_Enabled);
0059     const auto isActive = (opt.state & QStyle::State_Active);
0060     const auto isSelected = (opt.state & QStyle::State_Selected);
0061     const auto isEditing = (opt.state & QStyle::State_Editing);
0062 
0063     const auto startDate = task ? task->startDate() : QDate();
0064     const auto dueDate = task ? task->dueDate() : QDate();
0065     const auto projectInfo = index.data(Presentation::QueryTreeModelBase::ProjectRole);
0066     const auto dataSourceInfo = index.data(Presentation::QueryTreeModelBase::DataSourceRole);
0067     const auto contextListInfo = index.data(Presentation::QueryTreeModelBase::ContextListRole);
0068     const auto hasAdditionalInfo = projectInfo.isValid() || dataSourceInfo.isValid() || contextListInfo.isValid();
0069 
0070     const auto currentDate = Utils::DateTime::currentDate();
0071     const auto onStartDate = startDate.isValid() && startDate <= currentDate;
0072     const auto pastDueDate = dueDate.isValid() && dueDate < currentDate;
0073     const auto onDueDate = dueDate.isValid() && dueDate == currentDate;
0074 
0075     const auto baseFont = opt.font;
0076     const auto summaryFont = [=] {
0077         auto font = baseFont;
0078         font.setStrikeOut(isDone);
0079         font.setBold(!isDone && (onStartDate || onDueDate || pastDueDate));
0080         return font;
0081     }();
0082     const auto summaryMetrics = QFontMetrics(summaryFont);
0083 
0084     const auto colorGroup = (isEnabled && !isActive) ? QPalette::Inactive
0085                           : isEnabled ? QPalette::Normal
0086                           : QPalette::Disabled;
0087     const auto colorRole = (isSelected && !isEditing) ? QPalette::HighlightedText : QPalette::Text;
0088 
0089     const auto baseColor = opt.palette.color(colorGroup, colorRole);
0090     const auto summaryColor = isDone ? baseColor
0091                             : pastDueDate ? QColor(Qt::red)
0092                             : onDueDate ? QColor("orange")
0093                             : baseColor;
0094 
0095     const auto summaryText = opt.text;
0096     const auto dueDateText = dueDate.isValid() ? QLocale().toString(dueDate, QLocale::ShortFormat)
0097                                                : QString();
0098 
0099     const auto textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, widget) + 1;
0100     const auto dueDateWidth = dueDate.isValid() ? (summaryMetrics.horizontalAdvance(dueDateText) + 2 * textMargin) : 0;
0101 
0102 
0103     const auto checkRect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &opt, widget);
0104     const auto textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &opt, widget)
0105                              .adjusted(textMargin, 0, - textMargin, 0);
0106     auto summaryRect = textRect.adjusted(0, 0, -dueDateWidth, 0);
0107     if (hasAdditionalInfo)
0108         summaryRect.setHeight(summaryRect.height() - opt.fontMetrics.height());
0109     auto dueDateRect = textRect.adjusted(textRect.width() - dueDateWidth, 0, 0, 0);
0110     dueDateRect.setHeight(summaryRect.height());
0111 
0112     const auto additionalInfoRect = QRect(textRect.x(), summaryRect.bottom(), textRect.width(), textRect.height() - summaryRect.height());
0113 
0114     // Draw background
0115     style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, widget);
0116 
0117     // Draw the check box
0118     if (task) {
0119         auto checkOption = opt;
0120         checkOption.rect = checkRect;
0121         checkOption.state = option.state & ~QStyle::State_HasFocus;
0122         checkOption.state |= isDone ? QStyle::State_On : QStyle::State_Off;
0123         style->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &checkOption, painter, widget);
0124     }
0125 
0126     // Draw the summary
0127     if (!summaryText.isEmpty()) {
0128         painter->setPen(summaryColor);
0129         painter->setFont(summaryFont);
0130         painter->drawText(summaryRect, Qt::AlignVCenter,
0131                           summaryMetrics.elidedText(summaryText, Qt::ElideRight, summaryRect.width()));
0132     }
0133 
0134     // Draw the due date
0135     if (!dueDateText.isEmpty()) {
0136         painter->drawText(dueDateRect, Qt::AlignCenter, dueDateText);
0137     }
0138 
0139     // Draw the second line
0140     if (hasAdditionalInfo) {
0141         const auto additionalInfo = projectInfo.isValid() && !projectInfo.toString().isEmpty() ? i18n("Project: %1", projectInfo.toString())
0142                                   : dataSourceInfo.isValid() ? dataSourceInfo.toString()
0143                                   : i18n("Inbox");
0144 
0145         QFont additionalInfoFont = baseFont;
0146         additionalInfoFont.setItalic(true);
0147         additionalInfoFont.setPointSize(additionalInfoFont.pointSize() - 1);
0148         painter->setFont(additionalInfoFont);
0149         painter->drawText(additionalInfoRect, Qt::AlignLeft, additionalInfo);
0150     }
0151 }
0152 
0153 QWidget *ItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
0154 {
0155     m_editingState = EditingState::JustCreatedEditor;
0156     return QStyledItemDelegate::createEditor(parent, option, index);
0157 }
0158 
0159 void ItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
0160 {
0161     m_editingState = EditingState::NotEditing;
0162     QStyledItemDelegate::setModelData(editor, model, index);
0163 }
0164 
0165 void ItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
0166 {
0167     // The first call gets through (initial value)
0168     // And later call is rejected, the user's own value takes precedence
0169     if (m_editingState == EditingState::JustCreatedEditor) {
0170         m_editingState = EditingState::Editing;
0171         QStyledItemDelegate::setEditorData(editor, index);
0172     }
0173 }
0174 
0175 #include "moc_itemdelegate.cpp"