QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsshortcutsmanager.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsshortcutsmanager.cpp
3 ---------------------
4 begin : May 2009
5 copyright : (C) 2009 by Martin Dobias
6 email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsshortcutsmanager.h"
17#include "qgslogger.h"
18#include "qgssettings.h"
19
20#include <QShortcut>
21#include <QRegularExpression>
22#include <QWidgetAction>
23
24QgsShortcutsManager::QgsShortcutsManager( QObject *parent, const QString &settingsRoot )
25 : QObject( parent )
26 , mSettingsPath( settingsRoot )
27{
28}
29
30void QgsShortcutsManager::registerAllChildren( QObject *object, bool recursive, const QString &section )
31{
32 registerAllChildActions( object, recursive, section );
33 registerAllChildShortcuts( object, recursive, section );
34}
35
36void QgsShortcutsManager::registerAllChildActions( QObject *object, bool recursive, const QString &section )
37{
38 const QList<QObject *> children = object->children();
39 for ( QObject *child : children )
40 {
41 if ( QAction *a = qobject_cast< QAction * >( child ) )
42 {
43 registerAction( a, a->shortcut().toString( QKeySequence::NativeText ), section );
44 }
45 else if ( recursive )
46 {
47 const QString newSection = child->objectName().isEmpty() ? section : section + child->objectName() + "/";
48 registerAllChildActions( child, recursive, newSection );
49 }
50 }
51}
52
53void QgsShortcutsManager::registerAllChildShortcuts( QObject *object, bool recursive, const QString &section )
54{
55 const QList<QObject *> children = object->children();
56 for ( QObject *child : children )
57 {
58 if ( QShortcut *s = qobject_cast< QShortcut * >( child ) )
59 {
60 registerShortcut( s, s->key().toString( QKeySequence::NativeText ), section );
61 }
62 else if ( recursive )
63 {
64 const QString newSection = child->objectName().isEmpty() ? section : section + child->objectName() + "/";
65 registerAllChildShortcuts( child, recursive, newSection );
66 }
67 }
68}
69
70bool QgsShortcutsManager::registerAction( QAction *action, const QString &defaultSequence, const QString &section )
71{
72 if ( qobject_cast<QWidgetAction *>( action ) )
73 return false;
74
75 if ( mActions.contains( action ) )
76 return false; // already registered
77
78 // if using a debug build, warn on duplicate or non-compliant actions
79 if ( action->text().isEmpty() && action->objectName().isEmpty() )
80 {
81#ifdef QGISDEBUG
82 QgsLogger::warning( QStringLiteral( "Action has no text set: %1" ).arg( action->objectName() ) );
83#endif
84 return false;
85 }
86
87 QString key = action->objectName().isEmpty() ? action->text() : action->objectName();
88 key.remove( '&' ); // remove the accelerator
89
90#ifdef QGISDEBUG
91 if ( actionByName( key ) || shortcutByName( key ) )
92 QgsLogger::warning( QStringLiteral( "Duplicate shortcut registered: %1" ).arg( key ) );
93#endif
94
95 const QString settingKey = mSettingsPath + section + key;
96
97 mActions.insert( action, {defaultSequence, settingKey} );
98 connect( action, &QObject::destroyed, this, [action, this]() { actionDestroyed( action ); } );
99
100 // load overridden value from settings
101 const QgsSettings settings;
102 const QString sequence = settings.value( settingKey, defaultSequence ).toString();
103
104 action->setShortcut( sequence );
105 if ( !action->toolTip().isEmpty() )
106 {
107 const QStringList parts = action->toolTip().split( '\n' );
108 QString formatted = QStringLiteral( "<b>%1</b>" ).arg( parts.at( 0 ) );
109 if ( parts.count() > 1 )
110 {
111 for ( int i = 1; i < parts.count(); ++i )
112 formatted += QStringLiteral( "<p>%1</p>" ).arg( parts.at( i ) );
113 }
114
115 action->setToolTip( formatted );
116 updateActionToolTip( action, sequence );
117 }
118
119 return true;
120}
121
122bool QgsShortcutsManager::registerShortcut( QShortcut *shortcut, const QString &defaultSequence, const QString &section )
123{
124#ifdef QGISDEBUG
125 // if using a debug build, warn on duplicate or non-compliant actions
126 if ( shortcut->objectName().isEmpty() )
127 QgsLogger::warning( QStringLiteral( "Shortcut has no object name set: %1" ).arg( shortcut->key().toString() ) );
128 else if ( actionByName( shortcut->objectName() ) || shortcutByName( shortcut->objectName() ) )
129 QgsLogger::warning( QStringLiteral( "Duplicate shortcut registered: %1" ).arg( shortcut->objectName() ) );
130#endif
131
132 const QString settingKey = mSettingsPath + section + shortcut->objectName();
133
134 mShortcuts.insert( shortcut, {defaultSequence, settingKey} );
135 connect( shortcut, &QObject::destroyed, this, [shortcut, this]() { shortcutDestroyed( shortcut ); } );
136
137 // load overridden value from settings
138 const QgsSettings settings;
139 const QString keySequence = settings.value( settingKey, defaultSequence ).toString();
140
141 shortcut->setKey( keySequence );
142
143 return true;
144}
145
147{
148 if ( !mActions.contains( action ) )
149 return false;
150
151 mActions.remove( action );
152 return true;
153}
154
156{
157 if ( !mShortcuts.contains( shortcut ) )
158 return false;
159
160 mShortcuts.remove( shortcut );
161 return true;
162}
163
164QList<QAction *> QgsShortcutsManager::listActions() const
165{
166 return mActions.keys();
167}
168
169QList<QShortcut *> QgsShortcutsManager::listShortcuts() const
170{
171 return mShortcuts.keys();
172}
173
174QList<QObject *> QgsShortcutsManager::listAll() const
175{
176 QList<QObject *> list;
177 ActionsHash::const_iterator actionIt = mActions.constBegin();
178 for ( ; actionIt != mActions.constEnd(); ++actionIt )
179 {
180 list << actionIt.key();
181 }
182 ShortcutsHash::const_iterator shortcutIt = mShortcuts.constBegin();
183 for ( ; shortcutIt != mShortcuts.constEnd(); ++shortcutIt )
184 {
185 list << shortcutIt.key();
186 }
187 return list;
188}
189
190QString QgsShortcutsManager::objectDefaultKeySequence( QObject *object ) const
191{
192 if ( QAction *action = qobject_cast< QAction * >( object ) )
193 return defaultKeySequence( action );
194 else if ( QShortcut *shortcut = qobject_cast< QShortcut * >( object ) )
195 return defaultKeySequence( shortcut );
196 else
197 return QString();
198}
199
200QString QgsShortcutsManager::defaultKeySequence( QAction *action ) const
201{
202 return mActions.value( action ).first;
203}
204
205QString QgsShortcutsManager::defaultKeySequence( QShortcut *shortcut ) const
206{
207 return mShortcuts.value( shortcut ).first;
208}
209
210bool QgsShortcutsManager::setKeySequence( const QString &name, const QString &sequence )
211{
212 if ( QAction *action = actionByName( name ) )
213 return setKeySequence( action, sequence );
214 else if ( QShortcut *shortcut = shortcutByName( name ) )
215 return setKeySequence( shortcut, sequence );
216 else
217 return false;
218}
219
220bool QgsShortcutsManager::setObjectKeySequence( QObject *object, const QString &sequence )
221{
222 if ( QAction *action = qobject_cast< QAction * >( object ) )
223 return setKeySequence( action, sequence );
224 else if ( QShortcut *shortcut = qobject_cast< QShortcut * >( object ) )
225 return setKeySequence( shortcut, sequence );
226 else
227 return false;
228}
229
230bool QgsShortcutsManager::setKeySequence( QAction *action, const QString &sequence )
231{
232 if ( !mActions.contains( action ) )
233 {
234 return false;
235 }
236 action->setShortcut( sequence );
237 this->updateActionToolTip( action, sequence );
238
239 const QString settingKey = mActions[action].second;
240
241 // save to settings
242 QgsSettings settings;
243 settings.setValue( settingKey, sequence );
244 return true;
245}
246
247bool QgsShortcutsManager::setKeySequence( QShortcut *shortcut, const QString &sequence )
248{
249 if ( !mShortcuts.contains( shortcut ) )
250 {
251 return false;
252 }
253 shortcut->setKey( sequence );
254
255 const QString settingKey = mShortcuts[shortcut].second;
256
257 // save to settings
258 QgsSettings settings;
259 settings.setValue( settingKey, sequence );
260 return true;
261}
262
263QObject *QgsShortcutsManager::objectForSequence( const QKeySequence &sequence ) const
264{
265 if ( QAction *action = actionForSequence( sequence ) )
266 return action;
267 else if ( QShortcut *shortcut = shortcutForSequence( sequence ) )
268 return shortcut;
269 else
270 return nullptr;
271}
272
273QAction *QgsShortcutsManager::actionForSequence( const QKeySequence &sequence ) const
274{
275 if ( sequence.isEmpty() )
276 return nullptr;
277
278 for ( ActionsHash::const_iterator it = mActions.constBegin(); it != mActions.constEnd(); ++it )
279 {
280 if ( it.key()->shortcut() == sequence )
281 return it.key();
282 }
283
284 return nullptr;
285}
286
287QShortcut *QgsShortcutsManager::shortcutForSequence( const QKeySequence &sequence ) const
288{
289 if ( sequence.isEmpty() )
290 return nullptr;
291
292 for ( ShortcutsHash::const_iterator it = mShortcuts.constBegin(); it != mShortcuts.constEnd(); ++it )
293 {
294 if ( it.key()->key() == sequence )
295 return it.key();
296 }
297
298 return nullptr;
299}
300
301QAction *QgsShortcutsManager::actionByName( const QString &name ) const
302{
303 for ( ActionsHash::const_iterator it = mActions.constBegin(); it != mActions.constEnd(); ++it )
304 {
305 if ( it.key()->objectName() == name )
306 return it.key();
307 }
308 for ( ActionsHash::const_iterator it = mActions.constBegin(); it != mActions.constEnd(); ++it )
309 {
310 QString key = it.key()->text();
311 key.remove( '&' ); // remove the accelerator
312 if ( key == name )
313 return it.key();
314 }
315
316 return nullptr;
317}
318
319QShortcut *QgsShortcutsManager::shortcutByName( const QString &name ) const
320{
321 for ( ShortcutsHash::const_iterator it = mShortcuts.constBegin(); it != mShortcuts.constEnd(); ++it )
322 {
323 if ( it.key()->objectName() == name )
324 return it.key();
325 }
326
327 return nullptr;
328}
329
330void QgsShortcutsManager::actionDestroyed( QAction *action )
331{
332 mActions.remove( action );
333}
334
335QString QgsShortcutsManager::objectSettingKey( QObject *object ) const
336{
337 if ( auto action = qobject_cast< QAction * >( object ) )
338 {
339 return mActions.value( action ).second;
340 }
341 else if ( auto shortcut = qobject_cast< QShortcut * >( object ) )
342 {
343 return mShortcuts.value( shortcut ).second;
344 }
345 return QString();
346}
347
348QObject *QgsShortcutsManager::objectForSettingKey( const QString &settingKey ) const
349{
350 for ( ActionsHash::const_iterator it = mActions.constBegin(); it != mActions.constEnd(); ++it )
351 {
352 if ( it.value().second == settingKey )
353 return it.key();
354 }
355 for ( ShortcutsHash::const_iterator it = mShortcuts.constBegin(); it != mShortcuts.constEnd(); ++it )
356 {
357 if ( it.value().second == settingKey )
358 return it.key();
359 }
360 return nullptr;
361}
362
363void QgsShortcutsManager::shortcutDestroyed( QShortcut *shortcut )
364{
365 mShortcuts.remove( shortcut );
366}
367
368void QgsShortcutsManager::updateActionToolTip( QAction *action, const QString &sequence )
369{
370 QString current = action->toolTip();
371 // Remove the old shortcut.
372 const thread_local QRegularExpression rx( QStringLiteral( "\\(.*\\)" ) );
373 current.replace( rx, QString() );
374
375 if ( !sequence.isEmpty() )
376 {
377 action->setToolTip( current + " (" + sequence + ")" );
378 }
379 else
380 {
381 action->setToolTip( current );
382 }
383}
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:131
This class is a composition of two QSettings instances:
Definition: qgssettings.h:64
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
bool setKeySequence(const QString &name, const QString &sequence)
Modifies an action or shortcut's key sequence.
bool registerShortcut(QShortcut *shortcut, const QString &defaultSequence=QString(), const QString &section=QString())
Registers a QShortcut with the manager so the shortcut can be configured in GUI.
QList< QObject * > listAll() const
Returns a list of both actions and shortcuts in the manager.
void registerAllChildActions(QObject *object, bool recursive=false, const QString &section=QString())
Automatically registers all QActions which are children of the passed object.
QObject * objectForSettingKey(const QString &name) const
Returns the QShortcut or QAction matching the the full setting key Return nullptr if the key was not ...
void registerAllChildShortcuts(QObject *object, bool recursive=false, const QString &section=QString())
Automatically registers all QShortcuts which are children of the passed object.
QgsShortcutsManager(QObject *parent=nullptr, const QString &settingsRoot="/shortcuts/")
Constructor for QgsShortcutsManager.
QString objectDefaultKeySequence(QObject *object) const
Returns the default sequence for an object (either a QAction or QShortcut).
QList< QShortcut * > listShortcuts() const
Returns a list of shortcuts in the manager.
bool unregisterShortcut(QShortcut *shortcut)
Removes a shortcut from the manager.
QString defaultKeySequence(QAction *action) const
Returns the default sequence for an action.
bool setObjectKeySequence(QObject *object, const QString &sequence)
Modifies an object's (either a QAction or a QShortcut) key sequence.
bool registerAction(QAction *action, const QString &defaultShortcut=QString(), const QString &section=QString())
Registers an action with the manager so the shortcut can be configured in GUI.
void registerAllChildren(QObject *object, bool recursive=false, const QString &section=QString())
Automatically registers all QActions and QShortcuts which are children of the passed object.
QShortcut * shortcutByName(const QString &name) const
Returns a shortcut by its name, or nullptr if nothing found.
QAction * actionByName(const QString &name) const
Returns an action by its name, or nullptr if nothing found.
QShortcut * shortcutForSequence(const QKeySequence &sequence) const
Returns the shortcut which is associated for a key sequence, or nullptr if no shortcut is associated.
QList< QAction * > listActions() const
Returns a list of all actions in the manager.
QString objectSettingKey(QObject *object) const
Returns the full settings key matching the QShortcut or QAction Return an empty QString if the QObjec...
QAction * actionForSequence(const QKeySequence &sequence) const
Returns the action which is associated for a shortcut sequence, or nullptr if no action is associated...
bool unregisterAction(QAction *action)
Removes an action from the manager.
QObject * objectForSequence(const QKeySequence &sequence) const
Returns the object (QAction or QShortcut) matching the specified key sequence,.