23#include <QRegularExpression>
25#include <QWidgetAction>
27#include "moc_qgsshortcutsmanager.cpp"
31 , mSettingsPath( settingsRoot )
34 auto registerCommonAction = [
this](
CommonAction commonAction,
const QIcon &icon,
const QString &text,
const QString &toolTip,
const QString &sequence,
const QString &objectName,
const QString §ion ) {
35 QAction *action =
new QAction( icon, text,
this );
36 action->setToolTip( toolTip );
37 setObjectName( objectName );
40 action->setEnabled(
false );
41 action->setProperty(
"commonAction",
static_cast< int >( commonAction ) );
43 mCommonActions.insert(
static_cast< int >( commonAction ), action );
45 registerCommonAction(
CommonAction::CodeToggleComment,
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconCommentEditorConsole.svg" ), QgsApplication::palette().color( QPalette::ColorRole::WindowText ) ), tr(
"Toggle Comment" ), tr(
"Toggle comment" ), QStringLiteral(
"Ctrl+/" ), QStringLiteral(
"mEditorToggleComment" ), QStringLiteral(
"Editor" ) );
46 registerCommonAction(
CommonAction::CodeReformat,
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconFormatCode.svg" ) ), tr(
"Reformat Code" ), tr(
"Reformat code" ), QStringLiteral(
"Ctrl+Alt+F" ), QStringLiteral(
"mEditorReformatCode" ), QStringLiteral(
"Editor" ) );
48 registerCommonAction(
CommonAction::CodeRunSelection,
QgsApplication::getThemeIcon( QStringLiteral(
"mActionRunSelected.svg" ) ), tr(
"Run Selection" ), tr(
"Run selected part of script" ), QStringLiteral(
"Ctrl+E" ), QStringLiteral(
"mEditorRunSelection" ), QStringLiteral(
"Editor" ) );
55 const QHash< int, QAction * > commonActionsToCleanup = std::move( mCommonActions );
56 for (
auto it = commonActionsToCleanup.constBegin(); it != commonActionsToCleanup.constEnd(); ++it )
70 const QList<QObject *> children =
object->children();
71 for ( QObject *child : children )
73 if ( QAction *a = qobject_cast<QAction *>( child ) )
75 registerAction( a, a->shortcut().toString( QKeySequence::NativeText ), section );
79 const QString newSection = child->objectName().isEmpty() ? section : section + child->objectName() +
"/";
87 const QList<QObject *> children =
object->children();
88 for ( QObject *child : children )
90 if ( QShortcut *s = qobject_cast<QShortcut *>( child ) )
92 registerShortcut( s, s->key().toString( QKeySequence::NativeText ), section );
96 const QString newSection = child->objectName().isEmpty() ? section : section + child->objectName() +
"/";
104 if ( qobject_cast<QWidgetAction *>( action ) )
107 if ( mActions.contains( action ) )
111 if ( action->text().isEmpty() && action->objectName().isEmpty() )
114 QgsLogger::warning( QStringLiteral(
"Action has no text set: %1" ).arg( action->objectName() ) );
119 QString key = action->objectName().isEmpty() ? action->text() : action->objectName();
124 QgsLogger::warning( QStringLiteral(
"Duplicate shortcut registered: %1" ).arg( key ) );
127 const QString settingKey = mSettingsPath + ( section.isEmpty() || section.endsWith( QLatin1Char(
'/' ) ) ? section : section + QStringLiteral(
"/" ) ) + key;
129 mActions.insert( action, { defaultSequence, settingKey } );
130 connect( action, &QObject::destroyed,
this, [action,
this]() { actionDestroyed( action ); } );
134 const QString sequence = settings.
value( settingKey, defaultSequence ).toString();
136 action->setShortcut( sequence );
137 if ( !action->toolTip().isEmpty() )
139 action->setToolTip( formatActionToolTip( action->toolTip() ) );
140 updateActionToolTip( action, sequence );
148 const auto it = mCommonActions.constFind(
static_cast< int >( commonAction ) );
149 if ( it == mCommonActions.constEnd() )
153 action->setText( it.value()->text() );
154 action->setToolTip( it.value()->toolTip() );
155 action->setShortcut( it.value()->shortcut() );
157 mLinkedCommonActions.insert( action, commonAction );
158 connect( action, &QObject::destroyed,
this, [action,
this]() { actionDestroyed( action ); } );
165 if ( shortcut->objectName().isEmpty() )
166 QgsLogger::warning( QStringLiteral(
"Shortcut has no object name set: %1" ).arg( shortcut->key().toString() ) );
168 QgsLogger::warning( QStringLiteral(
"Duplicate shortcut registered: %1" ).arg( shortcut->objectName() ) );
171 const QString settingKey = mSettingsPath + ( section.isEmpty() || section.endsWith( QLatin1Char(
'/' ) ) ? section : section + QStringLiteral(
"/" ) ) + shortcut->objectName();
173 mShortcuts.insert( shortcut, { defaultSequence, settingKey } );
174 connect( shortcut, &QObject::destroyed,
this, [shortcut,
this]() { shortcutDestroyed( shortcut ); } );
178 const QString keySequence = settings.
value( settingKey, defaultSequence ).toString();
180 shortcut->setKey( keySequence );
187 if ( !mActions.contains( action ) )
190 mActions.remove( action );
196 if ( !mShortcuts.contains( shortcut ) )
199 mShortcuts.remove( shortcut );
205 return mActions.keys();
210 return mShortcuts.keys();
215 QList<QObject *> list;
216 ActionsHash::const_iterator actionIt = mActions.constBegin();
217 for ( ; actionIt != mActions.constEnd(); ++actionIt )
219 list << actionIt.key();
221 ShortcutsHash::const_iterator shortcutIt = mShortcuts.constBegin();
222 for ( ; shortcutIt != mShortcuts.constEnd(); ++shortcutIt )
224 list << shortcutIt.key();
231 if ( QAction *action = qobject_cast<QAction *>(
object ) )
233 else if ( QShortcut *shortcut = qobject_cast<QShortcut *>(
object ) )
241 return mActions.value( action ).first;
246 return mShortcuts.value( shortcut ).first;
261 if ( QAction *action = qobject_cast<QAction *>(
object ) )
263 else if ( QShortcut *shortcut = qobject_cast<QShortcut *>(
object ) )
271 if ( !mActions.contains( action ) )
275 action->setShortcut( sequence );
276 this->updateActionToolTip( action, sequence );
278 if ( action->property(
"commonAction" ).isValid() )
283 for (
auto it = mLinkedCommonActions.constBegin(); it != mLinkedCommonActions.constEnd(); ++it )
285 if ( it.value() == commonAction )
287 it.key()->setShortcut( action->shortcut() );
288 it.key()->setToolTip( action->toolTip() );
293 const QString settingKey = mActions[action].second;
297 settings.
setValue( settingKey, sequence );
303 if ( !mShortcuts.contains( shortcut ) )
307 shortcut->setKey( sequence );
309 const QString settingKey = mShortcuts[shortcut].second;
313 settings.
setValue( settingKey, sequence );
329 if ( sequence.isEmpty() )
332 for ( ActionsHash::const_iterator it = mActions.constBegin(); it != mActions.constEnd(); ++it )
334 if ( it.key()->shortcut() == sequence )
343 if ( sequence.isEmpty() )
346 for ( ShortcutsHash::const_iterator it = mShortcuts.constBegin(); it != mShortcuts.constEnd(); ++it )
348 if ( it.key()->key() == sequence )
357 const auto it = mCommonActions.constFind(
static_cast< int >( action ) );
358 if ( it == mCommonActions.constEnd() )
359 return QKeySequence();
361 return it.value()->shortcut();
366 for ( ActionsHash::const_iterator it = mActions.constBegin(); it != mActions.constEnd(); ++it )
368 if ( it.key()->objectName() == name )
371 for ( ActionsHash::const_iterator it = mActions.constBegin(); it != mActions.constEnd(); ++it )
373 QString key = it.key()->text();
384 for ( ShortcutsHash::const_iterator it = mShortcuts.constBegin(); it != mShortcuts.constEnd(); ++it )
386 if ( it.key()->objectName() == name )
393void QgsShortcutsManager::actionDestroyed( QAction *action )
395 mActions.remove( action );
396 mLinkedCommonActions.remove( action );
401 if (
auto action = qobject_cast<QAction *>(
object ) )
403 return mActions.value( action ).second;
405 else if (
auto shortcut = qobject_cast<QShortcut *>(
object ) )
407 return mShortcuts.value( shortcut ).second;
414 for ( ActionsHash::const_iterator it = mActions.constBegin(); it != mActions.constEnd(); ++it )
416 if ( it.value().second == settingKey )
419 for ( ShortcutsHash::const_iterator it = mShortcuts.constBegin(); it != mShortcuts.constEnd(); ++it )
421 if ( it.value().second == settingKey )
427void QgsShortcutsManager::shortcutDestroyed( QShortcut *shortcut )
429 mShortcuts.remove( shortcut );
432QString QgsShortcutsManager::formatActionToolTip(
const QString &toolTip )
434 if ( toolTip.isEmpty() )
437 const QStringList parts = toolTip.split(
'\n' );
438 QString formatted = QStringLiteral(
"<b>%1</b>" ).arg( parts.at( 0 ) );
439 if ( parts.count() > 1 )
441 for (
int i = 1; i < parts.count(); ++i )
442 formatted += QStringLiteral(
"<p>%1</p>" ).arg( parts.at( i ) );
448void QgsShortcutsManager::updateActionToolTip( QAction *action,
const QString &sequence )
450 QString current = action->toolTip();
451 const thread_local QRegularExpression rx( QStringLiteral(
"\\s*\\((.*)\\)" ) );
453 QRegularExpressionMatch match;
454 if ( current.lastIndexOf( rx, -1, &match ) != -1 )
457 const QStringList parts = QKeySequence( match.captured( 1 ) ).toString().split(
"," );
458 if ( std::all_of( parts.constBegin(), parts.constEnd(), [](
const QString &part ) { return !part.trimmed().isEmpty(); } ) )
460 current = current.remove( match.capturedStart( 0 ), match.capturedLength( 0 ) );
464 if ( !sequence.isEmpty() )
466 action->setToolTip( current +
" (" + sequence +
")" );
470 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,.