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:
