mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 18:32:49 +00:00
kdeui: rework KGlobalAccel
global shortcut resolution (the KGlobalAccel methods) work on application level, X11 as far as I can tell does not have a method to tell which application has grabbed a key (to fill KGlobalShortcutInfo). other than that no configuration interface for global shortcuts (not by KGlobalAccel itself, plasma and its applets have interface for that), also the shortcuts interface did not and still does not handle global shortcuts well so that is something to look into. one of the problems solved with this change is the fact that multiple plasma applets (e.g. multiple instances of the keyboard applet) could not use the same shortcut, now it is possible. as for which applet gets the shortcut action it is the one that has the grab first - that is how key grabbing works in X11 Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
parent
936a4e3e84
commit
594e901c4a
16 changed files with 246 additions and 1498 deletions
|
@ -150,8 +150,6 @@ set(kdeui_LIB_SRCS
|
|||
shortcuts/kshortcutschemeshelper.cpp
|
||||
shortcuts/kstandardshortcut.cpp
|
||||
shortcuts/kglobalaccel.cpp
|
||||
shortcuts/kglobalshortcutinfo.cpp
|
||||
shortcuts/kglobalshortcutinfo_dbus.cpp
|
||||
shortcuts/kacceleratormanager.cpp
|
||||
shortcuts/kcheckaccelerators.cpp
|
||||
spell/kspeller.cpp
|
||||
|
@ -242,16 +240,6 @@ if (X11_Xkb_FOUND AND X11_Xkbfile_FOUND)
|
|||
)
|
||||
endif()
|
||||
|
||||
set(kglobalaccel_xml shortcuts/org.kde.KGlobalAccel.xml)
|
||||
set_source_files_properties(${kglobalaccel_xml} PROPERTIES INCLUDE "kglobalshortcutinfo_p.h")
|
||||
qt4_add_dbus_interface(kdeui_LIB_SRCS ${kglobalaccel_xml} kglobalaccel_interface )
|
||||
install(FILES ${kglobalaccel_xml} DESTINATION ${KDE4_DBUS_INTERFACES_INSTALL_DIR})
|
||||
|
||||
set(kglobalaccel_component_xml shortcuts/org.kde.kglobalaccel.Component.xml)
|
||||
set_source_files_properties(${kglobalaccel_component_xml} PROPERTIES INCLUDE "kglobalshortcutinfo_p.h")
|
||||
qt4_add_dbus_interface(kdeui_LIB_SRCS ${kglobalaccel_component_xml} kglobalaccel_component_interface )
|
||||
install(FILES ${kglobalaccel_component_xml} DESTINATION ${KDE4_DBUS_INTERFACES_INSTALL_DIR})
|
||||
|
||||
set_source_files_properties(
|
||||
${CMAKE_SOURCE_DIR}/kdeui/kernel/kapplication.cpp
|
||||
${CMAKE_SOURCE_DIR}/kdeui/kernel/kglobalsettings.cpp
|
||||
|
@ -407,8 +395,6 @@ install(
|
|||
shortcuts/kshortcut.h
|
||||
shortcuts/kstandardshortcut.h
|
||||
shortcuts/kglobalaccel.h
|
||||
shortcuts/kglobalshortcutinfo.h
|
||||
shortcuts/kglobalshortcutinfo_p.h
|
||||
shortcuts/kacceleratormanager.h
|
||||
spell/kspeller.h
|
||||
spell/kspellhighlighter.h
|
||||
|
|
|
@ -440,7 +440,6 @@ public:
|
|||
bool event(QEvent *event);
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
/**
|
||||
* Emitted when the action is triggered. Also provides the state of the
|
||||
* keyboard modifiers and mouse buttons at the time.
|
||||
|
@ -454,7 +453,7 @@ Q_SIGNALS:
|
|||
void globalShortcutChanged(const QKeySequence&);
|
||||
|
||||
private:
|
||||
friend class KGlobalAccelPrivate; // Needs access to the component
|
||||
friend class KGlobalAccel; // Needs access to the component
|
||||
friend class KActionCollectionPrivate; // Needs access to the component
|
||||
friend class KShortcutsEditorDelegate; // Needs access to the component
|
||||
Q_PRIVATE_SLOT(d, void slotTriggered())
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
# Overall summary of global shortcut implementation
|
||||
|
||||
## KAction, KGlobalAccel and KdedGlobalAccel
|
||||
|
||||
[Basic functionality]
|
||||
- You call KAction::setGlobalShortcut() to set a shortcut for an action.
|
||||
KAction then calls KGlobalAccel which is, among other things, the interface
|
||||
to KdedGlobalAccel (communication via DBus). KdedGlobalAccel is a KDED module
|
||||
as you might have guessed.
|
||||
- KdedGlobalAccel then grabs the shortcut key in a platform-specific way and
|
||||
makes an entry of the mapping key<->action where actions are identified by
|
||||
their main component name and their own name.
|
||||
- When a key grab triggers, KdedGlobalAccel calls (via DBus) KGlobalAccel
|
||||
which tells the action to trigger.
|
||||
|
||||
The KdedGlobalAccel is responsible for actually handling the shortcuts,
|
||||
loading and saving the shortcut keys to kglobalshortcutrc. It doesn't
|
||||
really know the actions, it just know what KGlobalAccel gave it.
|
||||
|
||||
[Conflict resolution]
|
||||
KdedGlobalAccel has a list of all global shortcuts. If you try to assign a key
|
||||
twice, it will tell the appropriate KdedGlobalAccel/KGlobalAccel that the
|
||||
corresponding shortcut was changed to an empty one, which goes back to the
|
||||
KAction.
|
||||
When manually assigning shortcuts, the config widget asks
|
||||
KGlobalAccel/KdedGlobalAccel for conflicts and presents options to the user
|
||||
to fix them.
|
||||
To prevent all clashes as good as possible, KdedGlobalAccel remembers key<->
|
||||
action mappings even after the corresponding application shuts down.
|
||||
|
||||
[More details]
|
||||
KAction instances talk to the KGlobalAccel singleton to make it aware of global
|
||||
shortcuts changes via KGlobalAccel::updateGlobalShortcuts() (to define the shortcut)
|
||||
KGlobalAccel::updateGlobalShortcutsAllowed() (to enable/disable the shortcut)
|
||||
|
||||
These two methods do the following:
|
||||
- Create an action "id" which is a QStringList of two items: the application
|
||||
component and the action text (this is bound to cause trouble with i18n)
|
||||
|
||||
- Convert the KAction shortcuts to a QList<int>
|
||||
|
||||
- Pass all this via DBus to the KdedGlobalAccel instance, which lives in the
|
||||
kded4 process.
|
||||
|
||||
KGlobalAccel::updateGlobalShortcutsAllowed(true) sets the "SetPresent" flag when calling
|
||||
kglobalaccel, which makes kglobalaccel actually grab the key shortcut
|
||||
(so that the grab is done after the action has been defined, and only if it is enabled).
|
||||
kglobalaccel must know about inactive global shortcuts too (e.g. those defined in
|
||||
applications not running at the moment), for conflict resolution.
|
||||
|
||||
## kdebase side: keyboard shortcuts KCM
|
||||
|
||||
The keyboard shortcuts KCM can be found in kdebase/workspace/kcontrol/keys/
|
||||
|
||||
The KCM gets the global shortcut info from the KdedGlobalAccel instance via
|
||||
DBus. It uses KShortcutsEditor to let the user edit the shortcuts. Since
|
||||
KShortcutsEditor manipulates KAction instances, the kcm creates "fake" actions.
|
||||
|
||||
--
|
||||
|
||||
Aurélien Gâteau, 2008.02.01
|
||||
aurelien.gateau@free.fr
|
||||
David Faure, 2008.02.05
|
||||
faure@kde.org
|
|
@ -23,441 +23,259 @@
|
|||
#include "kglobalaccel.h"
|
||||
#include "kglobalaccel_p.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QtDBus/QDBusInterface>
|
||||
#include <QtDBus/QDBusMetaType>
|
||||
#ifdef Q_WS_X11
|
||||
#include <QtGui/qx11info_x11.h>
|
||||
#include <netwm_def.h>
|
||||
#endif
|
||||
|
||||
#include <kdebug.h>
|
||||
#include <ktoolinvocation.h>
|
||||
#include <kaboutdata.h>
|
||||
#include <kcomponentdata.h>
|
||||
#include "kaction.h"
|
||||
#include "kapplication.h"
|
||||
#include "klocale.h"
|
||||
#include "kaboutdata.h"
|
||||
#include "kaction_p.h"
|
||||
#include "kmessagebox.h"
|
||||
#include "kshortcut.h"
|
||||
#include "kkeyserver.h"
|
||||
#include "kxerrorhandler.h"
|
||||
#include "kdebug.h"
|
||||
|
||||
org::kde::kglobalaccel::Component *KGlobalAccelPrivate::getComponent(const QString &componentUnique, bool remember)
|
||||
K_GLOBAL_STATIC(KGlobalAccel, kGlobalAccel)
|
||||
|
||||
struct KGlobalAccelStruct
|
||||
{
|
||||
// Check if we already have this component
|
||||
if (components.contains(componentUnique)) {
|
||||
return components[componentUnique];
|
||||
KAction* action;
|
||||
uint keyModX;
|
||||
int keyCodeX;
|
||||
|
||||
bool operator==(const KGlobalAccelStruct &other) const
|
||||
{ return (action == other.action && keyModX == other.keyModX && keyCodeX == other.keyCodeX); }
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
static int XGrabErrorHandler(Display *, XErrorEvent *e) {
|
||||
if (e->error_code != BadAccess) {
|
||||
kWarning() << "grabKey: got X error " << e->type << " instead of BadAccess";
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static bool kGrabKey(const int keyQt, uint &keyModX, int &keyCodeX)
|
||||
{
|
||||
if (keyQt == 0) {
|
||||
kDebug() << "null keyQt";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
Display* display = QX11Info::display();
|
||||
const Qt::HANDLE approotwindow = QX11Info::appRootWindow();
|
||||
if (!display || !approotwindow) {
|
||||
kWarning() << "null display or application root window";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reply.error().name() == "org.kde.kglobalaccel.NoSuchComponent") {
|
||||
// No problem. The component doesn't exists. That's normal
|
||||
return nullptr;
|
||||
uint keySymX = 0;
|
||||
if (!KKeyServer::keyQtToModX(keyQt, &keyModX)) {
|
||||
kWarning() << "keyQt (0x" << QByteArray::number(keyQt, 16) << ") failed to resolve to x11 modifier";
|
||||
return false;
|
||||
}
|
||||
if (!KKeyServer::keyQtToSymX(keyQt, (int *)&keySymX) ) {
|
||||
kWarning() << "keyQt (0x" << QByteArray::number(keyQt, 16) << ") failed to resolve to x11 keycode";
|
||||
return false;
|
||||
}
|
||||
|
||||
keyCodeX = XKeysymToKeycode(display, keySymX);
|
||||
if (!keyCodeX) {
|
||||
kWarning() << "keyQt (0x" << QByteArray::number(keyQt, 16) << ") was resolved to x11 keycode 0";
|
||||
return false;
|
||||
}
|
||||
|
||||
KXErrorHandler handler(XGrabErrorHandler);
|
||||
XGrabKey(
|
||||
display, keyCodeX, keyModX & KKeyServer::accelModMaskX(),
|
||||
approotwindow, True, GrabModeAsync, GrabModeAsync
|
||||
);
|
||||
return !handler.error(true);
|
||||
}
|
||||
|
||||
static bool kUngrabKey(const uint keyModX, const int keyCodeX)
|
||||
{
|
||||
Display* display = QX11Info::display();
|
||||
const Qt::HANDLE approotwindow = QX11Info::appRootWindow();
|
||||
if (!display || !approotwindow) {
|
||||
kWarning() << "null display or application root window";
|
||||
return false;
|
||||
}
|
||||
KXErrorHandler handler(XGrabErrorHandler);
|
||||
XUngrabKey(display, keyCodeX, keyModX & KKeyServer::accelModMaskX(), approotwindow);
|
||||
return !handler.error(true);
|
||||
}
|
||||
|
||||
class KGlobalAccelFilter : public QWidget
|
||||
{
|
||||
public:
|
||||
QList<KGlobalAccelStruct> shortcuts;
|
||||
|
||||
protected:
|
||||
bool x11Event(XEvent *xevent) final;
|
||||
};
|
||||
|
||||
bool KGlobalAccelFilter::x11Event(XEvent *xevent)
|
||||
{
|
||||
if (xevent->type == KeyPress) {
|
||||
foreach (const KGlobalAccelStruct &shortcut, shortcuts) {
|
||||
if (xevent->xkey.state == shortcut.keyModX && xevent->xkey.keycode == shortcut.keyCodeX) {
|
||||
kDebug() << "triggering action" << shortcut.keyModX << shortcut.keyCodeX << shortcut.action;
|
||||
shortcut.action->trigger();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
KGlobalAccelPrivate::KGlobalAccelPrivate(KGlobalAccel *_q)
|
||||
: q(_q),
|
||||
filter(nullptr)
|
||||
{
|
||||
if (kapp) {
|
||||
filter = new KGlobalAccelFilter();
|
||||
kapp->installX11EventFilter(filter);
|
||||
kDebug() << "KGlobalAccelFilter is installed";
|
||||
} else {
|
||||
kWarning() << "no KApplication instance, KGlobalAccel will not work";
|
||||
}
|
||||
}
|
||||
|
||||
KGlobalAccelPrivate::~KGlobalAccelPrivate()
|
||||
{
|
||||
if (filter) {
|
||||
if (kapp) {
|
||||
kDebug() << "removing KGlobalAccelFilter";
|
||||
kapp->removeX11EventFilter(filter);
|
||||
}
|
||||
|
||||
// An unknown error.
|
||||
kError() << "Failed to get dbus path for component " << componentUnique << reply.error();
|
||||
return nullptr;
|
||||
QList<KGlobalAccelStruct> shortcuts = filter->shortcuts;
|
||||
kDebug() << "releasing shortcuts" << shortcuts.size();
|
||||
foreach (const KGlobalAccelStruct &shortcut, shortcuts) {
|
||||
remove(shortcut.action, KGlobalAccelPrivate::SetInactive);
|
||||
}
|
||||
delete filter;
|
||||
}
|
||||
|
||||
// Now get the component
|
||||
org::kde::kglobalaccel::Component *component = new org::kde::kglobalaccel::Component(
|
||||
"org.kde.kglobalaccel",
|
||||
reply.value().path(),
|
||||
QDBusConnection::sessionBus(),
|
||||
q);
|
||||
|
||||
// No component no cleaning
|
||||
if (!component->isValid()) {
|
||||
kDebug() << "Failed to get component" << componentUnique << QDBusConnection::sessionBus().lastError();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (remember) {
|
||||
// Connect to the signals we are interested in.
|
||||
q->connect(component, SIGNAL(globalShortcutPressed(QString,QString,qlonglong)),
|
||||
SLOT(_k_invokeAction(QString,QString,qlonglong)));
|
||||
|
||||
components[componentUnique] = component;
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
|
||||
|
||||
KGlobalAccelPrivate::KGlobalAccelPrivate(KGlobalAccel *q)
|
||||
: iface("org.kde.kglobalaccel", "/kglobalaccel", QDBusConnection::sessionBus()),
|
||||
q(q)
|
||||
void KGlobalAccelPrivate::updateGlobalShortcut(KAction *action, uint flags)
|
||||
{
|
||||
QDBusServiceWatcher *watcher = new QDBusServiceWatcher(iface.service(),
|
||||
QDBusConnection::sessionBus(),
|
||||
QDBusServiceWatcher::WatchForOwnerChange,
|
||||
q);
|
||||
q->connect(watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)),
|
||||
q, SLOT(_k_serviceOwnerChanged(QString,QString,QString)));
|
||||
if (!remove(action, KGlobalAccelPrivate::SetInactive)) {
|
||||
return;
|
||||
}
|
||||
doRegister(action);
|
||||
}
|
||||
|
||||
|
||||
void KGlobalAccelPrivate::readComponentData(const KComponentData &componentData)
|
||||
void KGlobalAccelPrivate::doRegister(KAction *action)
|
||||
{
|
||||
Q_ASSERT(!componentData.componentName().isEmpty());
|
||||
|
||||
mainComponent = componentData;
|
||||
if (componentData.aboutData()->programName().isEmpty()) {
|
||||
kDebug(125) << componentData.componentName() << " has empty programName()";
|
||||
foreach (const QKeySequence &keysequnece, action->globalShortcut().toList()) {
|
||||
uint keyModX = 0;
|
||||
int keyCodeX = 0;
|
||||
if (kGrabKey(keysequnece, keyModX, keyCodeX)) {
|
||||
KGlobalAccelStruct shortcut;
|
||||
shortcut.action = action;
|
||||
shortcut.keyModX = keyModX;
|
||||
shortcut.keyCodeX = keyCodeX;
|
||||
filter->shortcuts.append(shortcut);
|
||||
kDebug() << "grabbed shortcut" << shortcut.keyModX << shortcut.keyCodeX << shortcut.action;
|
||||
break;
|
||||
} else {
|
||||
kWarning() << "could not grab shortcut" << keysequnece << action;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool KGlobalAccelPrivate::remove(KAction *action, Removal r)
|
||||
{
|
||||
Q_UNUSED(r);
|
||||
foreach (const KGlobalAccelStruct &shortcut, filter->shortcuts) {
|
||||
if (shortcut.action == action) {
|
||||
if (kUngrabKey(shortcut.keyModX, shortcut.keyCodeX)) {
|
||||
kDebug() << "ungrabbed shortcut" << shortcut.keyModX << shortcut.keyCodeX << shortcut.action;
|
||||
filter->shortcuts.removeOne(shortcut);
|
||||
return true;
|
||||
}
|
||||
kWarning() << "could not ungrab shortcut" << shortcut.keyModX << shortcut.keyCodeX << shortcut.action;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
KGlobalAccel::KGlobalAccel()
|
||||
: d(new KGlobalAccelPrivate(this))
|
||||
{
|
||||
qDBusRegisterMetaType<QList<int> >();
|
||||
qDBusRegisterMetaType<QList<QStringList> >();
|
||||
qDBusRegisterMetaType<KGlobalShortcutInfo>();
|
||||
qDBusRegisterMetaType<QList<KGlobalShortcutInfo> >();
|
||||
|
||||
connect(&d->iface, SIGNAL(yourShortcutGotChanged(QStringList,QList<int>)),
|
||||
SLOT(_k_shortcutGotChanged(QStringList,QList<int>)));
|
||||
|
||||
if (KGlobal::hasMainComponent()) {
|
||||
d->readComponentData( KGlobal::mainComponent() );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
KGlobalAccel::~KGlobalAccel()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
|
||||
void KGlobalAccel::activateGlobalShortcutContext(
|
||||
const QString &contextUnique,
|
||||
const QString &contextFriendly,
|
||||
const KComponentData &component)
|
||||
KGlobalAccel* KGlobalAccel::self()
|
||||
{
|
||||
Q_UNUSED(contextFriendly);
|
||||
// TODO: provide contextFriendly
|
||||
self()->d->iface.activateGlobalShortcutContext(component.aboutData()->programName(), contextUnique);
|
||||
}
|
||||
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
KGlobalAccel *KGlobalAccel::self()
|
||||
{
|
||||
K_GLOBAL_STATIC(KGlobalAccel, s_instance)
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
|
||||
void KGlobalAccelPrivate::doRegister(KAction *action)
|
||||
{
|
||||
if (!action || action->objectName().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool isRegistered = actions.contains(action);
|
||||
if (isRegistered) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList actionId = makeActionId(action);
|
||||
|
||||
nameToAction.insertMulti(actionId.at(KGlobalAccel::ActionUnique), action);
|
||||
actions.insert(action);
|
||||
iface.doRegister(actionId);
|
||||
}
|
||||
|
||||
|
||||
void KGlobalAccelPrivate::remove(KAction *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)
|
||||
iface.unregister(actionId.at(KGlobalAccel::ComponentUnique), actionId.at(KGlobalAccel::ActionUnique));
|
||||
} else {
|
||||
// If the action is a configurationAction wen only remove it from our
|
||||
// internal registry. That happened above.
|
||||
if (!action->property("isConfigurationAction").toBool()) {
|
||||
// If it's a session shortcut unregister it.
|
||||
if (action->objectName().startsWith(QLatin1String("_k_session:"))) {
|
||||
iface.unregister(actionId.at(KGlobalAccel::ComponentUnique), actionId.at(KGlobalAccel::ActionUnique));
|
||||
} else {
|
||||
iface.setInactive(actionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void KGlobalAccelPrivate::updateGlobalShortcut(KAction *action, uint flags)
|
||||
{
|
||||
// No action or no objectname -> Do nothing
|
||||
// KAction::setGlobalShortcut informs the user
|
||||
if (!action || action->objectName().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList actionId = makeActionId(action);
|
||||
const KShortcut activeShortcut = action->globalShortcut();
|
||||
const KShortcut defaultShortcut = action->globalShortcut(KAction::DefaultShortcut);
|
||||
|
||||
uint setterFlags = 0;
|
||||
if (flags & KAction::NoAutoloading) {
|
||||
setterFlags |= NoAutoloading;
|
||||
}
|
||||
|
||||
if (flags & KAction::ActiveShortcut) {
|
||||
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 QList<int> result = iface.setShortcut(
|
||||
actionId,
|
||||
intListFromShortcut(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 KShortcut scResult(shortcutFromIntList(result));
|
||||
|
||||
if (isConfigurationAction && (flags & KAction::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 - _k_shortcutGotChanged() does it.
|
||||
// In practice it's probably better to get the change propagated here without
|
||||
// DBus delay as we do below.
|
||||
iface.setForeignShortcut(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.
|
||||
action->d->setActiveGlobalShortcutNoEnable(scResult);
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & KAction::DefaultShortcut) {
|
||||
iface.setShortcut(actionId, intListFromShortcut(defaultShortcut),
|
||||
setterFlags | IsDefault);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QStringList KGlobalAccelPrivate::makeActionId(const KAction *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 = KGlobal::locale()->removeAcceleratorMarker(action->text());
|
||||
ret.append(actionText); // Action Friendly Name
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QList<int> KGlobalAccelPrivate::intListFromShortcut(const KShortcut &cut)
|
||||
{
|
||||
QList<int> ret;
|
||||
ret.append(cut.primary()[0]);
|
||||
ret.append(cut.alternate()[0]);
|
||||
while (!ret.isEmpty() && ret.last() == 0)
|
||||
ret.removeLast();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
KShortcut KGlobalAccelPrivate::shortcutFromIntList(const QList<int> &list)
|
||||
{
|
||||
KShortcut ret;
|
||||
if (list.count() > 0)
|
||||
ret.setPrimary(list[0]);
|
||||
if (list.count() > 1)
|
||||
ret.setAlternate(list[1]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QString KGlobalAccelPrivate::componentUniqueForAction(const KAction *action)
|
||||
{
|
||||
Q_ASSERT(action->d->componentData.isValid());
|
||||
return action->d->componentData.componentName();
|
||||
}
|
||||
|
||||
|
||||
QString KGlobalAccelPrivate::componentFriendlyForAction(const KAction *action)
|
||||
{
|
||||
Q_ASSERT(action->d->componentData.isValid());
|
||||
return action->d->componentData.aboutData()->programName();
|
||||
}
|
||||
|
||||
void KGlobalAccelPrivate::_k_invokeAction(
|
||||
const QString &componentUnique,
|
||||
const QString &actionUnique,
|
||||
qlonglong timestamp)
|
||||
{
|
||||
KAction *action = 0;
|
||||
QList<KAction *> candidates = nameToAction.values(actionUnique);
|
||||
foreach (KAction *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;
|
||||
}
|
||||
|
||||
#ifdef Q_WS_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( NET::timestampCompare(timestamp, QX11Info::appTime()) > 0)
|
||||
QX11Info::setAppTime(timestamp);
|
||||
if( NET::timestampCompare(timestamp, QX11Info::appUserTime()) > 0)
|
||||
QX11Info::setAppUserTime(timestamp);
|
||||
#else
|
||||
Q_UNUSED(timestamp);
|
||||
#endif
|
||||
|
||||
action->trigger();
|
||||
}
|
||||
|
||||
|
||||
void KGlobalAccelPrivate::_k_shortcutGotChanged(const QStringList &actionId,
|
||||
const QList<int> &keys)
|
||||
{
|
||||
KAction *action = nameToAction.value(actionId.at(KGlobalAccel::ActionUnique));
|
||||
if (!action)
|
||||
return;
|
||||
|
||||
action->d->setActiveGlobalShortcutNoEnable(shortcutFromIntList(keys));
|
||||
}
|
||||
|
||||
void KGlobalAccelPrivate::_k_serviceOwnerChanged(const QString &name, const QString &oldOwner,
|
||||
const QString &newOwner)
|
||||
{
|
||||
Q_UNUSED(oldOwner);
|
||||
if (name == QLatin1String("org.kde.kglobalaccel") && !newOwner.isEmpty()) {
|
||||
// kglobalaccel was restarted
|
||||
kDebug(125) << "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.
|
||||
QSet<KAction *> allActions = actions;
|
||||
nameToAction.clear();
|
||||
actions.clear();
|
||||
foreach(KAction *const action, allActions) {
|
||||
doRegister(action);
|
||||
updateGlobalShortcut(action, KAction::Autoloading | KAction::ActiveShortcut);
|
||||
}
|
||||
return kGlobalAccel;
|
||||
}
|
||||
|
||||
QList<KGlobalShortcutInfo> KGlobalAccel::getGlobalShortcutsByKey(const QKeySequence &seq)
|
||||
{
|
||||
return self()->d->iface.getGlobalShortcutsByKey(seq[0]);
|
||||
QList<KGlobalShortcutInfo> result;
|
||||
foreach (const KGlobalAccelStruct &shortcut, d->filter->shortcuts) {
|
||||
if (shortcut.action->globalShortcut().contains(seq)) {
|
||||
KGlobalShortcutInfo globalshortcutinfo;
|
||||
globalshortcutinfo.componentFriendlyName = shortcut.action->d->componentData.aboutData()->programName();
|
||||
globalshortcutinfo.friendlyName = KGlobal::locale()->removeAcceleratorMarker(shortcut.action->text());
|
||||
globalshortcutinfo.contextFriendlyName = shortcut.action->objectName();
|
||||
result.append(globalshortcutinfo);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool KGlobalAccel::isGlobalShortcutAvailable(const QKeySequence &seq, const QString &comp)
|
||||
{
|
||||
return self()->d->iface.isGlobalShortcutAvailable(seq[0], comp);
|
||||
foreach (const KGlobalAccelStruct &shortcut, d->filter->shortcuts) {
|
||||
if (shortcut.action->globalShortcut().conflictsWith(seq)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//static
|
||||
bool KGlobalAccel::promptStealShortcutSystemwide(
|
||||
QWidget *parent,
|
||||
const QList<KGlobalShortcutInfo> &shortcuts,
|
||||
const QKeySequence &seq)
|
||||
void KGlobalAccel::stealShortcutSystemwide(const QKeySequence &seq)
|
||||
{
|
||||
foreach (const KGlobalAccelStruct &shortcut, d->filter->shortcuts) {
|
||||
if (shortcut.action->globalShortcut().conflictsWith(seq)) {
|
||||
d->remove(shortcut.action, KGlobalAccelPrivate::SetInactive);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 component = shortcuts[0].componentFriendlyName;
|
||||
|
||||
QString message;
|
||||
if (shortcuts.size()==1) {
|
||||
if (shortcuts.size() ==1) {
|
||||
message = i18n("The '%1' key combination is registered by application %2 for action %3:",
|
||||
seq.toString(),
|
||||
component,
|
||||
shortcuts[0].friendlyName());
|
||||
shortcuts[0].friendlyName);
|
||||
} else {
|
||||
QString actionList;
|
||||
Q_FOREACH(const KGlobalShortcutInfo &info, shortcuts) {
|
||||
actionList += i18n("In context '%1' for action '%2'\n",
|
||||
info.contextFriendlyName(),
|
||||
info.friendlyName());
|
||||
info.contextFriendlyName,
|
||||
info.friendlyName);
|
||||
}
|
||||
message = i18n("The '%1' key combination is registered by application %2.\n%3",
|
||||
seq.toString(),
|
||||
|
@ -467,25 +285,9 @@ bool KGlobalAccel::promptStealShortcutSystemwide(
|
|||
|
||||
QString title = i18n("Conflict With Registered Global Shortcut");
|
||||
|
||||
return KMessageBox::warningContinueCancel(parent, message, title, KGuiItem(i18n("Reassign")))
|
||||
== KMessageBox::Continue;
|
||||
}
|
||||
|
||||
|
||||
//static
|
||||
void KGlobalAccel::stealShortcutSystemwide(const QKeySequence &seq)
|
||||
{
|
||||
//get the shortcut, remove seq, and set the new shortcut
|
||||
const QStringList actionId = self()->d->iface.action(seq[0]);
|
||||
if (actionId.size() < 4) // not a global shortcut
|
||||
return;
|
||||
QList<int> sc = self()->d->iface.shortcut(actionId);
|
||||
|
||||
for (int i = 0; i < sc.count(); i++)
|
||||
if (sc[i] == seq[0])
|
||||
sc[i] = 0;
|
||||
|
||||
self()->d->iface.setForeignShortcut(actionId, sc);
|
||||
return KMessageBox::warningContinueCancel(
|
||||
parent, message, title, KGuiItem(i18n("Reassign"))
|
||||
) == KMessageBox::Continue;
|
||||
}
|
||||
|
||||
#include "moc_kglobalaccel.cpp"
|
||||
|
|
|
@ -19,19 +19,21 @@
|
|||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef _KGLOBALACCEL_H_
|
||||
#define _KGLOBALACCEL_H_
|
||||
#ifndef KGLOBALACCEL_H
|
||||
#define KGLOBALACCEL_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "kdeui_export.h"
|
||||
#include "kaction.h"
|
||||
#include "kglobal.h"
|
||||
#include "kglobalshortcutinfo.h"
|
||||
|
||||
class KShortcut;
|
||||
class KComponentData;
|
||||
class OrgKdeKglobalaccelComponentInterface;
|
||||
struct KGlobalShortcutInfo
|
||||
{
|
||||
QString componentFriendlyName;
|
||||
QString friendlyName;
|
||||
QString contextFriendlyName;
|
||||
};
|
||||
|
||||
/**
|
||||
* @short Configurable global shortcut support
|
||||
|
@ -48,82 +50,17 @@ class KDEUI_EXPORT KGlobalAccel : public QObject
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/// Creates a new KGlobalAccel object
|
||||
KGlobalAccel();
|
||||
|
||||
/**
|
||||
* 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
|
||||
};
|
||||
/// Destructor
|
||||
~KGlobalAccel();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Set global shortcut context.
|
||||
*
|
||||
* A global shortcut context allows an application to have different sets
|
||||
* of global shortcuts and to switch between them. This is used by
|
||||
* plasma to switch the active global shortcuts when switching between
|
||||
* activities.
|
||||
*
|
||||
* @param component the name of the component. KComponentData::componentName
|
||||
* @param context the name of the context.
|
||||
*
|
||||
* @since 4.2
|
||||
*/
|
||||
static void activateGlobalShortcutContext(
|
||||
const QString &contextUnique,
|
||||
const QString &contextFriendly,
|
||||
const KComponentData &component = KGlobal::mainComponent());
|
||||
|
||||
/**
|
||||
* 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 @seq.
|
||||
*
|
||||
|
@ -133,7 +70,7 @@ public:
|
|||
*
|
||||
* @since 4.2
|
||||
*/
|
||||
static QList<KGlobalShortcutInfo> getGlobalShortcutsByKey(const QKeySequence &seq);
|
||||
QList<KGlobalShortcutInfo> getGlobalShortcutsByKey(const QKeySequence &seq);
|
||||
|
||||
/**
|
||||
* Check if the shortcut @seq is available for the @p component. The
|
||||
|
@ -143,9 +80,16 @@ public:
|
|||
*
|
||||
* @since 4.2
|
||||
*/
|
||||
static bool isGlobalShortcutAvailable(
|
||||
const QKeySequence &seq,
|
||||
const QString &component = QString());
|
||||
bool isGlobalShortcutAvailable(const QKeySequence &seq,
|
||||
const QString &component = QString());
|
||||
|
||||
/**
|
||||
* 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()
|
||||
*/
|
||||
void stealShortcutSystemwide(const QKeySequence &seq);
|
||||
|
||||
/**
|
||||
* Show a messagebox to inform the user that a global shorcut is already occupied,
|
||||
|
@ -156,29 +100,13 @@ public:
|
|||
*
|
||||
* @since 4.2
|
||||
*/
|
||||
static bool promptStealShortcutSystemwide(
|
||||
QWidget *parent,
|
||||
const QList<KGlobalShortcutInfo> &shortcuts,
|
||||
const QKeySequence &seq);
|
||||
static bool promptStealShortcutSystemwide(QWidget *parent,
|
||||
const QList<KGlobalShortcutInfo> &shortcuts,
|
||||
const QKeySequence &seq);
|
||||
|
||||
private:
|
||||
|
||||
friend class KAction;
|
||||
|
||||
/// Creates a new KGlobalAccel object
|
||||
KGlobalAccel();
|
||||
|
||||
/// Destructor
|
||||
~KGlobalAccel();
|
||||
|
||||
//! get component @p componentUnique
|
||||
OrgKdeKglobalaccelComponentInterface* getComponent(const QString &componentUnique);
|
||||
|
||||
class KGlobalAccelPrivate *const d;
|
||||
|
||||
Q_PRIVATE_SLOT(d, void _k_invokeAction(const QString &, const QString &, qlonglong))
|
||||
Q_PRIVATE_SLOT(d, void _k_shortcutGotChanged(const QStringList&, const QList<int>&))
|
||||
Q_PRIVATE_SLOT(d, void _k_serviceOwnerChanged(const QString&, const QString&, const QString&))
|
||||
};
|
||||
|
||||
#endif // _KGLOBALACCEL_H_
|
||||
#endif // KGLOBALACCEL_H
|
||||
|
|
|
@ -22,22 +22,10 @@
|
|||
#ifndef KGLOBALACCEL_P_H
|
||||
#define KGLOBALACCEL_P_H
|
||||
|
||||
#include <QtCore/QSet>
|
||||
|
||||
#include "kcomponentdata.h"
|
||||
#include "kglobalaccel_interface.h"
|
||||
#include "kglobalaccel_component_interface.h"
|
||||
|
||||
class KAction;
|
||||
class KShortcut;
|
||||
|
||||
enum SetShortcutFlag
|
||||
{
|
||||
SetPresent =2,
|
||||
NoAutoloading = 4,
|
||||
IsDefault = 8
|
||||
};
|
||||
#include "kaction.h"
|
||||
|
||||
class KGlobalAccel;
|
||||
class KGlobalAccelFilter;
|
||||
|
||||
class KGlobalAccelPrivate
|
||||
{
|
||||
|
@ -46,49 +34,16 @@ public:
|
|||
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.
|
||||
void updateGlobalShortcut(KAction *action, /*KAction::ShortcutTypes*/uint flags);
|
||||
KGlobalAccelPrivate(KGlobalAccel *_q);
|
||||
~KGlobalAccelPrivate();
|
||||
|
||||
///Register the action in this class and in the KDED module
|
||||
void doRegister(KAction *action); //"register" is a C keyword :p
|
||||
///cf. the RemoveAction enum
|
||||
void remove(KAction *action, Removal r);
|
||||
void updateGlobalShortcut(KAction *action, uint flags);
|
||||
void doRegister(KAction *action);
|
||||
bool remove(KAction *action, Removal r);
|
||||
|
||||
//"private" helpers
|
||||
QString componentUniqueForAction(const KAction *action);
|
||||
QString componentFriendlyForAction(const KAction *action);
|
||||
QStringList makeActionId(const KAction *action);
|
||||
QList<int> intListFromShortcut(const KShortcut &cut);
|
||||
KShortcut shortcutFromIntList(const QList<int> &list);
|
||||
void readComponentData(const KComponentData &component);
|
||||
|
||||
//private slot implementations
|
||||
void _k_invokeAction(const QString &, const QString &,qlonglong);
|
||||
void _k_shortcutGotChanged(const QStringList&, const QList<int>&);
|
||||
void _k_serviceOwnerChanged(const QString& name, const QString& oldOwner, const QString& newOwner);
|
||||
void reRegisterAll();
|
||||
|
||||
//for all actions with (isEnabled() && globalShortcutAllowed())
|
||||
QMultiHash<QString, KAction *> nameToAction;
|
||||
QSet<KAction *> actions;
|
||||
|
||||
//! The main component data. For convenience
|
||||
KComponentData mainComponent;
|
||||
|
||||
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 = false);
|
||||
|
||||
//! Our owner
|
||||
KGlobalAccel *q;
|
||||
|
||||
//! The components the application is using
|
||||
QHash<QString, org::kde::kglobalaccel::Component *> components;
|
||||
KGlobalAccel* q;
|
||||
KGlobalAccelFilter* filter;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // KGLOBALACCEL_P_H
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
/* Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "kglobalshortcutinfo.h"
|
||||
#include "kglobalshortcutinfo_p.h"
|
||||
|
||||
KGlobalShortcutInfo::KGlobalShortcutInfo()
|
||||
: QObject(),
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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"
|
|
@ -1,78 +0,0 @@
|
|||
#ifndef KGLOBALSHORTCUTINFO_H
|
||||
#define KGLOBALSHORTCUTINFO_H
|
||||
/* Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <kdeui_export.h>
|
||||
|
||||
#include <QtGui/QKeySequence>
|
||||
#include <QtDBus/QDBusArgument>
|
||||
|
||||
class KGlobalShortcutInfoPrivate;
|
||||
|
||||
/**
|
||||
* @author Michael Jansen <kde@michael-jansen.biz>
|
||||
*/
|
||||
class KDEUI_EXPORT KGlobalShortcutInfo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_CLASSINFO("D-Bus Interface", "org.kde.kglobalaccel.KShortcutInfo")
|
||||
|
||||
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();
|
||||
KGlobalShortcutInfo(const KGlobalShortcutInfo &rhs);
|
||||
~KGlobalShortcutInfo();
|
||||
|
||||
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; // in kde-workspace/kglobalaccel/globalshortcut.*
|
||||
friend KDEUI_EXPORT const QDBusArgument &operator>> (
|
||||
const QDBusArgument &argument,
|
||||
KGlobalShortcutInfo &shortcut);
|
||||
|
||||
//! Implementation details
|
||||
KGlobalShortcutInfoPrivate *d;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(KGlobalShortcutInfo)
|
||||
Q_DECLARE_METATYPE(QList<KGlobalShortcutInfo>)
|
||||
|
||||
#endif /* #ifndef KGLOBALSHORTCUTINFO_H */
|
|
@ -1,70 +0,0 @@
|
|||
/* Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "kglobalshortcutinfo.h"
|
||||
#include "kglobalshortcutinfo_p.h"
|
||||
|
||||
QDBusArgument &operator<< (QDBusArgument &argument, const KGlobalShortcutInfo &shortcut)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument << shortcut.uniqueName()
|
||||
<< shortcut.friendlyName()
|
||||
<< shortcut.componentUniqueName()
|
||||
<< shortcut.componentFriendlyName()
|
||||
<< shortcut.contextUniqueName()
|
||||
<< shortcut.contextFriendlyName();
|
||||
argument.beginArray(qMetaTypeId<int>());
|
||||
Q_FOREACH(const QKeySequence &key, shortcut.keys()) {
|
||||
argument << key.operator int();
|
||||
}
|
||||
argument.endArray();
|
||||
argument.beginArray(qMetaTypeId<int>());
|
||||
Q_FOREACH(const QKeySequence &key, shortcut.defaultKeys()) {
|
||||
argument << key.operator int();
|
||||
}
|
||||
argument.endArray();
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
const QDBusArgument &operator>> (const QDBusArgument &argument, KGlobalShortcutInfo &shortcut)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument >> shortcut.d->uniqueName
|
||||
>> shortcut.d->friendlyName
|
||||
>> shortcut.d->componentUniqueName
|
||||
>> shortcut.d->componentFriendlyName
|
||||
>> shortcut.d->contextUniqueName
|
||||
>> shortcut.d->contextFriendlyName;
|
||||
argument.beginArray();
|
||||
while (!argument.atEnd()) {
|
||||
int key = 0;
|
||||
argument >> key;
|
||||
shortcut.d->keys.append(QKeySequence(key));
|
||||
}
|
||||
argument.endArray();
|
||||
argument.beginArray();
|
||||
while (!argument.atEnd()) {
|
||||
int key = 0;
|
||||
argument >> key;
|
||||
shortcut.d->defaultKeys.append(QKeySequence(key));
|
||||
}
|
||||
argument.endArray();
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
#ifndef KGLOBALSHORTCUTINFO_P_H
|
||||
#define KGLOBALSHORTCUTINFO_P_H
|
||||
/* Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
#include "kglobalshortcutinfo.h"
|
||||
|
||||
class KGlobalShortcutInfoPrivate
|
||||
{
|
||||
public:
|
||||
QString contextUniqueName;
|
||||
QString contextFriendlyName;
|
||||
QString componentUniqueName;
|
||||
QString componentFriendlyName;
|
||||
QString uniqueName;
|
||||
QString friendlyName;
|
||||
QList<QKeySequence> keys;
|
||||
QList<QKeySequence> defaultKeys;
|
||||
};
|
||||
|
||||
KDEUI_EXPORT QDBusArgument &operator<<(QDBusArgument &argument, const KGlobalShortcutInfo &shortcut);
|
||||
KDEUI_EXPORT const QDBusArgument &operator>>(const QDBusArgument &argument, KGlobalShortcutInfo &shortcut);
|
||||
|
||||
// these should be in kglobalaccel_interface.h. But this way it's easier to
|
||||
// regenerate that file.
|
||||
Q_DECLARE_METATYPE(QList<int>)
|
||||
Q_DECLARE_METATYPE(QList<QStringList>)
|
||||
|
||||
#endif /* #ifndef KGLOBALSHORTCUTINFO_P_H */
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
<!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.In1" value="QList<int>"/>
|
||||
</signal>
|
||||
<method name="allComponents">
|
||||
<arg type="ao" direction="out"/>
|
||||
</method>
|
||||
<method name="allMainComponents">
|
||||
<arg type="aas" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<QStringList>"/>
|
||||
</method>
|
||||
<method name="allActionsForComponent">
|
||||
<arg type="aas" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<QStringList>"/>
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
</method>
|
||||
<method name="action">
|
||||
<arg type="as" direction="out"/>
|
||||
<arg name="key" type="i" direction="in"/>
|
||||
</method>
|
||||
<method name="getComponent">
|
||||
<arg type="o" direction="out"/>
|
||||
<arg name="componentUnique" type="s" direction="in"/>
|
||||
</method>
|
||||
<method name="shortcut">
|
||||
<arg type="ai" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<int>"/>
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
</method>
|
||||
<method name="defaultShortcut">
|
||||
<arg type="ai" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<int>"/>
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
</method>
|
||||
<method name="setShortcut">
|
||||
<arg type="ai" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<int>"/>
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
<arg name="keys" type="ai" direction="in"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QList<int>"/>
|
||||
<arg name="flags" type="u" direction="in"/>
|
||||
</method>
|
||||
<method name="setForeignShortcut">
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
<arg name="keys" type="ai" direction="in"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QList<int>"/>
|
||||
</method>
|
||||
<method name="setInactive">
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
</method>
|
||||
<method name="doRegister">
|
||||
<arg name="actionId" type="as" direction="in"/>
|
||||
</method>
|
||||
<method name="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>
|
||||
</interface>
|
||||
</node>
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.kde.kglobalaccel.Component">
|
||||
<property name="friendlyName" type="s" access="read"/>
|
||||
<property name="uniqueName" type="s" access="read"/>
|
||||
<signal name="globalShortcutPressed">
|
||||
<arg name="componentUnique" type="s" direction="out"/>
|
||||
<arg name="actionUnique" type="s" direction="out"/>
|
||||
<arg name="timestamp" type="x" direction="out"/>
|
||||
</signal>
|
||||
<method name="cleanUp">
|
||||
<arg type="b" direction="out"/>
|
||||
</method>
|
||||
<method name="isActive">
|
||||
<arg type="b" direction="out"/>
|
||||
</method>
|
||||
<method name="shortcutNames">
|
||||
<arg type="as" direction="out"/>
|
||||
<arg name="context" type="s" direction="in"/>
|
||||
</method>
|
||||
<method name="shortcutNames">
|
||||
<arg type="as" direction="out"/>
|
||||
</method>
|
||||
<method name="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>
|
||||
|
|
@ -54,7 +54,6 @@ KDEUI_UNIT_TESTS(
|
|||
kselectaction_unittest
|
||||
klistwidgetsearchlinetest
|
||||
kconfigdialog_unittest
|
||||
kglobalshortcuttest
|
||||
klinkitemselectionmodeltest
|
||||
kstandardactiontest
|
||||
ktextedit_unittest
|
||||
|
|
|
@ -1,375 +0,0 @@
|
|||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
Copyright (c) 2007 Andreas Hartmetz <ahartmetz@gmail.com>
|
||||
Copyright (c) 2008 Michael Jansen <kde@michael-jansen.biz>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "kglobalshortcuttest.h"
|
||||
#include <qdbusinterface.h>
|
||||
#include <qtest_kde.h>
|
||||
#include <kaction.h>
|
||||
#include <kaction_p.h>
|
||||
#include <kactioncollection.h>
|
||||
#include <kglobal.h>
|
||||
#include <kglobalaccel.h>
|
||||
#include <kdebug.h>
|
||||
#include <kservice.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <QtDBus/QDBusConnectionInterface>
|
||||
|
||||
const QKeySequence sequenceA = QKeySequence(Qt::SHIFT + Qt::META + Qt::CTRL + Qt::ALT + Qt::Key_F28 );
|
||||
const QKeySequence sequenceB = QKeySequence(Qt::Key_F29);
|
||||
const QKeySequence sequenceC = QKeySequence(Qt::SHIFT + Qt::META + Qt::CTRL + Qt::Key_F28 );
|
||||
const QKeySequence sequenceD = QKeySequence(Qt::META + Qt::ALT + Qt::Key_F30);
|
||||
const QKeySequence sequenceE = QKeySequence(Qt::META + Qt::Key_F29);
|
||||
const QKeySequence sequenceF = QKeySequence(Qt::META + Qt::Key_F27);
|
||||
|
||||
/* These tests could be better. They don't include actually triggering actions,
|
||||
and we just choose very improbable shortcuts to avoid conflicts with real
|
||||
applications' shortcuts. */
|
||||
|
||||
//we need a KComponentData and a GUI so that the implementation can grab keys
|
||||
QTEST_KDEMAIN( KGlobalShortcutTest, GUI )
|
||||
|
||||
void KGlobalShortcutTest::initTestCase()
|
||||
{
|
||||
m_daemonInstalled = !KService::serviceByDesktopName("kglobalaccel").isNull();
|
||||
}
|
||||
|
||||
void KGlobalShortcutTest::setupTest(QString id)
|
||||
{
|
||||
if (m_actionA) {
|
||||
m_actionA->forgetGlobalShortcut();
|
||||
delete m_actionA;
|
||||
}
|
||||
|
||||
if (m_actionB) {
|
||||
m_actionB->forgetGlobalShortcut();
|
||||
delete m_actionB;
|
||||
}
|
||||
|
||||
// Ensure that the previous test did cleanup correctly
|
||||
KGlobalAccel *kga = KGlobalAccel::self();
|
||||
Q_UNUSED(kga);
|
||||
|
||||
m_actionA = new KAction("Text For Action A", this);
|
||||
m_actionA->setObjectName("Action A:" + id);
|
||||
m_actionA->setGlobalShortcut(
|
||||
KShortcut(sequenceA, sequenceB),
|
||||
KAction::ActiveShortcut|KAction::DefaultShortcut,
|
||||
KAction::NoAutoloading);
|
||||
|
||||
m_actionB = new KAction("Text For Action B", this);
|
||||
m_actionB->setObjectName("Action B:" + id);
|
||||
m_actionB->setGlobalShortcut(
|
||||
KShortcut(),
|
||||
KAction::ActiveShortcut|KAction::DefaultShortcut,
|
||||
KAction::NoAutoloading);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void KGlobalShortcutTest::testSetShortcut()
|
||||
{
|
||||
setupTest("testSetShortcut");
|
||||
|
||||
if (!m_daemonInstalled)
|
||||
QSKIP("kglobalaccel not installed", SkipAll);
|
||||
|
||||
// Just ensure that the desired values are set for both actions
|
||||
KShortcut cutA(sequenceA, sequenceB);
|
||||
QCOMPARE(m_actionA->globalShortcut(), cutA);
|
||||
QCOMPARE(m_actionA->globalShortcut(KAction::DefaultShortcut), cutA);
|
||||
|
||||
QVERIFY(m_actionB->globalShortcut().isEmpty());
|
||||
QVERIFY(m_actionB->globalShortcut(KAction::DefaultShortcut).isEmpty());
|
||||
}
|
||||
|
||||
// Current state
|
||||
// m_actionA: (sequenceA, sequenceB)
|
||||
// m_actionB: (,)
|
||||
|
||||
void KGlobalShortcutTest::testFindActionByKey()
|
||||
{
|
||||
// Skip this. The above testcase hasn't changed the actions
|
||||
setupTest("testFindActionByKey");
|
||||
if (!m_daemonInstalled)
|
||||
QSKIP("kglobalaccel not installed", SkipAll);
|
||||
|
||||
QList<KGlobalShortcutInfo> actionId = KGlobalAccel::self()->getGlobalShortcutsByKey(sequenceB);
|
||||
QCOMPARE(actionId.size(), 1);
|
||||
|
||||
QString actionIdAComponentUniqueName("qttest");
|
||||
QString actionIdAUniqueName("Action A:testFindActionByKey");
|
||||
QString actionIdAComponentFriendlyName("KDE Test Program");
|
||||
QString actionIdAFriendlyName("Text For Action A");
|
||||
|
||||
QCOMPARE(actionId.first().componentUniqueName(), actionIdAComponentUniqueName);
|
||||
QCOMPARE(actionId.first().uniqueName(), actionIdAUniqueName);
|
||||
QCOMPARE(actionId.first().componentFriendlyName(), actionIdAComponentFriendlyName);
|
||||
QCOMPARE(actionId.first().friendlyName(), actionIdAFriendlyName);
|
||||
|
||||
actionId = KGlobalAccel::self()->getGlobalShortcutsByKey(sequenceA);
|
||||
QCOMPARE(actionId.size(), 1);
|
||||
|
||||
QCOMPARE(actionId.first().componentUniqueName(), actionIdAComponentUniqueName);
|
||||
QCOMPARE(actionId.first().uniqueName(), actionIdAUniqueName);
|
||||
QCOMPARE(actionId.first().componentFriendlyName(), actionIdAComponentFriendlyName);
|
||||
QCOMPARE(actionId.first().friendlyName(), actionIdAFriendlyName);
|
||||
}
|
||||
|
||||
void KGlobalShortcutTest::testChangeShortcut()
|
||||
{
|
||||
// Skip this. The above testcase hasn't changed the actions
|
||||
setupTest("testChangeShortcut");
|
||||
|
||||
if (!m_daemonInstalled)
|
||||
QSKIP("kglobalaccel not installed", SkipAll);
|
||||
// Change the shortcut
|
||||
KShortcut newCutA(sequenceC);
|
||||
m_actionA->setGlobalShortcut(newCutA, KAction::ActiveShortcut, KAction::NoAutoloading);
|
||||
// Ensure that the change is active
|
||||
QCOMPARE(m_actionA->globalShortcut(), newCutA);
|
||||
// We haven't changed the default shortcut, ensure it is unchanged
|
||||
KShortcut cutA(sequenceA, sequenceB);
|
||||
QCOMPARE(m_actionA->globalShortcut(KAction::DefaultShortcut), cutA);
|
||||
|
||||
// Try to set a already take shortcut
|
||||
KShortcut cutB(m_actionA->globalShortcut().primary(), QKeySequence(sequenceE));
|
||||
m_actionB->setGlobalShortcut(cutB, KAction::ActiveShortcut, KAction::NoAutoloading);
|
||||
// Ensure that no change was made to the primary active shortcut
|
||||
QVERIFY(m_actionB->globalShortcut().primary().isEmpty());
|
||||
// Ensure that the change to the secondary active shortcut was made
|
||||
QCOMPARE(m_actionB->globalShortcut().alternate(), QKeySequence(sequenceE));
|
||||
// Ensure that the default shortcut is still empty
|
||||
QVERIFY(m_actionB->globalShortcut(KAction::DefaultShortcut).isEmpty()); // unchanged
|
||||
|
||||
// Only change the active shortcut
|
||||
cutB.setPrimary(sequenceD);
|
||||
m_actionB->setGlobalShortcut(cutB, KAction::ActiveShortcut, KAction::NoAutoloading);
|
||||
// Check that the change went through
|
||||
QCOMPARE(m_actionB->globalShortcut(), cutB);
|
||||
// Check that the default shortcut is not active
|
||||
QVERIFY(m_actionB->globalShortcut(KAction::DefaultShortcut).isEmpty()); // unchanged
|
||||
}
|
||||
|
||||
void KGlobalShortcutTest::testStealShortcut()
|
||||
{
|
||||
setupTest("testStealShortcut");
|
||||
if (!m_daemonInstalled)
|
||||
QSKIP("kglobalaccel not installed", SkipAll);
|
||||
|
||||
// Steal a shortcut from an action. First ensure the initial state is
|
||||
// correct
|
||||
KShortcut cutA(sequenceA, sequenceB);
|
||||
QCOMPARE(m_actionA->globalShortcut(), cutA);
|
||||
QCOMPARE(m_actionA->globalShortcut(KAction::DefaultShortcut), cutA);
|
||||
|
||||
KGlobalAccel::stealShortcutSystemwide(sequenceA);
|
||||
//let DBus do its thing in case it happens asynchronously
|
||||
QTest::qWait(200);
|
||||
QVERIFY(m_actionB->globalShortcut(KAction::ActiveShortcut).primary().isEmpty());
|
||||
}
|
||||
|
||||
|
||||
void KGlobalShortcutTest::testSaveRestore()
|
||||
{
|
||||
setupTest("testSaveRestore");
|
||||
|
||||
//It /would be nice/ to test persistent storage. That is not so easy...
|
||||
KShortcut cutA = m_actionA->globalShortcut();
|
||||
// Delete the action
|
||||
delete m_actionA;
|
||||
|
||||
// Recreate it
|
||||
m_actionA = new KAction("Text For Action A", this);
|
||||
m_actionA->setObjectName("Action A:testSaveRestore");
|
||||
|
||||
// Now it's empty
|
||||
QVERIFY(m_actionA->globalShortcut().isEmpty());
|
||||
|
||||
m_actionA->setGlobalShortcut(KShortcut());
|
||||
// Now it's restored
|
||||
QCOMPARE(m_actionA->globalShortcut(), cutA);
|
||||
|
||||
// And again
|
||||
delete m_actionA;
|
||||
m_actionA = new KAction("Text For Action A", this);
|
||||
m_actionA->setObjectName("Action A:testSaveRestore");
|
||||
m_actionA->setGlobalShortcut(KShortcut(QKeySequence(), cutA.primary()));
|
||||
QCOMPARE(m_actionA->globalShortcut(), cutA);
|
||||
|
||||
}
|
||||
|
||||
// Duplicated again!
|
||||
enum actionIdFields
|
||||
{
|
||||
ComponentUnique = 0,
|
||||
ActionUnique = 1,
|
||||
ComponentFriendly = 2,
|
||||
ActionFriendly = 3
|
||||
};
|
||||
|
||||
void KGlobalShortcutTest::testListActions()
|
||||
{
|
||||
setupTest("testListActions");
|
||||
if (!m_daemonInstalled)
|
||||
QSKIP("kglobalaccel not installed", SkipAll);
|
||||
|
||||
// As in kdebase/workspace/kcontrol/keys/globalshortcuts.cpp
|
||||
KGlobalAccel *kga = KGlobalAccel::self();
|
||||
Q_UNUSED(kga);
|
||||
|
||||
}
|
||||
|
||||
void KGlobalShortcutTest::testComponentAssignment()
|
||||
{
|
||||
// We don't use them here
|
||||
// setupTest();
|
||||
|
||||
KComponentData otherComponent("test_component1");
|
||||
KActionCollection coll((QObject*)NULL);
|
||||
coll.setComponentData(otherComponent);
|
||||
KShortcut cutB;
|
||||
/************************************************************
|
||||
* Ensure that the actions get a correct component assigned *
|
||||
************************************************************/
|
||||
// Action without action collection get the global component
|
||||
{
|
||||
KAction action("Text For Action A", NULL);
|
||||
action.setObjectName("Action C");
|
||||
|
||||
QVERIFY(action.d->componentData == KGlobal::mainComponent());
|
||||
action.setGlobalShortcut(cutB, KAction::ActiveShortcut, KAction::NoAutoloading);
|
||||
QVERIFY(action.d->componentData == KGlobal::mainComponent());
|
||||
// cleanup
|
||||
action.forgetGlobalShortcut();
|
||||
}
|
||||
|
||||
// Action with action collection get the component of the collection
|
||||
{
|
||||
KAction *action = coll.addAction("Action C");
|
||||
|
||||
QVERIFY(action->d->componentData == otherComponent);
|
||||
action->setGlobalShortcut(cutB, KAction::ActiveShortcut, KAction::NoAutoloading);
|
||||
QVERIFY(action->d->componentData == otherComponent);
|
||||
// cleanup
|
||||
action->forgetGlobalShortcut();
|
||||
delete action;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void KGlobalShortcutTest::testConfigurationActions()
|
||||
{
|
||||
setupTest("testConfigurationActions");
|
||||
|
||||
// Create a configuration action
|
||||
KAction cfg_action("Text For Action A", NULL);
|
||||
cfg_action.setObjectName("Action A:testConfigurationActions");
|
||||
cfg_action.setProperty("isConfigurationAction", true);
|
||||
cfg_action.setGlobalShortcut(KShortcut());
|
||||
|
||||
// Check that the configuration action has the correct shortcuts
|
||||
QCOMPARE(m_actionA->globalShortcut(), cfg_action.globalShortcut());
|
||||
|
||||
// TODO:
|
||||
// - change shortcut from configuration action and test for
|
||||
// yourShortcutGotChanged
|
||||
// - Ensure that the config action doesn't trigger(how?)
|
||||
// - Ensure that the original action is still working when the
|
||||
// configuration action is deleted
|
||||
}
|
||||
|
||||
void KGlobalShortcutTest::testOverrideMainComponentData()
|
||||
{
|
||||
setupTest("testOverrideMainComponentData");
|
||||
|
||||
KComponentData otherComponent("test_component1");
|
||||
KActionCollection coll((QObject*)NULL);
|
||||
coll.setComponentData(otherComponent);
|
||||
KShortcut cutB;
|
||||
|
||||
// Action without action collection
|
||||
KAction *action = new KAction("Text For Action A", this);
|
||||
QVERIFY(action->d->componentData == KGlobal::mainComponent());
|
||||
action->setObjectName("Action A");
|
||||
action->setGlobalShortcut(cutB, KAction::ActiveShortcut, KAction::NoAutoloading);
|
||||
QVERIFY(action->d->componentData == KGlobal::mainComponent());
|
||||
|
||||
// Action with action collection
|
||||
action->forgetGlobalShortcut();
|
||||
delete action;
|
||||
action = coll.addAction("Action A");
|
||||
QVERIFY(action->d->componentData == otherComponent);
|
||||
action->setGlobalShortcut(cutB, KAction::ActiveShortcut, KAction::NoAutoloading);
|
||||
QVERIFY(action->d->componentData == otherComponent);
|
||||
|
||||
// cleanup
|
||||
action->forgetGlobalShortcut();
|
||||
delete coll.takeAction(action);
|
||||
|
||||
}
|
||||
|
||||
void KGlobalShortcutTest::testNotification()
|
||||
{
|
||||
setupTest("testNotification");
|
||||
|
||||
// Action without action collection
|
||||
KAction *action = new KAction("Text For Action A", this);
|
||||
QVERIFY(action->d->componentData == KGlobal::mainComponent());
|
||||
action->setObjectName("Action A");
|
||||
KShortcut cutB;
|
||||
action->setGlobalShortcut(cutB, KAction::ActiveShortcut, KAction::NoAutoloading);
|
||||
QVERIFY(action->d->componentData == KGlobal::mainComponent());
|
||||
|
||||
// kglobalacceld collects registrations and shows the together. Give it
|
||||
// time to kick in.
|
||||
sleep(2);
|
||||
|
||||
action->forgetGlobalShortcut();
|
||||
}
|
||||
|
||||
void KGlobalShortcutTest::testForgetGlobalShortcut()
|
||||
{
|
||||
setupTest("testForgetGlobalShortcut");
|
||||
|
||||
// Ensure that forgetGlobalShortcut can be called on any action.
|
||||
KAction a("Test", NULL);
|
||||
a.forgetGlobalShortcut();
|
||||
if (!m_daemonInstalled)
|
||||
QSKIP("kglobalaccel not installed", SkipAll);
|
||||
|
||||
// We forget these two shortcuts and check that the component is gone
|
||||
// after that. If not it can mean the forgetGlobalShortcut() call is
|
||||
// broken OR someone messed up these tests to leave an additional global
|
||||
// shortcut behind.
|
||||
m_actionB->forgetGlobalShortcut();
|
||||
m_actionA->forgetGlobalShortcut();
|
||||
// kglobalaccel writes asynchronous.
|
||||
sleep(1);
|
||||
|
||||
KGlobalAccel *kga = KGlobalAccel::self();
|
||||
Q_UNUSED(kga);
|
||||
}
|
||||
|
||||
|
||||
#include "moc_kglobalshortcuttest.cpp"
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
Copyright (c) 2007 Andreas Hartmetz <ahartmetz@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef KGLOBALSHORTCUTTEST_H
|
||||
#define KGLOBALSHORTCUTTEST_H
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
class KAction;
|
||||
|
||||
class KGlobalShortcutTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void testSetShortcut();
|
||||
void testFindActionByKey();
|
||||
void testChangeShortcut();
|
||||
void testStealShortcut();
|
||||
void testSaveRestore();
|
||||
void testListActions();
|
||||
void testComponentAssignment();
|
||||
void testConfigurationActions();
|
||||
void testNotification();
|
||||
// This has to be the last before forgetGlobalShortcut
|
||||
void testOverrideMainComponentData();
|
||||
void testForgetGlobalShortcut();
|
||||
|
||||
public:
|
||||
|
||||
KGlobalShortcutTest() : m_actionA(0) ,m_actionB(0)
|
||||
{}
|
||||
|
||||
private:
|
||||
void setupTest(QString id);
|
||||
|
||||
KAction *m_actionA;
|
||||
KAction *m_actionB;
|
||||
bool m_daemonInstalled;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -121,8 +121,8 @@ public:
|
|||
clashingKeys += i18n(
|
||||
"Shortcut '%1' in Application %2 for action %3\n",
|
||||
seq.toString(),
|
||||
info.componentFriendlyName(),
|
||||
info.friendlyName()
|
||||
info.componentFriendlyName,
|
||||
info.friendlyName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -315,11 +315,12 @@ bool KKeySequenceWidgetPrivate::conflictWithGlobalShortcuts(const QKeySequence &
|
|||
|
||||
// Global shortcuts are on key+modifier shortcuts. They can clash with
|
||||
// each of the keys of a multi key shortcut.
|
||||
KGlobalAccel* kglobalaccel = KGlobalAccel::self();
|
||||
QHash<QKeySequence, QList<KGlobalShortcutInfo> > others;
|
||||
for (int i=0; i<keySequence.count(); ++i) {
|
||||
for (int i=0; i < keySequence.count(); ++i) {
|
||||
QKeySequence tmp(keySequence[i]);
|
||||
if (!KGlobalAccel::isGlobalShortcutAvailable(tmp, componentName)) {
|
||||
others.insert(tmp, KGlobalAccel::getGlobalShortcutsByKey(tmp));
|
||||
if (!kglobalaccel->isGlobalShortcutAvailable(tmp, componentName)) {
|
||||
others.insert(tmp, kglobalaccel->getGlobalShortcutsByKey(tmp));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -334,7 +335,7 @@ bool KKeySequenceWidgetPrivate::conflictWithGlobalShortcuts(const QKeySequence &
|
|||
// most likely the first action that is done in the slot
|
||||
// listening to keySequenceChanged().
|
||||
for (int i = 0; i < keySequence.count(); ++i) {
|
||||
KGlobalAccel::stealShortcutSystemwide(keySequence[i]);
|
||||
kglobalaccel->stealShortcutSystemwide(keySequence[i]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -384,7 +385,7 @@ bool KKeySequenceWidgetPrivate::conflictWithLocalShortcuts(const QKeySequence &k
|
|||
if (kaction->shortcut().conflictsWith(keySequence)) {
|
||||
// A conflict with a KAction. If that action is configurable
|
||||
// ask the user what to do. If not reject this keySequence.
|
||||
if(kaction->isShortcutConfigurable ()) {
|
||||
if (kaction->isShortcutConfigurable ()) {
|
||||
conflictingActions.append(kaction);
|
||||
} else {
|
||||
wontStealShortcut(kaction, keySequence);
|
||||
|
|
Loading…
Add table
Reference in a new issue