[kde-doc-english] [kde-workspace] /: GUI: Kwin appmenu support:

Cedric Bellegarde gnumdk at gmail.com
Fri Nov 9 12:47:52 UTC 2012


Git commit 1bb5e4fb39613675eac4a29c7c11f3a54303a083 by Cedric Bellegarde.
Committed on 09/11/2012 at 13:44.
Pushed by cedric into branch 'master'.

GUI: Kwin appmenu support:
- Add support for application menu button in Kwin
- Add kded appmenu configuration in kcm_style

M  +1    -0    kcontrol/style/CMakeLists.txt
M  +60   -1    kcontrol/style/finetuning.ui
M  +101  -0    kcontrol/style/kcmstyle.cpp
M  +2    -0    kcontrol/style/kcmstyle.h
M  +4    -0    kwin/CMakeLists.txt
M  +16   -0    kwin/bridge.cpp
M  +2    -0    kwin/bridge.h
M  +30   -0    kwin/client.cpp
M  +39   -0    kwin/client.h
M  +17   -3    kwin/clients/oxygen/oxygenbutton.cpp
M  +3    -0    kwin/clients/oxygen/oxygenbutton.h
M  +4    -1    kwin/clients/oxygen/oxygenclient.cpp
M  +1    -0    kwin/clients/oxygen/oxygenfactory.cpp
M  +1    -0    kwin/clients/oxygen/oxygenfactory.h
M  +1    -0    kwin/config-kwin.h.cmake
M  +26   -2    kwin/kcmkwin/kwindecoration/buttons.cpp
M  +9    -0    kwin/kcmkwin/kwindecoration/preview.cpp
M  +2    -0    kwin/kcmkwin/kwindecoration/preview.h
M  +60   -4    kwin/libkdecorations/kcommondecoration.cpp
M  +4    -0    kwin/libkdecorations/kcommondecoration.h
M  +11   -0    kwin/libkdecorations/kdecoration.cpp
M  +30   -2    kwin/libkdecorations/kdecoration.h
M  +2    -0    kwin/libkdecorations/kdecorationbridge.h
M  +20   -0    kwin/useractions.cpp
M  +49   -0    kwin/workspace.cpp
M  +13   -0    kwin/workspace.h

http://commits.kde.org/kde-workspace/1bb5e4fb39613675eac4a29c7c11f3a54303a083

diff --git a/kcontrol/style/CMakeLists.txt b/kcontrol/style/CMakeLists.txt
index cbab94d..0242c04 100644
--- a/kcontrol/style/CMakeLists.txt
+++ b/kcontrol/style/CMakeLists.txt
@@ -19,4 +19,5 @@ install(TARGETS kcm_style  DESTINATION ${PLUGIN_INSTALL_DIR})
 
 ########### install files ###############
 
+install( FILES kcmstyle.notifyrc  DESTINATION  ${DATA_INSTALL_DIR}/kcmstyle )
 install( FILES style.desktop  DESTINATION  ${SERVICES_INSTALL_DIR} )
diff --git a/kcontrol/style/finetuning.ui b/kcontrol/style/finetuning.ui
index 2d384bc..3abb692 100644
--- a/kcontrol/style/finetuning.ui
+++ b/kcontrol/style/finetuning.ui
@@ -173,7 +173,66 @@
      </layout>
     </widget>
    </item>
-   <item row="4" column="0">
+   <item row="4" column="0" colspan="3">
+    <widget class="QGroupBox" name="menubarBox">
+     <property name="title">
+      <string>Menubar</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout1">
+      <item row="0" column="0">
+       <widget class="QLabel" name="labelMenubarStyle">
+        <property name="text">
+         <string>Menubar style:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="buddy">
+         <cstring>comboMenubarStyle</cstring>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="KComboBox" name="comboMenubarStyle">
+        <item>
+         <property name="text">
+          <string>In application</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Title bar button</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Top screen menubar</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Only export</string>
+         </property>
+        </item>
+       </widget>
+      </item>
+      <item row="0" column="2">
+       <spacer name="horizontalSpacer_3">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>40</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="5" column="0">
     <spacer name="verticalSpacer">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
diff --git a/kcontrol/style/kcmstyle.cpp b/kcontrol/style/kcmstyle.cpp
index 849a49b..892e8c6 100644
--- a/kcontrol/style/kcmstyle.cpp
+++ b/kcontrol/style/kcmstyle.cpp
@@ -42,6 +42,7 @@
 #include <kstandarddirs.h>
 #include <kautostart.h>
 #include <KDebug>
+#include <KNotification>
 #include <KLibrary>
 #include <KColorScheme>
 #include <KStandardDirs>
@@ -259,9 +260,14 @@ KCMStyle::KCMStyle( QWidget* parent, const QVariantList& )
     connect(fineTuningUi.comboGraphicEffectsLevel, SIGNAL(activated(int)),   this, SLOT(setEffectsDirty()));
     connect(fineTuningUi.comboToolbarIcons,    SIGNAL(activated(int)), this, SLOT(setEffectsDirty()));
     connect(fineTuningUi.comboSecondaryToolbarIcons,    SIGNAL(activated(int)), this, SLOT(setEffectsDirty()));
+    connect(fineTuningUi.comboMenubarStyle,    SIGNAL(activated(int)), this, SLOT(setEffectsDirty()));
 
     addWhatsThis();
 
+    if (!QFile::exists(QLibraryInfo::location(QLibraryInfo::PluginsPath) + "/menubar/libappmenu-qt.so")) {
+        fineTuningUi.menubarBox->hide();
+    }
+
     // Insert the pages into the tabWidget
     tabWidget->addTab(page1, i18nc("@title:tab", "&Applications"));
     tabWidget->addTab(page2, i18nc("@title:tab", "&Fine Tuning"));
@@ -400,8 +406,71 @@ void KCMStyle::save()
                             toolbarButtonText(fineTuningUi.comboToolbarIcons->currentIndex()));
     toolbarStyleGroup.writeEntry("ToolButtonStyleOtherToolbars",
                             toolbarButtonText(fineTuningUi.comboSecondaryToolbarIcons->currentIndex()));
