File indexing completed on 2024-12-15 04:54:42
0001 /****************************************************************************** 0002 * 0003 * SPDX-FileCopyrightText: 2008 Szymon Tomasz Stefanek <pragma@kvirc.net> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 * 0007 *******************************************************************************/ 0008 0009 #include "core/themedelegate.h" 0010 #include "core/groupheaderitem.h" 0011 #include "core/messageitem.h" 0012 #include "messagelistsettings.h" 0013 0014 #include "MessageCore/MessageCoreSettings" 0015 #include "MessageCore/StringUtil" 0016 0017 #include <KColorScheme> 0018 #include <QAbstractItemView> 0019 #include <QFont> 0020 #include <QFontDatabase> 0021 #include <QFontMetrics> 0022 #include <QLinearGradient> 0023 #include <QPainter> 0024 #include <QPixmap> 0025 #include <QStyle> 0026 0027 using namespace MessageList::Core; 0028 0029 static const int gGroupHeaderOuterVerticalMargin = 1; 0030 static const int gGroupHeaderOuterHorizontalMargin = 1; 0031 static const int gGroupHeaderInnerVerticalMargin = 1; 0032 static const int gGroupHeaderInnerHorizontalMargin = 1; 0033 static const int gMessageVerticalMargin = 2; 0034 static const int gMessageHorizontalMargin = 2; 0035 static const int gHorizontalItemSpacing = 2; 0036 0037 ThemeDelegate::ThemeDelegate(QAbstractItemView *parent) 0038 : QStyledItemDelegate(parent) 0039 , mItemView(parent) 0040 { 0041 } 0042 0043 ThemeDelegate::~ThemeDelegate() = default; 0044 0045 void ThemeDelegate::setTheme(const Theme *theme) 0046 { 0047 mTheme = theme; 0048 0049 if (!mTheme) { 0050 return; // hum 0051 } 0052 0053 // Rebuild the group header background color cache 0054 switch (mTheme->groupHeaderBackgroundMode()) { 0055 case Theme::Transparent: 0056 mGroupHeaderBackgroundColor = QColor(); // invalid 0057 break; 0058 case Theme::CustomColor: 0059 mGroupHeaderBackgroundColor = mTheme->groupHeaderBackgroundColor(); 0060 break; 0061 case Theme::AutoColor: { 0062 QPalette pal = mItemView->palette(); 0063 QColor txt = pal.color(QPalette::Normal, QPalette::Text); 0064 QColor bck = pal.color(QPalette::Normal, QPalette::Base); 0065 mGroupHeaderBackgroundColor = QColor((txt.red() + (bck.red() * 3)) / 4, (txt.green() + (bck.green() * 3)) / 4, (txt.blue() + (bck.blue() * 3)) / 4); 0066 break; 0067 } 0068 } 0069 0070 generalFontChanged(); 0071 0072 mItemView->reset(); 0073 } 0074 0075 enum FontType { 0076 Normal, 0077 Bold, 0078 Italic, 0079 BoldItalic, 0080 0081 FontTypesCount, 0082 }; 0083 0084 static QFont sFontCache[FontTypesCount]; 0085 static QFontMetrics sFontMetricsCache[FontTypesCount] = {QFontMetrics(QFont()), QFontMetrics(QFont()), QFontMetrics(QFont()), QFontMetrics(QFont())}; 0086 static int sFontHeightCache = 0; 0087 0088 static inline const QFontMetrics &cachedFontMetrics(const Theme::ContentItem *ci) 0089 { 0090 return (!ci->isBold() && !ci->isItalic()) ? sFontMetricsCache[Normal] 0091 : (ci->isBold() && !ci->isItalic()) ? sFontMetricsCache[Bold] 0092 : (!ci->isBold() && ci->isItalic()) ? sFontMetricsCache[Italic] 0093 : sFontMetricsCache[BoldItalic]; 0094 } 0095 0096 static inline const QFont &cachedFont(const Theme::ContentItem *ci) 0097 { 0098 return (!ci->isBold() && !ci->isItalic()) ? sFontCache[Normal] 0099 : (ci->isBold() && !ci->isItalic()) ? sFontCache[Bold] 0100 : (!ci->isBold() && ci->isItalic()) ? sFontCache[Italic] 0101 : sFontCache[BoldItalic]; 0102 } 0103 0104 static inline const QFont &cachedFont(const Theme::ContentItem *ci, const Item *i) 0105 { 0106 if (i->type() != Item::Message) { 0107 return cachedFont(ci); 0108 } 0109 0110 const auto mi = static_cast<const MessageItem *>(i); 0111 const bool bold = ci->isBold() || mi->isBold(); 0112 const bool italic = ci->isItalic() || mi->isItalic(); 0113 return (!bold && !italic) ? sFontCache[Normal] : (bold && !italic) ? sFontCache[Bold] : (!bold && italic) ? sFontCache[Italic] : sFontCache[BoldItalic]; 0114 } 0115 0116 static inline void paint_right_aligned_elided_text(const QString &text, 0117 Theme::ContentItem *ci, 0118 QPainter *painter, 0119 int &left, 0120 int top, 0121 int &right, 0122 Qt::LayoutDirection layoutDir, 0123 const QFont &font) 0124 { 0125 painter->setFont(font); 0126 const QFontMetrics &fontMetrics = cachedFontMetrics(ci); 0127 const int w = right - left; 0128 const QString elidedText = fontMetrics.elidedText(text, layoutDir == Qt::LeftToRight ? Qt::ElideLeft : Qt::ElideRight, w); 0129 const QRect rct(left, top, w, sFontHeightCache); 0130 QRect outRct; 0131 0132 if (ci->softenByBlending()) { 0133 qreal oldOpacity = painter->opacity(); 0134 painter->setOpacity(0.6); 0135 painter->drawText(rct, Qt::AlignTop | Qt::AlignRight | Qt::TextSingleLine, elidedText, &outRct); 0136 painter->setOpacity(oldOpacity); 0137 } else { 0138 painter->drawText(rct, Qt::AlignTop | Qt::AlignRight | Qt::TextSingleLine, elidedText, &outRct); 0139 } 0140 if (layoutDir == Qt::LeftToRight) { 0141 right -= outRct.width() + gHorizontalItemSpacing; 0142 } else { 0143 left += outRct.width() + gHorizontalItemSpacing; 0144 } 0145 } 0146 0147 static inline void compute_bounding_rect_for_right_aligned_elided_text(const QString &text, 0148 Theme::ContentItem *ci, 0149 int &left, 0150 int top, 0151 int &right, 0152 QRect &outRect, 0153 Qt::LayoutDirection layoutDir, 0154 const QFont &font) 0155 { 0156 Q_UNUSED(font) 0157 const QFontMetrics &fontMetrics = cachedFontMetrics(ci); 0158 const int w = right - left; 0159 const QString elidedText = fontMetrics.elidedText(text, layoutDir == Qt::LeftToRight ? Qt::ElideLeft : Qt::ElideRight, w); 0160 const QRect rct(left, top, w, sFontHeightCache); 0161 const Qt::AlignmentFlag af = layoutDir == Qt::LeftToRight ? Qt::AlignRight : Qt::AlignLeft; 0162 outRect = fontMetrics.boundingRect(rct, Qt::AlignTop | af | Qt::TextSingleLine, elidedText); 0163 if (layoutDir == Qt::LeftToRight) { 0164 right -= outRect.width() + gHorizontalItemSpacing; 0165 } else { 0166 left += outRect.width() + gHorizontalItemSpacing; 0167 } 0168 } 0169 0170 static inline void paint_left_aligned_elided_text(const QString &text, 0171 Theme::ContentItem *ci, 0172 QPainter *painter, 0173 int &left, 0174 int top, 0175 int &right, 0176 Qt::LayoutDirection layoutDir, 0177 const QFont &font) 0178 { 0179 painter->setFont(font); 0180 const QFontMetrics &fontMetrics = cachedFontMetrics(ci); 0181 const int w = right - left; 0182 const QString elidedText = fontMetrics.elidedText(text, layoutDir == Qt::LeftToRight ? Qt::ElideRight : Qt::ElideLeft, w); 0183 const QRect rct(left, top, w, sFontHeightCache); 0184 QRect outRct; 0185 if (ci->softenByBlending()) { 0186 qreal oldOpacity = painter->opacity(); 0187 painter->setOpacity(0.6); 0188 painter->drawText(rct, Qt::AlignTop | Qt::AlignLeft | Qt::TextSingleLine, elidedText, &outRct); 0189 painter->setOpacity(oldOpacity); 0190 } else { 0191 painter->drawText(rct, Qt::AlignTop | Qt::AlignLeft | Qt::TextSingleLine, elidedText, &outRct); 0192 } 0193 if (layoutDir == Qt::LeftToRight) { 0194 left += outRct.width() + gHorizontalItemSpacing; 0195 } else { 0196 right -= outRct.width() + gHorizontalItemSpacing; 0197 } 0198 } 0199 0200 static inline void compute_bounding_rect_for_left_aligned_elided_text(const QString &text, 0201 Theme::ContentItem *ci, 0202 int &left, 0203 int top, 0204 int &right, 0205 QRect &outRect, 0206 Qt::LayoutDirection layoutDir, 0207 const QFont &font) 0208 { 0209 Q_UNUSED(font) 0210 const QFontMetrics &fontMetrics = cachedFontMetrics(ci); 0211 const int w = right - left; 0212 const QString elidedText = fontMetrics.elidedText(text, layoutDir == Qt::LeftToRight ? Qt::ElideRight : Qt::ElideLeft, w); 0213 const QRect rct(left, top, w, sFontHeightCache); 0214 const Qt::AlignmentFlag af = layoutDir == Qt::LeftToRight ? Qt::AlignLeft : Qt::AlignRight; 0215 outRect = fontMetrics.boundingRect(rct, Qt::AlignTop | af | Qt::TextSingleLine, elidedText); 0216 if (layoutDir == Qt::LeftToRight) { 0217 left += outRect.width() + gHorizontalItemSpacing; 0218 } else { 0219 right -= outRect.width() + gHorizontalItemSpacing; 0220 } 0221 } 0222 0223 static inline const QPixmap *get_read_state_icon(const Theme *theme, Item *item) 0224 { 0225 if (item->status().isQueued()) { 0226 return theme->pixmap(Theme::IconQueued); 0227 } else if (item->status().isSent()) { 0228 return theme->pixmap(Theme::IconSent); 0229 } else if (item->status().isRead()) { 0230 return theme->pixmap(Theme::IconRead); 0231 } else if (!item->status().isRead()) { 0232 return theme->pixmap(Theme::IconUnread); 0233 } else if (item->status().isDeleted()) { 0234 return theme->pixmap(Theme::IconDeleted); 0235 } 0236 0237 // Uhm... should never happen.. but fallback to "read"... 0238 return theme->pixmap(Theme::IconRead); 0239 } 0240 0241 static inline const QPixmap *get_combined_read_replied_state_icon(const Theme *theme, MessageItem *messageItem) 0242 { 0243 if (messageItem->status().isReplied()) { 0244 if (messageItem->status().isForwarded()) { 0245 return theme->pixmap(Theme::IconRepliedAndForwarded); 0246 } 0247 return theme->pixmap(Theme::IconReplied); 0248 } 0249 if (messageItem->status().isForwarded()) { 0250 return theme->pixmap(Theme::IconForwarded); 0251 } 0252 0253 return get_read_state_icon(theme, messageItem); 0254 } 0255 0256 static inline const QPixmap *get_encryption_state_icon(const Theme *theme, MessageItem *messageItem, bool *treatAsEnabled) 0257 { 0258 switch (messageItem->encryptionState()) { 0259 case MessageItem::FullyEncrypted: 0260 *treatAsEnabled = true; 0261 return theme->pixmap(Theme::IconFullyEncrypted); 0262 case MessageItem::PartiallyEncrypted: 0263 *treatAsEnabled = true; 0264 return theme->pixmap(Theme::IconPartiallyEncrypted); 0265 case MessageItem::EncryptionStateUnknown: 0266 *treatAsEnabled = false; 0267 return theme->pixmap(Theme::IconUndefinedEncrypted); 0268 case MessageItem::NotEncrypted: 0269 *treatAsEnabled = false; 0270 return theme->pixmap(Theme::IconNotEncrypted); 0271 default: 0272 // should never happen 0273 Q_ASSERT(false); 0274 break; 0275 } 0276 0277 *treatAsEnabled = false; 0278 return theme->pixmap(Theme::IconUndefinedEncrypted); 0279 } 0280 0281 static inline const QPixmap *get_signature_state_icon(const Theme *theme, MessageItem *messageItem, bool *treatAsEnabled) 0282 { 0283 switch (messageItem->signatureState()) { 0284 case MessageItem::FullySigned: 0285 *treatAsEnabled = true; 0286 return theme->pixmap(Theme::IconFullySigned); 0287 case MessageItem::PartiallySigned: 0288 *treatAsEnabled = true; 0289 return theme->pixmap(Theme::IconPartiallySigned); 0290 case MessageItem::SignatureStateUnknown: 0291 *treatAsEnabled = false; 0292 return theme->pixmap(Theme::IconUndefinedSigned); 0293 case MessageItem::NotSigned: 0294 *treatAsEnabled = false; 0295 return theme->pixmap(Theme::IconNotSigned); 0296 default: 0297 // should never happen 0298 Q_ASSERT(false); 0299 break; 0300 } 0301 0302 *treatAsEnabled = false; 0303 return theme->pixmap(Theme::IconUndefinedSigned); 0304 } 0305 0306 static inline const QPixmap *get_replied_state_icon(const Theme *theme, MessageItem *messageItem) 0307 { 0308 if (messageItem->status().isReplied()) { 0309 if (messageItem->status().isForwarded()) { 0310 return theme->pixmap(Theme::IconRepliedAndForwarded); 0311 } 0312 return theme->pixmap(Theme::IconReplied); 0313 } 0314 if (messageItem->status().isForwarded()) { 0315 return theme->pixmap(Theme::IconForwarded); 0316 } 0317 0318 return nullptr; 0319 } 0320 0321 static inline const QPixmap *get_spam_ham_state_icon(const Theme *theme, MessageItem *messageItem) 0322 { 0323 if (messageItem->status().isSpam()) { 0324 return theme->pixmap(Theme::IconSpam); 0325 } else if (messageItem->status().isHam()) { 0326 return theme->pixmap(Theme::IconHam); 0327 } 0328 return nullptr; 0329 } 0330 0331 static inline const QPixmap *get_watched_ignored_state_icon(const Theme *theme, MessageItem *messageItem) 0332 { 0333 if (messageItem->status().isIgnored()) { 0334 return theme->pixmap(Theme::IconIgnored); 0335 } else if (messageItem->status().isWatched()) { 0336 return theme->pixmap(Theme::IconWatched); 0337 } 0338 return nullptr; 0339 } 0340 0341 static inline void paint_vertical_line(QPainter *painter, int &left, int top, int &right, int bottom, bool alignOnRight) 0342 { 0343 if (alignOnRight) { 0344 right -= 1; 0345 if (right < 0) { 0346 return; 0347 } 0348 painter->drawLine(right, top, right, bottom); 0349 right -= 2; 0350 right -= gHorizontalItemSpacing; 0351 } else { 0352 left += 1; 0353 if (left > right) { 0354 return; 0355 } 0356 painter->drawLine(left, top, left, bottom); 0357 left += 2 + gHorizontalItemSpacing; 0358 } 0359 } 0360 0361 static inline void compute_bounding_rect_for_vertical_line(int &left, int top, int &right, int bottom, QRect &outRect, bool alignOnRight) 0362 { 0363 if (alignOnRight) { 0364 right -= 3; 0365 outRect = QRect(right, top, 3, bottom - top); 0366 right -= gHorizontalItemSpacing; 0367 } else { 0368 outRect = QRect(left, top, 3, bottom - top); 0369 left += 3 + gHorizontalItemSpacing; 0370 } 0371 } 0372 0373 static inline void paint_horizontal_spacer(int &left, int, int &right, int, bool alignOnRight) 0374 { 0375 if (alignOnRight) { 0376 right -= 3 + gHorizontalItemSpacing; 0377 } else { 0378 left += 3 + gHorizontalItemSpacing; 0379 } 0380 } 0381 0382 static inline void compute_bounding_rect_for_horizontal_spacer(int &left, int top, int &right, int bottom, QRect &outRect, bool alignOnRight) 0383 { 0384 if (alignOnRight) { 0385 right -= 3; 0386 outRect = QRect(right, top, 3, bottom - top); 0387 right -= gHorizontalItemSpacing; 0388 } else { 0389 outRect = QRect(left, top, 3, bottom - top); 0390 left += 3 + gHorizontalItemSpacing; 0391 } 0392 } 0393 0394 static inline void 0395 paint_permanent_icon(const QPixmap *pix, Theme::ContentItem *, QPainter *painter, int &left, int top, int &right, bool alignOnRight, int iconSize) 0396 { 0397 if (alignOnRight) { 0398 right -= iconSize; // this icon is always present 0399 if (right < 0) { 0400 return; 0401 } 0402 painter->drawPixmap(right, top, iconSize, iconSize, *pix); 0403 right -= gHorizontalItemSpacing; 0404 } else { 0405 if (left > (right - iconSize)) { 0406 return; 0407 } 0408 painter->drawPixmap(left, top, iconSize, iconSize, *pix); 0409 left += iconSize + gHorizontalItemSpacing; 0410 } 0411 } 0412 0413 static inline void 0414 compute_bounding_rect_for_permanent_icon(Theme::ContentItem *, int &left, int top, int &right, QRect &outRect, bool alignOnRight, int iconSize) 0415 { 0416 if (alignOnRight) { 0417 right -= iconSize; // this icon is always present 0418 outRect = QRect(right, top, iconSize, iconSize); 0419 right -= gHorizontalItemSpacing; 0420 } else { 0421 outRect = QRect(left, top, iconSize, iconSize); 0422 left += iconSize + gHorizontalItemSpacing; 0423 } 0424 } 0425 0426 static inline void paint_boolean_state_icon(bool enabled, 0427 const QPixmap *pix, 0428 Theme::ContentItem *ci, 0429 QPainter *painter, 0430 int &left, 0431 int top, 0432 int &right, 0433 bool alignOnRight, 0434 int iconSize) 0435 { 0436 if (enabled) { 0437 paint_permanent_icon(pix, ci, painter, left, top, right, alignOnRight, iconSize); 0438 return; 0439 } 0440 0441 // off -> icon disabled 0442 if (ci->hideWhenDisabled()) { 0443 return; // doesn't even take space 0444 } 0445 0446 if (ci->softenByBlendingWhenDisabled()) { 0447 // still paint, but very soft 0448 qreal oldOpacity = painter->opacity(); 0449 painter->setOpacity(0.1); 0450 paint_permanent_icon(pix, ci, painter, left, top, right, alignOnRight, iconSize); 0451 painter->setOpacity(oldOpacity); 0452 return; 0453 } 0454 0455 // just takes space 0456 if (alignOnRight) { 0457 right -= iconSize + gHorizontalItemSpacing; 0458 } else { 0459 left += iconSize + gHorizontalItemSpacing; 0460 } 0461 } 0462 0463 static inline void compute_bounding_rect_for_boolean_state_icon(bool enabled, 0464 Theme::ContentItem *ci, 0465 int &left, 0466 int top, 0467 int &right, 0468 QRect &outRect, 0469 bool alignOnRight, 0470 int iconSize) 0471 { 0472 if ((!enabled) && ci->hideWhenDisabled()) { 0473 outRect = QRect(); 0474 return; // doesn't even take space 0475 } 0476 0477 compute_bounding_rect_for_permanent_icon(ci, left, top, right, outRect, alignOnRight, iconSize); 0478 } 0479 0480 static inline void paint_tag_list(const QList<MessageItem::Tag *> &tagList, QPainter *painter, int &left, int top, int &right, bool alignOnRight, int iconSize) 0481 { 0482 if (alignOnRight) { 0483 for (const MessageItem::Tag *tag : tagList) { 0484 right -= iconSize; // this icon is always present 0485 if (right < 0) { 0486 return; 0487 } 0488 painter->drawPixmap(right, top, iconSize, iconSize, tag->pixmap()); 0489 right -= gHorizontalItemSpacing; 0490 } 0491 } else { 0492 for (const MessageItem::Tag *tag : tagList) { 0493 if (left > right - iconSize) { 0494 return; 0495 } 0496 painter->drawPixmap(left, top, iconSize, iconSize, tag->pixmap()); 0497 left += iconSize + gHorizontalItemSpacing; 0498 } 0499 } 0500 } 0501 0502 static inline void 0503 compute_bounding_rect_for_tag_list(const QList<MessageItem::Tag *> &tagList, int &left, int top, int &right, QRect &outRect, bool alignOnRight, int iconSize) 0504 { 0505 int width = tagList.count() * (iconSize + gHorizontalItemSpacing); 0506 if (alignOnRight) { 0507 right -= width; 0508 outRect = QRect(right, top, width, iconSize); 0509 } else { 0510 outRect = QRect(left, top, width, iconSize); 0511 left += width; 0512 } 0513 } 0514 0515 static inline void compute_size_hint_for_item(Theme::ContentItem *ci, int &maxh, int &totalw, int iconSize, const Item *item) 0516 { 0517 Q_UNUSED(item) 0518 if (ci->displaysText()) { 0519 if (sFontHeightCache > maxh) { 0520 maxh = sFontHeightCache; 0521 } 0522 totalw += ci->displaysLongText() ? 128 : 64; 0523 return; 0524 } 0525 0526 if (ci->isIcon()) { 0527 totalw += iconSize + gHorizontalItemSpacing; 0528 if (maxh < iconSize) { 0529 maxh = iconSize; 0530 } 0531 return; 0532 } 0533 0534 if (ci->isSpacer()) { 0535 if (18 > maxh) { 0536 maxh = 18; 0537 } 0538 totalw += 3 + gHorizontalItemSpacing; 0539 return; 0540 } 0541 0542 // should never be reached 0543 if (18 > maxh) { 0544 maxh = 18; 0545 } 0546 totalw += gHorizontalItemSpacing; 0547 } 0548 0549 static inline QSize compute_size_hint_for_row(const Theme::Row *r, int iconSize, const Item *item) 0550 { 0551 int maxh = 8; // at least 8 pixels for a pixmap 0552 int totalw = 0; 0553 0554 // right aligned stuff first 0555 auto items = r->rightItems(); 0556 for (const auto it : std::as_const(items)) { 0557 compute_size_hint_for_item(const_cast<Theme::ContentItem *>(it), maxh, totalw, iconSize, item); 0558 } 0559 0560 // then left aligned stuff 0561 items = r->leftItems(); 0562 for (const auto it : std::as_const(items)) { 0563 compute_size_hint_for_item(const_cast<Theme::ContentItem *>(it), maxh, totalw, iconSize, item); 0564 } 0565 0566 return {totalw, maxh}; 0567 } 0568 0569 void ThemeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const 0570 { 0571 if (!index.isValid()) { 0572 return; // bleah 0573 } 0574 0575 Item *item = itemFromIndex(index); 0576 if (!item) { 0577 return; // hm... 0578 } 0579 0580 QStyleOptionViewItem opt = option; 0581 initStyleOption(&opt, index); 0582 0583 opt.text.clear(); // draw no text for me, please.. I'll do it in a while 0584 0585 // Set background color of control if necessary 0586 if (item->type() == Item::Message) { 0587 auto msgItem = static_cast<MessageItem *>(item); 0588 if (msgItem->backgroundColor().isValid()) { 0589 opt.backgroundBrush = QBrush(msgItem->backgroundColor()); 0590 } 0591 } 0592 0593 QStyle *style = mItemView->style(); 0594 style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, mItemView); 0595 0596 if (!mTheme) { 0597 return; // hm hm... 0598 } 0599 0600 const Theme::Column *skcolumn = mTheme->column(index.column()); 0601 if (!skcolumn) { 0602 return; // bleah 0603 } 0604 0605 const QList<MessageList::Core::Theme::Row *> *rows; 0606 0607 MessageItem *messageItem = nullptr; 0608 GroupHeaderItem *groupHeaderItem = nullptr; 0609 0610 int top = opt.rect.top(); 0611 int right = opt.rect.left() + opt.rect.width(); // don't use opt.rect.right() since it's screwed 0612 int left = opt.rect.left(); 0613 0614 // Storing the changed members one by one is faster than saving the painter state 0615 QFont oldFont = painter->font(); 0616 QPen oldPen = painter->pen(); 0617 qreal oldOpacity = painter->opacity(); 0618 0619 QPen defaultPen; 0620 bool usingNonDefaultTextColor = false; 0621 0622 switch (item->type()) { 0623 case Item::Message: 0624 rows = &(skcolumn->messageRows()); 0625 messageItem = static_cast<MessageItem *>(item); 0626 0627 if ((!(opt.state & QStyle::State_Enabled)) || messageItem->aboutToBeRemoved() || (!messageItem->isValid())) { 0628 painter->setOpacity(0.5); 0629 defaultPen = QPen(opt.palette.brush(QPalette::Disabled, QPalette::Text), 0); 0630 } else { 0631 QPalette::ColorGroup cg; 0632 0633 if (opt.state & QStyle::State_Active) { 0634 cg = QPalette::Normal; 0635 } else { 0636 cg = QPalette::Inactive; 0637 } 0638 0639 if (opt.state & QStyle::State_Selected) { 0640 defaultPen = QPen(opt.palette.brush(cg, QPalette::HighlightedText), 0); 0641 } else { 0642 if (messageItem->textColor().isValid()) { 0643 usingNonDefaultTextColor = true; 0644 defaultPen = QPen(messageItem->textColor(), 0); 0645 } else { 0646 defaultPen = QPen(opt.palette.brush(cg, QPalette::Text), 0); 0647 } 0648 } 0649 } 0650 0651 top += gMessageVerticalMargin; 0652 right -= gMessageHorizontalMargin; 0653 left += gMessageHorizontalMargin; 0654 break; 0655 case Item::GroupHeader: { 0656 rows = &(skcolumn->groupHeaderRows()); 0657 groupHeaderItem = static_cast<GroupHeaderItem *>(item); 0658 0659 QPalette::ColorGroup cg = (opt.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; 0660 0661 if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) { 0662 cg = QPalette::Inactive; 0663 } 0664 0665 QPalette::ColorRole cr; 0666 0667 top += gGroupHeaderOuterVerticalMargin; 0668 right -= gGroupHeaderOuterHorizontalMargin; 0669 left += gGroupHeaderOuterHorizontalMargin; 0670 0671 switch (mTheme->groupHeaderBackgroundMode()) { 0672 case Theme::Transparent: 0673 cr = (opt.state & QStyle::State_Selected) ? QPalette::HighlightedText : QPalette::Text; 0674 defaultPen = QPen(opt.palette.brush(cg, cr), 0); 0675 break; 0676 case Theme::AutoColor: 0677 case Theme::CustomColor: 0678 switch (mTheme->groupHeaderBackgroundStyle()) { 0679 case Theme::PlainRect: 0680 painter->fillRect(QRect(left, top, right - left, opt.rect.height() - (gGroupHeaderInnerVerticalMargin * 2)), 0681 QBrush(mGroupHeaderBackgroundColor)); 0682 break; 0683 case Theme::PlainJoinedRect: { 0684 int rleft = (opt.viewItemPosition == QStyleOptionViewItem::Beginning) || (opt.viewItemPosition == QStyleOptionViewItem::OnlyOne) 0685 ? left 0686 : opt.rect.left(); 0687 int rright = (opt.viewItemPosition == QStyleOptionViewItem::End) || (opt.viewItemPosition == QStyleOptionViewItem::OnlyOne) 0688 ? right 0689 : opt.rect.left() + opt.rect.width(); 0690 painter->fillRect(QRect(rleft, top, rright - rleft, opt.rect.height() - (gGroupHeaderInnerVerticalMargin * 2)), 0691 QBrush(mGroupHeaderBackgroundColor)); 0692 break; 0693 } 0694 case Theme::RoundedJoinedRect: 0695 if (opt.viewItemPosition == QStyleOptionViewItem::Middle) { 0696 painter->fillRect(QRect(opt.rect.left(), top, opt.rect.width(), opt.rect.height() - (gGroupHeaderInnerVerticalMargin * 2)), 0697 QBrush(mGroupHeaderBackgroundColor)); 0698 break; // don't fall through 0699 } 0700 if (opt.viewItemPosition == QStyleOptionViewItem::Beginning) { 0701 painter->fillRect(QRect(opt.rect.left() + opt.rect.width() - 10, top, 10, opt.rect.height() - (gGroupHeaderInnerVerticalMargin * 2)), 0702 QBrush(mGroupHeaderBackgroundColor)); 0703 } else if (opt.viewItemPosition == QStyleOptionViewItem::End) { 0704 painter->fillRect(QRect(opt.rect.left(), top, 10, opt.rect.height() - (gGroupHeaderInnerVerticalMargin * 2)), 0705 QBrush(mGroupHeaderBackgroundColor)); 0706 } 0707 // fall through anyway 0708 [[fallthrough]]; 0709 case Theme::RoundedRect: { 0710 painter->setPen(Qt::NoPen); 0711 bool hadAntialiasing = painter->renderHints() & QPainter::Antialiasing; 0712 if (!hadAntialiasing) { 0713 painter->setRenderHint(QPainter::Antialiasing, true); 0714 } 0715 painter->setBrush(QBrush(mGroupHeaderBackgroundColor)); 0716 painter->setBackgroundMode(Qt::OpaqueMode); 0717 int w = right - left; 0718 if (w > 0) { 0719 painter->drawRoundedRect(QRect(left, top, w, opt.rect.height() - (gGroupHeaderInnerVerticalMargin * 2)), 4.0, 4.0); 0720 } 0721 if (!hadAntialiasing) { 0722 painter->setRenderHint(QPainter::Antialiasing, false); 0723 } 0724 painter->setBackgroundMode(Qt::TransparentMode); 0725 break; 0726 } 0727 case Theme::GradientJoinedRect: { 0728 // FIXME: Could cache this brush 0729 QLinearGradient gradient(0, top, 0, top + opt.rect.height() - (gGroupHeaderInnerVerticalMargin * 2)); 0730 gradient.setColorAt(0.0, KColorScheme::shade(mGroupHeaderBackgroundColor, KColorScheme::LightShade, 0.3)); 0731 gradient.setColorAt(1.0, mGroupHeaderBackgroundColor); 0732 if (opt.viewItemPosition == QStyleOptionViewItem::Middle) { 0733 painter->fillRect(QRect(opt.rect.left(), top, opt.rect.width(), opt.rect.height() - (gGroupHeaderInnerVerticalMargin * 2)), 0734 QBrush(gradient)); 0735 break; // don't fall through 0736 } 0737 if (opt.viewItemPosition == QStyleOptionViewItem::Beginning) { 0738 painter->fillRect(QRect(opt.rect.left() + opt.rect.width() - 10, top, 10, opt.rect.height() - (gGroupHeaderInnerVerticalMargin * 2)), 0739 QBrush(gradient)); 0740 } else if (opt.viewItemPosition == QStyleOptionViewItem::End) { 0741 painter->fillRect(QRect(opt.rect.left(), top, 10, opt.rect.height() - (gGroupHeaderInnerVerticalMargin * 2)), QBrush(gradient)); 0742 } 0743 // fall through anyway 0744 [[fallthrough]]; 0745 } 0746 case Theme::GradientRect: { 0747 // FIXME: Could cache this brush 0748 QLinearGradient gradient(0, top, 0, top + opt.rect.height() - (gGroupHeaderInnerVerticalMargin * 2)); 0749 gradient.setColorAt(0.0, KColorScheme::shade(mGroupHeaderBackgroundColor, KColorScheme::LightShade, 0.3)); 0750 gradient.setColorAt(1.0, mGroupHeaderBackgroundColor); 0751 painter->setPen(Qt::NoPen); 0752 bool hadAntialiasing = painter->renderHints() & QPainter::Antialiasing; 0753 if (!hadAntialiasing) { 0754 painter->setRenderHint(QPainter::Antialiasing, true); 0755 } 0756 painter->setBrush(QBrush(gradient)); 0757 painter->setBackgroundMode(Qt::OpaqueMode); 0758 int w = right - left; 0759 if (w > 0) { 0760 painter->drawRoundedRect(QRect(left, top, w, opt.rect.height() - (gGroupHeaderInnerVerticalMargin * 2)), 4.0, 4.0); 0761 } 0762 if (!hadAntialiasing) { 0763 painter->setRenderHint(QPainter::Antialiasing, false); 0764 } 0765 painter->setBackgroundMode(Qt::TransparentMode); 0766 break; 0767 } 0768 case Theme::StyledRect: 0769 // oxygen, for instance, has a nice graphics for selected items 0770 opt.rect = QRect(left, top, right - left, opt.rect.height() - (gGroupHeaderInnerVerticalMargin * 2)); 0771 opt.state |= QStyle::State_Selected; 0772 opt.viewItemPosition = QStyleOptionViewItem::OnlyOne; 0773 opt.palette.setColor(cg, QPalette::Highlight, mGroupHeaderBackgroundColor); 0774 style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, mItemView); 0775 break; 0776 case Theme::StyledJoinedRect: { 0777 int rleft = (opt.viewItemPosition == QStyleOptionViewItem::Beginning) || (opt.viewItemPosition == QStyleOptionViewItem::OnlyOne) 0778 ? left 0779 : opt.rect.left(); 0780 int rright = (opt.viewItemPosition == QStyleOptionViewItem::End) || (opt.viewItemPosition == QStyleOptionViewItem::OnlyOne) 0781 ? right 0782 : opt.rect.left() + opt.rect.width(); 0783 opt.rect = QRect(rleft, top, rright - rleft, opt.rect.height() - (gGroupHeaderInnerVerticalMargin * 2)); 0784 opt.state |= QStyle::State_Selected; 0785 opt.palette.setColor(cg, QPalette::Highlight, mGroupHeaderBackgroundColor); 0786 style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, mItemView); 0787 break; 0788 } 0789 } 0790 0791 defaultPen = QPen(opt.palette.brush(cg, QPalette::Text), 0); 0792 break; 0793 } 0794 top += gGroupHeaderInnerVerticalMargin; 0795 right -= gGroupHeaderInnerHorizontalMargin; 0796 left += gGroupHeaderInnerHorizontalMargin; 0797 break; 0798 } 0799 default: 0800 Q_ASSERT(false); 0801 return; // bug 0802 } 0803 0804 Qt::LayoutDirection layoutDir = mItemView->layoutDirection(); 0805 0806 for (const auto row : std::as_const(*rows)) { 0807 QSize rowSizeHint = compute_size_hint_for_row(row, mTheme->iconSize(), item); 0808 0809 int bottom = top + rowSizeHint.height(); 0810 0811 // paint right aligned stuff first 0812 int r = right; 0813 int l = left; 0814 const auto rightItems = row->rightItems(); 0815 for (const auto itemit : rightItems) { 0816 auto ci = const_cast<Theme::ContentItem *>(itemit); 0817 0818 if (ci->canUseCustomColor()) { 0819 if (ci->useCustomColor() && (!(opt.state & QStyle::State_Selected))) { 0820 if (usingNonDefaultTextColor) { 0821 // merge the colors 0822 QColor nonDefault = defaultPen.color(); 0823 QColor custom = ci->customColor(); 0824 QColor merged((nonDefault.red() + custom.red()) >> 1, 0825 (nonDefault.green() + custom.green()) >> 1, 0826 (nonDefault.blue() + custom.blue()) >> 1); 0827 painter->setPen(QPen(merged)); 0828 } else { 0829 painter->setPen(QPen(ci->customColor())); 0830 } 0831 } else { 0832 painter->setPen(defaultPen); 0833 } 0834 } // otherwise setting a pen is useless at this time 0835 0836 const QFont &font = cachedFont(ci, item); 0837 0838 switch (ci->type()) { 0839 case Theme::ContentItem::Subject: 0840 paint_right_aligned_elided_text(item->subject(), ci, painter, l, top, r, layoutDir, font); 0841 break; 0842 case Theme::ContentItem::SenderOrReceiver: 0843 paint_right_aligned_elided_text(item->displaySenderOrReceiver(), ci, painter, l, top, r, layoutDir, font); 0844 break; 0845 case Theme::ContentItem::Receiver: 0846 paint_right_aligned_elided_text(item->displayReceiver(), ci, painter, l, top, r, layoutDir, font); 0847 break; 0848 case Theme::ContentItem::Sender: 0849 paint_right_aligned_elided_text(item->displaySender(), ci, painter, l, top, r, layoutDir, font); 0850 break; 0851 case Theme::ContentItem::Date: 0852 paint_right_aligned_elided_text(item->formattedDate(), ci, painter, l, top, r, layoutDir, font); 0853 break; 0854 case Theme::ContentItem::MostRecentDate: 0855 paint_right_aligned_elided_text(item->formattedMaxDate(), ci, painter, l, top, r, layoutDir, font); 0856 break; 0857 case Theme::ContentItem::Size: 0858 paint_right_aligned_elided_text(item->formattedSize(), ci, painter, l, top, r, layoutDir, font); 0859 break; 0860 case Theme::ContentItem::Folder: 0861 paint_right_aligned_elided_text(item->folder(), ci, painter, l, top, r, layoutDir, font); 0862 break; 0863 case Theme::ContentItem::GroupHeaderLabel: 0864 if (groupHeaderItem) { 0865 paint_right_aligned_elided_text(groupHeaderItem->label(), ci, painter, l, top, r, layoutDir, font); 0866 } 0867 break; 0868 case Theme::ContentItem::ReadStateIcon: 0869 paint_permanent_icon(get_read_state_icon(mTheme, item), ci, painter, l, top, r, layoutDir == Qt::LeftToRight, mTheme->iconSize()); 0870 break; 0871 case Theme::ContentItem::CombinedReadRepliedStateIcon: 0872 if (messageItem) { 0873 paint_permanent_icon(get_combined_read_replied_state_icon(mTheme, messageItem), 0874 ci, 0875 painter, 0876 l, 0877 top, 0878 r, 0879 layoutDir == Qt::LeftToRight, 0880 mTheme->iconSize()); 0881 } 0882 break; 0883 case Theme::ContentItem::ExpandedStateIcon: { 0884 const QPixmap *pix = 0885 item->childItemCount() > 0 ? mTheme->pixmap((option.state & QStyle::State_Open) ? Theme::IconShowLess : Theme::IconShowMore) : nullptr; 0886 paint_boolean_state_icon(pix != nullptr, 0887 pix ? pix : mTheme->pixmap(Theme::IconShowMore), 0888 ci, 0889 painter, 0890 l, 0891 top, 0892 r, 0893 layoutDir == Qt::LeftToRight, 0894 mTheme->iconSize()); 0895 break; 0896 } 0897 case Theme::ContentItem::RepliedStateIcon: 0898 if (messageItem) { 0899 const QPixmap *pix = get_replied_state_icon(mTheme, messageItem); 0900 paint_boolean_state_icon(pix != nullptr, 0901 pix ? pix : mTheme->pixmap(Theme::IconReplied), 0902 ci, 0903 painter, 0904 l, 0905 top, 0906 r, 0907 layoutDir == Qt::LeftToRight, 0908 mTheme->iconSize()); 0909 } 0910 break; 0911 case Theme::ContentItem::EncryptionStateIcon: 0912 if (messageItem) { 0913 bool enabled; 0914 const QPixmap *pix = get_encryption_state_icon(mTheme, messageItem, &enabled); 0915 paint_boolean_state_icon(enabled, pix, ci, painter, l, top, r, layoutDir == Qt::LeftToRight, mTheme->iconSize()); 0916 } 0917 break; 0918 case Theme::ContentItem::SignatureStateIcon: 0919 if (messageItem) { 0920 bool enabled; 0921 const QPixmap *pix = get_signature_state_icon(mTheme, messageItem, &enabled); 0922 paint_boolean_state_icon(enabled, pix, ci, painter, l, top, r, layoutDir == Qt::LeftToRight, mTheme->iconSize()); 0923 } 0924 break; 0925 case Theme::ContentItem::SpamHamStateIcon: 0926 if (messageItem) { 0927 const QPixmap *pix = get_spam_ham_state_icon(mTheme, messageItem); 0928 paint_boolean_state_icon(pix != nullptr, 0929 pix ? pix : mTheme->pixmap(Theme::IconSpam), 0930 ci, 0931 painter, 0932 l, 0933 top, 0934 r, 0935 layoutDir == Qt::LeftToRight, 0936 mTheme->iconSize()); 0937 } 0938 break; 0939 case Theme::ContentItem::WatchedIgnoredStateIcon: 0940 if (messageItem) { 0941 const QPixmap *pix = get_watched_ignored_state_icon(mTheme, messageItem); 0942 paint_boolean_state_icon(pix != nullptr, 0943 pix ? pix : mTheme->pixmap(Theme::IconWatched), 0944 ci, 0945 painter, 0946 l, 0947 top, 0948 r, 0949 layoutDir == Qt::LeftToRight, 0950 mTheme->iconSize()); 0951 } 0952 break; 0953 case Theme::ContentItem::AttachmentStateIcon: 0954 if (messageItem) { 0955 paint_boolean_state_icon(messageItem->status().hasAttachment(), 0956 mTheme->pixmap(Theme::IconAttachment), 0957 ci, 0958 painter, 0959 l, 0960 top, 0961 r, 0962 layoutDir == Qt::LeftToRight, 0963 mTheme->iconSize()); 0964 } 0965 break; 0966 case Theme::ContentItem::AnnotationIcon: 0967 if (messageItem) { 0968 paint_boolean_state_icon(messageItem->hasAnnotation(), 0969 mTheme->pixmap(Theme::IconAnnotation), 0970 ci, 0971 painter, 0972 l, 0973 top, 0974 r, 0975 layoutDir == Qt::LeftToRight, 0976 mTheme->iconSize()); 0977 } 0978 break; 0979 case Theme::ContentItem::InvitationIcon: 0980 if (messageItem) { 0981 paint_boolean_state_icon(messageItem->status().hasInvitation(), 0982 mTheme->pixmap(Theme::IconInvitation), 0983 ci, 0984 painter, 0985 l, 0986 top, 0987 r, 0988 layoutDir == Qt::LeftToRight, 0989 mTheme->iconSize()); 0990 } 0991 break; 0992 case Theme::ContentItem::ActionItemStateIcon: 0993 if (messageItem) { 0994 paint_boolean_state_icon(messageItem->status().isToAct(), 0995 mTheme->pixmap(Theme::IconActionItem), 0996 ci, 0997 painter, 0998 l, 0999 top, 1000 r, 1001 layoutDir == Qt::LeftToRight, 1002 mTheme->iconSize()); 1003 } 1004 break; 1005 case Theme::ContentItem::ImportantStateIcon: 1006 if (messageItem) { 1007 paint_boolean_state_icon(messageItem->status().isImportant(), 1008 mTheme->pixmap(Theme::IconImportant), 1009 ci, 1010 painter, 1011 l, 1012 top, 1013 r, 1014 layoutDir == Qt::LeftToRight, 1015 mTheme->iconSize()); 1016 } 1017 break; 1018 case Theme::ContentItem::VerticalLine: 1019 paint_vertical_line(painter, l, top, r, bottom, layoutDir == Qt::LeftToRight); 1020 break; 1021 case Theme::ContentItem::HorizontalSpacer: 1022 paint_horizontal_spacer(l, top, r, bottom, layoutDir == Qt::LeftToRight); 1023 break; 1024 case Theme::ContentItem::TagList: 1025 if (messageItem) { 1026 const QList<MessageItem::Tag *> tagList = messageItem->tagList(); 1027 paint_tag_list(tagList, painter, l, top, r, layoutDir == Qt::LeftToRight, mTheme->iconSize()); 1028 } 1029 break; 1030 } 1031 } 1032 1033 // then paint left aligned stuff 1034 const auto leftItems = row->leftItems(); 1035 for (const auto itemit : leftItems) { 1036 auto ci = const_cast<Theme::ContentItem *>(itemit); 1037 1038 if (ci->canUseCustomColor()) { 1039 if (ci->useCustomColor() && (!(opt.state & QStyle::State_Selected))) { 1040 if (usingNonDefaultTextColor) { 1041 // merge the colors 1042 QColor nonDefault = defaultPen.color(); 1043 QColor custom = ci->customColor(); 1044 QColor merged((nonDefault.red() + custom.red()) >> 1, 1045 (nonDefault.green() + custom.green()) >> 1, 1046 (nonDefault.blue() + custom.blue()) >> 1); 1047 painter->setPen(QPen(merged)); 1048 } else { 1049 painter->setPen(QPen(ci->customColor())); 1050 } 1051 } else { 1052 painter->setPen(defaultPen); 1053 } 1054 } // otherwise setting a pen is useless at this time 1055 1056 const QFont &font = cachedFont(ci, item); 1057 1058 switch (ci->type()) { 1059 case Theme::ContentItem::Subject: 1060 paint_left_aligned_elided_text(item->subject(), ci, painter, l, top, r, layoutDir, font); 1061 break; 1062 case Theme::ContentItem::SenderOrReceiver: 1063 paint_left_aligned_elided_text(item->displaySenderOrReceiver(), ci, painter, l, top, r, layoutDir, font); 1064 break; 1065 case Theme::ContentItem::Receiver: 1066 paint_left_aligned_elided_text(item->displayReceiver(), ci, painter, l, top, r, layoutDir, font); 1067 break; 1068 case Theme::ContentItem::Sender: 1069 paint_left_aligned_elided_text(item->displaySender(), ci, painter, l, top, r, layoutDir, font); 1070 break; 1071 case Theme::ContentItem::Date: 1072 paint_left_aligned_elided_text(item->formattedDate(), ci, painter, l, top, r, layoutDir, font); 1073 break; 1074 case Theme::ContentItem::MostRecentDate: 1075 paint_left_aligned_elided_text(item->formattedMaxDate(), ci, painter, l, top, r, layoutDir, font); 1076 break; 1077 case Theme::ContentItem::Size: 1078 paint_left_aligned_elided_text(item->formattedSize(), ci, painter, l, top, r, layoutDir, font); 1079 break; 1080 case Theme::ContentItem::Folder: 1081 paint_left_aligned_elided_text(item->folder(), ci, painter, l, top, r, layoutDir, font); 1082 break; 1083 case Theme::ContentItem::GroupHeaderLabel: 1084 if (groupHeaderItem) { 1085 paint_left_aligned_elided_text(groupHeaderItem->label(), ci, painter, l, top, r, layoutDir, font); 1086 } 1087 break; 1088 case Theme::ContentItem::ReadStateIcon: 1089 paint_permanent_icon(get_read_state_icon(mTheme, item), ci, painter, l, top, r, layoutDir != Qt::LeftToRight, mTheme->iconSize()); 1090 break; 1091 case Theme::ContentItem::CombinedReadRepliedStateIcon: 1092 if (messageItem) { 1093 paint_permanent_icon(get_combined_read_replied_state_icon(mTheme, messageItem), 1094 ci, 1095 painter, 1096 l, 1097 top, 1098 r, 1099 layoutDir != Qt::LeftToRight, 1100 mTheme->iconSize()); 1101 } 1102 break; 1103 case Theme::ContentItem::ExpandedStateIcon: { 1104 const QPixmap *pix = 1105 item->childItemCount() > 0 ? mTheme->pixmap((option.state & QStyle::State_Open) ? Theme::IconShowLess : Theme::IconShowMore) : nullptr; 1106 paint_boolean_state_icon(pix != nullptr, 1107 pix ? pix : mTheme->pixmap(Theme::IconShowMore), 1108 ci, 1109 painter, 1110 l, 1111 top, 1112 r, 1113 layoutDir != Qt::LeftToRight, 1114 mTheme->iconSize()); 1115 break; 1116 } 1117 case Theme::ContentItem::RepliedStateIcon: 1118 if (messageItem) { 1119 const QPixmap *pix = get_replied_state_icon(mTheme, messageItem); 1120 paint_boolean_state_icon(pix != nullptr, 1121 pix ? pix : mTheme->pixmap(Theme::IconReplied), 1122 ci, 1123 painter, 1124 l, 1125 top, 1126 r, 1127 layoutDir != Qt::LeftToRight, 1128 mTheme->iconSize()); 1129 } 1130 break; 1131 case Theme::ContentItem::EncryptionStateIcon: 1132 if (messageItem) { 1133 bool enabled; 1134 const QPixmap *pix = get_encryption_state_icon(mTheme, messageItem, &enabled); 1135 paint_boolean_state_icon(enabled, pix, ci, painter, l, top, r, layoutDir != Qt::LeftToRight, mTheme->iconSize()); 1136 } 1137 break; 1138 case Theme::ContentItem::SignatureStateIcon: 1139 if (messageItem) { 1140 bool enabled; 1141 const QPixmap *pix = get_signature_state_icon(mTheme, messageItem, &enabled); 1142 paint_boolean_state_icon(enabled, pix, ci, painter, l, top, r, layoutDir != Qt::LeftToRight, mTheme->iconSize()); 1143 } 1144 break; 1145 case Theme::ContentItem::SpamHamStateIcon: 1146 if (messageItem) { 1147 const QPixmap *pix = get_spam_ham_state_icon(mTheme, messageItem); 1148 paint_boolean_state_icon(pix != nullptr, 1149 pix ? pix : mTheme->pixmap(Theme::IconSpam), 1150 ci, 1151 painter, 1152 l, 1153 top, 1154 r, 1155 layoutDir != Qt::LeftToRight, 1156 mTheme->iconSize()); 1157 } 1158 break; 1159 case Theme::ContentItem::WatchedIgnoredStateIcon: 1160 if (messageItem) { 1161 const QPixmap *pix = get_watched_ignored_state_icon(mTheme, messageItem); 1162 paint_boolean_state_icon(pix != nullptr, 1163 pix ? pix : mTheme->pixmap(Theme::IconWatched), 1164 ci, 1165 painter, 1166 l, 1167 top, 1168 r, 1169 layoutDir != Qt::LeftToRight, 1170 mTheme->iconSize()); 1171 } 1172 break; 1173 case Theme::ContentItem::AttachmentStateIcon: 1174 if (messageItem) { 1175 paint_boolean_state_icon(messageItem->status().hasAttachment(), 1176 mTheme->pixmap(Theme::IconAttachment), 1177 ci, 1178 painter, 1179 l, 1180 top, 1181 r, 1182 layoutDir != Qt::LeftToRight, 1183 mTheme->iconSize()); 1184 } 1185 break; 1186 case Theme::ContentItem::AnnotationIcon: 1187 if (messageItem) { 1188 paint_boolean_state_icon(messageItem->hasAnnotation(), 1189 mTheme->pixmap(Theme::IconAnnotation), 1190 ci, 1191 painter, 1192 l, 1193 top, 1194 r, 1195 layoutDir != Qt::LeftToRight, 1196 mTheme->iconSize()); 1197 } 1198 break; 1199 case Theme::ContentItem::InvitationIcon: 1200 if (messageItem) { 1201 paint_boolean_state_icon(messageItem->status().hasInvitation(), 1202 mTheme->pixmap(Theme::IconInvitation), 1203 ci, 1204 painter, 1205 l, 1206 top, 1207 r, 1208 layoutDir != Qt::LeftToRight, 1209 mTheme->iconSize()); 1210 } 1211 break; 1212 case Theme::ContentItem::ActionItemStateIcon: 1213 if (messageItem) { 1214 paint_boolean_state_icon(messageItem->status().isToAct(), 1215 mTheme->pixmap(Theme::IconActionItem), 1216 ci, 1217 painter, 1218 l, 1219 top, 1220 r, 1221 layoutDir != Qt::LeftToRight, 1222 mTheme->iconSize()); 1223 } 1224 break; 1225 case Theme::ContentItem::ImportantStateIcon: 1226 if (messageItem) { 1227 paint_boolean_state_icon(messageItem->status().isImportant(), 1228 mTheme->pixmap(Theme::IconImportant), 1229 ci, 1230 painter, 1231 l, 1232 top, 1233 r, 1234 layoutDir != Qt::LeftToRight, 1235 mTheme->iconSize()); 1236 } 1237 break; 1238 case Theme::ContentItem::VerticalLine: 1239 paint_vertical_line(painter, l, top, r, bottom, layoutDir != Qt::LeftToRight); 1240 break; 1241 case Theme::ContentItem::HorizontalSpacer: 1242 paint_horizontal_spacer(l, top, r, bottom, layoutDir != Qt::LeftToRight); 1243 break; 1244 case Theme::ContentItem::TagList: 1245 if (messageItem) { 1246 const QList<MessageItem::Tag *> tagList = messageItem->tagList(); 1247 paint_tag_list(tagList, painter, l, top, r, layoutDir != Qt::LeftToRight, mTheme->iconSize()); 1248 } 1249 break; 1250 } 1251 } 1252 1253 top = bottom; 1254 } 1255 1256 painter->setFont(oldFont); 1257 painter->setPen(oldPen); 1258 painter->setOpacity(oldOpacity); 1259 } 1260 1261 bool ThemeDelegate::hitTest(const QPoint &viewportPoint, bool exact) 1262 { 1263 mHitItem = nullptr; 1264 mHitColumn = nullptr; 1265 mHitRow = nullptr; 1266 mHitContentItem = nullptr; 1267 1268 if (!mTheme) { 1269 return false; // hm hm... 1270 } 1271 1272 mHitIndex = mItemView->indexAt(viewportPoint); 1273 1274 if (!mHitIndex.isValid()) { 1275 return false; // bleah 1276 } 1277 1278 mHitItem = itemFromIndex(mHitIndex); 1279 if (!mHitItem) { 1280 return false; // hm... 1281 } 1282 1283 mHitItemRect = mItemView->visualRect(mHitIndex); 1284 1285 mHitColumn = mTheme->column(mHitIndex.column()); 1286 if (!mHitColumn) { 1287 return false; // bleah 1288 } 1289 1290 const QList<Theme::Row *> *rows; // I'd like to have it as reference, but gcc complains... 1291 1292 MessageItem *messageItem = nullptr; 1293 GroupHeaderItem *groupHeaderItem = nullptr; 1294 1295 int top = mHitItemRect.top(); 1296 int right = mHitItemRect.right(); 1297 int left = mHitItemRect.left(); 1298 1299 mHitRow = nullptr; 1300 mHitRowIndex = -1; 1301 mHitContentItem = nullptr; 1302 1303 switch (mHitItem->type()) { 1304 case Item::Message: 1305 mHitRowIsMessageRow = true; 1306 rows = &(mHitColumn->messageRows()); 1307 messageItem = static_cast<MessageItem *>(mHitItem); 1308 // FIXME: paint eventual background here 1309 1310 top += gMessageVerticalMargin; 1311 right -= gMessageHorizontalMargin; 1312 left += gMessageHorizontalMargin; 1313 break; 1314 case Item::GroupHeader: 1315 mHitRowIsMessageRow = false; 1316 rows = &(mHitColumn->groupHeaderRows()); 1317 groupHeaderItem = static_cast<GroupHeaderItem *>(mHitItem); 1318 1319 top += gGroupHeaderOuterVerticalMargin + gGroupHeaderInnerVerticalMargin; 1320 right -= gGroupHeaderOuterHorizontalMargin + gGroupHeaderInnerHorizontalMargin; 1321 left += gGroupHeaderOuterHorizontalMargin + gGroupHeaderInnerHorizontalMargin; 1322 break; 1323 default: 1324 return false; // bug 1325 break; 1326 } 1327 1328 int rowIdx = 0; 1329 int bestInexactDistance = 0xffffff; 1330 bool bestInexactItemRight = false; 1331 QRect bestInexactRect; 1332 const Theme::ContentItem *bestInexactContentItem = nullptr; 1333 1334 Qt::LayoutDirection layoutDir = mItemView->layoutDirection(); 1335 1336 for (const auto row : std::as_const(*rows)) { 1337 QSize rowSizeHint = compute_size_hint_for_row(row, mTheme->iconSize(), mHitItem); 1338 1339 if ((viewportPoint.y() < top) && (rowIdx > 0)) { 1340 break; // not this row (tough we should have already found it... probably clicked upper margin) 1341 } 1342 1343 int bottom = top + rowSizeHint.height(); 1344 1345 if (viewportPoint.y() > bottom) { 1346 top += rowSizeHint.height(); 1347 rowIdx++; 1348 continue; // not this row 1349 } 1350 1351 bestInexactItemRight = false; 1352 bestInexactDistance = 0xffffff; 1353 bestInexactContentItem = nullptr; 1354 1355 // this row! 1356 mHitRow = row; 1357 mHitRowIndex = rowIdx; 1358 mHitRowRect = QRect(left, top, right - left, bottom - top); 1359 1360 // check right aligned stuff first 1361 mHitContentItemRight = true; 1362 1363 int r = right; 1364 int l = left; 1365 const auto rightItems = mHitRow->rightItems(); 1366 for (const auto itemit : rightItems) { 1367 auto ci = const_cast<Theme::ContentItem *>(itemit); 1368 1369 mHitContentItemRect = QRect(); 1370 1371 const QFont &font = cachedFont(ci, mHitItem); 1372 1373 switch (ci->type()) { 1374 case Theme::ContentItem::Subject: 1375 compute_bounding_rect_for_right_aligned_elided_text(mHitItem->subject(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1376 break; 1377 case Theme::ContentItem::SenderOrReceiver: 1378 compute_bounding_rect_for_right_aligned_elided_text(mHitItem->displaySenderOrReceiver(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1379 break; 1380 case Theme::ContentItem::Receiver: 1381 compute_bounding_rect_for_right_aligned_elided_text(mHitItem->displayReceiver(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1382 break; 1383 case Theme::ContentItem::Sender: 1384 compute_bounding_rect_for_right_aligned_elided_text(mHitItem->displaySender(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1385 break; 1386 case Theme::ContentItem::Date: 1387 compute_bounding_rect_for_right_aligned_elided_text(mHitItem->formattedDate(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1388 break; 1389 case Theme::ContentItem::MostRecentDate: 1390 compute_bounding_rect_for_right_aligned_elided_text(mHitItem->formattedMaxDate(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1391 break; 1392 case Theme::ContentItem::Size: 1393 compute_bounding_rect_for_right_aligned_elided_text(mHitItem->formattedSize(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1394 break; 1395 case Theme::ContentItem::Folder: 1396 compute_bounding_rect_for_right_aligned_elided_text(mHitItem->folder(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1397 break; 1398 case Theme::ContentItem::GroupHeaderLabel: 1399 if (groupHeaderItem) { 1400 compute_bounding_rect_for_right_aligned_elided_text(groupHeaderItem->label(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1401 } 1402 break; 1403 case Theme::ContentItem::ReadStateIcon: 1404 compute_bounding_rect_for_permanent_icon(ci, l, top, r, mHitContentItemRect, layoutDir == Qt::LeftToRight, mTheme->iconSize()); 1405 break; 1406 case Theme::ContentItem::CombinedReadRepliedStateIcon: 1407 compute_bounding_rect_for_permanent_icon(ci, l, top, r, mHitContentItemRect, layoutDir == Qt::LeftToRight, mTheme->iconSize()); 1408 break; 1409 case Theme::ContentItem::ExpandedStateIcon: 1410 compute_bounding_rect_for_boolean_state_icon(mHitItem->childItemCount() > 0, 1411 ci, 1412 l, 1413 top, 1414 r, 1415 mHitContentItemRect, 1416 layoutDir == Qt::LeftToRight, 1417 mTheme->iconSize()); 1418 break; 1419 case Theme::ContentItem::RepliedStateIcon: 1420 if (messageItem) { 1421 const QPixmap *pix = get_replied_state_icon(mTheme, messageItem); 1422 compute_bounding_rect_for_boolean_state_icon(pix != nullptr, 1423 ci, 1424 l, 1425 top, 1426 r, 1427 mHitContentItemRect, 1428 layoutDir == Qt::LeftToRight, 1429 mTheme->iconSize()); 1430 } 1431 break; 1432 case Theme::ContentItem::EncryptionStateIcon: 1433 if (messageItem) { 1434 bool enabled; 1435 get_encryption_state_icon(mTheme, messageItem, &enabled); 1436 compute_bounding_rect_for_boolean_state_icon(enabled, ci, l, top, r, mHitContentItemRect, layoutDir == Qt::LeftToRight, mTheme->iconSize()); 1437 } 1438 break; 1439 case Theme::ContentItem::SignatureStateIcon: 1440 if (messageItem) { 1441 bool enabled; 1442 get_signature_state_icon(mTheme, messageItem, &enabled); 1443 compute_bounding_rect_for_boolean_state_icon(enabled, ci, l, top, r, mHitContentItemRect, layoutDir == Qt::LeftToRight, mTheme->iconSize()); 1444 } 1445 break; 1446 case Theme::ContentItem::SpamHamStateIcon: 1447 if (messageItem) { 1448 const QPixmap *pix = get_spam_ham_state_icon(mTheme, messageItem); 1449 compute_bounding_rect_for_boolean_state_icon(pix != nullptr, 1450 ci, 1451 l, 1452 top, 1453 r, 1454 mHitContentItemRect, 1455 layoutDir == Qt::LeftToRight, 1456 mTheme->iconSize()); 1457 } 1458 break; 1459 case Theme::ContentItem::WatchedIgnoredStateIcon: 1460 if (messageItem) { 1461 const QPixmap *pix = get_watched_ignored_state_icon(mTheme, messageItem); 1462 compute_bounding_rect_for_boolean_state_icon(pix != nullptr, 1463 ci, 1464 l, 1465 top, 1466 r, 1467 mHitContentItemRect, 1468 layoutDir == Qt::LeftToRight, 1469 mTheme->iconSize()); 1470 } 1471 break; 1472 case Theme::ContentItem::AttachmentStateIcon: 1473 if (messageItem) { 1474 compute_bounding_rect_for_boolean_state_icon(messageItem->status().hasAttachment(), 1475 ci, 1476 l, 1477 top, 1478 r, 1479 mHitContentItemRect, 1480 layoutDir == Qt::LeftToRight, 1481 mTheme->iconSize()); 1482 } 1483 break; 1484 case Theme::ContentItem::AnnotationIcon: 1485 if (messageItem) { 1486 compute_bounding_rect_for_boolean_state_icon(messageItem->hasAnnotation(), 1487 ci, 1488 l, 1489 top, 1490 r, 1491 mHitContentItemRect, 1492 layoutDir == Qt::LeftToRight, 1493 mTheme->iconSize()); 1494 } 1495 break; 1496 case Theme::ContentItem::InvitationIcon: 1497 if (messageItem) { 1498 compute_bounding_rect_for_boolean_state_icon(messageItem->status().hasInvitation(), 1499 ci, 1500 l, 1501 top, 1502 r, 1503 mHitContentItemRect, 1504 layoutDir == Qt::LeftToRight, 1505 mTheme->iconSize()); 1506 } 1507 break; 1508 case Theme::ContentItem::ActionItemStateIcon: 1509 if (messageItem) { 1510 compute_bounding_rect_for_boolean_state_icon(messageItem->status().isToAct(), 1511 ci, 1512 l, 1513 top, 1514 r, 1515 mHitContentItemRect, 1516 layoutDir == Qt::LeftToRight, 1517 mTheme->iconSize()); 1518 } 1519 break; 1520 case Theme::ContentItem::ImportantStateIcon: 1521 if (messageItem) { 1522 compute_bounding_rect_for_boolean_state_icon(messageItem->status().isImportant(), 1523 ci, 1524 l, 1525 top, 1526 r, 1527 mHitContentItemRect, 1528 layoutDir == Qt::LeftToRight, 1529 mTheme->iconSize()); 1530 } 1531 break; 1532 case Theme::ContentItem::VerticalLine: 1533 compute_bounding_rect_for_vertical_line(l, top, r, bottom, mHitContentItemRect, layoutDir == Qt::LeftToRight); 1534 break; 1535 case Theme::ContentItem::HorizontalSpacer: 1536 compute_bounding_rect_for_horizontal_spacer(l, top, r, bottom, mHitContentItemRect, layoutDir == Qt::LeftToRight); 1537 break; 1538 case Theme::ContentItem::TagList: 1539 if (messageItem) { 1540 const QList<MessageItem::Tag *> tagList = messageItem->tagList(); 1541 compute_bounding_rect_for_tag_list(tagList, l, top, r, mHitContentItemRect, layoutDir == Qt::LeftToRight, mTheme->iconSize()); 1542 } 1543 break; 1544 } 1545 1546 if (mHitContentItemRect.isValid()) { 1547 if (mHitContentItemRect.contains(viewportPoint)) { 1548 // caught! 1549 mHitContentItem = ci; 1550 return true; 1551 } 1552 if (!exact) { 1553 QRect inexactRect(mHitContentItemRect.left(), mHitRowRect.top(), mHitContentItemRect.width(), mHitRowRect.height()); 1554 if (inexactRect.contains(viewportPoint)) { 1555 mHitContentItem = ci; 1556 return true; 1557 } 1558 1559 int inexactDistance = 1560 viewportPoint.x() > inexactRect.right() ? viewportPoint.x() - inexactRect.right() : inexactRect.left() - viewportPoint.x(); 1561 if (inexactDistance < bestInexactDistance) { 1562 bestInexactDistance = inexactDistance; 1563 bestInexactRect = mHitContentItemRect; 1564 bestInexactItemRight = true; 1565 bestInexactContentItem = ci; 1566 } 1567 } 1568 } 1569 } 1570 1571 // then check left aligned stuff 1572 mHitContentItemRight = false; 1573 1574 const auto leftItems = mHitRow->leftItems(); 1575 for (const auto itemit : leftItems) { 1576 auto ci = const_cast<Theme::ContentItem *>(itemit); 1577 1578 mHitContentItemRect = QRect(); 1579 1580 const QFont &font = cachedFont(ci, mHitItem); 1581 1582 switch (ci->type()) { 1583 case Theme::ContentItem::Subject: 1584 compute_bounding_rect_for_left_aligned_elided_text(mHitItem->subject(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1585 break; 1586 case Theme::ContentItem::SenderOrReceiver: 1587 compute_bounding_rect_for_left_aligned_elided_text(mHitItem->displaySenderOrReceiver(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1588 break; 1589 case Theme::ContentItem::Receiver: 1590 compute_bounding_rect_for_left_aligned_elided_text(mHitItem->displayReceiver(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1591 break; 1592 case Theme::ContentItem::Sender: 1593 compute_bounding_rect_for_left_aligned_elided_text(mHitItem->displaySender(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1594 break; 1595 case Theme::ContentItem::Date: 1596 compute_bounding_rect_for_left_aligned_elided_text(mHitItem->formattedDate(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1597 break; 1598 case Theme::ContentItem::MostRecentDate: 1599 compute_bounding_rect_for_left_aligned_elided_text(mHitItem->formattedMaxDate(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1600 break; 1601 case Theme::ContentItem::Size: 1602 compute_bounding_rect_for_left_aligned_elided_text(mHitItem->formattedSize(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1603 break; 1604 case Theme::ContentItem::Folder: 1605 compute_bounding_rect_for_left_aligned_elided_text(mHitItem->folder(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1606 break; 1607 case Theme::ContentItem::GroupHeaderLabel: 1608 if (groupHeaderItem) { 1609 compute_bounding_rect_for_left_aligned_elided_text(groupHeaderItem->label(), ci, l, top, r, mHitContentItemRect, layoutDir, font); 1610 } 1611 break; 1612 case Theme::ContentItem::ReadStateIcon: 1613 compute_bounding_rect_for_permanent_icon(ci, l, top, r, mHitContentItemRect, layoutDir != Qt::LeftToRight, mTheme->iconSize()); 1614 break; 1615 case Theme::ContentItem::CombinedReadRepliedStateIcon: 1616 compute_bounding_rect_for_permanent_icon(ci, l, top, r, mHitContentItemRect, layoutDir != Qt::LeftToRight, mTheme->iconSize()); 1617 break; 1618 case Theme::ContentItem::ExpandedStateIcon: 1619 compute_bounding_rect_for_boolean_state_icon(mHitItem->childItemCount() > 0, 1620 ci, 1621 l, 1622 top, 1623 r, 1624 mHitContentItemRect, 1625 layoutDir != Qt::LeftToRight, 1626 mTheme->iconSize()); 1627 break; 1628 case Theme::ContentItem::RepliedStateIcon: 1629 if (messageItem) { 1630 const QPixmap *pix = get_replied_state_icon(mTheme, messageItem); 1631 compute_bounding_rect_for_boolean_state_icon(pix != nullptr, 1632 ci, 1633 l, 1634 top, 1635 r, 1636 mHitContentItemRect, 1637 layoutDir != Qt::LeftToRight, 1638 mTheme->iconSize()); 1639 } 1640 break; 1641 case Theme::ContentItem::EncryptionStateIcon: 1642 if (messageItem) { 1643 bool enabled; 1644 get_encryption_state_icon(mTheme, messageItem, &enabled); 1645 compute_bounding_rect_for_boolean_state_icon(enabled, ci, l, top, r, mHitContentItemRect, layoutDir != Qt::LeftToRight, mTheme->iconSize()); 1646 } 1647 break; 1648 case Theme::ContentItem::SignatureStateIcon: 1649 if (messageItem) { 1650 bool enabled; 1651 get_signature_state_icon(mTheme, messageItem, &enabled); 1652 compute_bounding_rect_for_boolean_state_icon(enabled, ci, l, top, r, mHitContentItemRect, layoutDir != Qt::LeftToRight, mTheme->iconSize()); 1653 } 1654 break; 1655 case Theme::ContentItem::SpamHamStateIcon: 1656 if (messageItem) { 1657 const QPixmap *pix = get_spam_ham_state_icon(mTheme, messageItem); 1658 compute_bounding_rect_for_boolean_state_icon(pix != nullptr, 1659 ci, 1660 l, 1661 top, 1662 r, 1663 mHitContentItemRect, 1664 layoutDir != Qt::LeftToRight, 1665 mTheme->iconSize()); 1666 } 1667 break; 1668 case Theme::ContentItem::WatchedIgnoredStateIcon: 1669 if (messageItem) { 1670 const QPixmap *pix = get_watched_ignored_state_icon(mTheme, messageItem); 1671 compute_bounding_rect_for_boolean_state_icon(pix != nullptr, 1672 ci, 1673 l, 1674 top, 1675 r, 1676 mHitContentItemRect, 1677 layoutDir != Qt::LeftToRight, 1678 mTheme->iconSize()); 1679 } 1680 break; 1681 case Theme::ContentItem::AttachmentStateIcon: 1682 if (messageItem) { 1683 compute_bounding_rect_for_boolean_state_icon(messageItem->status().hasAttachment(), 1684 ci, 1685 l, 1686 top, 1687 r, 1688 mHitContentItemRect, 1689 layoutDir != Qt::LeftToRight, 1690 mTheme->iconSize()); 1691 } 1692 break; 1693 case Theme::ContentItem::AnnotationIcon: 1694 if (messageItem) { 1695 compute_bounding_rect_for_boolean_state_icon(messageItem->hasAnnotation(), 1696 ci, 1697 l, 1698 top, 1699 r, 1700 mHitContentItemRect, 1701 layoutDir != Qt::LeftToRight, 1702 mTheme->iconSize()); 1703 } 1704 break; 1705 case Theme::ContentItem::InvitationIcon: 1706 if (messageItem) { 1707 compute_bounding_rect_for_boolean_state_icon(messageItem->status().hasInvitation(), 1708 ci, 1709 l, 1710 top, 1711 r, 1712 mHitContentItemRect, 1713 layoutDir != Qt::LeftToRight, 1714 mTheme->iconSize()); 1715 } 1716 break; 1717 case Theme::ContentItem::ActionItemStateIcon: 1718 if (messageItem) { 1719 compute_bounding_rect_for_boolean_state_icon(messageItem->status().isToAct(), 1720 ci, 1721 l, 1722 top, 1723 r, 1724 mHitContentItemRect, 1725 layoutDir != Qt::LeftToRight, 1726 mTheme->iconSize()); 1727 } 1728 break; 1729 case Theme::ContentItem::ImportantStateIcon: 1730 if (messageItem) { 1731 compute_bounding_rect_for_boolean_state_icon(messageItem->status().isImportant(), 1732 ci, 1733 l, 1734 top, 1735 r, 1736 mHitContentItemRect, 1737 layoutDir != Qt::LeftToRight, 1738 mTheme->iconSize()); 1739 } 1740 break; 1741 case Theme::ContentItem::VerticalLine: 1742 compute_bounding_rect_for_vertical_line(l, top, r, bottom, mHitContentItemRect, layoutDir != Qt::LeftToRight); 1743 break; 1744 case Theme::ContentItem::HorizontalSpacer: 1745 compute_bounding_rect_for_horizontal_spacer(l, top, r, bottom, mHitContentItemRect, layoutDir != Qt::LeftToRight); 1746 break; 1747 case Theme::ContentItem::TagList: 1748 if (messageItem) { 1749 const QList<MessageItem::Tag *> tagList = messageItem->tagList(); 1750 compute_bounding_rect_for_tag_list(tagList, l, top, r, mHitContentItemRect, layoutDir != Qt::LeftToRight, mTheme->iconSize()); 1751 } 1752 break; 1753 } 1754 1755 if (mHitContentItemRect.isValid()) { 1756 if (mHitContentItemRect.contains(viewportPoint)) { 1757 // caught! 1758 mHitContentItem = ci; 1759 return true; 1760 } 1761 if (!exact) { 1762 QRect inexactRect(mHitContentItemRect.left(), mHitRowRect.top(), mHitContentItemRect.width(), mHitRowRect.height()); 1763 if (inexactRect.contains(viewportPoint)) { 1764 mHitContentItem = ci; 1765 return true; 1766 } 1767 1768 int inexactDistance = 1769 viewportPoint.x() > inexactRect.right() ? viewportPoint.x() - inexactRect.right() : inexactRect.left() - viewportPoint.x(); 1770 if (inexactDistance < bestInexactDistance) { 1771 bestInexactDistance = inexactDistance; 1772 bestInexactRect = mHitContentItemRect; 1773 bestInexactItemRight = false; 1774 bestInexactContentItem = ci; 1775 } 1776 } 1777 } 1778 } 1779 1780 top += rowSizeHint.height(); 1781 rowIdx++; 1782 } 1783 1784 mHitContentItem = bestInexactContentItem; 1785 mHitContentItemRight = bestInexactItemRight; 1786 mHitContentItemRect = bestInexactRect; 1787 return true; 1788 } 1789 1790 const QModelIndex &ThemeDelegate::hitIndex() const 1791 { 1792 return mHitIndex; 1793 } 1794 1795 Item *ThemeDelegate::hitItem() const 1796 { 1797 return mHitItem; 1798 } 1799 1800 QRect ThemeDelegate::hitItemRect() const 1801 { 1802 return mHitItemRect; 1803 } 1804 1805 const Theme::Column *ThemeDelegate::hitColumn() const 1806 { 1807 return mHitColumn; 1808 } 1809 1810 int ThemeDelegate::hitColumnIndex() const 1811 { 1812 return mHitIndex.column(); 1813 } 1814 1815 const Theme::Row *ThemeDelegate::hitRow() const 1816 { 1817 return mHitRow; 1818 } 1819 1820 int ThemeDelegate::hitRowIndex() const 1821 { 1822 return mHitRowIndex; 1823 } 1824 1825 QRect ThemeDelegate::hitRowRect() const 1826 { 1827 return mHitRowRect; 1828 } 1829 1830 bool ThemeDelegate::hitRowIsMessageRow() const 1831 { 1832 return mHitRowIsMessageRow; 1833 } 1834 1835 const Theme::ContentItem *ThemeDelegate::hitContentItem() const 1836 { 1837 return mHitContentItem; 1838 } 1839 1840 bool ThemeDelegate::hitContentItemRight() const 1841 { 1842 return mHitContentItemRight; 1843 } 1844 1845 QRect ThemeDelegate::hitContentItemRect() const 1846 { 1847 return mHitContentItemRect; 1848 } 1849 1850 QSize ThemeDelegate::sizeHintForItemTypeAndColumn(Item::Type type, int column, const Item *item) const 1851 { 1852 if (!mTheme) { 1853 return {16, 16}; // bleah 1854 } 1855 1856 const Theme::Column *skcolumn = mTheme->column(column); 1857 if (!skcolumn) { 1858 return {16, 16}; // bleah 1859 } 1860 1861 const QList<Theme::Row *> *rows; // I'd like to have it as reference, but gcc complains... 1862 1863 // The sizeHint() is layout direction independent. 1864 1865 int marginw; 1866 int marginh; 1867 1868 switch (type) { 1869 case Item::Message: 1870 rows = &(skcolumn->messageRows()); 1871 1872 marginh = gMessageVerticalMargin << 1; 1873 marginw = gMessageHorizontalMargin << 1; 1874 break; 1875 case Item::GroupHeader: 1876 rows = &(skcolumn->groupHeaderRows()); 1877 1878 marginh = (gGroupHeaderOuterVerticalMargin + gGroupHeaderInnerVerticalMargin) << 1; 1879 marginw = (gGroupHeaderOuterVerticalMargin + gGroupHeaderInnerVerticalMargin) << 1; 1880 break; 1881 default: 1882 return {16, 16}; // bug 1883 break; 1884 } 1885 1886 int totalh = 0; 1887 int maxw = 0; 1888 1889 for (QList<Theme::Row *>::ConstIterator rowit = rows->constBegin(), endRowIt = rows->constEnd(); rowit != endRowIt; ++rowit) { 1890 const QSize sh = compute_size_hint_for_row((*rowit), mTheme->iconSize(), item); 1891 totalh += sh.height(); 1892 if (sh.width() > maxw) { 1893 maxw = sh.width(); 1894 } 1895 } 1896 1897 return {maxw + marginw, totalh + marginh}; 1898 } 1899 1900 QSize ThemeDelegate::sizeHint(const QStyleOptionViewItem &, const QModelIndex &index) const 1901 { 1902 if (!mTheme || !index.isValid()) { 1903 return {16, 16}; // hm hm... 1904 } 1905 1906 Item *item = itemFromIndex(index); 1907 if (!item) { 1908 return {16, 16}; // hm... 1909 } 1910 1911 const Item::Type type = item->type(); 1912 if (type == Item::Message) { 1913 if (!mCachedMessageItemSizeHint.isValid()) { 1914 mCachedMessageItemSizeHint = sizeHintForItemTypeAndColumn(Item::Message, index.column(), item); 1915 } 1916 return mCachedMessageItemSizeHint; 1917 } else if (type == Item::GroupHeader) { 1918 if (!mCachedGroupHeaderItemSizeHint.isValid()) { 1919 mCachedGroupHeaderItemSizeHint = sizeHintForItemTypeAndColumn(Item::GroupHeader, index.column(), item); 1920 } 1921 return mCachedGroupHeaderItemSizeHint; 1922 } else { 1923 Q_ASSERT(false); 1924 return {}; 1925 } 1926 } 1927 1928 // Store the new fonts when the generalFont changes and flush sizeHint cache 1929 void ThemeDelegate::generalFontChanged() 1930 { 1931 mCachedMessageItemSizeHint = QSize(); 1932 mCachedGroupHeaderItemSizeHint = QSize(); 1933 1934 QFont font; 1935 if (MessageCore::MessageCoreSettings::self()->useDefaultFonts()) { 1936 font = QFontDatabase::systemFont(QFontDatabase::GeneralFont); 1937 } else { 1938 font = MessageListSettings::self()->messageListFont(); 1939 } 1940 sFontCache[Normal] = font; 1941 sFontMetricsCache[Normal] = QFontMetrics(font); 1942 font.setBold(true); 1943 sFontCache[Bold] = font; 1944 sFontMetricsCache[Bold] = QFontMetrics(font); 1945 font.setBold(false); 1946 font.setItalic(true); 1947 sFontCache[Italic] = font; 1948 sFontMetricsCache[Italic] = QFontMetrics(font); 1949 font.setBold(true); 1950 font.setItalic(true); 1951 sFontCache[BoldItalic] = font; 1952 sFontMetricsCache[BoldItalic] = QFontMetrics(font); 1953 1954 sFontHeightCache = sFontMetricsCache[Normal].height(); 1955 } 1956 1957 const Theme *ThemeDelegate::theme() const 1958 { 1959 return mTheme; 1960 } 1961 1962 #include "moc_themedelegate.cpp"