File indexing completed on 2024-05-12 05:20:45

0001 /*
0002    SPDX-FileCopyrightText: 2001 by Ryan Breen <ryan@porivo.com>
0003    SPDX-FileCopyrightText: 2010-2024 Laurent Montel <montel@kde.org>
0004 
0005    SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "kmsystemtray.h"
0009 #include "kmmainwidget.h"
0010 #include "unityservicemanager.h"
0011 #include <Akonadi/NewMailNotifierAttribute>
0012 #include <MailCommon/FolderTreeView>
0013 #include <MailCommon/MailKernel>
0014 #include <MailCommon/MailUtil>
0015 
0016 #include <KLocalizedString>
0017 #include <KWindowSystem>
0018 #include <QMenu>
0019 
0020 #include "widgets/kactionmenutransport.h"
0021 
0022 #if !defined(Q_OS_WIN) && !defined(Q_OS_MAC)
0023 #define HAVE_X11 1
0024 #include <KWindowInfo>
0025 #include <KX11Extras>
0026 #endif
0027 
0028 using namespace MailCommon;
0029 
0030 /**
0031  * Construct a KSystemTray icon to be displayed when new mail
0032  * has arrived in a non-system folder.  The KMSystemTray listens
0033  * for updateNewMessageNotification events from each non-system
0034  * KMFolder and maintains a store of all folders with unread
0035  * messages.
0036  *
0037  * The KMSystemTray also provides a popup menu listing each folder
0038  * with its count of unread messages, allowing the user to jump
0039  * to the first unread message in each folder.
0040  */
0041 using namespace KMail;
0042 
0043 KMSystemTray::KMSystemTray(QObject *parent)
0044     : KStatusNotifierItem(parent)
0045 {
0046     setToolTipTitle(i18n("KMail"));
0047     setToolTipIconByName(QStringLiteral("kmail"));
0048     setIconByName(QStringLiteral("kmail-symbolic"));
0049 
0050 #if HAVE_X11
0051     KMMainWidget *mainWidget = kmkernel->getKMMainWidget();
0052     if (mainWidget) {
0053         QWidget *mainWin = mainWidget->window();
0054         if (mainWin) {
0055             mDesktopOfMainWin = KWindowInfo(mainWin->winId(), NET::WMDesktop).desktop();
0056         }
0057     }
0058 #endif
0059 
0060     connect(this, &KMSystemTray::activateRequested, this, &KMSystemTray::slotActivated);
0061     connect(contextMenu(), &QMenu::aboutToShow, this, &KMSystemTray::slotContextMenuAboutToShow);
0062 }
0063 
0064 bool KMSystemTray::buildPopupMenu()
0065 {
0066     KMMainWidget *mainWidget = kmkernel->getKMMainWidget();
0067     if (!mainWidget || kmkernel->shuttingDown()) {
0068         return false;
0069     }
0070 
0071     if (mBuiltContextMenu) {
0072         return true;
0073     }
0074 
0075     contextMenu()->clear();
0076     contextMenu()->setIcon(qApp->windowIcon());
0077     contextMenu()->setTitle(i18n("KMail"));
0078     QAction *action = nullptr;
0079     if ((action = mainWidget->action(QStringLiteral("check_mail")))) {
0080         contextMenu()->addAction(action);
0081     }
0082     if ((action = mainWidget->action(QStringLiteral("check_mail_in")))) {
0083         contextMenu()->addAction(action);
0084     }
0085 
0086     mSendQueued = mainWidget->sendQueuedAction();
0087     contextMenu()->addAction(mSendQueued);
0088     contextMenu()->addAction(mainWidget->sendQueueViaMenu());
0089 
0090     mNewMessagesPopup = new QMenu();
0091     mNewMessagesPopup->setTitle(i18n("New Messages In"));
0092     mNewMessagesPopup->setEnabled(false);
0093     connect(mNewMessagesPopup, &QMenu::triggered, this, &KMSystemTray::slotSelectCollection);
0094     contextMenu()->addAction(mNewMessagesPopup->menuAction());
0095 
0096     contextMenu()->addSeparator();
0097     if ((action = mainWidget->action(QStringLiteral("new_message")))) {
0098         contextMenu()->addAction(action);
0099     }
0100     if ((action = mainWidget->action(QStringLiteral("kmail_configure_kmail")))) {
0101         contextMenu()->addAction(action);
0102     }
0103     contextMenu()->addSeparator();
0104     if ((action = mainWidget->action(QStringLiteral("akonadi_work_offline")))) {
0105         contextMenu()->addAction(action);
0106         contextMenu()->addSeparator();
0107     }
0108 
0109     if ((action = mainWidget->action(QStringLiteral("file_quit")))) {
0110         contextMenu()->addAction(action);
0111     }
0112 
0113     mBuiltContextMenu = true;
0114     return true;
0115 }
0116 
0117 KMSystemTray::~KMSystemTray()
0118 {
0119     delete mNewMessagesPopup;
0120 }
0121 
0122 void KMSystemTray::initialize(int count)
0123 {
0124     updateCount(count);
0125     updateStatus(count);
0126     updateToolTip(count);
0127 }
0128 
0129 /**
0130  * Update the count of unread messages.  If there are unread messages,
0131  * show the "unread new mail" KMail icon.
0132  * If there is no unread mail, restore the normal KMail icon.
0133  */
0134 void KMSystemTray::updateCount(int count)
0135 {
0136     if (count == 0) {
0137         setIconByName(QStringLiteral("kmail"));
0138     } else {
0139         setIconByName(QStringLiteral("mail-mark-unread-new"));
0140     }
0141 }
0142 
0143 void KMSystemTray::setUnityServiceManager(UnityServiceManager *unityServiceManager)
0144 {
0145     mUnityServiceManager = unityServiceManager;
0146 }
0147 
0148 /**
0149  * On left mouse click, switch focus to the first KMMainWidget.  On right
0150  * click, bring up a list of all folders with a count of unread messages.
0151  */
0152 void KMSystemTray::slotActivated()
0153 {
0154     KMMainWidget *mainWidget = kmkernel->getKMMainWidget();
0155     if (!mainWidget) {
0156         return;
0157     }
0158 
0159     QWidget *mainWin = mainWidget->window();
0160     if (!mainWin) {
0161         return;
0162     }
0163 
0164 #ifdef HAVE_X11
0165     KWindowInfo cur = KWindowInfo(mainWin->winId(), NET::WMDesktop);
0166 
0167     const bool wasMinimized = cur.isMinimized();
0168     const int currentDesktop = KX11Extras::currentDesktop();
0169 
0170     if (cur.valid()) {
0171         mDesktopOfMainWin = cur.desktop();
0172     }
0173 
0174     if (wasMinimized && (currentDesktop != mDesktopOfMainWin) && (mDesktopOfMainWin == NET::OnAllDesktops)) {
0175         KX11Extras::setOnDesktop(mainWin->winId(), currentDesktop);
0176     }
0177 
0178     if (mDesktopOfMainWin == NET::OnAllDesktops) {
0179         KX11Extras::setOnAllDesktops(mainWin->winId(), true);
0180     }
0181 
0182     KWindowSystem::activateWindow(mainWin->windowHandle());
0183 
0184     if (wasMinimized) {
0185         kmkernel->raise();
0186     }
0187 #else
0188     KWindowSystem::activateWindow(mainWin->windowHandle());
0189 #endif
0190 }
0191 
0192 void KMSystemTray::slotContextMenuAboutToShow()
0193 {
0194     // Repeat this check before showing the menu, to minimize a race condition if
0195     // the base KMainWidget is closed.
0196     if (!kmkernel->getKMMainWidget() || kmkernel->shuttingDown()) {
0197         return;
0198     }
0199 
0200     // Create the context menu the first time.
0201     if (!mBuiltContextMenu) {
0202         if (!buildPopupMenu()) {
0203             return;
0204         }
0205     }
0206 
0207     // Update the "New messages in" submenu.
0208     mHasUnreadMessage = false;
0209     mNewMessagesPopup->clear();
0210     fillFoldersMenu(mNewMessagesPopup, kmkernel->treeviewModelSelection());
0211     mNewMessagesPopup->setEnabled(mHasUnreadMessage);
0212 }
0213 
0214 void KMSystemTray::fillFoldersMenu(QMenu *menu, const QAbstractItemModel *model, const QString &parentName, const QModelIndex &parentIndex)
0215 {
0216     const int rowCount = model->rowCount(parentIndex);
0217     for (int row = 0; row < rowCount; ++row) {
0218         const QModelIndex index = model->index(row, 0, parentIndex);
0219         const auto collection = model->data(index, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
0220         qint64 count = 0;
0221         if (mUnityServiceManager && !mUnityServiceManager->excludeFolder(collection)) {
0222             const Akonadi::CollectionStatistics statistics = collection.statistics();
0223             count = qMax(0LL, statistics.unreadCount());
0224             if (count > 0) {
0225                 if (mUnityServiceManager->ignoreNewMailInFolder(collection)) {
0226                     count = 0;
0227                 } else {
0228                     mHasUnreadMessage = true;
0229                 }
0230             }
0231         }
0232         QString label = parentName.isEmpty() ? QString() : QString(parentName + QLatin1StringView("->"));
0233         label += model->data(index).toString();
0234         label.replace(QLatin1Char('&'), QStringLiteral("&&"));
0235         if (count > 0) {
0236             // insert an item
0237             QAction *action = menu->addAction(label);
0238             action->setData(collection.id());
0239         }
0240         if (model->rowCount(index) > 0) {
0241             fillFoldersMenu(menu, model, label, index);
0242         }
0243     }
0244 }
0245 
0246 void KMSystemTray::hideKMail()
0247 {
0248     KMMainWidget *mainWidget = kmkernel->getKMMainWidget();
0249     if (!mainWidget) {
0250         return;
0251     }
0252     QWidget *mainWin = mainWidget->window();
0253     Q_ASSERT(mainWin);
0254     if (mainWin) {
0255 #ifdef HAVE_X11
0256         mDesktopOfMainWin = KWindowInfo(mainWin->winId(), NET::WMDesktop).desktop();
0257         // iconifying is unnecessary, but it looks cooler
0258         KX11Extras::minimizeWindow(mainWin->winId());
0259 #endif
0260         mainWin->hide();
0261     }
0262 }
0263 
0264 void KMSystemTray::updateToolTip(int count)
0265 {
0266     setToolTipSubTitle(count == 0 ? i18n("There are no unread messages") : i18np("1 unread message", "%1 unread messages", count));
0267 }
0268 
0269 void KMSystemTray::updateStatus(int count)
0270 {
0271     if (status() == KStatusNotifierItem::Passive && (count > 0)) {
0272         setStatus(KStatusNotifierItem::Active);
0273     } else if (status() == KStatusNotifierItem::Active && (count == 0)) {
0274         setStatus(KStatusNotifierItem::Passive);
0275     }
0276 }
0277 
0278 void KMSystemTray::slotSelectCollection(QAction *act)
0279 {
0280     const auto id = act->data().value<Akonadi::Collection::Id>();
0281     kmkernel->selectCollectionFromId(id);
0282     KMMainWidget *mainWidget = kmkernel->getKMMainWidget();
0283     if (!mainWidget) {
0284         return;
0285     }
0286     QWidget *mainWin = mainWidget->window();
0287     if (mainWin && !mainWin->isVisible()) {
0288         activate();
0289     }
0290 }
0291 
0292 #include "moc_kmsystemtray.cpp"