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"