Messagelib

themeeditor.cpp
1/******************************************************************************
2 *
3 * SPDX-FileCopyrightText: 2008 Szymon Tomasz Stefanek <pragma@kvirc.net>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 *
7 *******************************************************************************/
8
9#include "utils/themeeditor.h"
10#include "core/groupheaderitem.h"
11#include "core/messageitem.h"
12#include "core/modelinvariantrowmapper.h"
13#include "utils/comboboxutils.h"
14#include <KLocalization>
15#include <ki18n_version.h>
16
17#include <Akonadi/MessageStatus>
18
19#include <KTextEdit>
20
21#include <QActionGroup>
22#include <QCheckBox>
23#include <QCursor>
24#include <QDrag>
25#include <QGridLayout>
26#include <QGroupBox>
27#include <QHeaderView>
28#include <QMimeData>
29#include <QMouseEvent>
30#include <QPaintEvent>
31#include <QPainter>
32#include <QPixmap>
33#include <QPushButton>
34#include <QStringList>
35
36#include <KIconLoader>
37#include <KLocalizedString>
38#include <QColorDialog>
39#include <QComboBox>
40#include <QLineEdit>
41#include <QMenu>
42#include <QSpinBox>
43
44#include <QDialogButtonBox>
45#include <QVBoxLayout>
46#include <ctime> // for time_t
47
48using namespace MessageList::Utils;
49using namespace MessageList::Core;
50
51static const char gThemeContentItemTypeDndMimeDataFormat[] = "application/x-kmail-messagelistview-theme-contentitem-type";
52
53ThemeColumnPropertiesDialog::ThemeColumnPropertiesDialog(QWidget *parent, Theme::Column *column, const QString &title)
54 : QDialog(parent)
55 , mColumn(column)
56{
57 auto mainLayout = new QVBoxLayout(this);
59 QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
60 okButton->setDefault(true);
62 connect(buttonBox, &QDialogButtonBox::rejected, this, &ThemeColumnPropertiesDialog::reject);
63 setWindowTitle(title);
64
65 auto base = new QWidget(this);
66 mainLayout->addWidget(base);
67 mainLayout->addWidget(buttonBox);
68
69 auto g = new QGridLayout(base);
70
71 auto l = new QLabel(i18nc("@label:textbox Property name", "Name:"), base);
72 g->addWidget(l, 0, 0);
73
74 mNameEdit = new QLineEdit(base);
75 mNameEdit->setToolTip(i18nc("@info:tooltip", "The label that will be displayed in the column header."));
76 g->addWidget(mNameEdit, 0, 1);
77
78 l = new QLabel(i18nc("@label:textbox", "Header click sorts messages:"), base);
79 g->addWidget(l, 1, 0);
80
81 mMessageSortingCombo = new QComboBox(base);
82 mMessageSortingCombo->setToolTip(i18nc("@info:tooltip", "The sorting order that clicking on this column header will switch to."));
83 g->addWidget(mMessageSortingCombo, 1, 1);
84
85 mVisibleByDefaultCheck = new QCheckBox(i18nc("@option:check", "Visible by default"), base);
86 mVisibleByDefaultCheck->setToolTip(i18nc("@info:tooltip", "Check this if this column should be visible when the theme is selected."));
87 g->addWidget(mVisibleByDefaultCheck, 2, 1);
88
89 mIsSenderOrReceiverCheck = new QCheckBox(i18nc("@option:check", "Contains \"Sender or Receiver\" field"), base);
90 mIsSenderOrReceiverCheck->setToolTip(
91 i18nc("@info:tooltip", "Check this if this column label should be updated depending on the folder \"inbound\"/\"outbound\" type."));
92 g->addWidget(mIsSenderOrReceiverCheck, 3, 1);
93
94 g->setColumnStretch(1, 1);
95 g->setRowStretch(10, 1);
96
97 connect(okButton, &QPushButton::clicked, this, &ThemeColumnPropertiesDialog::slotOkButtonClicked);
98
99 // Display the current settings
100 mNameEdit->setText(mColumn->label());
101 mVisibleByDefaultCheck->setChecked(mColumn->visibleByDefault());
102 mIsSenderOrReceiverCheck->setChecked(mColumn->isSenderOrReceiver());
104 ComboBoxUtils::setIntegerOptionComboValue(mMessageSortingCombo, mColumn->messageSorting());
105}
106
107void ThemeColumnPropertiesDialog::slotOkButtonClicked()
108{
109 QString text = mNameEdit->text();
110 if (text.isEmpty()) {
111 text = i18n("Unnamed Column");
112 }
113 mColumn->setLabel(text);
114 mColumn->setVisibleByDefault(mVisibleByDefaultCheck->isChecked());
115 mColumn->setIsSenderOrReceiver(mIsSenderOrReceiverCheck->isChecked());
116 mColumn->setMessageSorting(
118
119 accept();
120}
121
122ThemeContentItemSourceLabel::ThemeContentItemSourceLabel(QWidget *parent, Theme::ContentItem::Type type)
123 : QLabel(parent)
124 , mType(type)
125{
126 setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
127}
128
129ThemeContentItemSourceLabel::~ThemeContentItemSourceLabel() = default;
130
131MessageList::Core::Theme::ContentItem::Type ThemeContentItemSourceLabel::type() const
132{
133 return mType;
134}
135
136void ThemeContentItemSourceLabel::mousePressEvent(QMouseEvent *e)
137{
138 if (e->button() == Qt::LeftButton) {
139 mMousePressPoint = e->pos();
140 }
141}
142
143void ThemeContentItemSourceLabel::mouseMoveEvent(QMouseEvent *e)
144{
145 if (e->buttons() & Qt::LeftButton) {
146 const QPoint diff = mMousePressPoint - e->pos();
147 if (diff.manhattanLength() > 4) {
148 startDrag();
149 }
150 }
151}
152
153void ThemeContentItemSourceLabel::startDrag()
154{
155 // QPixmap pix = QPixmap::grabWidget( this );
156 // QPixmap alpha( pix.width(), pix.height() );
157 // alpha.fill(0x0f0f0f0f);
158 // pix.setAlphaChannel( alpha ); // <-- this crashes... no alpha for dragged pixmap :(
159 auto data = new QMimeData();
160 QByteArray arry;
161 arry.resize(sizeof(Theme::ContentItem::Type));
162 *((Theme::ContentItem::Type *)arry.data()) = mType;
163 data->setData(QLatin1StringView(gThemeContentItemTypeDndMimeDataFormat), arry);
164 auto drag = new QDrag(this);
165 drag->setMimeData(data);
166 // drag->setPixmap( pix );
167 // drag->setHotSpot( mapFromGlobal( QCursor::pos() ) );
168 drag->exec(Qt::CopyAction, Qt::CopyAction);
169}
170
171ThemePreviewDelegate::ThemePreviewDelegate(QAbstractItemView *parent)
172 : ThemeDelegate(parent)
173{
174 mRowMapper = new ModelInvariantRowMapper();
175
176 mSampleGroupHeaderItem = new GroupHeaderItem(i18n("Message Group"));
177
178 mSampleGroupHeaderItem->setDate(time(nullptr));
179 mSampleGroupHeaderItem->setMaxDate(time(nullptr) + 31337);
180 mSampleGroupHeaderItem->setSubject(i18n("Very long subject very long subject very long subject very long subject very long subject very long"));
181
182 mSampleMessageItem = new FakeItem();
183
184 mSampleMessageItem->setDate(time(nullptr));
185 mSampleMessageItem->setSize(0x31337);
186 mSampleMessageItem->setMaxDate(time(nullptr) + 31337);
187 mSampleMessageItem->setSender(i18n("Sender"));
188 mSampleMessageItem->setReceiver(i18n("Receiver"));
189 mSampleMessageItem->setSubject(i18n("Very long subject very long subject very long subject very long subject very long subject very long"));
190 mSampleMessageItem->setFolder(i18n("Folder"));
191 mSampleMessageItem->setSignatureState(MessageItem::FullySigned);
192 mSampleMessageItem->setEncryptionState(MessageItem::FullyEncrypted);
193
195 list.append(new MessageItem::Tag(QIcon::fromTheme(QStringLiteral("feed-subscribe")).pixmap(KIconLoader::SizeSmall), i18n("Sample Tag 1"), QString()));
196 list.append(new MessageItem::Tag(QIcon::fromTheme(QStringLiteral("feed-subscribe")).pixmap(KIconLoader::SizeSmall), i18n("Sample Tag 2"), QString()));
197 list.append(new MessageItem::Tag(QIcon::fromTheme(QStringLiteral("feed-subscribe")).pixmap(KIconLoader::SizeSmall), i18n("Sample Tag 3"), QString()));
198 mSampleMessageItem->setFakeTags(list);
199
200 mRowMapper->createModelInvariantIndex(0, mSampleMessageItem);
201
202 mSampleGroupHeaderItem->rawAppendChildItem(mSampleMessageItem);
203 mSampleMessageItem->setParent(mSampleGroupHeaderItem);
204
206
207 stat.fromQInt32(0x7fffffff);
208 stat.setQueued(false);
209 stat.setSent(false);
210 stat.setSpam(true);
211 stat.setWatched(true);
212 stat.setHasInvitation();
213 // stat.setHasAttachment( false );
214
215 mSampleMessageItem->setStatus(stat);
216}
217
218ThemePreviewDelegate::~ThemePreviewDelegate()
219{
220 delete mSampleGroupHeaderItem;
221 // delete mSampleMessageItem; (deleted by the parent)
222 delete mRowMapper;
223}
224
225Item *ThemePreviewDelegate::itemFromIndex(const QModelIndex &index) const
226{
227 if (index.parent().isValid()) {
228 return mSampleMessageItem;
229 }
230
231 return mSampleGroupHeaderItem;
232}
233
234ThemePreviewWidget::ThemePreviewWidget(QWidget *parent)
235 : QTreeWidget(parent)
236 , mTheme(nullptr)
237{
238 mSelectedThemeContentItem = nullptr;
239 mSelectedThemeColumn = nullptr;
240 mFirstShow = true;
241 mReadOnly = false;
242
243 mDelegate = new ThemePreviewDelegate(this);
244 setItemDelegate(mDelegate);
245 setRootIsDecorated(false);
246 viewport()->setAcceptDrops(true);
247
248 header()->setContextMenuPolicy(Qt::CustomContextMenu);
249 connect(header(), &QWidget::customContextMenuRequested, this, &ThemePreviewWidget::slotHeaderContextMenuRequested);
250
251 mGroupHeaderSampleItem = new QTreeWidgetItem(this);
252 mGroupHeaderSampleItem->setText(0, QString());
253 mGroupHeaderSampleItem->setFlags(Qt::ItemIsEnabled);
254
255 auto m = new QTreeWidgetItem(mGroupHeaderSampleItem);
256 m->setText(0, QString());
257
258 mGroupHeaderSampleItem->setExpanded(true);
259 header()->setSectionsMovable(false);
260}
261
262void ThemePreviewWidget::changeEvent(QEvent *event)
263{
264 if (event->type() == QEvent::FontChange) {
265 mDelegate->generalFontChanged();
266 }
268}
269
270ThemePreviewWidget::~ThemePreviewWidget() = default;
271
272QSize ThemePreviewWidget::sizeHint() const
273{
274 return {350, 180};
275}
276
277void ThemePreviewWidget::setReadOnly(bool readOnly)
278{
279 mReadOnly = readOnly;
280}
281
282void ThemePreviewWidget::applyThemeColumnWidths()
283{
284 if (!mTheme) {
285 return;
286 }
287
288 const QList<Theme::Column *> &columns = mTheme->columns();
289
290 if (columns.isEmpty()) {
291 viewport()->update(); // trigger a repaint
292 return;
293 }
294
295 // Now we want to distribute the available width on all the columns.
296 // The algorithm used here is very similar to the one used in View::applyThemeColumns().
297 // It just takes care of ALL the columns instead of the visible ones.
298
300
301 // Gather size hints for all sections.
302 int idx = 0;
303 int totalVisibleWidthHint = 0;
305
306 for (it = columns.constBegin(); it != end; ++it) {
307 totalVisibleWidthHint += mDelegate->sizeHintForItemTypeAndColumn(Item::Message, idx).width();
308 idx++;
309 }
310
311 if (totalVisibleWidthHint < 16) {
312 totalVisibleWidthHint = 16; // be reasonable
313 }
314
315 // Now we can compute proportional widths.
316
317 idx = 0;
318
319 QList<int> realWidths;
320 realWidths.reserve(columns.count());
321 int totalVisibleWidth = 0;
322
323 end = columns.constEnd();
324 for (it = columns.constBegin(); it != end; ++it) {
325 int hintWidth = mDelegate->sizeHintForItemTypeAndColumn(Item::Message, idx).width();
326 int realWidth;
327 if ((*it)->containsTextItems()) {
328 // the column contains text items, it should get more space
329 realWidth = ((hintWidth * viewport()->width()) / totalVisibleWidthHint) - 2; // -2 is heuristic
330 if (realWidth < (hintWidth + 2)) {
331 realWidth = hintWidth + 2; // can't be less
332 }
333 } else {
334 // the column contains no text items, it should get just a little bit more than its sizeHint().
335 realWidth = hintWidth + 2;
336 }
337
338 realWidths.append(realWidth);
339 totalVisibleWidth += realWidth;
340
341 idx++;
342 }
343
344 idx = 0;
345
346 totalVisibleWidth += 4; // account for some view's border
347
348 if (totalVisibleWidth < viewport()->width()) {
349 // give the additional space to the text columns
350 // also give more space to the first ones and less space to the last ones
351 int available = viewport()->width() - totalVisibleWidth;
352
353 for (it = columns.begin(); it != columns.end(); ++it) {
354 if (((*it)->visibleByDefault() || (idx == 0)) && (*it)->containsTextItems()) {
355 // give more space to this column
356 available >>= 1; // eat half of the available space
357 realWidths[idx] += available; // and give it to this column
358 }
359
360 idx++;
361 }
362
363 // if any space is still available, give it to the first column
364 if (available) {
365 realWidths[0] += available;
366 }
367 }
368
369 idx = 0;
370
371 // We're ready.
372 // Assign widths. Hide the sections that are not visible by default, show the other ones.
373 for (it = columns.begin(); it != columns.end(); ++it) {
374 header()->resizeSection(idx, realWidths[idx]);
375 idx++;
376 }
377
378#if 0
379 if (mTheme->viewHeaderPolicy() == Theme::NeverShowHeader) {
380 header()->hide();
381 } else {
382 header()->show();
383 }
384#endif
385}
386
387void ThemePreviewWidget::setTheme(Theme *theme)
388{
389 bool themeChanged = theme != mTheme;
390
391 mSelectedThemeContentItem = nullptr;
392 mThemeSelectedContentItemRect = QRect();
393 mDropIndicatorPoint1 = QPoint();
394 mDropIndicatorPoint2 = QPoint();
395 mTheme = theme;
396 mDelegate->setTheme(theme);
397 if (!mTheme) {
398 return;
399 }
400 mGroupHeaderSampleItem->setExpanded(true);
401
402 const QList<Theme::Column *> &columns = mTheme->columns();
403
404 setColumnCount(columns.count());
405
406 QStringList headerLabels;
407 headerLabels.reserve(columns.count());
409 for (QList<Theme::Column *>::ConstIterator it = columns.constBegin(); it != end; ++it) {
410 QString label = (*it)->label();
411 if ((*it)->visibleByDefault()) {
412 label += QStringLiteral(" (%1)").arg(i18nc("Indicates whether or not a header label is visible", "Visible"));
413 }
414
415 headerLabels.append(label);
416 }
417
418 setHeaderLabels(headerLabels);
419
420 if (themeChanged) {
421 applyThemeColumnWidths();
422 }
423
424 viewport()->update(); // trigger a repaint
425}
426
427void ThemePreviewWidget::internalHandleDragEnterEvent(QDragEnterEvent *e)
428{
429 e->ignore();
430
431 if (!e->mimeData()) {
432 return;
433 }
434 if (!e->mimeData()->hasFormat(QLatin1StringView(gThemeContentItemTypeDndMimeDataFormat))) {
435 return;
436 }
437
438 e->accept();
439}
440
441void ThemePreviewWidget::showEvent(QShowEvent *e)
442{
444
445 if (mFirstShow) {
446 // Make sure we re-apply column widths the first time we're shown.
447 // The first "apply" call was made while the widget was still hidden and
448 // almost surely had wrong sizes.
449 applyThemeColumnWidths();
450 mFirstShow = false;
451 }
452}
453
454void ThemePreviewWidget::dragEnterEvent(QDragEnterEvent *e)
455{
456 internalHandleDragEnterEvent(e);
457
458 mThemeSelectedContentItemRect = QRect();
459
460 viewport()->update(); // trigger a repaint
461}
462
463void ThemePreviewWidget::internalHandleDragMoveEvent(QDragMoveEvent *e)
464{
465 e->ignore();
466
467 if (mReadOnly) {
468 return;
469 }
470
471 if (!e->mimeData()) {
472 return;
473 }
474 if (!e->mimeData()->hasFormat(QLatin1StringView(gThemeContentItemTypeDndMimeDataFormat))) {
475 return;
476 }
477
478 QByteArray arry = e->mimeData()->data(QLatin1StringView(gThemeContentItemTypeDndMimeDataFormat));
479
480 if (arry.size() != sizeof(Theme::ContentItem::Type)) {
481 return; // ugh
482 }
483
485 if (!computeContentItemInsertPosition(e->position().toPoint(), type)) {
486 return;
487 }
488
489 e->accept();
490}
491
492void ThemePreviewWidget::dragMoveEvent(QDragMoveEvent *e)
493{
494 if (mReadOnly) {
495 return;
496 }
497
498 internalHandleDragMoveEvent(e);
499
500 mThemeSelectedContentItemRect = QRect();
501
502 viewport()->update(); // trigger a repaint
503}
504
505void ThemePreviewWidget::dropEvent(QDropEvent *e)
506{
507 mDropIndicatorPoint1 = mDropIndicatorPoint2;
508
509 e->ignore();
510
511 if (mReadOnly) {
512 return;
513 }
514
515 if (!e->mimeData()) {
516 return;
517 }
518
519 if (!e->mimeData()->hasFormat(QLatin1StringView(gThemeContentItemTypeDndMimeDataFormat))) {
520 return;
521 }
522
523 QByteArray arry = e->mimeData()->data(QLatin1StringView(gThemeContentItemTypeDndMimeDataFormat));
524
525 if (arry.size() != sizeof(Theme::ContentItem::Type)) {
526 return; // ugh
527 }
528
530 if (!computeContentItemInsertPosition(e->position().toPoint(), type)) {
531 viewport()->update();
532 return;
533 }
534
535 Theme::Row *row = nullptr;
536
537 switch (mRowInsertPosition) {
538 case AboveRow:
539 row = new Theme::Row();
540 if (mDelegate->hitItem()->type() == Item::Message) {
541 const_cast<Theme::Column *>(mDelegate->hitColumn())->insertMessageRow(mDelegate->hitRowIndex(), row);
542 } else {
543 const_cast<Theme::Column *>(mDelegate->hitColumn())->insertGroupHeaderRow(mDelegate->hitRowIndex(), row);
544 }
545 break;
546 case InsideRow:
547 row = const_cast<Theme::Row *>(mDelegate->hitRow());
548 break;
549 case BelowRow:
550 row = new Theme::Row();
551 if (mDelegate->hitItem()->type() == Item::Message) {
552 const_cast<Theme::Column *>(mDelegate->hitColumn())->insertMessageRow(mDelegate->hitRowIndex() + 1, row);
553 } else {
554 const_cast<Theme::Column *>(mDelegate->hitColumn())->insertGroupHeaderRow(mDelegate->hitRowIndex() + 1, row);
555 }
556 break;
557 }
558
559 if (!row) {
560 return;
561 }
562
563 auto ci = new Theme::ContentItem(type);
564 if (ci->canBeDisabled()) {
565 if (ci->isClickable()) {
566 ci->setSoftenByBlendingWhenDisabled(true); // default to softened
567 } else {
568 ci->setHideWhenDisabled(true); // default to hidden
569 }
570 }
571
572 int idx;
573
574 switch (mItemInsertPosition) {
575 case OnLeftOfItem:
576 if (!mDelegate->hitContentItem()) {
577 // bleah
578 delete ci;
579 return;
580 }
581 idx = mDelegate->hitContentItemRight() ? row->rightItems().indexOf(const_cast<Theme::ContentItem *>(mDelegate->hitContentItem()))
582 : row->leftItems().indexOf(const_cast<Theme::ContentItem *>(mDelegate->hitContentItem()));
583 if (idx < 0) {
584 // bleah
585 delete ci;
586 return;
587 }
588 if (mDelegate->hitContentItemRight()) {
589 row->insertRightItem(idx + 1, ci);
590 } else {
591 row->insertLeftItem(idx, ci);
592 }
593 break;
594 case OnRightOfItem:
595 if (!mDelegate->hitContentItem()) {
596 // bleah
597 delete ci;
598 return;
599 }
600 idx = mDelegate->hitContentItemRight() ? row->rightItems().indexOf(const_cast<Theme::ContentItem *>(mDelegate->hitContentItem()))
601 : row->leftItems().indexOf(const_cast<Theme::ContentItem *>(mDelegate->hitContentItem()));
602 if (idx < 0) {
603 // bleah
604 delete ci;
605 return;
606 }
607 if (mDelegate->hitContentItemRight()) {
608 row->insertRightItem(idx, ci);
609 } else {
610 row->insertLeftItem(idx + 1, ci);
611 }
612 break;
613 case AsLastLeftItem:
614 row->addLeftItem(ci);
615 break;
616 case AsLastRightItem:
617 row->addRightItem(ci);
618 break;
619 case AsFirstLeftItem:
620 row->insertLeftItem(0, ci);
621 break;
622 case AsFirstRightItem:
623 row->insertRightItem(0, ci);
624 break;
625 default: // should never happen
626 row->addRightItem(ci);
627 break;
628 }
629
631
632 mThemeSelectedContentItemRect = QRect();
633 mDropIndicatorPoint1 = mDropIndicatorPoint2;
634 mSelectedThemeContentItem = nullptr;
635
636 setTheme(mTheme); // this will reset theme cache and trigger a global update
637}
638
639bool ThemePreviewWidget::computeContentItemInsertPosition(const QPoint &pos, Theme::ContentItem::Type type)
640{
641 mDropIndicatorPoint1 = mDropIndicatorPoint2; // this marks the position as invalid
642
643 if (!mDelegate->hitTest(pos, false)) {
644 return false;
645 }
646
647 if (!mDelegate->hitRow()) {
648 return false;
649 }
650
651 if (mDelegate->hitRowIsMessageRow()) {
653 return false;
654 }
655 } else {
657 return false;
658 }
659 }
660
661 QRect rowRect = mDelegate->hitRowRect();
662
663 if (pos.y() < rowRect.top() + 3) {
664 // above a row
665 mRowInsertPosition = AboveRow;
666 if (pos.x() < (rowRect.left() + (rowRect.width() / 2))) {
667 mDropIndicatorPoint1 = rowRect.topLeft();
668 mItemInsertPosition = AsLastLeftItem;
669 } else {
670 mDropIndicatorPoint1 = rowRect.topRight();
671 mItemInsertPosition = AsLastRightItem;
672 }
673 mDropIndicatorPoint2 = QPoint(rowRect.left() + (rowRect.width() / 2), rowRect.top());
674 return true;
675 }
676
677 if (pos.y() > rowRect.bottom() - 3) {
678 // below a row
679 mRowInsertPosition = BelowRow;
680 if (pos.x() < (rowRect.left() + (rowRect.width() / 2))) {
681 mDropIndicatorPoint1 = rowRect.bottomLeft();
682 mItemInsertPosition = AsLastLeftItem;
683 } else {
684 mDropIndicatorPoint1 = rowRect.bottomRight();
685 mItemInsertPosition = AsLastRightItem;
686 }
687 mDropIndicatorPoint2 = QPoint(rowRect.left() + (rowRect.width() / 2), rowRect.bottom());
688 return true;
689 }
690
691 mRowInsertPosition = InsideRow;
692
693 if (!mDelegate->hitContentItem()) {
694 // didn't hit anything... probably no items in the row
695 if (pos.x() < (rowRect.left() + (rowRect.width() / 2))) {
696 mItemInsertPosition = AsLastLeftItem;
697 mDropIndicatorPoint1 = QPoint(rowRect.left(), rowRect.top());
698 mDropIndicatorPoint2 = QPoint(rowRect.left(), rowRect.bottom());
699 } else {
700 mItemInsertPosition = AsLastRightItem;
701 mDropIndicatorPoint1 = QPoint(rowRect.right(), rowRect.top());
702 mDropIndicatorPoint2 = QPoint(rowRect.right(), rowRect.bottom());
703 }
704 return true;
705 }
706
707 // hit something, maybe inexactly
708 QRect itemRect = mDelegate->hitContentItemRect();
709
710 if (!itemRect.contains(pos)) {
711 // inexact hit: outside an item
712 if (pos.x() > itemRect.right()) {
713 // right side of an item
714 if (mDelegate->hitRow()->rightItems().count() < 1) {
715 // between the last left item and the right side
716 if (pos.x() > (itemRect.right() + ((rowRect.right() - itemRect.right()) / 2))) {
717 // first/last right item
718 mItemInsertPosition = AsFirstRightItem;
719 mDropIndicatorPoint1 = rowRect.topRight();
720 mDropIndicatorPoint2 = rowRect.bottomRight();
721 }
722 return true;
723 }
724 // either there were some right items (so the theme delegate knows that the reported item is the closest)
725 // or there were no right items but the position is closest to the left item than the right row end
726 mItemInsertPosition = OnRightOfItem;
727 mDropIndicatorPoint1 = itemRect.topRight();
728 mDropIndicatorPoint2 = itemRect.bottomRight();
729 return true;
730 }
731
732 // left side of an item
733 if (mDelegate->hitRow()->leftItems().count() < 1) {
734 // between the left side and the leftmost right item
735 if (pos.x() < (itemRect.left() - ((itemRect.left() - rowRect.left()) / 2))) {
736 mItemInsertPosition = AsFirstLeftItem;
737 mDropIndicatorPoint1 = rowRect.topLeft();
738 mDropIndicatorPoint2 = rowRect.bottomLeft();
739 return true;
740 }
741 }
742 mItemInsertPosition = OnLeftOfItem;
743 mDropIndicatorPoint1 = itemRect.topLeft();
744 mDropIndicatorPoint2 = itemRect.bottomLeft();
745 return true;
746 }
747
748 // exact hit
749 if (pos.x() < (itemRect.left() + (itemRect.width() / 2))) {
750 // left side
751 mItemInsertPosition = OnLeftOfItem;
752 mDropIndicatorPoint1 = itemRect.topLeft();
753 mDropIndicatorPoint2 = itemRect.bottomLeft();
754 return true;
755 }
756
757 // right side
758 mItemInsertPosition = OnRightOfItem;
759 mDropIndicatorPoint1 = itemRect.topRight();
760 mDropIndicatorPoint2 = itemRect.bottomRight();
761 return true;
762}
763
764void ThemePreviewWidget::mouseMoveEvent(QMouseEvent *e)
765{
766 if (!(mSelectedThemeContentItem && (e->buttons() & Qt::LeftButton)) || mReadOnly) {
768 return;
769 }
770
771 if (mSelectedThemeContentItem != mDelegate->hitContentItem()) {
773 return; // ugh.. something weird happened
774 }
775
776 // starting a drag ?
777 const QPoint diff = e->pos() - mMouseDownPoint;
778 if (diff.manhattanLength() <= 4) {
780 return; // ugh.. something weird happened
781 }
782
783 // starting a drag
784 auto data = new QMimeData();
785 QByteArray arry;
786 arry.resize(sizeof(Theme::ContentItem::Type));
787 *((Theme::ContentItem::Type *)arry.data()) = mSelectedThemeContentItem->type();
788 data->setData(QLatin1StringView(gThemeContentItemTypeDndMimeDataFormat), arry);
789 auto drag = new QDrag(this);
790 drag->setMimeData(data);
791
792 // remove the Theme::ContentItem from the Theme
793 if (mDelegate->hitContentItemRight()) {
794 const_cast<Theme::Row *>(mDelegate->hitRow())->removeRightItem(mSelectedThemeContentItem);
795 } else {
796 const_cast<Theme::Row *>(mDelegate->hitRow())->removeLeftItem(mSelectedThemeContentItem);
797 }
798
799 delete mSelectedThemeContentItem;
800
801 if (mDelegate->hitRow()->rightItems().isEmpty() && mDelegate->hitRow()->leftItems().isEmpty()) {
802 if (mDelegate->hitItem()->type() == Item::Message) {
803 if (mDelegate->hitColumn()->messageRows().count() > 1) {
804 const_cast<Theme::Column *>(mDelegate->hitColumn())->removeMessageRow(const_cast<Theme::Row *>(mDelegate->hitRow()));
805 delete mDelegate->hitRow();
806 }
807 } else {
808 if (mDelegate->hitColumn()->groupHeaderRows().count() > 1) {
809 const_cast<Theme::Column *>(mDelegate->hitColumn())->removeGroupHeaderRow(const_cast<Theme::Row *>(mDelegate->hitRow()));
810 delete mDelegate->hitRow();
811 }
812 }
813 }
814
815 mSelectedThemeContentItem = nullptr;
816 mThemeSelectedContentItemRect = QRect();
817 mDropIndicatorPoint1 = mDropIndicatorPoint2;
818
819 setTheme(mTheme); // this will reset theme cache and trigger a global update
820
821 // and do drag
822 drag->exec(Qt::CopyAction, Qt::CopyAction);
823}
824
825void ThemePreviewWidget::mousePressEvent(QMouseEvent *e)
826{
827 if (mReadOnly) {
829 return;
830 }
831
832 mMouseDownPoint = e->pos();
833
834 if (mDelegate->hitTest(mMouseDownPoint)) {
835 mSelectedThemeContentItem = const_cast<Theme::ContentItem *>(mDelegate->hitContentItem());
836 mThemeSelectedContentItemRect = mSelectedThemeContentItem ? mDelegate->hitContentItemRect() : QRect();
837 } else {
838 mSelectedThemeContentItem = nullptr;
839 mThemeSelectedContentItemRect = QRect();
840 }
841
843 viewport()->update();
844
845 if (e->button() == Qt::RightButton) {
846 QMenu menu;
847
848 if (mSelectedThemeContentItem) {
849 menu.addSection(Theme::ContentItem::description(mSelectedThemeContentItem->type()));
850
851 if (mSelectedThemeContentItem->displaysText()) {
852 QAction *act = menu.addAction(i18nc("@action:inmenu soften the text color", "Soften"));
853 act->setCheckable(true);
854 act->setChecked(mSelectedThemeContentItem->softenByBlending());
855 connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotSoftenActionTriggered);
856
857 auto childmenu = new QMenu(&menu);
858
859 act = childmenu->addAction(i18nc("@action:inmenu Font setting", "Bold"));
860 act->setData(QVariant(static_cast<int>(Theme::ContentItem::IsBold)));
861 act->setCheckable(true);
862 act->setChecked(mSelectedThemeContentItem->isBold());
863 act = childmenu->addAction(i18nc("@action:inmenu Font setting", "Italic"));
864 act->setData(QVariant(static_cast<int>(Theme::ContentItem::IsItalic)));
865 act->setCheckable(true);
866 act->setChecked(mSelectedThemeContentItem->isItalic());
867
868 connect(childmenu, &QMenu::triggered, this, &ThemePreviewWidget::slotFontMenuTriggered);
869
870 menu.addMenu(childmenu)->setText(i18n("Font"));
871 }
872
873 if (mSelectedThemeContentItem->canUseCustomColor()) {
874 auto childmenu = new QMenu(&menu);
875
876 auto grp = new QActionGroup(childmenu);
877
878 QAction *act = childmenu->addAction(i18nc("@action:inmenu Foreground color setting", "Default"));
879 act->setData(QVariant(static_cast<int>(0)));
880 act->setCheckable(true);
881 act->setChecked(!mSelectedThemeContentItem->useCustomColor());
882 grp->addAction(act);
883 act = childmenu->addAction(i18nc("@action:inmenu Foreground color setting", "Custom..."));
884 act->setData(QVariant(static_cast<int>(Theme::ContentItem::UseCustomColor)));
885 act->setCheckable(true);
886 act->setChecked(mSelectedThemeContentItem->useCustomColor());
887 grp->addAction(act);
888
889 connect(childmenu, &QMenu::triggered, this, &ThemePreviewWidget::slotForegroundColorMenuTriggered);
890
891 menu.addMenu(childmenu)->setText(i18n("Foreground Color"));
892 }
893
894 if (mSelectedThemeContentItem->canBeDisabled()) {
895 auto childmenu = new QMenu(&menu);
896
897 auto grp = new QActionGroup(childmenu);
898
899 QAction *act =
900 childmenu->addAction(i18nc("Hide a mark if the mail does not have the attribute, e.g. Important mark on a non important mail", "Hide"));
902 act->setCheckable(true);
903 act->setChecked(mSelectedThemeContentItem->hideWhenDisabled());
904 grp->addAction(act);
905 act = childmenu->addAction(
906 i18nc("Keep a empty space in the list if the mail does not have the attribute, e.g. Important mark on a non important mail",
907 "Keep Empty Space"));
908 act->setData(QVariant(static_cast<int>(0)));
909 act->setCheckable(true);
910 act->setChecked(!(mSelectedThemeContentItem->softenByBlendingWhenDisabled() || mSelectedThemeContentItem->hideWhenDisabled()));
911 grp->addAction(act);
912 act = childmenu->addAction(
913 i18nc("Show the icon softened in the list if the mail does not have the attribute, e.g. Important mark on a non important mail",
914 "Keep Softened Icon"));
916 act->setCheckable(true);
917 act->setChecked(mSelectedThemeContentItem->softenByBlendingWhenDisabled());
918 grp->addAction(act);
919
920 connect(childmenu, &QMenu::triggered, this, &ThemePreviewWidget::slotDisabledFlagsMenuTriggered);
921
922 menu.addMenu(childmenu)->setText(i18n("When Disabled"));
923 }
924 }
925
926 if (mDelegate->hitItem()) {
927 if (mDelegate->hitItem()->type() == Item::GroupHeader) {
928 menu.addSection(i18n("Group Header"));
929
930 // Background color (mode) submenu
931 auto childmenu = new QMenu(&menu);
932
933 auto grp = new QActionGroup(childmenu);
934
935 QAction *act = childmenu->addAction(i18nc("@action:inmenu Group header background color setting", "None"));
936 act->setData(QVariant(static_cast<int>(Theme::Transparent)));
937 act->setCheckable(true);
939 grp->addAction(act);
940 act = childmenu->addAction(i18nc("@action:inmenu Group header background color setting", "Automatic"));
941 act->setData(QVariant(static_cast<int>(Theme::AutoColor)));
942 act->setCheckable(true);
944 grp->addAction(act);
945 act = childmenu->addAction(i18nc("@action:inmenu Group header background color setting", "Custom..."));
946 act->setData(QVariant(static_cast<int>(Theme::CustomColor)));
947 act->setCheckable(true);
949 grp->addAction(act);
950
951 connect(childmenu, &QMenu::triggered, this, &ThemePreviewWidget::slotGroupHeaderBackgroundModeMenuTriggered);
952
953 menu.addMenu(childmenu)->setText(i18n("Background Color"));
954
955 // Background style submenu
956 childmenu = new QMenu(&menu);
957
958 grp = new QActionGroup(childmenu);
960 QList<QPair<QString, int>>::ConstIterator end(styles.constEnd());
961
962 for (QList<QPair<QString, int>>::ConstIterator it = styles.constBegin(); it != end; ++it) {
963 act = childmenu->addAction((*it).first);
964 act->setData(QVariant((*it).second));
965 act->setCheckable(true);
966 act->setChecked(mTheme->groupHeaderBackgroundStyle() == static_cast<Theme::GroupHeaderBackgroundStyle>((*it).second));
967 grp->addAction(act);
968 }
969
970 connect(childmenu, &QMenu::triggered, this, &ThemePreviewWidget::slotGroupHeaderBackgroundStyleMenuTriggered);
971
972 act = menu.addMenu(childmenu);
973 act->setText(i18n("Background Style"));
975 act->setEnabled(false);
976 }
977 }
978 }
979
980 if (menu.isEmpty()) {
981 return;
982 }
983
984 menu.exec(viewport()->mapToGlobal(e->pos()));
985 }
986}
987
988void ThemePreviewWidget::slotDisabledFlagsMenuTriggered(QAction *act)
989{
990 if (!mSelectedThemeContentItem) {
991 return;
992 }
993
994 bool ok;
995 const int flags = act->data().toInt(&ok);
996 if (!ok) {
997 return;
998 }
999
1000 mSelectedThemeContentItem->setHideWhenDisabled(flags == Theme::ContentItem::HideWhenDisabled);
1002
1003 setTheme(mTheme); // this will reset theme cache and trigger a global update
1004}
1005
1006void ThemePreviewWidget::slotForegroundColorMenuTriggered(QAction *act)
1007{
1008 if (!mSelectedThemeContentItem) {
1009 return;
1010 }
1011
1012 bool ok;
1013 const int flag = act->data().toInt(&ok);
1014 if (!ok) {
1015 return;
1016 }
1017
1018 if (flag == 0) {
1019 mSelectedThemeContentItem->setUseCustomColor(false);
1020 setTheme(mTheme); // this will reset theme cache and trigger a global update
1021 return;
1022 }
1023
1024 QColor clr;
1025 clr = QColorDialog::getColor(mSelectedThemeContentItem->customColor(), this);
1026 if (!clr.isValid()) {
1027 return;
1028 }
1029
1030 mSelectedThemeContentItem->setCustomColor(clr);
1031 mSelectedThemeContentItem->setUseCustomColor(true);
1032
1033 setTheme(mTheme); // this will reset theme cache and trigger a global update
1034}
1035
1036void ThemePreviewWidget::slotSoftenActionTriggered(bool)
1037{
1038 if (!mSelectedThemeContentItem) {
1039 return;
1040 }
1041
1042 mSelectedThemeContentItem->setSoftenByBlending(!mSelectedThemeContentItem->softenByBlending());
1043 setTheme(mTheme); // this will reset theme cache and trigger a global update
1044}
1045
1046void ThemePreviewWidget::slotFontMenuTriggered(QAction *act)
1047{
1048 if (!mSelectedThemeContentItem) {
1049 return;
1050 }
1051
1052 bool ok;
1053 const int flag = act->data().toInt(&ok);
1054 if (!ok) {
1055 return;
1056 }
1057
1058 if (flag == Theme::ContentItem::IsBold && mSelectedThemeContentItem->isBold() != act->isChecked()) {
1059 mSelectedThemeContentItem->setBold(act->isChecked());
1060 setTheme(mTheme);
1061 } else if (flag == Theme::ContentItem::IsItalic && mSelectedThemeContentItem->isItalic() != act->isChecked()) {
1062 mSelectedThemeContentItem->setItalic(act->isChecked());
1063 setTheme(mTheme);
1064 }
1065}
1066
1067void ThemePreviewWidget::slotGroupHeaderBackgroundModeMenuTriggered(QAction *act)
1068{
1069 bool ok;
1070 Theme::GroupHeaderBackgroundMode mode = static_cast<Theme::GroupHeaderBackgroundMode>(act->data().toInt(&ok));
1071 if (!ok) {
1072 return;
1073 }
1074
1075 switch (mode) {
1076 case Theme::Transparent:
1078 break;
1079 case Theme::AutoColor:
1081 break;
1082 case Theme::CustomColor: {
1083 QColor clr;
1085 if (!clr.isValid()) {
1086 return;
1087 }
1088
1090 mTheme->setGroupHeaderBackgroundColor(clr);
1091 break;
1092 }
1093 }
1094
1095 setTheme(mTheme); // this will reset theme cache and trigger a global update
1096}
1097
1098void ThemePreviewWidget::slotGroupHeaderBackgroundStyleMenuTriggered(QAction *act)
1099{
1100 bool ok;
1101 Theme::GroupHeaderBackgroundStyle mode = static_cast<Theme::GroupHeaderBackgroundStyle>(act->data().toInt(&ok));
1102 if (!ok) {
1103 return;
1104 }
1105
1106 mTheme->setGroupHeaderBackgroundStyle(mode);
1107
1108 setTheme(mTheme); // this will reset theme cache and trigger a global update
1109}
1110
1111void ThemePreviewWidget::paintEvent(QPaintEvent *e)
1112{
1114
1115 if (mThemeSelectedContentItemRect.isValid() || (mDropIndicatorPoint1 != mDropIndicatorPoint2)) {
1116 QPainter painter(viewport());
1117
1118 if (mThemeSelectedContentItemRect.isValid()) {
1119 painter.setPen(QPen(Qt::black));
1120 painter.drawRect(mThemeSelectedContentItemRect);
1121 }
1122 if (mDropIndicatorPoint1 != mDropIndicatorPoint2) {
1123 painter.setPen(QPen(Qt::black, 3));
1124 painter.drawLine(mDropIndicatorPoint1, mDropIndicatorPoint2);
1125 }
1126 }
1127}
1128
1129void ThemePreviewWidget::slotHeaderContextMenuRequested(const QPoint &pos)
1130{
1131 if (mReadOnly) {
1132 return;
1133 }
1134
1135 QTreeWidgetItem *hitem = headerItem();
1136 if (!hitem) {
1137 return; // ooops
1138 }
1139
1140 int col = header()->logicalIndexAt(pos);
1141
1142 if (col < 0) {
1143 return;
1144 }
1145
1146 if (col >= mTheme->columns().count()) {
1147 return;
1148 }
1149
1150 mSelectedThemeColumn = mTheme->column(col);
1151 if (!mSelectedThemeColumn) {
1152 return;
1153 }
1154
1155 QMenu menu;
1156
1157 menu.setTitle(mSelectedThemeColumn->label());
1158
1159 QAction *act = menu.addAction(i18n("Column Properties..."));
1160 connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotColumnProperties);
1161
1162 act = menu.addAction(i18n("Add Column..."));
1163 connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotAddColumn);
1164
1165 act = menu.addAction(i18n("Delete Column"));
1166 connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotDeleteColumn);
1167 act->setEnabled(col > 0);
1168
1169 menu.addSeparator();
1170
1171 act = menu.addAction(i18n("Move Column to Left"));
1172 connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotMoveColumnToLeft);
1173 act->setEnabled(col > 0);
1174
1175 act = menu.addAction(i18n("Move Column to Right"));
1176 connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotMoveColumnToRight);
1177 act->setEnabled(col < mTheme->columns().count() - 1);
1178
1179 menu.exec(header()->mapToGlobal(pos));
1180}
1181
1182void ThemePreviewWidget::slotMoveColumnToLeft()
1183{
1184 if (!mSelectedThemeColumn) {
1185 return;
1186 }
1187
1188 const int columnIndex = mTheme->columns().indexOf(mSelectedThemeColumn);
1189 mTheme->moveColumn(columnIndex, columnIndex - 1);
1190 setTheme(mTheme); // this will reset theme cache and trigger a global update
1191}
1192
1193void ThemePreviewWidget::slotMoveColumnToRight()
1194{
1195 if (!mSelectedThemeColumn) {
1196 return;
1197 }
1198
1199 const int columnIndex = mTheme->columns().indexOf(mSelectedThemeColumn);
1200 mTheme->moveColumn(columnIndex, columnIndex + 1);
1201 setTheme(mTheme); // this will reset theme cache and trigger a global update
1202}
1203
1204void ThemePreviewWidget::slotAddColumn()
1205{
1206 int newColumnIndex = mTheme->columns().count();
1207
1208 if (mSelectedThemeColumn) {
1209 newColumnIndex = mTheme->columns().indexOf(mSelectedThemeColumn);
1210 if (newColumnIndex < 0) {
1211 newColumnIndex = mTheme->columns().count();
1212 } else {
1213 newColumnIndex++;
1214 }
1215 }
1216
1217 mSelectedThemeColumn = new Theme::Column();
1218 mSelectedThemeColumn->setLabel(i18n("New Column"));
1219 mSelectedThemeColumn->setVisibleByDefault(true);
1220
1221 mSelectedThemeColumn->addMessageRow(new Theme::Row());
1222 mSelectedThemeColumn->addGroupHeaderRow(new Theme::Row());
1223
1224 auto dlg = new ThemeColumnPropertiesDialog(this, mSelectedThemeColumn, i18n("Add New Column"));
1225
1226 if (dlg->exec() == QDialog::Accepted) {
1227 mTheme->insertColumn(newColumnIndex, mSelectedThemeColumn);
1228
1229 mSelectedThemeContentItem = nullptr;
1230 mThemeSelectedContentItemRect = QRect();
1231 mDropIndicatorPoint1 = mDropIndicatorPoint2;
1232
1233 setTheme(mTheme); // this will reset theme cache and trigger a global update
1234 } else {
1235 delete mSelectedThemeColumn;
1236 mSelectedThemeColumn = nullptr;
1237 }
1238
1239 delete dlg;
1240}
1241
1242void ThemePreviewWidget::slotColumnProperties()
1243{
1244 if (!mSelectedThemeColumn) {
1245 return;
1246 }
1247
1248 auto dlg = new ThemeColumnPropertiesDialog(this, mSelectedThemeColumn, i18n("Column Properties"));
1249
1250 if (dlg->exec() == QDialog::Accepted) {
1251 mSelectedThemeContentItem = nullptr;
1252 mThemeSelectedContentItemRect = QRect();
1253 mDropIndicatorPoint1 = mDropIndicatorPoint2;
1254
1255 setTheme(mTheme); // this will reset theme cache and trigger a global update
1256 }
1257
1258 delete dlg;
1259}
1260
1261void ThemePreviewWidget::slotDeleteColumn()
1262{
1263 if (!mSelectedThemeColumn) {
1264 return;
1265 }
1266
1267 const int idx = mTheme->columns().indexOf(mSelectedThemeColumn);
1268 if (idx < 1) { // first column can't be deleted
1269 return;
1270 }
1271
1272 mTheme->removeColumn(mSelectedThemeColumn);
1273 delete mSelectedThemeColumn;
1274 mSelectedThemeColumn = nullptr;
1275
1276 mSelectedThemeContentItem = nullptr;
1277 mThemeSelectedContentItemRect = QRect();
1278 mDropIndicatorPoint1 = mDropIndicatorPoint2;
1279
1280 setTheme(mTheme); // this will reset theme cache and trigger a global update
1281}
1282
1283ThemeEditor::ThemeEditor(QWidget *parent)
1284 : OptionSetEditor(parent)
1285{
1286 mCurrentTheme = nullptr;
1287
1288 // Appearance tab
1289 auto tab = new QWidget(this);
1290 addTab(tab, i18n("Appearance"));
1291
1292 auto tabg = new QGridLayout(tab);
1293
1294 auto gb = new QGroupBox(i18n("Content Items"), tab);
1295 tabg->addWidget(gb, 0, 0);
1296
1297 auto gblayout = new QGridLayout(gb);
1298
1299 Theme dummyTheme;
1300
1301 auto cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Subject);
1302 cil->setText(Theme::ContentItem::description(cil->type()));
1303 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1304 gblayout->addWidget(cil, 0, 0);
1305
1306 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Date);
1307 cil->setText(Theme::ContentItem::description(cil->type()));
1308 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1309 gblayout->addWidget(cil, 1, 0);
1310
1311 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Size);
1312 cil->setText(Theme::ContentItem::description(cil->type()));
1313 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1314 gblayout->addWidget(cil, 2, 0);
1315
1316 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Sender);
1317 cil->setText(Theme::ContentItem::description(cil->type()));
1318 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1319 gblayout->addWidget(cil, 0, 1);
1320
1321 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Receiver);
1322 cil->setText(Theme::ContentItem::description(cil->type()));
1323 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1324 gblayout->addWidget(cil, 1, 1);
1325
1326 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::SenderOrReceiver);
1327 cil->setText(Theme::ContentItem::description(cil->type()));
1328 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1329 gblayout->addWidget(cil, 2, 1);
1330
1331 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::MostRecentDate);
1332 cil->setText(Theme::ContentItem::description(cil->type()));
1333 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1334 gblayout->addWidget(cil, 0, 2);
1335
1336 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::TagList);
1337 cil->setText(Theme::ContentItem::description(cil->type()));
1338 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1339 gblayout->addWidget(cil, 1, 2);
1340
1341 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Folder);
1342 cil->setText(Theme::ContentItem::description(cil->type()));
1343 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1344 gblayout->addWidget(cil, 2, 2);
1345
1346 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::CombinedReadRepliedStateIcon);
1347 cil->setPixmap(*dummyTheme.pixmap(Theme::IconRepliedAndForwarded));
1348 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1349 gblayout->addWidget(cil, 0, 3);
1350
1351 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::ReadStateIcon);
1352 cil->setPixmap(*dummyTheme.pixmap(Theme::IconNew));
1353 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1354 gblayout->addWidget(cil, 1, 3);
1355
1356 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::RepliedStateIcon);
1357 cil->setPixmap(*dummyTheme.pixmap(Theme::IconReplied));
1358 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1359 gblayout->addWidget(cil, 2, 3);
1360
1361 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::AttachmentStateIcon);
1362 cil->setPixmap(*dummyTheme.pixmap(Theme::IconAttachment));
1363 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1364 gblayout->addWidget(cil, 0, 4);
1365
1366 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::EncryptionStateIcon);
1367 cil->setPixmap(*dummyTheme.pixmap(Theme::IconFullyEncrypted));
1368 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1369 gblayout->addWidget(cil, 1, 4);
1370
1371 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::SignatureStateIcon);
1372 cil->setPixmap(*dummyTheme.pixmap(Theme::IconFullySigned));
1373 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1374 gblayout->addWidget(cil, 2, 4);
1375
1376 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::ActionItemStateIcon);
1377 cil->setPixmap(*dummyTheme.pixmap(Theme::IconActionItem));
1378 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1379 gblayout->addWidget(cil, 0, 5);
1380
1381 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::InvitationIcon);
1382 cil->setPixmap(*dummyTheme.pixmap(Theme::IconInvitation));
1383 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1384 gblayout->addWidget(cil, 2, 5);
1385
1386 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::ImportantStateIcon);
1387 cil->setPixmap(*dummyTheme.pixmap(Theme::IconImportant));
1388 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1389 gblayout->addWidget(cil, 0, 6);
1390
1391 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::SpamHamStateIcon);
1392 cil->setPixmap(*dummyTheme.pixmap(Theme::IconSpam));
1393 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1394 gblayout->addWidget(cil, 1, 6);
1395
1396 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::WatchedIgnoredStateIcon);
1397 cil->setPixmap(*dummyTheme.pixmap(Theme::IconWatched));
1398 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1399 gblayout->addWidget(cil, 2, 6);
1400
1401 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::ExpandedStateIcon);
1402 cil->setPixmap(*dummyTheme.pixmap(Theme::IconShowMore));
1403 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1404 gblayout->addWidget(cil, 0, 7);
1405
1406 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::VerticalLine);
1407 cil->setPixmap(*dummyTheme.pixmap(Theme::IconVerticalLine));
1408 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1409 gblayout->addWidget(cil, 1, 7);
1410
1411 cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::HorizontalSpacer);
1412 cil->setPixmap(*dummyTheme.pixmap(Theme::IconHorizontalSpacer));
1413 cil->setToolTip(Theme::ContentItem::description(cil->type()));
1414 gblayout->addWidget(cil, 2, 7);
1415
1416 mPreviewWidget = new ThemePreviewWidget(tab);
1417 tabg->addWidget(mPreviewWidget, 1, 0);
1418
1419 auto l = new QLabel(tab);
1420 l->setText(
1421 i18n("Right click on the header to add or modify columns. Drag the content items and drop them on the columns in order to compose your theme. Right "
1422 "click on the items inside the view for more options."));
1423 l->setWordWrap(true);
1424 l->setAlignment(Qt::AlignCenter);
1425 tabg->addWidget(l, 2, 0);
1426
1427 tabg->setRowStretch(1, 1);
1428
1429 // Advanced tab
1430 tab = new QWidget(this);
1431 addTab(tab, i18nc("@title:tab Advanced theme settings", "Advanced"));
1432
1433 tabg = new QGridLayout(tab);
1434
1435 l = new QLabel(i18nc("@label:textbox", "Header:"), tab);
1436 tabg->addWidget(l, 0, 0);
1437
1438 mViewHeaderPolicyCombo = new QComboBox(tab);
1439 tabg->addWidget(mViewHeaderPolicyCombo, 0, 1);
1440
1441 l = new QLabel(i18nc("@label:textbox", "Icon size:"), tab);
1442 tabg->addWidget(l, 1, 0);
1443
1444 mIconSizeSpinBox = new QSpinBox(tab);
1445 mIconSizeSpinBox->setMinimum(8);
1446 mIconSizeSpinBox->setMaximum(64);
1447#if KI18N_VERSION > QT_VERSION_CHECK(6, 5, 0)
1448 KLocalization::setupSpinBoxFormatString(mIconSizeSpinBox, ki18ncp("suffix in a spinbox", "%v pixel", "%v pixels"));
1449#endif
1450
1451 QObject::connect(mIconSizeSpinBox, &QSpinBox::valueChanged, this, &ThemeEditor::slotIconSizeSpinBoxValueChanged);
1452
1453 tabg->addWidget(mIconSizeSpinBox, 1, 1);
1454
1455 tabg->setColumnStretch(1, 1);
1456 tabg->setRowStretch(2, 1);
1457 fillViewHeaderPolicyCombo();
1458}
1459
1460ThemeEditor::~ThemeEditor() = default;
1461
1462void ThemeEditor::editTheme(Theme *set)
1463{
1464 mCurrentTheme = set;
1465 mPreviewWidget->setTheme(mCurrentTheme);
1466
1467 if (!mCurrentTheme) {
1468 setEnabled(false);
1469 return;
1470 }
1471 setEnabled(true);
1472
1473 nameEdit()->setText(set->name());
1475
1476 ComboBoxUtils::setIntegerOptionComboValue(mViewHeaderPolicyCombo, (int)mCurrentTheme->viewHeaderPolicy());
1477
1478 mIconSizeSpinBox->setValue(set->iconSize());
1479 setReadOnly(mCurrentTheme->readOnly());
1480}
1481
1482void ThemeEditor::setReadOnly(bool readOnly)
1483{
1484 mPreviewWidget->setReadOnly(readOnly);
1485 mViewHeaderPolicyCombo->setEnabled(!readOnly);
1486 mIconSizeSpinBox->setEnabled(!readOnly);
1487 OptionSetEditor::setReadOnly(readOnly);
1488}
1489
1490void ThemeEditor::commit()
1491{
1492 if (!mCurrentTheme || mCurrentTheme->readOnly()) {
1493 return;
1494 }
1495
1496 mCurrentTheme->setName(nameEdit()->text());
1497 mCurrentTheme->setDescription(descriptionEdit()->toPlainText());
1498
1500 mCurrentTheme->setIconSize(mIconSizeSpinBox->value());
1501 // other settings are already committed to this theme
1502}
1503
1504void ThemeEditor::fillViewHeaderPolicyCombo()
1505{
1507}
1508
1509void ThemeEditor::slotNameEditTextEdited(const QString &newName)
1510{
1511 if (!mCurrentTheme) {
1512 return;
1513 }
1514 mCurrentTheme->setName(newName);
1515 Q_EMIT themeNameChanged();
1516}
1517
1518void ThemeEditor::slotIconSizeSpinBoxValueChanged(int val)
1519{
1520 if (!mCurrentTheme) {
1521 return;
1522 }
1523 mCurrentTheme->setIconSize(val);
1524
1525 mPreviewWidget->setTheme(mCurrentTheme); // will trigger a cache reset and a view update
1526}
1527
1528MessageList::Core::Theme *ThemeEditor::editedTheme() const
1529{
1530 return mCurrentTheme;
1531}
1532
1533#include "moc_themeeditor.cpp"
constexpr bool isEmpty() const
@ PerfectReferencesAndSubject
Thread by all of the above and try to match subjects too.
Definition aggregation.h:69
A message item that can have a fake tag list and a fake annotation.
Type type() const
Returns the type of this item.
Definition item.cpp:342
This class is an optimizing helper for dealing with large flat QAbstractItemModel objects.
const QString & description() const
Returns a description of this option set.
Definition optionset.h:79
void setName(const QString &name)
Sets the name of this OptionSet.
Definition optionset.h:69
const QString & name() const
Returns the name of this OptionSet.
Definition optionset.h:59
void setDescription(const QString &description)
Sets the description for this option set.
Definition optionset.h:87
MessageSorting
The available message sorting options.
Definition sortorder.h:60
@ NoMessageSorting
Don't sort the messages at all.
Definition sortorder.h:61
static QList< QPair< QString, int > > enumerateMessageSortingOptions(Aggregation::Threading t)
Enumerates the message sorting options compatible with the specified Threading setting.
Definition sortorder.cpp:60
The ThemeDelegate paints the message list view message and group items by using the supplied Theme.
QRect hitRowRect() const
Returns the rectangle of the row that was reported as hit by the previous call to hitTest().
const Theme::Column * hitColumn() const
Returns the theme column that was reported as hit by the previous call to hitTest().
bool hitTest(const QPoint &viewportPoint, bool exact=true)
Performs a hit test on the specified viewport point.
bool hitContentItemRight() const
Returns true if the hit theme content item was a right item and false otherwise.
Item * hitItem() const
Returns the Item that was reported as hit by the previous call to hitTest().
QRect hitContentItemRect() const
Returns the bounding rect of the content item that was reported as hit by the previous call to hitTes...
const Theme::ContentItem * hitContentItem() const
Returns the theme content item that was reported as hit by the previous call to hitTest().
QSize sizeHintForItemTypeAndColumn(Item::Type type, int column, const Item *item=nullptr) const
Returns a heuristic sizeHint() for the specified item type and column.
void generalFontChanged()
Called when the global fonts change (from systemsettings)
bool hitRowIsMessageRow() const
Returns true if the hitRow() is a message row, false otherwise.
const Theme::Row * hitRow() const
Returns the theme row that was reported as hit by the previous call to hitTest().
int hitRowIndex() const
Returns the index of the theme row that was reported as hit by the previous call to hitTest().
The Column class defines a view column available inside this theme.
Definition theme.h:501
void removeGroupHeaderRow(Row *row)
Removes the specified group header row.
Definition theme.cpp:757
void removeMessageRow(Row *row)
Removes the specified message row.
Definition theme.cpp:738
const QList< Row * > & groupHeaderRows() const
Returns the list of rows visible in this column for a GroupHeaderItem.
Definition theme.cpp:743
void insertMessageRow(int idx, Row *row)
Inserts a message row to this theme column in the specified position.
Definition theme.cpp:729
void setMessageSorting(SortOrder::MessageSorting ms)
Sets the sort order for messages that we should switch to when clicking on this column's header (if v...
Definition theme.cpp:675
void setVisibleByDefault(bool vbd)
Sets the "visible by default" tag for this column.
Definition theme.cpp:654
void setIsSenderOrReceiver(bool sor)
Marks this column as containing the "sender/receiver" field.
Definition theme.cpp:644
void addGroupHeaderRow(Row *row)
Appends a group header row to this theme.
Definition theme.cpp:724
const QList< Row * > & messageRows() const
Returns the list of rows visible in this column for a MessageItem.
Definition theme.cpp:700
void setLabel(const QString &label)
Sets the label for this column.
Definition theme.cpp:624
void insertGroupHeaderRow(int idx, Row *row)
Inserts a group header row to this theme column in the specified position.
Definition theme.cpp:748
const QString & label() const
Returns the label set for this column.
Definition theme.cpp:619
void addMessageRow(Row *row)
Appends a message row to this theme column.
Definition theme.cpp:712
The ContentItem class defines a content item inside a Row.
Definition theme.h:56
bool displaysText() const
Returns true if this item displays some kind of text.
Definition theme.cpp:77
bool canBeDisabled() const
Returns true if this ContentItem can be in a "disabled" state.
Definition theme.cpp:67
void setSoftenByBlending(bool softenByBlending)
Sets the flag that causes this item to be painted "softly".
Definition theme.cpp:256
bool hideWhenDisabled() const
Returns true if this item should be hidden when in disabled state.
Definition theme.cpp:223
void setBold(bool isBold)
Makes this item use a bold font.
Definition theme.cpp:200
void setUseCustomColor(bool useCustomColor)
Makes this item use the custom color that can be set by setCustomColor().
Definition theme.cpp:186
static QString description(Type type)
Returns a descriptive name for the specified content item type.
Definition theme.cpp:102
static bool applicableToMessageItems(Type type)
Static test that returns true if an instance of ContentItem with the specified type makes sense in a ...
Definition theme.cpp:275
void setSoftenByBlendingWhenDisabled(bool softenByBlendingWhenDisabled)
Sets the flag that causes this item to be painted "softly" when disabled.
Definition theme.cpp:242
bool isBold() const
Returns true if this item uses a bold text.
Definition theme.cpp:195
void setItalic(bool isItalic)
Makes this item use italic font.
Definition theme.cpp:214
void setCustomColor(const QColor &clr)
Sets the custom color for this item.
Definition theme.cpp:270
@ HideWhenDisabled
In disabled state the icon should take no space (overrides SoftenByBlendingWhenDisabled)
Definition theme.h:211
@ SoftenByBlendingWhenDisabled
In disabled state the icon should be still shown, but made very soft by alpha blending.
Definition theme.h:212
@ UseCustomColor
For text and vertical line. If set then always use a custom color, otherwise use default text color.
Definition theme.h:213
@ IsItalic
Fot text items. If set then always show as italic, otherwise use the default font style.
Definition theme.h:215
@ IsBold
For text items. If set then always show as bold, otherwise use the default font weight.
Definition theme.h:214
Type type() const
Returns the type of this content item.
Definition theme.cpp:62
bool canUseCustomColor() const
Returns true if this ContentItem can make use of a custom color.
Definition theme.cpp:72
bool useCustomColor() const
Returns true if this item uses a custom color.
Definition theme.cpp:181
bool softenByBlending() const
Returns true if this item should be always painted in a "soft" fashion.
Definition theme.cpp:251
bool softenByBlendingWhenDisabled() const
Returns true if this item should be painted in a "soft" fashion when in disabled state.
Definition theme.cpp:237
Type
The available ContentItem types.
Definition theme.h:106
@ InvitationIcon
Whether the message is an invitation.
Definition theme.h:198
@ CombinedReadRepliedStateIcon
The combined icon that displays the unread/read/replied/forwarded state (never disabled)
Definition theme.h:190
@ SignatureStateIcon
The Signature state icon for messages.
Definition theme.h:174
@ Date
Formatted date time of the message/group.
Definition theme.h:114
@ MostRecentDate
The date of the most recent message in subtree.
Definition theme.h:186
@ ReadStateIcon
The icon that displays the unread/read state (never disabled)
Definition theme.h:134
@ RepliedStateIcon
The icon that displays the replied/forwarded state (may be disabled)
Definition theme.h:142
@ WatchedIgnoredStateIcon
The Watched/Ignored state icon.
Definition theme.h:162
@ ImportantStateIcon
The Important tag icon.
Definition theme.h:154
@ Folder
Folder of the message.
Definition theme.h:202
@ AttachmentStateIcon
The icon that displays the attachment state (may be disabled)
Definition theme.h:138
@ ExpandedStateIcon
The Expanded state icon for group headers.
Definition theme.h:166
@ SenderOrReceiver
From: or To: strip, depending on the folder settings.
Definition theme.h:118
@ Subject
Display the subject of the message item.
Definition theme.h:110
@ VerticalLine
A vertical separation line.
Definition theme.h:178
@ HorizontalSpacer
A small empty spacer usable as separator.
Definition theme.h:182
@ EncryptionStateIcon
The Encryption state icon for messages.
Definition theme.h:170
@ TagList
The list of MessageItem::Tag entries.
Definition theme.h:194
@ Size
Formatted size of the message.
Definition theme.h:130
@ Receiver
To: strip, always.
Definition theme.h:126
@ ActionItemStateIcon
The ActionItem state icon.
Definition theme.h:150
@ Sender
From: strip, always.
Definition theme.h:122
@ SpamHamStateIcon
The Spam/Ham state icon.
Definition theme.h:158
void setHideWhenDisabled(bool hideWhenDisabled)
Sets the flag that causes this item to be hidden when disabled.
Definition theme.cpp:228
const QColor & customColor() const
Returns the custom color set for this item.
Definition theme.cpp:265
static bool applicableToGroupHeaderItems(Type type)
Static test that returns true if an instance of ContentItem with the specified type makes sense in a ...
Definition theme.cpp:280
bool isItalic() const
Returns true if this item uses an italic text.
Definition theme.cpp:209
The Row class defines a row of items inside a Column.
Definition theme.h:408
void removeLeftItem(ContentItem *item)
Removes the specified left aligned content item from this row.
Definition theme.cpp:393
void addRightItem(ContentItem *item)
Adds a right aligned item to this row.
Definition theme.cpp:379
void insertRightItem(int idx, ContentItem *item)
Adds a right aligned item at the specified position in this row.
Definition theme.cpp:403
void insertLeftItem(int idx, ContentItem *item)
Adds a left aligned item at the specified position in this row.
Definition theme.cpp:384
void removeRightItem(ContentItem *item)
Removes the specified right aligned content item from this row.
Definition theme.cpp:412
void addLeftItem(ContentItem *item)
Adds a left aligned item to this row.
Definition theme.cpp:367
const QList< ContentItem * > & rightItems() const
Returns the list of right aligned items for this row.
Definition theme.cpp:398
const QList< ContentItem * > & leftItems() const
Returns the list of left aligned items for this row.
Definition theme.cpp:489
The Theme class defines the visual appearance of the MessageList.
Definition theme.h:48
GroupHeaderBackgroundMode
Which color do we use to paint group header background ?
Definition theme.h:793
@ Transparent
No background at all: use style default.
Definition theme.h:794
@ CustomColor
Use a custom color.
Definition theme.h:796
@ AutoColor
Automatically determine the color (somewhere in the middle between background and text)
Definition theme.h:795
void setGroupHeaderBackgroundStyle(GroupHeaderBackgroundStyle groupHeaderBackgroundStyle)
Sets the group header background style for this theme.
Definition theme.cpp:1022
void setGroupHeaderBackgroundColor(const QColor &clr)
Sets the group header background color for this theme.
Definition theme.cpp:1012
void setViewHeaderPolicy(ViewHeaderPolicy vhp)
Sets the ViewHeaderPolicy for this theme.
Definition theme.cpp:1049
ViewHeaderPolicy
How do we manage the QHeaderView attached to our View ?
Definition theme.h:816
void removeColumn(Column *col)
Removes the specified message row.
Definition theme.cpp:981
void setGroupHeaderBackgroundMode(GroupHeaderBackgroundMode bm)
Sets the group header background mode for this theme.
Definition theme.cpp:999
void setIconSize(int iconSize)
Sets the icon size for this theme.
Definition theme.cpp:1059
const QColor & groupHeaderBackgroundColor() const
Returns the group header background color for this theme.
Definition theme.cpp:1007
static QList< QPair< QString, int > > enumerateGroupHeaderBackgroundStyles()
Enumerates the available group header background styles.
Definition theme.cpp:1032
GroupHeaderBackgroundMode groupHeaderBackgroundMode() const
Returns the group header background mode for this theme.
Definition theme.cpp:986
void insertColumn(int idx, Column *column)
Inserts a column to this theme at the specified position.
Definition theme.cpp:972
ViewHeaderPolicy viewHeaderPolicy() const
Returns the currently set ViewHeaderPolicy.
Definition theme.cpp:1044
GroupHeaderBackgroundStyle groupHeaderBackgroundStyle() const
Returns the group header background style for this theme.
Definition theme.cpp:1017
static QList< QPair< QString, int > > enumerateViewHeaderPolicyOptions()
Enumerates the available view header policy options.
Definition theme.cpp:1027
int iconSize() const
Returns the currently set icon size.
Definition theme.cpp:1054
const QList< Column * > & columns() const
Returns the list of columns available in this theme.
Definition theme.cpp:950
Column * column(int idx) const
Returns a pointer to the column at the specified index or 0 if there is no such column.
Definition theme.cpp:955
GroupHeaderBackgroundStyle
How do we paint group header background ?
Definition theme.h:802
The base class for the OptionSet editors.
QLineEdit * nameEdit() const
Returns the editor for the name of the OptionSet.
KTextEdit * descriptionEdit() const
Returns the editor for the description of the OptionSet.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
KLocalizedString KI18N_EXPORT ki18ncp(const char *context, const char *singular, const char *plural)
QString i18n(const char *text, const TYPE &arg...)
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
void setupSpinBoxFormatString(T *spinBox, const KLocalizedString &formatString)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QString label(StandardShortcut id)
const QList< QKeySequence > & end()
The implementation independent part of the MessageList library.
Definition aggregation.h:22
void fillIntegerOptionCombo(QComboBox *combo, const QList< QPair< QString, int > > &optionDescriptors)
Fills the specified QComboBox with the options available in optionDescriptors.
void setIntegerOptionComboValue(QComboBox *combo, int value)
Sets the currently selected option in the specified combo.
int getIntegerOptionComboValue(QComboBox *combo, int defaultValue)
Returns the identifier of the currently selected option in the specified combo.
bool isChecked() const const
void clicked(bool checked)
void setShortcut(const QKeySequence &key)
virtual void mouseMoveEvent(QMouseEvent *event) override
virtual void mousePressEvent(QMouseEvent *event) override
virtual void paintEvent(QPaintEvent *event) override
QWidget * viewport() const const
void setCheckable(bool)
void setChecked(bool)
QVariant data() const const
void setEnabled(bool)
void setData(const QVariant &data)
void setText(const QString &text)
void triggered(bool checked)
char * data()
void resize(qsizetype newSize, char c)
qsizetype size() const const
bool isValid() const const
QColor getColor(const QColor &initial, QWidget *parent, const QString &title, ColorDialogOptions options)
virtual void accept()
void acceptProposedAction()
const QMimeData * mimeData() const const
QPointF position() const const
void ignore()
virtual void changeEvent(QEvent *ev) override
int logicalIndexAt(const QPoint &pos) const const
void resizeSection(int logicalIndex, int size)
QIcon fromTheme(const QString &name)
void append(QList< T > &&value)
iterator begin()
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype count() const const
iterator end()
bool isEmpty() const const
void reserve(qsizetype size)
QAction * addAction(const QIcon &icon, const QString &text, Functor functor, const QKeySequence &shortcut)
QAction * addMenu(QMenu *menu)
QAction * addSection(const QIcon &icon, const QString &text)
QAction * addSeparator()
QAction * exec()
bool isEmpty() const const
void setTitle(const QString &title)
void triggered(QAction *action)
QByteArray data(const QString &mimeType) const const
virtual bool hasFormat(const QString &mimeType) const const
bool isValid() const const
QModelIndex parent() const const
QPoint pos() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
int manhattanLength() const const
QPoint toPoint() const const
void setDefault(bool)
int bottom() const const
QPoint bottomLeft() const const
QPoint bottomRight() const const
bool contains(const QPoint &point, bool proper) const const
bool isValid() const const
int left() const const
int right() const const
int top() const const
QPoint topLeft() const const
QPoint topRight() const const
int width() const const
Qt::MouseButton button() const const
Qt::MouseButtons buttons() const const
int width() const const
void setValue(int val)
void valueChanged(int i)
QString arg(Args &&... args) const const
AlignCenter
CustomContextMenu
CopyAction
ItemIsEnabled
Key_Return
LeftButton
void setPlainText(const QString &text)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QHeaderView * header() const const
void setColumnCount(int columns)
virtual bool event(QEvent *e) override
QTreeWidgetItem * headerItem() const const
void setHeaderLabels(const QStringList &labels)
void setExpanded(bool expand)
int toInt(bool *ok) const const
void customContextMenuRequested(const QPoint &pos)
void setEnabled(bool)
void hide()
QPoint mapToGlobal(const QPoint &pos) const const
void show()
virtual void showEvent(QShowEvent *event)
void update()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Sep 13 2024 11:48:47 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.