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"