Store the type of the current tab completion (user or channel) in the TabCompleter...
[quassel.git] / src / qtui / systemtray.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2010 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This contains code from KStatusNotifierItem, part of the KDE libs     *
6  *   Copyright (C) 2009 Marco Martin <notmart@gmail.com>                   *
7  *                                                                         *
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.                                           *
12  *                                                                         *
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.                          *
17  *                                                                         *
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  ***************************************************************************/
23 #include <QMenu>
24
25 #include "systemtray.h"
26
27 #include "actioncollection.h"
28 #include "client.h"
29 #include "iconloader.h"
30 #include "qtui.h"
31
32 #ifdef HAVE_KDE
33 #  include <KWindowInfo>
34 #  include <KWindowSystem>
35 #endif
36
37 SystemTray::SystemTray(QWidget *parent)
38 : QObject(parent),
39   _mode(Invalid),
40   _state(Passive),
41   _inhibitActivation(false),
42   _passiveIcon(DesktopIcon("quassel_inactive")),
43   _activeIcon(DesktopIcon("quassel")),
44   _needsAttentionIcon(DesktopIcon("quassel_message")),
45   _trayMenu(0),
46   _associatedWidget(parent)
47 {
48   Q_ASSERT(parent);
49
50 #ifdef Q_WS_WIN
51   _dwTickCount = 0;
52   associatedWidget()->installEventFilter(this);
53 #endif
54
55   qApp->installEventFilter(this);
56 }
57
58 SystemTray::~SystemTray() {
59 #ifdef Q_WS_WIN
60   associatedWidget()->removeEventFilter(this);
61 #endif
62
63   _trayMenu->deleteLater();
64 }
65
66 QWidget *SystemTray::associatedWidget() const {
67   return _associatedWidget;
68 }
69
70 void SystemTray::setTrayMenu(QMenu *menu) {
71   if(menu)
72     _trayMenu = menu;
73   else
74     _trayMenu = new QMenu();
75
76   ActionCollection *coll = QtUi::actionCollection("General");
77
78   _trayMenu->addAction(coll->action("ConnectCore"));
79   _trayMenu->addAction(coll->action("DisconnectCore"));
80   _trayMenu->addAction(coll->action("CoreInfo"));
81 #ifndef HAVE_KDE
82   _trayMenu->addSeparator();
83   _trayMenu->addAction(coll->action("Quit"));
84 #endif /* HAVE_KDE */
85 }
86
87 void SystemTray::setMode(Mode mode_) {
88   if(mode_ != _mode) {
89     _mode = mode_;
90     if(_mode == Legacy) {
91       _trayMenu->setWindowFlags(Qt::Popup);
92     } else {
93       _trayMenu->setWindowFlags(Qt::Window);
94     }
95   }
96 }
97
98 Icon SystemTray::stateIcon() const {
99   return stateIcon(state());
100 }
101
102 Icon SystemTray::stateIcon(State state) const {
103   switch(state) {
104   case Passive:
105     return _passiveIcon;
106   case Active:
107     return _activeIcon;
108   case NeedsAttention:
109     return _needsAttentionIcon;
110   }
111   return Icon();
112 }
113
114 void SystemTray::setState(State state) {
115   if(_state != state) {
116     _state = state;
117   }
118 }
119
120 void SystemTray::setAlert(bool alerted) {
121   if(alerted)
122     setState(NeedsAttention);
123   else
124     setState(Client::isConnected() ? Active : Passive);
125 }
126
127 void SystemTray::setVisible(bool visible) {
128   Q_UNUSED(visible)
129 }
130
131 void SystemTray::setToolTip(const QString &title, const QString &subtitle) {
132   _toolTipTitle = title;
133   _toolTipSubTitle = subtitle;
134   emit toolTipChanged(title, subtitle);
135 }
136
137 void SystemTray::showMessage(const QString &title, const QString &message, MessageIcon icon, int millisecondsTimeoutHint) {
138   Q_UNUSED(title)
139   Q_UNUSED(message)
140   Q_UNUSED(icon)
141   Q_UNUSED(millisecondsTimeoutHint)
142 }
143
144 void SystemTray::activate(SystemTray::ActivationReason reason) {
145
146   emit activated(reason);
147
148   if(reason == Trigger && !isActivationInhibited()) {
149     toggleMainWidget();
150   }
151 }
152
153 bool SystemTray::eventFilter(QObject *obj, QEvent *event) {
154   if(event->type() == QEvent::MouseMove || event->type() == QEvent::MouseButtonRelease) {
155     _inhibitActivation = false;
156   }
157 #ifdef Q_WS_WIN
158   if(obj == associatedWidget() && event->type() == QEvent::ActivationChange) {
159     _dwTickCount = GetTickCount();
160   }
161 #endif
162   return QObject::eventFilter(obj, event);
163 }
164
165 // Code taken from KStatusNotifierItem for handling minimize/restore
166
167 bool SystemTray::checkVisibility(bool perform) {
168 #ifdef Q_WS_WIN
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);
175   } else {
176     minimizeRestore(true);
177   }
178
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();
183
184   //    - not mapped -> show, raise, focus
185   //    - mapped
186   //        - obscured -> raise, focus
187   //        - not obscured -> hide
188   //info1.mappingState() != NET::Visible -> window on another desktop?
189   if(!mapped) {
190     if(perform)
191       minimizeRestore(true);
192     return true;
193
194   } else {
195     QListIterator< WId > it (KWindowSystem::stackingOrder());
196     it.toBack();
197     while(it.hasPrevious()) {
198       WId id = it.previous();
199       if(id == associatedWidget()->winId())
200         break;
201
202       KWindowInfo info2 = KWindowSystem::windowInfo(id, NET::WMDesktop | NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType);
203
204       if(info2.mappingState() != NET::Visible)
205         continue; // not visible on current desktop -> ignore
206
207       if(!info2.geometry().intersects(associatedWidget()->geometry()))
208         continue; // not obscuring the window -> ignore
209
210       if(!info1.hasState(NET::KeepAbove) && info2.hasState(NET::KeepAbove))
211         continue; // obscured by window kept above -> ignore
212
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);
216
217       if(type == NET::Dock || type == NET::TopMenu)
218         continue; // obscured by dock or topmenu -> ignore
219
220       if(perform) {
221         KWindowSystem::raiseWindow(associatedWidget()->winId());
222         KWindowSystem::activateWindow(associatedWidget()->winId());
223       }
224       return true;
225     }
226
227     //not on current desktop?
228     if(!info1.isOnCurrentDesktop()) {
229       if(perform)
230         KWindowSystem::activateWindow(associatedWidget()->winId());
231       return true;
232     }
233
234     if(perform)
235       minimizeRestore(false); // hide
236     return false;
237   }
238 #else
239
240   if(!associatedWidget()->isVisible() || associatedWidget()->isMinimized()) {
241     if(perform)
242       minimizeRestore(true);
243     return true;
244   } else {
245     if(perform)
246       minimizeRestore(false);
247     return false;
248   }
249
250 #endif
251
252   return true;
253 }
254
255 void SystemTray::minimizeRestore(bool show) {
256   if(show)
257     GraphicalUi::activateMainWidget();
258   else {
259     if(isSystemTrayAvailable()) {
260       if(!isVisible())
261         setVisible();
262       GraphicalUi::hideMainWidget();
263     }
264   }
265 }
266
267 void SystemTray::hideMainWidget() {
268   minimizeRestore(false);
269 }
270
271 void SystemTray::toggleMainWidget() {
272   checkVisibility(true);
273 }