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