1 /*
2
3 SPDX-FileCopyrightText: 2009-2021 Laurent Montel <montel@kde.org>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "foldertreeview.h"
9 #include "kernel/mailkernel.h"
10 #include "util/mailutil_p.h"
11
12 #include <Akonadi/CollectionStatistics>
13 #include <Akonadi/CollectionStatisticsDelegate>
14 #include <Akonadi/EntityTreeModel>
15
16 #include <KConfigGroup>
17 #include <KGuiItem>
18 #include <KLocalizedString>
19 #include <KMessageBox>
20 #include <QMenu>
21
22 #include <QHeaderView>
23 #include <QMouseEvent>
24
25 using namespace MailCommon;
26
FolderTreeView(QWidget * parent,bool showUnreadCount)27 FolderTreeView::FolderTreeView(QWidget *parent, bool showUnreadCount)
28 : Akonadi::EntityTreeView(parent)
29 {
30 init(showUnreadCount);
31 }
32
FolderTreeView(KXMLGUIClient * xmlGuiClient,QWidget * parent,bool showUnreadCount)33 FolderTreeView::FolderTreeView(KXMLGUIClient *xmlGuiClient, QWidget *parent, bool showUnreadCount)
34 : Akonadi::EntityTreeView(xmlGuiClient, parent)
35 {
36 init(showUnreadCount);
37 }
38
~FolderTreeView()39 FolderTreeView::~FolderTreeView()
40 {
41 }
42
disableSaveConfig()43 void FolderTreeView::disableSaveConfig()
44 {
45 mbDisableSaveConfig = true;
46 }
47
setTooltipsPolicy(FolderTreeWidget::ToolTipDisplayPolicy policy)48 void FolderTreeView::setTooltipsPolicy(FolderTreeWidget::ToolTipDisplayPolicy policy)
49 {
50 if (mToolTipDisplayPolicy == policy) {
51 return;
52 }
53
54 mToolTipDisplayPolicy = policy;
55 Q_EMIT changeTooltipsPolicy(mToolTipDisplayPolicy);
56 writeConfig();
57 }
58
disableContextMenuAndExtraColumn()59 void FolderTreeView::disableContextMenuAndExtraColumn()
60 {
61 mbDisableContextMenuAndExtraColumn = true;
62 const int nbColumn = header()->count();
63 for (int i = 1; i < nbColumn; ++i) {
64 setColumnHidden(i, true);
65 }
66 }
67
init(bool showUnreadCount)68 void FolderTreeView::init(bool showUnreadCount)
69 {
70 setIconSize(QSize(22, 22));
71 setUniformRowHeights(true);
72 mSortingPolicy = FolderTreeWidget::SortByCurrentColumn;
73 mToolTipDisplayPolicy = FolderTreeWidget::DisplayAlways;
74
75 header()->setContextMenuPolicy(Qt::CustomContextMenu);
76 connect(header(), &QWidget::customContextMenuRequested, this, &FolderTreeView::slotHeaderContextMenuRequested);
77
78 mCollectionStatisticsDelegate = new Akonadi::CollectionStatisticsDelegate(this);
79 mCollectionStatisticsDelegate->setProgressAnimationEnabled(true);
80 setItemDelegate(mCollectionStatisticsDelegate);
81 mCollectionStatisticsDelegate->setUnreadCountShown(showUnreadCount && !header()->isSectionHidden(1));
82 }
83
showStatisticAnimation(bool anim)84 void FolderTreeView::showStatisticAnimation(bool anim)
85 {
86 mCollectionStatisticsDelegate->setProgressAnimationEnabled(anim);
87 }
88
writeConfig()89 void FolderTreeView::writeConfig()
90 {
91 if (mbDisableSaveConfig) {
92 return;
93 }
94
95 KConfigGroup myGroup(KernelIf->config(), "MainFolderView");
96 myGroup.writeEntry("IconSize", iconSize().width());
97 myGroup.writeEntry("ToolTipDisplayPolicy", (int)mToolTipDisplayPolicy);
98 myGroup.writeEntry("SortingPolicy", (int)mSortingPolicy);
99 }
100
readConfig()101 void FolderTreeView::readConfig()
102 {
103 KConfigGroup myGroup(KernelIf->config(), "MainFolderView");
104 int iIconSize = myGroup.readEntry("IconSize", iconSize().width());
105 if ((iIconSize < 16) || (iIconSize > 32)) {
106 iIconSize = 22;
107 }
108 setIconSize(QSize(iIconSize, iIconSize));
109 mToolTipDisplayPolicy =
110 static_cast<FolderTreeWidget::ToolTipDisplayPolicy>(myGroup.readEntry("ToolTipDisplayPolicy", static_cast<int>(FolderTreeWidget::DisplayAlways)));
111
112 Q_EMIT changeTooltipsPolicy(mToolTipDisplayPolicy);
113
114 setSortingPolicy((FolderTreeWidget::SortingPolicy)myGroup.readEntry("SortingPolicy", (int)FolderTreeWidget::SortByCurrentColumn), false);
115 }
116
slotHeaderContextMenuRequested(const QPoint & pnt)117 void FolderTreeView::slotHeaderContextMenuRequested(const QPoint &pnt)
118 {
119 if (mbDisableContextMenuAndExtraColumn) {
120 readConfig();
121 return;
122 }
123
124 // the menu for the columns
125 QMenu menu;
126 QAction *act = nullptr;
127 const int nbColumn = header()->count();
128 if (nbColumn > 1) {
129 menu.addSection(i18n("View Columns"));
130 for (int i = 1; i < nbColumn; ++i) {
131 act = menu.addAction(model()->headerData(i, Qt::Horizontal).toString());
132 act->setCheckable(true);
133 act->setChecked(!header()->isSectionHidden(i));
134 act->setData(QVariant(i));
135 connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeHeader);
136 }
137 }
138
139 menu.addSection(i18n("Icon Size"));
140
141 static const int icon_sizes[] = {16, 22, 32};
142
143 auto grp = new QActionGroup(&menu);
144 for (int i : icon_sizes) {
145 act = menu.addAction(QStringLiteral("%1x%2").arg(i).arg(i));
146 act->setCheckable(true);
147 grp->addAction(act);
148 if (iconSize().width() == i) {
149 act->setChecked(true);
150 }
151 act->setData(QVariant(i));
152
153 connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeIconSize);
154 }
155 menu.addSection(i18n("Display Tooltips"));
156
157 grp = new QActionGroup(&menu);
158
159 act = menu.addAction(i18nc("@action:inmenu Always display tooltips", "Always"));
160 act->setCheckable(true);
161 grp->addAction(act);
162 act->setChecked(mToolTipDisplayPolicy == FolderTreeWidget::DisplayAlways);
163 act->setData(QVariant((int)FolderTreeWidget::DisplayAlways));
164 connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeToolTipDisplayPolicy);
165
166 act = menu.addAction(i18nc("@action:inmenu Never display tooltips.", "Never"));
167 act->setCheckable(true);
168 grp->addAction(act);
169 act->setChecked(mToolTipDisplayPolicy == FolderTreeWidget::DisplayNever);
170 act->setData(QVariant((int)FolderTreeWidget::DisplayNever));
171 connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeToolTipDisplayPolicy);
172
173 menu.addSection(i18nc("@action:inmenu", "Sort Items"));
174
175 grp = new QActionGroup(&menu);
176
177 act = menu.addAction(i18nc("@action:inmenu", "Automatically, by Current Column"));
178 act->setCheckable(true);
179 grp->addAction(act);
180 act->setChecked(mSortingPolicy == FolderTreeWidget::SortByCurrentColumn);
181 act->setData(QVariant((int)FolderTreeWidget::SortByCurrentColumn));
182 connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeSortingPolicy);
183
184 act = menu.addAction(i18nc("@action:inmenu", "Manually, by Drag And Drop"));
185 act->setCheckable(true);
186 grp->addAction(act);
187 act->setChecked(mSortingPolicy == FolderTreeWidget::SortByDragAndDropKey);
188 act->setData(QVariant((int)FolderTreeWidget::SortByDragAndDropKey));
189 connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeSortingPolicy);
190
191 menu.exec(header()->mapToGlobal(pnt));
192 }
193
slotHeaderContextMenuChangeSortingPolicy(bool)194 void FolderTreeView::slotHeaderContextMenuChangeSortingPolicy(bool)
195 {
196 auto act = qobject_cast<QAction *>(sender());
197 if (!act) {
198 return;
199 }
200
201 QVariant data = act->data();
202
203 bool ok;
204 int policy = data.toInt(&ok);
205 if (!ok) {
206 return;
207 }
208
209 setSortingPolicy((FolderTreeWidget::SortingPolicy)policy, true);
210 }
211
setSortingPolicy(FolderTreeWidget::SortingPolicy policy,bool writeInConfig)212 void FolderTreeView::setSortingPolicy(FolderTreeWidget::SortingPolicy policy, bool writeInConfig)
213 {
214 if (mSortingPolicy == policy) {
215 return;
216 }
217
218 mSortingPolicy = policy;
219 switch (mSortingPolicy) {
220 case FolderTreeWidget::SortByCurrentColumn:
221 header()->setSectionsClickable(true);
222 header()->setSortIndicatorShown(true);
223 setSortingEnabled(true);
224 Q_EMIT manualSortingChanged(false);
225 break;
226
227 case FolderTreeWidget::SortByDragAndDropKey:
228 header()->setSectionsClickable(false);
229 header()->setSortIndicatorShown(false);
230
231 setSortingEnabled(false); // hack for qutie bug: this call shouldn't be here at all
232 Q_EMIT manualSortingChanged(true);
233
234 break;
235 default:
236 // should never happen
237 break;
238 }
239 if (writeInConfig) {
240 writeConfig();
241 }
242 }
243
slotHeaderContextMenuChangeToolTipDisplayPolicy(bool)244 void FolderTreeView::slotHeaderContextMenuChangeToolTipDisplayPolicy(bool)
245 {
246 auto act = qobject_cast<QAction *>(sender());
247 if (!act) {
248 return;
249 }
250
251 QVariant data = act->data();
252
253 bool ok;
254 const int id = data.toInt(&ok);
255 if (!ok) {
256 return;
257 }
258 Q_EMIT changeTooltipsPolicy((FolderTreeWidget::ToolTipDisplayPolicy)id);
259 }
260
slotHeaderContextMenuChangeHeader(bool)261 void FolderTreeView::slotHeaderContextMenuChangeHeader(bool)
262 {
263 auto act = qobject_cast<QAction *>(sender());
264 if (!act) {
265 return;
266 }
267
268 QVariant data = act->data();
269
270 bool ok;
271 const int id = data.toInt(&ok);
272 if (!ok) {
273 return;
274 }
275
276 if (id >= header()->count()) {
277 return;
278 }
279
280 if (id == 1) {
281 mCollectionStatisticsDelegate->setUnreadCountShown(!act->isChecked());
282 }
283
284 setColumnHidden(id, !act->isChecked());
285 }
286
slotHeaderContextMenuChangeIconSize(bool)287 void FolderTreeView::slotHeaderContextMenuChangeIconSize(bool)
288 {
289 auto act = qobject_cast<QAction *>(sender());
290 if (!act) {
291 return;
292 }
293
294 QVariant data = act->data();
295
296 bool ok;
297 const int size = data.toInt(&ok);
298 if (!ok) {
299 return;
300 }
301
302 const QSize newIconSize(QSize(size, size));
303 if (newIconSize == iconSize()) {
304 return;
305 }
306 setIconSize(newIconSize);
307
308 writeConfig();
309 }
310
setCurrentModelIndex(const QModelIndex & index)311 void FolderTreeView::setCurrentModelIndex(const QModelIndex &index)
312 {
313 if (index.isValid()) {
314 clearSelection();
315 scrollTo(index);
316 selectionModel()->setCurrentIndex(index, QItemSelectionModel::Rows);
317 }
318 }
319
selectModelIndex(const QModelIndex & index)320 void FolderTreeView::selectModelIndex(const QModelIndex &index)
321 {
322 if (index.isValid()) {
323 scrollTo(index);
324 selectionModel()->select(index, QItemSelectionModel::Rows | QItemSelectionModel::Select | QItemSelectionModel::Current | QItemSelectionModel::Clear);
325 }
326 }
327
slotSelectFocusFolder()328 void FolderTreeView::slotSelectFocusFolder()
329 {
330 const QModelIndex index = currentIndex();
331 if (index.isValid()) {
332 setCurrentIndex(index);
333 }
334 }
335
slotFocusNextFolder()336 void FolderTreeView::slotFocusNextFolder()
337 {
338 const QModelIndex nextFolder = selectNextFolder(currentIndex());
339
340 if (nextFolder.isValid()) {
341 expand(nextFolder);
342 setCurrentModelIndex(nextFolder);
343 }
344 }
345
selectNextFolder(const QModelIndex & current)346 QModelIndex FolderTreeView::selectNextFolder(const QModelIndex ¤t)
347 {
348 QModelIndex below;
349 if (current.isValid()) {
350 model()->fetchMore(current);
351 if (model()->hasChildren(current)) {
352 expand(current);
353 below = indexBelow(current);
354 } else if (current.row() < model()->rowCount(model()->parent(current)) - 1) {
355 below = model()->index(current.row() + 1, current.column(), model()->parent(current));
356 } else {
357 below = indexBelow(current);
358 }
359 }
360 return below;
361 }
362
slotFocusPrevFolder()363 void FolderTreeView::slotFocusPrevFolder()
364 {
365 const QModelIndex current = currentIndex();
366 if (current.isValid()) {
367 QModelIndex above = indexAbove(current);
368 setCurrentModelIndex(above);
369 }
370 }
371
slotFocusFirstFolder()372 void FolderTreeView::slotFocusFirstFolder()
373 {
374 const QModelIndex first = moveCursor(QAbstractItemView::MoveHome, Qt::NoModifier);
375 if (first.isValid()) {
376 setCurrentModelIndex(first);
377 }
378 }
379
slotFocusLastFolder()380 void FolderTreeView::slotFocusLastFolder()
381 {
382 const QModelIndex last = moveCursor(QAbstractItemView::MoveEnd, Qt::NoModifier);
383 if (last.isValid()) {
384 setCurrentModelIndex(last);
385 }
386 }
387
selectNextUnreadFolder(bool confirm)388 void FolderTreeView::selectNextUnreadFolder(bool confirm)
389 {
390 // find next unread collection starting from current position
391 if (!trySelectNextUnreadFolder(currentIndex(), ForwardSearch, confirm)) {
392 // if there is none, jump to the last collection and try again
393 trySelectNextUnreadFolder(model()->index(0, 0), ForwardSearch, confirm);
394 }
395 }
396
397 // helper method to find last item in the model tree
lastChildOf(QAbstractItemModel * model,const QModelIndex & current)398 static QModelIndex lastChildOf(QAbstractItemModel *model, const QModelIndex ¤t)
399 {
400 if (model->rowCount(current) == 0) {
401 return current;
402 }
403
404 return lastChildOf(model, model->index(model->rowCount(current) - 1, 0, current));
405 }
406
selectPrevUnreadFolder(bool confirm)407 void FolderTreeView::selectPrevUnreadFolder(bool confirm)
408 {
409 // find next unread collection starting from current position
410 if (!trySelectNextUnreadFolder(currentIndex(), BackwardSearch, confirm)) {
411 // if there is none, jump to top and try again
412 const QModelIndex index = lastChildOf(model(), QModelIndex());
413 trySelectNextUnreadFolder(index, BackwardSearch, confirm);
414 }
415 }
416
trySelectNextUnreadFolder(const QModelIndex & current,SearchDirection direction,bool confirm)417 bool FolderTreeView::trySelectNextUnreadFolder(const QModelIndex ¤t, SearchDirection direction, bool confirm)
418 {
419 QModelIndex index = current;
420 while (true) {
421 index = nextUnreadCollection(index, direction);
422
423 if (!index.isValid()) {
424 return false;
425 }
426
427 const auto collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
428 if (collection == Kernel::self()->trashCollectionFolder() || collection == Kernel::self()->outboxCollectionFolder()) {
429 continue;
430 }
431
432 if (ignoreUnreadFolder(collection, confirm)) {
433 continue;
434 }
435
436 if (allowedToEnterFolder(collection, confirm)) {
437 expand(index);
438 setCurrentIndex(index);
439 selectModelIndex(index);
440 return true;
441 } else {
442 return false;
443 }
444 }
445
446 return false;
447 }
448
ignoreUnreadFolder(const Akonadi::Collection & collection,bool confirm) const449 bool FolderTreeView::ignoreUnreadFolder(const Akonadi::Collection &collection, bool confirm) const
450 {
451 if (!confirm) {
452 return false;
453 }
454
455 // Skip drafts, sent mail and templates as well, when reading mail with the
456 // space bar - but not when changing into the next folder with unread mail
457 // via ctrl+ or ctrl- so we do this only if (confirm == true), which means
458 // we are doing readOn.
459
460 return collection == Kernel::self()->draftsCollectionFolder() || collection == Kernel::self()->templatesCollectionFolder()
461 || collection == Kernel::self()->sentCollectionFolder();
462 }
463
allowedToEnterFolder(const Akonadi::Collection & collection,bool confirm) const464 bool FolderTreeView::allowedToEnterFolder(const Akonadi::Collection &collection, bool confirm) const
465 {
466 if (!confirm) {
467 return true;
468 }
469
470 // warn user that going to next folder - but keep track of
471 // whether he wishes to be notified again in "AskNextFolder"
472 // parameter (kept in the config file for kmail)
473 const int result = KMessageBox::questionYesNo(const_cast<FolderTreeView *>(this),
474 i18n("<qt>Go to the next unread message in folder <b>%1</b>?</qt>", collection.name()),
475 i18n("Go to Next Unread Message"),
476 KGuiItem(i18n("Go To")),
477 KGuiItem(i18n("Do Not Go To")), // defaults
478 QStringLiteral(":kmail_AskNextFolder"),
479 KMessageBox::Option());
480
481 return result == KMessageBox::Yes;
482 }
483
isUnreadFolder(const QModelIndex & current,QModelIndex & index,FolderTreeView::Move move,bool confirm)484 bool FolderTreeView::isUnreadFolder(const QModelIndex ¤t, QModelIndex &index, FolderTreeView::Move move, bool confirm)
485 {
486 if (current.isValid()) {
487 if (move == FolderTreeView::Next) {
488 index = selectNextFolder(current);
489 } else if (move == FolderTreeView::Previous) {
490 index = indexAbove(current);
491 }
492
493 if (index.isValid()) {
494 const auto collection = index.model()->data(current, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
495
496 if (collection.isValid()) {
497 if (collection.statistics().unreadCount() > 0) {
498 if (!confirm) {
499 selectModelIndex(current);
500 return true;
501 } else {
502 // Skip drafts, sent mail and templates as well, when reading mail with the
503 // space bar - but not when changing into the next folder with unread mail
504 // via ctrl+ or ctrl- so we do this only if (confirm == true), which means
505 // we are doing readOn.
506
507 if (collection == Kernel::self()->draftsCollectionFolder() || collection == Kernel::self()->templatesCollectionFolder()
508 || collection == Kernel::self()->sentCollectionFolder()) {
509 return false;
510 }
511
512 // warn user that going to next folder - but keep track of
513 // whether he wishes to be notified again in "AskNextFolder"
514 // parameter (kept in the config file for kmail)
515 if (KMessageBox::questionYesNo(this,
516 i18n("<qt>Go to the next unread message in folder <b>%1</b>?</qt>", collection.name()),
517 i18n("Go to Next Unread Message"),
518 KGuiItem(i18n("Go To")),
519 KGuiItem(i18n("Do Not Go To")), // defaults
520 QStringLiteral(":kmail_AskNextFolder"),
521 KMessageBox::Option())
522 == KMessageBox::No) {
523 return true; // assume selected (do not continue looping)
524 }
525
526 selectModelIndex(current);
527 return true;
528 }
529 }
530 }
531 }
532 }
533 return false;
534 }
535
currentFolder() const536 Akonadi::Collection FolderTreeView::currentFolder() const
537 {
538 const QModelIndex current = currentIndex();
539 if (current.isValid()) {
540 const auto collection = current.model()->data(current, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
541 return collection;
542 }
543 return Akonadi::Collection();
544 }
545
mousePressEvent(QMouseEvent * e)546 void FolderTreeView::mousePressEvent(QMouseEvent *e)
547 {
548 const bool buttonPressedIsMiddle = (e->button() == Qt::MiddleButton);
549 Q_EMIT newTabRequested(buttonPressedIsMiddle);
550 EntityTreeView::mousePressEvent(e);
551 }
552
restoreHeaderState(const QByteArray & data)553 void FolderTreeView::restoreHeaderState(const QByteArray &data)
554 {
555 if (data.isEmpty()) {
556 const int nbColumn = header()->count();
557 for (int i = 1; i < nbColumn; ++i) {
558 setColumnHidden(i, true);
559 }
560 } else {
561 header()->restoreState(data);
562 }
563 mCollectionStatisticsDelegate->setUnreadCountShown(header()->isSectionHidden(1));
564 }
565
updatePalette()566 void FolderTreeView::updatePalette()
567 {
568 mCollectionStatisticsDelegate->updatePalette();
569 }
570
keyboardSearch(const QString &)571 void FolderTreeView::keyboardSearch(const QString &)
572 {
573 // Disable keyboardSearch: it interfers with filtering in the
574 // FolderSelectionDialog. We don't want it in KMail main window
575 // either because KMail has one-letter keyboard shortcuts.
576 }
577
indexBelow(const QModelIndex & current) const578 QModelIndex FolderTreeView::indexBelow(const QModelIndex ¤t) const
579 {
580 // if we have children, return first child
581 if (model()->rowCount(current) > 0) {
582 return model()->index(0, 0, current);
583 }
584
585 // if we have siblings, return next sibling
586 const QModelIndex parent = model()->parent(current);
587 const QModelIndex sibling = model()->index(current.row() + 1, 0, parent);
588
589 if (sibling.isValid()) { // found valid sibling
590 return sibling;
591 }
592
593 if (!parent.isValid()) { // our parent is the tree root and we have no siblings
594 return QModelIndex(); // we reached the bottom of the tree
595 }
596
597 // We are the last child, the next index to check is our uncle, parent's first sibling
598 const QModelIndex parentsSibling = parent.sibling(parent.row() + 1, 0);
599 if (parentsSibling.isValid()) {
600 return parentsSibling;
601 }
602
603 // iterate over our parents back to root until we find a parent with a valid sibling
604 QModelIndex currentParent = parent;
605 QModelIndex grandParent = model()->parent(currentParent);
606 while (currentParent.isValid()) {
607 // check if the parent has children except from us
608 if (model()->rowCount(grandParent) > currentParent.row() + 1) {
609 const auto index = indexBelow(model()->index(currentParent.row() + 1, 0, grandParent));
610 if (index.isValid()) {
611 return index;
612 }
613 }
614
615 currentParent = grandParent;
616 grandParent = model()->parent(currentParent);
617 }
618
619 return QModelIndex(); // nothing found -> end of tree
620 }
621
lastChild(const QModelIndex & current) const622 QModelIndex FolderTreeView::lastChild(const QModelIndex ¤t) const
623 {
624 if (model()->rowCount(current) == 0) {
625 return current;
626 }
627
628 return lastChild(model()->index(model()->rowCount(current) - 1, 0, current));
629 }
630
indexAbove(const QModelIndex & current) const631 QModelIndex FolderTreeView::indexAbove(const QModelIndex ¤t) const
632 {
633 const QModelIndex parent = model()->parent(current);
634
635 if (current.row() == 0) {
636 // we have no previous siblings -> our parent is the next item above us
637 return parent;
638 }
639
640 // find previous sibling
641 const QModelIndex previousSibling = model()->index(current.row() - 1, 0, parent);
642
643 // the item above us is the last child (or grandchild, or grandgrandchild... etc)
644 // of our previous sibling
645 return lastChild(previousSibling);
646 }
647
nextUnreadCollection(const QModelIndex & current,SearchDirection direction) const648 QModelIndex FolderTreeView::nextUnreadCollection(const QModelIndex ¤t, SearchDirection direction) const
649 {
650 QModelIndex index = current;
651 while (true) {
652 if (direction == ForwardSearch) {
653 index = indexBelow(index);
654 } else if (direction == BackwardSearch) {
655 index = indexAbove(index);
656 }
657
658 if (!index.isValid()) { // reach end or top of the model
659 return QModelIndex();
660 }
661
662 // check if the index is a collection
663 const auto collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
664
665 if (collection.isValid()) {
666 // check if it is unread
667 if (collection.statistics().unreadCount() > 0) {
668 if (!MailCommon::Util::ignoreNewMailInFolder(collection)) {
669 return index; // we found the next unread collection
670 }
671 }
672 }
673 }
674
675 return QModelIndex(); // no unread collection found
676 }
677