17#include "moc_qgsconfigureshortcutsdialog.cpp"
27#include <QKeySequence>
30#include <QDomDocument>
36#include <QTextDocument>
39#include <QTextTableFormat>
40#include <QTextTableCellFormat>
41#include <QTextCharFormat>
50 mSaveMenu =
new QMenu(
this );
51 mSaveUserShortcuts =
new QAction( tr(
"Save User Shortcuts…" ),
this );
52 mSaveMenu->addAction( mSaveUserShortcuts );
53 connect( mSaveUserShortcuts, &QAction::triggered,
this, [
this] { saveShortcuts(
false ); } );
55 mSaveAllShortcuts =
new QAction( tr(
"Save All Shortcuts…" ),
this );
56 mSaveMenu->addAction( mSaveAllShortcuts );
57 connect( mSaveAllShortcuts, &QAction::triggered,
this, [
this] { saveShortcuts(); } );
59 mSaveAsPdf =
new QAction( tr(
"Save as PDF…" ),
this );
60 mSaveMenu->addAction( mSaveAsPdf );
61 connect( mSaveAsPdf, &QAction::triggered,
this, &QgsConfigureShortcutsDialog::saveShortcutsPdf );
63 btnSaveShortcuts->setMenu( mSaveMenu );
65 connect( mLeFilter, &QgsFilterLineEdit::textChanged,
this, &QgsConfigureShortcutsDialog::mLeFilter_textChanged );
70 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsConfigureShortcutsDialog::showHelp );
71 connect( btnChangeShortcut, &QAbstractButton::clicked,
this, &QgsConfigureShortcutsDialog::changeShortcut );
72 connect( btnResetShortcut, &QAbstractButton::clicked,
this, &QgsConfigureShortcutsDialog::resetShortcut );
73 connect( btnSetNoShortcut, &QAbstractButton::clicked,
this, &QgsConfigureShortcutsDialog::setNoShortcut );
74 connect( btnLoadShortcuts, &QAbstractButton::clicked,
this, &QgsConfigureShortcutsDialog::loadShortcuts );
76 connect( treeActions, &QTreeWidget::currentItemChanged,
this, &QgsConfigureShortcutsDialog::actionChanged );
81void QgsConfigureShortcutsDialog::populateActions()
83 const QList<QObject *> objects = mManager->
listAll();
85 QList<QTreeWidgetItem *> items;
86 items.reserve( objects.count() );
87 const auto constObjects = objects;
88 for ( QObject *obj : constObjects )
95 if ( QAction *action = qobject_cast<QAction *>( obj ) )
97 actionText = action->text();
98 actionText.remove(
'&' );
99 sequence = action->shortcut().toString( QKeySequence::NativeText );
100 icon = action->icon();
102 else if ( QShortcut *shortcut = qobject_cast<QShortcut *>( obj ) )
104 actionText = shortcut->whatsThis();
105 sequence = shortcut->key().toString( QKeySequence::NativeText );
106 icon = shortcut->property(
"Icon" ).value<QIcon>();
113 if ( actionText.isEmpty() )
119 lst << actionText << sequence;
120 QTreeWidgetItem *item =
new QTreeWidgetItem( lst );
121 item->setIcon( 0, icon );
122 item->setData( 0, Qt::UserRole, QVariant::fromValue( obj ) );
123 item->setToolTip( 0, settingKey );
124 items.append( item );
127 treeActions->addTopLevelItems( items );
130 treeActions->resizeColumnToContents( 0 );
131 treeActions->sortItems( 0, Qt::AscendingOrder );
133 actionChanged( treeActions->currentItem(),
nullptr );
136void QgsConfigureShortcutsDialog::saveShortcuts(
bool saveAll )
138 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Shortcuts" ), QDir::homePath(), tr(
"XML file" ) +
" (*.xml);;" + tr(
"All files" ) +
" (*)" );
143 if ( fileName.isEmpty() )
147 if ( !fileName.endsWith( QLatin1String(
".xml" ), Qt::CaseInsensitive ) )
149 fileName += QLatin1String(
".xml" );
152 QFile file( fileName );
153 if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
155 QMessageBox::warning(
this, tr(
"Saving Shortcuts" ), tr(
"Cannot write file %1:\n%2." ).arg( fileName, file.errorString() ) );
161 QDomDocument doc( QStringLiteral(
"shortcuts" ) );
162 QDomElement root = doc.createElement( QStringLiteral(
"qgsshortcuts" ) );
163 root.setAttribute( QStringLiteral(
"version" ), QStringLiteral(
"1.1" ) );
165 doc.appendChild( root );
167 const QList<QObject *> objects = mManager->
listAll();
168 for ( QObject *obj : objects )
171 QString actionShortcut;
172 QString actionSettingKey;
173 QKeySequence sequence;
175 if ( QAction *action = qobject_cast<QAction *>( obj ) )
177 actionText = action->text().remove(
'&' );
178 actionShortcut = action->shortcut().toString( QKeySequence::NativeText );
181 else if ( QShortcut *shortcut = qobject_cast<QShortcut *>( obj ) )
183 actionText = shortcut->whatsThis();
184 actionShortcut = shortcut->key().toString( QKeySequence::NativeText );
194 if ( actionSettingKey.isEmpty() )
200 if ( !saveAll && sequence == QKeySequence( actionShortcut ) )
205 QDomElement el = doc.createElement( QStringLiteral(
"action" ) );
206 el.setAttribute( QStringLiteral(
"name" ), actionText );
207 el.setAttribute( QStringLiteral(
"shortcut" ), actionShortcut );
208 el.setAttribute( QStringLiteral(
"setting" ), actionSettingKey );
209 root.appendChild( el );
212 QTextStream out( &file );
216void QgsConfigureShortcutsDialog::loadShortcuts()
218 const QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Shortcuts" ), QDir::homePath(), tr(
"XML file" ) +
" (*.xml);;" + tr(
"All files" ) +
" (*)" );
220 if ( fileName.isEmpty() )
225 QFile file( fileName );
226 if ( !file.open( QIODevice::ReadOnly | QIODevice::Text ) )
228 QMessageBox::warning(
this, tr(
"Loading Shortcuts" ), tr(
"Cannot read file %1:\n%2." ).arg( fileName, file.errorString() ) );
237 if ( !doc.setContent( &file,
true, &errorStr, &errorLine, &errorColumn ) )
239 QMessageBox::information(
this, tr(
"Loading Shortcuts" ), tr(
"Parse error at line %1, column %2:\n%3" ).arg( errorLine ).arg( errorColumn ).arg( errorStr ) );
243 const QDomElement root = doc.documentElement();
244 if ( root.tagName() != QLatin1String(
"qgsshortcuts" ) )
246 QMessageBox::information(
this, tr(
"Loading Shortcuts" ), tr(
"The file is not an shortcuts exchange file." ) );
250 QString currentLocale;
253 if ( localeOverrideFlag )
259 currentLocale = QLocale().name();
262 const QString versionStr = root.attribute( QStringLiteral(
"version" ) );
265 if ( root.attribute( QStringLiteral(
"locale" ) ) != currentLocale )
269 QMessageBox::information(
this, tr(
"Loading Shortcuts" ), tr(
"The file contains shortcuts created with different locale, so you can't use it." ) );
274 QMessageBox::information(
this, tr(
"Loading Shortcuts" ), tr(
"The file contains shortcuts created with different locale, so some shortcuts may not work." ) );
279 QString actionShortcut;
280 QString actionSettingKey;
282 QDomElement child = root.firstChildElement();
283 while ( !child.isNull() )
285 actionShortcut = child.attribute( QStringLiteral(
"shortcut" ) );
288 actionName = child.attribute( QStringLiteral(
"name" ) );
293 actionSettingKey = child.attribute( QStringLiteral(
"setting" ) );
299 child = child.nextSiblingElement();
302 treeActions->clear();
306void QgsConfigureShortcutsDialog::changeShortcut()
309 setGettingShortcut(
true );
312void QgsConfigureShortcutsDialog::resetShortcut()
314 QObject *
object = currentObject();
316 setCurrentActionShortcut( sequence );
319void QgsConfigureShortcutsDialog::setNoShortcut()
321 setCurrentActionShortcut( QKeySequence() );
324QAction *QgsConfigureShortcutsDialog::currentAction()
326 return qobject_cast<QAction *>( currentObject() );
329QShortcut *QgsConfigureShortcutsDialog::currentShortcut()
331 return qobject_cast<QShortcut *>( currentObject() );
334void QgsConfigureShortcutsDialog::actionChanged( QTreeWidgetItem *current, QTreeWidgetItem *previous )
339 setGettingShortcut(
false );
342 QKeySequence sequence;
343 if ( QAction *action = currentAction() )
347 sequence = action->shortcut();
349 else if ( QShortcut *
object = currentShortcut() )
353 sequence =
object->key();
360 if ( shortcut.isEmpty() )
361 shortcut = tr(
"None" );
362 btnResetShortcut->setText( tr(
"Set default (%1)" ).arg( shortcut ) );
365 btnSetNoShortcut->setEnabled( !sequence.isEmpty() );
367 btnResetShortcut->setEnabled( sequence != QKeySequence( shortcut ) );
372 if ( !mGettingShortcut )
374 QDialog::keyPressEvent( event );
378 const int key =
event->key();
383 mModifiers |= Qt::META;
384 updateShortcutText();
387 mModifiers |= Qt::ALT;
388 updateShortcutText();
390 case Qt::Key_Control:
391 mModifiers |= Qt::CTRL;
392 updateShortcutText();
395 mModifiers |= Qt::SHIFT;
396 updateShortcutText();
401 setGettingShortcut(
false );
406 updateShortcutText();
412 if ( !mGettingShortcut )
414 QDialog::keyReleaseEvent( event );
418 const int key =
event->key();
423 mModifiers &= ~Qt::META;
424 updateShortcutText();
427 mModifiers &= ~Qt::ALT;
428 updateShortcutText();
430 case Qt::Key_Control:
431 mModifiers &= ~Qt::CTRL;
432 updateShortcutText();
435 mModifiers &= ~Qt::SHIFT;
436 updateShortcutText();
445 setCurrentActionShortcut( QKeySequence( mModifiers + mKey ) );
446 setGettingShortcut(
false );
451QObject *QgsConfigureShortcutsDialog::currentObject()
453 if ( !treeActions->currentItem() )
456 QObject *
object = treeActions->currentItem()->data( 0, Qt::UserRole ).value<QObject *>();
460void QgsConfigureShortcutsDialog::updateShortcutText()
463 const QKeySequence s( mModifiers + mKey );
464 btnChangeShortcut->setText( tr(
"Input: " ) + s.toString( QKeySequence::NativeText ) );
467void QgsConfigureShortcutsDialog::setGettingShortcut(
bool getting )
471 mGettingShortcut = getting;
474 btnChangeShortcut->setChecked(
false );
475 btnChangeShortcut->setText( tr(
"Change" ) );
479 updateShortcutText();
483void QgsConfigureShortcutsDialog::setCurrentActionShortcut(
const QKeySequence &s )
485 QObject *
object = currentObject();
491 if ( otherObject ==
object )
497 if ( QAction *otherAction = qobject_cast<QAction *>( otherObject ) )
499 otherText = otherAction->text();
500 otherText.remove(
'&' );
502 else if ( QShortcut *otherShortcut = qobject_cast<QShortcut *>( otherObject ) )
504 otherText = otherShortcut->whatsThis();
507 const int res = QMessageBox::question(
this, tr(
"Change Shortcut" ), tr(
"This shortcut is already assigned to action %1. Reassign?" ).arg( otherText ), QMessageBox::Yes | QMessageBox::No );
509 if ( res != QMessageBox::Yes )
514 QList<QTreeWidgetItem *> items = treeActions->findItems( otherText, Qt::MatchExactly );
515 if ( !items.isEmpty() )
516 items[0]->setText( 1, QString() );
523 treeActions->currentItem()->setText( 1, s.toString( QKeySequence::NativeText ) );
525 actionChanged( treeActions->currentItem(),
nullptr );
528void QgsConfigureShortcutsDialog::mLeFilter_textChanged(
const QString &text )
530 for (
int i = 0; i < treeActions->topLevelItemCount(); i++ )
532 QTreeWidgetItem *item = treeActions->topLevelItem( i );
533 if ( !item->text( 0 ).contains( text, Qt::CaseInsensitive ) && !item->text( 1 ).contains( text, Qt::CaseInsensitive ) )
535 item->setHidden(
true );
539 item->setHidden(
false );
544void QgsConfigureShortcutsDialog::showHelp()
546 QgsHelp::openHelp( QStringLiteral(
"introduction/qgis_configuration.html#shortcuts" ) );
549void QgsConfigureShortcutsDialog::saveShortcutsPdf()
551 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Shortcuts" ), QDir::homePath(), tr(
"PDF file" ) +
" (*.pdf);;" + tr(
"All files" ) +
" (*)" );
556 if ( fileName.isEmpty() )
559 if ( !fileName.endsWith( QLatin1String(
".pdf" ), Qt::CaseInsensitive ) )
561 fileName += QLatin1String(
".pdf" );
564 QTextDocument *document =
new QTextDocument;
565 QTextCursor cursor( document );
567 QTextTableFormat tableFormat;
568 tableFormat.setBorder( 0 );
569 tableFormat.setCellSpacing( 0 );
570 tableFormat.setCellPadding( 4 );
571 tableFormat.setHeaderRowCount( 1 );
573 QVector<QTextLength> constraints;
574 constraints << QTextLength( QTextLength::PercentageLength, 5 );
575 constraints << QTextLength( QTextLength::PercentageLength, 80 );
576 constraints << QTextLength( QTextLength::PercentageLength, 15 );
577 tableFormat.setColumnWidthConstraints( constraints );
579 QTextTableCellFormat headerFormat;
580 headerFormat.setFontWeight( QFont::Bold );
581 headerFormat.setBottomPadding( 4 );
583 QTextCharFormat rowFormat;
584 rowFormat.setVerticalAlignment( QTextCharFormat::AlignMiddle );
586 QTextCharFormat altRowFormat;
587 altRowFormat.setBackground( QBrush( QColor( 238, 238, 236 ) ) );
588 altRowFormat.setVerticalAlignment( QTextCharFormat::AlignMiddle );
591 QTextTable *table = cursor.insertTable( 1, 3, tableFormat );
592 table->mergeCells( 0, 0, 1, 2 );
593 QTextCursor
c = table->cellAt( row, 0 ).firstCursorPosition();
594 c.setCharFormat( headerFormat );
595 c.insertText( tr(
"Action" ) );
596 c = table->cellAt( row, 2 ).firstCursorPosition();
597 c.setCharFormat( headerFormat );
598 c.insertText( tr(
"Shortcut" ) );
600 const QList<QObject *> objects = mManager->
listAll();
601 for ( QObject *obj : objects )
607 if ( QAction *action = qobject_cast<QAction *>( obj ) )
609 actionText = action->text().remove(
'&' );
610 sequence = action->shortcut().toString( QKeySequence::NativeText );
611 icon = action->icon();
613 else if ( QShortcut *shortcut = qobject_cast<QShortcut *>( obj ) )
615 actionText = shortcut->whatsThis();
616 sequence = shortcut->key().toString( QKeySequence::NativeText );
617 icon = shortcut->property(
"Icon" ).value<QIcon>();
625 if ( actionText.isEmpty() || sequence.isEmpty() )
631 table->appendRows( 1 );
635 table->cellAt( row, 0 ).setFormat( altRowFormat );
636 table->cellAt( row, 1 ).setFormat( altRowFormat );
637 table->cellAt( row, 2 ).setFormat( altRowFormat );
641 table->cellAt( row, 0 ).setFormat( rowFormat );
642 table->cellAt( row, 1 ).setFormat( rowFormat );
643 table->cellAt( row, 2 ).setFormat( rowFormat );
646 if ( !icon.isNull() )
648 c = table->cellAt( row, 0 ).firstCursorPosition();
649 c.insertImage( icon.pixmap( QSize( 24, 24 ) ).toImage() );
651 table->cellAt( row, 1 ).firstCursorPosition().insertText( actionText );
652 table->cellAt( row, 2 ).firstCursorPosition().insertText( sequence );
655 QPdfWriter pdfWriter( fileName );
656 pdfWriter.setPageLayout( QPageLayout( QPageSize( QPageSize::A4 ), QPageLayout::Portrait, QMarginsF( 20, 10, 10, 10 ) ) );
657 document->setPageSize( QSizeF( pdfWriter.pageLayout().fullRect( QPageLayout::Point ).size() ) );
658 document->print( &pdfWriter );
static const QgsSettingsEntryBool * settingsLocaleOverrideFlag
Settings entry locale override flag.
static const QgsSettingsEntryString * settingsLocaleUserLocale
Settings entry locale user locale.
static QgsShortcutsManager * shortcutsManager()
Returns the global shortcuts manager, used for managing a QAction and QShortcut sequences.
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
A class to describe the version of a project.
T valueWithDefaultOverride(const T &defaultValueOverride, const QString &dynamicKeyPart=QString()) const
Returns the settings value with a defaultValueOverride and with an optional dynamicKeyPart.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
QString key(const QString &dynamicKeyPart=QString()) const
Returns settings entry key.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Shortcuts manager is a class that contains a list of QActions and QShortcuts that have been registere...
bool setKeySequence(const QString &name, const QString &sequence)
Modifies an action or shortcut's key sequence.
QList< QObject * > listAll() const
Returns a list of both actions and shortcuts in the manager.
QObject * objectForSettingKey(const QString &name) const
Returns the QShortcut or QAction matching the the full setting key Return nullptr if the key was not ...
QString objectDefaultKeySequence(QObject *object) const
Returns the default sequence for an object (either a QAction or QShortcut).
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.
QString objectSettingKey(QObject *object) const
Returns the full settings key matching the QShortcut or QAction Return an empty QString if the QObjec...
QObject * objectForSequence(const QKeySequence &sequence) const
Returns the object (QAction or QShortcut) matching the specified key sequence,.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c