1/***************************************************************************
2 * Copyright (C) 2005-2014 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_KDE
35# include <KWindowInfo>
36# include <KWindowSystem>
37#endif
38
39GraphicalUi *GraphicalUi::_instance = 0;
40QWidget *GraphicalUi::_mainWidget = 0;
41QHash<QString, ActionCollection *> GraphicalUi::_actionCollections;
42ContextMenuActionProvider *GraphicalUi::_contextMenuActionProvider = 0;
43ToolBarActionProvider *GraphicalUi::_toolBarActionProvider = 0;
44UiStyle *GraphicalUi::_uiStyle = 0;
45bool GraphicalUi::_onAllDesktops = false;
46
47GraphicalUi::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
58void GraphicalUi::init()
59{
60#ifdef Q_OS_WIN
61 mainWidget()->installEventFilter(this);
62#endif
63}
64
65
66ActionCollection *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
84QHash<QString, ActionCollection *> GraphicalUi::actionCollections()
85{
86 return _actionCollections;
87}
88
89
90void GraphicalUi::loadShortcuts()
91{
92 foreach(ActionCollection *coll, actionCollections())
93 coll->readSettings();
94}
95
96
97void GraphicalUi::saveShortcuts()
98{
99 ShortcutSettings s;
100 s.clear();
101 foreach(ActionCollection *coll, actionCollections())
102 coll->writeSettings();
103}
104
105
106void GraphicalUi::setMainWidget(QWidget *widget)
107{
108 _mainWidget = widget;
109}
110
111
112void GraphicalUi::setContextMenuActionProvider(ContextMenuActionProvider *provider)
113{
114 _contextMenuActionProvider = provider;
115}
116
117
118void GraphicalUi::setToolBarActionProvider(ToolBarActionProvider *provider)
119{
120 _toolBarActionProvider = provider;
121}
122
123
124void GraphicalUi::setUiStyle(UiStyle *style)
125{
126 _uiStyle = style;
127}
128
129
130void GraphicalUi::disconnectedFromCore()
131{
132 _contextMenuActionProvider->disconnectedFromCore();
133 _toolBarActionProvider->disconnectedFromCore();
134 AbstractUi::disconnectedFromCore();
135}
136
137
138bool 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// Code taken from KStatusNotifierItem for handling minimize/restore
150
151bool GraphicalUi::checkMainWidgetVisibility(bool perform)
152{
153#ifdef Q_OS_WIN
154 // the problem is that we lose focus when the systray icon is activated
155 // and we don't know the former active window
156 // therefore we watch for activation event and use our stopwatch :)
157 if (GetTickCount() - _dwTickCount < 300) {
158 // we were active in the last 300ms -> hide it
159 if (perform)
160 minimizeRestore(false);
161 return false;
162 }
163 else {
164 if (perform)
165 minimizeRestore(true);
166 return true;
167 }
168
169#elif defined(HAVE_KDE) && defined(Q_WS_X11)
170 KWindowInfo info1 = KWindowSystem::windowInfo(mainWidget()->winId(), NET::XAWMState | NET::WMState | NET::WMDesktop);
171 // mapped = visible (but possibly obscured)
172 bool mapped = (info1.mappingState() == NET::Visible) && !info1.isMinimized();
173
174 // - not mapped -> show, raise, focus
175 // - mapped
176 // - obscured -> raise, focus
177 // - not obscured -> hide
178 //info1.mappingState() != NET::Visible -> window on another desktop?
179 if (!mapped) {
180 if (perform)
181 minimizeRestore(true);
182 return true;
183 }
184 else {
185 QListIterator<WId> it(KWindowSystem::stackingOrder());
186 it.toBack();
187 while (it.hasPrevious()) {
188 WId id = it.previous();
189 if (id == mainWidget()->winId())
190 break;
191
192 KWindowInfo info2 = KWindowSystem::windowInfo(id, NET::WMDesktop | NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType);
193
194 if (info2.mappingState() != NET::Visible)
195 continue; // not visible on current desktop -> ignore
196
197 if (!info2.geometry().intersects(mainWidget()->geometry()))
198 continue; // not obscuring the window -> ignore
199
200 if (!info1.hasState(NET::KeepAbove) && info2.hasState(NET::KeepAbove))
201 continue; // obscured by window kept above -> ignore
202
203 NET::WindowType type = info2.windowType(NET::NormalMask | NET::DesktopMask
204 | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
205 | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask);
206
207 if (type == NET::Dock || type == NET::TopMenu)
208 continue; // obscured by dock or topmenu -> ignore
209
210 if (perform) {
211 KWindowSystem::raiseWindow(mainWidget()->winId());
212 KWindowSystem::activateWindow(mainWidget()->winId());
213 }
214 return true;
215 }
216
217 //not on current desktop?
218 if (!info1.isOnCurrentDesktop()) {
219 if (perform)
220 KWindowSystem::activateWindow(mainWidget()->winId());
221 return true;
222 }
223
224 if (perform)
225 minimizeRestore(false); // hide
226 return false;
227 }
228#else
229
230 if (!mainWidget()->isVisible() || mainWidget()->isMinimized() || !mainWidget()->isActiveWindow()) {
231 if (perform)
232 minimizeRestore(true);
233 return true;
234 }
235 else {
236 if (perform)
237 minimizeRestore(false);
238 return false;
239 }
240
241#endif
242
243 return true;
244}
245
246
247bool GraphicalUi::isMainWidgetVisible()
248{
249 return !instance()->checkMainWidgetVisibility(false);
250}
251
252
253void GraphicalUi::minimizeRestore(bool show)
254{
255 if (show)
256 activateMainWidget();
257 else
258 hideMainWidget();
259}
260
261
262void GraphicalUi::activateMainWidget()
263{
264#ifdef HAVE_KDE
265# ifdef Q_WS_X11
266 KWindowInfo info = KWindowSystem::windowInfo(mainWidget()->winId(), NET::WMDesktop | NET::WMFrameExtents);
267 if (_onAllDesktops) {
268 KWindowSystem::setOnAllDesktops(mainWidget()->winId(), true);
269 }
270 else {
271 KWindowSystem::setCurrentDesktop(info.desktop());
272 }
273
274 mainWidget()->move(info.frameGeometry().topLeft()); // avoid placement policies
275 mainWidget()->show();
276 mainWidget()->raise();
277 KWindowSystem::raiseWindow(mainWidget()->winId());
278 KWindowSystem::activateWindow(mainWidget()->winId());
279# else
280 mainWidget()->show();
281 KWindowSystem::raiseWindow(mainWidget()->winId());
282 KWindowSystem::forceActiveWindow(mainWidget()->winId());
283# endif
284
285#else /* HAVE_KDE */
286
287#ifdef Q_WS_X11
288 // Bypass focus stealing prevention
289 QX11Info::setAppUserTime(QX11Info::appTime());
290#endif
291
292 if (mainWidget()->windowState() & Qt::WindowMinimized) {
293 // restore
294 mainWidget()->setWindowState((mainWidget()->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
295 }
296
297 // this does not actually work on all platforms... and causes more evil than good
298 // mainWidget()->move(mainWidget()->frameGeometry().topLeft()); // avoid placement policies
299 mainWidget()->show();
300 mainWidget()->raise();
301 mainWidget()->activateWindow();
302
303#endif /* HAVE_KDE */
304}
305
306
307void GraphicalUi::hideMainWidget()
308{
309#if defined(HAVE_KDE) && defined(Q_WS_X11)
310 KWindowInfo info = KWindowSystem::windowInfo(mainWidget()->winId(), NET::WMDesktop | NET::WMFrameExtents);
311 _onAllDesktops = info.onAllDesktops();
312#endif
313
314 if (instance()->isHidingMainWidgetAllowed())
315 mainWidget()->hide();
316}
317
318
319void GraphicalUi::toggleMainWidget()
320{
321 instance()->checkMainWidgetVisibility(true);
322}
323