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 #include <config.h> 00020 00021 #include "kmsystemtray.h" 00022 #include "kmfoldertree.h" 00023 #include "kmfoldermgr.h" 00024 #include "kmfolderimap.h" 00025 #include "kmmainwidget.h" 00026 #include "kmacctmgr.h" 00027 #include "globalsettings.h" 00028 00029 #include <kapplication.h> 00030 #include <kmainwindow.h> 00031 #include <kglobalsettings.h> 00032 #include <kiconloader.h> 00033 #include <kiconeffect.h> 00034 #include <kwin.h> 00035 #include <kdebug.h> 00036 00037 #include <qpainter.h> 00038 #include <qbitmap.h> 00039 #include <qtooltip.h> 00040 #include <qwidgetlist.h> 00041 #include <qobjectlist.h> 00042 00043 #include <math.h> 00044 #include <assert.h> 00045 00057 KMSystemTray::KMSystemTray(QWidget *parent, const char *name) 00058 : KSystemTray( parent, name ), 00059 mParentVisible( true ), 00060 mPosOfMainWin( 0, 0 ), 00061 mDesktopOfMainWin( 0 ), 00062 mMode( GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread ), 00063 mCount( 0 ), 00064 mNewMessagePopupId(-1), 00065 mPopupMenu(0) 00066 { 00067 setAlignment( AlignCenter ); 00068 kdDebug(5006) << "Initting systray" << endl; 00069 00070 mLastUpdate = time( 0 ); 00071 mUpdateTimer = new QTimer( this, "systraytimer" ); 00072 connect( mUpdateTimer, SIGNAL( timeout() ), SLOT( updateNewMessages() ) ); 00073 00074 mDefaultIcon = loadIcon( "kmail" ); 00075 mLightIconImage = loadIcon( "kmaillight" ).convertToImage(); 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 connect( kmkernel->acctMgr(), SIGNAL( checkedMail( bool, bool, const QMap<QString, int> & ) ), 00101 SLOT( updateNewMessages() ) ); 00102 } 00103 00104 void KMSystemTray::buildPopupMenu() 00105 { 00106 // Delete any previously created popup menu 00107 delete mPopupMenu; 00108 mPopupMenu = 0; 00109 00110 mPopupMenu = new KPopupMenu(); 00111 KMMainWidget * mainWidget = getKMMainWidget(); 00112 if ( !mainWidget ) 00113 return; 00114 00115 mPopupMenu->insertTitle(*(this->pixmap()), "KMail"); 00116 KAction * action; 00117 if ( ( action = mainWidget->action("check_mail") ) ) 00118 action->plug( mPopupMenu ); 00119 if ( ( action = mainWidget->action("check_mail_in") ) ) 00120 action->plug( mPopupMenu ); 00121 mPopupMenu->insertSeparator(); 00122 if ( ( action = mainWidget->action("new_message") ) ) 00123 action->plug( mPopupMenu ); 00124 if ( ( action = mainWidget->action("kmail_configure_kmail") ) ) 00125 action->plug( mPopupMenu ); 00126 mPopupMenu->insertSeparator(); 00127 00128 KMainWindow *mainWin = ::qt_cast<KMainWindow*>(getKMMainWidget()->topLevelWidget()); 00129 if(mainWin) 00130 if ( ( action=mainWin->actionCollection()->action("file_quit") ) ) 00131 action->plug( mPopupMenu ); 00132 } 00133 00134 KMSystemTray::~KMSystemTray() 00135 { 00136 // unregister the applet 00137 kmkernel->unregisterSystemTrayApplet( this ); 00138 00139 delete mPopupMenu; 00140 mPopupMenu = 0; 00141 } 00142 00143 void KMSystemTray::setMode(int newMode) 00144 { 00145 if(newMode == mMode) return; 00146 00147 kdDebug(5006) << "Setting systray mMode to " << newMode << endl; 00148 mMode = newMode; 00149 00150 switch ( mMode ) { 00151 case GlobalSettings::EnumSystemTrayPolicy::ShowAlways: 00152 if ( isHidden() ) 00153 show(); 00154 break; 00155 case GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread: 00156 if ( mCount == 0 && !isHidden() ) 00157 hide(); 00158 else if ( mCount > 0 && isHidden() ) 00159 show(); 00160 default: 00161 kdDebug(5006) << k_funcinfo << " Unknown systray mode " << mMode << endl; 00162 } 00163 } 00164 00165 int KMSystemTray::mode() const 00166 { 00167 return mMode; 00168 } 00169 00175 void KMSystemTray::updateCount() 00176 { 00177 if(mCount != 0) 00178 { 00179 int oldPixmapWidth = pixmap()->size().width(); 00180 int oldPixmapHeight = pixmap()->size().height(); 00181 00182 QString countString = QString::number( mCount ); 00183 QFont countFont = KGlobalSettings::generalFont(); 00184 countFont.setBold(true); 00185 00186 // decrease the size of the font for the number of unread messages if the 00187 // number doesn't fit into the available space 00188 float countFontSize = countFont.pointSizeFloat(); 00189 QFontMetrics qfm( countFont ); 00190 int width = qfm.width( countString ); 00191 if( width > oldPixmapWidth ) 00192 { 00193 countFontSize *= float( oldPixmapWidth ) / float( width ); 00194 countFont.setPointSizeFloat( countFontSize ); 00195 } 00196 00197 // Create an image which represents the number of unread messages 00198 // and which has a transparent background. 00199 // Unfortunately this required the following twisted code because for some 00200 // reason text that is drawn on a transparent pixmap is invisible 00201 // (apparently the alpha channel isn't changed when the text is drawn). 00202 // Therefore I have to draw the text on a solid background and then remove 00203 // the background by making it transparent with QPixmap::setMask. This 00204 // involves the slow createHeuristicMask() function (from the API docs: 00205 // "This function is slow because it involves transformation to a QImage, 00206 // non-trivial computations and a transformation back to a QBitmap."). Then 00207 // I have to convert the resulting QPixmap to a QImage in order to overlay 00208 // the light KMail icon with the number (because KIconEffect::overlay only 00209 // works with QImage). Finally the resulting QImage has to be converted 00210 // back to a QPixmap. 00211 // That's a lot of work for overlaying the KMail icon with the number of 00212 // unread messages, but every other approach I tried failed miserably. 00213 // IK, 2003-09-22 00214 QPixmap numberPixmap( oldPixmapWidth, oldPixmapHeight ); 00215 numberPixmap.fill( Qt::white ); 00216 QPainter p( &numberPixmap ); 00217 p.setFont( countFont ); 00218 p.setPen( Qt::blue ); 00219 p.drawText( numberPixmap.rect(), Qt::AlignCenter, countString ); 00220 numberPixmap.setMask( numberPixmap.createHeuristicMask() ); 00221 QImage numberImage = numberPixmap.convertToImage(); 00222 00223 // Overlay the light KMail icon with the number image 00224 QImage iconWithNumberImage = mLightIconImage.copy(); 00225 KIconEffect::overlay( iconWithNumberImage, numberImage ); 00226 00227 QPixmap iconWithNumber; 00228 iconWithNumber.convertFromImage( iconWithNumberImage ); 00229 setPixmap( iconWithNumber ); 00230 } else 00231 { 00232 setPixmap( mDefaultIcon ); 00233 } 00234 } 00235 00240 void KMSystemTray::foldersChanged() 00241 { 00246 mFoldersWithUnread.clear(); 00247 mCount = 0; 00248 00249 if ( mMode == GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread ) { 00250 hide(); 00251 } 00252 00254 disconnect(this, SLOT(updateNewMessageNotification(KMFolder *))); 00255 00256 QStringList folderNames; 00257 QValueList<QGuardedPtr<KMFolder> > folderList; 00258 kmkernel->folderMgr()->createFolderList(&folderNames, &folderList); 00259 kmkernel->imapFolderMgr()->createFolderList(&folderNames, &folderList); 00260 kmkernel->dimapFolderMgr()->createFolderList(&folderNames, &folderList); 00261 kmkernel->searchFolderMgr()->createFolderList(&folderNames, &folderList); 00262 00263 QStringList::iterator strIt = folderNames.begin(); 00264 00265 for(QValueList<QGuardedPtr<KMFolder> >::iterator it = folderList.begin(); 00266 it != folderList.end() && strIt != folderNames.end(); ++it, ++strIt) 00267 { 00268 KMFolder * currentFolder = *it; 00269 QString currentName = *strIt; 00270 00271 if ( ((!currentFolder->isSystemFolder() || (currentFolder->name().lower() == "inbox")) || 00272 (currentFolder->folderType() == KMFolderTypeImap)) && 00273 !currentFolder->ignoreNewMail() ) 00274 { 00276 connect(currentFolder, SIGNAL(numUnreadMsgsChanged(KMFolder *)), 00277 this, SLOT(updateNewMessageNotification(KMFolder *))); 00278 00280 updateNewMessageNotification(currentFolder); 00281 } 00282 } 00283 } 00284 00289 void KMSystemTray::mousePressEvent(QMouseEvent *e) 00290 { 00291 // switch to kmail on left mouse button 00292 if( e->button() == LeftButton ) 00293 { 00294 if( mParentVisible && mainWindowIsOnCurrentDesktop() ) 00295 hideKMail(); 00296 else 00297 showKMail(); 00298 } 00299 00300 // open popup menu on right mouse button 00301 if( e->button() == RightButton ) 00302 { 00303 mPopupFolders.clear(); 00304 mPopupFolders.resize(mFoldersWithUnread.count()); 00305 00306 // Rebuild popup menu at click time to minimize race condition if 00307 // the base KMainWidget is closed. 00308 buildPopupMenu(); 00309 00310 if(mNewMessagePopupId != -1) 00311 { 00312 mPopupMenu->removeItem(mNewMessagePopupId); 00313 } 00314 00315 if(mFoldersWithUnread.count() > 0) 00316 { 00317 KPopupMenu *newMessagesPopup = new KPopupMenu(); 00318 00319 QMap<QGuardedPtr<KMFolder>, int>::Iterator it = mFoldersWithUnread.begin(); 00320 for(uint i=0; it != mFoldersWithUnread.end(); ++i) 00321 { 00322 kdDebug(5006) << "Adding folder" << endl; 00323 if(i > mPopupFolders.size()) mPopupFolders.resize(i * 2); 00324 mPopupFolders.insert(i, it.key()); 00325 QString item = prettyName(it.key()) + "(" + QString::number(it.data()) + ")"; 00326 newMessagesPopup->insertItem(item, this, SLOT(selectedAccount(int)), 0, i); 00327 ++it; 00328 } 00329 00330 mNewMessagePopupId = mPopupMenu->insertItem(i18n("New Messages In"), 00331 newMessagesPopup, mNewMessagePopupId, 3); 00332 00333 kdDebug(5006) << "Folders added" << endl; 00334 } 00335 00336 mPopupMenu->popup(e->globalPos()); 00337 } 00338 00339 } 00340 00345 QString KMSystemTray::prettyName(KMFolder * fldr) 00346 { 00347 QString rvalue = fldr->label(); 00348 if(fldr->folderType() == KMFolderTypeImap) 00349 { 00350 KMFolderImap * imap = dynamic_cast<KMFolderImap*> (fldr->storage()); 00351 assert(imap); 00352 00353 if((imap->account() != 0) && 00354 (imap->account()->name() != 0) ) 00355 { 00356 kdDebug(5006) << "IMAP folder, prepend label with type" << endl; 00357 rvalue = imap->account()->name() + "->" + rvalue; 00358 } 00359 } 00360 00361 kdDebug(5006) << "Got label " << rvalue << endl; 00362 00363 return rvalue; 00364 } 00365 00366 00367 bool KMSystemTray::mainWindowIsOnCurrentDesktop() 00368 { 00369 KMMainWidget * mainWidget = getKMMainWidget(); 00370 if ( !mainWidget ) 00371 return false; 00372 00373 QWidget *mainWin = getKMMainWidget()->topLevelWidget(); 00374 if ( !mainWin ) 00375 return false; 00376 00377 return KWin::windowInfo( mainWin->winId(), 00378 NET::WMDesktop ).isOnCurrentDesktop(); 00379 } 00380 00385 void KMSystemTray::showKMail() 00386 { 00387 if (!getKMMainWidget()) 00388 return; 00389 QWidget *mainWin = getKMMainWidget()->topLevelWidget(); 00390 assert(mainWin); 00391 if(mainWin) 00392 { 00393 // switch to appropriate desktop 00394 if ( mDesktopOfMainWin != NET::OnAllDesktops ) 00395 KWin::setCurrentDesktop( mDesktopOfMainWin ); 00396 if ( !mParentVisible ) { 00397 if ( mDesktopOfMainWin == NET::OnAllDesktops ) 00398 KWin::setOnAllDesktops( mainWin->winId(), true ); 00399 mainWin->move( mPosOfMainWin ); 00400 mainWin->show(); 00401 } 00402 KWin::activateWindow( mainWin->winId() ); 00403 mParentVisible = true; 00404 } 00405 00406 //Fake that the folders have changed so that the icon status is correct 00407 foldersChanged(); 00408 } 00409 00410 void KMSystemTray::hideKMail() 00411 { 00412 if (!getKMMainWidget()) 00413 return; 00414 QWidget *mainWin = getKMMainWidget()->topLevelWidget(); 00415 assert(mainWin); 00416 if(mainWin) 00417 { 00418 mDesktopOfMainWin = KWin::windowInfo( mainWin->winId(), 00419 NET::WMDesktop ).desktop(); 00420 mPosOfMainWin = mainWin->pos(); 00421 // iconifying is unnecessary, but it looks cooler 00422 KWin::iconifyWindow( mainWin->winId() ); 00423 mainWin->hide(); 00424 mParentVisible = false; 00425 } 00426 } 00427 00431 KMMainWidget * KMSystemTray::getKMMainWidget() 00432 { 00433 QWidgetList *l = kapp->topLevelWidgets(); 00434 QWidgetListIt it( *l ); 00435 QWidget *wid; 00436 00437 while ( (wid = it.current()) != 0 ) { 00438 ++it; 00439 QObjectList *l2 = wid->topLevelWidget()->queryList("KMMainWidget"); 00440 if (l2 && l2->first()) 00441 { 00442 KMMainWidget* kmmw = dynamic_cast<KMMainWidget *>(l2->first()); 00443 assert (kmmw); 00444 delete l2; 00445 delete l; 00446 return kmmw; 00447 } 00448 delete l2; 00449 } 00450 delete l; 00451 return 0; 00452 } 00453 00460 void KMSystemTray::updateNewMessageNotification(KMFolder * fldr) 00461 { 00462 //We don't want to count messages from search folders as they 00463 // already counted as part of their original folders 00464 if( !fldr || 00465 fldr->folderType() == KMFolderTypeSearch ) 00466 { 00467 // kdDebug(5006) << "Null or a search folder, can't mess with that" << endl; 00468 return; 00469 } 00470 00471 mPendingUpdates[ fldr ] = true; 00472 if ( time( 0 ) - mLastUpdate > 2 ) { 00473 mUpdateTimer->stop(); 00474 updateNewMessages(); 00475 } 00476 else { 00477 mUpdateTimer->start(150, true); 00478 } 00479 } 00480 00481 void KMSystemTray::updateNewMessages() 00482 { 00483 for ( QMap<QGuardedPtr<KMFolder>, bool>::Iterator it = mPendingUpdates.begin(); 00484 it != mPendingUpdates.end(); ++it) 00485 { 00486 KMFolder *fldr = it.key(); 00487 if ( !fldr ) // deleted folder 00488 continue; 00489 00491 int unread = fldr->countUnread(); 00492 00493 QMap<QGuardedPtr<KMFolder>, int>::Iterator it = 00494 mFoldersWithUnread.find(fldr); 00495 bool unmapped = (it == mFoldersWithUnread.end()); 00496 00499 if(unmapped) mCount += unread; 00500 /* Otherwise, get the difference between the numUnread in the folder and 00501 * our last known version, and adjust mCount with that difference */ 00502 else 00503 { 00504 int diff = unread - it.data(); 00505 mCount += diff; 00506 } 00507 00508 if(unread > 0) 00509 { 00511 mFoldersWithUnread.insert(fldr, unread); 00512 //kdDebug(5006) << "There are now " << mFoldersWithUnread.count() << " folders with unread" << endl; 00513 } 00514 00520 if(unmapped) 00521 { 00523 if(unread == 0) continue; 00524 00526 if ( ( mMode == GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread ) 00527 && isHidden() ) { 00528 show(); 00529 } 00530 00531 } else 00532 { 00533 00534 if(unread == 0) 00535 { 00536 kdDebug(5006) << "Removing folder from internal store " << fldr->name() << endl; 00537 00539 mFoldersWithUnread.remove(fldr); 00540 00542 if(mFoldersWithUnread.count() == 0) 00543 { 00544 mPopupFolders.clear(); 00545 disconnect(this, SLOT(selectedAccount(int))); 00546 00547 mCount = 0; 00548 00549 if ( mMode == GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread ) { 00550 hide(); 00551 } 00552 } 00553 } 00554 } 00555 00556 } 00557 mPendingUpdates.clear(); 00558 updateCount(); 00559 00561 QToolTip::remove(this); 00562 QToolTip::add(this, i18n("There is 1 unread message.", 00563 "There are %n unread messages.", 00564 mCount)); 00565 00566 mLastUpdate = time( 0 ); 00567 } 00568 00574 void KMSystemTray::selectedAccount(int id) 00575 { 00576 showKMail(); 00577 00578 KMMainWidget * mainWidget = getKMMainWidget(); 00579 if (!mainWidget) 00580 { 00581 kmkernel->openReader(); 00582 mainWidget = getKMMainWidget(); 00583 } 00584 00585 assert(mainWidget); 00586 00588 KMFolder * fldr = mPopupFolders.at(id); 00589 if(!fldr) return; 00590 KMFolderTree * ft = mainWidget->folderTree(); 00591 if(!ft) return; 00592 QListViewItem * fldrIdx = ft->indexOfFolder(fldr); 00593 if(!fldrIdx) return; 00594 00595 ft->setCurrentItem(fldrIdx); 00596 ft->selectCurrentFolder(); 00597 } 00598 00599 #include "kmsystemtray.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Oct 1 15:19:24 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003