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 );