23#include <QRegularExpression>
26#include <QWidgetAction>
28#include "moc_qgsshortcutsmanager.cpp"
30using namespace Qt::StringLiterals;
34 , mSettingsPath( settingsRoot )
37 auto registerCommonAction = [
this](
CommonAction commonAction,
const QIcon &icon,
const QString &text,
const QString &toolTip,
const QString &sequence,
const QString &objectName,
const QString §ion ) {
38 QAction *action =
new QAction( icon, text,
this );
39 action->setToolTip( toolTip );
40 setObjectName( objectName );
43 action->setEnabled(
false );
44 action->setProperty(
"commonAction",
static_cast< int >( commonAction ) );
46 mCommonActions.insert(
static_cast< int >( commonAction ), action );
50 registerCommonAction(
CommonAction::CodeToggleComment,
QgsApplication::getThemeIcon( u
"console/iconCommentEditorConsole.svg"_s, QgsApplication::palette().color( QPalette::ColorRole::WindowText ) ), tr(
"Toggle Comment" ), tr(
"Toggle comment" ), u
"Ctrl+:"_s, u
"mEditorToggleComment"_s, u
"Editor"_s );
52 registerCommonAction(
CommonAction::CodeToggleComment,
QgsApplication::getThemeIcon( u
"console/iconCommentEditorConsole.svg"_s, QgsApplication::palette().color( QPalette::ColorRole::WindowText ) ), tr(
"Toggle Comment" ), tr(
"Toggle comment" ), u
"Ctrl+/"_s, u
"mEditorToggleComment"_s, u
"Editor"_s );
63 const QHash< int, QAction * > commonActionsToCleanup = std::move( mCommonActions );
64 for (
auto it = commonActionsToCleanup.constBegin(); it != commonActionsToCleanup.constEnd(); ++it )
78 const QList<QObject *> children =
object->children();
79 for ( QObject *child : children )
81 if ( QAction *a = qobject_cast<QAction *>( child ) )
83 registerAction( a, a->shortcut().toString( QKeySequence::NativeText ), section );
87 const QString newSection = child->objectName().isEmpty() ? section : section + child->objectName() +
"/";
95 const QList<QObject *> children =
object->children();
96 for ( QObject *child : children )
98 if ( QShortcut *s = qobject_cast<QShortcut *>( child ) )
100 registerShortcut( s, s->key().toString( QKeySequence::NativeText ), section );
102 else if ( recursive )
104 const QString newSection = child->objectName().isEmpty() ? section : section + child->objectName() +
"/";
112 if ( qobject_cast<QWidgetAction *>( action ) )
115 if ( mActions.contains( action ) )
119 if ( action->text().isEmpty() && action->objectName().isEmpty() )
127 QString key = action->objectName().isEmpty() ? action->text() : action->objectName();
135 const QString settingKey = mSettingsPath + ( section.isEmpty() || section.endsWith(
'/'_L1 ) ? section : section + u
"/"_s ) + key;
137 mActions.insert( action, { defaultSequence, settingKey } );
138 connect( action, &QObject::destroyed,
this, [action,
this]() { actionDestroyed( action ); } );
142 const QString sequence = settings.
value( settingKey, defaultSequence ).toString();
144 action->setShortcut( sequence );
145 if ( !action->toolTip().isEmpty() )
147 action->setToolTip( formatActionToolTip( action->toolTip() ) );
148 updateActionToolTip( action, sequence );
156 const auto it = mCommonActions.constFind(
static_cast< int >( commonAction ) );
157 if ( it == mCommonActions.constEnd() )
161 action->setText( it.value()->text() );
162 action->setToolTip( it.value()->toolTip() );
163 action->setShortcut( it.value()->shortcut() );
165 mLinkedCommonActions.insert( action, commonAction );
166 connect( action, &QObject::destroyed,
this, [action,
this]() { actionDestroyed( action ); } );
173 if ( shortcut->objectName().isEmpty() )
174 QgsLogger::warning( u
"Shortcut has no object name set: %1"_s.arg( shortcut->key().toString() ) );
176 QgsLogger::warning( u
"Duplicate shortcut registered: %1"_s.arg( shortcut->objectName() ) );
179 const QString settingKey = mSettingsPath + ( section.isEmpty() || section.endsWith(
'/'_L1 ) ? section : section + u
"/"_s ) + shortcut->objectName();
181 mShortcuts.insert( shortcut, { defaultSequence, settingKey } );
182 connect( shortcut, &QObject::destroyed,
this, [shortcut,
this]() { shortcutDestroyed( shortcut ); } );
186 const QString keySequence = settings.
value( settingKey, defaultSequence ).toString();
188 shortcut->setKey( keySequence );
195 if ( !mActions.contains( action ) )
198 mActions.remove( action );
204 if ( !mShortcuts.contains( shortcut ) )
207 mShortcuts.remove( shortcut );
213 return mActions.keys();
218 return mShortcuts.keys();
223 QList<QObject *> list;
224 ActionsHash::const_iterator actionIt = mActions.constBegin();
225 for ( ; actionIt != mActions.constEnd(); ++actionIt )
227 list << actionIt.key();
229 ShortcutsHash::const_iterator shortcutIt = mShortcuts.constBegin();
230 for ( ; shortcutIt != mShortcuts.constEnd(); ++shortcutIt )
232 list << shortcutIt.key();
239 if ( QAction *action = qobject_cast<QAction *>(
object ) )
241 else if ( QShortcut *shortcut = qobject_cast<QShortcut *>(
object ) )
249 return mActions.value( action ).first;
254 return mShortcuts.value( shortcut ).first;
269 if ( QAction *action = qobject_cast<QAction *>(
object ) )
271 else if ( QShortcut *shortcut = qobject_cast<QShortcut *>(
object ) )
279 if ( !mActions.contains( action ) )
283 action->setShortcut( sequence );
284 this->updateActionToolTip( action, sequence );
286 if ( action->property(
"commonAction" ).isValid() )
291 for (
auto it = mLinkedCommonActions.constBegin(); it != mLinkedCommonActions.constEnd(); ++it )
293 if ( it.value() == commonAction )
295 it.key()->setShortcut( action->shortcut() );
296 it.key()->setToolTip( action->toolTip() );
301 const QString settingKey = mActions[action].second;
305 settings.
setValue( settingKey, sequence );
311 if ( !mShortcuts.contains( shortcut ) )
315 shortcut->setKey( sequence );
317 const QString settingKey = mShortcuts[shortcut].second;
321 settings.
setValue( settingKey, sequence );
337 if ( sequence.isEmpty() )
340 for ( ActionsHash::const_iterator it = mActions.constBegin(); it != mActions.constEnd(); ++it )
342 if ( it.key()->shortcut() == sequence )
351 if ( sequence.isEmpty() )
354 for ( ShortcutsHash::const_iterator it = mShortcuts.constBegin(); it != mShortcuts.constEnd(); ++it )
356 if ( it.key()->key() == sequence )
365 const auto it = mCommonActions.constFind(
static_cast< int >( action ) );
366 if ( it == mCommonActions.constEnd() )
367 return QKeySequence();
369 return it.value()->shortcut();
374 for ( ActionsHash::const_iterator it = mActions.constBegin(); it != mActions.constEnd(); ++it )
376 if ( it.key()->objectName() == name )
379 for ( ActionsHash::const_iterator it = mActions.constBegin(); it != mActions.constEnd(); ++it )
381 QString key = it.key()->text();
392 for ( ShortcutsHash::const_iterator it = mShortcuts.constBegin(); it != mShortcuts.constEnd(); ++it )
394 if ( it.key()->objectName() == name )
401void QgsShortcutsManager::actionDestroyed( QAction *action )
403 mActions.remove( action );
404 mLinkedCommonActions.remove( action );
409 if (
auto action = qobject_cast<QAction *>(
object ) )
411 return mActions.value( action ).second;
413 else if (
auto shortcut = qobject_cast<QShortcut *>(
object ) )
415 return mShortcuts.value( shortcut ).second;
422 for ( ActionsHash::const_iterator it = mActions.constBegin(); it != mActions.constEnd(); ++it )
424 if ( it.value().second == settingKey )
427 for ( ShortcutsHash::const_iterator it = mShortcuts.constBegin(); it != mShortcuts.constEnd(); ++it )
429 if ( it.value().second == settingKey )
435void QgsShortcutsManager::shortcutDestroyed( QShortcut *shortcut )
437 mShortcuts.remove( shortcut );
440QString QgsShortcutsManager::formatActionToolTip(
const QString &toolTip )
442 if ( toolTip.isEmpty() )
445 const QStringList parts = toolTip.split(
'\n' );
446 QString formatted = u
"<b>%1</b>"_s.arg( parts.at( 0 ) );
447 if ( parts.count() > 1 )
449 for (
int i = 1; i < parts.count(); ++i )
450 formatted += u
"<p>%1</p>"_s.arg( parts.at( i ) );
456void QgsShortcutsManager::updateActionToolTip( QAction *action,
const QString &sequence )
458 QString current = action->toolTip();
459 const thread_local QRegularExpression rx( u
"\\s*\\((.*)\\)"_s );
461 QRegularExpressionMatch match;
462 if ( current.lastIndexOf( rx, -1, &match ) != -1 )
465 const QStringList parts = QKeySequence( match.captured( 1 ) ).toString().split(
"," );
466 if ( std::all_of( parts.constBegin(), parts.constEnd(), [](
const QString &part ) { return !part.trimmed().isEmpty(); } ) )
468 current = current.remove( match.capturedStart( 0 ), match.capturedLength( 0 ) );
472 if ( !sequence.isEmpty() )
474 action->setToolTip( current +
" (" + sequence +
")" );
478 action->setToolTip( current );
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static void warning(const QString &msg)
Goes to qWarning.
Stores settings for use within QGIS.
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 §ion=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 §ion=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 §ion=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.
CommonAction
Contains common actions which are used across a variety of classes.
@ CodeRunScript
Run script.
@ CodeReformat
Reformat code.
@ CodeRunSelection
Run selection from script.
@ CodeToggleComment
Toggle code comments.
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 §ion=QString())
Registers an action with the manager so the shortcut can be configured in GUI.
void registerAllChildren(QObject *object, bool recursive=false, const QString §ion=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.
QKeySequence sequenceForCommonAction(CommonAction action) const
Returns the key sequence which is associated with a common action, or an empty sequence if no shortcu...
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...
void initializeCommonAction(QAction *action, CommonAction commonAction)
Initializes an action as a common action.
bool unregisterAction(QAction *action)
Removes an action from the manager.
~QgsShortcutsManager() override
QObject * objectForSequence(const QKeySequence &sequence) const
Returns the object (QAction or QShortcut) matching the specified key sequence,.