d1e106d10722c335c9363b749d9b7caf2440e7c3
[quassel.git] / src / uisupport / graphicalui.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2015 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  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
22  ***************************************************************************/
23
24 #include "graphicalui.h"
25
26 #include "actioncollection.h"
27 #include "uisettings.h"
28 #include "contextmenuactionprovider.h"
29 #include "toolbaractionprovider.h"
30
31 #ifdef Q_WS_X11
32 #  include <QX11Info>
33 #endif
34 #ifdef HAVE_KDE4
35 #  include <KWindowInfo>
36 #  include <KWindowSystem>
37 #endif
38
39 GraphicalUi *GraphicalUi::_instance = 0;
40 QWidget *GraphicalUi::_mainWidget = 0;
41 QHash<QString, ActionCollection *> GraphicalUi::_actionCollections;
42 ContextMenuActionProvider *GraphicalUi::_contextMenuActionProvider = 0;
43 ToolBarActionProvider *GraphicalUi::_toolBarActionProvider = 0;
44 UiStyle *GraphicalUi::_uiStyle = 0;
45 bool GraphicalUi::_onAllDesktops = false;
46
47 GraphicalUi::GraphicalUi(QObject *parent) : AbstractUi(parent)
48 {
49     Q_ASSERT(!_instance);
50     _instance = this;
51
52 #ifdef Q_OS_WIN
53     _dwTickCount = 0;
54 #endif
55 }
56
57
58 void GraphicalUi::init()
59 {
60 #ifdef Q_OS_WIN
61     mainWidget()->installEventFilter(this);
62 #endif
63 }
64
65
66 ActionCollection *GraphicalUi::actionCollection(const QString &category, const QString &translatedCategory)
67 {
68     if (_actionCollections.contains(category))
69         return _actionCollections.value(category);
70     ActionCollection *coll = new ActionCollection(_mainWidget);
71
72     if (!translatedCategory.isEmpty())
73         coll->setProperty("Category", translatedCategory);
74     else
75         coll->setProperty("Category", category);
76
77     if (_mainWidget)
78         coll->addAssociatedWidget(_mainWidget);
79     _actionCollections.insert(category, coll);
80     return coll;
81 }
82
83
84 QHash<QString, ActionCollection *> GraphicalUi::actionCollections()
85 {
86     return _actionCollections;
87 }
88
89
90 void GraphicalUi::loadShortcuts()
91 {
92     foreach(ActionCollection *coll, actionCollections())
93     coll->readSettings();
94 }
95
96
97 void GraphicalUi::saveShortcuts()
98 {
99     ShortcutSettings s;
100     s.clear();
101     foreach(ActionCollection *coll, actionCollections())
102     coll->writeSettings();
103 }
104
105
106 void GraphicalUi::setMainWidget(QWidget *widget)
107 {
108     _mainWidget = widget;
109 }
110
111
112 void GraphicalUi::setContextMenuActionProvider(ContextMenuActionProvider *provider)
113 {
114     _contextMenuActionProvider = provider;
115 }
116
117
118 void GraphicalUi::setToolBarActionProvider(ToolBarActionProvider *provider)
119 {
120     _toolBarActionProvider = provider;
121 }
122
123
124 void GraphicalUi::setUiStyle(UiStyle *style)
125 {
126     _uiStyle = style;
127 }
128
129
130 void GraphicalUi::disconnectedFromCore()
131 {
132     _contextMenuActionProvider->disconnectedFromCore();
133     _toolBarActionProvider->disconnectedFromCore();
134     AbstractUi::disconnectedFromCore();
135 }
136
137
138 bool GraphicalUi::eventFilter(QObject *obj, QEvent *event)
139 {
140 #ifdef Q_OS_WIN
141     if (obj == mainWidget() && event->type() == QEvent::ActivationChange) {
142         _dwTickCount = GetTickCount();
143     }
144 #endif
145     return AbstractUi::eventFilter(obj, event);
146 }
147
148
149 // NOTE: Window activation stuff seems to work just fine in Plasma 5 without requiring X11 hacks.
150 // TODO: Evaluate cleaning all this up once we can get rid of Qt4/KDE4
151
152 // Code taken from KStatusNotifierItem for handling minimize/restore
153
154 bool GraphicalUi::checkMainWidgetVisibility(bool perform)
155 {
156 #ifdef Q_OS_WIN
157     // the problem is that we lose focus when the systray icon is activated
158     // and we don't know the former active window
159     // therefore we watch for activation event and use our stopwatch :)
160     if (GetTickCount() - _dwTickCount < 300) {
161         // we were active in the last 300ms -> hide it
162         if (perform)
163             minimizeRestore(false);
164         return false;
165     }
166     else {
167         if (perform)
168             minimizeRestore(true);
169         return true;
170     }
171
172 #elif defined(HAVE_KDE4) && defined(Q_WS_X11)
173     KWindowInfo info1 = KWindowSystem::windowInfo(mainWidget()->winId(), NET::XAWMState | NET::WMState | NET::WMDesktop);
174     // mapped = visible (but possibly obscured)
175     bool mapped = (info1.mappingState() == NET::Visible) && !info1.isMinimized();
176
177     //    - not mapped -> show, raise, focus
178     //    - mapped
179     //        - obscured -> raise, focus
180     //        - not obscured -> hide
181     //info1.mappingState() != NET::Visible -> window on another desktop?
182     if (!mapped) {
183         if (perform)
184             minimizeRestore(true);
185         return true;
186     }
187     else {
188         QListIterator<WId> it(KWindowSystem::stackingOrder());
189         it.toBack();
190         while (it.hasPrevious()) {
191             WId id = it.previous();
192             if (id == mainWidget()->winId())
193                 break;
194
195             KWindowInfo info2 = KWindowSystem::windowInfo(id, NET::WMDesktop | NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType);
196
197             if (info2.mappingState() != NET::Visible)
198                 continue;  // not visible on current desktop -> ignore
199
200             if (!info2.geometry().intersects(mainWidget()->geometry()))
201                 continue;  // not obscuring the window -> ignore
202
203             if (!info1.hasState(NET::KeepAbove) && info2.hasState(NET::KeepAbove))
204                 continue;  // obscured by window kept above -> ignore
205
206             NET::WindowType type = info2.windowType(NET::NormalMask | NET::DesktopMask
207                 | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
208                 | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask);
209
210             if (type == NET::Dock || type == NET::TopMenu)
211                 continue;  // obscured by dock or topmenu -> ignore
212
213             if (perform) {
214                 KWindowSystem::raiseWindow(mainWidget()->winId());
215                 KWindowSystem::activateWindow(mainWidget()->winId());
216             }
217             return true;
218         }
219
220         //not on current desktop?
221         if (!info1.isOnCurrentDesktop()) {
222             if (perform)
223                 KWindowSystem::activateWindow(mainWidget()->winId());
224             return true;
225         }
226
227         if (perform)
228             minimizeRestore(false);  // hide
229         return false;
230     }
231 #else
232
233     if (!mainWidget()->isVisible() || mainWidget()->isMinimized() || !mainWidget()->isActiveWindow()) {
234         if (perform)
235             minimizeRestore(true);
236         return true;
237     }
238     else {
239         if (perform)
240             minimizeRestore(false);
241         return false;
242     }
243
244 #endif
245
246     return true;
247 }
248
249
250 bool GraphicalUi::isMainWidgetVisible()
251 {
252     return !instance()->checkMainWidgetVisibility(false);
253 }
254
255
256 void GraphicalUi::minimizeRestore(bool show)
257 {
258     if (show)
259         activateMainWidget();
260     else
261         hideMainWidget();
262 }
263
264
265 void GraphicalUi::activateMainWidget()
266 {
267 #ifdef HAVE_KDE4
268 #  ifdef Q_WS_X11
269     KWindowInfo info = KWindowSystem::windowInfo(mainWidget()->winId(), NET::WMDesktop | NET::WMFrameExtents);
270     if (_onAllDesktops) {
271         KWindowSystem::setOnAllDesktops(mainWidget()->winId(), true);
272     }
273     else {
274         KWindowSystem::setCurrentDesktop(info.desktop());
275     }
276
277     mainWidget()->move(info.frameGeometry().topLeft()); // avoid placement policies
278     mainWidget()->show();
279     mainWidget()->raise();
280     KWindowSystem::raiseWindow(mainWidget()->winId());
281     KWindowSystem::activateWindow(mainWidget()->winId());
282 #  else
283     mainWidget()->show();
284     KWindowSystem::raiseWindow(mainWidget()->winId());
285     KWindowSystem::forceActiveWindow(mainWidget()->winId());
286 #  endif
287
288 #else /* HAVE_KDE4 */
289
290 #ifdef Q_WS_X11
291     // Bypass focus stealing prevention
292     QX11Info::setAppUserTime(QX11Info::appTime());
293 #endif
294
295     if (mainWidget()->windowState() & Qt::WindowMinimized) {
296         // restore
297         mainWidget()->setWindowState((mainWidget()->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
298     }
299
300     // this does not actually work on all platforms... and causes more evil than good
301     // mainWidget()->move(mainWidget()->frameGeometry().topLeft()); // avoid placement policies
302     mainWidget()->show();
303     mainWidget()->raise();
304     mainWidget()->activateWindow();
305
306 #endif /* HAVE_KDE4 */
307 }
308
309
310 void GraphicalUi::hideMainWidget()
311 {
312 #if defined(HAVE_KDE4) && defined(Q_WS_X11)
313     KWindowInfo info = KWindowSystem::windowInfo(mainWidget()->winId(), NET::WMDesktop | NET::WMFrameExtents);
314     _onAllDesktops = info.onAllDesktops();
315 #endif
316
317     if (instance()->isHidingMainWidgetAllowed())
318         mainWidget()->hide();
319 }
320
321
322 void GraphicalUi::toggleMainWidget()
323 {
324     instance()->checkMainWidgetVisibility(true);
325 }