25 #include <QKeySequence>
26 #include <QMessageBox>
28 #include <QDomDocument>
29 #include <QFileDialog>
30 #include <QTextStream>
34 #include <QTextDocument>
35 #include <QTextCursor>
37 #include <QTextTableFormat>
38 #include <QTextTableCellFormat>
39 #include <QTextCharFormat>
48 mSaveMenu =
new QMenu(
this );
49 mSaveUserShortcuts =
new QAction( tr(
"Save User Shortcuts…" ),
this );
50 mSaveMenu->addAction( mSaveUserShortcuts );
51 connect( mSaveUserShortcuts, &QAction::triggered,
this, [
this] { saveShortcuts(
false ); } );
53 mSaveAllShortcuts =
new QAction( tr(
"Save All Shortcuts…" ),
this );
54 mSaveMenu->addAction( mSaveAllShortcuts );
55 connect( mSaveAllShortcuts, &QAction::triggered,
this, [
this] { saveShortcuts(); } );
57 mSaveAsPdf =
new QAction( tr(
"Save as PDF…" ),
this );
58 mSaveMenu->addAction( mSaveAsPdf );
59 connect( mSaveAsPdf, &QAction::triggered,
this, &QgsConfigureShortcutsDialog::saveShortcutsPdf );
61 btnSaveShortcuts->setMenu( mSaveMenu );
63 connect( mLeFilter, &QgsFilterLineEdit::textChanged,
this, &QgsConfigureShortcutsDialog::mLeFilter_textChanged );
68 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsConfigureShortcutsDialog::showHelp );
69 connect( btnChangeShortcut, &QAbstractButton::clicked,
this, &QgsConfigureShortcutsDialog::changeShortcut );
70 connect( btnResetShortcut, &QAbstractButton::clicked,
this, &QgsConfigureShortcutsDialog::resetShortcut );
71 connect( btnSetNoShortcut, &QAbstractButton::clicked,
this, &QgsConfigureShortcutsDialog::setNoShortcut );
72 connect( btnLoadShortcuts, &QAbstractButton::clicked,
this, &QgsConfigureShortcutsDialog::loadShortcuts );
74 connect( treeActions, &QTreeWidget::currentItemChanged,
75 this, &QgsConfigureShortcutsDialog::actionChanged );
80 void QgsConfigureShortcutsDialog::populateActions()
82 const QList<QObject *> objects = mManager->
listAll();
84 QList<QTreeWidgetItem *> items;
85 items.reserve( objects.count() );
86 const auto constObjects = objects;
87 for ( QObject *obj : constObjects )
93 if ( QAction *action = qobject_cast< QAction * >( obj ) )
95 actionText = action->text();
96 actionText.remove(
'&' );
97 sequence = action->shortcut().toString( QKeySequence::NativeText );
98 icon = action->icon();
100 else if ( QShortcut *shortcut = qobject_cast< QShortcut * >( obj ) )
102 actionText = shortcut->whatsThis();
103 sequence = shortcut->key().toString( QKeySequence::NativeText );
104 icon = shortcut->property(
"Icon" ).value<QIcon>();
111 if ( actionText.isEmpty() )
117 lst << actionText << sequence;
118 QTreeWidgetItem *item =
new QTreeWidgetItem( lst );
119 item->setIcon( 0, icon );
120 item->setData( 0, Qt::UserRole, QVariant::fromValue( obj ) );
121 items.append( item );
124 treeActions->addTopLevelItems( items );
127 treeActions->resizeColumnToContents( 0 );
128 treeActions->sortItems( 0, Qt::AscendingOrder );
130 actionChanged( treeActions->currentItem(),
nullptr );
133 void QgsConfigureShortcutsDialog::saveShortcuts(
bool saveAll )
135 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Shortcuts" ), QDir::homePath(),
136 tr(
"XML file" ) +
" (*.xml);;" + tr(
"All files" ) +
" (*)" );
138 if ( fileName.isEmpty() )
142 if ( !fileName.endsWith( QLatin1String(
".xml" ), Qt::CaseInsensitive ) )
144 fileName += QLatin1String(
".xml" );
147 QFile file( fileName );
148 if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
150 QMessageBox::warning(
this, tr(
"Saving Shortcuts" ),
151 tr(
"Cannot write file %1:\n%2." )
153 file.errorString() ) );
159 QDomDocument doc( QStringLiteral(
"shortcuts" ) );
160 QDomElement root = doc.createElement( QStringLiteral(
"qgsshortcuts" ) );
161 root.setAttribute( QStringLiteral(
"version" ), QStringLiteral(
"1.0" ) );
163 doc.appendChild( root );
165 const QList<QObject *> objects = mManager->
listAll();
166 for ( QObject *obj : objects )
169 QString actionShortcut;
170 QKeySequence sequence;
172 if ( QAction *action = qobject_cast< QAction * >( obj ) )
174 actionText = action->text().remove(
'&' );
175 actionShortcut = action->shortcut().toString( QKeySequence::NativeText );
178 else if ( QShortcut *shortcut = qobject_cast< QShortcut * >( obj ) )
180 actionText = shortcut->whatsThis();
181 actionShortcut = shortcut->key().toString( QKeySequence::NativeText );
189 if ( actionText.isEmpty() || actionShortcut.isEmpty() )
195 if ( !saveAll && sequence == QKeySequence( actionShortcut ) )
200 QDomElement el = doc.createElement( QStringLiteral(
"action" ) );
201 el.setAttribute( QStringLiteral(
"name" ), actionText );
202 el.setAttribute( QStringLiteral(
"shortcut" ), actionShortcut );
203 root.appendChild( el );
206 QTextStream out( &file );
210 void QgsConfigureShortcutsDialog::loadShortcuts()
212 const QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Shortcuts" ), QDir::homePath(),
213 tr(
"XML file" ) +
" (*.xml);;" + tr(
"All files" ) +
" (*)" );
215 if ( fileName.isEmpty() )
220 QFile file( fileName );
221 if ( !file.open( QIODevice::ReadOnly | QIODevice::Text ) )
223 QMessageBox::warning(
this, tr(
"Loading Shortcuts" ),
224 tr(
"Cannot read file %1:\n%2." )
226 file.errorString() ) );
235 if ( !doc.setContent( &file,
true, &errorStr, &errorLine, &errorColumn ) )
237 QMessageBox::information(
this, tr(
"Loading Shortcuts" ),
238 tr(
"Parse error at line %1, column %2:\n%3" )
245 const QDomElement root = doc.documentElement();
246 if ( root.tagName() != QLatin1String(
"qgsshortcuts" ) )
248 QMessageBox::information(
this, tr(
"Loading Shortcuts" ),
249 tr(
"The file is not an shortcuts exchange file." ) );
253 QString currentLocale;
256 if ( localeOverrideFlag )
262 currentLocale = QLocale().name();
265 if ( root.attribute( QStringLiteral(
"locale" ) ) != currentLocale )
267 QMessageBox::information(
this, tr(
"Loading Shortcuts" ),
268 tr(
"The file contains shortcuts created with different locale, so you can't use it." ) );
273 QString actionShortcut;
275 QDomElement child = root.firstChildElement();
276 while ( !child.isNull() )
278 actionName = child.attribute( QStringLiteral(
"name" ) );
279 actionShortcut = child.attribute( QStringLiteral(
"shortcut" ) );
282 child = child.nextSiblingElement();
285 treeActions->clear();
289 void QgsConfigureShortcutsDialog::changeShortcut()
292 setGettingShortcut(
true );
295 void QgsConfigureShortcutsDialog::resetShortcut()
297 QObject *
object = currentObject();
299 setCurrentActionShortcut( sequence );
302 void QgsConfigureShortcutsDialog::setNoShortcut()
304 setCurrentActionShortcut( QKeySequence() );
307 QAction *QgsConfigureShortcutsDialog::currentAction()
309 return qobject_cast<QAction *>( currentObject() );
312 QShortcut *QgsConfigureShortcutsDialog::currentShortcut()
314 return qobject_cast<QShortcut *>( currentObject() );
317 void QgsConfigureShortcutsDialog::actionChanged( QTreeWidgetItem *current, QTreeWidgetItem *previous )
322 setGettingShortcut(
false );
325 QKeySequence sequence;
326 if ( QAction *action = currentAction() )
330 sequence = action->shortcut();
332 else if ( QShortcut *
object = currentShortcut() )
336 sequence =
object->key();
343 if ( shortcut.isEmpty() )
344 shortcut = tr(
"None" );
345 btnResetShortcut->setText( tr(
"Set default (%1)" ).arg( shortcut ) );
348 btnSetNoShortcut->setEnabled( !sequence.isEmpty() );
350 btnResetShortcut->setEnabled( sequence != QKeySequence( shortcut ) );
355 if ( !mGettingShortcut )
357 QDialog::keyPressEvent( event );
361 const int key =
event->key();
366 mModifiers |= Qt::META;
367 updateShortcutText();
370 mModifiers |= Qt::ALT;
371 updateShortcutText();
373 case Qt::Key_Control:
374 mModifiers |= Qt::CTRL;
375 updateShortcutText();
378 mModifiers |= Qt::SHIFT;
379 updateShortcutText();
384 setGettingShortcut(
false );
389 updateShortcutText();
395 if ( !mGettingShortcut )
397 QDialog::keyReleaseEvent( event );
401 const int key =
event->key();
406 mModifiers &= ~Qt::META;
407 updateShortcutText();
410 mModifiers &= ~Qt::ALT;
411 updateShortcutText();
413 case Qt::Key_Control:
414 mModifiers &= ~Qt::CTRL;
415 updateShortcutText();
418 mModifiers &= ~Qt::SHIFT;
419 updateShortcutText();
428 setCurrentActionShortcut( QKeySequence( mModifiers + mKey ) );
429 setGettingShortcut(
false );
434 QObject *QgsConfigureShortcutsDialog::currentObject()
436 if ( !treeActions->currentItem() )
439 QObject *
object = treeActions->currentItem()->data( 0, Qt::UserRole ).value<QObject *>();
443 void QgsConfigureShortcutsDialog::updateShortcutText()
446 const QKeySequence s( mModifiers + mKey );
447 btnChangeShortcut->setText( tr(
"Input: " ) + s.toString( QKeySequence::NativeText ) );
450 void QgsConfigureShortcutsDialog::setGettingShortcut(
bool getting )
454 mGettingShortcut = getting;
457 btnChangeShortcut->setChecked(
false );
458 btnChangeShortcut->setText( tr(
"Change" ) );
462 updateShortcutText();
466 void QgsConfigureShortcutsDialog::setCurrentActionShortcut(
const QKeySequence &s )
468 QObject *
object = currentObject();
474 if ( otherObject ==
object )
480 if ( QAction *otherAction = qobject_cast< QAction * >( otherObject ) )
482 otherText = otherAction->text();
483 otherText.remove(
'&' );
485 else if ( QShortcut *otherShortcut = qobject_cast< QShortcut * >( otherObject ) )
487 otherText = otherShortcut->whatsThis();
490 const int res = QMessageBox::question(
this, tr(
"Change Shortcut" ),
491 tr(
"This shortcut is already assigned to action %1. Reassign?" ).arg( otherText ),
492 QMessageBox::Yes | QMessageBox::No );
494 if ( res != QMessageBox::Yes )
499 QList<QTreeWidgetItem *> items = treeActions->findItems( otherText, Qt::MatchExactly );
500 if ( !items.isEmpty() )
501 items[0]->setText( 1, QString() );
508 treeActions->currentItem()->setText( 1, s.toString( QKeySequence::NativeText ) );
510 actionChanged( treeActions->currentItem(),
nullptr );
513 void QgsConfigureShortcutsDialog::mLeFilter_textChanged(
const QString &text )
515 for (
int i = 0; i < treeActions->topLevelItemCount(); i++ )
517 QTreeWidgetItem *item = treeActions->topLevelItem( i );
518 if ( !item->text( 0 ).contains( text, Qt::CaseInsensitive ) && !item->text( 1 ).contains( text, Qt::CaseInsensitive ) )
520 item->setHidden(
true );
524 item->setHidden(
false );
529 void QgsConfigureShortcutsDialog::showHelp()
531 QgsHelp::openHelp( QStringLiteral(
"introduction/qgis_configuration.html#keyboard-shortcuts" ) );
534 void QgsConfigureShortcutsDialog::saveShortcutsPdf()
536 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Shortcuts" ), QDir::homePath(),
537 tr(
"PDF file" ) +
" (*.pdf);;" + tr(
"All files" ) +
" (*)" );
539 if ( fileName.isEmpty() )
542 if ( !fileName.endsWith( QLatin1String(
".pdf" ), Qt::CaseInsensitive ) )
544 fileName += QLatin1String(
".pdf" );
547 QTextDocument *document =
new QTextDocument;
548 QTextCursor cursor( document );
550 QTextTableFormat tableFormat;
551 tableFormat.setBorder( 0 );
552 tableFormat.setCellSpacing( 0 );
553 tableFormat.setCellPadding( 4 );
554 tableFormat.setHeaderRowCount( 1 );
556 QVector<QTextLength> constraints;
557 constraints << QTextLength( QTextLength::PercentageLength, 5 );
558 constraints << QTextLength( QTextLength::PercentageLength, 80 );
559 constraints << QTextLength( QTextLength::PercentageLength, 15 );
560 tableFormat.setColumnWidthConstraints( constraints );
562 QTextTableCellFormat headerFormat;
563 headerFormat.setFontWeight( QFont::Bold );
564 headerFormat.setBottomPadding( 4 );
566 QTextCharFormat rowFormat;
567 rowFormat.setVerticalAlignment( QTextCharFormat::AlignMiddle );
569 QTextCharFormat altRowFormat;
570 altRowFormat.setBackground( QBrush( QColor( 238, 238, 236 ) ) );
571 altRowFormat.setVerticalAlignment( QTextCharFormat::AlignMiddle );
574 QTextTable *table = cursor.insertTable( 1, 3, tableFormat );
575 table->mergeCells( 0, 0, 1, 2 );
576 QTextCursor
c = table->cellAt( row, 0 ).firstCursorPosition();
577 c.setCharFormat( headerFormat );
578 c.insertText( tr(
"Action" ) );
579 c = table->cellAt( row, 2 ).firstCursorPosition();
580 c.setCharFormat( headerFormat );
581 c.insertText( tr(
"Shortcut" ) );
583 const QList<QObject *> objects = mManager->
listAll();
584 for ( QObject *obj : objects )
590 if ( QAction *action = qobject_cast< QAction * >( obj ) )
592 actionText = action->text().remove(
'&' );
593 sequence = action->shortcut().toString( QKeySequence::NativeText );
594 icon = action->icon();
596 else if ( QShortcut *shortcut = qobject_cast< QShortcut * >( obj ) )
598 actionText = shortcut->whatsThis();
599 sequence = shortcut->key().toString( QKeySequence::NativeText );
600 icon = shortcut->property(
"Icon" ).value<QIcon>();
608 if ( actionText.isEmpty() || sequence.isEmpty() )
614 table->appendRows( 1 );
618 table->cellAt( row, 0 ).setFormat( altRowFormat );
619 table->cellAt( row, 1 ).setFormat( altRowFormat );
620 table->cellAt( row, 2 ).setFormat( altRowFormat );
624 table->cellAt( row, 0 ).setFormat( rowFormat );
625 table->cellAt( row, 1 ).setFormat( rowFormat );
626 table->cellAt( row, 2 ).setFormat( rowFormat );
629 if ( !icon.isNull() )
631 c = table->cellAt( row, 0 ).firstCursorPosition();
632 c.insertImage( icon.pixmap( QSize( 24, 24 ) ).toImage() );
634 table->cellAt( row, 1 ).firstCursorPosition().insertText( actionText );
635 table->cellAt( row, 2 ).firstCursorPosition().insertText( sequence );
638 QPrinter printer( QPrinter::ScreenResolution );
639 printer.setOutputFormat( QPrinter::PdfFormat );
640 printer.setPaperSize( QPrinter::A4 );
641 printer.setPageOrientation( QPageLayout::Portrait );
642 printer.setPageMargins( QMarginsF( 20, 10, 10, 10 ), QPageLayout::Millimeter );
643 printer.setOutputFileName( fileName );
644 document->setPageSize( QSizeF( printer.pageRect().size() ) );
645 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.
QString key(const QString &dynamicKeyPart=QString()) const
Returns settings entry key.
bool value(const QString &dynamicKeyPart=QString(), bool useDefaultValueOverride=false, bool defaultValueOverride=false) const
Returns settings value.
QString value(const QString &dynamicKeyPart=QString(), bool useDefaultValueOverride=false, const QString &defaultValueOverride=QString()) const
Returns settings value.
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.
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.
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