Advance Wayland and KDE package bring-up
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
configure_file(config-kglobalaccel.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kglobalaccel.h)
|
||||
|
||||
set(kglobalaccel_SRCS
|
||||
kglobalaccel.cpp
|
||||
kglobalshortcutinfo.cpp
|
||||
kglobalshortcutinfo_dbus.cpp
|
||||
)
|
||||
ecm_qt_declare_logging_category(kglobalaccel_SRCS
|
||||
HEADER kglobalaccel_debug.h
|
||||
IDENTIFIER KGLOBALACCEL_LOG
|
||||
CATEGORY_NAME kf.globalaccel
|
||||
OLD_CATEGORY_NAMES kf5.kglobalaccel
|
||||
DESCRIPTION "KGlobalAccel"
|
||||
EXPORT KGLOBALACCEL
|
||||
)
|
||||
|
||||
ecm_create_qm_loader(kglobalaccel_SRCS kglobalaccel6_qt)
|
||||
|
||||
set(kglobalaccel_xml ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.KGlobalAccel.xml)
|
||||
set_source_files_properties(${kglobalaccel_xml} PROPERTIES INCLUDE "kglobalshortcutinfo_p.h")
|
||||
qt_add_dbus_interface(kglobalaccel_SRCS ${kglobalaccel_xml} kglobalaccel_interface)
|
||||
install(FILES ${kglobalaccel_xml} DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} RENAME kf6_org.kde.KGlobalAccel.xml)
|
||||
|
||||
set(kglobalaccel_component_xml ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.kglobalaccel.Component.xml)
|
||||
set_source_files_properties(${kglobalaccel_component_xml} PROPERTIES INCLUDE "kglobalshortcutinfo_p.h")
|
||||
qt_add_dbus_interface(kglobalaccel_SRCS ${kglobalaccel_component_xml} kglobalaccel_component_interface)
|
||||
install(FILES ${kglobalaccel_component_xml} DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} RENAME kf6_org.kde.kglobalaccel.Component.xml)
|
||||
|
||||
add_library(KF6GlobalAccel ${kglobalaccel_SRCS})
|
||||
add_library(KF6::GlobalAccel ALIAS KF6GlobalAccel)
|
||||
|
||||
ecm_generate_export_header(KF6GlobalAccel
|
||||
BASE_NAME KGlobalAccel
|
||||
GROUP_BASE_NAME KF
|
||||
VERSION ${KF_VERSION}
|
||||
USE_VERSION_HEADER
|
||||
DEPRECATED_BASE_VERSION 0
|
||||
DEPRECATION_VERSIONS 5.102
|
||||
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
|
||||
)
|
||||
|
||||
target_include_directories(KF6GlobalAccel INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KGlobalAccel>")
|
||||
|
||||
target_link_libraries(KF6GlobalAccel
|
||||
PUBLIC
|
||||
Qt6::DBus
|
||||
Qt6::Widgets
|
||||
)
|
||||
|
||||
if(WITH_X11)
|
||||
target_link_libraries(KF6GlobalAccel PRIVATE Qt6::GuiPrivate) # qtx11extras_p.h
|
||||
endif()
|
||||
|
||||
set_target_properties(KF6GlobalAccel PROPERTIES VERSION ${KGLOBALACCEL_VERSION}
|
||||
SOVERSION ${KGLOBALACCEL_SOVERSION}
|
||||
EXPORT_NAME "GlobalAccel"
|
||||
)
|
||||
|
||||
ecm_generate_headers(KGlobalAccel_HEADERS
|
||||
HEADER_NAMES
|
||||
KGlobalAccel
|
||||
KGlobalShortcutInfo
|
||||
|
||||
REQUIRED_HEADERS KGlobalAccel_HEADERS
|
||||
)
|
||||
|
||||
install(TARGETS KF6GlobalAccel EXPORT KF6GlobalAccelTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
install(FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/kglobalaccel_export.h
|
||||
${KGlobalAccel_HEADERS}
|
||||
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KGlobalAccel COMPONENT Devel
|
||||
)
|
||||
|
||||
if(BUILD_QCH)
|
||||
ecm_add_qch(
|
||||
KF6GlobalAccel_QCH
|
||||
NAME KGlobalAccel
|
||||
BASE_NAME KF6GlobalAccel
|
||||
VERSION ${KF_VERSION}
|
||||
ORG_DOMAIN org.kde
|
||||
SOURCES # using only public headers, to cover only public API
|
||||
${KGlobalAccel_HEADERS}
|
||||
MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
|
||||
LINK_QCHS
|
||||
Qt6Widgets_QCH
|
||||
Qt6DBus_QCH
|
||||
INCLUDE_DIRS
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
BLANK_MACROS
|
||||
KGLOBALACCEL_EXPORT
|
||||
KGLOBALACCEL_DEPRECATED
|
||||
KGLOBALACCEL_DEPRECATED_EXPORT
|
||||
"KGLOBALACCEL_DEPRECATED_VERSION(x, y, t)"
|
||||
TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
|
||||
QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
|
||||
COMPONENT Devel
|
||||
)
|
||||
endif()
|
||||
|
||||
ecm_qt_install_logging_categories(
|
||||
EXPORT KGLOBALACCEL
|
||||
FILE kglobalaccel.categories
|
||||
DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Extract strings from all source files.
|
||||
# EXTRACT_TR_STRINGS extracts strings with lupdate and convert them to .pot with
|
||||
# lconvert.
|
||||
$EXTRACT_TR_STRINGS `find . -name \*.cpp -o -name \*.h -o -name \*.ui -o -name \*.qml` -o $podir/kglobalaccel6_qt.pot
|
||||
@@ -0,0 +1,4 @@
|
||||
/* config-kglobalaccel.h. Generated by cmake from config-kglobalaccel.h.cmake */
|
||||
|
||||
/* Define to 1 if cmake option selected for Qt6::GuiPrivate */
|
||||
#cmakedefine01 WITH_X11
|
||||
@@ -0,0 +1,703 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2001, 2002 Ellis Whitehead <ellis@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com>
|
||||
SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kglobalaccel.h"
|
||||
#include "kglobalaccel_debug.h"
|
||||
#include "kglobalaccel_p.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QAction>
|
||||
#include <QDBusMessage>
|
||||
#include <QDBusMetaType>
|
||||
#include <QGuiApplication>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <config-kglobalaccel.h>
|
||||
|
||||
#if WITH_X11
|
||||
#include <private/qtx11extras_p.h>
|
||||
#endif
|
||||
|
||||
org::kde::kglobalaccel::Component *KGlobalAccelPrivate::getComponent(const QString &componentUnique, bool remember = false)
|
||||
{
|
||||
// Check if we already have this component
|
||||
{
|
||||
auto component = components.value(componentUnique);
|
||||
if (component) {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the path for our component. We have to do that because
|
||||
// componentUnique is probably not a valid dbus object path
|
||||
QDBusReply<QDBusObjectPath> reply = iface()->getComponent(componentUnique);
|
||||
if (!reply.isValid()) {
|
||||
if (reply.error().name() == QLatin1String("org.kde.kglobalaccel.NoSuchComponent")) {
|
||||
// No problem. The component doesn't exists. That's normal
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// An unknown error.
|
||||
qCDebug(KGLOBALACCEL_LOG) << "Failed to get dbus path for component " << componentUnique << reply.error();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Now get the component
|
||||
org::kde::kglobalaccel::Component *component =
|
||||
new org::kde::kglobalaccel::Component(QStringLiteral("org.kde.kglobalaccel"), reply.value().path(), QDBusConnection::sessionBus(), q);
|
||||
|
||||
// No component no cleaning
|
||||
if (!component->isValid()) {
|
||||
qCDebug(KGLOBALACCEL_LOG) << "Failed to get component" << componentUnique << QDBusConnection::sessionBus().lastError();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (remember) {
|
||||
// Connect to the signals we are interested in.
|
||||
QObject::connect(component,
|
||||
&org::kde::kglobalaccel::Component::globalShortcutPressed,
|
||||
q,
|
||||
[this](const QString &componentUnique, const QString &shortcutUnique, qlonglong timestamp) {
|
||||
invokeAction(componentUnique, shortcutUnique, timestamp);
|
||||
});
|
||||
|
||||
QObject::connect(component,
|
||||
&org::kde::kglobalaccel::Component::globalShortcutReleased,
|
||||
q,
|
||||
[this](const QString &componentUnique, const QString &shortcutUnique, qlonglong) {
|
||||
invokeDeactivate(componentUnique, shortcutUnique);
|
||||
});
|
||||
|
||||
components[componentUnique] = component;
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
QString serviceName()
|
||||
{
|
||||
return QStringLiteral("org.kde.kglobalaccel");
|
||||
}
|
||||
}
|
||||
|
||||
void KGlobalAccelPrivate::cleanup()
|
||||
{
|
||||
qDeleteAll(components);
|
||||
delete m_iface;
|
||||
m_iface = nullptr;
|
||||
delete m_watcher;
|
||||
m_watcher = nullptr;
|
||||
}
|
||||
|
||||
KGlobalAccelPrivate::KGlobalAccelPrivate(KGlobalAccel *qq)
|
||||
: q(qq)
|
||||
{
|
||||
m_watcher = new QDBusServiceWatcher(serviceName(), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, q);
|
||||
QObject::connect(m_watcher,
|
||||
&QDBusServiceWatcher::serviceOwnerChanged,
|
||||
q,
|
||||
[this](const QString &serviceName, const QString &oldOwner, const QString &newOwner) {
|
||||
serviceOwnerChanged(serviceName, oldOwner, newOwner);
|
||||
});
|
||||
}
|
||||
|
||||
org::kde::KGlobalAccel *KGlobalAccelPrivate::iface()
|
||||
{
|
||||
if (!m_iface) {
|
||||
m_iface = new org::kde::KGlobalAccel(serviceName(), QStringLiteral("/kglobalaccel"), QDBusConnection::sessionBus());
|
||||
// Make sure kglobalaccel is running. The iface declaration above somehow works anyway.
|
||||
QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface();
|
||||
if (bus && !bus->isServiceRegistered(serviceName())) {
|
||||
QDBusReply<void> reply = bus->startService(serviceName());
|
||||
if (!reply.isValid()) {
|
||||
qCritical() << "Couldn't start kglobalaccel from org.kde.kglobalaccel.service:" << reply.error();
|
||||
}
|
||||
}
|
||||
|
||||
QObject::connect(m_iface, &org::kde::KGlobalAccel::yourShortcutsChanged, q, [this](const QStringList &actionId, const QList<QKeySequence> &newKeys) {
|
||||
shortcutsChanged(actionId, newKeys);
|
||||
});
|
||||
}
|
||||
return m_iface;
|
||||
}
|
||||
|
||||
KGlobalAccel::KGlobalAccel()
|
||||
: d(new KGlobalAccelPrivate(this))
|
||||
{
|
||||
qDBusRegisterMetaType<QList<int>>();
|
||||
qDBusRegisterMetaType<QKeySequence>();
|
||||
qDBusRegisterMetaType<QList<QKeySequence>>();
|
||||
qDBusRegisterMetaType<QList<QStringList>>();
|
||||
qDBusRegisterMetaType<KGlobalShortcutInfo>();
|
||||
qDBusRegisterMetaType<QList<KGlobalShortcutInfo>>();
|
||||
qDBusRegisterMetaType<KGlobalAccel::MatchType>();
|
||||
}
|
||||
|
||||
KGlobalAccel::~KGlobalAccel()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
// static
|
||||
bool KGlobalAccel::cleanComponent(const QString &componentUnique)
|
||||
{
|
||||
org::kde::kglobalaccel::Component *component = self()->getComponent(componentUnique);
|
||||
if (!component) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return component->cleanUp();
|
||||
}
|
||||
|
||||
// static
|
||||
bool KGlobalAccel::isComponentActive(const QString &componentUnique)
|
||||
{
|
||||
org::kde::kglobalaccel::Component *component = self()->getComponent(componentUnique);
|
||||
if (!component) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return component->isActive();
|
||||
}
|
||||
|
||||
org::kde::kglobalaccel::Component *KGlobalAccel::getComponent(const QString &componentUnique)
|
||||
{
|
||||
return d->getComponent(componentUnique);
|
||||
}
|
||||
|
||||
class KGlobalAccelSingleton
|
||||
{
|
||||
public:
|
||||
KGlobalAccelSingleton();
|
||||
|
||||
KGlobalAccel instance;
|
||||
};
|
||||
|
||||
Q_GLOBAL_STATIC(KGlobalAccelSingleton, s_instance)
|
||||
|
||||
KGlobalAccelSingleton::KGlobalAccelSingleton()
|
||||
{
|
||||
qAddPostRoutine([]() {
|
||||
s_instance->instance.d->cleanup();
|
||||
});
|
||||
}
|
||||
|
||||
KGlobalAccel *KGlobalAccel::self()
|
||||
{
|
||||
return &s_instance()->instance;
|
||||
}
|
||||
|
||||
bool KGlobalAccelPrivate::doRegister(QAction *action)
|
||||
{
|
||||
if (!action || action->objectName().isEmpty() || action->objectName().startsWith(QLatin1String("unnamed-"))) {
|
||||
qWarning() << "Attempt to set global shortcut for action without objectName()."
|
||||
" Read the setGlobalShortcut() documentation.";
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool isRegistered = actions.contains(action);
|
||||
if (isRegistered) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList actionId = makeActionId(action);
|
||||
|
||||
nameToAction.insert(actionId.at(KGlobalAccel::ActionUnique), action);
|
||||
actions.insert(action);
|
||||
iface()->doRegister(actionId);
|
||||
|
||||
QObject::connect(action, &QObject::destroyed, q, [this, action](QObject *) {
|
||||
if (actions.contains(action) && (actionShortcuts.contains(action) || actionDefaultShortcuts.contains(action))) {
|
||||
remove(action, KGlobalAccelPrivate::SetInactive);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KGlobalAccelPrivate::remove(QAction *action, Removal removal)
|
||||
{
|
||||
if (!action || action->objectName().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool isRegistered = actions.contains(action);
|
||||
if (!isRegistered) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList actionId = makeActionId(action);
|
||||
|
||||
nameToAction.remove(actionId.at(KGlobalAccel::ActionUnique), action);
|
||||
actions.remove(action);
|
||||
|
||||
if (removal == UnRegister) {
|
||||
// Complete removal of the shortcut is requested
|
||||
// (forgetGlobalShortcut)
|
||||
unregister(actionId);
|
||||
} else {
|
||||
// If the action is a configurationAction wen only remove it from our
|
||||
// internal registry. That happened above.
|
||||
|
||||
// If we are merely marking a callback as inactive there is nothing for kglobalaccel to do if kglobalaccel is not running
|
||||
// this can happen on shutdown where all apps and kglobalaccel are all torn down at once
|
||||
// For this reason we turn off the autostart flag in the DBus message call
|
||||
|
||||
if (!action->property("isConfigurationAction").toBool()) {
|
||||
// If it's a session shortcut unregister it.
|
||||
if (action->objectName().startsWith(QLatin1String("_k_session:"))) {
|
||||
unregister(actionId);
|
||||
} else {
|
||||
setInactive(actionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actionDefaultShortcuts.remove(action);
|
||||
actionShortcuts.remove(action);
|
||||
}
|
||||
|
||||
void KGlobalAccelPrivate::unregister(const QStringList &actionId)
|
||||
{
|
||||
const auto component = actionId.at(KGlobalAccel::ComponentUnique);
|
||||
const auto action = actionId.at(KGlobalAccel::ActionUnique);
|
||||
|
||||
auto message = QDBusMessage::createMethodCall(iface()->service(), iface()->path(), iface()->interface(), QStringLiteral("unregister"));
|
||||
message.setArguments({component, action});
|
||||
message.setAutoStartService(false);
|
||||
QDBusConnection::sessionBus().call(message);
|
||||
}
|
||||
|
||||
void KGlobalAccelPrivate::setInactive(const QStringList &actionId)
|
||||
{
|
||||
auto message = QDBusMessage::createMethodCall(iface()->service(), iface()->path(), iface()->interface(), QStringLiteral("setInactive"));
|
||||
message.setArguments({actionId});
|
||||
message.setAutoStartService(false);
|
||||
QDBusConnection::sessionBus().call(message);
|
||||
}
|
||||
|
||||
void KGlobalAccelPrivate::updateGlobalShortcut(/*const would be better*/ QAction *action,
|
||||
ShortcutTypes actionFlags,
|
||||
KGlobalAccel::GlobalShortcutLoading globalFlags)
|
||||
{
|
||||
// No action or no objectname -> Do nothing
|
||||
if (!action || action->objectName().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList actionId = makeActionId(action);
|
||||
|
||||
uint setterFlags = 0;
|
||||
if (globalFlags & NoAutoloading) {
|
||||
setterFlags |= NoAutoloading;
|
||||
}
|
||||
|
||||
if (actionFlags & ActiveShortcut) {
|
||||
const QList<QKeySequence> activeShortcut = actionShortcuts.value(action);
|
||||
bool isConfigurationAction = action->property("isConfigurationAction").toBool();
|
||||
uint activeSetterFlags = setterFlags;
|
||||
|
||||
// setPresent tells kglobalaccel that the shortcut is active
|
||||
if (!isConfigurationAction) {
|
||||
activeSetterFlags |= SetPresent;
|
||||
}
|
||||
|
||||
// Sets the shortcut, returns the active/real keys
|
||||
const auto result = iface()->setShortcutKeys(actionId, activeShortcut, activeSetterFlags);
|
||||
|
||||
// Make sure we get informed about changes in the component by kglobalaccel
|
||||
getComponent(componentUniqueForAction(action), true);
|
||||
|
||||
// Create a shortcut from the result
|
||||
const QList<QKeySequence> scResult(result);
|
||||
|
||||
if (isConfigurationAction && (globalFlags & NoAutoloading)) {
|
||||
// If this is a configuration action and we have set the shortcut,
|
||||
// inform the real owner of the change.
|
||||
// Note that setForeignShortcut will cause a signal to be sent to applications
|
||||
// even if it did not "see" that the shortcut has changed. This is Good because
|
||||
// at the time of comparison (now) the action *already has* the new shortcut.
|
||||
// We called setShortcut(), remember?
|
||||
// Also note that we will see our own signal so we may not need to call
|
||||
// setActiveGlobalShortcutNoEnable - shortcutGotChanged() does it.
|
||||
// In practice it's probably better to get the change propagated here without
|
||||
// DBus delay as we do below.
|
||||
iface()->setForeignShortcutKeys(actionId, result);
|
||||
}
|
||||
if (scResult != activeShortcut) {
|
||||
// If kglobalaccel returned a shortcut that differs from the one we
|
||||
// sent, use that one. There must have been clashes or some other problem.
|
||||
actionShortcuts.insert(action, scResult);
|
||||
Q_EMIT q->globalShortcutChanged(action, scResult.isEmpty() ? QKeySequence() : scResult.first());
|
||||
}
|
||||
}
|
||||
|
||||
if (actionFlags & DefaultShortcut) {
|
||||
const QList<QKeySequence> defaultShortcut = actionDefaultShortcuts.value(action);
|
||||
iface()->setShortcutKeys(actionId, defaultShortcut, setterFlags | IsDefault);
|
||||
}
|
||||
}
|
||||
|
||||
QStringList KGlobalAccelPrivate::makeActionId(const QAction *action)
|
||||
{
|
||||
QStringList ret(componentUniqueForAction(action)); // Component Unique Id ( see actionIdFields )
|
||||
Q_ASSERT(!ret.at(KGlobalAccel::ComponentUnique).isEmpty());
|
||||
Q_ASSERT(!action->objectName().isEmpty());
|
||||
ret.append(action->objectName()); // Action Unique Name
|
||||
ret.append(componentFriendlyForAction(action)); // Component Friendly name
|
||||
const QString actionText = action->text().replace(QLatin1Char('&'), QStringLiteral(""));
|
||||
ret.append(actionText); // Action Friendly Name
|
||||
return ret;
|
||||
}
|
||||
|
||||
QList<int> KGlobalAccelPrivate::intListFromShortcut(const QList<QKeySequence> &cut)
|
||||
{
|
||||
QList<int> ret;
|
||||
for (const QKeySequence &sequence : cut) {
|
||||
ret.append(sequence[0].toCombined());
|
||||
}
|
||||
while (!ret.isEmpty() && ret.last() == 0) {
|
||||
ret.removeLast();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
QList<QKeySequence> KGlobalAccelPrivate::shortcutFromIntList(const QList<int> &list)
|
||||
{
|
||||
QList<QKeySequence> ret;
|
||||
ret.reserve(list.size());
|
||||
std::transform(list.begin(), list.end(), std::back_inserter(ret), [](int i) {
|
||||
return QKeySequence(i);
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString KGlobalAccelPrivate::componentUniqueForAction(const QAction *action)
|
||||
{
|
||||
if (!action->property("componentName").isValid()) {
|
||||
return QCoreApplication::applicationName();
|
||||
} else {
|
||||
return action->property("componentName").toString();
|
||||
}
|
||||
}
|
||||
|
||||
QString KGlobalAccelPrivate::componentFriendlyForAction(const QAction *action)
|
||||
{
|
||||
QString property = action->property("componentDisplayName").toString();
|
||||
if (!property.isEmpty()) {
|
||||
return property;
|
||||
}
|
||||
if (!QGuiApplication::applicationDisplayName().isEmpty()) {
|
||||
return QGuiApplication::applicationDisplayName();
|
||||
}
|
||||
return QCoreApplication::applicationName();
|
||||
}
|
||||
|
||||
#if WITH_X11
|
||||
int timestampCompare(unsigned long time1_, unsigned long time2_) // like strcmp()
|
||||
{
|
||||
quint32 time1 = time1_;
|
||||
quint32 time2 = time2_;
|
||||
if (time1 == time2) {
|
||||
return 0;
|
||||
}
|
||||
return quint32(time1 - time2) < 0x7fffffffU ? 1 : -1; // time1 > time2 -> 1, handle wrapping
|
||||
}
|
||||
#endif
|
||||
|
||||
QAction *KGlobalAccelPrivate::findAction(const QString &componentUnique, const QString &actionUnique)
|
||||
{
|
||||
QAction *action = nullptr;
|
||||
const QList<QAction *> candidates = nameToAction.values(actionUnique);
|
||||
for (QAction *const a : candidates) {
|
||||
if (componentUniqueForAction(a) == componentUnique) {
|
||||
action = a;
|
||||
}
|
||||
}
|
||||
|
||||
// We do not trigger if
|
||||
// - there is no action
|
||||
// - the action is not enabled
|
||||
// - the action is an configuration action
|
||||
if (!action || !action->isEnabled() || action->property("isConfigurationAction").toBool()) {
|
||||
return nullptr;
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
void KGlobalAccelPrivate::invokeAction(const QString &componentUnique, const QString &actionUnique, qlonglong timestamp)
|
||||
{
|
||||
QAction *action = findAction(componentUnique, actionUnique);
|
||||
if (!action) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if WITH_X11
|
||||
// Update this application's X timestamp if needed.
|
||||
// TODO The 100%-correct solution should probably be handling this action
|
||||
// in the proper place in relation to the X events queue in order to avoid
|
||||
// the possibility of wrong ordering of user events.
|
||||
if (QX11Info::isPlatformX11()) {
|
||||
if (timestampCompare(timestamp, QX11Info::appTime()) > 0) {
|
||||
QX11Info::setAppTime(timestamp);
|
||||
}
|
||||
if (timestampCompare(timestamp, QX11Info::appUserTime()) > 0) {
|
||||
QX11Info::setAppUserTime(timestamp);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
action->setProperty("org.kde.kglobalaccel.activationTimestamp", timestamp);
|
||||
|
||||
if (m_lastActivatedAction != action) {
|
||||
Q_EMIT q->globalShortcutActiveChanged(action, true);
|
||||
m_lastActivatedAction = action;
|
||||
}
|
||||
action->trigger();
|
||||
}
|
||||
|
||||
void KGlobalAccelPrivate::invokeDeactivate(const QString &componentUnique, const QString &actionUnique)
|
||||
{
|
||||
QAction *action = findAction(componentUnique, actionUnique);
|
||||
if (!action) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_lastActivatedAction.clear();
|
||||
|
||||
Q_EMIT q->globalShortcutActiveChanged(action, false);
|
||||
}
|
||||
|
||||
void KGlobalAccelPrivate::shortcutsChanged(const QStringList &actionId, const QList<QKeySequence> &keys)
|
||||
{
|
||||
QAction *action = nameToAction.value(actionId.at(KGlobalAccel::ActionUnique));
|
||||
if (!action) {
|
||||
return;
|
||||
}
|
||||
|
||||
actionShortcuts.insert(action, keys);
|
||||
Q_EMIT q->globalShortcutChanged(action, keys.isEmpty() ? QKeySequence() : keys.first());
|
||||
}
|
||||
|
||||
void KGlobalAccelPrivate::serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
|
||||
{
|
||||
Q_UNUSED(oldOwner);
|
||||
if (name == QLatin1String("org.kde.kglobalaccel") && !newOwner.isEmpty()) {
|
||||
// kglobalaccel was restarted
|
||||
qCDebug(KGLOBALACCEL_LOG) << "detected kglobalaccel restarting, re-registering all shortcut keys";
|
||||
reRegisterAll();
|
||||
}
|
||||
}
|
||||
|
||||
void KGlobalAccelPrivate::reRegisterAll()
|
||||
{
|
||||
// We clear all our data, assume that all data on the other side is clear too,
|
||||
// and register each action as if it just was allowed to have global shortcuts.
|
||||
// If the kded side still has the data it doesn't matter because of the
|
||||
// autoloading mechanism. The worst case I can imagine is that an action's
|
||||
// shortcut was changed but the kded side died before it got the message so
|
||||
// autoloading will now assign an old shortcut to the action. Particularly
|
||||
// picky apps might assert or misbehave.
|
||||
const QSet<QAction *> allActions = actions;
|
||||
nameToAction.clear();
|
||||
actions.clear();
|
||||
for (QAction *const action : allActions) {
|
||||
if (doRegister(action)) {
|
||||
updateGlobalShortcut(action, ActiveShortcut, KGlobalAccel::Autoloading);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<KGlobalShortcutInfo> KGlobalAccel::globalShortcutsByKey(const QKeySequence &seq, MatchType type)
|
||||
{
|
||||
return self()->d->iface()->globalShortcutsByKey(seq, type);
|
||||
}
|
||||
|
||||
bool KGlobalAccel::isGlobalShortcutAvailable(const QKeySequence &seq, const QString &comp)
|
||||
{
|
||||
return self()->d->iface()->globalShortcutAvailable(seq, comp);
|
||||
}
|
||||
|
||||
// static
|
||||
bool KGlobalAccel::promptStealShortcutSystemwide(QWidget *parent, const QList<KGlobalShortcutInfo> &shortcuts, const QKeySequence &seq)
|
||||
{
|
||||
if (shortcuts.isEmpty()) {
|
||||
// Usage error. Just say no
|
||||
return false;
|
||||
}
|
||||
|
||||
QString component = shortcuts[0].componentFriendlyName();
|
||||
|
||||
QString message;
|
||||
if (shortcuts.size() == 1) {
|
||||
message = tr("The '%1' key combination is registered by application %2 for action %3.").arg(seq.toString(), component, shortcuts[0].friendlyName());
|
||||
} else {
|
||||
QString actionList;
|
||||
for (const KGlobalShortcutInfo &info : shortcuts) {
|
||||
actionList += tr("In context '%1' for action '%2'\n").arg(info.contextFriendlyName(), info.friendlyName());
|
||||
}
|
||||
message = tr("The '%1' key combination is registered by application %2.\n%3").arg(seq.toString(), component, actionList);
|
||||
}
|
||||
|
||||
QString title = tr("Conflict With Registered Global Shortcut");
|
||||
|
||||
QMessageBox box(parent);
|
||||
box.setWindowTitle(title);
|
||||
box.setText(message);
|
||||
box.addButton(QMessageBox::Ok)->setText(tr("Reassign"));
|
||||
box.addButton(QMessageBox::Cancel);
|
||||
|
||||
return box.exec() == QMessageBox::Ok;
|
||||
}
|
||||
|
||||
// static
|
||||
void KGlobalAccel::stealShortcutSystemwide(const QKeySequence &seq)
|
||||
{
|
||||
// get the shortcut, remove seq, and set the new shortcut
|
||||
const QStringList actionId = self()->d->iface()->actionList(seq);
|
||||
if (actionId.size() < 4) { // not a global shortcut
|
||||
return;
|
||||
}
|
||||
QList<QKeySequence> sc = self()->d->iface()->shortcutKeys(actionId);
|
||||
|
||||
for (int i = 0; i < sc.count(); i++) {
|
||||
if (sc[i] == seq) {
|
||||
sc[i] = QKeySequence();
|
||||
}
|
||||
}
|
||||
|
||||
self()->d->iface()->setForeignShortcutKeys(actionId, sc);
|
||||
}
|
||||
|
||||
bool checkGarbageKeycode(const QList<QKeySequence> &shortcut)
|
||||
{
|
||||
// protect against garbage keycode -1 that Qt sometimes produces for exotic keys;
|
||||
// at the moment (~mid 2008) Multimedia PlayPause is one of those keys.
|
||||
for (const QKeySequence &sequence : shortcut) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (sequence[i].toCombined() == -1) {
|
||||
qWarning() << "Encountered garbage keycode (keycode = -1) in input, not doing anything.";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KGlobalAccel::setDefaultShortcut(QAction *action, const QList<QKeySequence> &shortcut, GlobalShortcutLoading loadFlag)
|
||||
{
|
||||
if (checkGarbageKeycode(shortcut)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!d->doRegister(action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d->actionDefaultShortcuts.insert(action, shortcut);
|
||||
d->updateGlobalShortcut(action, KGlobalAccelPrivate::DefaultShortcut, loadFlag);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KGlobalAccel::setShortcut(QAction *action, const QList<QKeySequence> &shortcut, GlobalShortcutLoading loadFlag)
|
||||
{
|
||||
if (checkGarbageKeycode(shortcut)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!d->doRegister(action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d->actionShortcuts.insert(action, shortcut);
|
||||
d->updateGlobalShortcut(action, KGlobalAccelPrivate::ActiveShortcut, loadFlag);
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<QKeySequence> KGlobalAccel::defaultShortcut(const QAction *action) const
|
||||
{
|
||||
return d->actionDefaultShortcuts.value(action);
|
||||
}
|
||||
|
||||
QList<QKeySequence> KGlobalAccel::shortcut(const QAction *action) const
|
||||
{
|
||||
return d->actionShortcuts.value(action);
|
||||
}
|
||||
|
||||
QList<QKeySequence> KGlobalAccel::globalShortcut(const QString &componentName, const QString &actionId) const
|
||||
{
|
||||
// see also d->updateGlobalShortcut(action, KGlobalAccelPrivate::ActiveShortcut, KGlobalAccel::Autoloading);
|
||||
|
||||
// how componentName and actionId map to QAction, e.g:
|
||||
// action->setProperty("componentName", "kwin");
|
||||
// action->setObjectName("Kill Window");
|
||||
|
||||
const QList<QKeySequence> scResult = self()->d->iface()->shortcutKeys({componentName, actionId, QString(), QString()});
|
||||
return scResult;
|
||||
}
|
||||
|
||||
void KGlobalAccel::removeAllShortcuts(QAction *action)
|
||||
{
|
||||
d->remove(action, KGlobalAccelPrivate::UnRegister);
|
||||
}
|
||||
|
||||
bool KGlobalAccel::hasShortcut(const QAction *action) const
|
||||
{
|
||||
return d->actionShortcuts.contains(action) || d->actionDefaultShortcuts.contains(action);
|
||||
}
|
||||
|
||||
bool KGlobalAccel::setGlobalShortcut(QAction *action, const QList<QKeySequence> &shortcut)
|
||||
{
|
||||
KGlobalAccel *g = KGlobalAccel::self();
|
||||
return g->d->setShortcutWithDefault(action, shortcut, Autoloading);
|
||||
}
|
||||
|
||||
bool KGlobalAccel::setGlobalShortcut(QAction *action, const QKeySequence &shortcut)
|
||||
{
|
||||
return KGlobalAccel::setGlobalShortcut(action, QList<QKeySequence>() << shortcut);
|
||||
}
|
||||
|
||||
bool KGlobalAccelPrivate::setShortcutWithDefault(QAction *action, const QList<QKeySequence> &shortcut, KGlobalAccel::GlobalShortcutLoading loadFlag)
|
||||
{
|
||||
if (checkGarbageKeycode(shortcut)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!doRegister(action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
actionDefaultShortcuts.insert(action, shortcut);
|
||||
actionShortcuts.insert(action, shortcut);
|
||||
updateGlobalShortcut(action, KGlobalAccelPrivate::DefaultShortcut | KGlobalAccelPrivate::ActiveShortcut, loadFlag);
|
||||
return true;
|
||||
}
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const KGlobalAccel::MatchType &type)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument << static_cast<int>(type);
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, KGlobalAccel::MatchType &type)
|
||||
{
|
||||
argument.beginStructure();
|
||||
int arg;
|
||||
argument >> arg;
|
||||
type = static_cast<KGlobalAccel::MatchType>(arg);
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
#include "moc_kglobalaccel.cpp"
|
||||
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2001, 2002 Ellis Whitehead <ellis@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef _KGLOBALACCEL_H_
|
||||
#define _KGLOBALACCEL_H_
|
||||
|
||||
#include "kglobalshortcutinfo.h"
|
||||
#include <kglobalaccel_export.h>
|
||||
|
||||
#include <QKeySequence>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
|
||||
class QAction;
|
||||
class OrgKdeKglobalaccelComponentInterface;
|
||||
|
||||
/**
|
||||
* @short Configurable global shortcut support
|
||||
*
|
||||
* KGlobalAccel allows you to have global accelerators that are independent of
|
||||
* the focused window. Unlike regular shortcuts, the application's window does not need focus
|
||||
* for them to be activated.
|
||||
*
|
||||
* @see KKeyChooser
|
||||
* @see KKeyDialog
|
||||
*/
|
||||
class KGLOBALACCEL_EXPORT KGlobalAccel : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* An enum about global shortcut setter semantics
|
||||
*/
|
||||
enum GlobalShortcutLoading {
|
||||
/// Look up the action in global settings (using its main component's name and text())
|
||||
/// and set the shortcut as saved there.
|
||||
/// @see setGlobalShortcut()
|
||||
Autoloading = 0x0,
|
||||
/// Prevent autoloading of saved global shortcut for action
|
||||
NoAutoloading = 0x4,
|
||||
};
|
||||
|
||||
/**
|
||||
* Index for actionId QStringLists
|
||||
*/
|
||||
enum actionIdFields {
|
||||
ComponentUnique = 0, //!< Components Unique Name (ID)
|
||||
ActionUnique = 1, //!< Actions Unique Name(ID)
|
||||
ComponentFriendly = 2, //!< Components Friendly Translated Name
|
||||
ActionFriendly = 3, //!< Actions Friendly Translated Name
|
||||
};
|
||||
|
||||
/**
|
||||
* Keysequence match semantics.
|
||||
*
|
||||
* Assuming we have an Emacs-style shortcut, for example (Alt+B, Alt+F, Alt+G) already assigned,
|
||||
* how a new shortcut is compared depends on which value of the enum is used:
|
||||
* @c Equal : Exact matching: (Alt+B, Alt+F, Alt+G)
|
||||
* @c Shadows : Sequence shadowing: (Alt+B, Alt+F), (Alt+F, Alt+G)
|
||||
* @c Shadowed : Sequence being shadowed: (Alt+B, Alt+F, Alt+G, <any key>), (<any key>, Alt+B, Alt+F, Alt+G)
|
||||
*
|
||||
* @since 5.90
|
||||
*/
|
||||
enum MatchType {
|
||||
Equal,
|
||||
Shadows,
|
||||
Shadowed,
|
||||
};
|
||||
Q_ENUM(MatchType)
|
||||
|
||||
/**
|
||||
* Returns (and creates if necessary) the singleton instance
|
||||
*/
|
||||
static KGlobalAccel *self();
|
||||
|
||||
/**
|
||||
* Take away the given shortcut from the named action it belongs to.
|
||||
* This applies to all actions with global shortcuts in any KDE application.
|
||||
*
|
||||
* @see promptStealShortcutSystemwide()
|
||||
*/
|
||||
static void stealShortcutSystemwide(const QKeySequence &seq);
|
||||
|
||||
/**
|
||||
* Clean the shortcuts for component @a componentUnique.
|
||||
*
|
||||
* If the component is not active all global shortcut registrations are
|
||||
* purged and the component is removed completely.
|
||||
*
|
||||
* If the component is active all global shortcut registrations not in use
|
||||
* will be purged. If there is no shortcut registration left the component
|
||||
* is purged too.
|
||||
*
|
||||
* If a purged component or shortcut is activated the next time it will
|
||||
* reregister itself. All you probably will lose on wrong usage are the
|
||||
* user's set shortcuts.
|
||||
*
|
||||
* If you make sure your component is running and all global shortcuts it
|
||||
* has are active this function can be used to clean up the registry.
|
||||
*
|
||||
* Handle with care!
|
||||
*
|
||||
* If the method return @c true at least one shortcut was purged so handle
|
||||
* all previously acquired information with care.
|
||||
*/
|
||||
static bool cleanComponent(const QString &componentUnique);
|
||||
|
||||
/**
|
||||
* Check if @a component is active.
|
||||
*
|
||||
* @param componentUnique the components unique identifier
|
||||
* @return @c true if active, @false if not
|
||||
*/
|
||||
static bool isComponentActive(const QString &componentName);
|
||||
|
||||
/**
|
||||
* Returns a list of global shortcuts registered for the shortcut @p seq.
|
||||
*
|
||||
* If the list contains more that one entry it means the component
|
||||
* that registered the shortcuts uses global shortcut contexts. All
|
||||
* returned shortcuts belong to the same component.
|
||||
*
|
||||
* @param type a value from the KGlobalAccel::MatchType enum
|
||||
*
|
||||
* @since 5.90
|
||||
*/
|
||||
static QList<KGlobalShortcutInfo> globalShortcutsByKey(const QKeySequence &seq, MatchType type = Equal);
|
||||
|
||||
/**
|
||||
* Check if the shortcut @seq is available for the @p component. The
|
||||
* component is only of interest if the current application uses global shortcut
|
||||
* contexts. In that case a global shortcut by @p component in an inactive
|
||||
* global shortcut contexts does not block the @p seq for us.
|
||||
*
|
||||
* @since 4.2
|
||||
*/
|
||||
static bool isGlobalShortcutAvailable(const QKeySequence &seq, const QString &component = QString());
|
||||
|
||||
/**
|
||||
* Show a messagebox to inform the user that a global shortcut is already occupied,
|
||||
* and ask to take it away from its current action(s). This is GUI only, so nothing will
|
||||
* be actually changed.
|
||||
*
|
||||
* @see stealShortcutSystemwide()
|
||||
*
|
||||
* @since 4.2
|
||||
*/
|
||||
static bool promptStealShortcutSystemwide(QWidget *parent, const QList<KGlobalShortcutInfo> &shortcuts, const QKeySequence &seq);
|
||||
|
||||
/**
|
||||
* Assign a default global shortcut for a given QAction.
|
||||
* For more information about global shortcuts @see setShortcut
|
||||
* Upon shortcut change the globalShortcutChanged will be triggered so other applications get notified
|
||||
*
|
||||
* @sa globalShortcutChanged
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
bool setDefaultShortcut(QAction *action, const QList<QKeySequence> &shortcut, GlobalShortcutLoading loadFlag = Autoloading);
|
||||
|
||||
/**
|
||||
* Assign a global shortcut for the given action. Global shortcuts
|
||||
* allow an action to respond to key shortcuts independently of the focused window,
|
||||
* i.e. the action will trigger if the keys were pressed no matter where in the X session.
|
||||
*
|
||||
* The action must have a per main component unique
|
||||
* action->objectName() to enable cross-application bookkeeping. If the action->objectName() is empty this method will
|
||||
* do nothing and will return false.
|
||||
*
|
||||
* It is mandatory that the action->objectName() doesn't change once the shortcut has been successfully registered.
|
||||
*
|
||||
* \note KActionCollection::insert(name, action) will set action's objectName to name so you often
|
||||
* don't have to set an objectName explicitly.
|
||||
*
|
||||
* When an action, identified by main component name and objectName(), is assigned
|
||||
* a global shortcut for the first time on a KDE installation the assignment will
|
||||
* be saved. The shortcut will then be restored every time setGlobalShortcut() is
|
||||
* called with @p loading == Autoloading.
|
||||
*
|
||||
* If you actually want to change the global shortcut you have to set
|
||||
* @p loading to NoAutoloading. The new shortcut will be automatically saved again.
|
||||
*
|
||||
* @param action the action for which the shortcut will be assigned
|
||||
* @param shortcut global shortcut(s) to assign. Will be ignored unless @p loading is set to NoAutoloading or this is the first time ever you call this
|
||||
* method (see above).
|
||||
* @param loadFlag if Autoloading, assign the global shortcut this action has previously had if any.
|
||||
* That way user preferences and changes made to avoid clashes will be conserved.
|
||||
* if NoAutoloading the given shortcut will be assigned without looking up old values.
|
||||
* You should only do this if the user wants to change the shortcut or if you have
|
||||
* another very good reason. Key combinations that clash with other shortcuts will be
|
||||
* dropped.
|
||||
*
|
||||
* \note the default shortcut will never be influenced by autoloading - it will be set as given.
|
||||
* @sa shortcut()
|
||||
* @sa globalShortcutChanged
|
||||
* @since 5.0
|
||||
*/
|
||||
bool setShortcut(QAction *action, const QList<QKeySequence> &shortcut, GlobalShortcutLoading loadFlag = Autoloading);
|
||||
|
||||
/**
|
||||
* Convenient method to set both active and default shortcut.
|
||||
*
|
||||
* If more control for loading the shortcuts is needed use the variants offering more control.
|
||||
*
|
||||
* @sa setShortcut
|
||||
* @sa setDefaultShortcut
|
||||
* @since 5.0
|
||||
**/
|
||||
static bool setGlobalShortcut(QAction *action, const QList<QKeySequence> &shortcut);
|
||||
|
||||
/**
|
||||
* Convenient method to set both active and default shortcut.
|
||||
*
|
||||
* This method is suited for the case that only one shortcut is to be configured.
|
||||
*
|
||||
* If more control for loading the shortcuts is needed use the variants offering more control.
|
||||
*
|
||||
* @sa setShortcut
|
||||
* @sa setDefaultShortcut
|
||||
* @since 5.0
|
||||
**/
|
||||
static bool setGlobalShortcut(QAction *action, const QKeySequence &shortcut);
|
||||
|
||||
/**
|
||||
* Get the global default shortcut for this action, if one exists. Global shortcuts
|
||||
* allow your actions to respond to accellerators independently of the focused window.
|
||||
* Unlike regular shortcuts, the application's window does not need focus
|
||||
* for them to be activated.
|
||||
*
|
||||
* @sa setDefaultShortcut()
|
||||
* @since 5.0
|
||||
*/
|
||||
QList<QKeySequence> defaultShortcut(const QAction *action) const;
|
||||
|
||||
/**
|
||||
* Get the global shortcut for this action, if one exists. Global shortcuts
|
||||
* allow your actions to respond to accellerators independently of the focused window.
|
||||
* Unlike regular shortcuts, the application's window does not need focus
|
||||
* for them to be activated.
|
||||
*
|
||||
* @note that this method only works together with setShortcut() because the action pointer
|
||||
* is used to retrieve the result. If you would like to retrieve the shortcut as stored
|
||||
* in the global settings, use the globalShortcut(componentName, actionId) instead.
|
||||
*
|
||||
* @sa setShortcut()
|
||||
* @since 5.0
|
||||
*/
|
||||
QList<QKeySequence> shortcut(const QAction *action) const;
|
||||
|
||||
/**
|
||||
* Retrieves the shortcut as defined in global settings by
|
||||
* componentName (e.g. "kwin") and actionId (e.g. "Kill Window").
|
||||
*
|
||||
* @since 5.10
|
||||
*/
|
||||
QList<QKeySequence> globalShortcut(const QString &componentName, const QString &actionId) const;
|
||||
|
||||
/**
|
||||
* Unregister and remove all defined global shortcuts for the given action.
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
void removeAllShortcuts(QAction *action);
|
||||
|
||||
/**
|
||||
* Returns true if a shortcut or a default shortcut has been registered for the given action
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
bool hasShortcut(const QAction *action) const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when the global shortcut is changed. A global shortcut is
|
||||
* subject to be changed by the global shortcuts kcm.
|
||||
*
|
||||
* @param action pointer to the action for which the changed shortcut was registered
|
||||
* @param seq the key sequence that corresponds to the changed shortcut
|
||||
*
|
||||
* @see setGlobalShortcut
|
||||
* @see setDefaultShortcut
|
||||
* @since 5.0
|
||||
*/
|
||||
void globalShortcutChanged(QAction *action, const QKeySequence &seq);
|
||||
void globalShortcutActiveChanged(QAction *action, bool active);
|
||||
|
||||
private:
|
||||
/// Creates a new KGlobalAccel object
|
||||
KGLOBALACCEL_NO_EXPORT KGlobalAccel();
|
||||
|
||||
/// Destructor
|
||||
KGLOBALACCEL_NO_EXPORT ~KGlobalAccel() override;
|
||||
|
||||
//! get component @p componentUnique
|
||||
KGLOBALACCEL_NO_EXPORT OrgKdeKglobalaccelComponentInterface *getComponent(const QString &componentUnique);
|
||||
|
||||
class KGlobalAccelPrivate *const d;
|
||||
|
||||
friend class KGlobalAccelSingleton;
|
||||
};
|
||||
|
||||
KGLOBALACCEL_EXPORT QDBusArgument &operator<<(QDBusArgument &argument, const KGlobalAccel::MatchType &type);
|
||||
KGLOBALACCEL_EXPORT const QDBusArgument &operator>>(const QDBusArgument &argument, KGlobalAccel::MatchType &type);
|
||||
|
||||
#endif // _KGLOBALACCEL_H_
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2001, 2002 Ellis Whitehead <ellis@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KGLOBALACCEL_P_H
|
||||
#define KGLOBALACCEL_P_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QKeySequence>
|
||||
#include <QList>
|
||||
#include <QStringList>
|
||||
|
||||
#include "kglobalaccel.h"
|
||||
#include "kglobalaccel_component_interface.h"
|
||||
#include "kglobalaccel_interface.h"
|
||||
|
||||
enum SetShortcutFlag {
|
||||
SetPresent = 2,
|
||||
NoAutoloading = 4,
|
||||
IsDefault = 8,
|
||||
};
|
||||
|
||||
class KGlobalAccelPrivate
|
||||
{
|
||||
public:
|
||||
enum ShortcutType {
|
||||
/// The shortcut will immediately become active but may be reset to "default".
|
||||
ActiveShortcut = 0x1,
|
||||
/// The shortcut is a default shortcut - it becomes active when somebody decides to
|
||||
/// reset shortcuts to default.
|
||||
DefaultShortcut = 0x2,
|
||||
};
|
||||
|
||||
Q_DECLARE_FLAGS(ShortcutTypes, ShortcutType)
|
||||
enum Removal {
|
||||
SetInactive = 0, ///< Forget the action in this class and mark it as not present in the KDED module
|
||||
UnRegister, ///< Remove any trace of the action in this class and in the KDED module
|
||||
};
|
||||
KGlobalAccelPrivate(KGlobalAccel *);
|
||||
|
||||
/// Propagate any shortcut changes to the KDED module that does the bookkeeping
|
||||
/// and the key grabbing.
|
||||
///@todo KF6
|
||||
void updateGlobalShortcut(/*const would be better*/ QAction *action,
|
||||
KGlobalAccelPrivate::ShortcutTypes actionFlags,
|
||||
KGlobalAccel::GlobalShortcutLoading globalFlags);
|
||||
|
||||
/// Register the action in this class and in the KDED module
|
||||
bool doRegister(QAction *action); //"register" is a C keyword :p
|
||||
/// cf. the RemoveAction enum
|
||||
void remove(QAction *action, Removal r);
|
||||
|
||||
//"private" helpers
|
||||
QString componentUniqueForAction(const QAction *action);
|
||||
QString componentFriendlyForAction(const QAction *action);
|
||||
QStringList makeActionId(const QAction *action);
|
||||
QList<int> intListFromShortcut(const QList<QKeySequence> &cut);
|
||||
QList<QKeySequence> shortcutFromIntList(const QList<int> &list);
|
||||
|
||||
void cleanup();
|
||||
|
||||
// private slot implementations
|
||||
QAction *findAction(const QString &, const QString &);
|
||||
void invokeAction(const QString &, const QString &, qlonglong);
|
||||
void invokeDeactivate(const QString &, const QString &);
|
||||
void shortcutGotChanged(const QStringList &, const QList<int> &);
|
||||
void shortcutsChanged(const QStringList &, const QList<QKeySequence> &);
|
||||
void serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner);
|
||||
void reRegisterAll();
|
||||
|
||||
// for all actions with (isEnabled() && globalShortcutAllowed())
|
||||
QMultiHash<QString, QAction *> nameToAction;
|
||||
QSet<QAction *> actions;
|
||||
|
||||
org::kde::KGlobalAccel *iface();
|
||||
|
||||
//! Get the component @p componentUnique. If @p remember is true the instance is cached and we
|
||||
//! subscribe to signals about changes to the component.
|
||||
org::kde::kglobalaccel::Component *getComponent(const QString &componentUnique, bool remember);
|
||||
|
||||
//! Our owner
|
||||
KGlobalAccel *q;
|
||||
|
||||
//! The components the application is using
|
||||
QHash<QString, org::kde::kglobalaccel::Component *> components;
|
||||
QMap<const QAction *, QList<QKeySequence>> actionDefaultShortcuts;
|
||||
QMap<const QAction *, QList<QKeySequence>> actionShortcuts;
|
||||
|
||||
bool setShortcutWithDefault(QAction *action, const QList<QKeySequence> &shortcut, KGlobalAccel::GlobalShortcutLoading loadFlag);
|
||||
|
||||
void unregister(const QStringList &actionId);
|
||||
void setInactive(const QStringList &actionId);
|
||||
|
||||
private:
|
||||
org::kde::KGlobalAccel *m_iface = nullptr;
|
||||
QPointer<QAction> m_lastActivatedAction;
|
||||
QDBusServiceWatcher *m_watcher;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(KGlobalAccelPrivate::ShortcutTypes)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kglobalshortcutinfo.h"
|
||||
#include "kglobalshortcutinfo_p.h"
|
||||
|
||||
KGlobalShortcutInfo::KGlobalShortcutInfo()
|
||||
: d(new KGlobalShortcutInfoPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
KGlobalShortcutInfo::KGlobalShortcutInfo(const KGlobalShortcutInfo &rhs)
|
||||
: QObject()
|
||||
, d(new KGlobalShortcutInfoPrivate)
|
||||
{
|
||||
d->contextUniqueName = rhs.d->contextUniqueName;
|
||||
d->contextFriendlyName = rhs.d->contextFriendlyName;
|
||||
d->componentFriendlyName = rhs.d->componentFriendlyName;
|
||||
d->componentUniqueName = rhs.d->componentUniqueName;
|
||||
d->friendlyName = rhs.d->friendlyName;
|
||||
d->uniqueName = rhs.d->uniqueName;
|
||||
d->keys = rhs.d->keys;
|
||||
d->defaultKeys = rhs.d->defaultKeys;
|
||||
}
|
||||
|
||||
KGlobalShortcutInfo::~KGlobalShortcutInfo()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
KGlobalShortcutInfo &KGlobalShortcutInfo::operator=(const KGlobalShortcutInfo &rhs)
|
||||
{
|
||||
KGlobalShortcutInfo tmp(rhs);
|
||||
KGlobalShortcutInfoPrivate *swap;
|
||||
swap = d;
|
||||
d = tmp.d;
|
||||
tmp.d = swap;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QString KGlobalShortcutInfo::contextFriendlyName() const
|
||||
{
|
||||
return d->contextFriendlyName.isEmpty() ? d->contextUniqueName : d->contextFriendlyName;
|
||||
}
|
||||
|
||||
QString KGlobalShortcutInfo::contextUniqueName() const
|
||||
{
|
||||
return d->contextUniqueName;
|
||||
}
|
||||
|
||||
QString KGlobalShortcutInfo::componentFriendlyName() const
|
||||
{
|
||||
return d->componentFriendlyName.isEmpty() ? d->componentUniqueName : d->componentFriendlyName;
|
||||
}
|
||||
|
||||
QString KGlobalShortcutInfo::componentUniqueName() const
|
||||
{
|
||||
return d->componentUniqueName;
|
||||
}
|
||||
|
||||
QList<QKeySequence> KGlobalShortcutInfo::defaultKeys() const
|
||||
{
|
||||
return d->defaultKeys;
|
||||
}
|
||||
|
||||
QString KGlobalShortcutInfo::friendlyName() const
|
||||
{
|
||||
return d->friendlyName;
|
||||
}
|
||||
|
||||
QList<QKeySequence> KGlobalShortcutInfo::keys() const
|
||||
{
|
||||
return d->keys;
|
||||
}
|
||||
|
||||
QString KGlobalShortcutInfo::uniqueName() const
|
||||
{
|
||||
return d->uniqueName;
|
||||
}
|
||||
|
||||
#include "moc_kglobalshortcutinfo.cpp"
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KGLOBALSHORTCUTINFO_H
|
||||
#define KGLOBALSHORTCUTINFO_H
|
||||
|
||||
#include <kglobalaccel_export.h>
|
||||
|
||||
#include <QDBusArgument>
|
||||
#include <QKeySequence>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
|
||||
class KGlobalShortcutInfoPrivate;
|
||||
|
||||
/**
|
||||
* @author Michael Jansen <kde@michael-jansen.biz>
|
||||
*/
|
||||
class KGLOBALACCEL_EXPORT KGlobalShortcutInfo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_CLASSINFO("D-Bus Interface", "org.kde.kglobalaccel.KShortcutInfo")
|
||||
|
||||
/* clang-format off */
|
||||
Q_SCRIPTABLE Q_PROPERTY(QString uniqueName READ uniqueName)
|
||||
Q_SCRIPTABLE Q_PROPERTY(QString friendlyName READ friendlyName)
|
||||
|
||||
Q_SCRIPTABLE Q_PROPERTY(QString componentUniqueName READ componentUniqueName)
|
||||
Q_SCRIPTABLE Q_PROPERTY(QString componentFriendlyName READ componentFriendlyName)
|
||||
|
||||
Q_SCRIPTABLE Q_PROPERTY(QString contextUniqueName READ contextUniqueName)
|
||||
Q_SCRIPTABLE Q_PROPERTY(QString contextFriendlyName READ contextFriendlyName)
|
||||
|
||||
Q_SCRIPTABLE Q_PROPERTY(QList<QKeySequence> keys READ keys)
|
||||
Q_SCRIPTABLE Q_PROPERTY(QList<QKeySequence> defaultKeys READ keys)
|
||||
|
||||
public:
|
||||
KGlobalShortcutInfo();
|
||||
/* clang-format on */
|
||||
|
||||
KGlobalShortcutInfo(const KGlobalShortcutInfo &rhs);
|
||||
|
||||
~KGlobalShortcutInfo() override;
|
||||
|
||||
KGlobalShortcutInfo &operator=(const KGlobalShortcutInfo &rhs);
|
||||
|
||||
QString contextFriendlyName() const;
|
||||
|
||||
QString contextUniqueName() const;
|
||||
|
||||
QString componentFriendlyName() const;
|
||||
|
||||
QString componentUniqueName() const;
|
||||
|
||||
QList<QKeySequence> defaultKeys() const;
|
||||
|
||||
QString friendlyName() const;
|
||||
|
||||
QList<QKeySequence> keys() const;
|
||||
|
||||
QString uniqueName() const;
|
||||
|
||||
private:
|
||||
friend class GlobalShortcut;
|
||||
|
||||
friend KGLOBALACCEL_EXPORT const QDBusArgument &operator>>(const QDBusArgument &argument, KGlobalShortcutInfo &shortcut);
|
||||
friend KGLOBALACCEL_EXPORT const QDBusArgument &operator>>(const QDBusArgument &argument, QKeySequence &sequence);
|
||||
|
||||
//! Implementation details
|
||||
KGlobalShortcutInfoPrivate *d;
|
||||
};
|
||||
|
||||
KGLOBALACCEL_EXPORT QDBusArgument &operator<<(QDBusArgument &argument, const KGlobalShortcutInfo &shortcut);
|
||||
KGLOBALACCEL_EXPORT QDBusArgument &operator<<(QDBusArgument &argument, const QKeySequence &sequence);
|
||||
|
||||
KGLOBALACCEL_EXPORT const QDBusArgument &operator>>(const QDBusArgument &argument, KGlobalShortcutInfo &shortcut);
|
||||
KGLOBALACCEL_EXPORT const QDBusArgument &operator>>(const QDBusArgument &argument, QKeySequence &sequence);
|
||||
|
||||
Q_DECLARE_METATYPE(KGlobalShortcutInfo)
|
||||
|
||||
#endif /* #ifndef KGLOBALSHORTCUTINFO_H */
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kglobalshortcutinfo.h"
|
||||
#include "kglobalshortcutinfo_p.h"
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const QKeySequence &sequence)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument.beginArray(qMetaTypeId<int>());
|
||||
for (int i = 0; i < maxSequenceLength; i++) {
|
||||
argument << (i < sequence.count() ? sequence[i].toCombined() : 0);
|
||||
}
|
||||
argument.endArray();
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, QKeySequence &sequence)
|
||||
{
|
||||
int s1;
|
||||
int s2;
|
||||
int s3;
|
||||
int s4;
|
||||
argument.beginStructure();
|
||||
argument.beginArray();
|
||||
argument >> s1 >> s2 >> s3 >> s4;
|
||||
sequence = QKeySequence(s1, s2, s3, s4);
|
||||
argument.endArray();
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const KGlobalShortcutInfo &shortcut)
|
||||
{
|
||||
argument.beginStructure();
|
||||
/* clang-format off */
|
||||
argument << shortcut.uniqueName()
|
||||
<< shortcut.friendlyName()
|
||||
<< shortcut.componentUniqueName()
|
||||
<< shortcut.componentFriendlyName()
|
||||
<< shortcut.contextUniqueName()
|
||||
<< shortcut.contextFriendlyName();
|
||||
/* clang-format on */
|
||||
argument.beginArray(qMetaTypeId<int>());
|
||||
|
||||
const QList<QKeySequence> keys = shortcut.keys();
|
||||
for (const QKeySequence &key : keys) {
|
||||
argument << key[0].toCombined();
|
||||
}
|
||||
argument.endArray();
|
||||
argument.beginArray(qMetaTypeId<int>());
|
||||
|
||||
const QList<QKeySequence> defaultKeys = shortcut.defaultKeys();
|
||||
for (const QKeySequence &key : defaultKeys) {
|
||||
argument << key[0].toCombined();
|
||||
}
|
||||
argument.endArray();
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, KGlobalShortcutInfo &shortcut)
|
||||
{
|
||||
argument.beginStructure();
|
||||
/* clang-format off */
|
||||
argument >> shortcut.d->uniqueName
|
||||
>> shortcut.d->friendlyName
|
||||
>> shortcut.d->componentUniqueName
|
||||
>> shortcut.d->componentFriendlyName
|
||||
>> shortcut.d->contextUniqueName
|
||||
>> shortcut.d->contextFriendlyName;
|
||||
/* clang-format on */
|
||||
|
||||
argument.beginArray();
|
||||
while (!argument.atEnd()) {
|
||||
int key;
|
||||
argument >> key;
|
||||
shortcut.d->keys.append(QKeySequence(key));
|
||||
}
|
||||
argument.endArray();
|
||||
argument.beginArray();
|
||||
while (!argument.atEnd()) {
|
||||
int key;
|
||||
argument >> key;
|
||||
shortcut.d->defaultKeys.append(QKeySequence(key));
|
||||
}
|
||||
argument.endArray();
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KGLOBALSHORTCUTINFO_P_H
|
||||
#define KGLOBALSHORTCUTINFO_P_H
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
static const int maxSequenceLength = 4;
|
||||
|
||||
#include "kglobalaccel.h"
|
||||
#include "kglobalshortcutinfo.h"
|
||||
|
||||
class KGlobalShortcutInfoPrivate
|
||||
{
|
||||
public:
|
||||
QString contextUniqueName;
|
||||
QString contextFriendlyName;
|
||||
QString componentUniqueName;
|
||||
QString componentFriendlyName;
|
||||
QString uniqueName;
|
||||
QString friendlyName;
|
||||
QList<QKeySequence> keys;
|
||||
QList<QKeySequence> defaultKeys;
|
||||
};
|
||||
|
||||
#endif /* #ifndef KGLOBALSHORTCUTINFO_P_H */
|
||||
@@ -0,0 +1,135 @@
|
||||
<!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>
|
||||
<!-- Deprecated use unregister -->
|
||||
<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="getGlobalShortcutsByKey">
|
||||
<arg type="a(ssssssaiai)" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<KGlobalShortcutInfo>"/>
|
||||
<arg name="key" type="i" 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>
|
||||
<method name="unregister">
|
||||
<arg type="b" direction="out"/>
|
||||
<arg name="componentUnique" type="s" direction="in"/>
|
||||
<arg name="shortcutUnique" type="s" direction="in"/>
|
||||
</method>
|
||||
<method name="blockGlobalShortcuts">
|
||||
<arg type="b" direction="in"/>
|
||||
</method>
|
||||
<!-- v2 interface -->
|
||||
<signal name="yourShortcutsChanged">
|
||||
<arg name="actionId" type="as" direction="out"/>
|
||||
<arg name="newKeys" type="a(ai)" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QList<QKeySequence>"/>
|
||||
</signal>
|
||||
<method name="shortcutKeys">
|
||||
<arg type="a(ai)" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<QKeySequence>"/>
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
</method>
|
||||
<method name="defaultShortcutKeys">
|
||||
<arg type="a(ai)" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<QKeySequence>"/>
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
</method>
|
||||
<method name="actionList">
|
||||
<arg type="as" direction="out"/>
|
||||
<arg name="key" type="(ai)" direction="in"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QKeySequence"/>
|
||||
</method>
|
||||
<method name="setShortcutKeys">
|
||||
<arg type="a(ai)" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<QKeySequence>"/>
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
<arg name="keys" type="a(ai)" direction="in"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QList<QKeySequence>"/>
|
||||
<arg name="flags" type="u" direction="in"/>
|
||||
</method>
|
||||
<method name="setForeignShortcutKeys">
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
<arg name="keys" type="a(ai)" direction="in"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QList<QKeySequence>"/>
|
||||
</method>
|
||||
<method name="globalShortcutsByKey">
|
||||
<arg type="a(ssssssaiai)" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<KGlobalShortcutInfo>"/>
|
||||
<arg name="key" type="(ai)" direction="in"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QKeySequence"/>
|
||||
<arg name="matchType" type="(i)" direction="in"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="KGlobalAccel::MatchType"/>
|
||||
</method>
|
||||
<method name="globalShortcutAvailable">
|
||||
<arg type="b" direction="out"/>
|
||||
<arg name="key" type="(ai)" direction="in"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QKeySequence"/>
|
||||
<arg name="component" type="s" direction="in"/>
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<!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>
|
||||
<signal name="globalShortcutReleased">
|
||||
<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="allShortcutInfos">
|
||||
<arg type="a(ssssssaiai)" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<KGlobalShortcutInfo>"/>
|
||||
<arg name="context" type="s" direction="in"/>
|
||||
</method>
|
||||
<method name="allShortcutInfos">
|
||||
<arg type="a(ssssssaiai)" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<KGlobalShortcutInfo>"/>
|
||||
</method>
|
||||
<method name="getShortcutContexts">
|
||||
<arg type="as" direction="out"/>
|
||||
</method>
|
||||
<method name="invokeShortcut">
|
||||
<arg name="actionName" type="s" direction="in"/>
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
#include <QKeySequence>
|
||||
|
||||
#include "kglobalshortcutinfo_p.h"
|
||||
#include "sequencehelpers_p.h"
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
QKeySequence reverseKey(const QKeySequence &key)
|
||||
{
|
||||
int k[maxSequenceLength] = {0, 0, 0, 0};
|
||||
int count = key.count();
|
||||
for (int i = 0; i < count; i++) {
|
||||
k[count - i - 1] = key[i].toCombined();
|
||||
}
|
||||
|
||||
return QKeySequence(k[0], k[1], k[2], k[3]);
|
||||
}
|
||||
|
||||
QKeySequence cropKey(const QKeySequence &key, int count)
|
||||
{
|
||||
if (count < 1) {
|
||||
return key;
|
||||
}
|
||||
|
||||
// Key is shorter than count we want to cut off
|
||||
if (key.count() < count) {
|
||||
return QKeySequence();
|
||||
}
|
||||
|
||||
int k[maxSequenceLength] = {0, 0, 0, 0};
|
||||
// cut from beginning
|
||||
for (int i = count; i < key.count(); i++) {
|
||||
k[i - count] = key[i].toCombined();
|
||||
}
|
||||
|
||||
return QKeySequence(k[0], k[1], k[2], k[3]);
|
||||
}
|
||||
|
||||
bool contains(const QKeySequence &key, const QKeySequence &other)
|
||||
{
|
||||
int minLength = std::min(key.count(), other.count());
|
||||
|
||||
// There's an empty key, assume it matches nothing
|
||||
if (!minLength) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
for (int i = 0; i <= other.count() - minLength; i++) {
|
||||
QKeySequence otherCropped = cropKey(other, i);
|
||||
if (key.matches(otherCropped) == QKeySequence::PartialMatch || reverseKey(key).matches(reverseKey(otherCropped)) == QKeySequence::PartialMatch) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool matchSequences(const QKeySequence &key, const QList<QKeySequence> &keys)
|
||||
{
|
||||
// Since we're testing sequences, we need to check for all possible matches
|
||||
// between existing and new sequences.
|
||||
|
||||
// Let's assume we have (Alt+B, Alt+F, Alt+G) assigned. Examples of bad shortcuts are:
|
||||
// 1) Exact matching: (Alt+B, Alt+F, Alt+G)
|
||||
// 2) Sequence shadowing: (Alt+B, Alt+F)
|
||||
// 3) Sequence being shadowed: (Alt+B, Alt+F, Alt+G, <any key>)
|
||||
// 4) Shadowing at the end: (Alt+F, Alt+G)
|
||||
// 5) Being shadowed from the end: (<any key>, Alt+B, Alt+F, Alt+G)
|
||||
|
||||
for (const QKeySequence &otherKey : keys) {
|
||||
if (otherKey.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (key.matches(otherKey) == QKeySequence::ExactMatch || contains(key, otherKey) || contains(otherKey, key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QKeySequence mangleKey(const QKeySequence &key)
|
||||
{
|
||||
// Qt triggers both shortcuts that include Shift+Backtab and Shift+Tab
|
||||
// when user presses Shift+Tab. Make no difference here.
|
||||
int k[maxSequenceLength] = {0, 0, 0, 0};
|
||||
for (int i = 0; i < key.count(); i++) {
|
||||
// Qt triggers both shortcuts that include Shift+Backtab and Shift+Tab
|
||||
// when user presses Shift+Tab. Make no difference here.
|
||||
int keySym = key[i].toCombined() & ~Qt::KeyboardModifierMask;
|
||||
int keyMod = key[i].toCombined() & Qt::KeyboardModifierMask;
|
||||
if ((keyMod & Qt::SHIFT) && (keySym == Qt::Key_Backtab || keySym == Qt::Key_Tab)) {
|
||||
k[i] = keyMod | Qt::Key_Tab;
|
||||
} else {
|
||||
k[i] = key[i].toCombined();
|
||||
}
|
||||
}
|
||||
|
||||
return QKeySequence(k[0], k[1], k[2], k[3]);
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef SEQUENCEHELPERS_H
|
||||
#define SEQUENCEHELPERS_H
|
||||
|
||||
#include <kglobalaccel_export.h>
|
||||
|
||||
#include <QKeySequence>
|
||||
|
||||
// Some methods are exported for the unittest
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
KGLOBALACCEL_EXPORT QKeySequence reverseKey(const QKeySequence &key);
|
||||
|
||||
KGLOBALACCEL_EXPORT QKeySequence cropKey(const QKeySequence &key, int count);
|
||||
|
||||
bool contains(const QKeySequence &key, const QKeySequence &other);
|
||||
|
||||
KGLOBALACCEL_EXPORT bool matchSequences(const QKeySequence &key, const QList<QKeySequence> &keys);
|
||||
|
||||
KGLOBALACCEL_EXPORT QKeySequence mangleKey(const QKeySequence &key);
|
||||
}
|
||||
|
||||
#endif // SEQUENCEHELPERS_H
|
||||
Reference in New Issue
Block a user