You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

495 lines
16 KiB

//
/***************************************************************************
kmsystemtray.cpp - description
------------------
begin : Fri Aug 31 22:38:44 EDT 2001
copyright : (C) 2001 by Ryan Breen
email : ryan@porivo.com
Copyright (c) 2010-2015 Montel Laurent <montel@kde.org>
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "kmsystemtray.h"
#include "kmmainwidget.h"
#include "settings/kmailsettings.h"
#include "mailcommon/mailutil.h"
#include "MailCommon/MailKernel"
#include "MailCommon/FolderTreeView"
#include <AkonadiCore/NewMailNotifierAttribute>
#include <kiconloader.h>
#include <kcolorscheme.h>
#include <kwindowsystem.h>
#include "kmail_debug.h"
#include <QMenu>
#include <KLocalizedString>
#include <QAction>
#include <KActionMenu>
#include "widgets/kactionmenutransport.h"
#include <KActionCollection>
#include <QPainter>
#include <AkonadiCore/ChangeRecorder>
#include <AkonadiCore/EntityTreeModel>
#include <QFontDatabase>
#include <AkonadiCore/EntityMimeTypeFilterModel>
using namespace MailCommon;
/**
* Construct a KSystemTray icon to be displayed when new mail
* has arrived in a non-system folder. The KMSystemTray listens
* for updateNewMessageNotification events from each non-system
* KMFolder and maintains a store of all folders with unread
* messages.
*
* The KMSystemTray also provides a popup menu listing each folder
* with its count of unread messages, allowing the user to jump
* to the first unread message in each folder.
*/
namespace KMail
{
KMSystemTray::KMSystemTray(QObject *parent)
: KStatusNotifierItem(parent),
mIcon(QIcon::fromTheme(QStringLiteral("mail-unread-new"))),
mDesktopOfMainWin(0),
mMode(KMailSettings::EnumSystemTrayPolicy::ShowOnUnread),
mCount(0),
mShowUnreadMailCount(true),
mIconNotificationsEnabled(true),
mNewMessagesPopup(Q_NULLPTR),
mSendQueued(Q_NULLPTR)
{
qCDebug(KMAIL_LOG) << "Initting systray";
setToolTipTitle(i18n("KMail"));
setToolTipIconByName(QStringLiteral("kmail"));
setIconByName(QStringLiteral("kmail"));
KMMainWidget *mainWidget = kmkernel->getKMMainWidget();
if (mainWidget) {
QWidget *mainWin = mainWidget->window();
if (mainWin) {
mDesktopOfMainWin = KWindowSystem::windowInfo(mainWin->winId(),
NET::WMDesktop).desktop();
}
}
connect(this, &KMSystemTray::activateRequested, this, &KMSystemTray::slotActivated);
connect(contextMenu(), &QMenu::aboutToShow,
this, &KMSystemTray::slotContextMenuAboutToShow);
connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionStatisticsChanged, this, &KMSystemTray::slotCollectionStatisticsChanged);
connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionAdded, this, &KMSystemTray::initListOfCollection);
connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionRemoved, this, &KMSystemTray::initListOfCollection);
connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionSubscribed, this, &KMSystemTray::initListOfCollection);
connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::collectionUnsubscribed, this, &KMSystemTray::initListOfCollection);
initListOfCollection();
}
bool KMSystemTray::buildPopupMenu()
{
KMMainWidget *mainWidget = kmkernel->getKMMainWidget();
if (!mainWidget || kmkernel->shuttingDown()) {
return false;
}
if (!contextMenu()) {
setContextMenu(new QMenu());
}
contextMenu()->clear();
contextMenu()->setIcon(qApp->windowIcon());
contextMenu()->setTitle(i18n("KMail"));
QAction *action;
if ((action = mainWidget->action(QStringLiteral("check_mail")))) {
contextMenu()->addAction(action);
}
if ((action = mainWidget->action(QStringLiteral("check_mail_in")))) {
contextMenu()->addAction(action);
}
mSendQueued = mainWidget->sendQueuedAction();
contextMenu()->addAction(mSendQueued);
contextMenu()->addAction(mainWidget->sendQueueViaMenu());
contextMenu()->addSeparator();
if ((action = mainWidget->action(QStringLiteral("new_message")))) {
contextMenu()->addAction(action);
}
if ((action = mainWidget->action(QStringLiteral("kmail_configure_kmail")))) {
contextMenu()->addAction(action);
}
contextMenu()->addSeparator();
if ((action = mainWidget->action(QStringLiteral("akonadi_work_offline")))) {
contextMenu()->addAction(action);
}
contextMenu()->addSeparator();
if ((action = mainWidget->action(QStringLiteral("file_quit")))) {
contextMenu()->addAction(action);
}
return true;
}
KMSystemTray::~KMSystemTray()
{
}
void KMSystemTray::setShowUnreadCount(bool showUnreadCount)
{
if (mShowUnreadMailCount == showUnreadCount) {
return;
}
mShowUnreadMailCount = showUnreadCount;
updateSystemTray();
}
void KMSystemTray::setMode(int newMode)
{
if (newMode == mMode) {
return;
}
qCDebug(KMAIL_LOG) << "Setting systray mMode to" << newMode;
mMode = newMode;
switch (mMode) {
case KMailSettings::EnumSystemTrayPolicy::ShowAlways:
setStatus(KStatusNotifierItem::Active);
break;
case KMailSettings::EnumSystemTrayPolicy::ShowOnUnread:
setStatus(mCount > 0 ? KStatusNotifierItem::Active : KStatusNotifierItem::Passive);
break;
default:
qCDebug(KMAIL_LOG) << "Unknown systray mode" << mMode;
}
}
int KMSystemTray::mode() const
{
return mMode;
}
void KMSystemTray::slotGeneralFontChanged()
{
updateSystemTray();
}
void KMSystemTray::slotGeneralPaletteChanged()
{
const KColorScheme scheme(QPalette::Active, KColorScheme::View);
mTextColor = scheme.foreground(KColorScheme::LinkText).color();
updateSystemTray();
}
/**
* Update the count of unread messages. If there are unread messages,
* overlay the count on top of a transparent version of the KMail icon.
* If there is no unread mail, restore the normal KMail icon.
*/
void KMSystemTray::updateCount()
{
if (mCount == 0 || !mIconNotificationsEnabled) {
setIconByName(QStringLiteral("kmail"));
return;
}
if (mShowUnreadMailCount) {
const int overlaySize = IconSize(KIconLoader::Panel);
const QString countString = QString::number(mCount);
QFont countFont = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
countFont.setBold(true);
// decrease the size of the font for the number of unread messages if the
// number doesn't fit into the available space
float countFontSize = countFont.pointSizeF();
QFontMetrics qfm(countFont);
const int width = qfm.width(countString);
if (width > (overlaySize - 2)) {
countFontSize *= float(overlaySize - 2) / float(width);
countFont.setPointSizeF(countFontSize);
}
// Paint the number in a pixmap
QPixmap overlayPixmap(overlaySize, overlaySize);
overlayPixmap.fill(Qt::transparent);
QPainter p(&overlayPixmap);
p.setFont(countFont);
if (!mTextColor.isValid()) {
slotGeneralPaletteChanged();
}
p.setBrush(Qt::NoBrush);
p.setPen(mTextColor);
p.setOpacity(1.0);
p.drawText(overlayPixmap.rect(), Qt::AlignCenter, countString);
p.end();
QPixmap iconPixmap = mIcon.pixmap(overlaySize, overlaySize);
QPainter pp(&iconPixmap);
pp.drawPixmap(0, 0, overlayPixmap);
pp.end();
setIconByPixmap(iconPixmap);
} else {
setIconByPixmap(mIcon);
}
}
void KMSystemTray::setSystrayIconNotificationsEnabled(bool enabled)
{
if (enabled != mIconNotificationsEnabled) {
mIconNotificationsEnabled = enabled;
updateSystemTray();
}
}
/**
* On left mouse click, switch focus to the first KMMainWidget. On right
* click, bring up a list of all folders with a count of unread messages.
*/
void KMSystemTray::slotActivated()
{
KMMainWidget *mainWidget = kmkernel->getKMMainWidget();
if (!mainWidget) {
return ;
}
QWidget *mainWin = mainWidget->window();
if (!mainWin) {
return ;
}
KWindowInfo cur = KWindowSystem::windowInfo(mainWin->winId(), NET::WMDesktop);
const int currentDesktop = KWindowSystem::currentDesktop();
const bool wasMinimized = cur.isMinimized();
if (cur.valid()) {
mDesktopOfMainWin = cur.desktop();
}
if (wasMinimized && (currentDesktop != mDesktopOfMainWin) && (mDesktopOfMainWin == NET::OnAllDesktops)) {
KWindowSystem::setOnDesktop(mainWin->winId(), currentDesktop);
}
if (mDesktopOfMainWin == NET::OnAllDesktops) {
KWindowSystem::setOnAllDesktops(mainWin->winId(), true);
}
KWindowSystem::activateWindow(mainWin->winId());
if (wasMinimized) {
kmkernel->raise();
}
}
void KMSystemTray::slotContextMenuAboutToShow()
{
// Rebuild popup menu before show to minimize race condition if
// the base KMainWidget is closed.
if (!buildPopupMenu()) {
return;
}
if (mNewMessagesPopup != Q_NULLPTR) {
contextMenu()->removeAction(mNewMessagesPopup->menuAction());
delete mNewMessagesPopup;
mNewMessagesPopup = Q_NULLPTR;
}
mNewMessagesPopup = new QMenu();
fillFoldersMenu(mNewMessagesPopup, kmkernel->treeviewModelSelection());
connect(mNewMessagesPopup, &QMenu::triggered, this, &KMSystemTray::slotSelectCollection);
if (mCount > 0) {
mNewMessagesPopup->setTitle(i18n("New Messages In"));
contextMenu()->insertAction(mSendQueued, mNewMessagesPopup->menuAction());
}
}
void KMSystemTray::fillFoldersMenu(QMenu *menu, const QAbstractItemModel *model, const QString &parentName, const QModelIndex &parentIndex)
{
const int rowCount = model->rowCount(parentIndex);
for (int row = 0; row < rowCount; ++row) {
const QModelIndex index = model->index(row, 0, parentIndex);
const Akonadi::Collection collection = model->data(index, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
qint64 count = 0;
if (!excludeFolder(collection)) {
Akonadi::CollectionStatistics statistics = collection.statistics();
count = qMax(0LL, statistics.unreadCount());
if (count > 0) {
if (ignoreNewMailInFolder(collection)) {
count = 0;
} else {
mCount += count;
}
}
}
QString label = parentName.isEmpty() ? QString() : QString(parentName + QLatin1String("->"));
label += model->data(index).toString();
label.replace(QLatin1Char('&'), QStringLiteral("&&"));
if (count > 0) {
// insert an item
QAction *action = menu->addAction(label);
action->setData(collection.id());
}
if (model->rowCount(index) > 0) {
fillFoldersMenu(menu, model, label, index);
}
}
}
void KMSystemTray::hideKMail()
{
KMMainWidget *mainWidget = kmkernel->getKMMainWidget();
if (!mainWidget) {
return;
}
QWidget *mainWin = mainWidget->window();
Q_ASSERT(mainWin);
if (mainWin) {
mDesktopOfMainWin = KWindowSystem::windowInfo(mainWin->winId(),
NET::WMDesktop).desktop();
// iconifying is unnecessary, but it looks cooler
KWindowSystem::minimizeWindow(mainWin->winId());
mainWin->hide();
}
}
void KMSystemTray::initListOfCollection()
{
mCount = 0;
const QAbstractItemModel *model = kmkernel->collectionModel();
if (model->rowCount() == 0) {
QTimer::singleShot(1000, this, &KMSystemTray::initListOfCollection);
return;
}
unreadMail(model);
if (mMode == KMailSettings::EnumSystemTrayPolicy::ShowOnUnread) {
if (status() == KStatusNotifierItem::Passive && (mCount > 0)) {
setStatus(KStatusNotifierItem::Active);
} else if (status() == KStatusNotifierItem::Active && (mCount == 0)) {
setStatus(KStatusNotifierItem::Passive);
}
}
//qCDebug(KMAIL_LOG)<<" mCount :"<<mCount;
updateCount();
}
void KMSystemTray::unreadMail(const QAbstractItemModel *model, const QModelIndex &parentIndex)
{
const int rowCount = model->rowCount(parentIndex);
for (int row = 0; row < rowCount; ++row) {
const QModelIndex index = model->index(row, 0, parentIndex);
const Akonadi::Collection collection = model->data(index, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
if (!excludeFolder(collection)) {
const Akonadi::CollectionStatistics statistics = collection.statistics();
const qint64 count = qMax(0LL, statistics.unreadCount());
if (count > 0) {
if (!ignoreNewMailInFolder(collection)) {
mCount += count;
}
}
}
if (model->rowCount(index) > 0) {
unreadMail(model, index);
}
}
// Update tooltip to reflect count of unread messages
setToolTipSubTitle(mCount == 0 ? i18n("There are no unread messages")
: i18np("1 unread message",
"%1 unread messages",
mCount));
}
bool KMSystemTray::hasUnreadMail() const
{
return (mCount != 0);
}
void KMSystemTray::slotSelectCollection(QAction *act)
{
const Akonadi::Collection::Id id = act->data().value<Akonadi::Collection::Id>();
kmkernel->selectCollectionFromId(id);
KMMainWidget *mainWidget = kmkernel->getKMMainWidget();
if (!mainWidget) {
return ;
}
QWidget *mainWin = mainWidget->window();
if (mainWin && !mainWin->isVisible()) {
activate();
}
}
void KMSystemTray::updateSystemTray()
{
initListOfCollection();
}
void KMSystemTray::slotCollectionStatisticsChanged(Akonadi::Collection::Id id, const Akonadi::CollectionStatistics &)
{
//Exclude sent mail folder
if (CommonKernel->outboxCollectionFolder().id() == id ||
CommonKernel->sentCollectionFolder().id() == id ||
CommonKernel->templatesCollectionFolder().id() == id ||
CommonKernel->trashCollectionFolder().id() == id ||
CommonKernel->draftsCollectionFolder().id() == id) {
return;
}
initListOfCollection();
}
bool KMSystemTray::excludeFolder(const Akonadi::Collection &collection) const
{
if (!collection.isValid()) {
return true;
}
if (!collection.contentMimeTypes().contains(KMime::Message::mimeType())) {
return true;
}
if (CommonKernel->outboxCollectionFolder() == collection ||
CommonKernel->sentCollectionFolder() == collection ||
CommonKernel->templatesCollectionFolder() == collection ||
CommonKernel->trashCollectionFolder() == collection ||
CommonKernel->draftsCollectionFolder() == collection) {
return true;
}
if (MailCommon::Util::isVirtualCollection(collection)) {
return true;
}
return false;
}
bool KMSystemTray::ignoreNewMailInFolder(const Akonadi::Collection &collection)
{
if (collection.hasAttribute<Akonadi::NewMailNotifierAttribute>()) {
if (collection.attribute<Akonadi::NewMailNotifierAttribute>()->ignoreNewMail()) {
return true;
}
}
return false;
}
}