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

KDE's Doxygen guidelines are available online.