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" ) +
" (*)" );
141 if ( fileName.isEmpty() )
145 if ( !fileName.endsWith( QLatin1String(
".xml" ), Qt::CaseInsensitive ) )
147 fileName += QLatin1String(
".xml" );
150 QFile file( fileName );
151 if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
153 QMessageBox::warning(
this, tr(
"Saving Shortcuts" ),
154 tr(
"Cannot write file %1:\n%2." )
156 file.errorString() ) );
162 QDomDocument doc( QStringLiteral(
"shortcuts" ) );
163 QDomElement root = doc.createElement( QStringLiteral(
"qgsshortcuts" ) );
164 root.setAttribute( QStringLiteral(
"version" ), QStringLiteral(
"1.1" ) );
166 doc.appendChild( root );
168 const QList<QObject *> objects = mManager->
listAll();
169 for ( QObject *obj : objects )
172 QString actionShortcut;
173 QString actionSettingKey;
174 QKeySequence sequence;
176 if ( QAction *action = qobject_cast< QAction * >( obj ) )
178 actionText = action->text().remove(
'&' );
179 actionShortcut = action->shortcut().toString( QKeySequence::NativeText );
182 else if ( QShortcut *shortcut = qobject_cast< QShortcut * >( obj ) )
184 actionText = shortcut->whatsThis();
185 actionShortcut = shortcut->key().toString( QKeySequence::NativeText );
195 if ( actionSettingKey.isEmpty() )
201 if ( !saveAll && sequence == QKeySequence( actionShortcut ) )
206 QDomElement el = doc.createElement( QStringLiteral(
"action" ) );
207 el.setAttribute( QStringLiteral(
"name" ), actionText );
208 el.setAttribute( QStringLiteral(
"shortcut" ), actionShortcut );
209 el.setAttribute( QStringLiteral(
"setting" ), actionSettingKey );
210 root.appendChild( el );
213 QTextStream out( &file );
217void QgsConfigureShortcutsDialog::loadShortcuts()
219 const QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Shortcuts" ), QDir::homePath(),
220 tr(
"XML file" ) +
" (*.xml);;" + tr(
"All files" ) +
" (*)" );
222 if ( fileName.isEmpty() )
227 QFile file( fileName );
228 if ( !file.open( QIODevice::ReadOnly | QIODevice::Text ) )
230 QMessageBox::warning(
this, tr(
"Loading Shortcuts" ),
231 tr(
"Cannot read file %1:\n%2." )
233 file.errorString() ) );
242 if ( !doc.setContent( &file,
true, &errorStr, &errorLine, &errorColumn ) )
244 QMessageBox::information(
this, tr(
"Loading Shortcuts" ),
245 tr(
"Parse error at line %1, column %2:\n%3" )
252 const QDomElement root = doc.documentElement();
253 if ( root.tagName() != QLatin1String(
"qgsshortcuts" ) )
255 QMessageBox::information(
this, tr(
"Loading Shortcuts" ),
256 tr(
"The file is not an shortcuts exchange file." ) );
260 QString currentLocale;
263 if ( localeOverrideFlag )
269 currentLocale = QLocale().name();
272 const QString versionStr = root.attribute( QStringLiteral(
"version" ) );
275 if ( root.attribute( QStringLiteral(
"locale" ) ) != currentLocale )
279 QMessageBox::information(
this, tr(
"Loading Shortcuts" ),
280 tr(
"The file contains shortcuts created with different locale, so you can't use it." ) );
285 QMessageBox::information(
this, tr(
"Loading Shortcuts" ),
286 tr(
"The file contains shortcuts created with different locale, so some shortcuts may not work." ) );
291 QString actionShortcut;
292 QString actionSettingKey;
294 QDomElement child = root.firstChildElement();
295 while ( !child.isNull() )
297 actionShortcut = child.attribute( QStringLiteral(
"shortcut" ) );
300 actionName = child.attribute( QStringLiteral(
"name" ) );
305 actionSettingKey = child.attribute( QStringLiteral(
"setting" ) );
312 child = child.nextSiblingElement();
315 treeActions->clear();
319void QgsConfigureShortcutsDialog::changeShortcut()
322 setGettingShortcut(
true );
325void QgsConfigureShortcutsDialog::resetShortcut()
327 QObject *
object = currentObject();
329 setCurrentActionShortcut( sequence );
332void QgsConfigureShortcutsDialog::setNoShortcut()
334 setCurrentActionShortcut( QKeySequence() );
337QAction *QgsConfigureShortcutsDialog::currentAction()
339 return qobject_cast<QAction *>( currentObject() );
342QShortcut *QgsConfigureShortcutsDialog::currentShortcut()
344 return qobject_cast<QShortcut *>( currentObject() );
347void QgsConfigureShortcutsDialog::actionChanged( QTreeWidgetItem *current, QTreeWidgetItem *previous )
352 setGettingShortcut(
false );
355 QKeySequence sequence;
356 if ( QAction *action = currentAction() )
360 sequence = action->shortcut();
362 else if ( QShortcut *
object = currentShortcut() )
366 sequence =
object->key();
373 if ( shortcut.isEmpty() )
374 shortcut = tr(
"None" );
375 btnResetShortcut->setText( tr(
"Set default (%1)" ).arg( shortcut ) );
378 btnSetNoShortcut->setEnabled( !sequence.isEmpty() );
380 btnResetShortcut->setEnabled( sequence != QKeySequence( shortcut ) );
385 if ( !mGettingShortcut )
387 QDialog::keyPressEvent( event );
391 const int key =
event->key();
396 mModifiers |= Qt::META;
397 updateShortcutText();
400 mModifiers |= Qt::ALT;
401 updateShortcutText();
403 case Qt::Key_Control:
404 mModifiers |= Qt::CTRL;
405 updateShortcutText();
408 mModifiers |= Qt::SHIFT;
409 updateShortcutText();
414 setGettingShortcut(
false );
419 updateShortcutText();
425 if ( !mGettingShortcut )
427 QDialog::keyReleaseEvent( event );
431 const int key =
event->key();
436 mModifiers &= ~Qt::META;
437 updateShortcutText();
440 mModifiers &= ~Qt::ALT;
441 updateShortcutText();
443 case Qt::Key_Control:
444 mModifiers &= ~Qt::CTRL;
445 updateShortcutText();
448 mModifiers &= ~Qt::SHIFT;
449 updateShortcutText();
458 setCurrentActionShortcut( QKeySequence( mModifiers + mKey ) );
459 setGettingShortcut(
false );
464QObject *QgsConfigureShortcutsDialog::currentObject()
466 if ( !treeActions->currentItem() )
469 QObject *
object = treeActions->currentItem()->data( 0, Qt::UserRole ).value<QObject *>();
473void QgsConfigureShortcutsDialog::updateShortcutText()
476 const QKeySequence s( mModifiers + mKey );
477 btnChangeShortcut->setText( tr(
"Input: " ) + s.toString( QKeySequence::NativeText ) );
480void QgsConfigureShortcutsDialog::setGettingShortcut(
bool getting )
484 mGettingShortcut = getting;
487 btnChangeShortcut->setChecked(
false );
488 btnChangeShortcut->setText( tr(
"Change" ) );
492 updateShortcutText();
496void QgsConfigureShortcutsDialog::setCurrentActionShortcut(
const QKeySequence &s )
498 QObject *
object = currentObject();
504 if ( otherObject ==
object )
510 if ( QAction *otherAction = qobject_cast< QAction * >( otherObject ) )
512 otherText = otherAction->text();
513 otherText.remove(
'&' );
515 else if ( QShortcut *otherShortcut = qobject_cast< QShortcut * >( otherObject ) )
517 otherText = otherShortcut->whatsThis();
520 const int res = QMessageBox::question(
this, tr(
"Change Shortcut" ),
521 tr(
"This shortcut is already assigned to action %1. Reassign?" ).arg( otherText ),
522 QMessageBox::Yes | QMessageBox::No );
524 if ( res != QMessageBox::Yes )
529 QList<QTreeWidgetItem *> items = treeActions->findItems( otherText, Qt::MatchExactly );
530 if ( !items.isEmpty() )
531 items[0]->setText( 1, QString() );
538 treeActions->currentItem()->setText( 1, s.toString( QKeySequence::NativeText ) );
540 actionChanged( treeActions->currentItem(),
nullptr );
543void QgsConfigureShortcutsDialog::mLeFilter_textChanged(
const QString &text )
545 for (
int i = 0; i < treeActions->topLevelItemCount(); i++ )
547 QTreeWidgetItem *item = treeActions->topLevelItem( i );
548 if ( !item->text( 0 ).contains( text, Qt::CaseInsensitive ) && !item->text( 1 ).contains( text, Qt::CaseInsensitive ) )
550 item->setHidden(
true );
554 item->setHidden(
false );
559void QgsConfigureShortcutsDialog::showHelp()
561 QgsHelp::openHelp( QStringLiteral(
"introduction/qgis_configuration.html#keyboard-shortcuts" ) );
564void QgsConfigureShortcutsDialog::saveShortcutsPdf()
566 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Shortcuts" ), QDir::homePath(),
567 tr(
"PDF file" ) +
" (*.pdf);;" + tr(
"All files" ) +
" (*)" );
569 if ( fileName.isEmpty() )
572 if ( !fileName.endsWith( QLatin1String(
".pdf" ), Qt::CaseInsensitive ) )
574 fileName += QLatin1String(
".pdf" );
577 QTextDocument *document =
new QTextDocument;
578 QTextCursor cursor( document );
580 QTextTableFormat tableFormat;
581 tableFormat.setBorder( 0 );
582 tableFormat.setCellSpacing( 0 );
583 tableFormat.setCellPadding( 4 );
584 tableFormat.setHeaderRowCount( 1 );
586 QVector<QTextLength> constraints;
587 constraints << QTextLength( QTextLength::PercentageLength, 5 );
588 constraints << QTextLength( QTextLength::PercentageLength, 80 );
589 constraints << QTextLength( QTextLength::PercentageLength, 15 );
590 tableFormat.setColumnWidthConstraints( constraints );
592 QTextTableCellFormat headerFormat;
593 headerFormat.setFontWeight( QFont::Bold );
594 headerFormat.setBottomPadding( 4 );
596 QTextCharFormat rowFormat;
597 rowFormat.setVerticalAlignment( QTextCharFormat::AlignMiddle );
599 QTextCharFormat altRowFormat;
600 altRowFormat.setBackground( QBrush( QColor( 238, 238, 236 ) ) );
601 altRowFormat.setVerticalAlignment( QTextCharFormat::AlignMiddle );
604 QTextTable *table = cursor.insertTable( 1, 3, tableFormat );
605 table->mergeCells( 0, 0, 1, 2 );
606 QTextCursor
c = table->cellAt( row, 0 ).firstCursorPosition();
607 c.setCharFormat( headerFormat );
608 c.insertText( tr(
"Action" ) );
609 c = table->cellAt( row, 2 ).firstCursorPosition();
610 c.setCharFormat( headerFormat );
611 c.insertText( tr(
"Shortcut" ) );
613 const QList<QObject *> objects = mManager->
listAll();
614 for ( QObject *obj : objects )
620 if ( QAction *action = qobject_cast< QAction * >( obj ) )
622 actionText = action->text().remove(
'&' );
623 sequence = action->shortcut().toString( QKeySequence::NativeText );
624 icon = action->icon();
626 else if ( QShortcut *shortcut = qobject_cast< QShortcut * >( obj ) )
628 actionText = shortcut->whatsThis();
629 sequence = shortcut->key().toString( QKeySequence::NativeText );
630 icon = shortcut->property(
"Icon" ).value<QIcon>();
638 if ( actionText.isEmpty() || sequence.isEmpty() )
644 table->appendRows( 1 );
648 table->cellAt( row, 0 ).setFormat( altRowFormat );
649 table->cellAt( row, 1 ).setFormat( altRowFormat );
650 table->cellAt( row, 2 ).setFormat( altRowFormat );
654 table->cellAt( row, 0 ).setFormat( rowFormat );
655 table->cellAt( row, 1 ).setFormat( rowFormat );
656 table->cellAt( row, 2 ).setFormat( rowFormat );
659 if ( !icon.isNull() )
661 c = table->cellAt( row, 0 ).firstCursorPosition();
662 c.insertImage( icon.pixmap( QSize( 24, 24 ) ).toImage() );
664 table->cellAt( row, 1 ).firstCursorPosition().insertText( actionText );
665 table->cellAt( row, 2 ).firstCursorPosition().insertText( sequence );
668 QPrinter printer( QPrinter::ScreenResolution );
669 printer.setOutputFormat( QPrinter::PdfFormat );
670 printer.setPageLayout( QPageLayout( QPageSize( QPageSize::A4 ), QPageLayout::Portrait, QMarginsF( 20, 10, 10, 10 ) ) );
671 printer.setOutputFileName( fileName );
672 document->setPageSize( QSizeF( printer.pageRect( QPrinter::DevicePixel ).size() ) );
674 document->print( &printer );
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