File indexing completed on 2024-11-24 04:53:13

0001 /* Copyright (C) 2006 - 2014 Jan Kundrát <jkt@flaska.net>
0002 
0003    This file is part of the Trojita Qt IMAP e-mail client,
0004    http://trojita.flaska.net/
0005 
0006    This program is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU General Public License as
0008    published by the Free Software Foundation; either version 2 of
0009    the License or (at your option) version 3 or any later version
0010    accepted by the membership of KDE e.V. (or its successor approved
0011    by the membership of KDE e.V.), which shall act as a proxy
0012    defined in Section 14 of version 3 of the license.
0013 
0014    This program is distributed in the hope that it will be useful,
0015    but WITHOUT ANY WARRANTY; without even the implied warranty of
0016    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0017    GNU General Public License for more details.
0018 
0019    You should have received a copy of the GNU General Public License
0020    along with this program.  If not, see <http://www.gnu.org/licenses/>.
0021 */
0022 #include "PrettyMailboxModel.h"
0023 #include "ItemRoles.h"
0024 
0025 #include <QFont>
0026 #include "UiUtils/Formatting.h"
0027 #include "UiUtils/IconLoader.h"
0028 
0029 namespace Imap
0030 {
0031 
0032 namespace Mailbox
0033 {
0034 
0035 PrettyMailboxModel::PrettyMailboxModel(QObject *parent, QAbstractItemModel *mailboxModel):
0036     QSortFilterProxyModel(parent), m_showOnlySubscribed(false)
0037 {
0038     setDynamicSortFilter(true);
0039     setSourceModel(mailboxModel);
0040 }
0041 
0042 QVariant PrettyMailboxModel::data(const QModelIndex &index, int role) const
0043 {
0044     if (!index.isValid())
0045         return QVariant();
0046 
0047     if (index.column() != 0)
0048         return QVariant();
0049 
0050     if (index.row() < 0 || index.row() >= rowCount(index.parent()) || index.model() != this)
0051         return QVariant();
0052 
0053     switch (role) {
0054     case Qt::DisplayRole:
0055     {
0056         QModelIndex translated = mapToSource(index);
0057         qlonglong unreadCount = translated.data(RoleUnreadMessageCount).toLongLong();
0058         qlonglong recentCount = translated.data(RoleRecentMessageCount).toLongLong();
0059         // The "problem" is that the \Recent and (lack of) \Seen flags are orthogonal states.
0060         //
0061         // There is no rule saying that a recent message is still unseen (trivial example: an already-read message being copied),
0062         // or that an unread message is recent.
0063         //
0064         // That's not really so much of a problem after the mailbox has been synced because we have all flags for each and every
0065         // message, and can therefore easily compute the difference. The problem, however, is that there is no such feature
0066         // in the SELECT command (and before you ask, SEARCH requires a selected mailbox, and "selected" is very close to
0067         // "synchronized" in Trojita). Trojita tries to satisfy all requests for "message numbers" at once, via the STATUS command
0068         // for unselected mailboxes, or through the already-known and cached information in the message flags for the recently
0069         // synchronized ones. In theory, it is possible to implement a workaround involving EXAMINE and (E)SEARCH, but even that
0070         // has its drawbacks (like the necessary serialization of requests and the requirement for a ton of new code).
0071         //
0072         // So in short, this is why there's no "(1 + 5)" result with six unread an one recent message.
0073         //
0074         // We also deliberately put an emphasis on the "unread count", even to an extent where there's no special information
0075         // for mailboxes with some recent, but no unread messages.
0076         if (recentCount && unreadCount) {
0077             return tr("%1 (%2/%3)")
0078                    .arg(QSortFilterProxyModel::data(index, RoleShortMailboxName).toString(),
0079                         QString::number(recentCount), QString::number(unreadCount));
0080         } else if (unreadCount) {
0081             return tr("%1 (%2)")
0082                    .arg(QSortFilterProxyModel::data(index, RoleShortMailboxName).toString(),
0083                         QString::number(unreadCount));
0084         } else {
0085             return QSortFilterProxyModel::data(index, RoleShortMailboxName);
0086         }
0087     }
0088     case Qt::FontRole:
0089     {
0090         QModelIndex translated = mapToSource(index);
0091         if (translated.data(RoleMailboxNumbersFetched).toBool() &&
0092             translated.data(RoleUnreadMessageCount).toULongLong() > 0) {
0093             QFont font;
0094             font.setBold(true);
0095             return font;
0096         } else {
0097             return QVariant();
0098         }
0099     }
0100     case Qt::DecorationRole:
0101     {
0102         QModelIndex translated = mapToSource(index);
0103         if (translated.data(RoleMailboxItemsAreLoading).toBool())
0104             return UiUtils::loadIcon(QStringLiteral("folder-grey"));
0105         else if (translated.data(RoleMailboxIsINBOX).toBool())
0106             return UiUtils::loadIcon(QStringLiteral("mail-folder-inbox"));
0107         else if (translated.data(RoleRecentMessageCount).toInt() > 0)
0108             return UiUtils::loadIcon(QStringLiteral("folder-bookmark"));
0109         else if (translated.data(RoleMailboxIsSelectable).toBool())
0110             return UiUtils::loadIcon(QStringLiteral("folder"));
0111         else
0112             return UiUtils::loadIcon(QStringLiteral("folder-open"));
0113     }
0114     case Qt::ToolTipRole:
0115     {
0116         QModelIndex translated = mapToSource(index);
0117         return QStringLiteral("<p>%1</p>\n<p>%2<br/>%3<br/>%4</p>").arg(
0118                     UiUtils::Formatting::htmlEscaped(translated.data(RoleShortMailboxName).toString()),
0119                     tr("%n messages", 0, translated.data(RoleTotalMessageCount).toInt()),
0120                     tr("%n unread", 0, translated.data(RoleUnreadMessageCount).toInt()),
0121                     tr("%n recent", 0, translated.data(RoleRecentMessageCount).toInt()));
0122     }
0123     default:
0124         return QSortFilterProxyModel::data(index, role);
0125     }
0126 }
0127 
0128 bool PrettyMailboxModel::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const
0129 {
0130     Q_UNUSED(source_parent);
0131     return source_column == 0;
0132 }
0133 
0134 bool PrettyMailboxModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
0135 {
0136     if (!m_showOnlySubscribed)
0137         return true;
0138 
0139     QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
0140     Q_ASSERT(index.isValid());
0141 
0142     // FIXME: walk the tree resursively from here instead of just checking for children
0143     return index.data(RoleMailboxIsSubscribed).toBool() || sourceModel()->hasChildren(index);
0144 }
0145 
0146 bool PrettyMailboxModel::hasChildren(const QModelIndex &parent) const
0147 {
0148     return sourceModel()->hasChildren(mapToSource(parent));
0149 }
0150 
0151 void PrettyMailboxModel::setShowOnlySubscribed(bool filterUnsubscribed)
0152 {
0153     m_showOnlySubscribed = filterUnsubscribed;
0154     invalidateFilter();
0155 }
0156 
0157 }
0158 
0159 }