kmail Library API Documentation

kmsystemtray.cpp

00001 // -*- mode: C++; c-file-style: "gnu" -*-
00002 /***************************************************************************
00003                           kmsystemtray.cpp  -  description
00004                              -------------------
00005     begin                : Fri Aug 31 22:38:44 EDT 2001
00006     copyright            : (C) 2001 by Ryan Breen
00007     email                : ryan@porivo.com
00008  ***************************************************************************/
00009 
00010 /***************************************************************************
00011  *                                                                         *
00012  *   This program is free software; you can redistribute it and/or modify  *
00013  *   it under the terms of the GNU General Public License as published by  *
00014  *   the Free Software Foundation; either version 2 of the License, or     *
00015  *   (at your option) any later version.                                   *
00016  *                                                                         *
00017  ***************************************************************************/
00018 
00019 #ifdef HAVE_CONFIG_H
00020 #include <config.h>
00021 #endif
00022 
00023 #include "kmsystemtray.h"
00024 #include "kmfoldertree.h"
00025 #include "kmfoldermgr.h"
00026 #include "kmfolderimap.h"
00027 #include "kmmainwidget.h"
00028 
00029 #include <kapplication.h>
00030 #include <kglobalsettings.h>
00031 #include <kiconloader.h>
00032 #include <kiconeffect.h>
00033 #include <kwin.h>
00034 #include <kdebug.h>
00035 
00036 #include <qpainter.h>
00037 #include <qbitmap.h>
00038 #include <qtooltip.h>
00039 #include <qwidgetlist.h>
00040 #include <qobjectlist.h>
00041 
00042 #include <math.h>
00043 #include <assert.h>
00044 
00056 KMSystemTray::KMSystemTray(QWidget *parent, const char *name)
00057   : KSystemTray( parent, name ),
00058     mParentVisible( true ),
00059     mPosOfMainWin( 0, 0 ),
00060     mDesktopOfMainWin( 0 ),
00061     mMode( OnNewMail ),
00062     mCount( 0 ),
00063     mNewMessagePopupId(-1),
00064     mPopupMenu(0)
00065 {
00066   setAlignment( AlignCenter );
00067   kdDebug(5006) << "Initting systray" << endl;
00068 
00069 #if KDE_IS_VERSION( 3, 1,92 )
00070   mDefaultIcon = loadIcon( "kmail" );
00071   mLightIconImage = loadIcon( "kmaillight" ).convertToImage();
00072 #else
00073   mDefaultIcon = SmallIcon( "kmail" );
00074   mLightIconImage = SmallIcon( "kmaillight" ).convertToImage();
00075 #endif
00076 
00077   setPixmap(mDefaultIcon);
00078 
00079   KMMainWidget * mainWidget = getKMMainWidget();
00080   if ( mainWidget ) {
00081     QWidget * mainWin = mainWidget->topLevelWidget();
00082     if ( mainWin ) {
00083       mDesktopOfMainWin = KWin::windowInfo( mainWin->winId(),
00084                                             NET::WMDesktop ).desktop();
00085       mPosOfMainWin = mainWin->pos();
00086     }
00087   }
00088 
00089   // register the applet with the kernel
00090   kmkernel->registerSystemTrayApplet( this );
00091 
00093   foldersChanged();
00094 
00095   connect( kmkernel->folderMgr(), SIGNAL(changed()), SLOT(foldersChanged()));
00096   connect( kmkernel->imapFolderMgr(), SIGNAL(changed()), SLOT(foldersChanged()));
00097   connect( kmkernel->dimapFolderMgr(), SIGNAL(changed()), SLOT(foldersChanged()));
00098   connect( kmkernel->searchFolderMgr(), SIGNAL(changed()), SLOT(foldersChanged()));
00099 }
00100 
00101 void KMSystemTray::buildPopupMenu()
00102 {
00103   // Delete any previously created popup menu
00104   delete mPopupMenu;
00105   mPopupMenu = 0;
00106 
00107   mPopupMenu = new KPopupMenu();
00108   KMMainWidget * mainWidget = getKMMainWidget();
00109   if ( !mainWidget )
00110     return;
00111 
00112   mPopupMenu->insertTitle(*(this->pixmap()), "KMail");
00113   KAction * action;
00114   if ( ( action = mainWidget->action("check_mail") ) )
00115     action->plug( mPopupMenu );
00116   if ( ( action = mainWidget->action("check_mail_in") ) )
00117     action->plug( mPopupMenu );
00118   mPopupMenu->insertSeparator();
00119   if ( ( action = mainWidget->action("new_message") ) )
00120     action->plug( mPopupMenu );
00121   if ( ( action = mainWidget->action("kmail_configure_kmail") ) )
00122     action->plug( mPopupMenu );
00123   mPopupMenu->insertSeparator();
00124   if ( ( action = mainWidget->action("file_quit") ) )
00125     action->plug( mPopupMenu );
00126 }
00127 
00128 KMSystemTray::~KMSystemTray()
00129 {
00130   // unregister the applet
00131   kmkernel->unregisterSystemTrayApplet( this );
00132 
00133   delete mPopupMenu;
00134   mPopupMenu = 0;
00135 }
00136 
00137 void KMSystemTray::setMode(int newMode)
00138 {
00139   if(newMode == mMode) return;
00140 
00141   kdDebug(5006) << "Setting systray mMode to " << newMode << endl;
00142   mMode = newMode;
00143 
00144   if(mMode == AlwaysOn)
00145   {
00146     kdDebug(5006) << "Initting alwayson mMode" << endl;
00147 
00148     if(isHidden()) show();
00149   } else
00150   {
00151     if(mCount == 0)
00152     {
00153       hide();
00154     }
00155   }
00156 }
00157 
00163 void KMSystemTray::updateCount()
00164 {
00165   if(mCount != 0)
00166   {
00167     int oldPixmapWidth = pixmap()->size().width();
00168     int oldPixmapHeight = pixmap()->size().height();
00169 
00170     QString countString = QString::number( mCount );
00171     QFont countFont = KGlobalSettings::generalFont();
00172     countFont.setBold(true);
00173 
00174     // decrease the size of the font for the number of unread messages if the
00175     // number doesn't fit into the available space
00176     float countFontSize = countFont.pointSizeFloat();
00177     QFontMetrics qfm( countFont );
00178     int width = qfm.width( countString );
00179     if( width > oldPixmapWidth )
00180     {
00181       countFontSize *= float( oldPixmapWidth ) / float( width );
00182       countFont.setPointSizeFloat( countFontSize );
00183     }
00184 
00185     // Create an image which represents the number of unread messages
00186     // and which has a transparent background.
00187     // Unfortunately this required the following twisted code because for some
00188     // reason text that is drawn on a transparent pixmap is invisible
00189     // (apparently the alpha channel isn't changed when the text is drawn).
00190     // Therefore I have to draw the text on a solid background and then remove
00191     // the background by making it transparent with QPixmap::setMask. This
00192     // involves the slow createHeuristicMask() function (from the API docs:
00193     // "This function is slow because it involves transformation to a QImage,
00194     // non-trivial computations and a transformation back to a QBitmap."). Then
00195     // I have to convert the resulting QPixmap to a QImage in order to overlay
00196     // the light KMail icon with the number (because KIconEffect::overlay only
00197     // works with QImage). Finally the resulting QImage has to be converted
00198     // back to a QPixmap.
00199     // That's a lot of work for overlaying the KMail icon with the number of
00200     // unread messages, but every other approach I tried failed miserably.
00201     //                                                           IK, 2003-09-22
00202     QPixmap numberPixmap( oldPixmapWidth, oldPixmapHeight );
00203     numberPixmap.fill( Qt::white );
00204     QPainter p( &numberPixmap );
00205     p.setFont( countFont );
00206     p.setPen( Qt::blue );
00207     p.drawText( numberPixmap.rect(), Qt::AlignCenter, countString );
00208     numberPixmap.setMask( numberPixmap.createHeuristicMask() );
00209     QImage numberImage = numberPixmap.convertToImage();
00210 
00211     // Overlay the light KMail icon with the number image
00212     QImage iconWithNumberImage = mLightIconImage.copy();
00213     KIconEffect::overlay( iconWithNumberImage, numberImage );
00214 
00215     QPixmap iconWithNumber;
00216     iconWithNumber.convertFromImage( iconWithNumberImage );
00217     setPixmap( iconWithNumber );
00218   } else
00219   {
00220     setPixmap( mDefaultIcon );
00221   }
00222 }
00223 
00228 void KMSystemTray::foldersChanged()
00229 {
00234   mFoldersWithUnread.clear();
00235   mCount = 0;
00236 
00237   if(mMode == OnNewMail)
00238   {
00239     hide();
00240   }
00241 
00243   disconnect(this, SLOT(updateNewMessageNotification(KMFolder *)));
00244 
00245   QStringList folderNames;
00246   QValueList<QGuardedPtr<KMFolder> > folderList;
00247   kmkernel->folderMgr()->createFolderList(&folderNames, &folderList);
00248   kmkernel->imapFolderMgr()->createFolderList(&folderNames, &folderList);
00249   kmkernel->dimapFolderMgr()->createFolderList(&folderNames, &folderList);
00250   kmkernel->searchFolderMgr()->createFolderList(&folderNames, &folderList);
00251 
00252   QStringList::iterator strIt = folderNames.begin();
00253 
00254   for(QValueList<QGuardedPtr<KMFolder> >::iterator it = folderList.begin();
00255      it != folderList.end() && strIt != folderNames.end(); ++it, ++strIt)
00256   {
00257     KMFolder * currentFolder = *it;
00258     QString currentName = *strIt;
00259 
00260     if((!currentFolder->isSystemFolder() || (currentFolder->name().lower() == "inbox")) ||
00261        (currentFolder->folderType() == KMFolderTypeImap))
00262     {
00264       connect(currentFolder, SIGNAL(numUnreadMsgsChanged(KMFolder *)),
00265               this, SLOT(updateNewMessageNotification(KMFolder *)));
00266 
00268       updateNewMessageNotification(currentFolder);
00269     }
00270   }
00271 }
00272 
00277 void KMSystemTray::mousePressEvent(QMouseEvent *e)
00278 {
00279   // switch to kmail on left mouse button
00280   if( e->button() == LeftButton )
00281   {
00282     if( mParentVisible && mainWindowIsOnCurrentDesktop() )
00283       hideKMail();
00284     else
00285       showKMail();
00286   }
00287 
00288   // open popup menu on right mouse button
00289   if( e->button() == RightButton )
00290   {
00291     mPopupFolders.clear();
00292     mPopupFolders.resize(mFoldersWithUnread.count());
00293 
00294     // Rebuild popup menu at click time to minimize race condition if
00295     // the base KMainWidget is closed.
00296     buildPopupMenu();
00297 
00298     if(mNewMessagePopupId != -1)
00299     {
00300       mPopupMenu->removeItem(mNewMessagePopupId);
00301     }
00302 
00303     if(mFoldersWithUnread.count() > 0)
00304     {
00305       KPopupMenu *newMessagesPopup = new KPopupMenu();
00306 
00307       QMap<QGuardedPtr<KMFolder>, int>::Iterator it = mFoldersWithUnread.begin();
00308       for(uint i=0; it != mFoldersWithUnread.end(); ++i)
00309       {
00310         kdDebug(5006) << "Adding folder" << endl;
00311         if(i > mPopupFolders.size()) mPopupFolders.resize(i * 2);
00312         mPopupFolders.insert(i, it.key());
00313         QString item = prettyName(it.key()) + "(" + QString::number(it.data()) + ")";
00314         newMessagesPopup->insertItem(item, this, SLOT(selectedAccount(int)), 0, i);
00315         ++it;
00316       }
00317 
00318       mNewMessagePopupId = mPopupMenu->insertItem(i18n("New Messages In..."),
00319                                                   newMessagesPopup, mNewMessagePopupId, 3);
00320 
00321       kdDebug(5006) << "Folders added" << endl;
00322     }
00323 
00324     mPopupMenu->popup(e->globalPos());
00325   }
00326 
00327 }
00328 
00333 QString KMSystemTray::prettyName(KMFolder * fldr)
00334 {
00335   QString rvalue = fldr->label();
00336   if(fldr->folderType() == KMFolderTypeImap)
00337   {
00338     KMFolderImap * imap = dynamic_cast<KMFolderImap*> (fldr);
00339     assert(imap);
00340 
00341     if((imap->account() != 0) &&
00342        (imap->account()->name() != 0) )
00343     {
00344       kdDebug(5006) << "IMAP folder, prepend label with type" << endl;
00345       rvalue = imap->account()->name() + "->" + rvalue;
00346     }
00347   }
00348 
00349   kdDebug(5006) << "Got label " << rvalue << endl;
00350 
00351   return rvalue;
00352 }
00353 
00354 
00355 bool KMSystemTray::mainWindowIsOnCurrentDesktop()
00356 {
00357   KMMainWidget * mainWidget = getKMMainWidget();
00358   if ( !mainWidget )
00359     return false;
00360 
00361   QWidget *mainWin = getKMMainWidget()->topLevelWidget();
00362   if ( !mainWin )
00363     return false;
00364 
00365   return KWin::windowInfo( mainWin->winId(),
00366                            NET::WMDesktop ).isOnCurrentDesktop();
00367 }
00368 
00373 void KMSystemTray::showKMail()
00374 {
00375   if (!getKMMainWidget())
00376     return;
00377   QWidget *mainWin = getKMMainWidget()->topLevelWidget();
00378   assert(mainWin);
00379   if(mainWin)
00380   {
00381     // switch to appropriate desktop
00382     if ( mDesktopOfMainWin != NET::OnAllDesktops )
00383       KWin::setCurrentDesktop( mDesktopOfMainWin );
00384     if ( !mParentVisible ) {
00385       if ( mDesktopOfMainWin == NET::OnAllDesktops )
00386         KWin::setOnAllDesktops( mainWin->winId(), true );
00387       mainWin->move( mPosOfMainWin );
00388       mainWin->show();
00389     }
00390     KWin::activateWindow( mainWin->winId() );
00391     mParentVisible = true;
00392   }
00393 }
00394 
00395 void KMSystemTray::hideKMail()
00396 {
00397   if (!getKMMainWidget())
00398     return;
00399   QWidget *mainWin = getKMMainWidget()->topLevelWidget();
00400   assert(mainWin);
00401   if(mainWin)
00402   {
00403     mDesktopOfMainWin = KWin::windowInfo( mainWin->winId(),
00404                                           NET::WMDesktop ).desktop();
00405     mPosOfMainWin = mainWin->pos();
00406     // iconifying is unnecessary, but it looks cooler
00407     KWin::iconifyWindow( mainWin->winId() );
00408     mainWin->hide();
00409     mParentVisible = false;
00410   }
00411 }
00412 
00416 KMMainWidget * KMSystemTray::getKMMainWidget()
00417 {
00418   QWidgetList *l = kapp->topLevelWidgets();
00419   QWidgetListIt it( *l );
00420   QWidget *wid;
00421 
00422   while ( (wid = it.current()) != 0 ) {
00423     ++it;
00424     QObjectList *l2 = wid->topLevelWidget()->queryList("KMMainWidget");
00425     if (l2 && l2->first())
00426     {
00427       KMMainWidget* kmmw = dynamic_cast<KMMainWidget *>(l2->first());
00428       assert (kmmw);
00429       delete l2;
00430       delete l;
00431       return kmmw;
00432     }
00433     delete l2;
00434   }
00435   delete l;
00436   return 0;
00437 }
00438 
00445 void KMSystemTray::updateNewMessageNotification(KMFolder * fldr)
00446 {
00447   //We don't want to count messages from search folders as they
00448   //  already counted as part of their original folders
00449   if( !fldr ||
00450       fldr->folderType() == KMFolderTypeSearch )
00451   {
00452     kdDebug(5006) << "Null or a search folder, can't mess with that" << endl;
00453     return;
00454   }
00455 
00457   int unread = fldr->countUnread();
00458 
00459   QMap<QGuardedPtr<KMFolder>, int>::Iterator it =
00460   mFoldersWithUnread.find(fldr);
00461   bool unmapped = (it == mFoldersWithUnread.end());
00462 
00465   if(unmapped) mCount += unread;
00466   /* Otherwise, get the difference between the numUnread in the folder and
00467    * our last known version, and adjust mCount with that difference */
00468   else
00469   {
00470     int diff = unread - it.data();
00471     mCount += diff;
00472   }
00473 
00474   if(unread > 0)
00475   {
00477     mFoldersWithUnread.insert(fldr, unread);
00478     kdDebug(5006) << "There are now " << mFoldersWithUnread.count() << " folders with unread" << endl;
00479   }
00480 
00486   if(unmapped)
00487   {
00489     if(unread == 0) return;
00490 
00492     if(mMode == OnNewMail)
00493     {
00494       if(isHidden()) show();
00495     }
00496 
00497   } else
00498   {
00499 
00500     if(unread == 0)
00501     {
00502       kdDebug(5006) << "Removing folder from internal store " << fldr->name() << endl;
00503 
00505       mFoldersWithUnread.remove(fldr);
00506 
00508       if(mFoldersWithUnread.count() == 0)
00509       {
00510         mPopupFolders.clear();
00511         disconnect(this, SLOT(selectedAccount(int)));
00512 
00513         mCount = 0;
00514 
00515         if(mMode == OnNewMail)
00516           hide();
00517       }
00518     }
00519   }
00520 
00521   updateCount();
00522 
00524   QToolTip::remove(this);
00525   QToolTip::add(this, i18n("There is 1 unread message.",
00526                            "There are %n unread messages.",
00527                            mCount));
00528 }
00529 
00535 void KMSystemTray::selectedAccount(int id)
00536 {
00537   showKMail();
00538 
00539   KMMainWidget * mainWidget = getKMMainWidget();
00540   if (!mainWidget)
00541   {
00542     kmkernel->openReader();
00543     mainWidget = getKMMainWidget();
00544   }
00545 
00546   assert(mainWidget);
00547 
00549   KMFolder * fldr = mPopupFolders.at(id);
00550   if(!fldr) return;
00551   KMFolderTree * ft = mainWidget->folderTree();
00552   if(!ft) return;
00553   QListViewItem * fldrIdx = ft->indexOfFolder(fldr);
00554   if(!fldrIdx) return;
00555 
00556   ft->setCurrentItem(fldrIdx);
00557   ft->selectCurrentFolder();
00558 }
00559 
00560 
00561 #include "kmsystemtray.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:37:34 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003