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,
77 this, &QgsConfigureShortcutsDialog::actionChanged );
82void QgsConfigureShortcutsDialog::populateActions()
84 const QList<QObject *> objects = mManager->
listAll();
86 QList<QTreeWidgetItem *> items;
87 items.reserve( objects.count() );
88 const auto constObjects = objects;
89 for ( QObject *obj : constObjects )
96 if ( QAction *action = qobject_cast< QAction * >( obj ) )
98 actionText = action->text();
99 actionText.remove(
'&' );
100 sequence = action->shortcut().toString( QKeySequence::NativeText );
101 icon = action->icon();
103 else if ( QShortcut *shortcut = qobject_cast< QShortcut * >( obj ) )
105 actionText = shortcut->whatsThis();
106 sequence = shortcut->key().toString( QKeySequence::NativeText );
107 icon = shortcut->property(
"Icon" ).value<QIcon>();
114 if ( actionText.isEmpty() )
120 lst << actionText << sequence;
121 QTreeWidgetItem *item =
new QTreeWidgetItem( lst );
122 item->setIcon( 0, icon );
123 item->setData( 0, Qt::UserRole, QVariant::fromValue( obj ) );
124 item->setToolTip( 0, settingKey );
125 items.append( item );
128 treeActions->addTopLevelItems( items );
131 treeActions->resizeColumnToContents( 0 );
132 treeActions->sortItems( 0, Qt::AscendingOrder );
134 actionChanged( treeActions->currentItem(),
nullptr );
137void QgsConfigureShortcutsDialog::saveShortcuts(
bool saveAll )
139 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Shortcuts" ), QDir::homePath(),
140 tr(
"XML file" ) +
" (*.xml);;" + tr(
"All files" ) +
" (*)" );
145 if ( fileName.isEmpty() )
149 if ( !fileName.endsWith( QLatin1String(
".xml" ), Qt::CaseInsensitive ) )
151 fileName += QLatin1String(
".xml" );
154 QFile file( fileName );
155 if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
157 QMessageBox::warning(
this, tr(
"Saving Shortcuts" ),
158 tr(
"Cannot write file %1:\n%2." )
160 file.errorString() ) );
166 QDomDocument doc( QStringLiteral(
"shortcuts" ) );
167 QDomElement root = doc.createElement( QStringLiteral(
"qgsshortcuts" ) );
168 root.setAttribute( QStringLiteral(
"version" ), QStringLiteral(
"1.1" ) );
170 doc.appendChild( root );
172 const QList<QObject *> objects = mManager->
listAll();
173 for ( QObject *obj : objects )
176 QString actionShortcut;
177 QString actionSettingKey;
178 QKeySequence sequence;
180 if ( QAction *action = qobject_cast< QAction * >( obj ) )
182 actionText = action->text().remove(
'&' );
183 actionShortcut = action->shortcut().toString( QKeySequence::NativeText );
186 else if ( QShortcut *shortcut = qobject_cast< QShortcut * >( obj ) )
188 actionText = shortcut->whatsThis();
189 actionShortcut = shortcut->key().toString( QKeySequence::NativeText );
199 if ( actionSettingKey.isEmpty() )
205 if ( !saveAll && sequence == QKeySequence( actionShortcut ) )
210 QDomElement el = doc.createElement( QStringLiteral(
"action" ) );
211 el.setAttribute( QStringLiteral(
"name" ), actionText );
212 el.setAttribute( QStringLiteral(
"shortcut" ), actionShortcut );
213 el.setAttribute( QStringLiteral(
"setting" ), actionSettingKey );
214 root.appendChild( el );
217 QTextStream out( &file );
221void QgsConfigureShortcutsDialog::loadShortcuts()
223 const QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Shortcuts" ), QDir::homePath(),
224 tr(
"XML file" ) +
" (*.xml);;" + tr(
"All files" ) +
" (*)" );
226 if ( fileName.isEmpty() )
231 QFile file( fileName );
232 if ( !file.open( QIODevice::ReadOnly | QIODevice::Text ) )
234 QMessageBox::warning(
this, tr(
"Loading Shortcuts" ),
235 tr(
"Cannot read file %1:\n%2." )
237 file.errorString() ) );
246 if ( !doc.setContent( &file,
true, &errorStr, &errorLine, &errorColumn ) )
248 QMessageBox::information(
this, tr(
"Loading Shortcuts" ),
249 tr(
"Parse error at line %1, column %2:\n%3" )
256 const QDomElement root = doc.documentElement();
257 if ( root.tagName() != QLatin1String(
"qgsshortcuts" ) )
259 QMessageBox::information(
this, tr(
"Loading Shortcuts" ),
260 tr(
"The file is not an shortcuts exchange file." ) );
264 QString currentLocale;
267 if ( localeOverrideFlag )
273 currentLocale = QLocale().name();
276 const QString versionStr = root.attribute( QStringLiteral(
"version" ) );
279 if ( root.attribute( QStringLiteral(
"locale" ) ) != currentLocale )
283 QMessageBox::information(
this, tr(
"Loading Shortcuts" ),
284 tr(
"The file contains shortcuts created with different locale, so you can't use it." ) );
289 QMessageBox::information(
this, tr(
"Loading Shortcuts" ),
290 tr(
"The file contains shortcuts created with different locale, so some shortcuts may not work." ) );
295 QString actionShortcut;
296 QString actionSettingKey;
298 QDomElement child = root.firstChildElement();
299 while ( !child.isNull() )
301 actionShortcut = child.attribute( QStringLiteral(
"shortcut" ) );
304 actionName = child.attribute( QStringLiteral(
"name" ) );
309 actionSettingKey = child.attribute( QStringLiteral(
"setting" ) );
316 child = child.nextSiblingElement();
319 treeActions->clear();
323void QgsConfigureShortcutsDialog::changeShortcut()
326 setGettingShortcut(
true );
329void QgsConfigureShortcutsDialog::resetShortcut()
331 QObject *
object = currentObject();
333 setCurrentActionShortcut( sequence );
336void QgsConfigureShortcutsDialog::setNoShortcut()
338 setCurrentActionShortcut( QKeySequence() );
341QAction *QgsConfigureShortcutsDialog::currentAction()
343 return qobject_cast<QAction *>( currentObject() );
346QShortcut *QgsConfigureShortcutsDialog::currentShortcut()
348 return qobject_cast<QShortcut *>( currentObject() );
351void QgsConfigureShortcutsDialog::actionChanged( QTreeWidgetItem *current, QTreeWidgetItem *previous )
356 setGettingShortcut(
false );
359 QKeySequence sequence;
360 if ( QAction *action = currentAction() )
364 sequence = action->shortcut();
366 else if ( QShortcut *
object = currentShortcut() )
370 sequence =
object->key();
377 if ( shortcut.isEmpty() )
378 shortcut = tr(
"None" );
379 btnResetShortcut->setText( tr(
"Set default (%1)" ).arg( shortcut ) );
382 btnSetNoShortcut->setEnabled( !sequence.isEmpty() );
384 btnResetShortcut->setEnabled( sequence != QKeySequence( shortcut ) );
389 if ( !mGettingShortcut )
391 QDialog::keyPressEvent( event );
395 const int key =
event->key();
400 mModifiers |= Qt::META;
401 updateShortcutText();
404 mModifiers |= Qt::ALT;
405 updateShortcutText();
407 case Qt::Key_Control:
408 mModifiers |= Qt::CTRL;
409 updateShortcutText();
412 mModifiers |= Qt::SHIFT;
413 updateShortcutText();
418 setGettingShortcut(
false );
423 updateShortcutText();
429 if ( !mGettingShortcut )
431 QDialog::keyReleaseEvent( event );
435 const int key =
event->key();
440 mModifiers &= ~Qt::META;
441 updateShortcutText();
444 mModifiers &= ~Qt::ALT;
445 updateShortcutText();
447 case Qt::Key_Control:
448 mModifiers &= ~Qt::CTRL;
449 updateShortcutText();
452 mModifiers &= ~Qt::SHIFT;
453 updateShortcutText();
462 setCurrentActionShortcut( QKeySequence( mModifiers + mKey ) );
463 setGettingShortcut(
false );
468QObject *QgsConfigureShortcutsDialog::currentObject()
470 if ( !treeActions->currentItem() )
473 QObject *
object = treeActions->currentItem()->data( 0, Qt::UserRole ).value<QObject *>();
477void QgsConfigureShortcutsDialog::updateShortcutText()
480 const QKeySequence s( mModifiers + mKey );
481 btnChangeShortcut->setText( tr(
"Input: " ) + s.toString( QKeySequence::NativeText ) );
484void QgsConfigureShortcutsDialog::setGettingShortcut(
bool getting )
488 mGettingShortcut = getting;
491 btnChangeShortcut->setChecked(
false );
492 btnChangeShortcut->setText( tr(
"Change" ) );
496 updateShortcutText();
500void QgsConfigureShortcutsDialog::setCurrentActionShortcut(
const QKeySequence &s )
502 QObject *
object = currentObject();
508 if ( otherObject ==
object )
514 if ( QAction *otherAction = qobject_cast< QAction * >( otherObject ) )
516 otherText = otherAction->text();
517 otherText.remove(
'&' );
519 else if ( QShortcut *otherShortcut = qobject_cast< QShortcut * >( otherObject ) )
521 otherText = otherShortcut->whatsThis();
524 const int res = QMessageBox::question(
this, tr(
"Change Shortcut" ),
525 tr(
"This shortcut is already assigned to action %1. Reassign?" ).arg( otherText ),
526 QMessageBox::Yes | QMessageBox::No );
528 if ( res != QMessageBox::Yes )
533 QList<QTreeWidgetItem *> items = treeActions->findItems( otherText, Qt::MatchExactly );
534 if ( !items.isEmpty() )
535 items[0]->setText( 1, QString() );
542 treeActions->currentItem()->setText( 1, s.toString( QKeySequence::NativeText ) );
544 actionChanged( treeActions->currentItem(),
nullptr );
547void QgsConfigureShortcutsDialog::mLeFilter_textChanged(
const QString &text )
549 for (
int i = 0; i < treeActions->topLevelItemCount(); i++ )
551 QTreeWidgetItem *item = treeActions->topLevelItem( i );
552 if ( !item->text( 0 ).contains( text, Qt::CaseInsensitive ) && !item->text( 1 ).contains( text, Qt::CaseInsensitive ) )
554 item->setHidden(
true );
558 item->setHidden(
false );
563void QgsConfigureShortcutsDialog::showHelp()
565 QgsHelp::openHelp( QStringLiteral(
"introduction/qgis_configuration.html#shortcuts" ) );
568void QgsConfigureShortcutsDialog::saveShortcutsPdf()
570 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Shortcuts" ), QDir::homePath(),
571 tr(
"PDF file" ) +
" (*.pdf);;" + tr(
"All files" ) +
" (*)" );
576 if ( fileName.isEmpty() )
579 if ( !fileName.endsWith( QLatin1String(
".pdf" ), Qt::CaseInsensitive ) )
581 fileName += QLatin1String(
".pdf" );
584 QTextDocument *document =
new QTextDocument;
585 QTextCursor cursor( document );
587 QTextTableFormat tableFormat;
588 tableFormat.setBorder( 0 );
589 tableFormat.setCellSpacing( 0 );
590 tableFormat.setCellPadding( 4 );
591 tableFormat.setHeaderRowCount( 1 );
593 QVector<QTextLength> constraints;
594 constraints << QTextLength( QTextLength::PercentageLength, 5 );
595 constraints << QTextLength( QTextLength::PercentageLength, 80 );
596 constraints << QTextLength( QTextLength::PercentageLength, 15 );
597 tableFormat.setColumnWidthConstraints( constraints );
599 QTextTableCellFormat headerFormat;
600 headerFormat.setFontWeight( QFont::Bold );
601 headerFormat.setBottomPadding( 4 );
603 QTextCharFormat rowFormat;
604 rowFormat.setVerticalAlignment( QTextCharFormat::AlignMiddle );
606 QTextCharFormat altRowFormat;
607 altRowFormat.setBackground( QBrush( QColor( 238, 238, 236 ) ) );
608 altRowFormat.setVerticalAlignment( QTextCharFormat::AlignMiddle );
611 QTextTable *table = cursor.insertTable( 1, 3, tableFormat );
612 table->mergeCells( 0, 0, 1, 2 );
613 QTextCursor
c = table->cellAt( row, 0 ).firstCursorPosition();
614 c.setCharFormat( headerFormat );
615 c.insertText( tr(
"Action" ) );
616 c = table->cellAt( row, 2 ).firstCursorPosition();
617 c.setCharFormat( headerFormat );
618 c.insertText( tr(
"Shortcut" ) );
620 const QList<QObject *> objects = mManager->
listAll();
621 for ( QObject *obj : objects )
627 if ( QAction *action = qobject_cast< QAction * >( obj ) )
629 actionText = action->text().remove(
'&' );
630 sequence = action->shortcut().toString( QKeySequence::NativeText );
631 icon = action->icon();
633 else if ( QShortcut *shortcut = qobject_cast< QShortcut * >( obj ) )
635 actionText = shortcut->whatsThis();
636 sequence = shortcut->key().toString( QKeySequence::NativeText );
637 icon = shortcut->property(
"Icon" ).value<QIcon>();
645 if ( actionText.isEmpty() || sequence.isEmpty() )
651 table->appendRows( 1 );
655 table->cellAt( row, 0 ).setFormat( altRowFormat );
656 table->cellAt( row, 1 ).setFormat( altRowFormat );
657 table->cellAt( row, 2 ).setFormat( altRowFormat );
661 table->cellAt( row, 0 ).setFormat( rowFormat );
662 table->cellAt( row, 1 ).setFormat( rowFormat );
663 table->cellAt( row, 2 ).setFormat( rowFormat );
666 if ( !icon.isNull() )
668 c = table->cellAt( row, 0 ).firstCursorPosition();
669 c.insertImage( icon.pixmap( QSize( 24, 24 ) ).toImage() );
671 table->cellAt( row, 1 ).firstCursorPosition().insertText( actionText );
672 table->cellAt( row, 2 ).firstCursorPosition().insertText( sequence );
675 QPdfWriter pdfWriter( fileName );
676 pdfWriter.setPageLayout( QPageLayout( QPageSize( QPageSize::A4 ), QPageLayout::Portrait, QMarginsF( 20, 10, 10, 10 ) ) );
677 document->setPageSize( QSizeF( pdfWriter.pageLayout().fullRect( QPageLayout::Point ).size() ) );
678 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