1 /***************************************************************************
2 * Copyright (C) 2005-2010 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This contains code from KStatusNotifierItem, part of the KDE libs *
6 * Copyright (C) 2009 Marco Martin <notmart@gmail.com> *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) version 3. *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
22 ***************************************************************************/
25 #include "systemtray.h"
27 #include "actioncollection.h"
29 #include "iconloader.h"
33 # include <KWindowInfo>
34 # include <KWindowSystem>
37 SystemTray::SystemTray(QWidget *parent)
41 _inhibitActivation(false),
42 _passiveIcon(DesktopIcon("quassel_inactive")),
43 _activeIcon(DesktopIcon("quassel")),
44 _needsAttentionIcon(DesktopIcon("quassel_message")),
46 _associatedWidget(parent)
52 associatedWidget()->installEventFilter(this);
55 qApp->installEventFilter(this);
58 SystemTray::~SystemTray() {
60 associatedWidget()->removeEventFilter(this);
63 _trayMenu->deleteLater();
66 QWidget *SystemTray::associatedWidget() const {
67 return _associatedWidget;
70 void SystemTray::setTrayMenu(QMenu *menu) {
74 _trayMenu = new QMenu();
76 ActionCollection *coll = QtUi::actionCollection("General");
78 _trayMenu->addAction(coll->action("ConnectCore"));
79 _trayMenu->addAction(coll->action("DisconnectCore"));
80 _trayMenu->addAction(coll->action("CoreInfo"));
82 _trayMenu->addSeparator();
83 _trayMenu->addAction(coll->action("Quit"));
87 void SystemTray::setMode(Mode mode_) {
91 _trayMenu->setWindowFlags(Qt::Popup);
93 _trayMenu->setWindowFlags(Qt::Window);
98 Icon SystemTray::stateIcon() const {
99 return stateIcon(state());
102 Icon SystemTray::stateIcon(State state) const {
109 return _needsAttentionIcon;
114 void SystemTray::setState(State state) {
115 if(_state != state) {
120 void SystemTray::setAlert(bool alerted) {
122 setState(NeedsAttention);
124 setState(Client::isConnected() ? Active : Passive);
127 void SystemTray::setVisible(bool visible) {
131 void SystemTray::setToolTip(const QString &title, const QString &subtitle) {
132 _toolTipTitle = title;
133 _toolTipSubTitle = subtitle;
134 emit toolTipChanged(title, subtitle);
137 void SystemTray::showMessage(const QString &title, const QString &message, MessageIcon icon, int millisecondsTimeoutHint) {
141 Q_UNUSED(millisecondsTimeoutHint)
144 void SystemTray::activate(SystemTray::ActivationReason reason) {
146 emit activated(reason);
148 if(reason == Trigger && !isActivationInhibited()) {
153 bool SystemTray::eventFilter(QObject *obj, QEvent *event) {
154 if(event->type() == QEvent::MouseMove || event->type() == QEvent::MouseButtonRelease) {
155 _inhibitActivation = false;
158 if(obj == associatedWidget() && event->type() == QEvent::ActivationChange) {
159 _dwTickCount = GetTickCount();
162 return QObject::eventFilter(obj, event);
165 // Code taken from KStatusNotifierItem for handling minimize/restore
167 bool SystemTray::checkVisibility(bool perform) {
169 // the problem is that we lose focus when the systray icon is activated
170 // and we don't know the former active window
171 // therefore we watch for activation event and use our stopwatch :)
172 if(GetTickCount() - _dwTickCount < 300) {
173 // we were active in the last 300ms -> hide it
174 minimizeRestore(false);
176 minimizeRestore(true);
179 #elif defined(HAVE_KDE) && defined(Q_WS_X11)
180 KWindowInfo info1 = KWindowSystem::windowInfo(associatedWidget()->winId(), NET::XAWMState | NET::WMState | NET::WMDesktop);
181 // mapped = visible (but possibly obscured)
182 bool mapped = (info1.mappingState() == NET::Visible) && !info1.isMinimized();
184 // - not mapped -> show, raise, focus
186 // - obscured -> raise, focus
187 // - not obscured -> hide
188 //info1.mappingState() != NET::Visible -> window on another desktop?
191 minimizeRestore(true);
195 QListIterator< WId > it (KWindowSystem::stackingOrder());
197 while(it.hasPrevious()) {
198 WId id = it.previous();
199 if(id == associatedWidget()->winId())
202 KWindowInfo info2 = KWindowSystem::windowInfo(id, NET::WMDesktop | NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType);
204 if(info2.mappingState() != NET::Visible)
205 continue; // not visible on current desktop -> ignore
207 if(!info2.geometry().intersects(associatedWidget()->geometry()))
208 continue; // not obscuring the window -> ignore
210 if(!info1.hasState(NET::KeepAbove) && info2.hasState(NET::KeepAbove))
211 continue; // obscured by window kept above -> ignore
213 NET::WindowType type = info2.windowType(NET::NormalMask | NET::DesktopMask
214 | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
215 | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask);
217 if(type == NET::Dock || type == NET::TopMenu)
218 continue; // obscured by dock or topmenu -> ignore
221 KWindowSystem::raiseWindow(associatedWidget()->winId());
222 KWindowSystem::activateWindow(associatedWidget()->winId());
227 //not on current desktop?
228 if(!info1.isOnCurrentDesktop()) {
230 KWindowSystem::activateWindow(associatedWidget()->winId());
235 minimizeRestore(false); // hide
240 if(!associatedWidget()->isVisible() || associatedWidget()->isMinimized()) {
242 minimizeRestore(true);
246 minimizeRestore(false);
255 void SystemTray::minimizeRestore(bool show) {
257 GraphicalUi::activateMainWidget();
259 if(isSystemTrayAvailable()) {
262 GraphicalUi::hideMainWidget();
267 void SystemTray::hideMainWidget() {
268 minimizeRestore(false);
271 void SystemTray::toggleMainWidget() {
272 checkVisibility(true);