Add comment regarding the removal of X11 hacks to GraphicalUi
[quassel.git] / src / uisupport / graphicalui.cpp
index 8e5fe94..d1e106d 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2010 by the Quassel Project                        *
+ *   Copyright (C) 2005-2015 by the Quassel Project                        *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This contains code from KStatusNotifierItem, part of the KDE libs     *
  *   You should have received a copy of the GNU General Public License     *
  *   along with this program; if not, write to the                         *
  *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
  ***************************************************************************/
 
 #include "graphicalui.h"
 
 #include "actioncollection.h"
+#include "uisettings.h"
 #include "contextmenuactionprovider.h"
+#include "toolbaractionprovider.h"
 
 #ifdef Q_WS_X11
 #  include <QX11Info>
 #endif
-#ifdef HAVE_KDE
+#ifdef HAVE_KDE4
 #  include <KWindowInfo>
 #  include <KWindowSystem>
 #endif
@@ -42,169 +44,234 @@ ToolBarActionProvider *GraphicalUi::_toolBarActionProvider = 0;
 UiStyle *GraphicalUi::_uiStyle = 0;
 bool GraphicalUi::_onAllDesktops = false;
 
-GraphicalUi::GraphicalUi(QObject *parent) : AbstractUi(parent) {
-  Q_ASSERT(!_instance);
-  _instance = this;
+GraphicalUi::GraphicalUi(QObject *parent) : AbstractUi(parent)
+{
+    Q_ASSERT(!_instance);
+    _instance = this;
 
-#ifdef Q_WS_WIN
-  _dwTickCount = 0;
+#ifdef Q_OS_WIN
+    _dwTickCount = 0;
 #endif
 }
 
-void GraphicalUi::init() {
-#ifdef Q_WS_WIN
-  mainWidget()->installEventFilter(this);
+
+void GraphicalUi::init()
+{
+#ifdef Q_OS_WIN
+    mainWidget()->installEventFilter(this);
 #endif
 }
 
-ActionCollection *GraphicalUi::actionCollection(const QString &category) {
-  if(_actionCollections.contains(category))
-    return _actionCollections.value(category);
-  ActionCollection *coll = new ActionCollection(_mainWidget);
-  if(_mainWidget)
-    coll->addAssociatedWidget(_mainWidget);
-  _actionCollections.insert(category, coll);
-  return coll;
-}
 
-void GraphicalUi::setMainWidget(QWidget *widget) {
-  _mainWidget = widget;
+ActionCollection *GraphicalUi::actionCollection(const QString &category, const QString &translatedCategory)
+{
+    if (_actionCollections.contains(category))
+        return _actionCollections.value(category);
+    ActionCollection *coll = new ActionCollection(_mainWidget);
+
+    if (!translatedCategory.isEmpty())
+        coll->setProperty("Category", translatedCategory);
+    else
+        coll->setProperty("Category", category);
+
+    if (_mainWidget)
+        coll->addAssociatedWidget(_mainWidget);
+    _actionCollections.insert(category, coll);
+    return coll;
 }
 
-void GraphicalUi::setContextMenuActionProvider(ContextMenuActionProvider *provider) {
-  _contextMenuActionProvider = provider;
+
+QHash<QString, ActionCollection *> GraphicalUi::actionCollections()
+{
+    return _actionCollections;
 }
 
-void GraphicalUi::setToolBarActionProvider(ToolBarActionProvider *provider) {
-  _toolBarActionProvider = provider;
+
+void GraphicalUi::loadShortcuts()
+{
+    foreach(ActionCollection *coll, actionCollections())
+    coll->readSettings();
 }
 
-void GraphicalUi::setUiStyle(UiStyle *style) {
-  _uiStyle = style;
+
+void GraphicalUi::saveShortcuts()
+{
+    ShortcutSettings s;
+    s.clear();
+    foreach(ActionCollection *coll, actionCollections())
+    coll->writeSettings();
 }
 
-bool GraphicalUi::eventFilter(QObject *obj, QEvent *event) {
-#ifdef Q_WS_WIN
-  if(obj == mainWidget() && event->type() == QEvent::ActivationChange) {
-    _dwTickCount = GetTickCount();
-  }
-#endif
-  return AbstractUi::eventFilter(obj, event);
+
+void GraphicalUi::setMainWidget(QWidget *widget)
+{
+    _mainWidget = widget;
 }
 
-// Code taken from KStatusNotifierItem for handling minimize/restore
 
-bool GraphicalUi::checkMainWidgetVisibility(bool perform) {
-#ifdef Q_WS_WIN
-  // the problem is that we lose focus when the systray icon is activated
-  // and we don't know the former active window
-  // therefore we watch for activation event and use our stopwatch :)
-  if(GetTickCount() - _dwTickCount < 300) {
-    // we were active in the last 300ms -> hide it
-    if(perform)
-      minimizeRestore(false);
-    return false;
-  } else {
-    if(perform)
-      minimizeRestore(true);
-    return true;
-  }
-
-#elif defined(HAVE_KDE) && defined(Q_WS_X11)
-  KWindowInfo info1 = KWindowSystem::windowInfo(mainWidget()->winId(), NET::XAWMState | NET::WMState | NET::WMDesktop);
-  // mapped = visible (but possibly obscured)
-  bool mapped = (info1.mappingState() == NET::Visible) && !info1.isMinimized();
-
-  //    - not mapped -> show, raise, focus
-  //    - mapped
-  //        - obscured -> raise, focus
-  //        - not obscured -> hide
-  //info1.mappingState() != NET::Visible -> window on another desktop?
-  if(!mapped) {
-    if(perform)
-      minimizeRestore(true);
-    return true;
+void GraphicalUi::setContextMenuActionProvider(ContextMenuActionProvider *provider)
+{
+    _contextMenuActionProvider = provider;
+}
 
-  } else {
-    QListIterator< WId > it (KWindowSystem::stackingOrder());
-    it.toBack();
-    while(it.hasPrevious()) {
-      WId id = it.previous();
-      if(id == mainWidget()->winId())
-        break;
 
-      KWindowInfo info2 = KWindowSystem::windowInfo(id, NET::WMDesktop | NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType);
+void GraphicalUi::setToolBarActionProvider(ToolBarActionProvider *provider)
+{
+    _toolBarActionProvider = provider;
+}
 
-      if(info2.mappingState() != NET::Visible)
-        continue; // not visible on current desktop -> ignore
 
-      if(!info2.geometry().intersects(mainWidget()->geometry()))
-        continue; // not obscuring the window -> ignore
+void GraphicalUi::setUiStyle(UiStyle *style)
+{
+    _uiStyle = style;
+}
 
-      if(!info1.hasState(NET::KeepAbove) && info2.hasState(NET::KeepAbove))
-        continue; // obscured by window kept above -> ignore
 
-      NET::WindowType type = info2.windowType(NET::NormalMask | NET::DesktopMask
-                                              | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
-                                              | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask);
+void GraphicalUi::disconnectedFromCore()
+{
+    _contextMenuActionProvider->disconnectedFromCore();
+    _toolBarActionProvider->disconnectedFromCore();
+    AbstractUi::disconnectedFromCore();
+}
 
-      if(type == NET::Dock || type == NET::TopMenu)
-        continue; // obscured by dock or topmenu -> ignore
 
-      if(perform) {
-        KWindowSystem::raiseWindow(mainWidget()->winId());
-        KWindowSystem::activateWindow(mainWidget()->winId());
-      }
-      return true;
+bool GraphicalUi::eventFilter(QObject *obj, QEvent *event)
+{
+#ifdef Q_OS_WIN
+    if (obj == mainWidget() && event->type() == QEvent::ActivationChange) {
+        _dwTickCount = GetTickCount();
     }
+#endif
+    return AbstractUi::eventFilter(obj, event);
+}
+
 
-    //not on current desktop?
-    if(!info1.isOnCurrentDesktop()) {
-      if(perform)
-        KWindowSystem::activateWindow(mainWidget()->winId());
-      return true;
+// NOTE: Window activation stuff seems to work just fine in Plasma 5 without requiring X11 hacks.
+// TODO: Evaluate cleaning all this up once we can get rid of Qt4/KDE4
+
+// Code taken from KStatusNotifierItem for handling minimize/restore
+
+bool GraphicalUi::checkMainWidgetVisibility(bool perform)
+{
+#ifdef Q_OS_WIN
+    // the problem is that we lose focus when the systray icon is activated
+    // and we don't know the former active window
+    // therefore we watch for activation event and use our stopwatch :)
+    if (GetTickCount() - _dwTickCount < 300) {
+        // we were active in the last 300ms -> hide it
+        if (perform)
+            minimizeRestore(false);
+        return false;
+    }
+    else {
+        if (perform)
+            minimizeRestore(true);
+        return true;
     }
 
-    if(perform)
-      minimizeRestore(false); // hide
-    return false;
-  }
+#elif defined(HAVE_KDE4) && defined(Q_WS_X11)
+    KWindowInfo info1 = KWindowSystem::windowInfo(mainWidget()->winId(), NET::XAWMState | NET::WMState | NET::WMDesktop);
+    // mapped = visible (but possibly obscured)
+    bool mapped = (info1.mappingState() == NET::Visible) && !info1.isMinimized();
+
+    //    - not mapped -> show, raise, focus
+    //    - mapped
+    //        - obscured -> raise, focus
+    //        - not obscured -> hide
+    //info1.mappingState() != NET::Visible -> window on another desktop?
+    if (!mapped) {
+        if (perform)
+            minimizeRestore(true);
+        return true;
+    }
+    else {
+        QListIterator<WId> it(KWindowSystem::stackingOrder());
+        it.toBack();
+        while (it.hasPrevious()) {
+            WId id = it.previous();
+            if (id == mainWidget()->winId())
+                break;
+
+            KWindowInfo info2 = KWindowSystem::windowInfo(id, NET::WMDesktop | NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType);
+
+            if (info2.mappingState() != NET::Visible)
+                continue;  // not visible on current desktop -> ignore
+
+            if (!info2.geometry().intersects(mainWidget()->geometry()))
+                continue;  // not obscuring the window -> ignore
+
+            if (!info1.hasState(NET::KeepAbove) && info2.hasState(NET::KeepAbove))
+                continue;  // obscured by window kept above -> ignore
+
+            NET::WindowType type = info2.windowType(NET::NormalMask | NET::DesktopMask
+                | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
+                | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask);
+
+            if (type == NET::Dock || type == NET::TopMenu)
+                continue;  // obscured by dock or topmenu -> ignore
+
+            if (perform) {
+                KWindowSystem::raiseWindow(mainWidget()->winId());
+                KWindowSystem::activateWindow(mainWidget()->winId());
+            }
+            return true;
+        }
+
+        //not on current desktop?
+        if (!info1.isOnCurrentDesktop()) {
+            if (perform)
+                KWindowSystem::activateWindow(mainWidget()->winId());
+            return true;
+        }
+
+        if (perform)
+            minimizeRestore(false);  // hide
+        return false;
+    }
 #else
 
-  if(!mainWidget()->isVisible() || mainWidget()->isMinimized()) {
-    if(perform)
-      minimizeRestore(true);
-    return true;
-  } else {
-    if(perform)
-      minimizeRestore(false);
-    return false;
-  }
+    if (!mainWidget()->isVisible() || mainWidget()->isMinimized() || !mainWidget()->isActiveWindow()) {
+        if (perform)
+            minimizeRestore(true);
+        return true;
+    }
+    else {
+        if (perform)
+            minimizeRestore(false);
+        return false;
+    }
 
 #endif
 
-  return true;
+    return true;
 }
 
-bool GraphicalUi::isMainWidgetVisible() {
-  return !instance()->checkMainWidgetVisibility(false);
+
+bool GraphicalUi::isMainWidgetVisible()
+{
+    return !instance()->checkMainWidgetVisibility(false);
 }
 
-void GraphicalUi::minimizeRestore(bool show) {
-  if(show)
-    activateMainWidget();
-  else
-    hideMainWidget();
+
+void GraphicalUi::minimizeRestore(bool show)
+{
+    if (show)
+        activateMainWidget();
+    else
+        hideMainWidget();
 }
 
-void GraphicalUi::activateMainWidget() {
-#ifdef HAVE_KDE
+
+void GraphicalUi::activateMainWidget()
+{
+#ifdef HAVE_KDE4
 #  ifdef Q_WS_X11
     KWindowInfo info = KWindowSystem::windowInfo(mainWidget()->winId(), NET::WMDesktop | NET::WMFrameExtents);
-    if(_onAllDesktops) {
-      KWindowSystem::setOnAllDesktops(mainWidget()->winId(), true);
-    } else {
-      KWindowSystem::setCurrentDesktop(info.desktop());
+    if (_onAllDesktops) {
+        KWindowSystem::setOnAllDesktops(mainWidget()->winId(), true);
+    }
+    else {
+        KWindowSystem::setCurrentDesktop(info.desktop());
     }
 
     mainWidget()->move(info.frameGeometry().topLeft()); // avoid placement policies
@@ -218,38 +285,41 @@ void GraphicalUi::activateMainWidget() {
     KWindowSystem::forceActiveWindow(mainWidget()->winId());
 #  endif
 
-#else /* HAVE_KDE */
+#else /* HAVE_KDE4 */
 
 #ifdef Q_WS_X11
-  // Bypass focus stealing prevention
-  QX11Info::setAppUserTime(QX11Info::appTime());
+    // Bypass focus stealing prevention
+    QX11Info::setAppUserTime(QX11Info::appTime());
 #endif
 
-  if(mainWidget()->windowState() & Qt::WindowMinimized) {
-    // restore
-    mainWidget()->setWindowState((mainWidget()->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
-  }
+    if (mainWidget()->windowState() & Qt::WindowMinimized) {
+        // restore
+        mainWidget()->setWindowState((mainWidget()->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
+    }
 
-  // this does not actually work on all platforms... and causes more evil than good
-  // mainWidget()->move(mainWidget()->frameGeometry().topLeft()); // avoid placement policies
-  mainWidget()->show();
-  mainWidget()->raise();
-  mainWidget()->activateWindow();
+    // this does not actually work on all platforms... and causes more evil than good
+    // mainWidget()->move(mainWidget()->frameGeometry().topLeft()); // avoid placement policies
+    mainWidget()->show();
+    mainWidget()->raise();
+    mainWidget()->activateWindow();
 
-#endif /* HAVE_KDE */
+#endif /* HAVE_KDE4 */
 }
 
-void GraphicalUi::hideMainWidget() {
 
-#if defined(HAVE_KDE) && defined(Q_WS_X11)
-  KWindowInfo info = KWindowSystem::windowInfo(mainWidget()->winId(), NET::WMDesktop | NET::WMFrameExtents);
-  _onAllDesktops = info.onAllDesktops();
+void GraphicalUi::hideMainWidget()
+{
+#if defined(HAVE_KDE4) && defined(Q_WS_X11)
+    KWindowInfo info = KWindowSystem::windowInfo(mainWidget()->winId(), NET::WMDesktop | NET::WMFrameExtents);
+    _onAllDesktops = info.onAllDesktops();
 #endif
 
-  if(instance()->isHidingMainWidgetAllowed())
-    mainWidget()->hide();
+    if (instance()->isHidingMainWidgetAllowed())
+        mainWidget()->hide();
 }
 
-void GraphicalUi::toggleMainWidget() {
-  instance()->checkMainWidgetVisibility(true);
+
+void GraphicalUi::toggleMainWidget()
+{
+    instance()->checkMainWidgetVisibility(true);
 }