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

KDE's Doxygen guidelines are available online.