You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
676 lines
22 KiB
676 lines
22 KiB
/* |
|
|
|
SPDX-FileCopyrightText: 2009-2021 Laurent Montel <montel@kde.org> |
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
|
|
#include "foldertreeview.h" |
|
#include "kernel/mailkernel.h" |
|
#include "util/mailutil_p.h" |
|
|
|
#include <Akonadi/CollectionStatistics> |
|
#include <Akonadi/CollectionStatisticsDelegate> |
|
#include <Akonadi/EntityTreeModel> |
|
|
|
#include <KConfigGroup> |
|
#include <KGuiItem> |
|
#include <KLocalizedString> |
|
#include <KMessageBox> |
|
#include <QMenu> |
|
|
|
#include <QHeaderView> |
|
#include <QMouseEvent> |
|
|
|
using namespace MailCommon; |
|
|
|
FolderTreeView::FolderTreeView(QWidget *parent, bool showUnreadCount) |
|
: Akonadi::EntityTreeView(parent) |
|
{ |
|
init(showUnreadCount); |
|
} |
|
|
|
FolderTreeView::FolderTreeView(KXMLGUIClient *xmlGuiClient, QWidget *parent, bool showUnreadCount) |
|
: Akonadi::EntityTreeView(xmlGuiClient, parent) |
|
{ |
|
init(showUnreadCount); |
|
} |
|
|
|
FolderTreeView::~FolderTreeView() |
|
{ |
|
} |
|
|
|
void FolderTreeView::disableSaveConfig() |
|
{ |
|
mbDisableSaveConfig = true; |
|
} |
|
|
|
void FolderTreeView::setTooltipsPolicy(FolderTreeWidget::ToolTipDisplayPolicy policy) |
|
{ |
|
if (mToolTipDisplayPolicy == policy) { |
|
return; |
|
} |
|
|
|
mToolTipDisplayPolicy = policy; |
|
Q_EMIT changeTooltipsPolicy(mToolTipDisplayPolicy); |
|
writeConfig(); |
|
} |
|
|
|
void FolderTreeView::disableContextMenuAndExtraColumn() |
|
{ |
|
mbDisableContextMenuAndExtraColumn = true; |
|
const int nbColumn = header()->count(); |
|
for (int i = 1; i < nbColumn; ++i) { |
|
setColumnHidden(i, true); |
|
} |
|
} |
|
|
|
void FolderTreeView::init(bool showUnreadCount) |
|
{ |
|
setIconSize(QSize(22, 22)); |
|
setUniformRowHeights(true); |
|
mSortingPolicy = FolderTreeWidget::SortByCurrentColumn; |
|
mToolTipDisplayPolicy = FolderTreeWidget::DisplayAlways; |
|
|
|
header()->setContextMenuPolicy(Qt::CustomContextMenu); |
|
connect(header(), &QWidget::customContextMenuRequested, this, &FolderTreeView::slotHeaderContextMenuRequested); |
|
|
|
mCollectionStatisticsDelegate = new Akonadi::CollectionStatisticsDelegate(this); |
|
mCollectionStatisticsDelegate->setProgressAnimationEnabled(true); |
|
setItemDelegate(mCollectionStatisticsDelegate); |
|
mCollectionStatisticsDelegate->setUnreadCountShown(showUnreadCount && !header()->isSectionHidden(1)); |
|
} |
|
|
|
void FolderTreeView::showStatisticAnimation(bool anim) |
|
{ |
|
mCollectionStatisticsDelegate->setProgressAnimationEnabled(anim); |
|
} |
|
|
|
void FolderTreeView::writeConfig() |
|
{ |
|
if (mbDisableSaveConfig) { |
|
return; |
|
} |
|
|
|
KConfigGroup myGroup(KernelIf->config(), "MainFolderView"); |
|
myGroup.writeEntry("IconSize", iconSize().width()); |
|
myGroup.writeEntry("ToolTipDisplayPolicy", (int)mToolTipDisplayPolicy); |
|
myGroup.writeEntry("SortingPolicy", (int)mSortingPolicy); |
|
} |
|
|
|
void FolderTreeView::readConfig() |
|
{ |
|
KConfigGroup myGroup(KernelIf->config(), "MainFolderView"); |
|
int iIconSize = myGroup.readEntry("IconSize", iconSize().width()); |
|
if ((iIconSize < 16) || (iIconSize > 32)) { |
|
iIconSize = 22; |
|
} |
|
setIconSize(QSize(iIconSize, iIconSize)); |
|
mToolTipDisplayPolicy = |
|
static_cast<FolderTreeWidget::ToolTipDisplayPolicy>(myGroup.readEntry("ToolTipDisplayPolicy", static_cast<int>(FolderTreeWidget::DisplayAlways))); |
|
|
|
Q_EMIT changeTooltipsPolicy(mToolTipDisplayPolicy); |
|
|
|
setSortingPolicy((FolderTreeWidget::SortingPolicy)myGroup.readEntry("SortingPolicy", (int)FolderTreeWidget::SortByCurrentColumn), false); |
|
} |
|
|
|
void FolderTreeView::slotHeaderContextMenuRequested(const QPoint &pnt) |
|
{ |
|
if (mbDisableContextMenuAndExtraColumn) { |
|
readConfig(); |
|
return; |
|
} |
|
|
|
// the menu for the columns |
|
QMenu menu; |
|
QAction *act = nullptr; |
|
const int nbColumn = header()->count(); |
|
if (nbColumn > 1) { |
|
menu.addSection(i18n("View Columns")); |
|
for (int i = 1; i < nbColumn; ++i) { |
|
act = menu.addAction(model()->headerData(i, Qt::Horizontal).toString()); |
|
act->setCheckable(true); |
|
act->setChecked(!header()->isSectionHidden(i)); |
|
act->setData(QVariant(i)); |
|
connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeHeader); |
|
} |
|
} |
|
|
|
menu.addSection(i18n("Icon Size")); |
|
|
|
static const int icon_sizes[] = {16, 22, 32}; |
|
|
|
auto grp = new QActionGroup(&menu); |
|
for (int i : icon_sizes) { |
|
act = menu.addAction(QStringLiteral("%1x%2").arg(i).arg(i)); |
|
act->setCheckable(true); |
|
grp->addAction(act); |
|
if (iconSize().width() == i) { |
|
act->setChecked(true); |
|
} |
|
act->setData(QVariant(i)); |
|
|
|
connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeIconSize); |
|
} |
|
menu.addSection(i18n("Display Tooltips")); |
|
|
|
grp = new QActionGroup(&menu); |
|
|
|
act = menu.addAction(i18nc("@action:inmenu Always display tooltips", "Always")); |
|
act->setCheckable(true); |
|
grp->addAction(act); |
|
act->setChecked(mToolTipDisplayPolicy == FolderTreeWidget::DisplayAlways); |
|
act->setData(QVariant((int)FolderTreeWidget::DisplayAlways)); |
|
connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeToolTipDisplayPolicy); |
|
|
|
act = menu.addAction(i18nc("@action:inmenu Never display tooltips.", "Never")); |
|
act->setCheckable(true); |
|
grp->addAction(act); |
|
act->setChecked(mToolTipDisplayPolicy == FolderTreeWidget::DisplayNever); |
|
act->setData(QVariant((int)FolderTreeWidget::DisplayNever)); |
|
connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeToolTipDisplayPolicy); |
|
|
|
menu.addSection(i18nc("@action:inmenu", "Sort Items")); |
|
|
|
grp = new QActionGroup(&menu); |
|
|
|
act = menu.addAction(i18nc("@action:inmenu", "Automatically, by Current Column")); |
|
act->setCheckable(true); |
|
grp->addAction(act); |
|
act->setChecked(mSortingPolicy == FolderTreeWidget::SortByCurrentColumn); |
|
act->setData(QVariant((int)FolderTreeWidget::SortByCurrentColumn)); |
|
connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeSortingPolicy); |
|
|
|
act = menu.addAction(i18nc("@action:inmenu", "Manually, by Drag And Drop")); |
|
act->setCheckable(true); |
|
grp->addAction(act); |
|
act->setChecked(mSortingPolicy == FolderTreeWidget::SortByDragAndDropKey); |
|
act->setData(QVariant((int)FolderTreeWidget::SortByDragAndDropKey)); |
|
connect(act, &QAction::triggered, this, &FolderTreeView::slotHeaderContextMenuChangeSortingPolicy); |
|
|
|
menu.exec(header()->mapToGlobal(pnt)); |
|
} |
|
|
|
void FolderTreeView::slotHeaderContextMenuChangeSortingPolicy(bool) |
|
{ |
|
auto act = qobject_cast<QAction *>(sender()); |
|
if (!act) { |
|
return; |
|
} |
|
|
|
QVariant data = act->data(); |
|
|
|
bool ok; |
|
int policy = data.toInt(&ok); |
|
if (!ok) { |
|
return; |
|
} |
|
|
|
setSortingPolicy((FolderTreeWidget::SortingPolicy)policy, true); |
|
} |
|
|
|
void FolderTreeView::setSortingPolicy(FolderTreeWidget::SortingPolicy policy, bool writeInConfig) |
|
{ |
|
if (mSortingPolicy == policy) { |
|
return; |
|
} |
|
|
|
mSortingPolicy = policy; |
|
switch (mSortingPolicy) { |
|
case FolderTreeWidget::SortByCurrentColumn: |
|
header()->setSectionsClickable(true); |
|
header()->setSortIndicatorShown(true); |
|
setSortingEnabled(true); |
|
Q_EMIT manualSortingChanged(false); |
|
break; |
|
|
|
case FolderTreeWidget::SortByDragAndDropKey: |
|
header()->setSectionsClickable(false); |
|
header()->setSortIndicatorShown(false); |
|
|
|
setSortingEnabled(false); // hack for qutie bug: this call shouldn't be here at all |
|
Q_EMIT manualSortingChanged(true); |
|
|
|
break; |
|
default: |
|
// should never happen |
|
break; |
|
} |
|
if (writeInConfig) { |
|
writeConfig(); |
|
} |
|
} |
|
|
|
void FolderTreeView::slotHeaderContextMenuChangeToolTipDisplayPolicy(bool) |
|
{ |
|
auto act = qobject_cast<QAction *>(sender()); |
|
if (!act) { |
|
return; |
|
} |
|
|
|
QVariant data = act->data(); |
|
|
|
bool ok; |
|
const int id = data.toInt(&ok); |
|
if (!ok) { |
|
return; |
|
} |
|
Q_EMIT changeTooltipsPolicy((FolderTreeWidget::ToolTipDisplayPolicy)id); |
|
} |
|
|
|
void FolderTreeView::slotHeaderContextMenuChangeHeader(bool) |
|
{ |
|
auto act = qobject_cast<QAction *>(sender()); |
|
if (!act) { |
|
return; |
|
} |
|
|
|
QVariant data = act->data(); |
|
|
|
bool ok; |
|
const int id = data.toInt(&ok); |
|
if (!ok) { |
|
return; |
|
} |
|
|
|
if (id >= header()->count()) { |
|
return; |
|
} |
|
|
|
if (id == 1) { |
|
mCollectionStatisticsDelegate->setUnreadCountShown(!act->isChecked()); |
|
} |
|
|
|
setColumnHidden(id, !act->isChecked()); |
|
} |
|
|
|
void FolderTreeView::slotHeaderContextMenuChangeIconSize(bool) |
|
{ |
|
auto act = qobject_cast<QAction *>(sender()); |
|
if (!act) { |
|
return; |
|
} |
|
|
|
QVariant data = act->data(); |
|
|
|
bool ok; |
|
const int size = data.toInt(&ok); |
|
if (!ok) { |
|
return; |
|
} |
|
|
|
const QSize newIconSize(QSize(size, size)); |
|
if (newIconSize == iconSize()) { |
|
return; |
|
} |
|
setIconSize(newIconSize); |
|
|
|
writeConfig(); |
|
} |
|
|
|
void FolderTreeView::setCurrentModelIndex(const QModelIndex &index) |
|
{ |
|
if (index.isValid()) { |
|
clearSelection(); |
|
scrollTo(index); |
|
selectionModel()->setCurrentIndex(index, QItemSelectionModel::Rows); |
|
} |
|
} |
|
|
|
void FolderTreeView::selectModelIndex(const QModelIndex &index) |
|
{ |
|
if (index.isValid()) { |
|
scrollTo(index); |
|
selectionModel()->select(index, QItemSelectionModel::Rows | QItemSelectionModel::Select | QItemSelectionModel::Current | QItemSelectionModel::Clear); |
|
} |
|
} |
|
|
|
void FolderTreeView::slotSelectFocusFolder() |
|
{ |
|
const QModelIndex index = currentIndex(); |
|
if (index.isValid()) { |
|
setCurrentIndex(index); |
|
} |
|
} |
|
|
|
void FolderTreeView::slotFocusNextFolder() |
|
{ |
|
const QModelIndex nextFolder = selectNextFolder(currentIndex()); |
|
|
|
if (nextFolder.isValid()) { |
|
expand(nextFolder); |
|
setCurrentModelIndex(nextFolder); |
|
} |
|
} |
|
|
|
QModelIndex FolderTreeView::selectNextFolder(const QModelIndex ¤t) |
|
{ |
|
QModelIndex below; |
|
if (current.isValid()) { |
|
model()->fetchMore(current); |
|
if (model()->hasChildren(current)) { |
|
expand(current); |
|
below = indexBelow(current); |
|
} else if (current.row() < model()->rowCount(model()->parent(current)) - 1) { |
|
below = model()->index(current.row() + 1, current.column(), model()->parent(current)); |
|
} else { |
|
below = indexBelow(current); |
|
} |
|
} |
|
return below; |
|
} |
|
|
|
void FolderTreeView::slotFocusPrevFolder() |
|
{ |
|
const QModelIndex current = currentIndex(); |
|
if (current.isValid()) { |
|
QModelIndex above = indexAbove(current); |
|
setCurrentModelIndex(above); |
|
} |
|
} |
|
|
|
void FolderTreeView::slotFocusFirstFolder() |
|
{ |
|
const QModelIndex first = moveCursor(QAbstractItemView::MoveHome, Qt::NoModifier); |
|
if (first.isValid()) { |
|
setCurrentModelIndex(first); |
|
} |
|
} |
|
|
|
void FolderTreeView::slotFocusLastFolder() |
|
{ |
|
const QModelIndex last = moveCursor(QAbstractItemView::MoveEnd, Qt::NoModifier); |
|
if (last.isValid()) { |
|
setCurrentModelIndex(last); |
|
} |
|
} |
|
|
|
void FolderTreeView::selectNextUnreadFolder(bool confirm) |
|
{ |
|
// find next unread collection starting from current position |
|
if (!trySelectNextUnreadFolder(currentIndex(), ForwardSearch, confirm)) { |
|
// if there is none, jump to the last collection and try again |
|
trySelectNextUnreadFolder(model()->index(0, 0), ForwardSearch, confirm); |
|
} |
|
} |
|
|
|
// helper method to find last item in the model tree |
|
static QModelIndex lastChildOf(QAbstractItemModel *model, const QModelIndex ¤t) |
|
{ |
|
if (model->rowCount(current) == 0) { |
|
return current; |
|
} |
|
|
|
return lastChildOf(model, model->index(model->rowCount(current) - 1, 0, current)); |
|
} |
|
|
|
void FolderTreeView::selectPrevUnreadFolder(bool confirm) |
|
{ |
|
// find next unread collection starting from current position |
|
if (!trySelectNextUnreadFolder(currentIndex(), BackwardSearch, confirm)) { |
|
// if there is none, jump to top and try again |
|
const QModelIndex index = lastChildOf(model(), QModelIndex()); |
|
trySelectNextUnreadFolder(index, BackwardSearch, confirm); |
|
} |
|
} |
|
|
|
bool FolderTreeView::trySelectNextUnreadFolder(const QModelIndex ¤t, SearchDirection direction, bool confirm) |
|
{ |
|
QModelIndex index = current; |
|
while (true) { |
|
index = nextUnreadCollection(index, direction); |
|
|
|
if (!index.isValid()) { |
|
return false; |
|
} |
|
|
|
const auto collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>(); |
|
if (collection == Kernel::self()->trashCollectionFolder() || collection == Kernel::self()->outboxCollectionFolder()) { |
|
continue; |
|
} |
|
|
|
if (ignoreUnreadFolder(collection, confirm)) { |
|
continue; |
|
} |
|
|
|
if (allowedToEnterFolder(collection, confirm)) { |
|
expand(index); |
|
setCurrentIndex(index); |
|
selectModelIndex(index); |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
bool FolderTreeView::ignoreUnreadFolder(const Akonadi::Collection &collection, bool confirm) const |
|
{ |
|
if (!confirm) { |
|
return false; |
|
} |
|
|
|
// Skip drafts, sent mail and templates as well, when reading mail with the |
|
// space bar - but not when changing into the next folder with unread mail |
|
// via ctrl+ or ctrl- so we do this only if (confirm == true), which means |
|
// we are doing readOn. |
|
|
|
return collection == Kernel::self()->draftsCollectionFolder() || collection == Kernel::self()->templatesCollectionFolder() |
|
|| collection == Kernel::self()->sentCollectionFolder(); |
|
} |
|
|
|
bool FolderTreeView::allowedToEnterFolder(const Akonadi::Collection &collection, bool confirm) const |
|
{ |
|
if (!confirm) { |
|
return true; |
|
} |
|
|
|
// warn user that going to next folder - but keep track of |
|
// whether he wishes to be notified again in "AskNextFolder" |
|
// parameter (kept in the config file for kmail) |
|
const int result = KMessageBox::questionYesNo(const_cast<FolderTreeView *>(this), |
|
i18n("<qt>Go to the next unread message in folder <b>%1</b>?</qt>", collection.name()), |
|
i18n("Go to Next Unread Message"), |
|
KGuiItem(i18n("Go To")), |
|
KGuiItem(i18n("Do Not Go To")), // defaults |
|
QStringLiteral(":kmail_AskNextFolder"), |
|
KMessageBox::Option()); |
|
|
|
return result == KMessageBox::Yes; |
|
} |
|
|
|
bool FolderTreeView::isUnreadFolder(const QModelIndex ¤t, QModelIndex &index, FolderTreeView::Move move, bool confirm) |
|
{ |
|
if (current.isValid()) { |
|
if (move == FolderTreeView::Next) { |
|
index = selectNextFolder(current); |
|
} else if (move == FolderTreeView::Previous) { |
|
index = indexAbove(current); |
|
} |
|
|
|
if (index.isValid()) { |
|
const auto collection = index.model()->data(current, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>(); |
|
|
|
if (collection.isValid()) { |
|
if (collection.statistics().unreadCount() > 0) { |
|
if (!confirm) { |
|
selectModelIndex(current); |
|
return true; |
|
} else { |
|
// Skip drafts, sent mail and templates as well, when reading mail with the |
|
// space bar - but not when changing into the next folder with unread mail |
|
// via ctrl+ or ctrl- so we do this only if (confirm == true), which means |
|
// we are doing readOn. |
|
|
|
if (collection == Kernel::self()->draftsCollectionFolder() || collection == Kernel::self()->templatesCollectionFolder() |
|
|| collection == Kernel::self()->sentCollectionFolder()) { |
|
return false; |
|
} |
|
|
|
// warn user that going to next folder - but keep track of |
|
// whether he wishes to be notified again in "AskNextFolder" |
|
// parameter (kept in the config file for kmail) |
|
if (KMessageBox::questionYesNo(this, |
|
i18n("<qt>Go to the next unread message in folder <b>%1</b>?</qt>", collection.name()), |
|
i18n("Go to Next Unread Message"), |
|
KGuiItem(i18n("Go To")), |
|
KGuiItem(i18n("Do Not Go To")), // defaults |
|
QStringLiteral(":kmail_AskNextFolder"), |
|
KMessageBox::Option()) |
|
== KMessageBox::No) { |
|
return true; // assume selected (do not continue looping) |
|
} |
|
|
|
selectModelIndex(current); |
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
Akonadi::Collection FolderTreeView::currentFolder() const |
|
{ |
|
const QModelIndex current = currentIndex(); |
|
if (current.isValid()) { |
|
const auto collection = current.model()->data(current, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>(); |
|
return collection; |
|
} |
|
return Akonadi::Collection(); |
|
} |
|
|
|
void FolderTreeView::mousePressEvent(QMouseEvent *e) |
|
{ |
|
const bool buttonPressedIsMiddle = (e->button() == Qt::MiddleButton); |
|
Q_EMIT newTabRequested(buttonPressedIsMiddle); |
|
EntityTreeView::mousePressEvent(e); |
|
} |
|
|
|
void FolderTreeView::restoreHeaderState(const QByteArray &data) |
|
{ |
|
if (data.isEmpty()) { |
|
const int nbColumn = header()->count(); |
|
for (int i = 1; i < nbColumn; ++i) { |
|
setColumnHidden(i, true); |
|
} |
|
} else { |
|
header()->restoreState(data); |
|
} |
|
mCollectionStatisticsDelegate->setUnreadCountShown(header()->isSectionHidden(1)); |
|
} |
|
|
|
void FolderTreeView::updatePalette() |
|
{ |
|
mCollectionStatisticsDelegate->updatePalette(); |
|
} |
|
|
|
void FolderTreeView::keyboardSearch(const QString &) |
|
{ |
|
// Disable keyboardSearch: it interfers with filtering in the |
|
// FolderSelectionDialog. We don't want it in KMail main window |
|
// either because KMail has one-letter keyboard shortcuts. |
|
} |
|
|
|
QModelIndex FolderTreeView::indexBelow(const QModelIndex ¤t) const |
|
{ |
|
// if we have children, return first child |
|
if (model()->rowCount(current) > 0) { |
|
return model()->index(0, 0, current); |
|
} |
|
|
|
// if we have siblings, return next sibling |
|
const QModelIndex parent = model()->parent(current); |
|
const QModelIndex sibling = model()->index(current.row() + 1, 0, parent); |
|
|
|
if (sibling.isValid()) { // found valid sibling |
|
return sibling; |
|
} |
|
|
|
if (!parent.isValid()) { // our parent is the tree root and we have no siblings |
|
return QModelIndex(); // we reached the bottom of the tree |
|
} |
|
|
|
// We are the last child, the next index to check is our uncle, parent's first sibling |
|
const QModelIndex parentsSibling = parent.sibling(parent.row() + 1, 0); |
|
if (parentsSibling.isValid()) { |
|
return parentsSibling; |
|
} |
|
|
|
// iterate over our parents back to root until we find a parent with a valid sibling |
|
QModelIndex currentParent = parent; |
|
QModelIndex grandParent = model()->parent(currentParent); |
|
while (currentParent.isValid()) { |
|
// check if the parent has children except from us |
|
if (model()->rowCount(grandParent) > currentParent.row() + 1) { |
|
const auto index = indexBelow(model()->index(currentParent.row() + 1, 0, grandParent)); |
|
if (index.isValid()) { |
|
return index; |
|
} |
|
} |
|
|
|
currentParent = grandParent; |
|
grandParent = model()->parent(currentParent); |
|
} |
|
|
|
return QModelIndex(); // nothing found -> end of tree |
|
} |
|
|
|
QModelIndex FolderTreeView::lastChild(const QModelIndex ¤t) const |
|
{ |
|
if (model()->rowCount(current) == 0) { |
|
return current; |
|
} |
|
|
|
return lastChild(model()->index(model()->rowCount(current) - 1, 0, current)); |
|
} |
|
|
|
QModelIndex FolderTreeView::indexAbove(const QModelIndex ¤t) const |
|
{ |
|
const QModelIndex parent = model()->parent(current); |
|
|
|
if (current.row() == 0) { |
|
// we have no previous siblings -> our parent is the next item above us |
|
return parent; |
|
} |
|
|
|
// find previous sibling |
|
const QModelIndex previousSibling = model()->index(current.row() - 1, 0, parent); |
|
|
|
// the item above us is the last child (or grandchild, or grandgrandchild... etc) |
|
// of our previous sibling |
|
return lastChild(previousSibling); |
|
} |
|
|
|
QModelIndex FolderTreeView::nextUnreadCollection(const QModelIndex ¤t, SearchDirection direction) const |
|
{ |
|
QModelIndex index = current; |
|
while (true) { |
|
if (direction == ForwardSearch) { |
|
index = indexBelow(index); |
|
} else if (direction == BackwardSearch) { |
|
index = indexAbove(index); |
|
} |
|
|
|
if (!index.isValid()) { // reach end or top of the model |
|
return QModelIndex(); |
|
} |
|
|
|
// check if the index is a collection |
|
const auto collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>(); |
|
|
|
if (collection.isValid()) { |
|
// check if it is unread |
|
if (collection.statistics().unreadCount() > 0) { |
|
if (!MailCommon::Util::ignoreNewMailInFolder(collection)) { |
|
return index; // we found the next unread collection |
|
} |
|
} |
|
} |
|
} |
|
|
|
return QModelIndex(); // no unread collection found |
|
}
|
|
|