+
+    // menubar page
+    KConfigGroup menuBarStyleGroup(&_config, "Appmenu Style");
+
+    // load kded module if needed
+    bool load = false;
+    QList<QVariant> args;
+
+    QString style = menuBarStyleText(fineTuningUi.comboMenubarStyle->currentIndex());
+
+    QString previous = menuBarStyleGroup.readEntry("Style", "InApplication");
+    menuBarStyleGroup.writeEntry("Style", style);
     _config.sync();
 
+    QDBusMessage method = QDBusMessage::createMethodCall("org.kde.kded",
+                                                         "/modules/appmenu",
+                                                         "org.kde.kded",
+                                                         "reconfigure");
+    QDBusConnection::sessionBus().asyncCall(method);
+
+    if (previous == "InApplication" && style != "InApplication") {
+        load = true;
+        KNotification *notification = new KNotification("reload", 0);
+        notification->setComponentData(KComponentData("kcmstyle"));
+        notification->setText(i18n("Settings changes will take effect only on application restart"));
+        notification->sendEvent();
+    }
+
+    // If user select ButtonVertical, we add (if needed) a button to titlebar
+    if (style == "ButtonVertical") {
+        KConfig _kwinConfig("kwinrc", KConfig::NoGlobals);
+        KConfigGroup kwinConfig(&_kwinConfig, "Style");
+        QString buttonsOnLeft = kwinConfig.readEntry("ButtonsOnLeft", "");
+        QString buttonsOnRight = kwinConfig.readEntry("ButtonsOnRight", "IMX");
+        qDebug() << buttonsOnLeft << buttonsOnRight;
+        if (!buttonsOnLeft.contains("N") && !buttonsOnRight.contains("N")) {
+            buttonsOnLeft = "N" + buttonsOnLeft;
+        }
+        kwinConfig.writeEntry("ButtonsOnLeft", buttonsOnLeft);
+        kwinConfig.writeEntry("CustomButtonPositions", "true");
+    }
+
+    args = QList<QVariant>() << "appmenu" << load;
+    method = QDBusMessage::createMethodCall("org.kde.kded",
+                                            "/kded",
+                                            "org.kde.kded",
+                                            "setModuleAutoloading");
+    method.setArguments(args);
+    QDBusConnection::sessionBus().asyncCall(method);
+
+    args = QList<QVariant>() << "appmenu";
+    if (load) {
+        method = QDBusMessage::createMethodCall("org.kde.kded",
+                                                "/kded",
+                                                "org.kde.kded",
+                                                "loadModule");
+    } else {
+        method = QDBusMessage::createMethodCall("org.kde.kded",
+                                                "/kded",
+                                                "org.kde.kded",
+                                                "unloadModule");
+    }
+    method.setArguments(args);
+    QDBusConnection::sessionBus().asyncCall(method);
+
     // Export the changes we made to qtrc, and update all qt-only
     // applications on the fly, ensuring that we still follow the user's
     // export fonts/colors settings.
@@ -487,6 +556,7 @@ void KCMStyle::defaults()
     // Effects
     fineTuningUi.comboToolbarIcons->setCurrentIndex(toolbarButtonIndex("TextBesideIcon"));
     fineTuningUi.comboSecondaryToolbarIcons->setCurrentIndex(toolbarButtonIndex("TextBesideIcon"));
+    fineTuningUi.comboMenubarStyle->setCurrentIndex(menuBarStyleIndex("InApplication"));
     fineTuningUi.cbIconsOnButtons->setChecked(true);
     fineTuningUi.cbIconsInMenus->setChecked(true);
     fineTuningUi.comboGraphicEffectsLevel->setCurrentIndex(fineTuningUi.comboGraphicEffectsLevel->findData(((int) KGlobalSettings::graphicEffectsLevelDefault())));
@@ -708,6 +778,33 @@ int KCMStyle::toolbarButtonIndex(const QString &text)
     return 0;
 }
 
