File indexing completed on 2024-12-22 05:01:14

0001 /*
0002  * SPDX-FileCopyrightText: 2011-2024 Laurent Montel <montel@kde.org>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only
0005  */
0006 #include "kmsearchmessagemodel.h"
0007 #include <MailCommon/MailUtil>
0008 #include <MessageList/MessageListUtil>
0009 
0010 #include <MessageCore/StringUtil>
0011 
0012 #include <Akonadi/ItemFetchScope>
0013 #include <Akonadi/Monitor>
0014 #include <Akonadi/Session>
0015 
0016 #include <Akonadi/MessageParts>
0017 #include <KMime/KMimeMessage>
0018 
0019 #include <KLocalizedString>
0020 #include <QApplication>
0021 #include <QColor>
0022 #include <QPalette>
0023 
0024 KMSearchMessageModel::KMSearchMessageModel(Akonadi::Monitor *monitor, QObject *parent)
0025     : Akonadi::MessageModel(monitor, parent)
0026 {
0027     monitor->itemFetchScope().fetchFullPayload();
0028     monitor->itemFetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::All);
0029 }
0030 
0031 KMSearchMessageModel::~KMSearchMessageModel() = default;
0032 
0033 static QString toolTip(const Akonadi::Item &item)
0034 {
0035     auto msg = item.payload<KMime::Message::Ptr>();
0036 
0037     const QColor bckColor = QApplication::palette().color(QPalette::ToolTipBase);
0038     const QColor txtColor = QApplication::palette().color(QPalette::ToolTipText);
0039 
0040     const QString bckColorName = bckColor.name();
0041     const QString txtColorName = txtColor.name();
0042     const bool textIsLeftToRight = (QApplication::layoutDirection() == Qt::LeftToRight);
0043     const QString textDirection = textIsLeftToRight ? QStringLiteral("left") : QStringLiteral("right");
0044 
0045     QString tip = QStringLiteral("<table width=\"100%\" border=\"0\" cellpadding=\"2\" cellspacing=\"0\">");
0046     QString subject;
0047     if (auto msgSubject = msg->subject(false)) {
0048         subject = msgSubject->asUnicodeString();
0049     }
0050     tip += QStringLiteral(
0051                "<tr>"
0052                "<td bgcolor=\"%1\" align=\"%4\" valign=\"middle\">"
0053                "<div style=\"color: %2; font-weight: bold;\">"
0054                "%3"
0055                "</div>"
0056                "</td>"
0057                "</tr>")
0058                .arg(txtColorName, bckColorName, subject.toHtmlEscaped(), textDirection);
0059 
0060     tip += QStringLiteral(
0061         "<tr>"
0062         "<td align=\"center\" valign=\"middle\">"
0063         "<table width=\"100%\" border=\"0\" cellpadding=\"2\" cellspacing=\"0\">");
0064 
0065     const QString htmlCodeForStandardRow = QStringLiteral(
0066         "<tr>"
0067         "<td align=\"right\" valign=\"top\" width=\"45\">"
0068         "<div style=\"font-weight: bold;\"><nobr>"
0069         "%1:"
0070         "</nobr></div>"
0071         "</td>"
0072         "<td align=\"left\" valign=\"top\">"
0073         "%2"
0074         "</td>"
0075         "</tr>");
0076 
0077     QString content = MessageList::Util::contentSummary(item);
0078 
0079     if (textIsLeftToRight) {
0080         tip += htmlCodeForStandardRow.arg(i18n("From"), msg->from()->displayString());
0081         tip += htmlCodeForStandardRow.arg(i18nc("Receiver of the email", "To"), msg->to()->displayString());
0082         tip += htmlCodeForStandardRow.arg(i18n("Date"), QLocale().toString(msg->date()->dateTime()));
0083         if (!content.isEmpty()) {
0084             tip += htmlCodeForStandardRow.arg(i18n("Preview"), content.replace(QLatin1Char('\n'), QStringLiteral("<br>")));
0085         }
0086     } else {
0087         tip += htmlCodeForStandardRow.arg(msg->from()->displayString(), i18n("From"));
0088         tip += htmlCodeForStandardRow.arg(msg->to()->displayString(), i18nc("Receiver of the email", "To"));
0089         tip += htmlCodeForStandardRow.arg(QLocale().toString(msg->date()->dateTime()), i18n("Date"));
0090         if (!content.isEmpty()) {
0091             tip += htmlCodeForStandardRow.arg(content.replace(QLatin1Char('\n'), QStringLiteral("<br>")), i18n("Preview"));
0092         }
0093     }
0094     tip += QLatin1StringView(
0095         "</table"
0096         "</td>"
0097         "</tr>");
0098     return tip;
0099 }
0100 
0101 int KMSearchMessageModel::entityColumnCount(HeaderGroup headerGroup) const
0102 {
0103     if (headerGroup == Akonadi::EntityTreeModel::ItemListHeaders) {
0104         return 6; // keep in sync with the column type enum
0105     }
0106 
0107     return Akonadi::MessageModel::entityColumnCount(headerGroup);
0108 }
0109 
0110 QString KMSearchMessageModel::fullCollectionPath(Akonadi::Collection::Id id) const
0111 {
0112     QString path = m_collectionFullPathCache.value(id);
0113     if (path.isEmpty()) {
0114         path = MailCommon::Util::fullCollectionPath(Akonadi::Collection(id));
0115         m_collectionFullPathCache.insert(id, path);
0116     }
0117     return path;
0118 }
0119 
0120 QVariant KMSearchMessageModel::entityData(const Akonadi::Item &item, int column, int role) const
0121 {
0122     if (!item.isValid()) {
0123         QVariant();
0124     }
0125 
0126     if (role == Qt::ToolTipRole) {
0127         return toolTip(item);
0128     }
0129 
0130     // The Collection column is first and is added by this model
0131     if (column == Collection) {
0132         if (role == Qt::DisplayRole || role == Qt::EditRole) {
0133             if (item.storageCollectionId() >= 0) {
0134                 return fullCollectionPath(item.storageCollectionId());
0135             }
0136             return fullCollectionPath(item.parentCollection().id());
0137         }
0138         return {};
0139     } else {
0140         // Delegate the remaining columns to the MessageModel
0141         return Akonadi::MessageModel::entityData(item, column - 1, role);
0142     }
0143 }
0144 
0145 QVariant KMSearchMessageModel::entityHeaderData(int section, Qt::Orientation orientation, int role, HeaderGroup headerGroup) const
0146 {
0147     if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == Collection) {
0148         return i18nc("@title:column, folder (e.g. email)", "Folder");
0149     }
0150     return Akonadi::MessageModel::entityHeaderData((section - 1), orientation, role, headerGroup);
0151 }
0152 
0153 #include "moc_kmsearchmessagemodel.cpp"