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:
2026-04-14 10:51:06 +01:00
parent 51f3c21121
commit cf12defd28
15214 changed files with 20594243 additions and 269 deletions
@@ -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&lt;int&gt;"/>
</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&lt;QStringList&gt;"/>
</method>
<method name="allActionsForComponent">
<arg type="aas" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList&lt;QStringList&gt;"/>
<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&lt;int&gt;"/>
<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&lt;int&gt;"/>
<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&lt;int&gt;"/>
<arg name="actionId" type="as" direction="in"/>
<arg name="keys" type="ai" direction="in"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QList&lt;int&gt;"/>
<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&lt;int&gt;"/>
</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&lt;KGlobalShortcutInfo&gt;"/>
<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&lt;QKeySequence&gt;"/>
</signal>
<method name="shortcutKeys">
<arg type="a(ai)" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList&lt;QKeySequence&gt;"/>
<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&lt;QKeySequence&gt;"/>
<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&lt;QKeySequence&gt;"/>
<arg name="actionId" type="as" direction="in"/>
<arg name="keys" type="a(ai)" direction="in"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QList&lt;QKeySequence&gt;"/>
<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&lt;QKeySequence&gt;"/>
</method>
<method name="globalShortcutsByKey">
<arg type="a(ssssssaiai)" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList&lt;KGlobalShortcutInfo&gt;"/>
<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&lt;KGlobalShortcutInfo&gt;"/>
<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&lt;KGlobalShortcutInfo&gt;"/>
</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