1 /***************************************************************************
2 * Copyright (C) 2005-2018 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 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
22 ***************************************************************************/
24 #include "graphicalui.h"
26 #include "actioncollection.h"
27 #include "uisettings.h"
28 #include "contextmenuactionprovider.h"
29 #include "toolbaractionprovider.h"
35 # include <KWindowInfo>
36 # include <KWindowSystem>
39 QWidget *GraphicalUi::_mainWidget = 0;
40 QHash<QString, ActionCollection *> GraphicalUi::_actionCollections;
41 ContextMenuActionProvider *GraphicalUi::_contextMenuActionProvider = 0;
42 ToolBarActionProvider *GraphicalUi::_toolBarActionProvider = 0;
43 UiStyle *GraphicalUi::_uiStyle = 0;
44 bool GraphicalUi::_onAllDesktops = false;
48 GraphicalUi *_instance{nullptr};
53 GraphicalUi *GraphicalUi::instance() {
58 GraphicalUi::GraphicalUi(QObject *parent) : AbstractUi(parent)
67 GetFrontProcess(&_procNum);
72 void GraphicalUi::init()
75 mainWidget()->installEventFilter(this);
80 ActionCollection *GraphicalUi::actionCollection(const QString &category, const QString &translatedCategory)
82 if (_actionCollections.contains(category))
83 return _actionCollections.value(category);
84 ActionCollection *coll = new ActionCollection(_mainWidget);
86 if (!translatedCategory.isEmpty())
87 coll->setProperty("Category", translatedCategory);
89 coll->setProperty("Category", category);
92 coll->addAssociatedWidget(_mainWidget);
93 _actionCollections.insert(category, coll);
98 QHash<QString, ActionCollection *> GraphicalUi::actionCollections()
100 return _actionCollections;
104 void GraphicalUi::loadShortcuts()
106 foreach(ActionCollection *coll, actionCollections())
107 coll->readSettings();
111 void GraphicalUi::saveShortcuts()
115 foreach(ActionCollection *coll, actionCollections())
116 coll->writeSettings();
120 void GraphicalUi::setMainWidget(QWidget *widget)
122 _mainWidget = widget;
126 void GraphicalUi::setContextMenuActionProvider(ContextMenuActionProvider *provider)
128 _contextMenuActionProvider = provider;
132 void GraphicalUi::setToolBarActionProvider(ToolBarActionProvider *provider)
134 _toolBarActionProvider = provider;
138 void GraphicalUi::setUiStyle(UiStyle *style)
144 void GraphicalUi::disconnectedFromCore()
146 _contextMenuActionProvider->disconnectedFromCore();
147 _toolBarActionProvider->disconnectedFromCore();
148 AbstractUi::disconnectedFromCore();
152 bool GraphicalUi::eventFilter(QObject *obj, QEvent *event)
155 if (obj == mainWidget() && event->type() == QEvent::ActivationChange) {
156 _dwTickCount = GetTickCount();
159 return AbstractUi::eventFilter(obj, event);
163 // NOTE: Window activation stuff seems to work just fine in Plasma 5 without requiring X11 hacks.
164 // TODO: Evaluate cleaning all this up once we can get rid of Qt4/KDE4
166 // Code taken from KStatusNotifierItem for handling minimize/restore
168 bool GraphicalUi::checkMainWidgetVisibility(bool perform)
171 // the problem is that we lose focus when the systray icon is activated
172 // and we don't know the former active window
173 // therefore we watch for activation event and use our stopwatch :)
174 if (GetTickCount() - _dwTickCount < 300) {
175 // we were active in the last 300ms -> hide it
177 minimizeRestore(false);
182 minimizeRestore(true);
186 #elif defined(HAVE_KDE4) && defined(Q_WS_X11)
187 KWindowInfo info1 = KWindowSystem::windowInfo(mainWidget()->winId(), NET::XAWMState | NET::WMState | NET::WMDesktop);
188 // mapped = visible (but possibly obscured)
189 bool mapped = (info1.mappingState() == NET::Visible) && !info1.isMinimized();
191 // - not mapped -> show, raise, focus
193 // - obscured -> raise, focus
194 // - not obscured -> hide
195 //info1.mappingState() != NET::Visible -> window on another desktop?
198 minimizeRestore(true);
202 QListIterator<WId> it(KWindowSystem::stackingOrder());
204 while (it.hasPrevious()) {
205 WId id = it.previous();
206 if (id == mainWidget()->winId())
209 KWindowInfo info2 = KWindowSystem::windowInfo(id, NET::WMDesktop | NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType);
211 if (info2.mappingState() != NET::Visible)
212 continue; // not visible on current desktop -> ignore
214 if (!info2.geometry().intersects(mainWidget()->geometry()))
215 continue; // not obscuring the window -> ignore
217 if (!info1.hasState(NET::KeepAbove) && info2.hasState(NET::KeepAbove))
218 continue; // obscured by window kept above -> ignore
220 NET::WindowType type = info2.windowType(NET::NormalMask | NET::DesktopMask
221 | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
222 | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask);
224 if (type == NET::Dock || type == NET::TopMenu)
225 continue; // obscured by dock or topmenu -> ignore
228 KWindowSystem::raiseWindow(mainWidget()->winId());
229 KWindowSystem::activateWindow(mainWidget()->winId());
234 //not on current desktop?
235 if (!info1.isOnCurrentDesktop()) {
237 KWindowSystem::activateWindow(mainWidget()->winId());
242 minimizeRestore(false); // hide
247 if (!mainWidget()->isVisible() || mainWidget()->isMinimized() || !mainWidget()->isActiveWindow()) {
249 minimizeRestore(true);
254 minimizeRestore(false);
264 bool GraphicalUi::isMainWidgetVisible()
266 return !instance()->checkMainWidgetVisibility(false);
270 void GraphicalUi::minimizeRestore(bool show)
273 activateMainWidget();
279 void GraphicalUi::activateMainWidget()
283 KWindowInfo info = KWindowSystem::windowInfo(mainWidget()->winId(), NET::WMDesktop | NET::WMFrameExtents);
284 if (_onAllDesktops) {
285 KWindowSystem::setOnAllDesktops(mainWidget()->winId(), true);
288 KWindowSystem::setCurrentDesktop(info.desktop());
291 mainWidget()->move(info.frameGeometry().topLeft()); // avoid placement policies
292 mainWidget()->show();
293 mainWidget()->raise();
294 KWindowSystem::raiseWindow(mainWidget()->winId());
295 KWindowSystem::activateWindow(mainWidget()->winId());
297 mainWidget()->show();
298 KWindowSystem::raiseWindow(mainWidget()->winId());
299 KWindowSystem::forceActiveWindow(mainWidget()->winId());
302 #else /* HAVE_KDE4 */
305 // Bypass focus stealing prevention
306 QX11Info::setAppUserTime(QX11Info::appTime());
309 if (mainWidget()->windowState() & Qt::WindowMinimized) {
311 mainWidget()->setWindowState((mainWidget()->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
314 // this does not actually work on all platforms... and causes more evil than good
315 // mainWidget()->move(mainWidget()->frameGeometry().topLeft()); // avoid placement policies
317 SetFrontProcess(&instance()->_procNum);
319 mainWidget()->show();
320 mainWidget()->raise();
321 mainWidget()->activateWindow();
324 #endif /* HAVE_KDE4 */
328 void GraphicalUi::hideMainWidget()
330 #if defined(HAVE_KDE4) && defined(Q_WS_X11)
331 KWindowInfo info = KWindowSystem::windowInfo(mainWidget()->winId(), NET::WMDesktop | NET::WMFrameExtents);
332 _onAllDesktops = info.onAllDesktops();
335 if (instance()->isHidingMainWidgetAllowed())
337 ShowHideProcess(&instance()->_procNum, false);
339 mainWidget()->hide();
344 void GraphicalUi::toggleMainWidget()
346 instance()->checkMainWidgetVisibility(true);