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 Wed Jul 28 23:58:04 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003