1 /* This file is part of the KDE libraries
3 Copyright (C) 1999 Matthias Ettrich (ettrich@kde.org)
4 Copyright (c) 2007 by Charles Connell <charles@connells.org>
5 Copyright (C) 2008 Lukas Appelhans <l.appelhans@gmx.de>
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
23 #include "ksystemtrayicon.h"
24 #include "kaboutdata.h"
26 #include "kcomponentdata.h"
29 #include "kmessagebox.h"
30 #include "kshortcut.h"
31 #include "kactioncollection.h"
32 #include "kstandardaction.h"
33 #include <kwindowsystem.h>
42 #include <kiconloader.h>
43 #include <kapplication.h>
44 #include <kconfiggroup.h>
46 #include <QMouseEvent>
47 #include <QToolButton>
52 class KSystemTrayIconPrivate
: public QObject
54 class KSystemTrayIconPrivate
58 KSystemTrayIconPrivate(KSystemTrayIcon
* trayIcon
, QWidget
* parent
)
61 actionCollection
= new KActionCollection( trayIcon
);
63 onAllDesktops
= false;
68 window
->installEventFilter( this );
73 ~KSystemTrayIconPrivate()
77 window
->removeEventFilter( this );
80 delete actionCollection
;
85 void _k_slotNewFrame()
87 q
->setIcon(QIcon(movie
->currentPixmap()));
91 bool eventFilter(QObject
*obj
, QEvent
*ev
)
93 if(ev
->type() == QEvent::ActivationChange
) {
94 dwTickCount
= GetTickCount();
96 return QObject::eventFilter(obj
, ev
);
102 KActionCollection
* actionCollection
;
105 QAction
* titleAction
;
106 bool onAllDesktops
: 1; // valid only when the parent widget was hidden
108 QPointer
<QMovie
> movie
;
111 KSystemTrayIcon::KSystemTrayIcon( QWidget
* parent
)
112 : QSystemTrayIcon( parent
),
113 d( new KSystemTrayIconPrivate( this, parent
) )
118 KSystemTrayIcon::KSystemTrayIcon( const QString
& icon
, QWidget
* parent
)
119 : QSystemTrayIcon( loadIcon( icon
), parent
),
120 d( new KSystemTrayIconPrivate( this, parent
) )
125 KSystemTrayIcon::KSystemTrayIcon( const QIcon
& icon
, QWidget
* parent
)
126 : QSystemTrayIcon( icon
, parent
),
127 d( new KSystemTrayIconPrivate( this, parent
) )
132 KSystemTrayIcon::KSystemTrayIcon(QMovie
* movie
, QWidget
*parent
)
133 : QSystemTrayIcon(parent
),
134 d( new KSystemTrayIconPrivate( this, parent
) )
140 void KSystemTrayIcon::init( QWidget
* parent
)
142 // Ensure that closing the last KMainWindow doesn't exit the application
143 // if a system tray icon is still present.
145 d
->menu
= new KMenu( parent
);
146 d
->titleAction
= d
->menu
->addTitle( qApp
->windowIcon(), KGlobal::caption() );
147 d
->menu
->setTitle( KGlobal::mainComponent().aboutData()->programName() );
148 connect( d
->menu
, SIGNAL( aboutToShow() ), this, SLOT( contextMenuAboutToShow() ) );
149 setContextMenu( d
->menu
);
151 KStandardAction::quit( this, SLOT( maybeQuit() ), d
->actionCollection
);
155 QAction
*action
= d
->actionCollection
->addAction("minimizeRestore");
156 action
->setText(i18n("Minimize"));
157 connect( action
, SIGNAL( triggered( bool ) ), this, SLOT( minimizeRestoreAction() ) );
160 KWindowInfo info
= KWindowSystem::windowInfo( parent
->winId(), NET::WMDesktop
);
161 d
->onAllDesktops
= info
.onAllDesktops();
163 d
->onAllDesktops
= false;
168 d
->onAllDesktops
= false;
171 connect( this, SIGNAL( activated( QSystemTrayIcon::ActivationReason
) ),
172 SLOT( activateOrHide( QSystemTrayIcon::ActivationReason
) ) );
175 QWidget
*KSystemTrayIcon::parentWidget() const
180 KSystemTrayIcon::~KSystemTrayIcon()
186 void KSystemTrayIcon::contextMenuAboutToShow( )
190 // we need to add the actions to the menu afterwards so that these items
191 // appear at the _END_ of the menu
192 d
->menu
->addSeparator();
193 QAction
* action
= d
->actionCollection
->action( "minimizeRestore" );
197 d
->menu
->addAction( action
);
200 action
= d
->actionCollection
->action( KStandardAction::name( KStandardAction::Quit
) );
204 d
->menu
->addAction( action
);
212 QAction
* action
= d
->actionCollection
->action("minimizeRestore");
213 if ( d
->window
->isVisible() )
215 action
->setText( i18n("&Minimize") );
219 action
->setText( i18n("&Restore") );
224 // called from the popup menu - always do what the menu entry says,
225 // i.e. if the window is shown, no matter if active or not, the menu
226 // entry is "minimize", otherwise it's "restore"
227 void KSystemTrayIcon::minimizeRestoreAction()
231 bool restore
= !( d
->window
->isVisible() );
232 minimizeRestore( restore
);
236 void KSystemTrayIcon::maybeQuit()
238 QString caption
= KGlobal::caption();
239 QString query
= i18n("<qt>Are you sure you want to quit <b>%1</b>?</qt>",
241 if (KMessageBox::warningContinueCancel(d
->window
, query
,
242 i18n("Confirm Quit From System Tray"),
243 KStandardGuiItem::quit(),
244 KStandardGuiItem::cancel(),
245 QString("systemtrayquit%1")
247 KMessageBox::Continue
)
256 // if the window is not the active one, show it if needed, and activate it
257 // (just like taskbar); otherwise hide it
258 void KSystemTrayIcon::activateOrHide( QSystemTrayIcon::ActivationReason reasonCalled
)
260 if ( reasonCalled
!= QSystemTrayIcon::Trigger
)
265 QWidget
*pw
= d
->window
;
271 // the problem is that we lose focus when the systray icon is activated
272 // and we don't know the former active window
273 // therefore we watch for activation event and use our stopwatch :)
274 if( GetTickCount() - d
->dwTickCount
< 300 ) {
275 // we were active in the last 300ms -> hide it
276 minimizeRestore( false );
278 minimizeRestore( true );
280 #elif defined(Q_WS_X11)
281 KWindowInfo info1
= KWindowSystem::windowInfo( pw
->winId(), NET::XAWMState
| NET::WMState
);
282 // mapped = visible (but possibly obscured)
283 bool mapped
= (info1
.mappingState() == NET::Visible
) && !info1
.isMinimized();
284 // - not mapped -> show, raise, focus
286 // - obscured -> raise, focus
287 // - not obscured -> hide
289 minimizeRestore( true );
292 QListIterator
< WId
> it (KWindowSystem::stackingOrder());
294 while( it
.hasPrevious() )
296 WId id
= it
.previous();
297 if( id
== pw
->winId() )
299 KWindowInfo info2
= KWindowSystem::windowInfo( id
,
300 NET::WMGeometry
| NET::XAWMState
| NET::WMState
| NET::WMWindowType
);
301 if( info2
.mappingState() != NET::Visible
)
302 continue; // not visible on current desktop -> ignore
303 if( !info2
.geometry().intersects( pw
->geometry()))
304 continue; // not obscuring the window -> ignore
305 if( !info1
.hasState( NET::KeepAbove
) && info2
.hasState( NET::KeepAbove
))
306 continue; // obscured by window kept above -> ignore
307 NET::WindowType type
= info2
.windowType( NET::NormalMask
| NET::DesktopMask
308 | NET::DockMask
| NET::ToolbarMask
| NET::MenuMask
| NET::DialogMask
309 | NET::OverrideMask
| NET::TopMenuMask
| NET::UtilityMask
| NET::SplashMask
);
310 if( type
== NET::Dock
|| type
== NET::TopMenu
)
311 continue; // obscured by dock or topmenu -> ignore
313 KWindowSystem::activateWindow( pw
->winId());
316 minimizeRestore( false ); // hide
321 void KSystemTrayIcon::minimizeRestore( bool restore
)
323 QWidget
* pw
= d
->window
;
327 KWindowInfo info
= KWindowSystem::windowInfo(pw
->winId(), NET::WMGeometry
| NET::WMDesktop
);
329 if (d
->onAllDesktops
) {
330 KWindowSystem::setOnAllDesktops(pw
->winId(), true);
332 KWindowSystem::setCurrentDesktop(info
.desktop());
334 pw
->move(info
.geometry().topLeft()); // avoid placement policies
337 KWindowSystem::activateWindow(pw
->winId());
339 d
->onAllDesktops
= info
.onAllDesktops();
347 KWindowSystem::forceActiveWindow( pw
->winId() );
354 KActionCollection
* KSystemTrayIcon::actionCollection()
356 return d
->actionCollection
;
359 QIcon
KSystemTrayIcon::loadIcon(const QString
&icon
, const KComponentData
&componentData
)
361 KConfigGroup
cg(componentData
.config(), "System Tray");
362 const int iconWidth
= cg
.readEntry("systrayIconWidth", 22);
363 return KIconLoader::global()->loadIcon( icon
, KIconLoader::Panel
, iconWidth
);
366 void KSystemTrayIcon::toggleActive()
368 activateOrHide( QSystemTrayIcon::Trigger
);
371 bool KSystemTrayIcon::parentWidgetTrayClose() const
373 if( kapp
!= NULL
&& kapp
->sessionSaving())
374 return false; // normal close
378 void KSystemTrayIcon::setContextMenuTitle(QAction
*action
)
380 // can never be null, and is always the requsted type, so no need to do null checks after casts.
381 QToolButton
*button
= static_cast<QToolButton
*>((static_cast<QWidgetAction
*>(d
->titleAction
))->defaultWidget());
382 button
->setDefaultAction(action
);
385 QAction
*KSystemTrayIcon::contextMenuTitle() const
387 QToolButton
*button
= static_cast<QToolButton
*>((static_cast<QWidgetAction
*>(d
->titleAction
))->defaultWidget());
388 return button
->defaultAction();
391 void KSystemTrayIcon::setMovie(QMovie
* m
)
396 connect(d
->movie
, SIGNAL(frameChanged(int)), this, SLOT(_k_slotNewFrame()));
397 d
->movie
->setCacheMode(QMovie::CacheAll
);
400 const QMovie
* KSystemTrayIcon::movie() const
405 #include "ksystemtrayicon.moc"