Commit 1d568009 authored by David Redondo's avatar David Redondo 🏎
Browse files

Port to KGlobalAccel

Summary:
Port to KGlobalAccel. This enables us to drop khotkeys and display a configuration
dialog inside the application. The update script correctly sets the defaults and migrates
possibly user changed shortcuts. I didn't know where to place the KActionCollection
and put it in SpectacleConfig for the moment.

FEATURE: 388592
FIXED-IN: 19.08.0

Test Plan: Shortcuts should work as before.

Reviewers: #spectacle, davidedmundson, ngraham

Reviewed By: #spectacle, davidedmundson, ngraham

Subscribers: asturmlechner, ngraham, #spectacle

Tags: #spectacle

Maniphest Tasks: T10519

Differential Revision: https://phabricator.kde.org/D19310
parent ab896620
......@@ -61,6 +61,8 @@ find_package(
WindowSystem
DocTools
NewStuff
GlobalAccel
XmlGui
)
# optional components
......
#! /bin/sh
#This file outputs in a separate line each file with a .desktop syntax
#that needs to be translated but has a non .desktop extension
find -name \*.khotkeys -print
......@@ -5,17 +5,28 @@ install(
DESTINATION ${XDG_APPS_INSTALL_DIR}
)
install( DIRECTORY DESTINATION "${KDE_INSTALL_FULL_DATAROOTDIR}/kglobalaccel" )
install(
FILES spectacle.notifyrc
DESTINATION ${KNOTIFYRC_INSTALL_DIR}
CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink \"${KDE_INSTALL_FULL_APPDIR}/org.kde.dolphin.desktop\" \"\$ENV{DESTDIR}${KDE_INSTALL_FULL_DATAROOTDIR}/kglobalaccel/org.kde.dolphin.desktop\")"
)
install(
FILES spectacle.khotkeys
DESTINATION ${DATA_INSTALL_DIR}/khotkeys
FILES spectacle.notifyrc
DESTINATION ${KNOTIFYRC_INSTALL_DIR}
)
install(
FILES org.kde.spectacle.appdata.xml
DESTINATION ${KDE_INSTALL_METAINFODIR}
)
install(
FILES spectacle_shortcuts.upd
DESTINATION ${KDE_INSTALL_KCONFUPDATEDIR}
)
add_executable(spectacle-migrate-shortcuts MigrateShortcuts.cpp)
target_link_libraries(spectacle-migrate-shortcuts Qt5::DBus KF5::GlobalAccel KF5::ConfigCore KF5::XmlGui KF5::I18n)
install(
TARGETS spectacle-migrate-shortcuts
DESTINATION ${KDE_INSTALL_LIBDIR}/kconf_update_bin
)
#include <iostream>
#include <QCoreApplication>
#include <QDBusInterface>
#include <QString>
#include <QDebug>
#include <KActionCollection>
#include <KConfig>
#include <KConfigGroup>
#include <KGlobalAccel>
#include <KLocalizedString>
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
QDBusInterface khotkeys(QStringLiteral("org.kde.kded5"), QStringLiteral("/modules/khotkeys"),
QStringLiteral("org.kde.khotkeys"));
KConfig khotkeysrc(QStringLiteral("khotkeysrc"), KConfig::SimpleConfig);
int dataCount = KConfigGroup(&khotkeysrc, "Data").readEntry("DataCount", 0);
bool found_spectacle = false;
int spectacleIndex;
for (int i = 1; i <= dataCount; ++i) {
if( KConfigGroup(&khotkeysrc, QStringLiteral("Data_%1").arg(i)).readEntry("ImportId", QString()) == QStringLiteral("spectacle")) {
found_spectacle = true;
spectacleIndex = i;
break;
}
}
QList<QKeySequence> launchKey;
QList<QKeySequence> fullScreenKey;
QList<QKeySequence> regionKey;
QList<QKeySequence> activeWindowKey;
QStringList ids;
if (found_spectacle) {
for (int i = 1; i <= 4; ++i) {
QString groupName = QStringLiteral("Data_%1_%2").arg(spectacleIndex).arg(i);
QString method = KConfigGroup(&khotkeysrc, groupName + QStringLiteral("Actions0")).readEntry("Call");
QString id = KConfigGroup(&khotkeysrc, groupName + QStringLiteral("Triggers0")).readEntry("Uuid");
QList<QKeySequence> shortcut = KGlobalAccel::self()->globalShortcut(QStringLiteral("khotkeys"), id);
ids.append(id);
/* Name and Comment field are translated but we can find out which action is which by looking at the called
* D-Bus Method */
if (method == QStringLiteral("StartAgent")) {
launchKey = shortcut;
} else if (method == QStringLiteral("FullScreen")) {
fullScreenKey = shortcut;
} else if (method == QStringLiteral("ActiveWindow")) {
activeWindowKey = shortcut;
} else if (method == QStringLiteral("RectangularRegion")) {
regionKey = shortcut;
}
// Delete the groups from khotkeysrc
khotkeysrc.deleteGroup(groupName);
khotkeysrc.deleteGroup(groupName + QStringLiteral("Actions"));
khotkeysrc.deleteGroup(groupName + QStringLiteral("Actions0"));
khotkeysrc.deleteGroup(groupName + QStringLiteral("Conditions"));
khotkeysrc.deleteGroup(groupName + QStringLiteral("Triggers"));
khotkeysrc.deleteGroup(groupName + QStringLiteral("Triggers0"));
}
khotkeysrc.deleteGroup(QStringLiteral("Data_%1").arg(spectacleIndex));
khotkeysrc.deleteGroup(QStringLiteral("Data_%1Conditions").arg(spectacleIndex));
khotkeysrc.sync();
}
QDBusInterface kglobalaccel(QStringLiteral("org.kde.kglobalaccel"), QStringLiteral("/kglobalaccel"),
QStringLiteral("org.kde.KGlobalAccel"));
// Unregister the khotkeysActions from globalAccel, removeAll didn't Work, so using D-Bus
for(const QString &action : ids) {
kglobalaccel.call(QStringLiteral("unregister"), QStringLiteral("khotkeys"), action);
}
//Setup the default Shortcuts
KActionCollection shortCutActions(static_cast<QObject*>(nullptr));
shortCutActions.setComponentName(QStringLiteral("org.kde.spectacle.desktop"));
shortCutActions.setComponentDisplayName(QStringLiteral("Spectacle"));
{
QAction *action = new QAction(i18n("Launch Spectacle"));
action->setObjectName(QStringLiteral("_launch"));
shortCutActions.addAction(action->objectName(), action);
}
{
QAction *action = new QAction(i18n("Capture Entire Desktop"));
action->setObjectName(QStringLiteral("FullScreenScreenShot"));
shortCutActions.addAction(action->objectName(), action);
}
{
QAction *action = new QAction(i18n("Capture Current Monitor"));
action->setObjectName(QStringLiteral("CurrentMonitorScreenShot"));
shortCutActions.addAction(action->objectName(), action);
}
{
QAction *action = new QAction(i18n("Capture Active Window"));
action->setObjectName(QStringLiteral("ActiveWindowScreenShot"));
shortCutActions.addAction(action->objectName(), action);
}
{
QAction *action = new QAction(i18n("Capture Rectangular Region"));
action->setObjectName(QStringLiteral("RectangularRegionScreenShot"));
shortCutActions.addAction(action->objectName(), action);
}
QAction* openAction = shortCutActions.action(QStringLiteral("_launch"));
KGlobalAccel::self()->setDefaultShortcut(openAction, {Qt::Key_Print});
QAction* fullScreenAction = shortCutActions.action(QStringLiteral("FullScreenScreenShot"));
KGlobalAccel::self()->setDefaultShortcut(fullScreenAction, {Qt::SHIFT + Qt::Key_Print});
QAction* currentScreenAction = shortCutActions.action(QStringLiteral("CurrentMonitorScreenShot"));
QAction* activeWindowAction = shortCutActions.action(QStringLiteral("ActiveWindowScreenShot"));
KGlobalAccel::self()->setDefaultShortcut(activeWindowAction, {Qt::META + Qt::Key_Print});
QAction* regionAction = shortCutActions.action(QStringLiteral("RectangularRegionScreenShot"));
KGlobalAccel::self()->setDefaultShortcut(regionAction, {Qt::META + Qt::SHIFT + Qt::Key_Print});
// Finally reinstate the old shortcuts
if (found_spectacle) {
KGlobalAccel::self()->setShortcut(openAction, launchKey, KGlobalAccel::NoAutoloading);
KGlobalAccel::self()->setShortcut(fullScreenAction, fullScreenKey, KGlobalAccel::NoAutoloading);
KGlobalAccel::self()->setShortcut(activeWindowAction, activeWindowKey, KGlobalAccel::NoAutoloading);
KGlobalAccel::self()->setShortcut(regionAction, regionKey, KGlobalAccel::NoAutoloading);
}
}
......@@ -121,6 +121,7 @@ StartupNotify=false
Actions=FullScreenScreenShot;CurrentMonitorScreenShot;ActiveWindowScreenShot;RectangularRegionScreenShot;
X-DBUS-StartupType=Multi
X-DBUS-ServiceName=org.kde.Spectacle
X-KDE-Shortcuts=Print
[Desktop Action FullScreenScreenShot]
Name=Capture Entire Desktop
......@@ -163,6 +164,7 @@ Name[x-test]=xxCapture Entire Desktopxx
Name[zh_CN]=抓取整个桌面
Name[zh_TW]=擷取整個桌面
Exec=qdbus org.kde.Spectacle / FullScreen false
X-KDE-Shortcuts=Shift+Print
[Desktop Action CurrentMonitorScreenShot]
Name=Capture Current Monitor
......@@ -206,6 +208,7 @@ Name[zh_CN]=抓取当前显示器
Name[zh_TW]=擷取目前螢幕
Exec=qdbus org.kde.Spectacle / CurrentScreen false
[Desktop Action ActiveWindowScreenShot]
Name=Capture Active Window
Name[ar]=التقط النّافذة النّشطة
......@@ -247,6 +250,7 @@ Name[x-test]=xxCapture Active Windowxx
Name[zh_CN]=抓取当前活动窗口
Name[zh_TW]=擷取作用中的視窗
Exec=qdbus org.kde.Spectacle / ActiveWindow true false
X-KDE-Shortcuts=Meta+Print
[Desktop Action RectangularRegionScreenShot]
Name=Capture Rectangular Region
......@@ -289,3 +293,4 @@ Name[x-test]=xxCapture Rectangular Regionxx
Name[zh_CN]=抓取方形区域
Name[zh_TW]=擷取矩形區域
Exec=qdbus org.kde.Spectacle / RectangularRegion true
X-KDE-Shortcuts=Meta+Shift+Print
This diff is collapsed.
Version=5
Id=spectacle-migrate-shortcuts
File=khotkeysrc
Script=spectacle-migrate-shortcuts
......@@ -38,6 +38,7 @@ set(
Gui/SettingsDialog/SettingsPage.cpp
Gui/SettingsDialog/SaveOptionsPage.cpp
Gui/SettingsDialog/GeneralOptionsPage.cpp
Gui/SettingsDialog/ShortcutsOptionsPage.cpp
QuickEditor/QuickEditor.cpp
)
......@@ -80,6 +81,8 @@ target_link_libraries(
KF5::KIOWidgets
KF5::WindowSystem
KF5::NewStuff
KF5::GlobalAccel
KF5::XmlGui
)
if(XCB_FOUND)
......
......@@ -21,8 +21,10 @@
#include "GeneralOptionsPage.h"
#include "SaveOptionsPage.h"
#include "ShortcutsOptionsPage.h"
#include <KLocalizedString>
#include <KShortcutWidget>
#include <QIcon>
#include <QMessageBox>
......@@ -53,6 +55,12 @@ void SettingsDialog::initPages()
addPage(saveOptions);
mPages.insert(saveOptions);
KPageWidgetItem *shortcutOptions = new KPageWidgetItem(new ShortcutsOptionsPage(this), i18n("Shortcuts"));
shortcutOptions->setHeader(i18n("Shortcuts"));
shortcutOptions->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-keyboard")));
addPage(shortcutOptions);
mPages.insert(shortcutOptions);
connect(this, &SettingsDialog::currentPageChanged, this, &SettingsDialog::onPageChanged);
}
......
#include "ShortcutsOptionsPage.h"
#include "SpectacleConfig.h"
#include <KLocalizedString>
#include <KShortcutsEditor>
#include <QButtonGroup>
#include <QRadioButton>
#include <QVBoxLayout>
ShortcutsOptionsPage::ShortcutsOptionsPage(QWidget* parent) : SettingsPage(parent)
{
QVBoxLayout *mainLayout = new QVBoxLayout(this);
setLayout(mainLayout);
mEditor = new KShortcutsEditor(SpectacleConfig::instance()->shortCutActions, this, KShortcutsEditor::ActionType::GlobalAction);
mainLayout->addWidget(mEditor);
connect(mEditor, &KShortcutsEditor::keyChange, this, &ShortcutsOptionsPage::markDirty);
}
ShortcutsOptionsPage::~ShortcutsOptionsPage()
{
mEditor->undoChanges();
}
void ShortcutsOptionsPage::resetChanges()
{
mEditor->undoChanges();
mChangesMade = false;
}
void ShortcutsOptionsPage::saveChanges()
{
mEditor->commit();
mChangesMade = false;
}
void ShortcutsOptionsPage::markDirty()
{
mChangesMade = true;
}
#ifndef SHORTCUTSOPTIONSPAGE_H
#define SHORTCUTSOPTIONSPAGE_H
#include "SettingsPage.h"
class KShortcutsEditor;
class ShortcutsOptionsPage : public SettingsPage
{
Q_OBJECT
public:
explicit ShortcutsOptionsPage ( QWidget* parent );
~ShortcutsOptionsPage();
public Q_SLOTS:
void saveChanges() override;
void resetChanges() override;
private Q_SLOTS:
void markDirty();
private:
KShortcutsEditor* mEditor;
};
#endif // SHORTCUTSOPTIONSPAGE_H
......@@ -19,14 +19,55 @@
#include "SpectacleConfig.h"
#include <KLocalizedString>
#include <KWindowSystem>
#include <KGlobalAccel>
#include <QDebug>
SpectacleConfig::SpectacleConfig(QObject *parent) :
QObject(parent)
{
mConfig = KSharedConfig::openConfig(QStringLiteral("spectaclerc"));
mGeneralConfig = KConfigGroup(mConfig, "General");
mGuiConfig = KConfigGroup(mConfig, "GuiConfig");
shortCutActions = new KActionCollection(this);
//everything here is named to match the jumplist actions in our .desktop file
shortCutActions->setComponentName(QStringLiteral("org.kde.spectacle.desktop"));
//qdbus org.kde.kglobalaccel /component/org_kde_spectacle_desktop org.kde.kglobalaccel.Component.shortcutNames
// ActiveWindowScreenShot
// CurrentMonitorScreenShot
// RectangularRegionScreenShot
// FullScreenScreenShot
// _launch
{
QAction *action = new QAction(i18n("Launch Spectacle"));
action->setObjectName(QStringLiteral("_launch"));
shortCutActions->addAction(action->objectName(), action);
}
{
QAction *action = new QAction(i18n("Capture Entire Desktop"));
action->setObjectName(QStringLiteral("FullScreenScreenShot"));
shortCutActions->addAction(action->objectName(), action);
}
{
QAction *action = new QAction(i18n("Capture Current Monitor"));
action->setObjectName(QStringLiteral("CurrentMonitorScreenShot"));
shortCutActions->addAction(action->objectName(), action);
}
{
QAction *action = new QAction(i18n("Capture Active Window"));
action->setObjectName(QStringLiteral("ActiveWindowScreenShot"));
shortCutActions->addAction(action->objectName(), action);
}
{
QAction *action = new QAction(i18n("Capture Rectangular Region"));
action->setObjectName(QStringLiteral("RectangularRegionScreenShot"));
shortCutActions->addAction(action->objectName(), action);
}
}
SpectacleConfig::~SpectacleConfig()
......
......@@ -20,10 +20,12 @@
#ifndef SPECTACLECONFIG_H
#define SPECTACLECONFIG_H
#include <QAction>
#include <QObject>
#include <QUrl>
#include <QRect>
#include <KActionCollection>
#include <KSharedConfig>
#include <KConfigGroup>
......@@ -54,6 +56,8 @@ class SpectacleConfig : public QObject
FocusWindow
};
KActionCollection* shortCutActions;
private:
explicit SpectacleConfig(QObject *parent = nullptr);
......
......@@ -22,6 +22,7 @@
#include "Config.h"
#include <KGlobalAccel>
#include <KLocalizedString>
#include <KMessageBox>
#include <KNotification>
......@@ -33,6 +34,7 @@
#include <QDebug>
#include <QDir>
#include <QDrag>
#include <QKeySequence>
#include <QMimeData>
#include <QProcess>
#include <QTimer>
......@@ -105,6 +107,24 @@ SpectacleCore::SpectacleCore(StartMode theStartMode,
initGui(lGuiConfig.readEntry("includePointer", true), lGuiConfig.readEntry("includeDecorations", true));
break;
}
setUpShortcuts();
}
void SpectacleCore::setUpShortcuts()
{
SpectacleConfig* config = SpectacleConfig::instance();
QAction* openAction = config->shortCutActions->action(QStringLiteral("_launch"));
KGlobalAccel::self()->setGlobalShortcut(openAction, Qt::Key_Print);
QAction* fullScreenAction = config->shortCutActions->action(QStringLiteral("FullScreenScreenShot"));
KGlobalAccel::self()->setGlobalShortcut(fullScreenAction, Qt::SHIFT + Qt::Key_Print);
QAction* activeWindowAction = config->shortCutActions->action(QStringLiteral("ActiveWindowScreenShot"));
KGlobalAccel::self()->setGlobalShortcut(activeWindowAction, Qt::META + Qt::Key_Print);
QAction* regionAction = config->shortCutActions->action(QStringLiteral("RectangularRegionScreenShot"));
KGlobalAccel::self()->setGlobalShortcut(regionAction, Qt::META + Qt::SHIFT + Qt::Key_Print);
}
QString SpectacleCore::filename() const
......@@ -144,7 +164,10 @@ void SpectacleCore::dbusStartAgent()
break;
}
case Actions::FocusWindow:
KWindowSystem::forceActiveWindow(mMainWindow->winId());
if (mMainWindow->isMinimized()) {
mMainWindow->setWindowState(mMainWindow->windowState() & ~Qt::WindowMinimized);
}
mMainWindow->activateWindow();
break;
case Actions::StartNewInstance:
QProcess newInstance;
......
......@@ -77,8 +77,10 @@ class SpectacleCore: public QObject
private:
void initGui(bool theIncludePointer, bool theIncludeDecorations);
Platform::GrabMode toPlatformGrabMode(Spectacle::CaptureMode theCaptureMode);
void setUpShortcuts();
StartMode mStartMode;
bool mNotify;
......
......@@ -5,5 +5,5 @@ ecm_add_test(FilenameTest.cpp
../src/ExportManager.cpp ../src/SpectacleConfig.cpp ../src/Platforms/Platform.cpp
TEST_NAME "filename_test"
LINK_LIBRARIES Qt5::Test
Qt5::PrintSupport KF5::I18n KF5::ConfigCore KF5::KIOCore KF5::WindowSystem
Qt5::PrintSupport KF5::I18n KF5::ConfigCore KF5::GlobalAccel KF5::KIOCore KF5::WindowSystem KF5::XmlGui
)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment