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 "PrettyMsgListModel.h"
0023 #include "ItemRoles.h"
0024 #include "MsgListModel.h"
0025 #include "ThreadingMsgListModel.h"
0026 #include "UiUtils/Formatting.h"
0027 #include "UiUtils/IconLoader.h"
0028 
0029 
0030 namespace Imap
0031 {
0032 
0033 namespace Mailbox
0034 {
0035 
0036 PrettyMsgListModel::PrettyMsgListModel(QObject *parent):
0037     QSortFilterProxyModel(parent), m_hideRead(false)
0038 {
0039     setDynamicSortFilter(true);
0040 }
0041 
0042 QVariant PrettyMsgListModel::data(const QModelIndex &index, int role) const
0043 {
0044     if (! index.isValid() || index.model() != this)
0045         return QVariant();
0046 
0047     if (index.column() < 0 || index.column() >= columnCount(index.parent()))
0048         return QVariant();
0049 
0050     if (index.row() < 0 || index.row() >= rowCount(index.parent()))
0051         return QVariant();
0052 
0053     QModelIndex translated = mapToSource(index);
0054 
0055     switch (role) {
0056 
0057     case Qt::DisplayRole:
0058     case Qt::ToolTipRole:
0059         switch (index.column()) {
0060         case MsgListModel::TO:
0061         case MsgListModel::FROM:
0062         case MsgListModel::CC:
0063         case MsgListModel::BCC:
0064         {
0065             int backendRole = 0;
0066             switch (index.column()) {
0067             case MsgListModel::FROM:
0068                 backendRole = RoleMessageFrom;
0069                 break;
0070             case MsgListModel::TO:
0071                 backendRole = RoleMessageTo;
0072                 break;
0073             case MsgListModel::CC:
0074                 backendRole = RoleMessageCc;
0075                 break;
0076             case MsgListModel::BCC:
0077                 backendRole = RoleMessageBcc;
0078                 break;
0079             }
0080             QVariantList items = translated.data(backendRole).toList();
0081             if (role == Qt::DisplayRole) {
0082                 return Imap::Message::MailAddress::prettyList(items, Imap::Message::MailAddress::FORMAT_JUST_NAME);
0083             } else {
0084                 return UiUtils::Formatting::htmlEscaped(Imap::Message::MailAddress::prettyList(items, Imap::Message::MailAddress::FORMAT_READABLE));
0085             }
0086         }
0087         case MsgListModel::DATE:
0088         case MsgListModel::RECEIVED_DATE:
0089         {
0090             QDateTime res = translated.data(RoleMessageDate).toDateTime();
0091             if (role == Qt::ToolTipRole) {
0092                 // tooltips shall always show the full and complete data
0093                 return QLocale().toString(res.toLocalTime(), QLocale::LongFormat);
0094             }
0095             return UiUtils::Formatting::prettyDate(res.toLocalTime());
0096         }
0097         case MsgListModel::SIZE:
0098         {
0099             QVariant size = translated.data(RoleMessageSize);
0100             if (!size.isValid()) {
0101                 return QVariant();
0102             }
0103             return UiUtils::Formatting::prettySize(size.toULongLong());
0104         }
0105         case MsgListModel::SUBJECT:
0106         {
0107             if (!translated.data(RoleIsFetched).toBool())
0108                 return tr("Loading...");
0109             QString subject = translated.data(RoleMessageSubject).toString();
0110             if (role == Qt::ToolTipRole) {
0111                 subject = UiUtils::Formatting::htmlEscaped(subject);
0112             }
0113             return subject.isEmpty() ? tr("(no subject)") : subject;
0114         }
0115         }
0116         break;
0117 
0118 
0119     case Qt::TextAlignmentRole:
0120         switch (index.column()) {
0121         case MsgListModel::DATE:
0122         case MsgListModel::RECEIVED_DATE:
0123         case MsgListModel::SIZE:
0124             return int(Qt::AlignRight | Qt::AlignVCenter);
0125         default:
0126             return int(Qt::AlignLeft | Qt::AlignVCenter);
0127         } // we return int because QVariant cannot hold QFlag: https://bugreports.qt.io/browse/QTBUG-54210
0128 
0129     case Qt::DecorationRole:
0130         // We will need the data, but asking for Flags or IsMarkedXYZ doesn't cause a fetch
0131         translated.data(RoleMessageSubject);
0132 
0133         switch (index.column()) {
0134         case MsgListModel::SUBJECT:
0135         {
0136             if (! translated.data(RoleIsFetched).toBool())
0137                 return QVariant();
0138 
0139             bool isForwarded = translated.data(RoleMessageIsMarkedForwarded).toBool();
0140             bool isReplied = translated.data(RoleMessageIsMarkedReplied).toBool();
0141 
0142             if (isForwarded && isReplied)
0143                 return UiUtils::loadIcon(QStringLiteral("mail-forwarded-replied"));
0144             else if (isReplied)
0145                 return UiUtils::loadIcon(QStringLiteral("mail-replied"));
0146             else if (isForwarded)
0147                 return UiUtils::loadIcon(QStringLiteral("mail-forwarded"));
0148             else if (translated.data(RoleMessageIsMarkedRecent).toBool())
0149                 return UiUtils::loadIcon(QStringLiteral("mail-mark-unread-new"));
0150             else if (!translated.data(RoleMessageIsMarkedRead).toBool())
0151                 return UiUtils::loadIcon(QStringLiteral("mail-mark-unread"));
0152             else
0153                 return UiUtils::loadIcon(QStringLiteral("mail-mark-read"));
0154         }
0155         case MsgListModel::SEEN:
0156             if (! translated.data(RoleIsFetched).toBool())
0157                 return QVariant();
0158             break;
0159         case MsgListModel::FLAGGED:
0160             if (! translated.data(RoleIsFetched).toBool())
0161                 return QVariant();
0162             if (translated.data(RoleMessageIsMarkedFlagged).toBool())
0163                 return UiUtils::loadIcon(QStringLiteral("mail-flagged"));
0164             else
0165                 return UiUtils::loadIcon(QStringLiteral("mail-unflagged"));
0166         case MsgListModel::ATTACHMENT:
0167             if (translated.data(RoleMessageHasAttachments).toBool())
0168                 return UiUtils::loadIcon(QStringLiteral("mail-attachment"));
0169             else
0170                 return QVariant();
0171         default:
0172             return QVariant();
0173         }
0174 
0175     }
0176 
0177     return QSortFilterProxyModel::data(index, role);
0178 }
0179 
0180 void PrettyMsgListModel::setHideRead(bool value)
0181 {
0182     m_hideRead = value;
0183     invalidateFilter();
0184 }
0185 
0186 bool PrettyMsgListModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
0187 {
0188     if (!m_hideRead)
0189         return true;
0190 
0191     QModelIndex source_index = sourceModel()->index(source_row, 0, source_parent);
0192 
0193     for (QModelIndex test = source_index; test.isValid(); test = test.parent())
0194         if (test.data(RoleThreadRootWithUnreadMessages).toBool() || test.data(RoleMessageWasUnread).toBool())
0195             return true;
0196 
0197     return false;
0198 }
0199 
0200 
0201 void PrettyMsgListModel::sort(int column, Qt::SortOrder order)
0202 {
0203     ThreadingMsgListModel *threadingModel = qobject_cast<ThreadingMsgListModel*>(sourceModel());
0204     Q_ASSERT(threadingModel);
0205 
0206     ThreadingMsgListModel::SortCriterium criterium = ThreadingMsgListModel::SORT_NONE;
0207     switch (column) {
0208     case MsgListModel::SEEN:
0209     case MsgListModel::FLAGGED:
0210     case MsgListModel::ATTACHMENT:
0211     case MsgListModel::COLUMN_COUNT:
0212     case MsgListModel::BCC:
0213     case -1:
0214         criterium = ThreadingMsgListModel::SORT_NONE;
0215         break;
0216     case MsgListModel::SUBJECT:
0217         criterium = ThreadingMsgListModel::SORT_SUBJECT;
0218         break;
0219     case MsgListModel::FROM:
0220         criterium = ThreadingMsgListModel::SORT_FROM;
0221         break;
0222     case MsgListModel::TO:
0223         criterium = ThreadingMsgListModel::SORT_TO;
0224         break;
0225     case MsgListModel::CC:
0226         criterium = ThreadingMsgListModel::SORT_CC;
0227         break;
0228     case MsgListModel::DATE:
0229         criterium = ThreadingMsgListModel::SORT_DATE;
0230         break;
0231     case MsgListModel::RECEIVED_DATE:
0232         criterium = ThreadingMsgListModel::SORT_ARRIVAL;
0233         break;
0234     case MsgListModel::SIZE:
0235         criterium = ThreadingMsgListModel::SORT_SIZE;
0236         break;
0237     }
0238 
0239     bool willSort = threadingModel->setUserSearchingSortingPreference(threadingModel->currentSearchCondition(), criterium, order);
0240 
0241     // Now let the view know about whether we accept such a sorting criteria.
0242     // This is needed because the QHeaderView doesn't offer a way to say "hey, you cannot sort in columns XYZ, only on ABC".
0243     if (criterium != ThreadingMsgListModel::SORT_NONE && willSort)
0244         emit sortingPreferenceChanged(column, order);
0245     else
0246         emit sortingPreferenceChanged(-1, order);
0247 }
0248 
0249 }
0250 
0251 }