kmail

kmsystemtray.cpp
1/***************************************************************************
2 kmsystemtray.cpp - description
3 -------------------
4 begin : Fri Aug 31 22:38:44 EDT 2001
5 copyright : (C) 2001 by Ryan Breen
6 email : ryan@porivo.com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include <config.h>
19
20#include "kmsystemtray.h"
21#include "kmfolder.h"
22#include "kmfoldertree.h"
23#include "kmfoldermgr.h"
24#include "kmfolderimap.h"
25#include "kmmainwidget.h"
26#include "accountmanager.h"
28#include "globalsettings.h"
29
30#include <tdeapplication.h>
31#include <tdemainwindow.h>
32#include <tdeglobalsettings.h>
33#include <kiconloader.h>
34#include <kiconeffect.h>
35#include <twin.h>
36#include <kdebug.h>
37#include <tdepopupmenu.h>
38
39#include <tqpainter.h>
40#include <tqbitmap.h>
41#include <tqtooltip.h>
42#include <tqwidgetlist.h>
43#include <tqobjectlist.h>
44
45#include <math.h>
46#include <assert.h>
47
59KMSystemTray::KMSystemTray(TQWidget *parent, const char *name)
60 : KSystemTray( parent, name ),
61 mParentVisible( true ),
62 mPosOfMainWin( 0, 0 ),
63 mDesktopOfMainWin( 0 ),
64 mMode( GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread ),
65 mCount( 0 ),
66 mNewMessagePopupId(-1),
67 mPopupMenu(0)
68{
69 setAlignment( AlignCenter );
70 kdDebug(5006) << "Initting systray" << endl;
71
72 mLastUpdate = time( 0 );
73 mUpdateTimer = new TQTimer( this, "systraytimer" );
74 connect( mUpdateTimer, TQ_SIGNAL( timeout() ), TQ_SLOT( updateNewMessages() ) );
75
76 mDefaultIcon = loadIcon( "kmail" );
77 mLightIconImage = loadIcon( "kmaillight" ).convertToImage();
78
79 setPixmap(mDefaultIcon);
80
81 KMMainWidget * mainWidget = kmkernel->getKMMainWidget();
82 if ( mainWidget ) {
83 TQWidget * mainWin = mainWidget->topLevelWidget();
84 if ( mainWin ) {
85 mDesktopOfMainWin = KWin::windowInfo( mainWin->winId(),
86 NET::WMDesktop ).desktop();
87 mPosOfMainWin = mainWin->pos();
88 }
89 }
90
91 // register the applet with the kernel
92 kmkernel->registerSystemTrayApplet( this );
93
96
97 connect( kmkernel->folderMgr(), TQ_SIGNAL(changed()), TQ_SLOT(foldersChanged()));
98 connect( kmkernel->imapFolderMgr(), TQ_SIGNAL(changed()), TQ_SLOT(foldersChanged()));
99 connect( kmkernel->dimapFolderMgr(), TQ_SIGNAL(changed()), TQ_SLOT(foldersChanged()));
100 connect( kmkernel->searchFolderMgr(), TQ_SIGNAL(changed()), TQ_SLOT(foldersChanged()));
101
102 connect( kmkernel->acctMgr(), TQ_SIGNAL( checkedMail( bool, bool, const TQMap<TQString, int> & ) ),
103 TQ_SLOT( updateNewMessages() ) );
104
105 connect( this, TQ_SIGNAL( quitSelected() ), TQ_SLOT( tray_quit() ) );
106}
107
108void KMSystemTray::buildPopupMenu()
109{
110 // Delete any previously created popup menu
111 delete mPopupMenu;
112
113 mPopupMenu = new TDEPopupMenu();
114 KMMainWidget * mainWidget = kmkernel->getKMMainWidget();
115 if ( !mainWidget )
116 return;
117
118 mPopupMenu->insertTitle(*(this->pixmap()), "KMail");
119 TDEAction * action;
120 if ( ( action = mainWidget->action("check_mail") ) )
121 action->plug( mPopupMenu );
122 if ( ( action = mainWidget->action("check_mail_in") ) )
123 action->plug( mPopupMenu );
124 if ( ( action = mainWidget->action("send_queued") ) )
125 action->plug( mPopupMenu );
126 if ( ( action = mainWidget->action("send_queued_via") ) )
127 action->plug( mPopupMenu );
128 mPopupMenu->insertSeparator();
129 if ( ( action = mainWidget->action("new_message") ) )
130 action->plug( mPopupMenu );
131 if ( ( action = mainWidget->action("kmail_configure_kmail") ) )
132 action->plug( mPopupMenu );
133 mPopupMenu->insertSeparator();
134
135 mPopupMenu->insertItem( SmallIcon("system-log-out"), i18n("&Quit"), this, TQ_SLOT(maybeQuit()) );
136}
137
138void KMSystemTray::tray_quit()
139{
140 // Quit all of KMail
141 tdeApp->quit();
142}
143
145{
146 // unregister the applet
147 kmkernel->unregisterSystemTrayApplet( this );
148
149 delete mPopupMenu;
150 mPopupMenu = 0;
151}
152
153void KMSystemTray::setMode(int newMode)
154{
155 if(newMode == mMode) return;
156
157 kdDebug(5006) << "Setting systray mMode to " << newMode << endl;
158 mMode = newMode;
159
160 switch ( mMode ) {
161 case GlobalSettings::EnumSystemTrayPolicy::ShowAlways:
162 if ( isHidden() )
163 show();
164 break;
165 case GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread:
166 if ( mCount == 0 && !isHidden() )
167 hide();
168 else if ( mCount > 0 && isHidden() )
169 show();
170 break;
171 default:
172 kdDebug(5006) << k_funcinfo << " Unknown systray mode " << mMode << endl;
173 }
174}
175
176int KMSystemTray::mode() const
177{
178 return mMode;
179}
180
181void KMSystemTray::resizeEvent(TQResizeEvent *)
182{
183 updateCount();
184}
185
192{
193 if(mCount != 0)
194 {
195 int oldPixmapWidth = pixmap()->size().width();
196 int oldPixmapHeight = pixmap()->size().height();
197
198 TQString countString = TQString::number( mCount );
199 TQFont countFont = TDEGlobalSettings::generalFont();
200 countFont.setBold(true);
201
202 // increase the size of the font for the number of unread messages if the
203 // icon size is less than 22 pixels
204 // see bug 1251
205 int realIconHeight = height();
206 if (realIconHeight < 22) {
207 countFont.setPointSizeFloat( countFont.pointSizeFloat() * 2.0 );
208 }
209
210 // decrease the size of the font for the number of unread messages if the
211 // number doesn't fit into the available space
212 float countFontSize = countFont.pointSizeFloat();
213 TQFontMetrics qfm( countFont );
214 int width = qfm.width( countString );
215 if( width > oldPixmapWidth )
216 {
217 countFontSize *= float( oldPixmapWidth ) / float( width );
218 countFont.setPointSizeFloat( countFontSize );
219 }
220
221 // Create an image which represents the number of unread messages
222 // and which has a transparent background.
223 // Unfortunately this required the following twisted code because for some
224 // reason text that is drawn on a transparent pixmap is invisible
225 // (apparently the alpha channel isn't changed when the text is drawn).
226 // Therefore I have to draw the text on a solid background and then remove
227 // the background by making it transparent with TQPixmap::setMask. This
228 // involves the slow createHeuristicMask() function (from the API docs:
229 // "This function is slow because it involves transformation to a TQImage,
230 // non-trivial computations and a transformation back to a TQBitmap."). Then
231 // I have to convert the resulting TQPixmap to a TQImage in order to overlay
232 // the light KMail icon with the number (because TDEIconEffect::overlay only
233 // works with TQImage). Finally the resulting TQImage has to be converted
234 // back to a TQPixmap.
235 // That's a lot of work for overlaying the KMail icon with the number of
236 // unread messages, but every other approach I tried failed miserably.
237 // IK, 2003-09-22
238 TQPixmap numberPixmap( oldPixmapWidth, oldPixmapHeight );
239 numberPixmap.fill( TQt::white );
240 TQPainter p( &numberPixmap );
241 p.setFont( countFont );
242 p.setPen( TQt::blue );
243 p.drawText( numberPixmap.rect(), TQt::AlignCenter, countString );
244 numberPixmap.setMask( numberPixmap.createHeuristicMask() );
245 TQImage numberImage = numberPixmap.convertToImage();
246
247 // Overlay the light KMail icon with the number image
248 TQImage iconWithNumberImage = mLightIconImage.copy();
249 TDEIconEffect::overlay( iconWithNumberImage, numberImage );
250
251 TQPixmap iconWithNumber;
252 iconWithNumber.convertFromImage( iconWithNumberImage );
253 setPixmap( iconWithNumber );
254 } else
255 {
256 setPixmap( mDefaultIcon );
257 }
258}
259
265{
270 mFoldersWithUnread.clear();
271 mCount = 0;
272
273 if ( mMode == GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread ) {
274 hide();
275 }
276
278 disconnect(this, TQ_SLOT(updateNewMessageNotification(KMFolder *)));
279
280 TQStringList folderNames;
281 TQValueList<TQGuardedPtr<KMFolder> > folderList;
282 kmkernel->folderMgr()->createFolderList(&folderNames, &folderList);
283 kmkernel->imapFolderMgr()->createFolderList(&folderNames, &folderList);
284 kmkernel->dimapFolderMgr()->createFolderList(&folderNames, &folderList);
285 kmkernel->searchFolderMgr()->createFolderList(&folderNames, &folderList);
286
287 TQStringList::iterator strIt = folderNames.begin();
288
289 for(TQValueList<TQGuardedPtr<KMFolder> >::iterator it = folderList.begin();
290 it != folderList.end() && strIt != folderNames.end(); ++it, ++strIt)
291 {
292 KMFolder * currentFolder = *it;
293 TQString currentName = *strIt;
294
295 if ( ((!currentFolder->isSystemFolder() || (currentFolder->name().lower() == "inbox")) ||
296 (currentFolder->folderType() == KMFolderTypeImap)) &&
297 !currentFolder->ignoreNewMail() )
298 {
300 connect(currentFolder, TQ_SIGNAL(numUnreadMsgsChanged(KMFolder *)),
301 this, TQ_SLOT(updateNewMessageNotification(KMFolder *)));
302
304 updateNewMessageNotification(currentFolder);
305 }
306 else {
307 disconnect(currentFolder, TQ_SIGNAL(numUnreadMsgsChanged(KMFolder *)), this, TQ_SLOT(updateNewMessageNotification(KMFolder *)) );
308 }
309 }
310}
311
316void KMSystemTray::mousePressEvent(TQMouseEvent *e)
317{
318 // switch to kmail on left mouse button
319 if( e->button() == TQt::LeftButton )
320 {
321 if( mParentVisible && mainWindowIsOnCurrentDesktop() )
322 hideKMail();
323 else
324 showKMail();
325 }
326
327 // open popup menu on right mouse button
328 if( e->button() == TQt::RightButton )
329 {
330 mPopupFolders.clear();
331 mPopupFolders.reserve( mFoldersWithUnread.count() );
332
333 // Rebuild popup menu at click time to minimize race condition if
334 // the base TDEMainWidget is closed.
335 buildPopupMenu();
336
337 if(mNewMessagePopupId != -1)
338 {
339 mPopupMenu->removeItem(mNewMessagePopupId);
340 }
341
342 if(mFoldersWithUnread.count() > 0)
343 {
344 TDEPopupMenu *newMessagesPopup = new TDEPopupMenu();
345
346 TQMap<TQGuardedPtr<KMFolder>, int>::Iterator it = mFoldersWithUnread.begin();
347 for(uint i=0; it != mFoldersWithUnread.end(); ++i)
348 {
349 kdDebug(5006) << "Adding folder" << endl;
350 mPopupFolders.append( it.key() );
351 TQString item = prettyName(it.key()) + " (" + TQString::number(it.data()) + ")";
352 newMessagesPopup->insertItem(item, this, TQ_SLOT(selectedAccount(int)), 0, i);
353 ++it;
354 }
355
356 mNewMessagePopupId = mPopupMenu->insertItem(i18n("New Messages In"),
357 newMessagesPopup, mNewMessagePopupId, 3);
358
359 kdDebug(5006) << "Folders added" << endl;
360 }
361
362 mPopupMenu->popup(e->globalPos());
363 }
364
365}
366
372{
373 TQString rvalue = fldr->label();
374 if(fldr->folderType() == KMFolderTypeImap)
375 {
376 KMFolderImap * imap = dynamic_cast<KMFolderImap*> (fldr->storage());
377 assert(imap);
378
379 if((imap->account() != 0) &&
380 (imap->account()->name() != 0) )
381 {
382 kdDebug(5006) << "IMAP folder, prepend label with type" << endl;
383 rvalue = imap->account()->name() + "->" + rvalue;
384 }
385 }
386
387 kdDebug(5006) << "Got label " << rvalue << endl;
388
389 return rvalue;
390}
391
392
393bool KMSystemTray::mainWindowIsOnCurrentDesktop()
394{
395 KMMainWidget * mainWidget = kmkernel->getKMMainWidget();
396 if ( !mainWidget )
397 return false;
398
399 TQWidget *mainWin = kmkernel->getKMMainWidget()->topLevelWidget();
400 if ( !mainWin )
401 return false;
402
403 return KWin::windowInfo( mainWin->winId(),
404 NET::WMDesktop ).isOnCurrentDesktop();
405}
406
412{
413 if (!kmkernel->getKMMainWidget())
414 return;
415 TQWidget *mainWin = kmkernel->getKMMainWidget()->topLevelWidget();
416 assert(mainWin);
417 if(mainWin)
418 {
419 KWin::WindowInfo cur = KWin::windowInfo( mainWin->winId(), NET::WMDesktop );
420 if ( cur.valid() ) mDesktopOfMainWin = cur.desktop();
421 // switch to appropriate desktop
422 if ( mDesktopOfMainWin != NET::OnAllDesktops )
423 KWin::setCurrentDesktop( mDesktopOfMainWin );
424 if ( !mParentVisible ) {
425 if ( mDesktopOfMainWin == NET::OnAllDesktops )
426 KWin::setOnAllDesktops( mainWin->winId(), true );
427 mainWin->move( mPosOfMainWin );
428 mainWin->show();
429 }
430 KWin::activateWindow( mainWin->winId() );
431 mParentVisible = true;
432 }
433 kmkernel->raise();
434
435 //Fake that the folders have changed so that the icon status is correct
437}
438
439void KMSystemTray::hideKMail()
440{
441 if (!kmkernel->getKMMainWidget())
442 return;
443 TQWidget *mainWin = kmkernel->getKMMainWidget()->topLevelWidget();
444 assert(mainWin);
445 if(mainWin)
446 {
447 mDesktopOfMainWin = KWin::windowInfo( mainWin->winId(),
448 NET::WMDesktop ).desktop();
449 mPosOfMainWin = mainWin->pos();
450 // iconifying is unnecessary, but it looks cooler
451 KWin::iconifyWindow( mainWin->winId() );
452 mainWin->hide();
453 mParentVisible = false;
454 }
455}
456
463void KMSystemTray::updateNewMessageNotification(KMFolder * fldr)
464{
465 //We don't want to count messages from search folders as they
466 // already counted as part of their original folders
467 if( !fldr ||
468 fldr->folderType() == KMFolderTypeSearch )
469 {
470 // kdDebug(5006) << "Null or a search folder, can't mess with that" << endl;
471 return;
472 }
473
474 mPendingUpdates[ fldr ] = true;
475 if ( time( 0 ) - mLastUpdate > 2 ) {
476 mUpdateTimer->stop();
477 updateNewMessages();
478 }
479 else {
480 mUpdateTimer->start(150, true);
481 }
482}
483
484void KMSystemTray::updateNewMessages()
485{
486 for ( TQMap<TQGuardedPtr<KMFolder>, bool>::Iterator it = mPendingUpdates.begin();
487 it != mPendingUpdates.end(); ++it)
488 {
489 KMFolder *fldr = it.key();
490 if ( !fldr ) // deleted folder
491 continue;
492
494 int unread = fldr->countUnread();
495
496 TQMap<TQGuardedPtr<KMFolder>, int>::Iterator unread_it =
497 mFoldersWithUnread.find(fldr);
498 bool unmapped = (unread_it == mFoldersWithUnread.end());
499
502 if(unmapped) mCount += unread;
503 /* Otherwise, get the difference between the numUnread in the folder and
504 * our last known version, and adjust mCount with that difference */
505 else
506 {
507 int diff = unread - unread_it.data();
508 mCount += diff;
509 }
510
511 if(unread > 0)
512 {
514 mFoldersWithUnread.insert(fldr, unread);
515 //kdDebug(5006) << "There are now " << mFoldersWithUnread.count() << " folders with unread" << endl;
516 }
517
523 if(unmapped)
524 {
526 if(unread == 0) continue;
527
529 if ( ( mMode == GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread )
530 && isHidden() ) {
531 show();
532 }
533
534 } else
535 {
536
537 if(unread == 0)
538 {
539 kdDebug(5006) << "Removing folder from internal store " << fldr->name() << endl;
540
542 mFoldersWithUnread.remove(fldr);
543
545 if(mFoldersWithUnread.count() == 0)
546 {
547 mPopupFolders.clear();
548 disconnect(this, TQ_SLOT(selectedAccount(int)));
549
550 mCount = 0;
551
552 if ( mMode == GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread ) {
553 hide();
554 }
555 }
556 }
557 }
558
559 }
560 mPendingUpdates.clear();
561 updateCount();
562
564 TQToolTip::remove(this);
565 TQToolTip::add(this, mCount == 0 ?
566 i18n("There are no unread messages")
567 : i18n("There is 1 unread message.",
568 "There are %n unread messages.",
569 mCount));
570
571 mLastUpdate = time( 0 );
572}
573
579void KMSystemTray::selectedAccount(int id)
580{
581 showKMail();
582
583 KMMainWidget * mainWidget = kmkernel->getKMMainWidget();
584 if (!mainWidget)
585 {
586 kmkernel->openReader();
587 mainWidget = kmkernel->getKMMainWidget();
588 }
589
590 assert(mainWidget);
591
593 KMFolder * fldr = mPopupFolders.at(id);
594 if(!fldr) return;
595 KMFolderTree * ft = mainWidget->folderTree();
596 if(!ft) return;
597 TQListViewItem * fldrIdx = ft->indexOfFolder(fldr);
598 if(!fldrIdx) return;
599
600 ft->setCurrentItem(fldrIdx);
601 ft->selectCurrentFolder();
602}
603
604bool KMSystemTray::hasUnreadMail() const
605{
606 return ( mCount != 0 );
607}
608
609#include "kmsystemtray.moc"
Mail folder.
Definition: kmfolder.h:69
int countUnread()
Number of new or unread messages in this folder.
Definition: kmfolder.cpp:450
virtual TQString label() const
Returns the label of the folder for visualization.
Definition: kmfolder.cpp:581
bool ignoreNewMail() const
Returns true if the user doesn't want to get notified about new mail in this folder.
Definition: kmfolder.h:526
bool isSystemFolder() const
Returns true if the folder is a kmail system folder.
Definition: kmfolder.h:369
KMFolderType folderType() const
Returns the type of this folder.
Definition: kmfolder.cpp:233
TQString prettyName(KMFolder *)
Return the name of the folder in which the mail is deposited, prepended with the account name if the ...
void updateCount()
Update the count of unread messages.
void foldersChanged()
Refreshes the list of folders we are monitoring.
~KMSystemTray()
destructor
KMSystemTray(TQWidget *parent=0, const char *name=0)
construtor
void mousePressEvent(TQMouseEvent *)
On left mouse click, switch focus to the first KMMainWidget.
void showKMail()
Shows and raises the first KMMainWidget and switches to the appropriate virtual desktop.
The account manager is responsible for creating accounts of various types via the factory method crea...