26#include <QKeySequence>
29#include <QDomDocument>
35#include <QTextDocument>
38#include <QTextTableFormat>
39#include <QTextTableCellFormat>
40#include <QTextCharFormat>
49 mSaveMenu =
new QMenu(
this );
50 mSaveUserShortcuts =
new QAction( tr(
"Save User Shortcuts…" ),
this );
51 mSaveMenu->addAction( mSaveUserShortcuts );
52 connect( mSaveUserShortcuts, &QAction::triggered,
this, [
this] { saveShortcuts(
false ); } );
54 mSaveAllShortcuts =
new QAction( tr(
"Save All Shortcuts…" ),
this );
55 mSaveMenu->addAction( mSaveAllShortcuts );
56 connect( mSaveAllShortcuts, &QAction::triggered,
this, [
this] { saveShortcuts(); } );
58 mSaveAsPdf =
new QAction( tr(
"Save as PDF…" ),
this );
59 mSaveMenu->addAction( mSaveAsPdf );
60 connect( mSaveAsPdf, &QAction::triggered,
this, &QgsConfigureShortcutsDialog::saveShortcutsPdf );
62 btnSaveShortcuts->setMenu( mSaveMenu );
64 connect( mLeFilter, &QgsFilterLineEdit::textChanged,
this, &QgsConfigureShortcutsDialog::mLeFilter_textChanged );
69 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsConfigureShortcutsDialog::showHelp );
70 connect( btnChangeShortcut, &QAbstractButton::clicked,
this, &QgsConfigureShortcutsDialog::changeShortcut );
71 connect( btnResetShortcut, &QAbstractButton::clicked,
this, &QgsConfigureShortcutsDialog::resetShortcut );
72 connect( btnSetNoShortcut, &QAbstractButton::clicked,
this, &QgsConfigureShortcutsDialog::setNoShortcut );
73 connect( btnLoadShortcuts, &QAbstractButton::clicked,
this, &QgsConfigureShortcutsDialog::loadShortcuts );
75 connect( treeActions, &QTreeWidget::currentItemChanged,
76 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(),
139 tr(
"XML file" ) +
" (*.xml);;" + tr(
"All files" ) +
" (*)" );
144 if ( fileName.isEmpty() )
148 if ( !fileName.endsWith( QLatin1String(
".xml" ), Qt::CaseInsensitive ) )
150 fileName += QLatin1String(
".xml" );
153 QFile file( fileName );
154 if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
156 QMessageBox::warning(
this, tr(
"Saving Shortcuts" ),
157 tr(
"Cannot write file %1:\n%2." )
159 file.errorString() ) );
165 QDomDocument doc( QStringLiteral(
"shortcuts" ) );
166 QDomElement root = doc.createElement( QStringLiteral(
"qgsshortcuts" ) );
167 root.setAttribute( QStringLiteral(
"version" ), QStringLiteral(
"1.1" ) );
169 doc.appendChild( root );
171 const QList<QObject *> objects = mManager->
listAll();
172 for ( QObject *obj : objects )
175 QString actionShortcut;
176 QString actionSettingKey;
177 QKeySequence sequence;
179 if ( QAction *action = qobject_cast< QAction * >( obj ) )
181 actionText = action->text().remove(
'&' );
182 actionShortcut = action->shortcut().toString( QKeySequence::NativeText );
185 else if ( QShortcut *shortcut = qobject_cast< QShortcut * >( obj ) )
187 actionText = shortcut->whatsThis();
188 actionShortcut = shortcut->key().toString( QKeySequence::NativeText );
198 if ( actionSettingKey.isEmpty() )
204 if ( !saveAll && sequence == QKeySequence( actionShortcut ) )
209 QDomElement el = doc.createElement( QStringLiteral(
"action" ) );
210 el.setAttribute( QStringLiteral(
"name" ), actionText );
211 el.setAttribute( QStringLiteral(
"shortcut" ), actionShortcut );
212 el.setAttribute( QStringLiteral(
"setting" ), actionSettingKey );
213 root.appendChild( el );
216 QTextStream out( &file );
220void QgsConfigureShortcutsDialog::loadShortcuts()
222 const QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Shortcuts" ), QDir::homePath(),
223 tr(
"XML file" ) +
" (*.xml);;" + tr(
"All files" ) +
" (*)" );
225 if ( fileName.isEmpty() )
230 QFile file( fileName );
231 if ( !file.open( QIODevice::ReadOnly | QIODevice::Text ) )
233 QMessageBox::warning(
this, tr(
"Loading Shortcuts" ),
234 tr(
"Cannot read file %1:\n%2." )
236 file.errorString() ) );
245 if ( !doc.setContent( &file,
true, &errorStr, &errorLine, &errorColumn ) )
247 QMessageBox::information(
this, tr(
"Loading Shortcuts" ),
248 tr(
"Parse error at line %1, column %2:\n%3" )
255 const QDomElement root = doc.documentElement();
256 if ( root.tagName() != QLatin1String(
"qgsshortcuts" ) )
258 QMessageBox::information(
this, tr(
"Loading Shortcuts" ),
259 tr(
"The file is not an shortcuts exchange file." ) );
263 QString currentLocale;
266 if ( localeOverrideFlag )
272 currentLocale = QLocale().name();
275 const QString versionStr = root.attribute( QStringLiteral(
"version" ) );
278 if ( root.attribute( QStringLiteral(
"locale" ) ) != currentLocale )
282 QMessageBox::information(
this, tr(
"Loading Shortcuts" ),
283 tr(
"The file contains shortcuts created with different locale, so you can't use it." ) );
288 QMessageBox::information(
this, tr(
"Loading Shortcuts" ),
289 tr(
"The file contains shortcuts created with different locale, so some shortcuts may not work." ) );
294 QString actionShortcut;
295 QString actionSettingKey;
297 QDomElement child = root.firstChildElement();
298 while ( !child.isNull() )
300 actionShortcut = child.attribute( QStringLiteral(
"shortcut" ) );
303 actionName = child.attribute( QStringLiteral(
"name" ) );
308 actionSettingKey = child.attribute( QStringLiteral(
"setting" ) );
315 child = child.nextSiblingElement();
318 treeActions->clear();
322void QgsConfigureShortcutsDialog::changeShortcut()
325 setGettingShortcut(
true );
328void QgsConfigureShortcutsDialog::resetShortcut()
330 QObject *
object = currentObject();
332 setCurrentActionShortcut( sequence );
335void QgsConfigureShortcutsDialog::setNoShortcut()
337 setCurrentActionShortcut( QKeySequence() );
340QAction *QgsConfigureShortcutsDialog::currentAction()
342 return qobject_cast<QAction *>( currentObject() );
345QShortcut *QgsConfigureShortcutsDialog::currentShortcut()
347 return qobject_cast<QShortcut *>( currentObject() );
350void QgsConfigureShortcutsDialog::actionChanged( QTreeWidgetItem *current, QTreeWidgetItem *previous )
355 setGettingShortcut(
false );
358 QKeySequence sequence;
359 if ( QAction *action = currentAction() )
363 sequence = action->shortcut();
365 else if ( QShortcut *
object = currentShortcut() )
369 sequence =
object->key();
376 if ( shortcut.isEmpty() )
377 shortcut = tr(
"None" );
378 btnResetShortcut->setText( tr(
"Set default (%1)" ).arg( shortcut ) );
381 btnSetNoShortcut->setEnabled( !sequence.isEmpty() );
383 btnResetShortcut->setEnabled( sequence != QKeySequence( shortcut ) );
388 if ( !mGettingShortcut )
390 QDialog::keyPressEvent( event );
394 const int key =
event->key();
399 mModifiers |= Qt::META;
400 updateShortcutText();
403 mModifiers |= Qt::ALT;
404 updateShortcutText();
406 case Qt::Key_Control:
407 mModifiers |= Qt::CTRL;
408 updateShortcutText();
411 mModifiers |= Qt::SHIFT;
412 updateShortcutText();
417 setGettingShortcut(
false );
422 updateShortcutText();
428 if ( !mGettingShortcut )
430 QDialog::keyReleaseEvent( event );
434 const int key =
event->key();
439 mModifiers &= ~Qt::META;
440 updateShortcutText();
443 mModifiers &= ~Qt::ALT;
444 updateShortcutText();
446 case Qt::Key_Control:
447 mModifiers &= ~Qt::CTRL;
448 updateShortcutText();
451 mModifiers &= ~Qt::SHIFT;
452 updateShortcutText();
461 setCurrentActionShortcut( QKeySequence( mModifiers + mKey ) );
462 setGettingShortcut(
false );
467QObject *QgsConfigureShortcutsDialog::currentObject()
469 if ( !treeActions->currentItem() )
472 QObject *
object = treeActions->currentItem()->data( 0, Qt::UserRole ).value<QObject *>();
476void QgsConfigureShortcutsDialog::updateShortcutText()
479 const QKeySequence s( mModifiers + mKey );
480 btnChangeShortcut->setText( tr(
"Input: " ) + s.toString( QKeySequence::NativeText ) );
483void QgsConfigureShortcutsDialog::setGettingShortcut(
bool getting )
487 mGettingShortcut = getting;
490 btnChangeShortcut->setChecked(
false );
491 btnChangeShortcut->setText( tr(
"Change" ) );
495 updateShortcutText();
499void QgsConfigureShortcutsDialog::setCurrentActionShortcut(
const QKeySequence &s )
501 QObject *
object = currentObject();
507 if ( otherObject ==
object )
513 if ( QAction *otherAction = qobject_cast< QAction * >( otherObject ) )
515 otherText = otherAction->text();
516 otherText.remove(
'&' );
518 else if ( QShortcut *otherShortcut = qobject_cast< QShortcut * >( otherObject ) )
520 otherText = otherShortcut->whatsThis();
523 const int res = QMessageBox::question(
this, tr(
"Change Shortcut" ),
524 tr(
"This shortcut is already assigned to action %1. Reassign?" ).arg( otherText ),
525 QMessageBox::Yes | QMessageBox::No );
527 if ( res != QMessageBox::Yes )
532 QList<QTreeWidgetItem *> items = treeActions->findItems( otherText, Qt::MatchExactly );
533 if ( !items.isEmpty() )
534 items[0]->setText( 1, QString() );
541 treeActions->currentItem()->setText( 1, s.toString( QKeySequence::NativeText ) );
543 actionChanged( treeActions->currentItem(),
nullptr );
546void QgsConfigureShortcutsDialog::mLeFilter_textChanged(
const QString &text )
548 for (
int i = 0; i < treeActions->topLevelItemCount(); i++ )
550 QTreeWidgetItem *item = treeActions->topLevelItem( i );
551 if ( !item->text( 0 ).contains( text, Qt::CaseInsensitive ) && !item->text( 1 ).contains( text, Qt::CaseInsensitive ) )
553 item->setHidden(
true );
557 item->setHidden(
false );
562void QgsConfigureShortcutsDialog::showHelp()
564 QgsHelp::openHelp( QStringLiteral(
"introduction/qgis_configuration.html#shortcuts" ) );
567void QgsConfigureShortcutsDialog::saveShortcutsPdf()
569 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Shortcuts" ), QDir::homePath(),
570 tr(
"PDF file" ) +
" (*.pdf);;" + tr(
"All files" ) +
" (*)" );
575 if ( fileName.isEmpty() )
578 if ( !fileName.endsWith( QLatin1String(
".pdf" ), Qt::CaseInsensitive ) )
580 fileName += QLatin1String(
".pdf" );
583 QTextDocument *document =
new QTextDocument;
584 QTextCursor cursor( document );
586 QTextTableFormat tableFormat;
587 tableFormat.setBorder( 0 );
588 tableFormat.setCellSpacing( 0 );
589 tableFormat.setCellPadding( 4 );
590 tableFormat.setHeaderRowCount( 1 );
592 QVector<QTextLength> constraints;
593 constraints << QTextLength( QTextLength::PercentageLength, 5 );
594 constraints << QTextLength( QTextLength::PercentageLength, 80 );
595 constraints << QTextLength( QTextLength::PercentageLength, 15 );
596 tableFormat.setColumnWidthConstraints( constraints );
598 QTextTableCellFormat headerFormat;
599 headerFormat.setFontWeight( QFont::Bold );
600 headerFormat.setBottomPadding( 4 );
602 QTextCharFormat rowFormat;
603 rowFormat.setVerticalAlignment( QTextCharFormat::AlignMiddle );
605 QTextCharFormat altRowFormat;
606 altRowFormat.setBackground( QBrush( QColor( 238, 238, 236 ) ) );
607 altRowFormat.setVerticalAlignment( QTextCharFormat::AlignMiddle );
610 QTextTable *table = cursor.insertTable( 1, 3, tableFormat );
611 table->mergeCells( 0, 0, 1, 2 );
612 QTextCursor
c = table->cellAt( row, 0 ).firstCursorPosition();
613 c.setCharFormat( headerFormat );
614 c.insertText( tr(
"Action" ) );
615 c = table->cellAt( row, 2 ).firstCursorPosition();
616 c.setCharFormat( headerFormat );
617 c.insertText( tr(
"Shortcut" ) );
619 const QList<QObject *> objects = mManager->
listAll();
620 for ( QObject *obj : objects )
626 if ( QAction *action = qobject_cast< QAction * >( obj ) )
628 actionText = action->text().remove(
'&' );
629 sequence = action->shortcut().toString( QKeySequence::NativeText );
630 icon = action->icon();
632 else if ( QShortcut *shortcut = qobject_cast< QShortcut * >( obj ) )
634 actionText = shortcut->whatsThis();
635 sequence = shortcut->key().toString( QKeySequence::NativeText );
636 icon = shortcut->property(
"Icon" ).value<QIcon>();
644 if ( actionText.isEmpty() || sequence.isEmpty() )
650 table->appendRows( 1 );
654 table->cellAt( row, 0 ).setFormat( altRowFormat );
655 table->cellAt( row, 1 ).setFormat( altRowFormat );
656 table->cellAt( row, 2 ).setFormat( altRowFormat );
660 table->cellAt( row, 0 ).setFormat( rowFormat );
661 table->cellAt( row, 1 ).setFormat( rowFormat );
662 table->cellAt( row, 2 ).setFormat( rowFormat );
665 if ( !icon.isNull() )
667 c = table->cellAt( row, 0 ).firstCursorPosition();
668 c.insertImage( icon.pixmap( QSize( 24, 24 ) ).toImage() );
670 table->cellAt( row, 1 ).firstCursorPosition().insertText( actionText );
671 table->cellAt( row, 2 ).firstCursorPosition().insertText( sequence );
674 QPdfWriter pdfWriter( fileName );
675 pdfWriter.setPageLayout( QPageLayout( QPageSize( QPageSize::A4 ), QPageLayout::Portrait, QMarginsF( 20, 10, 10, 10 ) ) );
676 document->setPageSize( QSizeF( pdfWriter.pageLayout().fullRect( QPageLayout::Point ).size() ) );
677 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