Warning, file /pim/mailcommon/src/folder/foldertreeview.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002 
0003   SPDX-FileCopyrightText: 2009-2024 Laurent Montel <montel@kde.org>
0004 
0005   SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "foldertreeview.h"
0009 #include "kernel/mailkernel.h"
0010 #include "util/mailutil_p.h"
0011 
0012 #include <Akonadi/CollectionStatistics>
0013 #include <Akonadi/CollectionStatisticsDelegate>
0014 #include <Akonadi/EntityTreeModel>
0015 
0016 #include <KConfigGroup>
0017 #include <KGuiItem>
0018 #include <KLocalizedString>
0019 #include <KMessageBox>
0020 #include <QMenu>
0021 
0022 #include <QActionGroup>
0023 #include <QHeaderView>
0024 #include <QMouseEvent>
0025 
0026 using namespace MailCommon;
0027 
0028 FolderTreeView::FolderTreeView(QWidget *parent, bool showUnreadCount)
0029     : Akonadi::EntityTreeView(parent)
0030 {
0031     setProperty("_breeze_force_frame", false);
0032     init(showUnreadCount);
0033 }
0034 
0035 FolderTreeView::FolderTreeView(KXMLGUIClient *xmlGuiClient, QWidget *parent, bool showUnreadCount)
0036     : Akonadi::EntityTreeView(xmlGuiClient, parent)
0037 {
0038     setProperty("_breeze_force_frame", false);
0039     init(showUnreadCount);
0040 }
0041 
0042 FolderTreeView::~FolderTreeView() = default;
0043 
0044 void FolderTreeView::disableSaveConfig()
0045 {
0046     mbDisableSaveConfig = true;
0047 }
0048 
0049 void FolderTreeView::setTooltipsPolicy(FolderTreeWidget::ToolTipDisplayPolicy policy)
0050 {
0051     if (mToolTipDisplayPolicy == policy) {
0052         return;
0053     }
0054 
0055     mToolTipDisplayPolicy = policy;
0056     Q_EMIT changeTooltipsPolicy(mToolTipDisplayPolicy);
0057     writeConfig();
0058 }
0059 
0060 void FolderTreeView::disableContextMenuAndExtraColumn()
0061 {
0062     mbDisableContextMenuAndExtraColumn = true;
0063     const int nbColumn = header()->count();
0064     for (int i = 1; i < nbColumn; ++i) {
0065         setColumnHidden(i, true);
0066     }
0067 }
0068 
0069 void FolderTreeView::init(bool showUnreadCount)
0070 {
0071     setIconSize(QSize(22, 22));
0072     setUniformRowHeights(true);
0073     mSortingPolicy = FolderTreeWidget::SortByCurrentColumn;
0074     mToolTipDisplayPolicy = FolderTreeWidget::DisplayAlways;
0075 
0076     header()->setContextMenuPolicy(Qt::CustomContextMenu);
0077     connect(header(), &QWidget::customContextMenuRequested, this, &FolderTreeView::slotHeaderContextMenuRequested);
0078 
0079     mCollectionStatisticsDelegate = new Akonadi::CollectionStatisticsDelegate(this);
0080     mCollectionStatisticsDelegate->setProgressAnimationEnabled(true);
0081     setItemDelegate(mCollectionStatisticsDelegate);
0082     mCollectionStatisticsDelegate->setUnreadCountShown(showUnreadCount && !header()->isSectionHidden(1));
0083 }
0084 
0085 void FolderTreeView::showStatisticAnimation(bool anim)
0086 {
0087     mCollectionStatisticsDelegate->setProgressAnimationEnabled(anim);
0088 }
0089 
0090 void FolderTreeView::writeConfig()
0091 {
0092     if (mbDisableSaveConfig) {
0093         return;
0094     }
0095 
0096     KConfigGroup myGroup(KernelIf->config(), QStringLiteral("MainFolderView"));
0097     myGroup.writeEntry("IconSize", iconSize().width());
0098     myGroup.writeEntry("ToolTipDisplayPolicy", (int)mToolTipDisplayPolicy);
0099     myGroup.writeEntry("SortingPolicy", (int)mSortingPolicy);
0100 }
0101 
0102 void FolderTreeView::readConfig()
0103 {
0104     KConfigGroup myGroup(KernelIf->config(), QStringLiteral("MainFolderView"));
0105     int iIconSize = myGroup.readEntry("IconSize", iconSize().width());
0106     if ((iIconSize < 16) || (iIconSize > 32)) {
0107         iIconSize = 22;
0108     }
0109     setIconSize(QSize(iIconSize, iIconSize));
0110     mToolTipDisplayPolicy =
0111         static_cast<FolderTreeWidget::ToolTipDisplayPolicy>(myGroup.readEntry("ToolTipDisplayPolicy", static_cast<int>(FolderTreeWidget::DisplayAlways)));
0112 
0113     Q_EMIT changeTooltipsPolicy(mToolTipDisplayPolicy);
0114 
0115     setSortingPolicy((FolderTreeWidget::SortingPolicy)myGroup.readEntry("SortingPolicy", (int)FolderTreeWidget::SortByCurrentColumn), false);
0116 }
0117 
0118 void FolderTreeView::slotHeaderContextMenuRequested(const QPoint &pnt)
0119 {
0120     if (mbDisableContextMenuAndExtraColumn) {
0121         readConfig();
0122         return;
0123     }
0124 
0125     // the menu for the columns
0126     QMenu menu;
0127     QAction *act = nullptr;
0128     const int nbColumn = header()->count();
0129     if (nbColumn > 1) {
0130         menu.addSection(i18n("View Columns"));
0131         for (int i = 1; i < nbColumn; ++i) {
0132             act = menu.addAction(model()->headerData(i, Qt::Horizontal).toString());
0133             act->setCheckable(true);
0134             act->setChecked(!header()->isSectionHidden(i));
0135             act->setData(QVariant(i));
0136             connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeHeader);
0137         }
0138     }
0139 
0140     menu.addSection(i18n("Icon Size"));
0141 
0142     static const int icon_sizes[] = {16, 22, 32};
0143 
0144     auto grp = new QActionGroup(&menu);
0145     for (int i : icon_sizes) {
0146         act = menu.addAction(QStringLiteral("%1x%2").arg(i).arg(i));
0147         act->setCheckable(true);
0148         grp->addAction(act);
0149         if (iconSize().width() == i) {
0150             act->setChecked(true);
0151         }
0152         act->setData(QVariant(i));
0153 
0154         connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeIconSize);
0155     }
0156     menu.addSection(i18n("Display Tooltips"));
0157 
0158     grp = new QActionGroup(&menu);
0159 
0160     act = menu.addAction(i18nc("@action:inmenu Always display tooltips", "Always"));
0161     act->setCheckable(true);
0162     grp->addAction(act);
0163     act->setChecked(mToolTipDisplayPolicy == FolderTreeWidget::DisplayAlways);
0164     act->setData(QVariant((int)FolderTreeWidget::DisplayAlways));
0165     connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeToolTipDisplayPolicy);
0166 
0167     act = menu.addAction(i18nc("@action:inmenu Never display tooltips.", "Never"));
0168     act->setCheckable(true);
0169     grp->addAction(act);
0170     act->setChecked(mToolTipDisplayPolicy == FolderTreeWidget::DisplayNever);
0171     act->setData(QVariant((int)FolderTreeWidget::DisplayNever));
0172     connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeToolTipDisplayPolicy);
0173 
0174     menu.addSection(i18nc("@action:inmenu", "Sort Items"));
0175 
0176     grp = new QActionGroup(&menu);
0177 
0178     act = menu.addAction(i18nc("@action:inmenu", "Automatically, by Current Column"));
0179     act->setCheckable(true);
0180     grp->addAction(act);
0181     act->setChecked(mSortingPolicy == FolderTreeWidget::SortByCurrentColumn);
0182     act->setData(QVariant((int)FolderTreeWidget::SortByCurrentColumn));
0183     connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeSortingPolicy);
0184 
0185     act = menu.addAction(i18nc("@action:inmenu", "Manually, by Drag And Drop"));
0186     act->setCheckable(true);
0187     grp->addAction(act);
0188     act->setChecked(mSortingPolicy == FolderTreeWidget::SortByDragAndDropKey);
0189     act->setData(QVariant((int)FolderTreeWidget::SortByDragAndDropKey));
0190     connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeSortingPolicy);
0191 
0192     menu.exec(header()->mapToGlobal(pnt));
0193 }
0194 
0195 void FolderTreeView::slotHeaderContextMenuChangeSortingPolicy(bool)
0196 {
0197     auto act = qobject_cast<QAction *>(sender());
0198     if (!act) {
0199         return;
0200     }
0201 
0202     QVariant data = act->data();
0203 
0204     bool ok;
0205     int policy = data.toInt(&ok);
0206     if (!ok) {
0207         return;
0208     }
0209 
0210     setSortingPolicy((FolderTreeWidget::SortingPolicy)policy, true);
0211 }
0212 
0213 void FolderTreeView::setSortingPolicy(FolderTreeWidget::SortingPolicy policy, bool writeInConfig)
0214 {
0215     if (mSortingPolicy == policy) {
0216         return;
0217     }
0218 
0219     mSortingPolicy = policy;
0220     switch (mSortingPolicy) {
0221     case FolderTreeWidget::SortByCurrentColumn:
0222         header()->setSectionsClickable(true);
0223         header()->setSortIndicatorShown(true);
0224         setSortingEnabled(true);
0225         Q_EMIT manualSortingChanged(false);
0226         break;
0227 
0228     case FolderTreeWidget::SortByDragAndDropKey:
0229         header()->setSectionsClickable(false);
0230         header()->setSortIndicatorShown(false);
0231 
0232         setSortingEnabled(false); // hack for qutie bug: this call shouldn't be here at all
0233         Q_EMIT manualSortingChanged(true);
0234 
0235         break;
0236     default:
0237         // should never happen
0238         break;
0239     }
0240     if (writeInConfig) {
0241         writeConfig();
0242     }
0243 }
0244 
0245 void FolderTreeView::slotHeaderContextMenuChangeToolTipDisplayPolicy(bool)
0246 {
0247     auto act = qobject_cast<QAction *>(sender());
0248     if (!act) {
0249         return;
0250     }
0251 
0252     QVariant data = act->data();
0253 
0254     bool ok;
0255     const int id = data.toInt(&ok);
0256     if (!ok) {
0257         return;
0258     }
0259     Q_EMIT changeTooltipsPolicy((FolderTreeWidget::ToolTipDisplayPolicy)id);
0260 }
0261 
0262 void FolderTreeView::slotHeaderContextMenuChangeHeader(bool)
0263 {
0264     auto act = qobject_cast<QAction *>(sender());
0265     if (!act) {
0266         return;
0267     }
0268 
0269     QVariant data = act->data();
0270 
0271     bool ok;
0272     const int id = data.toInt(&ok);
0273     if (!ok) {
0274         return;
0275     }
0276 
0277     if (id >= header()->count()) {
0278         return;
0279     }
0280 
0281     if (id == 1) {
0282         mCollectionStatisticsDelegate->setUnreadCountShown(!act->isChecked());
0283     }
0284 
0285     setColumnHidden(id, !act->isChecked());
0286 }
0287 
0288 void FolderTreeView::slotHeaderContextMenuChangeIconSize(bool)
0289 {
0290     auto act = qobject_cast<QAction *>(sender());
0291     if (!act) {
0292         return;
0293     }
0294 
0295     QVariant data = act->data();
0296 
0297     bool ok;
0298     const int size = data.toInt(&ok);
0299     if (!ok) {
0300         return;
0301     }
0302 
0303     const QSize newIconSize(QSize(size, size));
0304     if (newIconSize == iconSize()) {
0305         return;
0306     }
0307     setIconSize(newIconSize);
0308 
0309     writeConfig();
0310 }
0311 
0312 void FolderTreeView::setCurrentModelIndex(const QModelIndex &index)
0313 {
0314     if (index.isValid()) {
0315         clearSelection();
0316         scrollTo(index);
0317         selectionModel()->setCurrentIndex(index, QItemSelectionModel::Rows);
0318     }
0319 }
0320 
0321 void FolderTreeView::selectModelIndex(const QModelIndex &index)
0322 {
0323     if (index.isValid()) {
0324         scrollTo(index);
0325         selectionModel()->select(index, QItemSelectionModel::Rows | QItemSelectionModel::Select | QItemSelectionModel::Current | QItemSelectionModel::Clear);
0326     }
0327 }
0328 
0329 void FolderTreeView::slotSelectFocusFolder()
0330 {
0331     const QModelIndex index = currentIndex();
0332     if (index.isValid()) {
0333         setCurrentIndex(index);
0334     }
0335 }
0336 
0337 void FolderTreeView::slotFocusNextFolder()
0338 {
0339     const QModelIndex nextFolder = selectNextFolder(currentIndex());
0340 
0341     if (nextFolder.isValid()) {
0342         expand(nextFolder);
0343         setCurrentModelIndex(nextFolder);
0344     }
0345 }
0346 
0347 QModelIndex FolderTreeView::selectNextFolder(const QModelIndex &current)
0348 {
0349     QModelIndex below;
0350     if (current.isValid()) {
0351         model()->fetchMore(current);
0352         if (model()->hasChildren(current)) {
0353             expand(current);
0354             below = indexBelow(current);
0355         } else if (current.row() < model()->rowCount(model()->parent(current)) - 1) {
0356             below = model()->index(current.row() + 1, current.column(), model()->parent(current));
0357         } else {
0358             below = indexBelow(current);
0359         }
0360     }
0361     return below;
0362 }
0363 
0364 void FolderTreeView::slotFocusPrevFolder()
0365 {
0366     const QModelIndex current = currentIndex();
0367     if (current.isValid()) {
0368         QModelIndex above = indexAbove(current);
0369         setCurrentModelIndex(above);
0370     }
0371 }
0372 
0373 void FolderTreeView::slotFocusFirstFolder()
0374 {
0375     const QModelIndex first = moveCursor(QAbstractItemView::MoveHome, Qt::NoModifier);
0376     if (first.isValid()) {
0377         setCurrentModelIndex(first);
0378     }
0379 }
0380 
0381 void FolderTreeView::slotFocusLastFolder()
0382 {
0383     const QModelIndex last = moveCursor(QAbstractItemView::MoveEnd, Qt::NoModifier);
0384     if (last.isValid()) {
0385         setCurrentModelIndex(last);
0386     }
0387 }
0388 
0389 void FolderTreeView::selectNextUnreadFolder(bool confirm)
0390 {
0391     // find next unread collection starting from current position
0392     if (!trySelectNextUnreadFolder(currentIndex(), ForwardSearch, confirm)) {
0393         // if there is none, jump to the last collection and try again
0394         trySelectNextUnreadFolder(model()->index(0, 0), ForwardSearch, confirm);
0395     }
0396 }
0397 
0398 // helper method to find last item in the model tree
0399 static QModelIndex lastChildOf(QAbstractItemModel *model, const QModelIndex &current)
0400 {
0401     if (model->rowCount(current) == 0) {
0402         return current;
0403     }
0404 
0405     return lastChildOf(model, model->index(model->rowCount(current) - 1, 0, current));
0406 }
0407 
0408 void FolderTreeView::selectPrevUnreadFolder(bool confirm)
0409 {
0410     // find next unread collection starting from current position
0411     if (!trySelectNextUnreadFolder(currentIndex(), BackwardSearch, confirm)) {
0412         // if there is none, jump to top and try again
0413         const QModelIndex index = lastChildOf(model(), QModelIndex());
0414         trySelectNextUnreadFolder(index, BackwardSearch, confirm);
0415     }
0416 }
0417 
0418 bool FolderTreeView::trySelectNextUnreadFolder(const QModelIndex &current, SearchDirection direction, bool confirm)
0419 {
0420     QModelIndex index = current;
0421     while (true) {
0422         index = nextUnreadCollection(index, direction);
0423 
0424         if (!index.isValid()) {
0425             return false;
0426         }
0427 
0428         const auto collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
0429         if (collection == Kernel::self()->trashCollectionFolder() || collection == Kernel::self()->outboxCollectionFolder()) {
0430             continue;
0431         }
0432 
0433         if (ignoreUnreadFolder(collection, confirm)) {
0434             continue;
0435         }
0436 
0437         if (allowedToEnterFolder(collection, confirm)) {
0438             expand(index);
0439             setCurrentIndex(index);
0440             selectModelIndex(index);
0441             return true;
0442         } else {
0443             return false;
0444         }
0445     }
0446 
0447     return false;
0448 }
0449 
0450 bool FolderTreeView::ignoreUnreadFolder(const Akonadi::Collection &collection, bool confirm) const
0451 {
0452     if (!confirm) {
0453         return false;
0454     }
0455 
0456     // Skip drafts, sent mail and templates as well, when reading mail with the
0457     // space bar - but not when changing into the next folder with unread mail
0458     // via ctrl+ or ctrl- so we do this only if (confirm == true), which means
0459     // we are doing readOn.
0460 
0461     return collection == Kernel::self()->draftsCollectionFolder() || collection == Kernel::self()->templatesCollectionFolder()
0462         || collection == Kernel::self()->sentCollectionFolder();
0463 }
0464 
0465 bool FolderTreeView::allowedToEnterFolder(const Akonadi::Collection &collection, bool confirm) const
0466 {
0467     if (!confirm) {
0468         return true;
0469     }
0470 
0471     // warn user that going to next folder - but keep track of
0472     // whether he wishes to be notified again in "AskNextFolder"
0473     // parameter (kept in the config file for kmail)
0474     const int result = KMessageBox::questionTwoActions(const_cast<FolderTreeView *>(this),
0475                                                        i18n("<qt>Go to the next unread message in folder <b>%1</b>?</qt>", collection.name()),
0476                                                        i18n("Go to Next Unread Message"),
0477                                                        KGuiItem(i18n("Go To")),
0478                                                        KGuiItem(i18n("Do Not Go To")), // defaults
0479                                                        QStringLiteral(":kmail_AskNextFolder"),
0480                                                        KMessageBox::Option());
0481 
0482     return result == KMessageBox::ButtonCode::PrimaryAction;
0483 }
0484 
0485 bool FolderTreeView::isUnreadFolder(const QModelIndex &current, QModelIndex &index, FolderTreeView::Move move, bool confirm)
0486 {
0487     if (current.isValid()) {
0488         if (move == FolderTreeView::Next) {
0489             index = selectNextFolder(current);
0490         } else if (move == FolderTreeView::Previous) {
0491             index = indexAbove(current);
0492         }
0493 
0494         if (index.isValid()) {
0495             const auto collection = index.model()->data(current, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
0496 
0497             if (collection.isValid()) {
0498                 if (collection.statistics().unreadCount() > 0) {
0499                     if (!confirm) {
0500                         selectModelIndex(current);
0501                         return true;
0502                     } else {
0503                         // Skip drafts, sent mail and templates as well, when reading mail with the
0504                         // space bar - but not when changing into the next folder with unread mail
0505                         // via ctrl+ or ctrl- so we do this only if (confirm == true), which means
0506                         // we are doing readOn.
0507 
0508                         if (collection == Kernel::self()->draftsCollectionFolder() || collection == Kernel::self()->templatesCollectionFolder()
0509                             || collection == Kernel::self()->sentCollectionFolder()) {
0510                             return false;
0511                         }
0512 
0513                         // warn user that going to next folder - but keep track of
0514                         // whether he wishes to be notified again in "AskNextFolder"
0515                         // parameter (kept in the config file for kmail)
0516                         if (KMessageBox::questionTwoActions(this,
0517                                                             i18n("<qt>Go to the next unread message in folder <b>%1</b>?</qt>", collection.name()),
0518                                                             i18n("Go to Next Unread Message"),
0519                                                             KGuiItem(i18n("Go To")),
0520                                                             KGuiItem(i18n("Do Not Go To")), // defaults
0521                                                             QStringLiteral(":kmail_AskNextFolder"),
0522                                                             KMessageBox::Option())
0523                             == KMessageBox::ButtonCode::SecondaryAction) {
0524                             return true; // assume selected (do not continue looping)
0525                         }
0526 
0527                         selectModelIndex(current);
0528                         return true;
0529                     }
0530                 }
0531             }
0532         }
0533     }
0534     return false;
0535 }
0536 
0537 Akonadi::Collection FolderTreeView::currentFolder() const
0538 {
0539     const QModelIndex current = currentIndex();
0540     if (current.isValid()) {
0541         const auto collection = current.model()->data(current, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
0542         return collection;
0543     }
0544     return {};
0545 }
0546 
0547 void FolderTreeView::mousePressEvent(QMouseEvent *e)
0548 {
0549     const bool buttonPressedIsMiddle = (e->button() == Qt::MiddleButton);
0550     Q_EMIT newTabRequested(buttonPressedIsMiddle);
0551     EntityTreeView::mousePressEvent(e);
0552 }
0553 
0554 void FolderTreeView::restoreHeaderState(const QByteArray &data)
0555 {
0556     if (data.isEmpty()) {
0557         const int nbColumn = header()->count();
0558         for (int i = 1; i < nbColumn; ++i) {
0559             setColumnHidden(i, true);
0560         }
0561     } else {
0562         header()->restoreState(data);
0563     }
0564     mCollectionStatisticsDelegate->setUnreadCountShown(header()->isSectionHidden(1));
0565 }
0566 
0567 void FolderTreeView::updatePalette()
0568 {
0569     mCollectionStatisticsDelegate->updatePalette();
0570 }
0571 
0572 void FolderTreeView::keyboardSearch(const QString &)
0573 {
0574     // Disable keyboardSearch: it interfers with filtering in the
0575     // FolderSelectionDialog. We don't want it in KMail main window
0576     // either because KMail has one-letter keyboard shortcuts.
0577 }
0578 
0579 void FolderTreeView::setEnableDragDrop(bool enabled)
0580 {
0581 #ifndef QT_NO_DRAGANDDROP
0582     setDragDropMode(enabled ? QAbstractItemView::DragDrop : QAbstractItemView::NoDragDrop);
0583 #endif
0584 }
0585 
0586 QModelIndex FolderTreeView::indexBelow(const QModelIndex &current) const
0587 {
0588     // if we have children, return first child
0589     if (model()->rowCount(current) > 0) {
0590         return model()->index(0, 0, current);
0591     }
0592 
0593     // if we have siblings, return next sibling
0594     const QModelIndex parent = model()->parent(current);
0595     const QModelIndex sibling = model()->index(current.row() + 1, 0, parent);
0596 
0597     if (sibling.isValid()) { // found valid sibling
0598         return sibling;
0599     }
0600 
0601     if (!parent.isValid()) { // our parent is the tree root and we have no siblings
0602         return {}; // we reached the bottom of the tree
0603     }
0604 
0605     // We are the last child, the next index to check is our uncle, parent's first sibling
0606     const QModelIndex parentsSibling = parent.sibling(parent.row() + 1, 0);
0607     if (parentsSibling.isValid()) {
0608         return parentsSibling;
0609     }
0610 
0611     // iterate over our parents back to root until we find a parent with a valid sibling
0612     QModelIndex currentParent = parent;
0613     QModelIndex grandParent = model()->parent(currentParent);
0614     while (currentParent.isValid()) {
0615         // check if the parent has children except from us
0616         if (model()->rowCount(grandParent) > currentParent.row() + 1) {
0617             const auto index = indexBelow(model()->index(currentParent.row() + 1, 0, grandParent));
0618             if (index.isValid()) {
0619                 return index;
0620             }
0621         }
0622 
0623         currentParent = grandParent;
0624         grandParent = model()->parent(currentParent);
0625     }
0626 
0627     return {}; // nothing found -> end of tree
0628 }
0629 
0630 QModelIndex FolderTreeView::lastChild(const QModelIndex &current) const
0631 {
0632     if (model()->rowCount(current) == 0) {
0633         return current;
0634     }
0635 
0636     return lastChild(model()->index(model()->rowCount(current) - 1, 0, current));
0637 }
0638 
0639 QModelIndex FolderTreeView::indexAbove(const QModelIndex &current) const
0640 {
0641     const QModelIndex parent = model()->parent(current);
0642 
0643     if (current.row() == 0) {
0644         // we have no previous siblings -> our parent is the next item above us
0645         return parent;
0646     }
0647 
0648     // find previous sibling
0649     const QModelIndex previousSibling = model()->index(current.row() - 1, 0, parent);
0650 
0651     // the item above us is the last child (or grandchild, or grandgrandchild... etc)
0652     // of our previous sibling
0653     return lastChild(previousSibling);
0654 }
0655 
0656 QModelIndex FolderTreeView::nextUnreadCollection(const QModelIndex &current, SearchDirection direction) const
0657 {
0658     QModelIndex index = current;
0659     while (true) {
0660         if (direction == ForwardSearch) {
0661             index = indexBelow(index);
0662         } else if (direction == BackwardSearch) {
0663             index = indexAbove(index);
0664         }
0665 
0666         if (!index.isValid()) { // reach end or top of the model
0667             return {};
0668         }
0669 
0670         // check if the index is a collection
0671         const auto collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
0672 
0673         if (collection.isValid()) {
0674             // check if it is unread
0675             if (collection.statistics().unreadCount() > 0) {
0676                 if (!MailCommon::Util::ignoreNewMailInFolder(collection)) {
0677                     return index; // we found the next unread collection
0678                 }
0679             }
0680         }
0681     }
0682 
0683     return {}; // no unread collection found
0684 }
0685 
0686 #include "moc_foldertreeview.cpp"