+QString KCMStyle::menuBarStyleText(int index)
+{
+    switch (index) {
+        case 1:
+            return "ButtonVertical";
+        case 2:
+            return "TopMenuBar";
+        case 3:
+            return "Others";
+    }
+
+    return "InApplication";
+}
+
+int KCMStyle::menuBarStyleIndex(const QString &text)
+{
+    if (text == "ButtonVertical") {
+        return 1;
+    } else if (text == "TopMenuBar") {
+        return 2;
+    } else if (text == "Others") {
+        return 3;
+    }
+
+    return 0;
+}
+
 void KCMStyle::loadEffects( KConfig& config )
 {
     // KDE's Part via KConfig
@@ -718,6 +815,10 @@ void KCMStyle::loadEffects( KConfig& config )
     tbIcon = configGroup.readEntry("ToolButtonStyleOtherToolbars", "TextBesideIcon");
     fineTuningUi.comboSecondaryToolbarIcons->setCurrentIndex(toolbarButtonIndex(tbIcon));
 
+    configGroup = config.group("Appmenu Style");
+    QString menuBarStyle = configGroup.readEntry("Style", "InApplication");
+    fineTuningUi.comboMenubarStyle->setCurrentIndex(menuBarStyleIndex(menuBarStyle));
+
     configGroup = config.group("KDE");
     fineTuningUi.cbIconsOnButtons->setChecked(configGroup.readEntry("ShowIconsOnPushButtons", true));
     fineTuningUi.cbIconsInMenus->setChecked(configGroup.readEntry("ShowIconsInMenuItems", true));
diff --git a/kcontrol/style/kcmstyle.h b/kcontrol/style/kcmstyle.h
index cd1472f..9938915 100644
--- a/kcontrol/style/kcmstyle.h
+++ b/kcontrol/style/kcmstyle.h
@@ -89,6 +89,8 @@ private:
     QString currentStyle();
     static QString toolbarButtonText(int index);
     static int toolbarButtonIndex(const QString &text);
+    static QString menuBarStyleText(int index);
+    static int menuBarStyleIndex(const QString &text);
 
     bool m_bStyleDirty, m_bEffectsDirty;
     QHash <QString,StyleEntry*> styleEntries;
diff --git a/kwin/CMakeLists.txt b/kwin/CMakeLists.txt
index b341427..11c47b2 100644
--- a/kwin/CMakeLists.txt
+++ b/kwin/CMakeLists.txt
@@ -1,4 +1,5 @@
 ########### configure tests ###############
+INCLUDE(CMakeDependentOption)
 
 OPTION(KWIN_BUILD_DECORATIONS "Enable building of KWin decorations." ON)
 OPTION(KWIN_BUILD_OXYGEN "Enable building of default decoration Oxygen" ON)
@@ -7,6 +8,7 @@ OPTION(KWIN_MOBILE_EFFECTS "Only build effects relevant for mobile devices" OFF)
 OPTION(KWIN_BUILD_TABBOX "Enable building of KWin Tabbox functionality" ON)
 OPTION(KWIN_BUILD_SCREENEDGES "Enable building of KWin with screen edge support" ON)
 OPTION(KWIN_BUILD_SCRIPTING "Enable building of KWin with scripting support" ON)
+OPTION(KWIN_BUILD_KAPPMENU "Enable building of KWin with application menu support" ON)
 OPTION(KWIN_BUILD_XRENDER_COMPOSITING "Enable building of KWin with XRender Compositing support" ON)
 OPTION(KWIN_BUILD_OPENGL_1_COMPOSITING "Enable support for OpenGL 1.x, automatically disabled when building for OpenGL ES 2.0" ON)
 OPTION(KWIN_BUILD_ACTIVITIES "Enable building of KWin with kactivities support" ON)
@@ -30,6 +32,8 @@ if(KWIN_PLASMA_ACTIVE)
     set(KWIN_NAME "kwinactive")
 endif(KWIN_PLASMA_ACTIVE)
 
+cmake_dependent_option(KWIN_BUILD_KAPPMENU "Build without appmenu support" ON "KWIN_BUILD_DECORATIONS" FALSE)
+
 # KWIN_HAVE_XRENDER_COMPOSITING - whether XRender-based compositing support is available: may be disabled
 if( KWIN_BUILD_XRENDER_COMPOSITING )
     set( KWIN_HAVE_XRENDER_COMPOSITING 1 )
diff --git a/kwin/bridge.cpp b/kwin/bridge.cpp
index 78a1fe1..d54f059 100644
--- a/kwin/bridge.cpp
+++ b/kwin/bridge.cpp
@@ -113,6 +113,22 @@ void Bridge::showWindowMenu(const QRect &p)
     c->workspace()->showWindowMenu(p, c);
 }
 
