Merge pull request #6470 from lacc97/kglobalaccel
Add KGlobalAccel global shortcuts backend
This commit is contained in:
commit
998f12699b
@ -82,6 +82,7 @@ set(SOURCES
|
||||
core/globalshortcutbackend.cpp
|
||||
core/globalshortcuts.cpp
|
||||
core/gnomeglobalshortcutbackend.cpp
|
||||
core/kglobalaccelglobalshortcutbackend.cpp
|
||||
core/mergedproxymodel.cpp
|
||||
core/metatypes.cpp
|
||||
core/multisortfilterproxy.cpp
|
||||
@ -413,6 +414,7 @@ set(HEADERS
|
||||
core/globalshortcuts.h
|
||||
core/globalshortcutbackend.h
|
||||
core/gnomeglobalshortcutbackend.h
|
||||
core/kglobalaccelglobalshortcutbackend.h
|
||||
core/mergedproxymodel.h
|
||||
core/mimedata.h
|
||||
core/network.h
|
||||
@ -937,6 +939,15 @@ if(UNIX AND HAVE_DBUS)
|
||||
dbus/org.gnome.SettingsDaemon.MediaKeys.xml
|
||||
dbus/gnomesettingsdaemon)
|
||||
|
||||
# org.kde.KGlobalAccel interfaces
|
||||
# these are taken from the KGlobalAccel sources (LGPL 2.1)
|
||||
qt5_add_dbus_interface(SOURCES
|
||||
dbus/org.kde.KGlobalAccel.xml
|
||||
dbus/kglobalaccel)
|
||||
qt5_add_dbus_interface(SOURCES
|
||||
dbus/org.kde.kglobalaccel.Component.xml
|
||||
dbus/kglobalaccelcomponent)
|
||||
|
||||
# org.freedesktop.Avahi.Server interface
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "config.h"
|
||||
#include "globalshortcuts.h"
|
||||
#include "gnomeglobalshortcutbackend.h"
|
||||
#include "kglobalaccelglobalshortcutbackend.h"
|
||||
#include "macglobalshortcutbackend.h"
|
||||
#include "qxtglobalshortcutbackend.h"
|
||||
|
||||
@ -41,7 +42,9 @@ GlobalShortcuts::GlobalShortcuts(QWidget* parent)
|
||||
: QWidget(parent),
|
||||
gnome_backend_(nullptr),
|
||||
system_backend_(nullptr),
|
||||
use_gnome_(false) {
|
||||
use_gnome_(false),
|
||||
have_kglobalaccel_(
|
||||
KGlobalAccelShortcutBackend::IsKGlobalAccelAvailable()) {
|
||||
settings_.beginGroup(kSettingsGroup);
|
||||
|
||||
// Create actions
|
||||
@ -91,6 +94,7 @@ GlobalShortcuts::GlobalShortcuts(QWidget* parent)
|
||||
|
||||
// Create backends - these do the actual shortcut registration
|
||||
gnome_backend_ = new GnomeGlobalShortcutBackend(this);
|
||||
kglobalaccel_backend_ = new KGlobalAccelShortcutBackend(this);
|
||||
|
||||
#ifndef Q_OS_DARWIN
|
||||
system_backend_ = new QxtGlobalShortcutBackend(this);
|
||||
@ -158,10 +162,12 @@ void GlobalShortcuts::ReloadSettings() {
|
||||
|
||||
void GlobalShortcuts::Unregister() {
|
||||
if (gnome_backend_->is_active()) gnome_backend_->Unregister();
|
||||
if (kglobalaccel_backend_->is_active()) kglobalaccel_backend_->Unregister();
|
||||
if (system_backend_->is_active()) system_backend_->Unregister();
|
||||
}
|
||||
|
||||
void GlobalShortcuts::Register() {
|
||||
if (have_kglobalaccel_ && kglobalaccel_backend_->Register()) return;
|
||||
if (use_gnome_ && gnome_backend_->Register()) return;
|
||||
system_backend_->Register();
|
||||
}
|
||||
|
@ -93,12 +93,14 @@ signals:
|
||||
|
||||
private:
|
||||
GlobalShortcutBackend* gnome_backend_;
|
||||
GlobalShortcutBackend* kglobalaccel_backend_;
|
||||
GlobalShortcutBackend* system_backend_;
|
||||
|
||||
QMap<QString, Shortcut> shortcuts_;
|
||||
QSettings settings_;
|
||||
|
||||
bool use_gnome_;
|
||||
bool have_kglobalaccel_;
|
||||
};
|
||||
|
||||
#endif // CORE_GLOBALSHORTCUTS_H_
|
||||
|
268
src/core/kglobalaccelglobalshortcutbackend.cpp
Normal file
268
src/core/kglobalaccelglobalshortcutbackend.cpp
Normal file
@ -0,0 +1,268 @@
|
||||
#include "core/logging.h"
|
||||
#include "kglobalaccelglobalshortcutbackend.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QGuiApplication>
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
|
||||
#include <dbus/kglobalaccel.h>
|
||||
#include <dbus/kglobalaccelcomponent.h>
|
||||
|
||||
#endif
|
||||
|
||||
// Most of this file is based on the KGlobalAccel sources
|
||||
// (https://phabricator.kde.org/source/kglobalaccel)
|
||||
|
||||
namespace {
|
||||
#ifdef HAVE_DBUS
|
||||
QString ComponentDisplayName() {
|
||||
return QGuiApplication::applicationDisplayName().isEmpty()
|
||||
? QCoreApplication::applicationName()
|
||||
: QGuiApplication::applicationDisplayName();
|
||||
}
|
||||
|
||||
QString ComponentUniqueName() { return QCoreApplication::applicationName(); }
|
||||
|
||||
const QString& IdActionUniqueName(const QStringList& id) { return id.at(1); }
|
||||
|
||||
bool IsCorrectMediaKeyShortcut(const GlobalShortcuts::Shortcut& shortcut) {
|
||||
if (shortcut.id == QStringLiteral("play_pause")) {
|
||||
return shortcut.action->shortcut() == QKeySequence(Qt::Key_MediaPlay);
|
||||
} else if (shortcut.id == QStringLiteral("stop")) {
|
||||
return shortcut.action->shortcut() == QKeySequence(Qt::Key_MediaStop);
|
||||
} else if (shortcut.id == QStringLiteral("next_track")) {
|
||||
return shortcut.action->shortcut() == QKeySequence(Qt::Key_MediaNext);
|
||||
} else if (shortcut.id == QStringLiteral("prev_track")) {
|
||||
return shortcut.action->shortcut() == QKeySequence(Qt::Key_MediaPrevious);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif // HAVE_DBUS
|
||||
} // namespace
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
|
||||
KGlobalAccelShortcutBackend::KGlobalAccelShortcutBackend(
|
||||
GlobalShortcuts* parent)
|
||||
: GlobalShortcutBackend(parent), iface_(nullptr), component_(nullptr) {}
|
||||
|
||||
#else // HAVE_DBUS
|
||||
KGlobalAccelShortcutBackend::KGlobalAccelShortcutBackend(
|
||||
GlobalShortcuts* parent)
|
||||
: GlobalShortcutBackend(parent) {}
|
||||
#endif // HAVE_DBUS
|
||||
|
||||
bool KGlobalAccelShortcutBackend::IsKGlobalAccelAvailable() {
|
||||
#ifdef HAVE_DBUS
|
||||
return QDBusConnection::sessionBus().interface()->isServiceRegistered(
|
||||
kService);
|
||||
#else // HAVE_DBUS
|
||||
return false;
|
||||
#endif // HAVE_DBUS
|
||||
}
|
||||
|
||||
bool KGlobalAccelShortcutBackend::DoRegister() {
|
||||
#ifdef HAVE_DBUS
|
||||
qLog(Debug) << "Registering shortcuts";
|
||||
|
||||
if (!AcquireInterface()) return false;
|
||||
|
||||
bool complete = true;
|
||||
for (const GlobalShortcuts::Shortcut& shortcut :
|
||||
manager_->shortcuts().values()) {
|
||||
if (shortcut.action->shortcut().isEmpty()) continue;
|
||||
|
||||
if (!RegisterShortcut(shortcut)) complete = false;
|
||||
}
|
||||
|
||||
if (!AcquireComponent()) return false;
|
||||
|
||||
QObject::connect(component_,
|
||||
&OrgKdeKglobalaccelComponentInterface::globalShortcutPressed,
|
||||
this, &KGlobalAccelShortcutBackend::OnShortcutPressed,
|
||||
Qt::UniqueConnection);
|
||||
|
||||
return complete;
|
||||
#else // HAVE_DBUS
|
||||
qLog(Warning) << "dbus not available";
|
||||
return false;
|
||||
#endif // HAVE_DBUS
|
||||
}
|
||||
|
||||
void KGlobalAccelShortcutBackend::DoUnregister() {
|
||||
#ifdef HAVE_DBUS
|
||||
if (!AcquireInterface()) return;
|
||||
|
||||
if (!AcquireComponent()) return;
|
||||
|
||||
for (const GlobalShortcuts::Shortcut& shortcut : manager_->shortcuts())
|
||||
UnregisterAction(shortcut.id, shortcut.action);
|
||||
|
||||
QObject::disconnect(
|
||||
component_, &OrgKdeKglobalaccelComponentInterface::globalShortcutPressed,
|
||||
this, &KGlobalAccelShortcutBackend::OnShortcutPressed);
|
||||
#endif // HAVE_DBUS
|
||||
}
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
|
||||
const char* KGlobalAccelShortcutBackend::kService = "org.kde.kglobalaccel";
|
||||
const char* KGlobalAccelShortcutBackend::kPath = "/kglobalaccel";
|
||||
|
||||
bool KGlobalAccelShortcutBackend::AcquireComponent() {
|
||||
Q_ASSERT(iface_ && iface_->isValid());
|
||||
|
||||
if (component_) return true;
|
||||
|
||||
QDBusReply<QDBusObjectPath> reply =
|
||||
iface_->getComponent(ComponentUniqueName());
|
||||
if (!reply.isValid()) {
|
||||
if (reply.error().name() ==
|
||||
QLatin1String("org.kde.kglobalaccel.NoSuchComponent"))
|
||||
return false;
|
||||
|
||||
qLog(Warning) << "Failed to get DBus path for KGlobalAccel component";
|
||||
return false;
|
||||
}
|
||||
|
||||
component_ = new org::kde::kglobalaccel::Component(
|
||||
kService, reply.value().path(), QDBusConnection::sessionBus(), iface_);
|
||||
|
||||
if (!component_->isValid()) {
|
||||
qLog(Warning) << "Failed to get KGlobalAccel component:"
|
||||
<< QDBusConnection::sessionBus().lastError();
|
||||
component_->deleteLater();
|
||||
component_ = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KGlobalAccelShortcutBackend::AcquireInterface() {
|
||||
if (iface_ && iface_->isValid()) return true;
|
||||
|
||||
if (IsKGlobalAccelAvailable()) {
|
||||
iface_ = new OrgKdeKGlobalAccelInterface(
|
||||
kService, kPath, QDBusConnection::sessionBus(), this);
|
||||
}
|
||||
|
||||
if (iface_ && iface_->isValid()) return true;
|
||||
|
||||
if (!iface_)
|
||||
qLog(Warning) << "KGlobalAccel daemon not registered";
|
||||
else if (!iface_->isValid())
|
||||
qLog(Warning) << "KGlobalAccel daemon is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList KGlobalAccelShortcutBackend::GetId(const QString& name,
|
||||
const QAction* action) {
|
||||
Q_ASSERT(action);
|
||||
|
||||
QStringList ret;
|
||||
ret << ComponentUniqueName();
|
||||
ret << name;
|
||||
ret << ComponentDisplayName();
|
||||
ret << action->text().replace(QLatin1Char('&'), QStringLiteral(""));
|
||||
if (ret.back().isEmpty()) ret.back() = name;
|
||||
return ret;
|
||||
}
|
||||
|
||||
QList<int> KGlobalAccelShortcutBackend::ToIntList(
|
||||
const QList<QKeySequence>& seq) {
|
||||
QList<int> ret;
|
||||
for (const QKeySequence& sequence : seq) {
|
||||
ret.append(sequence[0]);
|
||||
}
|
||||
while (!ret.isEmpty() && ret.last() == 0) {
|
||||
ret.removeLast();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool KGlobalAccelShortcutBackend::RegisterAction(const QString& name,
|
||||
QAction* action) {
|
||||
Q_ASSERT(action);
|
||||
|
||||
if (name.isEmpty() &&
|
||||
(action->objectName().isEmpty() ||
|
||||
action->objectName().startsWith(QLatin1String("unnamed-")))) {
|
||||
qLog(Warning) << "Cannot register shortcut for unnamed action";
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList action_id = GetId(name, action);
|
||||
name_to_action_.insertMulti(IdActionUniqueName(action_id), action);
|
||||
iface_->doRegister(action_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KGlobalAccelShortcutBackend::RegisterShortcut(
|
||||
const GlobalShortcuts::Shortcut& shortcut) {
|
||||
if (!RegisterAction(shortcut.id, shortcut.action)) return false;
|
||||
|
||||
QList<QKeySequence> active_shortcut;
|
||||
active_shortcut << shortcut.action->shortcut();
|
||||
|
||||
QStringList action_id = GetId(shortcut.id, shortcut.action);
|
||||
const QList<int> result = iface_->setShortcut(
|
||||
action_id, ToIntList(active_shortcut), SetShortcutFlag::SetPresent);
|
||||
|
||||
const QList<QKeySequence> result_sequence = ToKeySequenceList(result);
|
||||
if (result_sequence != active_shortcut) {
|
||||
qLog(Warning) << "Tried setting global shortcut" << active_shortcut
|
||||
<< "but KGlobalAccel returned" << result_sequence;
|
||||
|
||||
if (!result_sequence.isEmpty()) {
|
||||
if (!IsCorrectMediaKeyShortcut(shortcut)) {
|
||||
// there is some conflict with our preferred shortcut so we use
|
||||
// the new shortcut that kglobalaccel suggests
|
||||
shortcut.action->setShortcut(result_sequence[0]);
|
||||
} else {
|
||||
// media keys are properly handled by plasma through the
|
||||
// media player plasmoid so we don't do anything in those cases
|
||||
qLog(Debug) << "Leaving media key shortcuts unchanged";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<QKeySequence> KGlobalAccelShortcutBackend::ToKeySequenceList(
|
||||
const QList<int>& seq) {
|
||||
QList<QKeySequence> ret;
|
||||
for (int i : seq) {
|
||||
ret.append(i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void KGlobalAccelShortcutBackend::UnregisterAction(const QString& name,
|
||||
QAction* action) {
|
||||
Q_ASSERT(action);
|
||||
|
||||
QStringList action_id = GetId(name, action);
|
||||
name_to_action_.remove(IdActionUniqueName(action_id), action);
|
||||
iface_->unRegister(action_id);
|
||||
}
|
||||
|
||||
void KGlobalAccelShortcutBackend::OnShortcutPressed(
|
||||
const QString& component_unique, const QString& action_unique,
|
||||
qlonglong timestamp) const {
|
||||
QAction* action = nullptr;
|
||||
const QList<QAction*> candidates = name_to_action_.values(action_unique);
|
||||
for (QAction* a : candidates) {
|
||||
if (ComponentUniqueName() == component_unique) {
|
||||
action = a;
|
||||
}
|
||||
}
|
||||
|
||||
if (action && action->isEnabled()) action->trigger();
|
||||
}
|
||||
|
||||
#endif // HAVE_DBUS
|
66
src/core/kglobalaccelglobalshortcutbackend.h
Normal file
66
src/core/kglobalaccelglobalshortcutbackend.h
Normal file
@ -0,0 +1,66 @@
|
||||
#ifndef CORE_KGLOBALACCELGLOBALSHORTCUTBACKEND_H_
|
||||
#define CORE_KGLOBALACCELGLOBALSHORTCUTBACKEND_H_
|
||||
|
||||
#include "config.h"
|
||||
#include "globalshortcutbackend.h"
|
||||
#include "globalshortcuts.h"
|
||||
|
||||
#include <QSet>
|
||||
#include <QStringList>
|
||||
|
||||
class QAction;
|
||||
|
||||
class OrgKdeKGlobalAccelInterface;
|
||||
|
||||
class OrgKdeKglobalaccelComponentInterface;
|
||||
|
||||
class KGlobalAccelShortcutBackend : public GlobalShortcutBackend {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KGlobalAccelShortcutBackend(GlobalShortcuts* parent);
|
||||
|
||||
static bool IsKGlobalAccelAvailable();
|
||||
|
||||
protected:
|
||||
bool DoRegister() override;
|
||||
|
||||
void DoUnregister() override;
|
||||
|
||||
private:
|
||||
#ifdef HAVE_DBUS
|
||||
enum SetShortcutFlag { SetPresent = 2, NoAutoloading = 4, IsDefault = 8 };
|
||||
|
||||
bool AcquireComponent();
|
||||
|
||||
bool AcquireInterface();
|
||||
|
||||
static QStringList GetId(const QString& name, const QAction* action);
|
||||
|
||||
static QList<int> ToIntList(const QList<QKeySequence>& seq);
|
||||
|
||||
bool RegisterAction(const QString& name, QAction* action);
|
||||
|
||||
bool RegisterShortcut(const GlobalShortcuts::Shortcut& shortcut);
|
||||
|
||||
static QList<QKeySequence> ToKeySequenceList(const QList<int>& seq);
|
||||
|
||||
void UnregisterAction(const QString& name, QAction* action);
|
||||
|
||||
private slots:
|
||||
|
||||
void OnShortcutPressed(const QString& component_unique,
|
||||
const QString& action_unique,
|
||||
qlonglong timestamp) const;
|
||||
|
||||
private:
|
||||
static const char* kService;
|
||||
static const char* kPath;
|
||||
|
||||
OrgKdeKGlobalAccelInterface* iface_;
|
||||
OrgKdeKglobalaccelComponentInterface* component_;
|
||||
QMultiHash<QString, QAction*> name_to_action_;
|
||||
#endif // HAVE_DBUS
|
||||
};
|
||||
|
||||
#endif // CORE_KGLOBALACCELGLOBALSHORTCUTBACKEND_H_
|
72
src/dbus/org.kde.KGlobalAccel.xml
Normal file
72
src/dbus/org.kde.KGlobalAccel.xml
Normal file
@ -0,0 +1,72 @@
|
||||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.kde.KGlobalAccel">
|
||||
<signal name="yourShortcutGotChanged">
|
||||
<arg name="actionId" type="as" direction="out"/>
|
||||
<arg name="newKeys" type="ai" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QList<int>"/>
|
||||
</signal>
|
||||
<method name="allComponents">
|
||||
<arg type="ao" direction="out"/>
|
||||
</method>
|
||||
<method name="allMainComponents">
|
||||
<arg type="aas" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<QStringList>"/>
|
||||
</method>
|
||||
<method name="allActionsForComponent">
|
||||
<arg type="aas" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<QStringList>"/>
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
</method>
|
||||
<method name="action">
|
||||
<arg type="as" direction="out"/>
|
||||
<arg name="key" type="i" direction="in"/>
|
||||
</method>
|
||||
<method name="getComponent">
|
||||
<arg type="o" direction="out"/>
|
||||
<arg name="componentUnique" type="s" direction="in"/>
|
||||
</method>
|
||||
<method name="shortcut">
|
||||
<arg type="ai" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<int>"/>
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
</method>
|
||||
<method name="defaultShortcut">
|
||||
<arg type="ai" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<int>"/>
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
</method>
|
||||
<method name="setShortcut">
|
||||
<arg type="ai" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<int>"/>
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
<arg name="keys" type="ai" direction="in"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QList<int>"/>
|
||||
<arg name="flags" type="u" direction="in"/>
|
||||
</method>
|
||||
<method name="setForeignShortcut">
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
<arg name="keys" type="ai" direction="in"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QList<int>"/>
|
||||
</method>
|
||||
<method name="setInactive">
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
</method>
|
||||
<method name="doRegister">
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
</method>
|
||||
<method name="unRegister">
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
</method>
|
||||
<method name="activateGlobalShortcutContext">
|
||||
<arg name="component" type="s" direction="in"/>
|
||||
<arg name="context" type="s" direction="in"/>
|
||||
</method>
|
||||
<method name="isGlobalShortcutAvailable">
|
||||
<arg type="b" direction="out"/>
|
||||
<arg name="key" type="i" direction="in"/>
|
||||
<arg name="component" type="s" direction="in"/>
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
32
src/dbus/org.kde.kglobalaccel.Component.xml
Normal file
32
src/dbus/org.kde.kglobalaccel.Component.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.kde.kglobalaccel.Component">
|
||||
<property name="friendlyName" type="s" access="read"/>
|
||||
<property name="uniqueName" type="s" access="read"/>
|
||||
<signal name="globalShortcutPressed">
|
||||
<arg name="componentUnique" type="s" direction="out"/>
|
||||
<arg name="actionUnique" type="s" direction="out"/>
|
||||
<arg name="timestamp" type="x" direction="out"/>
|
||||
</signal>
|
||||
<method name="cleanUp">
|
||||
<arg type="b" direction="out"/>
|
||||
</method>
|
||||
<method name="isActive">
|
||||
<arg type="b" direction="out"/>
|
||||
</method>
|
||||
<method name="shortcutNames">
|
||||
<arg type="as" direction="out"/>
|
||||
<arg name="context" type="s" direction="in"/>
|
||||
</method>
|
||||
<method name="shortcutNames">
|
||||
<arg type="as" direction="out"/>
|
||||
</method>
|
||||
<method name="getShortcutContexts">
|
||||
<arg type="as" direction="out"/>
|
||||
</method>
|
||||
<method name="invokeShortcut">
|
||||
<arg name="actionName" type="s" direction="in"/>
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
Loading…
x
Reference in New Issue
Block a user