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