File indexing completed on 2024-11-17 04:42:41

0001 /*
0002   This file is part of KOrganizer.
0003 
0004   SPDX-FileCopyrightText: 2008 Thomas Thrainer <tom_t@gmx.at>
0005 
0006   SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
0007 */
0008 
0009 #include "tododelegates.h"
0010 #include "todoviewview.h"
0011 
0012 #include <Akonadi/TagSelectionComboBox>
0013 #include <Akonadi/TodoModel>
0014 #include <CalendarSupport/CategoryHierarchyReader>
0015 
0016 #include <KCalendarCore/CalFilter>
0017 
0018 #include <KDateComboBox>
0019 
0020 #include <QApplication>
0021 #include <QPainter>
0022 #include <QTextDocument>
0023 #include <QToolTip>
0024 
0025 // ---------------- COMPLETION DELEGATE --------------------------
0026 // ---------------------------------------------------------------
0027 
0028 TodoCompleteDelegate::TodoCompleteDelegate(QObject *parent)
0029     : QStyledItemDelegate(parent)
0030 {
0031 }
0032 
0033 void TodoCompleteDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
0034 {
0035     QStyleOptionViewItem opt = option;
0036     initStyleOption(&opt, index);
0037 
0038     QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
0039     style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter);
0040 
0041     if (index.data(Qt::EditRole).toInt() > 0) {
0042         bool isEditing = false;
0043         auto view = qobject_cast<TodoViewView *>(parent());
0044         if (view) {
0045             isEditing = view->isEditing(index);
0046         }
0047 
0048         // TODO QTreeView does not set State_Editing. Qt task id 205051
0049         // should be fixed with Qt 4.5, but wasn't. According to the
0050         // task tracker the fix arrives in "Some future release".
0051         if (!(opt.state & QStyle::State_Editing) && !isEditing) {
0052             QStyleOptionProgressBar pbOption;
0053             pbOption.QStyleOption::operator=(option);
0054             initStyleOptionProgressBar(&pbOption, index);
0055 
0056             style->drawControl(QStyle::CE_ProgressBar, &pbOption, painter);
0057         }
0058     }
0059 }
0060 
0061 QSize TodoCompleteDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
0062 {
0063     QStyleOptionViewItem opt = option;
0064     initStyleOption(&opt, index);
0065 
0066     QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
0067 
0068     QStyleOptionProgressBar pbOption;
0069     pbOption.QStyleOption::operator=(option);
0070     initStyleOptionProgressBar(&pbOption, index);
0071 
0072     return style->sizeFromContents(QStyle::CT_ProgressBar, &pbOption, QSize(), opt.widget);
0073 }
0074 
0075 void TodoCompleteDelegate::initStyleOptionProgressBar(QStyleOptionProgressBar *option, const QModelIndex &index) const
0076 {
0077     option->rect.adjust(0, 1, 0, -1);
0078     option->maximum = 100;
0079     option->minimum = 0;
0080     option->progress = index.data().toInt();
0081     option->text = index.data().toString() + QChar::fromLatin1('%');
0082     option->textAlignment = Qt::AlignCenter;
0083     option->textVisible = true;
0084     option->state |= QStyle::State_Horizontal;
0085 }
0086 
0087 QWidget *TodoCompleteDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
0088 {
0089     Q_UNUSED(option)
0090     Q_UNUSED(index)
0091 
0092     auto slider = new TodoCompleteSlider(parent);
0093 
0094     slider->setRange(0, 100);
0095     slider->setOrientation(Qt::Horizontal);
0096 
0097     return slider;
0098 }
0099 
0100 void TodoCompleteDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
0101 {
0102     auto slider = static_cast<QSlider *>(editor);
0103 
0104     slider->setValue(index.data(Qt::EditRole).toInt());
0105 }
0106 
0107 void TodoCompleteDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
0108 {
0109     auto slider = static_cast<QSlider *>(editor);
0110 
0111     model->setData(index, slider->value());
0112 }
0113 
0114 void TodoCompleteDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
0115 {
0116     Q_UNUSED(index)
0117 
0118     editor->setGeometry(option.rect);
0119 }
0120 
0121 TodoCompleteSlider::TodoCompleteSlider(QWidget *parent)
0122     : QSlider(parent)
0123 {
0124     connect(this, &TodoCompleteSlider::valueChanged, this, &TodoCompleteSlider::updateTip);
0125 }
0126 
0127 void TodoCompleteSlider::updateTip(int value)
0128 {
0129     QPoint p;
0130     p.setY(height() / 2);
0131     p.setX(style()->sliderPositionFromValue(minimum(), maximum(), value, width()));
0132 
0133     const QString text = QStringLiteral("%1%").arg(value);
0134     QToolTip::showText(mapToGlobal(p), text, this);
0135 }
0136 
0137 // ---------------- PRIORITY DELEGATE ----------------------------
0138 // ---------------------------------------------------------------
0139 
0140 TodoPriorityDelegate::TodoPriorityDelegate(QObject *parent)
0141     : QStyledItemDelegate(parent)
0142 {
0143 }
0144 
0145 QWidget *TodoPriorityDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
0146 {
0147     Q_UNUSED(option)
0148     Q_UNUSED(index)
0149 
0150     auto combo = new QComboBox(parent);
0151 
0152     combo->addItem(i18nc("@action:inmenu Unspecified priority", "unspecified"));
0153     combo->addItem(i18nc("@action:inmenu highest priority", "1 (highest)"));
0154     combo->addItem(i18nc("@action:inmenu", "2"));
0155     combo->addItem(i18nc("@action:inmenu", "3"));
0156     combo->addItem(i18nc("@action:inmenu", "4"));
0157     combo->addItem(i18nc("@action:inmenu medium priority", "5 (medium)"));
0158     combo->addItem(i18nc("@action:inmenu", "6"));
0159     combo->addItem(i18nc("@action:inmenu", "7"));
0160     combo->addItem(i18nc("@action:inmenu", "8"));
0161     combo->addItem(i18nc("@action:inmenu lowest priority", "9 (lowest)"));
0162 
0163     return combo;
0164 }
0165 
0166 void TodoPriorityDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
0167 {
0168     auto combo = static_cast<QComboBox *>(editor);
0169 
0170     combo->setCurrentIndex(index.data(Qt::EditRole).toInt());
0171 }
0172 
0173 void TodoPriorityDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
0174 {
0175     auto combo = static_cast<QComboBox *>(editor);
0176 
0177     model->setData(index, combo->currentIndex());
0178 }
0179 
0180 void TodoPriorityDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
0181 {
0182     Q_UNUSED(index)
0183 
0184     editor->setGeometry(option.rect);
0185 }
0186 
0187 // ---------------- DUE DATE DELEGATE ----------------------------
0188 // ---------------------------------------------------------------
0189 
0190 TodoDueDateDelegate::TodoDueDateDelegate(QObject *parent)
0191     : QStyledItemDelegate(parent)
0192 {
0193 }
0194 
0195 QWidget *TodoDueDateDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
0196 {
0197     Q_UNUSED(option)
0198     Q_UNUSED(index)
0199 
0200     auto dateEdit = new KDateComboBox(parent);
0201 
0202     return dateEdit;
0203 }
0204 
0205 void TodoDueDateDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
0206 {
0207     auto dateEdit = static_cast<KDateComboBox *>(editor);
0208 
0209     dateEdit->setDate(index.data(Qt::EditRole).toDate());
0210 }
0211 
0212 void TodoDueDateDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
0213 {
0214     auto dateEdit = static_cast<KDateComboBox *>(editor);
0215 
0216     model->setData(index, dateEdit->date());
0217 }
0218 
0219 void TodoDueDateDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
0220 {
0221     Q_UNUSED(index)
0222     editor->setGeometry(QStyle::alignedRect(QApplication::layoutDirection(), Qt::AlignCenter, editor->size(), option.rect));
0223 }
0224 
0225 // ---------------- CATEGORIES DELEGATE --------------------------
0226 // ---------------------------------------------------------------
0227 
0228 TodoCategoriesDelegate::TodoCategoriesDelegate(QObject *parent)
0229     : QStyledItemDelegate(parent)
0230 {
0231 }
0232 
0233 QWidget *TodoCategoriesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
0234 {
0235     Q_UNUSED(option)
0236     Q_UNUSED(index)
0237 
0238     return new Akonadi::TagSelectionComboBox(parent);
0239 }
0240 
0241 void TodoCategoriesDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
0242 {
0243     auto combo = static_cast<Akonadi::TagSelectionComboBox *>(editor);
0244     combo->setSelection(index.data(Qt::EditRole).toStringList());
0245 }
0246 
0247 void TodoCategoriesDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
0248 {
0249     auto combo = static_cast<Akonadi::TagSelectionComboBox *>(editor);
0250     model->setData(index, combo->selectionNames());
0251 }
0252 
0253 void TodoCategoriesDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
0254 {
0255     Q_UNUSED(index)
0256 
0257     editor->setGeometry(option.rect);
0258 }
0259 
0260 // ---------------- RICH TEXT DELEGATE ---------------------------
0261 // ---------------------------------------------------------------
0262 
0263 TodoRichTextDelegate::TodoRichTextDelegate(QObject *parent)
0264     : QStyledItemDelegate(parent)
0265 {
0266     m_textDoc = new QTextDocument(this);
0267 }
0268 
0269 void TodoRichTextDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
0270 {
0271     if (index.data(Akonadi::TodoModel::IsRichTextRole).toBool()) {
0272         QStyleOptionViewItem opt = option;
0273         initStyleOption(&opt, index);
0274 
0275         const QWidget *widget = opt.widget;
0276         QStyle *style = widget ? widget->style() : QApplication::style();
0277 
0278         QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &opt, widget);
0279 
0280         // draw the item without text
0281         opt.text.clear();
0282         style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
0283 
0284         // draw the text (rich text)
0285         QPalette::ColorGroup cg = (opt.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled;
0286         if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) {
0287             cg = QPalette::Inactive;
0288         }
0289 
0290         if (opt.state & QStyle::State_Selected) {
0291             painter->setPen(QPen(opt.palette.brush(cg, QPalette::HighlightedText), 0));
0292         } else {
0293             painter->setPen(QPen(opt.palette.brush(cg, QPalette::Text), 0));
0294         }
0295         if (opt.state & QStyle::State_Editing) {
0296             painter->setPen(QPen(opt.palette.brush(cg, QPalette::Text), 0));
0297             painter->drawRect(textRect.adjusted(0, 0, -1, -1));
0298         }
0299 
0300         m_textDoc->setHtml(index.data().toString());
0301 
0302         painter->save();
0303         painter->translate(textRect.topLeft());
0304 
0305         QRect tmpRect = textRect;
0306         tmpRect.moveTo(0, 0);
0307         m_textDoc->setTextWidth(tmpRect.width());
0308         m_textDoc->drawContents(painter, tmpRect);
0309 
0310         painter->restore();
0311     } else {
0312         // align the text at top so that when it has more than two lines
0313         // it will just cut the extra lines out instead of aligning centered vertically
0314         QStyleOptionViewItem copy = option;
0315         copy.displayAlignment = Qt::AlignLeft | Qt::AlignTop;
0316         QStyledItemDelegate::paint(painter, copy, index);
0317     }
0318 }
0319 
0320 QSize TodoRichTextDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
0321 {
0322     QSize ret = QStyledItemDelegate::sizeHint(option, index);
0323     if (index.data(Akonadi::TodoModel::IsRichTextRole).toBool()) {
0324         m_textDoc->setHtml(index.data().toString());
0325         ret = ret.expandedTo(m_textDoc->size().toSize());
0326     }
0327     // limit height to max. 2 lines
0328     // TODO add graphical hint when truncating! make configurable height?
0329     if (ret.height() > option.fontMetrics.height() * 2) {
0330         ret.setHeight(option.fontMetrics.height() * 2);
0331     }
0332 
0333     // This row might not have a checkbox, so give it more height so it appears the same size as other rows.
0334     const int checkboxHeight = QApplication::style()->sizeFromContents(QStyle::CT_CheckBox, &option, QSize()).height();
0335     return {ret.width(), qMax(ret.height(), checkboxHeight)};
0336 }
0337 
0338 #include "moc_tododelegates.cpp"