+void Bridge::showApplicationMenu(const QPoint &p)
+{
+#ifdef KWIN_BUILD_KAPPMENU
+    c->showApplicationMenu(p);
+#endif
+}
+
+bool Bridge::menuAvailable() const
+{
+#ifdef KWIN_BUILD_KAPPMENU
+    return c->menuAvailable();
+#else
+    return false;
+#endif
+}
+
 void Bridge::performWindowOperation(WindowOperation op)
 {
     c->workspace()->performWindowOperation(c, op);
diff --git a/kwin/bridge.h b/kwin/bridge.h
index 64108e5..9b34fbd 100644
--- a/kwin/bridge.h
+++ b/kwin/bridge.h
@@ -53,6 +53,8 @@ public:
     virtual void processMousePressEvent(QMouseEvent*);
     virtual void showWindowMenu(const QPoint &);
     virtual void showWindowMenu(const QRect &);
+    virtual void showApplicationMenu(const QPoint &);
+    virtual bool menuAvailable() const;
     virtual void performWindowOperation(WindowOperation);
     virtual void setMask(const QRegion&, int);
     virtual bool isPreview() const;
diff --git a/kwin/client.cpp b/kwin/client.cpp
index 8aa072f..bff1309 100644
--- a/kwin/client.cpp
+++ b/kwin/client.cpp
@@ -26,10 +26,12 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include <QDateTime>
 #include <QProcess>
 #include <QPaintEngine>
+
 #ifdef KWIN_BUILD_SCRIPTING
 #include <QScriptEngine>
 #include <QScriptProgram>
 #endif
+
 #include <unistd.h>
 #include <kstandarddirs.h>
 #include <QWhatsThis>
@@ -133,6 +135,9 @@ Client::Client(Workspace* ws)
     , electricMaximizing(false)
     , activitiesDefined(false)
     , needsSessionInteract(false)
+#ifdef KWIN_BUILD_KAPPMENU
+    , m_menuAvailable(false)
+#endif
     , input_window(None)
 {
     // TODO: Do all as initialization
@@ -405,6 +410,12 @@ void Client::updateDecoration(bool check_workspace_pos, bool force)
     if (!noBorder()) {
         setMask(QRegion());  // Reset shape mask
         decoration = workspace()->createDecoration(bridge);
+#ifdef KWIN_BUILD_KAPPMENU
+        connect(this, SIGNAL(showRequest()), decoration, SIGNAL(showRequest()));
+        connect(this, SIGNAL(appMenuAvailable()), decoration, SIGNAL(appMenuAvailable()));
+        connect(this, SIGNAL(appMenuUnavailable()), decoration, SIGNAL(appMenuUnavailable()));
+        connect(this, SIGNAL(menuHidden()), decoration, SIGNAL(menuHidden()));
+#endif
         // TODO: Check decoration's minimum size?
         decoration->init();
         decoration->widget()->installEventFilter(this);
@@ -2401,6 +2412,25 @@ bool Client::isClient() const
     return true;
 }
 
+#ifdef KWIN_BUILD_KAPPMENU
+void Client::setAppMenuAvailable()
+{
+    m_menuAvailable = true;
+    emit appMenuAvailable();
+}
+
+void Client::setAppMenuUnavailable()
+{
+    m_menuAvailable = false;
+    emit appMenuUnavailable();
+}
+
+void Client::showApplicationMenu(const QPoint &p)
+{
+    workspace()->showApplicationMenu(p, window());
+}
+#endif
+
 NET::WindowType Client::windowType(bool direct, int supportedTypes) const
 {
     // TODO: does it make sense to cache the returned window type for SUPPORTED_MANAGED_WINDOW_TYPES_MASK?
diff --git a/kwin/client.h b/kwin/client.h
index ced58d7..51419b9 100644
--- a/kwin/client.h
+++ b/kwin/client.h
@@ -24,6 +24,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 #include <config-X11.h>
 
+#include "config-kwin.h"
+
 #include <QFrame>
 #include <QPixmap>
 #include <netwm.h>
@@ -634,6 +636,22 @@ public:
     void setSessionInteract(bool needed);
     virtual bool isClient() const;
 
+#ifdef KWIN_BUILD_KAPPMENU
+    // Used by workspace
+    void emitShowRequest() {
+        emit showRequest();
+    }
+    void emitMenuHidden() {
+        emit menuHidden();
+    }
+    void setAppMenuAvailable();
+    void setAppMenuUnavailable();
+    void showApplicationMenu(const QPoint&);
+    bool menuAvailable() {
+        return m_menuAvailable;
+    }
+#endif
+
 public slots:
     void closeWindow();
 
@@ -723,6 +741,24 @@ signals:
      * another group, but not when a Client gets added or removed to the Client's ClientGroup.
      **/
     void tabGroupChanged();
+
+    /**
+     * Emitted whenever the Client want to show it menu
+     */
+    void showRequest();
+    /**
+     * Emitted whenever the Client's menu is closed
+     */
+    void menuHidden();
+    /**
+     * Emitted whenever the Client's menu is available
+     **/
+    void appMenuAvailable();
+    /**
+     * Emitted whenever the Client's menu is unavailable
+     */
+    void appMenuUnavailable();
+
     /**
      * Emitted whenever the demands attention state changes.
      **/
@@ -952,6 +988,9 @@ private:
 
     bool needsSessionInteract;
 
+#ifdef KWIN_BUILD_KAPPMENU
+    bool m_menuAvailable;
+#endif
     Window input_window;
     QPoint input_offset;
 };
diff --git a/kwin/clients/oxygen/oxygenbutton.cpp b/kwin/clients/oxygen/oxygenbutton.cpp
index 28aa8b1..ee523fa 100644
--- a/kwin/clients/oxygen/oxygenbutton.cpp
+++ b/kwin/clients/oxygen/oxygenbutton.cpp
@@ -215,9 +215,11 @@ namespace Oxygen
     //___________________________________________________
     void Button::mouseReleaseEvent( QMouseEvent* event )
     {
-
-        _status &= ~Pressed;
-        parentUpdate();
+        if (_type != ButtonApplicationMenu)
+        {
+            _status &= ~Pressed;
+            parentUpdate();
+        }
 
         KCommonDecorationButton::mouseReleaseEvent( event );
     }
@@ -338,6 +340,12 @@ namespace Oxygen
             painter->translate(-1.5, -1.5);
             break;
 
+            case ButtonApplicationMenu:
+            painter->drawLine(QPointF(7.5, 7.5), QPointF(13.5, 7.5));
+            painter->drawLine(QPointF(7.5, 10.5), QPointF(13.5, 10.5));
+            painter->drawLine(QPointF(7.5, 13.5), QPointF(13.5, 13.5));
+            break;
+
             case ButtonMin:
             painter->drawLine(QPointF( 7.5, 9.5), QPointF(10.5,12.5));
             painter->drawLine(QPointF(10.5,12.5), QPointF(13.5, 9.5));
@@ -410,4 +418,10 @@ namespace Oxygen
         return;
     }
 
+    void Button::slotAppMenuHidden()
+    {
+        _status = Normal;
+        update();
+    }
+
 }
diff --git a/kwin/clients/oxygen/oxygenbutton.h b/kwin/clients/oxygen/oxygenbutton.h
index ea11717..06259ea 100644
--- a/kwin/clients/oxygen/oxygenbutton.h
+++ b/kwin/clients/oxygen/oxygenbutton.h
@@ -173,6 +173,9 @@ namespace Oxygen
 
         //@}
 
+        private slots:
+            void slotAppMenuHidden();
+
         private:
 
         //! parent client
