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.
454 lines
12 KiB
454 lines
12 KiB
/*************************************************************************** |
|
kmsystemtray.cpp - description |
|
------------------- |
|
begin : Fri Aug 31 22:38:44 EDT 2001 |
|
copyright : (C) 2001 by Ryan Breen |
|
email : ryan@porivo.com |
|
***************************************************************************/ |
|
|
|
/*************************************************************************** |
|
* * |
|
* 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. * |
|
* * |
|
***************************************************************************/ |
|
|
|
#ifdef HAVE_CONFIG_H |
|
#include <config.h> |
|
#endif |
|
|
|
#include <kpopupmenu.h> |
|
|
|
#include <kdebug.h> |
|
#include <kiconloader.h> |
|
#include <kwin.h> |
|
|
|
#include <qevent.h> |
|
#include <qlistview.h> |
|
#include <qstring.h> |
|
#include <qtooltip.h> |
|
|
|
#include <qimage.h> |
|
#include <qpixmap.h> |
|
#include <qpixmapcache.h> |
|
|
|
#include "kmmainwin.h" |
|
#include "kmsystemtray.h" |
|
#include "kmfolder.h" |
|
#include "kmfoldertree.h" |
|
#include "kmkernel.h" |
|
#include "kmfoldermgr.h" |
|
#include "kmfolderimap.h" |
|
//#include "kmmainwidget.h" |
|
|
|
/** |
|
* 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. |
|
*/ |
|
KMSystemTray::KMSystemTray(QWidget *parent, const char *name) : KSystemTray(parent, name) |
|
{ |
|
kdDebug(5006) << "Initting systray" << endl; |
|
KIconLoader *loader = KGlobal::iconLoader(); |
|
|
|
mDefaultIcon = loader->loadIcon("kmail", KIcon::Small); |
|
this->setPixmap(mDefaultIcon); |
|
mParentVisible = true; |
|
mStep = 0; |
|
|
|
/** Initiate connections between folders and this object */ |
|
foldersChanged(); |
|
|
|
KMFolderMgr * mgr = kernel->folderMgr(); |
|
KMFolderMgr * imgr = kernel->imapFolderMgr(); |
|
//KMFolderMgr * smgr = kernel->searchFolderMgr(); |
|
connect(mgr, SIGNAL(changed()), this, SLOT(foldersChanged())); |
|
connect(imgr, SIGNAL(changed()), this, SLOT(foldersChanged())); |
|
//connect(smgr, SIGNAL(changed()), this, SLOT(foldersChanged())); |
|
} |
|
|
|
KMSystemTray::~KMSystemTray() |
|
{ |
|
} |
|
|
|
void KMSystemTray::setMode(int newMode) |
|
{ |
|
if(newMode == mMode) return; |
|
|
|
kdDebug(5006) << "Setting systray mMode to " << newMode << endl; |
|
mMode = newMode; |
|
|
|
if(mMode == AlwaysOn) |
|
{ |
|
kdDebug(5006) << "Initting alwayson mMode" << endl; |
|
|
|
mAnimating = false; |
|
mInverted = false; |
|
if(this->isHidden()) |
|
{ |
|
this->show(); |
|
} else |
|
{ |
|
// Already visible, so there must be new mail. Start mAnimating |
|
startAnimation(); |
|
} |
|
} else |
|
{ |
|
if(mAnimating) |
|
{ |
|
// Animating, so there must be new mail. Turn off animation |
|
mAnimating = false; |
|
} else |
|
{ |
|
this->hide(); |
|
} |
|
} |
|
} |
|
|
|
void KMSystemTray::startAnimation() |
|
{ |
|
mAnimating = true; |
|
this->clear(); |
|
kdDebug(5006) << "Called start animate" << endl; |
|
slotAnimate(); |
|
} |
|
|
|
void KMSystemTray::slotAnimate() |
|
{ |
|
|
|
// Swap the icons and check again in half a second as long |
|
// as the user has not signalled |
|
// to stop animation by clicking on the system tray |
|
if( mAnimating ) |
|
{ |
|
switchIcon(); |
|
QTimer::singleShot( 70, this, SLOT(slotAnimate())); |
|
} else |
|
{ |
|
// Animation is over, so start polling again |
|
kdDebug(5006) << "User interrupted animation, poll" << endl; |
|
|
|
// Switch back to the default icon since animation is done |
|
this->setPixmap(mDefaultIcon); |
|
} |
|
} |
|
|
|
/** |
|
* Switch the Pixmap to the next icon in the sequence, moving |
|
* to progressively larger images before cycling back to the |
|
* default. QPixmapCache is used to store the images so building |
|
* the pixmap is only a first time hit. |
|
*/ |
|
void KMSystemTray::switchIcon() |
|
{ |
|
//kdDebug(5006) << "step is " << mStep << endl; |
|
|
|
QString iconName = QString("icon%1").arg(mStep); |
|
QPixmap icon; |
|
|
|
if ( !QPixmapCache::find(iconName, icon) ) { |
|
int w = mDefaultIcon.width() + (mStep * 1); |
|
int h = mDefaultIcon.width() + (mStep * 1); |
|
|
|
//kdDebug(5006) << "Initting icon " << iconName << endl; |
|
|
|
// Scale icon out from default |
|
icon.convertFromImage(mDefaultIcon.convertToImage().smoothScale(w, h)); |
|
|
|
QPixmapCache::insert(iconName, icon); |
|
} |
|
|
|
if(mStep == 9) mInverted = true; |
|
if(mStep == 0) mInverted = false; |
|
|
|
if(mInverted) --mStep; |
|
else ++mStep; |
|
|
|
this->setPixmap(icon); |
|
} |
|
|
|
/** |
|
* Refreshes the list of folders we are monitoring. This is called on |
|
* startup and is also connected to the 'changed' signal on the KMFolderMgr. |
|
*/ |
|
void KMSystemTray::foldersChanged() |
|
{ |
|
/** |
|
* Hide and remove all unread mappings to cover the case where the only |
|
* unread message was in a folder that was just removed. |
|
*/ |
|
mFoldersWithUnread.clear(); |
|
|
|
if(mMode == OnNewMail) |
|
{ |
|
this->hide(); |
|
} |
|
|
|
/** Disconnect all previous connections */ |
|
disconnect(this, SLOT(updateNewMessageNotification(KMFolder *))); |
|
|
|
QStringList folderNames; |
|
QValueList<QGuardedPtr<KMFolder> > folderList; |
|
KMFolderMgr * mgr = kernel->folderMgr(); |
|
KMFolderMgr * imgr = kernel->imapFolderMgr(); |
|
//KMFolderMgr * smgr = kernel->searchFolderMgr(); |
|
mgr->createFolderList(&folderNames, &folderList); |
|
imgr->createFolderList(&folderNames, &folderList); |
|
//smgr->createFolderList(&folderNames, &folderList); |
|
|
|
QStringList::iterator strIt = folderNames.begin(); |
|
|
|
for(QValueList<QGuardedPtr<KMFolder> >::iterator it = folderList.begin(); |
|
it != folderList.end() && strIt != folderNames.end(); ++it, ++strIt) |
|
{ |
|
KMFolder * currentFolder = *it; |
|
QString currentName = *strIt; |
|
|
|
if(!currentFolder->isSystemFolder() || |
|
(currentFolder->protocol() == "imap")) |
|
{ |
|
/** If this is a new folder, start listening for messages */ |
|
connect(currentFolder, SIGNAL(numUnreadMsgsChanged(KMFolder *)), |
|
this, SLOT(updateNewMessageNotification(KMFolder *))); |
|
|
|
/** Check all new folders to see if we started with any new messages */ |
|
updateNewMessageNotification(currentFolder); |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* On left mouse click, switch focus to the first KMMainWin. On right click, |
|
* bring up a list of all folders with a count of unread messages. |
|
*/ |
|
void KMSystemTray::mousePressEvent(QMouseEvent *e) |
|
{ |
|
mAnimating = false; |
|
|
|
// switch to kmail on left mouse button |
|
if( e->button() == LeftButton ) |
|
{ |
|
if(mParentVisible) hideKMail(); |
|
else showKMail(); |
|
|
|
mParentVisible = !mParentVisible; |
|
} |
|
|
|
// open popup menu on right mouse button |
|
if( e->button() == RightButton ) |
|
{ |
|
KPopupMenu* popup = new KPopupMenu(); |
|
popup->insertTitle(*(this->pixmap()), "KMail"); |
|
mPopupFolders.clear(); |
|
mPopupFolders.resize(mFoldersWithUnread.count()); |
|
|
|
QMap<QGuardedPtr<KMFolder>, int>::Iterator it = mFoldersWithUnread.begin(); |
|
for(uint i=0; it != mFoldersWithUnread.end(); ++i) |
|
{ |
|
kdDebug(5006) << "Adding folder" << endl; |
|
if(i > mPopupFolders.size()) mPopupFolders.resize(i * 2); |
|
mPopupFolders.insert(i, it.key()); |
|
QString item = prettyName(it.key()) + "(" + QString::number(it.data()) + ")"; |
|
popup->insertItem(item, this, SLOT(selectedAccount(int)), 0, i); |
|
++it; |
|
} |
|
|
|
kdDebug(5006) << "Folders added" << endl; |
|
|
|
popup->popup(e->globalPos()); |
|
} |
|
|
|
} |
|
|
|
/** |
|
* Return the name of the folder in which the mail is deposited, prepended |
|
* with the account name if the folder is IMAP. |
|
*/ |
|
QString KMSystemTray::prettyName(KMFolder * fldr) |
|
{ |
|
QString rvalue = fldr->label(); |
|
if(fldr->protocol() == "imap") |
|
{ |
|
KMFolderImap * imap = dynamic_cast<KMFolderImap*> (fldr); |
|
assert(imap); |
|
|
|
if((imap->account() != 0) && |
|
(imap->account()->name() != 0) ) |
|
{ |
|
kdDebug(5006) << "IMAP folder, prepend label with type" << endl; |
|
rvalue = imap->account()->name() + "->" + rvalue; |
|
} |
|
} |
|
|
|
kdDebug(5006) << "Got label " << rvalue << endl; |
|
|
|
return rvalue; |
|
} |
|
|
|
/** |
|
* Shows and raises the first KMMainWin in the KMainWindow memberlist and |
|
* switches to the appropriate virtual desktop. |
|
*/ |
|
void KMSystemTray::showKMail() |
|
{ |
|
KMMainWin * mainWin = getKMMainWin(); |
|
assert(mainWin); |
|
if(mainWin) |
|
{ |
|
/** Force window to grab focus */ |
|
mainWin->show(); |
|
mainWin->raise(); |
|
|
|
/** Switch to appropriate desktop */ |
|
int desk = KWin::info(mainWin->winId()).desktop; |
|
KWin::setCurrentDesktop(desk); |
|
} |
|
} |
|
|
|
void KMSystemTray::hideKMail() |
|
{ |
|
|
|
KMMainWin * mainWin = getKMMainWin(); |
|
assert(mainWin); |
|
if(mainWin) |
|
{ |
|
mainWin->hide(); |
|
} |
|
} |
|
|
|
/** |
|
* Grab a pointer to the first KMMainWin in the KMainWindow memberlist. Note |
|
* that this code may not necessarily be safe from race conditions since the |
|
* returned KMMainWin may have been closed by other events between the time that |
|
* this pointer is retrieved and used. |
|
*/ |
|
KMMainWin * KMSystemTray::getKMMainWin() |
|
{ |
|
KMainWindow *kmWin = 0; |
|
|
|
for(kmWin = KMainWindow::memberList->first(); kmWin; |
|
kmWin = KMainWindow::memberList->next()) |
|
if(kmWin->isA("KMMainWin")) break; |
|
if(kmWin && kmWin->isA("KMMainWin")) |
|
{ |
|
return static_cast<KMMainWin *> (kmWin); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* Called on startup of the KMSystemTray and when the numUnreadMsgsChanged signal |
|
* is emitted for one of the watched folders. Shows the system tray icon if there |
|
* are new messages and the icon was hidden, or hides the system tray icon if there |
|
* are no more new messages. |
|
*/ |
|
void KMSystemTray::updateNewMessageNotification(KMFolder * fldr) |
|
{ |
|
|
|
if(!fldr) |
|
{ |
|
kdDebug(5006) << "Null folder, can't mess with that" << endl; |
|
return; |
|
} |
|
|
|
/** The number of unread messages in that folder */ |
|
int unread = fldr->countUnread(); |
|
|
|
bool unmapped = (mFoldersWithUnread.find(fldr) == mFoldersWithUnread.end()); |
|
|
|
if(unread > 0) |
|
{ |
|
/** Add folder to our internal store, or update unread count if already mapped */ |
|
mFoldersWithUnread.insert(fldr, unread); |
|
} |
|
|
|
/** |
|
* Look for folder in the list of folders already represented. If there are |
|
* unread messages and the system tray icon is hidden, show it. If there are |
|
* no unread messages, remove the folder from the mapping. |
|
*/ |
|
if(unmapped) |
|
{ |
|
/** Spurious notification, ignore */ |
|
if(unread == 0) return; |
|
|
|
/** Make sure the icon will be displayed */ |
|
if(mMode == OnNewMail) |
|
{ |
|
if(this->isHidden()) this->show(); |
|
} else |
|
{ |
|
if(!mAnimating) this->startAnimation(); |
|
} |
|
|
|
} else |
|
{ |
|
|
|
if(unread == 0) |
|
{ |
|
kdDebug(5006) << "Removing folder " << fldr->name() << endl; |
|
|
|
/** Remove the folder from the internal store */ |
|
mFoldersWithUnread.remove(fldr); |
|
|
|
/** if this was the last folder in the dictionary, hide the systemtray icon */ |
|
if(mFoldersWithUnread.count() == 0) |
|
{ |
|
mPopupFolders.clear(); |
|
this->disconnect(this, SLOT(selectedAccount(int))); |
|
|
|
if(mMode == OnNewMail) |
|
{ |
|
this->hide(); |
|
} else |
|
{ |
|
mAnimating = false; |
|
} |
|
} |
|
} |
|
} |
|
|
|
/** Update tooltip to reflect count of folders with unread messages */ |
|
int folderCount = mFoldersWithUnread.count(); |
|
|
|
QToolTip::add(this, i18n("There is 1 folder with unread messages.", |
|
"There are %n folders with unread messages.", |
|
folderCount)); |
|
} |
|
|
|
/** |
|
* Called when user selects a folder name from the popup menu. Shows |
|
* the first KMMainWin in the memberlist and jumps to the first unread |
|
* message in the selected folder. |
|
*/ |
|
void KMSystemTray::selectedAccount(int id) |
|
{ |
|
showKMail(); |
|
|
|
KMMainWin * mainWin = getKMMainWin(); |
|
assert(mainWin); |
|
|
|
/** Select folder */ |
|
KMFolder * fldr = mPopupFolders.at(id); |
|
if(!fldr) return; |
|
KMFolderTree * ft = mainWin->folderTree(); |
|
if(!ft) return; |
|
QListViewItem * fldrIdx = ft->indexOfFolder(fldr); |
|
if(!fldrIdx) return; |
|
|
|
ft->setCurrentItem(fldrIdx); |
|
ft->selectCurrentFolder(); |
|
mainWin->folderSelectedUnread(fldr); |
|
} |
|
|
|
|
|
#include "kmsystemtray.moc"
|
|
|