Work around problems in QTreeView when using Qt 4.8
[quassel.git] / src / uisupport / graphicalui.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
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
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   Q_ASSERT(!_instance);
49   _instance = this;
50
51 #ifdef Q_WS_WIN
52   _dwTickCount = 0;
53 #endif
54 }
55
56 void GraphicalUi::init() {
57 #ifdef Q_WS_WIN
58   mainWidget()->installEventFilter(this);
59 #endif
60 }
61
62 ActionCollection *GraphicalUi::actionCollection(const QString &category, const QString &translatedCategory) {
63   if(_actionCollections.contains(category))
64     return _actionCollections.value(category);
65   ActionCollection *coll = new ActionCollection(_mainWidget);
66
67   if(!translatedCategory.isEmpty())
68     coll->setProperty("Category", translatedCategory);
69   else
70     coll->setProperty("Category", category);
71
72   if(_mainWidget)
73     coll->addAssociatedWidget(_mainWidget);
74   _actionCollections.insert(category, coll);
75   return coll;
76 }
77
78 QHash<QString, ActionCollection *> GraphicalUi::actionCollections() {
79   return _actionCollections;
80 }
81
82 void GraphicalUi::loadShortcuts() {
83   foreach(ActionCollection *coll, actionCollections())
84     coll->readSettings();
85 }
86
87 void GraphicalUi::saveShortcuts() {
88   ShortcutSettings s;
89   s.clear();
90   foreach(ActionCollection *coll, actionCollections())
91     coll->writeSettings();
92 }
93
94 void GraphicalUi::setMainWidget(QWidget *widget) {
95   _mainWidget = widget;
96 }
97
98 void GraphicalUi::setContextMenuActionProvider(ContextMenuActionProvider *provider) {
99   _contextMenuActionProvider = provider;
100 }
101
102 void GraphicalUi::setToolBarActionProvider(ToolBarActionProvider *provider) {
103   _toolBarActionProvider = provider;
104 }
105
106 void GraphicalUi::setUiStyle(UiStyle *style) {
107   _uiStyle = style;
108 }
109
110 void GraphicalUi::disconnectedFromCore() {
111   _contextMenuActionProvider->disconnectedFromCore();
112   _toolBarActionProvider->disconnectedFromCore();
113   AbstractUi::disconnectedFromCore();
114 }
115
116 bool GraphicalUi::eventFilter(QObject *obj, QEvent *event) {
117 #ifdef Q_WS_WIN
118   if(obj == mainWidget() && event->type() == QEvent::ActivationChange) {
119     _dwTickCount = GetTickCount();
120   }
121 #endif
122   return AbstractUi::eventFilter(obj, event);
123 }
124
125 // Code taken from KStatusNotifierItem for handling minimize/restore
126
127 bool GraphicalUi::checkMainWidgetVisibility(bool perform) {
128 #ifdef Q_WS_WIN
129   // the problem is that we lose focus when the systray icon is activated
130   // and we don't know the former active window
131   // therefore we watch for activation event and use our stopwatch :)
132   if(GetTickCount() - _dwTickCount < 300) {
133     // we were active in the last 300ms -> hide it
134     if(perform)
135       minimizeRestore(false);
136     return false;
137   } else {
138     if(perform)
139       minimizeRestore(true);
140     return true;
141   }
142
143 #elif defined(HAVE_KDE) && defined(Q_WS_X11)
144   KWindowInfo info1 = KWindowSystem::windowInfo(mainWidget()->winId(), NET::XAWMState | NET::WMState | NET::WMDesktop);
145   // mapped = visible (but possibly obscured)
146   bool mapped = (info1.mappingState() == NET::Visible) && !info1.isMinimized();
147
148   //    - not mapped -> show, raise, focus
149   //    - mapped
150   //        - obscured -> raise, focus
151   //        - not obscured -> hide
152   //info1.mappingState() != NET::Visible -> window on another desktop?
153   if(!mapped) {
154     if(perform)
155       minimizeRestore(true);
156     return true;
157
158   } else {
159     QListIterator< WId > it (KWindowSystem::stackingOrder());
160     it.toBack();
161     while(it.hasPrevious()) {
162       WId id = it.previous();
163       if(id == mainWidget()->winId())
164         break;
165
166       KWindowInfo info2 = KWindowSystem::windowInfo(id, NET::WMDesktop | NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType);
167
168       if(info2.mappingState() != NET::Visible)
169         continue; // not visible on current desktop -> ignore
170
171       if(!info2.geometry().intersects(mainWidget()->geometry()))
172         continue; // not obscuring the window -> ignore
173
174       if(!info1.hasState(NET::KeepAbove) && info2.hasState(NET::KeepAbove))
175         continue; // obscured by window kept above -> ignore
176
177       NET::WindowType type = info2.windowType(NET::NormalMask | NET::DesktopMask
178                                               | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
179                                               | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask);
180
181       if(type == NET::Dock || type == NET::TopMenu)
182         continue; // obscured by dock or topmenu -> ignore
183
184       if(perform) {
185         KWindowSystem::raiseWindow(mainWidget()->winId());
186         KWindowSystem::activateWindow(mainWidget()->winId());
187       }
188       return true;
189     }
190
191     //not on current desktop?
192     if(!info1.isOnCurrentDesktop()) {
193       if(perform)
194         KWindowSystem::activateWindow(mainWidget()->winId());
195       return true;
196     }
197
198     if(perform)
199       minimizeRestore(false); // hide
200     return false;
201   }
202 #else
203
204   if(!mainWidget()->isVisible() || mainWidget()->isMinimized() || !mainWidget()->isActiveWindow()) {
205     if(perform)
206       minimizeRestore(true);
207     return true;
208   } else {
209     if(perform)
210       minimizeRestore(false);
211     return false;
212   }
213
214 #endif
215
216   return true;
217 }
218
219 bool GraphicalUi::isMainWidgetVisible() {
220   return !instance()->checkMainWidgetVisibility(false);
221 }
222
223 void GraphicalUi::minimizeRestore(bool show) {
224   if(show)
225     activateMainWidget();
226   else
227     hideMainWidget();
228 }
229
230 void GraphicalUi::activateMainWidget() {
231 #ifdef HAVE_KDE
232 #  ifdef Q_WS_X11
233     KWindowInfo info = KWindowSystem::windowInfo(mainWidget()->winId(), NET::WMDesktop | NET::WMFrameExtents);
234     if(_onAllDesktops) {
235       KWindowSystem::setOnAllDesktops(mainWidget()->winId(), true);
236     } else {
237       KWindowSystem::setCurrentDesktop(info.desktop());
238     }
239
240     mainWidget()->move(info.frameGeometry().topLeft()); // avoid placement policies
241     mainWidget()->show();
242     mainWidget()->raise();
243     KWindowSystem::raiseWindow(mainWidget()->winId());
244     KWindowSystem::activateWindow(mainWidget()->winId());
245 #  else
246     mainWidget()->show();
247     KWindowSystem::raiseWindow(mainWidget()->winId());
248     KWindowSystem::forceActiveWindow(mainWidget()->winId());
249 #  endif
250
251 #else /* HAVE_KDE */
252
253 #ifdef Q_WS_X11
254   // Bypass focus stealing prevention
255   QX11Info::setAppUserTime(QX11Info::appTime());
256 #endif
257
258   if(mainWidget()->windowState() & Qt::WindowMinimized) {
259     // restore
260     mainWidget()->setWindowState((mainWidget()->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
261   }
262
263   // this does not actually work on all platforms... and causes more evil than good
264   // mainWidget()->move(mainWidget()->frameGeometry().topLeft()); // avoid placement policies
265   mainWidget()->show();
266   mainWidget()->raise();
267   mainWidget()->activateWindow();
268
269 #endif /* HAVE_KDE */
270 }
271
272 void GraphicalUi::hideMainWidget() {
273
274 #if defined(HAVE_KDE) && defined(Q_WS_X11)
275   KWindowInfo info = KWindowSystem::windowInfo(mainWidget()->winId(), NET::WMDesktop | NET::WMFrameExtents);
276   _onAllDesktops = info.onAllDesktops();
277 #endif
278
279   if(instance()->isHidingMainWidgetAllowed())
280     mainWidget()->hide();
281 }
282
283 void GraphicalUi::toggleMainWidget() {
284   instance()->checkMainWidgetVisibility(true);
285 }