KDEUI
ksystemtrayicon.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 00003 Copyright (C) 1999 Matthias Ettrich (ettrich@kde.org) 00004 Copyright (c) 2007 by Charles Connell <charles@connells.org> 00005 Copyright (C) 2008 Lukas Appelhans <l.appelhans@gmx.de> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Library General Public License for more details. 00016 00017 You should have received a copy of the GNU Library General Public License 00018 along with this library; see the file COPYING.LIB. If not, write to 00019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00020 Boston, MA 02110-1301, USA. 00021 */ 00022 00023 #include "ksystemtrayicon.h" 00024 #include "kaboutdata.h" 00025 #include "kaction.h" 00026 #include "kcomponentdata.h" 00027 #include "klocale.h" 00028 #include "kmenu.h" 00029 #include "kmessagebox.h" 00030 #include "kshortcut.h" 00031 #include "kactioncollection.h" 00032 #include "kstandardaction.h" 00033 #include <kwindowsystem.h> 00034 00035 #ifdef Q_WS_X11 00036 #include <QX11Info> 00037 #endif 00038 #ifdef Q_WS_WIN 00039 #include <windows.h> 00040 #endif 00041 00042 #include <kiconloader.h> 00043 #include <kapplication.h> 00044 #include <kconfiggroup.h> 00045 00046 #include <QMouseEvent> 00047 #include <QToolButton> 00048 #include <QMovie> 00049 #include <QPointer> 00050 00051 #ifdef Q_WS_WIN 00052 class KSystemTrayIconPrivate : public QObject 00053 #else 00054 class KSystemTrayIconPrivate 00055 #endif 00056 { 00057 public: 00058 KSystemTrayIconPrivate(KSystemTrayIcon* trayIcon, QWidget* parent) 00059 : q(trayIcon) 00060 { 00061 actionCollection = new KActionCollection( trayIcon ); 00062 hasQuit = false; 00063 onAllDesktops = false; 00064 window = parent; 00065 movie = 0; 00066 #ifdef Q_WS_WIN 00067 if ( window ) { 00068 window->installEventFilter( this ); 00069 } 00070 #endif 00071 } 00072 00073 ~KSystemTrayIconPrivate() 00074 { 00075 #ifdef Q_WS_WIN 00076 if ( window ) { 00077 window->removeEventFilter( this ); 00078 } 00079 #endif 00080 delete actionCollection; 00081 delete menu; 00082 } 00083 00084 00085 void _k_slotNewFrame() 00086 { 00087 q->setIcon(QIcon(movie->currentPixmap())); 00088 } 00089 00090 #ifdef Q_WS_WIN 00091 bool eventFilter(QObject *obj, QEvent *ev) 00092 { 00093 if(ev->type() == QEvent::ActivationChange) { 00094 dwTickCount = GetTickCount(); 00095 } 00096 return QObject::eventFilter(obj, ev); 00097 } 00098 DWORD dwTickCount; 00099 #endif 00100 00101 KSystemTrayIcon* q; 00102 KActionCollection* actionCollection; 00103 KMenu* menu; 00104 QWidget* window; 00105 QAction* titleAction; 00106 bool onAllDesktops : 1; // valid only when the parent widget was hidden 00107 bool hasQuit : 1; 00108 QPointer<QMovie> movie; 00109 }; 00110 00111 KSystemTrayIcon::KSystemTrayIcon( QWidget* parent ) 00112 : QSystemTrayIcon( parent ), 00113 d( new KSystemTrayIconPrivate( this, parent ) ) 00114 { 00115 init( parent ); 00116 } 00117 00118 KSystemTrayIcon::KSystemTrayIcon( const QString& icon, QWidget* parent ) 00119 : QSystemTrayIcon( loadIcon( icon ), parent ), 00120 d( new KSystemTrayIconPrivate( this, parent ) ) 00121 { 00122 init( parent ); 00123 } 00124 00125 KSystemTrayIcon::KSystemTrayIcon( const QIcon& icon, QWidget* parent ) 00126 : QSystemTrayIcon( icon, parent ), 00127 d( new KSystemTrayIconPrivate( this, parent ) ) 00128 { 00129 init( parent ); 00130 } 00131 00132 KSystemTrayIcon::KSystemTrayIcon(QMovie* movie, QWidget *parent) 00133 : QSystemTrayIcon(parent), 00134 d( new KSystemTrayIconPrivate( this, parent ) ) 00135 { 00136 init(parent); 00137 setMovie(movie); 00138 } 00139 00140 void KSystemTrayIcon::init( QWidget* parent ) 00141 { 00142 // Ensure that closing the last KMainWindow doesn't exit the application 00143 // if a system tray icon is still present. 00144 KGlobal::ref(); 00145 d->menu = new KMenu( parent ); 00146 d->titleAction = d->menu->addTitle( qApp->windowIcon(), KGlobal::caption() ); 00147 d->menu->setTitle( KGlobal::mainComponent().aboutData()->programName() ); 00148 connect( d->menu, SIGNAL( aboutToShow() ), this, SLOT( contextMenuAboutToShow() ) ); 00149 setContextMenu( d->menu ); 00150 00151 KStandardAction::quit( this, SLOT( maybeQuit() ), d->actionCollection ); 00152 00153 if ( parent ) 00154 { 00155 QAction *action = d->actionCollection->addAction("minimizeRestore"); 00156 action->setText(i18n("Minimize")); 00157 connect( action, SIGNAL( triggered( bool ) ), this, SLOT( minimizeRestoreAction() ) ); 00158 00159 #ifdef Q_WS_X11 00160 KWindowInfo info = KWindowSystem::windowInfo( parent->winId(), NET::WMDesktop ); 00161 d->onAllDesktops = info.onAllDesktops(); 00162 #else 00163 d->onAllDesktops = false; 00164 #endif 00165 } 00166 else 00167 { 00168 d->onAllDesktops = false; 00169 } 00170 00171 connect( this, SIGNAL( activated( QSystemTrayIcon::ActivationReason ) ), 00172 SLOT( activateOrHide( QSystemTrayIcon::ActivationReason ) ) ); 00173 } 00174 00175 QWidget *KSystemTrayIcon::parentWidget() const 00176 { 00177 return d->window; 00178 } 00179 00180 KSystemTrayIcon::~KSystemTrayIcon() 00181 { 00182 delete d; 00183 KGlobal::deref(); 00184 } 00185 00186 void KSystemTrayIcon::contextMenuAboutToShow( ) 00187 { 00188 if ( !d->hasQuit ) 00189 { 00190 // we need to add the actions to the menu afterwards so that these items 00191 // appear at the _END_ of the menu 00192 d->menu->addSeparator(); 00193 QAction* action = d->actionCollection->action( "minimizeRestore" ); 00194 00195 if ( action ) 00196 { 00197 d->menu->addAction( action ); 00198 } 00199 00200 action = d->actionCollection->action( KStandardAction::name( KStandardAction::Quit ) ); 00201 00202 if ( action ) 00203 { 00204 d->menu->addAction( action ); 00205 } 00206 00207 d->hasQuit = true; 00208 } 00209 00210 if ( d->window ) 00211 { 00212 QAction* action = d->actionCollection->action("minimizeRestore"); 00213 if ( d->window->isVisible() ) 00214 { 00215 action->setText( i18n("&Minimize") ); 00216 } 00217 else 00218 { 00219 action->setText( i18n("&Restore") ); 00220 } 00221 } 00222 } 00223 00224 // called from the popup menu - always do what the menu entry says, 00225 // i.e. if the window is shown, no matter if active or not, the menu 00226 // entry is "minimize", otherwise it's "restore" 00227 void KSystemTrayIcon::minimizeRestoreAction() 00228 { 00229 if ( d->window ) 00230 { 00231 bool restore = !( d->window->isVisible() ); 00232 minimizeRestore( restore ); 00233 } 00234 } 00235 00236 void KSystemTrayIcon::maybeQuit() 00237 { 00238 QString caption = KGlobal::caption(); 00239 QString query = i18n("<qt>Are you sure you want to quit <b>%1</b>?</qt>", 00240 caption); 00241 if (KMessageBox::warningContinueCancel(d->window, query, 00242 i18n("Confirm Quit From System Tray"), 00243 KStandardGuiItem::quit(), 00244 KStandardGuiItem::cancel(), 00245 QString("systemtrayquit%1") 00246 .arg(caption)) != 00247 KMessageBox::Continue) 00248 { 00249 return; 00250 } 00251 00252 emit quitSelected(); 00253 qApp->quit(); 00254 } 00255 00256 // if the window is not the active one, show it if needed, and activate it 00257 // (just like taskbar); otherwise hide it 00258 void KSystemTrayIcon::activateOrHide( QSystemTrayIcon::ActivationReason reasonCalled ) 00259 { 00260 if ( reasonCalled != QSystemTrayIcon::Trigger ) 00261 { 00262 return; 00263 } 00264 00265 QWidget *pw = d->window; 00266 if ( !pw ) 00267 { 00268 return; 00269 } 00270 #ifdef Q_WS_WIN 00271 // the problem is that we lose focus when the systray icon is activated 00272 // and we don't know the former active window 00273 // therefore we watch for activation event and use our stopwatch :) 00274 if( GetTickCount() - d->dwTickCount < 300 ) { 00275 // we were active in the last 300ms -> hide it 00276 minimizeRestore( false ); 00277 } else { 00278 minimizeRestore( true ); 00279 } 00280 #elif defined(Q_WS_X11) 00281 KWindowInfo info1 = KWindowSystem::windowInfo( pw->winId(), NET::XAWMState | NET::WMState ); 00282 // mapped = visible (but possibly obscured) 00283 bool mapped = (info1.mappingState() == NET::Visible) && !info1.isMinimized(); 00284 // - not mapped -> show, raise, focus 00285 // - mapped 00286 // - obscured -> raise, focus 00287 // - not obscured -> hide 00288 if( !mapped ) 00289 minimizeRestore( true ); 00290 else 00291 { 00292 QListIterator< WId > it (KWindowSystem::stackingOrder()); 00293 it.toBack(); 00294 while( it.hasPrevious() ) 00295 { 00296 WId id = it.previous(); 00297 if( id == pw->winId() ) 00298 break; 00299 KWindowInfo info2 = KWindowSystem::windowInfo( id, 00300 NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType ); 00301 if( info2.mappingState() != NET::Visible ) 00302 continue; // not visible on current desktop -> ignore 00303 if( !info2.geometry().intersects( pw->geometry())) 00304 continue; // not obscuring the window -> ignore 00305 if( !info1.hasState( NET::KeepAbove ) && info2.hasState( NET::KeepAbove )) 00306 continue; // obscured by window kept above -> ignore 00307 NET::WindowType type = info2.windowType( NET::NormalMask | NET::DesktopMask 00308 | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask 00309 | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask ); 00310 if( type == NET::Dock || type == NET::TopMenu ) 00311 continue; // obscured by dock or topmenu -> ignore 00312 pw->raise(); 00313 KWindowSystem::activateWindow( pw->winId()); 00314 return; 00315 } 00316 minimizeRestore( false ); // hide 00317 } 00318 #endif 00319 } 00320 00321 void KSystemTrayIcon::minimizeRestore( bool restore ) 00322 { 00323 QWidget* pw = d->window; 00324 if (!pw) 00325 return; 00326 #ifdef Q_WS_X11 00327 KWindowInfo info = KWindowSystem::windowInfo(pw->winId(), NET::WMGeometry | NET::WMDesktop); 00328 if (restore) { 00329 if (d->onAllDesktops) { 00330 KWindowSystem::setOnAllDesktops(pw->winId(), true); 00331 } else { 00332 KWindowSystem::setCurrentDesktop(info.desktop()); 00333 } 00334 pw->move(info.geometry().topLeft()); // avoid placement policies 00335 pw->show(); 00336 pw->raise(); 00337 KWindowSystem::activateWindow(pw->winId()); 00338 } else { 00339 d->onAllDesktops = info.onAllDesktops(); 00340 pw->hide(); 00341 } 00342 #else 00343 if ( restore ) 00344 { 00345 pw->show(); 00346 pw->raise(); 00347 KWindowSystem::forceActiveWindow( pw->winId() ); 00348 } else { 00349 pw->hide(); 00350 } 00351 #endif 00352 } 00353 00354 KActionCollection* KSystemTrayIcon::actionCollection() 00355 { 00356 return d->actionCollection; 00357 } 00358 00359 QIcon KSystemTrayIcon::loadIcon(const QString &icon, const KComponentData &componentData) 00360 { 00361 KConfigGroup cg(componentData.config(), "System Tray"); 00362 const int iconWidth = cg.readEntry("systrayIconWidth", 22); 00363 return KIconLoader::global()->loadIcon( icon, KIconLoader::Panel, iconWidth ); 00364 } 00365 00366 void KSystemTrayIcon::toggleActive() 00367 { 00368 activateOrHide( QSystemTrayIcon::Trigger ); 00369 } 00370 00371 bool KSystemTrayIcon::parentWidgetTrayClose() const 00372 { 00373 if( kapp != NULL && kapp->sessionSaving()) 00374 return false; // normal close 00375 return true; 00376 } 00377 00378 void KSystemTrayIcon::setContextMenuTitle(QAction *action) 00379 { 00380 // can never be null, and is always the requsted type, so no need to do null checks after casts. 00381 QToolButton *button = static_cast<QToolButton*>((static_cast<QWidgetAction*>(d->titleAction))->defaultWidget()); 00382 button->setDefaultAction(action); 00383 } 00384 00385 QAction *KSystemTrayIcon::contextMenuTitle() const 00386 { 00387 QToolButton *button = static_cast<QToolButton*>((static_cast<QWidgetAction*>(d->titleAction))->defaultWidget()); 00388 return button->defaultAction(); 00389 } 00390 00391 void KSystemTrayIcon::setMovie(QMovie* m) 00392 { 00393 if (d->movie == m) { 00394 return; 00395 } 00396 delete d->movie; 00397 m->setParent(this); 00398 d->movie = m; 00399 connect(d->movie, SIGNAL(frameChanged(int)), this, SLOT(_k_slotNewFrame())); 00400 d->movie->setCacheMode(QMovie::CacheAll); 00401 } 00402 00403 const QMovie* KSystemTrayIcon::movie() const 00404 { 00405 return d->movie; 00406 } 00407 00408 #include "ksystemtrayicon.moc"
KDE 4.6 API Reference