diff --git a/kwin/clients/oxygen/oxygenclient.cpp b/kwin/clients/oxygen/oxygenclient.cpp
index 05e786e..4cd5006 100644
--- a/kwin/clients/oxygen/oxygenclient.cpp
+++ b/kwin/clients/oxygen/oxygenclient.cpp
@@ -212,7 +212,10 @@ namespace Oxygen
         switch (type) {
 
             case MenuButton:
-            return new Button(*this, i18n("Menu"), ButtonMenu);
+            return new Button(*this, i18n("Window Actions Menu"), ButtonMenu);
+
+            case AppMenuButton:
+            return new Button(*this, i18n("Application Menu"), ButtonApplicationMenu);
 
             case HelpButton:
             return new Button(*this, i18n("Help"), ButtonHelp);
diff --git a/kwin/clients/oxygen/oxygenfactory.cpp b/kwin/clients/oxygen/oxygenfactory.cpp
index 73d3ac7..4e2505b 100644
--- a/kwin/clients/oxygen/oxygenfactory.cpp
+++ b/kwin/clients/oxygen/oxygenfactory.cpp
@@ -144,6 +144,7 @@ namespace Oxygen
 
             // buttons
             case AbilityButtonMenu:
+            case AbilityButtonApplicationMenu:
             case AbilityButtonHelp:
             case AbilityButtonMinimize:
             case AbilityButtonMaximize:
diff --git a/kwin/clients/oxygen/oxygenfactory.h b/kwin/clients/oxygen/oxygenfactory.h
index b38b097..5026063 100644
--- a/kwin/clients/oxygen/oxygenfactory.h
+++ b/kwin/clients/oxygen/oxygenfactory.h
@@ -52,6 +52,7 @@ namespace Oxygen
         ButtonAbove,
         ButtonBelow,
         ButtonShade,
+        ButtonApplicationMenu,
         ButtonTypeCount,
 
         // Close only one tab
diff --git a/kwin/config-kwin.h.cmake b/kwin/config-kwin.h.cmake
index c48f569..e335488 100644
--- a/kwin/config-kwin.h.cmake
+++ b/kwin/config-kwin.h.cmake
@@ -3,6 +3,7 @@
 #cmakedefine KWIN_BUILD_DESKTOPCHANGEOSD 1
 #cmakedefine KWIN_BUILD_SCREENEDGES 1
 #cmakedefine KWIN_BUILD_SCRIPTING 1
+#cmakedefine KWIN_BUILD_KAPPMENU 1
 #cmakedefine KWIN_BUILD_ACTIVITIES 1
 #cmakedefine KWIN_BUILD_OXYGEN 1
 #define KWIN_NAME "${KWIN_NAME}"
diff --git a/kwin/kcmkwin/kwindecoration/buttons.cpp b/kwin/kcmkwin/kwindecoration/buttons.cpp
index 2d12244..a4ebf9e 100644
--- a/kwin/kcmkwin/kwindecoration/buttons.cpp
+++ b/kwin/kcmkwin/kwindecoration/buttons.cpp
@@ -31,6 +31,7 @@
 
 #include "buttons.h"
 #include "pixmaps.h"
+#include "config-kwin.h"
 
 #include <QApplication>
 #include <QPainter>
@@ -42,6 +43,11 @@
 #include <klocale.h>
 #include <kglobalsettings.h>
 
+#ifdef KWIN_BUILD_KAPPMENU
+#include <KConfigGroup>
+#include <KConfig>
+#endif
+
 #include <kdecorationfactory.h>
 
 
@@ -680,7 +686,19 @@ ButtonPositionWidget::ButtonPositionWidget(QWidget *parent)
 
     // insert all possible buttons into the source (backwards to keep the preferred order...)
     bool dummy;
-    m_supportedButtons = "MSHIAX_FBLR"; // support all buttons
+
+    m_supportedButtons = "MSHIAX_FBLR";
+#ifdef KWIN_BUILD_KAPPMENU
+    KConfig config("kdeglobals", KConfig::FullConfig);
+    KConfigGroup configGroup = config.group("Appmenu Style");
+    QString style = configGroup.readEntry("Style", "InApplication");
+
+    if (style == "ButtonVertical") {
+        m_supportedButtons = "MNSHIAX_FBLR"; // support all buttons
+        new ButtonSourceItem(m_buttonSource, getButton('N', dummy));
+    }
+#endif
+
     new ButtonSourceItem(m_buttonSource, getButton('R', dummy));
     new ButtonSourceItem(m_buttonSource, getButton('L', dummy));
     new ButtonSourceItem(m_buttonSource, getButton('B', dummy));
@@ -741,7 +759,13 @@ Button ButtonPositionWidget::getButton(QChar type, bool& success)
     } else if (type == 'M') {
         QBitmap bmp = QBitmap::fromData(QSize(menu_width, menu_height), menu_bits);
         bmp.createMaskFromColor(Qt::white);
-        return Button(i18n("Menu"), bmp, 'M', false, m_supportedButtons.contains('M'));
+        return Button(i18nc("Button showing window actions menu", "Window Menu"), bmp, 'M', false, m_supportedButtons.contains('M'));
+#ifdef KWIN_BUILD_KAPPMENU
+    } else if (type == 'N') {
+        QBitmap bmp = QBitmap::fromData(QSize(menu_width, menu_height), menu_bits);
+        bmp.createMaskFromColor(Qt::white);
+        return Button(i18nc("Button showing application menu imported from dbusmenu", "Application Menu"), bmp, 'N', false, m_supportedButtons.contains('N'));
+#endif
     } else if (type == '_') {
         QBitmap bmp = QBitmap::fromData(QSize(spacer_width, spacer_height), spacer_bits);
         bmp.createMaskFromColor(Qt::white);
diff --git a/kwin/kcmkwin/kwindecoration/preview.cpp b/kwin/kcmkwin/kwindecoration/preview.cpp
index 587df7c..0b19986 100644
--- a/kwin/kcmkwin/kwindecoration/preview.cpp
+++ b/kwin/kcmkwin/kwindecoration/preview.cpp
@@ -385,6 +385,15 @@ void KDecorationPreviewBridge::showWindowMenu(const QPoint &)
 {
 }
 
+void KDecorationPreviewBridge::showApplicationMenu(const QPoint &)
+{
+}
+
+bool KDecorationPreviewBridge::menuAvailable() const
+{
+    return false;
+}
+
 void KDecorationPreviewBridge::performWindowOperation(WindowOperation)
 {
 }
diff --git a/kwin/kcmkwin/kwindecoration/preview.h b/kwin/kcmkwin/kwindecoration/preview.h
index 52645fd..72cf7c1 100644
--- a/kwin/kcmkwin/kwindecoration/preview.h
+++ b/kwin/kcmkwin/kwindecoration/preview.h
@@ -95,6 +95,8 @@ public:
     virtual void processMousePressEvent(QMouseEvent*);
     virtual void showWindowMenu(const QRect &);
     virtual void showWindowMenu(const QPoint &);
+    virtual void showApplicationMenu(const QPoint &);
+    virtual bool menuAvailable() const;
     virtual void performWindowOperation(WindowOperation);
     virtual void setMask(const QRegion&, int);
     virtual bool isPreview() const;
diff --git a/kwin/libkdecorations/kcommondecoration.cpp b/kwin/libkdecorations/kcommondecoration.cpp
index 9f30c8d..31bf397 100644
--- a/kwin/libkdecorations/kcommondecoration.cpp
+++ b/kwin/libkdecorations/kcommondecoration.cpp
@@ -377,7 +377,7 @@ void KCommonDecoration::addButtons(ButtonContainer &btnContainer, const QString&
                 if (!m_button[MenuButton]) {
                     btn = createButton(MenuButton);
                     if (!btn) break;
-                    btn->setTipText(i18n("Menu"));
+                    btn->setTipText(i18nc("Button showing window actions menu", "Window Menu"));
                     btn->setRealizeButtons(Qt::LeftButton | Qt::RightButton);
                     connect(btn, SIGNAL(pressed()), SLOT(menuButtonPressed()));
                     connect(btn, SIGNAL(released()), this, SLOT(menuButtonReleased()));
@@ -388,6 +388,27 @@ void KCommonDecoration::addButtons(ButtonContainer &btnContainer, const QString&
                     m_button[MenuButton] = btn;
                 }
                 break;
+             case 'N': // Application Menu button
+                if (!m_button[AppMenuButton]) {
+                    btn = createButton(AppMenuButton);
+                    if (!btn) break;
+                    btn->setTipText(i18nc("Button showing application menu", "Application Menu"));
+                    btn->setRealizeButtons(Qt::LeftButton);
+                    connect(btn, SIGNAL(pressed()), SLOT(appMenuButtonPressed()));
+                    // Application want to show it menu
+                    connect(decoration(), SIGNAL(showRequest()), this, SLOT(appMenuButtonPressed()), Qt::UniqueConnection);
+                    // Wait for menu to become available before displaying any button
+                    connect(decoration(), SIGNAL(appMenuAvailable()), this, SLOT(slotAppMenuAvailable()), Qt::UniqueConnection);
+                    // On Kded module shutdown, hide application menu button
+                    connect(decoration(), SIGNAL(appMenuUnavailable()), this, SLOT(slotAppMenuUnavailable()), Qt::UniqueConnection);
+                    // Application menu button may need to be modified on this signal
+                    connect(decoration(), SIGNAL(menuHidden()), btn, SLOT(slotAppMenuHidden()), Qt::UniqueConnection);
+
+                    // fix double deletion, see objDestroyed()
+                    connect(btn, SIGNAL(destroyed(QObject*)), this, SLOT(objDestroyed(QObject*)));
+                    m_button[AppMenuButton] = btn;
+                }
+                break;
             case 'S': // OnAllDesktops button
                 if (!m_button[OnAllDesktopsButton]) {
                     btn = createButton(OnAllDesktopsButton);
@@ -516,7 +537,13 @@ void KCommonDecoration::addButtons(ButtonContainer &btnContainer, const QString&
             if (btn) {
                 btn->setLeft(isLeft);
                 btn->setSize(QSize(layoutMetric(LM_ButtonWidth, true, btn), layoutMetric(LM_ButtonHeight, true, btn)));
-                btn->show();
+                // will be shown later on window registration
+                if (btn->type() == AppMenuButton && !wrapper->menuAvailable()) {
+                    btn->hide();
+                } else {
+                    btn->show();
+                }
+
                 btnContainer.append(btn);
             }
 
@@ -532,7 +559,7 @@ void KCommonDecoration::calcHiddenButtons()
     btnHideLastWidth = width();
 
     //Hide buttons in the following order:
-    KCommonDecorationButton* btnArray[] = { m_button[HelpButton], m_button[ShadeButton], m_button[BelowButton],
+    KCommonDecorationButton* btnArray[] = { m_button[HelpButton], m_button[AppMenuButton], m_button[ShadeButton], m_button[BelowButton],
                                             m_button[AboveButton], m_button[OnAllDesktopsButton], m_button[MaxButton],
                                             m_button[MinButton], m_button[MenuButton], m_button[CloseButton]
                                           };
@@ -557,7 +584,8 @@ void KCommonDecoration::calcHiddenButtons()
             if (! btnArray[i]->isHidden())
                 break; // all buttons shown...
 
-            btnArray[i]->show();
+            if (btnArray[i]->type() != AppMenuButton || wrapper->menuAvailable())
+                btnArray[i]->show();
         }
     }
 }
@@ -755,6 +783,34 @@ void KCommonDecoration::doShowWindowMenu()
     showWindowMenu(QRect(menutop, menubottom));
 }
 
+
+void KCommonDecoration::appMenuButtonPressed()
+{
+    QRect menuRect = m_button[AppMenuButton]->rect();
+    wrapper->showApplicationMenu(m_button[AppMenuButton]->mapToGlobal(menuRect.bottomLeft()));
+
+    KDecorationFactory* f = factory();
+    if (!f->exists(decoration()))   // 'this' was deleted
+        return;
+    m_button[AppMenuButton]->setDown(false);
+}
+
+void KCommonDecoration::slotAppMenuAvailable()
+{
+    if (m_button[AppMenuButton]) {
+        m_button[AppMenuButton]->show();
+        updateLayout();
+    }
+}
+
+void KCommonDecoration::slotAppMenuUnavailable()
+{
+    if (m_button[AppMenuButton]) {
+        m_button[AppMenuButton]->hide();
+        updateLayout();
+    }
+}
+
 void KCommonDecoration::resizeEvent(QResizeEvent */*e*/)
 {
     if (decorationBehaviour(DB_ButtonHide))
diff --git a/kwin/libkdecorations/kcommondecoration.h b/kwin/libkdecorations/kcommondecoration.h
index 97bdfff..6919f29 100644
--- a/kwin/libkdecorations/kcommondecoration.h
+++ b/kwin/libkdecorations/kcommondecoration.h
@@ -44,6 +44,7 @@ enum ButtonType {
     AboveButton,
     BelowButton,
     ShadeButton,
+    AppMenuButton,
     NumButtons,
     ItemCloseButton = 100, // Close only one tab
     ItemMenuButton // shows the window menu for one tab
@@ -274,6 +275,9 @@ public Q_SLOTS:
     void slotKeepBelow();
     void menuButtonPressed();
     void menuButtonReleased();
+    void appMenuButtonPressed();
+    void slotAppMenuAvailable();
+    void slotAppMenuUnavailable();
 public:
     virtual Position mousePosition(const QPoint &point) const;
 
diff --git a/kwin/libkdecorations/kdecoration.cpp b/kwin/libkdecorations/kdecoration.cpp
index 125d6c8..505824f 100644
--- a/kwin/libkdecorations/kdecoration.cpp
+++ b/kwin/libkdecorations/kdecoration.cpp
@@ -38,6 +38,7 @@ DEALINGS IN THE SOFTWARE.
 #include "kdecorationfactory.h"
 #include "kdecorationbridge.h"
 
+
 /*
 
 Extending KDecoration:
@@ -222,6 +223,16 @@ void KDecoration::showWindowMenu(QPoint pos)
     bridge_->showWindowMenu(pos);
 }
 
+void KDecoration::showApplicationMenu(const QPoint &p)
+{
+    bridge_->showApplicationMenu(p);
+}
+
+bool KDecoration::menuAvailable() const
+{
+    return bridge_->menuAvailable();
+}
+
 void KDecoration::performWindowOperation(WindowOperation op)
 {
     bridge_->performWindowOperation(op);
diff --git a/kwin/libkdecorations/kdecoration.h b/kwin/libkdecorations/kdecoration.h
index aefa91f..2e15674 100644
--- a/kwin/libkdecorations/kdecoration.h
+++ b/kwin/libkdecorations/kdecoration.h
@@ -188,7 +188,7 @@ public:
         AbilityAnnounceButtons = 0, ///< decoration supports AbilityButton* values (always use)
         AbilityAnnounceColors = 1, ///< decoration supports AbilityColor* values (always use), @deprecated @todo remove KDE5
         // buttons
-        AbilityButtonMenu = 1000,   ///< decoration supports the menu button
+        AbilityButtonMenu = 1000,   ///< decoration supports the window menu button
         AbilityButtonOnAllDesktops = 1001, ///< decoration supports the on all desktops button
         AbilityButtonSpacer = 1002, ///< decoration supports inserting spacers between buttons
         AbilityButtonHelp = 1003,   ///< decoration supports what's this help button
@@ -199,6 +199,7 @@ public:
         AbilityButtonBelowOthers = 1008, ///< decoration supports a below button
         AbilityButtonShade = 1009, ///< decoration supports a shade button
         AbilityButtonResize = 1010, ///< decoration supports a resize button
+        AbilityButtonApplicationMenu = 1011,   ///< decoration supports the application menu button
         // colors
         AbilityColorTitleBack = 2000, ///< decoration supports titlebar background color, @deprecated @todo remove KDE5
         ABILITYCOLOR_FIRST = AbilityColorTitleBack, ///< @internal, @deprecated @todo remove KDE5
@@ -354,7 +355,8 @@ public:
      * If customButtonPositions() returns true, titleButtonsLeft
      * returns which buttons should be on the left side of the titlebar from left
      * to right. Characters in the returned string have this meaning :
-     * @li 'M' menu button
+     * @li 'N' application menu button
+     * @li 'M' window menu button
      * @li 'S' on_all_desktops button
      * @li 'H' quickhelp button
      * @li 'I' minimize ( iconify ) button
@@ -624,6 +626,14 @@ public:
      */
     void showWindowMenu(QPoint pos);
     /**
+     * show application menu at p
+     */
+    void showApplicationMenu(const QPoint& p);
+    /**
+     * Returns @a true if menu available for client
+     */
+    bool menuAvailable() const;
+    /**
      * This function performs the given window operation. This function may destroy
      * the current decoration object, just like showWindowMenu().
      */
@@ -809,6 +819,24 @@ Q_SIGNALS:
     void keepBelowChanged(bool);
 
     /**
+     * This signal is emitted whenever application menu is closed
+     * Application menu button may need to be modified on this signal
+     */
+    void menuHidden();
+    /**
+     * This signal is emitted whenever application want to show it menu
+     */
+    void showRequest();
+    /**
+     * This signal is emitted whenever application menu becomes available
+     */
+    void appMenuAvailable();
+    /**
+     * This signal is emitted whenever application menu becomes unavailable
+     */
+    void appMenuUnavailable();
+
+    /**
      * This signal is emitted whenever the decoration changes it's alpha enabled
      * change. Only relevant in case the decoration provides AbilityAnnounceAlphaChannel.
      *
diff --git a/kwin/libkdecorations/kdecorationbridge.h b/kwin/libkdecorations/kdecorationbridge.h
index b7d0619..2cb36c9 100644
--- a/kwin/libkdecorations/kdecorationbridge.h
+++ b/kwin/libkdecorations/kdecorationbridge.h
@@ -65,6 +65,8 @@ public:
     virtual void processMousePressEvent(QMouseEvent*) = 0;
     virtual void showWindowMenu(const QRect &) = 0;
     virtual void showWindowMenu(const QPoint &) = 0;
+    virtual void showApplicationMenu(const QPoint&) = 0;
+    virtual bool menuAvailable() const = 0;
     virtual void performWindowOperation(WindowOperation) = 0;
     virtual void setMask(const QRegion&, int) = 0;
     virtual bool isPreview() const = 0;
diff --git a/kwin/useractions.cpp b/kwin/useractions.cpp
index 8fcdba2..7ce01b0 100755
--- a/kwin/useractions.cpp
+++ b/kwin/useractions.cpp
@@ -73,9 +73,21 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "tabbox.h"
 #endif
 
+#ifdef KWIN_BUILD_KAPPMENU
+#include <QDBusMessage>
+#include <QDBusConnection>
+#include <QDBusPendingCall>
+#endif
+
 namespace KWin
 {
 
+#ifdef KWIN_BUILD_KAPPMENU
+static const char *KDED_SERVICE = "org.kde.kded";
+static const char *KDED_APPMENU_PATH = "/modules/appmenu";
+static const char *KDED_INTERFACE = "org.kde.kded";
+#endif
+
 UserActionsMenu::UserActionsMenu(QObject *parent)
     : QObject(parent)
     , m_menu(NULL)
@@ -1231,6 +1243,14 @@ void Workspace::showWindowMenuAt(unsigned long, int, int)
     slotWindowOperations();
 }
 
+void Workspace::showApplicationMenu(const QPoint &p, const WId id)
+{
+    QList<QVariant> args = QList<QVariant>() << p.x() << p.y() << qulonglong(id);
+    QDBusMessage method = QDBusMessage::createMethodCall(KDED_SERVICE, KDED_APPMENU_PATH, KDED_INTERFACE, "showMenu");
+    method.setArguments(args);
+    QDBusConnection::sessionBus().asyncCall(method);
+}
+
 void Workspace::slotActivateAttentionWindow()
 {
     if (attention_chain.count() > 0)
diff --git a/kwin/workspace.cpp b/kwin/workspace.cpp
index 5983d0b..5e5bd7b 100644
--- a/kwin/workspace.cpp
+++ b/kwin/workspace.cpp
@@ -83,6 +83,12 @@ namespace KWin
 extern int screen_number;
 static const int KWIN_MAX_NUMBER_DESKTOPS = 20;
 
+#ifdef KWIN_BUILD_KAPPMENU
+static const char *KDED_SERVICE = "org.kde.kded";
+static const char *KDED_APPMENU_PATH = "/modules/appmenu";
+static const char *KDED_INTERFACE = "org.kde.kded";
+#endif
+
 Workspace* Workspace::_self = 0;
 
 //-----------------------------------------------------------------------------
@@ -148,6 +154,18 @@ Workspace::Workspace(bool restore)
     // If KWin was already running it saved its configuration after loosing the selection -> Reread
     QFuture<void> reparseConfigFuture = QtConcurrent::run(options, &Options::reparseConfiguration);
 
+#ifdef KWIN_BUILD_KAPPMENU
+    QDBusConnection dbus = QDBusConnection::sessionBus();
+    dbus.connect(KDED_SERVICE, KDED_APPMENU_PATH, KDED_INTERFACE, "showRequest",
+                 this, SLOT(slotShowRequest(qulonglong)));
+    dbus.connect(KDED_SERVICE, KDED_APPMENU_PATH, KDED_INTERFACE, "menuAvailable",
+                 this, SLOT(slotMenuAvailable(qulonglong)));
+    dbus.connect(KDED_SERVICE, KDED_APPMENU_PATH, KDED_INTERFACE, "menuHidden",
+                 this, SLOT(slotMenuHidden(qulonglong)));
+    dbus.connect(KDED_SERVICE, KDED_APPMENU_PATH, KDED_INTERFACE, "clearMenus",
+                 this, SLOT(slotClearMenus()));
+#endif
+
     // Initialize desktop grid array
     desktopGrid_[0] = 0;
     desktopGrid_[1] = 0;
@@ -622,6 +640,10 @@ void Workspace::addClient(Client* c, allowed_t)
     if (tabBox()->isDisplayed())
         tab_box->reset(true);
 #endif
+#ifdef KWIN_BUILD_KAPPMENU
+        if (m_windowsMenu.removeOne(c->window()))
+            c->setAppMenuAvailable();
+#endif
 }
 
 void Workspace::addUnmanaged(Unmanaged* c, allowed_t)
@@ -918,7 +940,34 @@ void Workspace::slotReloadConfig()
 {
     reconfigure();
 }
+#ifdef KWIN_BUILD_KAPPMENU
+void Workspace::slotShowRequest(qulonglong wid)
+{
+    if (Client *c = findClient(WindowMatchPredicate(wid)))
+        c->emitShowRequest();
+}
+
+void Workspace::slotMenuAvailable(qulonglong wid)
+{
+    if (Client *c = findClient(WindowMatchPredicate(wid)))
+        c->setAppMenuAvailable();
+    else
+        m_windowsMenu.append(wid);
+}
+
+void Workspace::slotMenuHidden(qulonglong wid)
+{
+    if (Client *c = findClient(WindowMatchPredicate(wid)))
+        c->emitMenuHidden();
+}
 
+void Workspace::slotClearMenus()
+{
+    foreach (Client *c, clients) {
+       c->setAppMenuUnavailable();
+    }
+}
+#endif
 void Workspace::reconfigure()
 {
     reconfigureTimer.start(200);
diff --git a/kwin/workspace.h b/kwin/workspace.h
index d0f6f9b..efe9a63 100644
--- a/kwin/workspace.h
+++ b/kwin/workspace.h
@@ -419,6 +419,8 @@ public:
         return m_userActionsMenu;
     }
 
+    void showApplicationMenu(const QPoint &, const WId);
+
     void updateMinimizedOfTransients(Client*);
     void updateOnAllDesktopsOfTransients(Client*);
     void updateOnAllActivitiesOfTransients(Client*);
@@ -625,6 +627,12 @@ private slots:
     void writeWindowRules();
     void slotBlockShortcuts(int data);
     void slotReloadConfig();
+#ifdef KWIN_BUILD_KAPPMENU
+    void slotShowRequest(qulonglong wid);
+    void slotMenuAvailable(qulonglong wid);
+    void slotMenuHidden(qulonglong wid);
+    void slotClearMenus();
+#endif
     void resetCursorPosTime();
     void updateCurrentActivity(const QString &new_activity);
     void slotActivityRemoved(const QString &activity);
@@ -852,6 +860,11 @@ private:
     QSlider* transSlider;
     QPushButton* transButton;
 
+#ifdef KWIN_BUILD_KAPPMENU
+    //used for menu available before window is mapped
+    QList<WId> m_windowsMenu;
+#endif
+
     Scripting *m_scripting;
 
 private:


More information about the kde-doc-english mailing list