31 #include <QFileDialog> 
   32 #include <QHeaderView> 
   33 #include <QMessageBox> 
   35 #include <QImageWriter> 
   36 #include <QTableWidget> 
   40   , mLayer( actions.layer() )
 
   43   QHeaderView *header = mAttributeActionTable->horizontalHeader();
 
   44   header->setHighlightSections( 
false );
 
   45   header->setStretchLastSection( 
true );
 
   46   mAttributeActionTable->setColumnWidth( 0, 100 );
 
   47   mAttributeActionTable->setColumnWidth( 1, 230 );
 
   48   mAttributeActionTable->setCornerButtonEnabled( 
false );
 
   49   mAttributeActionTable->setEditTriggers( QAbstractItemView::AnyKeyPressed | QAbstractItemView::SelectedClicked );
 
   51   connect( mAttributeActionTable, &QTableWidget::itemDoubleClicked, 
this, &QgsAttributeActionDialog::itemDoubleClicked );
 
   52   connect( mAttributeActionTable, &QTableWidget::itemSelectionChanged, 
this, &QgsAttributeActionDialog::updateButtons );
 
   53   connect( mMoveUpButton, &QAbstractButton::clicked, 
this, &QgsAttributeActionDialog::moveUp );
 
   54   connect( mMoveDownButton, &QAbstractButton::clicked, 
this, &QgsAttributeActionDialog::moveDown );
 
   55   connect( mRemoveButton, &QAbstractButton::clicked, 
this, &QgsAttributeActionDialog::remove );
 
   56   connect( mAddButton, &QAbstractButton::clicked, 
this, &QgsAttributeActionDialog::insert );
 
   57   connect( mAddDefaultActionsButton, &QAbstractButton::clicked, 
this, &QgsAttributeActionDialog::addDefaultActions );
 
   65   mAttributeActionTable->setRowCount( 0 );
 
   69   const auto constActions = 
actions.actions();
 
   70   for ( 
const QgsAction &action : constActions )
 
   72     insertRow( i++, action );
 
   79   visibleActionWidgetConfig.
hidden = 
false;
 
   82   mAttributeTableWidgetType->setCurrentIndex( attributeTableConfig.
actionWidgetStyle() );
 
   89   for ( 
int i = 0; i < mAttributeActionTable->rowCount(); ++i )
 
   91     actions.append( rowToAction( i ) );
 
   99   return mShowInAttributeTable->isChecked();
 
  107 void QgsAttributeActionDialog::insertRow( 
int row, 
const QgsAction &action )
 
  109   QTableWidgetItem *item = 
nullptr;
 
  110   mAttributeActionTable->insertRow( row );
 
  113   item = 
new QTableWidgetItem( textForType( action.
type() ) );
 
  114   item->setData( Role::ActionType, action.
type() );
 
  115   item->setData( Role::ActionId, action.
id() );
 
  116   item->setFlags( item->flags() & ~Qt::ItemIsEditable );
 
  117   mAttributeActionTable->setItem( row, Type, item );
 
  120   mAttributeActionTable->setItem( row, Description, 
new QTableWidgetItem( action.
name() ) );
 
  123   mAttributeActionTable->setItem( row, ShortTitle, 
new QTableWidgetItem( action.
shortTitle() ) );
 
  126   item = 
new QTableWidgetItem( action.
command().length() > 30 ? action.
command().left( 27 ) + 
"…" : action.
command() );
 
  127   item->setData( Qt::UserRole, action.
command() );
 
  128   mAttributeActionTable->setItem( row, ActionText, item );
 
  131   item = 
new QTableWidgetItem();
 
  132   item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
 
  133   item->setCheckState( action.
capture() ? Qt::Checked : Qt::Unchecked );
 
  134   mAttributeActionTable->setItem( row, Capture, item );
 
  137   item = 
new QTableWidgetItem();
 
  138   item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
 
  139   QStringList actionScopes = qgis::setToList( action.
actionScopes() );
 
  140   std::sort( actionScopes.begin(), actionScopes.end() );
 
  141   item->setText( actionScopes.join( QLatin1String( 
", " ) ) );
 
  142   item->setData( Qt::UserRole, QVariant::fromValue<QSet<QString>>( action.
actionScopes() ) );
 
  143   mAttributeActionTable->setItem( row, ActionScopes, item );
 
  146   const QIcon icon = action.
icon();
 
  147   QTableWidgetItem *headerItem = 
new QTableWidgetItem( icon, QString() );
 
  148   headerItem->setData( Qt::UserRole, action.
iconPath() );
 
  149   mAttributeActionTable->setVerticalHeaderItem( row, headerItem );
 
  152   mAttributeActionTable->setItem( row, NotificationMessage, 
new QTableWidgetItem( action.
notificationMessage() ) );
 
  155   item = 
new QTableWidgetItem();
 
  156   item->setFlags( item->flags() & ~( Qt::ItemIsEditable ) );
 
  158   mAttributeActionTable->setItem( row, EnabledOnlyWhenEditable, item );
 
  163 void QgsAttributeActionDialog::insertRow( 
int row, 
QgsAction::ActionType type, 
const QString &name, 
const QString &actionText, 
const QString &iconPath, 
bool capture, 
const QString &shortTitle, 
const QSet<QString> &actionScopes, 
const QString ¬ificationMessage, 
bool isEnabledOnlyWhenEditable )
 
  165   if ( uniqueName( name ) == name )
 
  166     insertRow( row, 
QgsAction( type, name, actionText, iconPath, capture, shortTitle, actionScopes, notificationMessage, isEnabledOnlyWhenEditable ) );
 
  169 void QgsAttributeActionDialog::moveUp()
 
  173   int row1 = -1, row2 = -1;
 
  174   QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
 
  175   if ( !selection.isEmpty() )
 
  177     row1 = selection.first()->row();
 
  183   if ( row1 != -1 && row2 != -1 )
 
  185     swapRows( row1, row2 );
 
  187     mAttributeActionTable->selectRow( row2 );
 
  191 void QgsAttributeActionDialog::moveDown()
 
  194   int row1 = -1, row2 = -1;
 
  195   QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
 
  196   if ( !selection.isEmpty() )
 
  198     row1 = selection.first()->row();
 
  201   if ( row1 < mAttributeActionTable->rowCount() - 1 )
 
  204   if ( row1 != -1 && row2 != -1 )
 
  206     swapRows( row1, row2 );
 
  208     mAttributeActionTable->selectRow( row2 );
 
  212 void QgsAttributeActionDialog::swapRows( 
int row1, 
int row2 )
 
  214   const int colCount = mAttributeActionTable->columnCount();
 
  215   for ( 
int col = 0; col < colCount; col++ )
 
  217     QTableWidgetItem *item = mAttributeActionTable->takeItem( row1, col );
 
  218     mAttributeActionTable->setItem( row1, col, mAttributeActionTable->takeItem( row2, col ) );
 
  219     mAttributeActionTable->setItem( row2, col, item );
 
  221   QTableWidgetItem *header = mAttributeActionTable->takeVerticalHeaderItem( row1 );
 
  222   mAttributeActionTable->setVerticalHeaderItem( row1, mAttributeActionTable->takeVerticalHeaderItem( row2 ) );
 
  223   mAttributeActionTable->setVerticalHeaderItem( row2, header );
 
  226 QgsAction QgsAttributeActionDialog::rowToAction( 
int row )
 const 
  228   const QUuid 
id { mAttributeActionTable->item( row, Type )->data( Role::ActionId ).toUuid() };
 
  230                     static_cast<QgsAction::ActionType>( mAttributeActionTable->item( row, Type )->data( Role::ActionType ).toInt() ),
 
  231                     mAttributeActionTable->item( row, Description )->text(),
 
  232                     mAttributeActionTable->item( row, ActionText )->data( Qt::UserRole ).toString(),
 
  233                     mAttributeActionTable->verticalHeaderItem( row )->data( Qt::UserRole ).toString(),
 
  234                     mAttributeActionTable->item( row, Capture )->checkState() == Qt::Checked,
 
  235                     mAttributeActionTable->item( row, ShortTitle )->text(),
 
  236                     mAttributeActionTable->item( row, ActionScopes )->data( Qt::UserRole ).value<QSet<QString>>(),
 
  237                     mAttributeActionTable->item( row, NotificationMessage )->text(),
 
  238                     mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->checkState() == Qt::Checked
 
  248       return tr( 
"Generic" );
 
  250       return tr( 
"Python" );
 
  254       return tr( 
"Windows" );
 
  258       return tr( 
"Open URL" );
 
  260       return tr( 
"Submit URL (urlencoded or JSON)" );
 
  262       return tr( 
"Submit URL (multipart)" );
 
  267 void QgsAttributeActionDialog::remove()
 
  269   QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
 
  270   if ( !selection.isEmpty() )
 
  273     int row = selection.first()->row();
 
  274     mAttributeActionTable->removeRow( row );
 
  277     if ( row >= mAttributeActionTable->rowCount() )
 
  278       row = mAttributeActionTable->rowCount() - 1;
 
  279     mAttributeActionTable->selectRow( row );
 
  285 void QgsAttributeActionDialog::insert()
 
  288   const int pos = mAttributeActionTable->rowCount();
 
  291   dlg.setWindowTitle( tr( 
"Add New Action" ) );
 
  295     const QString name = uniqueName( dlg.description() );
 
  297     insertRow( pos, dlg.type(), name, dlg.actionText(), dlg.iconPath(), dlg.capture(), dlg.shortTitle(), dlg.actionScopes(), dlg.notificationMessage(), dlg.isEnabledOnlyWhenEditable() );
 
  301 void QgsAttributeActionDialog::updateButtons()
 
  303   QList<QTableWidgetItem *> selection = mAttributeActionTable->selectedItems();
 
  304   const bool hasSelection = !selection.isEmpty();
 
  308     const int row = selection.first()->row();
 
  309     mMoveUpButton->setEnabled( row >= 1 );
 
  310     mMoveDownButton->setEnabled( row >= 0 && row < mAttributeActionTable->rowCount() - 1 );
 
  314     mMoveUpButton->setEnabled( 
false );
 
  315     mMoveDownButton->setEnabled( 
false );
 
  318   mRemoveButton->setEnabled( hasSelection );
 
  321 void QgsAttributeActionDialog::addDefaultActions()
 
  324   insertRow( pos++, 
QgsAction::Generic, tr( 
"Echo attribute's value" ), QStringLiteral( 
"echo \"[% \"MY_FIELD\" %]\"" ), QString(), 
true, tr( 
"Attribute Value" ), QSet<QString>() << QStringLiteral( 
"Field" ), QString() );
 
  325   insertRow( pos++, 
QgsAction::Generic, tr( 
"Run an application" ), QStringLiteral( 
"ogr2ogr -f \"GPKG\" \"[% \"OUTPUT_PATH\" %]\" \"[% \"INPUT_FILE\" %]\"" ), QString(), 
true, tr( 
"Run application" ), QSet<QString>() << QStringLiteral( 
"Feature" ) << QStringLiteral( 
"Canvas" ), QString() );
 
  326   insertRow( pos++, 
QgsAction::GenericPython, tr( 
"Get feature id" ), QStringLiteral( 
"from qgis.PyQt import QtWidgets\n\nQtWidgets.QMessageBox.information(None, \"Feature id\", \"feature id is [% $id %]\")" ), QString(), 
false, tr( 
"Feature ID" ), QSet<QString>() << QStringLiteral( 
"Feature" ) << QStringLiteral( 
"Canvas" ), QString() );
 
  327   insertRow( pos++, 
QgsAction::GenericPython, tr( 
"Selected field's value (Identify features tool)" ), QStringLiteral( 
"from qgis.PyQt import QtWidgets\n\nQtWidgets.QMessageBox.information(None, \"Current field's value\", \"[% @field_value %]\")" ), QString(), 
false, tr( 
"Field Value" ), QSet<QString>() << QStringLiteral( 
"Field" ), QString() );
 
  328   insertRow( pos++, 
QgsAction::GenericPython, tr( 
"Clicked coordinates (Run feature actions tool)" ), QStringLiteral( 
"from qgis.PyQt import QtWidgets\n\nQtWidgets.QMessageBox.information(None, \"Clicked coords\", \"layer: [% @layer_id %]\\ncoords: ([% @click_x %],[% @click_y %])\")" ), QString(), 
false, tr( 
"Clicked Coordinate" ), QSet<QString>() << QStringLiteral( 
"Canvas" ), QString() );
 
  329   insertRow( pos++, 
QgsAction::OpenUrl, tr( 
"Open file" ), QStringLiteral( 
"[% \"PATH\" %]" ), QString(), 
false, tr( 
"Open file" ), QSet<QString>() << QStringLiteral( 
"Feature" ) << QStringLiteral( 
"Canvas" ), QString() );
 
  330   insertRow( pos++, 
QgsAction::OpenUrl, tr( 
"Search on web based on attribute's value" ), QStringLiteral( 
"http://www.google.com/search?q=[% \"ATTRIBUTE\" %]" ), QString(), 
false, tr( 
"Search Web" ), QSet<QString>() << QStringLiteral( 
"Field" ), QString() );
 
  331   insertRow( pos++, 
QgsAction::GenericPython, tr( 
"List feature ids" ), QStringLiteral( 
"from qgis.PyQt import QtWidgets\n\nlayer = QgsProject.instance().mapLayer('[% @layer_id %]')\nif layer.selectedFeatureCount():\n    ids = layer.selectedFeatureIds()\nelse:\n    ids = [f.id() for f in layer.getFeatures()]\n\nQtWidgets.QMessageBox.information(None, \"Feature ids\", ', '.join([str(id) for id in ids]))" ), QString(), 
false, tr( 
"List feature ids" ), QSet<QString>() << QStringLiteral( 
"Layer" ), QString() );
 
  332   insertRow( pos++, 
QgsAction::GenericPython, tr( 
"Duplicate selected features" ), QStringLiteral( 
"project = QgsProject.instance()\nlayer = QgsProject.instance().mapLayer('[% @layer_id %]')\nif not layer.isEditable():\n    qgis.utils.iface.messageBar().pushMessage( 'Cannot duplicate feature in not editable mode on layer {layer}'.format( layer=layer.name() ) )\nelse:\n    features=[]\n    if len('[% $id %]')>0:\n        features.append( layer.getFeature( [% $id %] ) )\n    else:\n        for x in layer.selectedFeatures():\n            features.append( x )\n    feature_count=0\n    children_info=''\n    featureids=[]\n    for f in features:\n        result=QgsVectorLayerUtils.duplicateFeature(layer, f, project, 0 )\n        featureids.append( result[0].id() )\n        feature_count+=1\n        for ch_layer in result[1].layers():\n            children_info+='{number_of_children} children on layer {children_layer}\\n'.format( number_of_children=str( len( result[1].duplicatedFeatures(ch_layer) ) ), children_layer=ch_layer.name() )\n            ch_layer.selectByIds( result[1].duplicatedFeatures(ch_layer) )\n    layer.selectByIds( featureids )\n    qgis.utils.iface.messageBar().pushMessage( '{number_of_features} features on layer {layer} duplicated with\\n{children_info}'.format( number_of_features=str( feature_count ), layer=layer.name(), children_info=children_info ) )" ), QString(), 
false, tr( 
"Duplicate selected" ), QSet<QString>() << QStringLiteral( 
"Layer" ), QString(), 
true );
 
  336 void QgsAttributeActionDialog::itemDoubleClicked( QTableWidgetItem *item )
 
  338   const int row = item->row();
 
  341     static_cast<QgsAction::ActionType>( mAttributeActionTable->item( row, Type )->data( Role::ActionType ).toInt() ),
 
  342     mAttributeActionTable->item( row, Description )->text(),
 
  343     mAttributeActionTable->item( row, ShortTitle )->text(),
 
  344     mAttributeActionTable->verticalHeaderItem( row )->data( Qt::UserRole ).toString(),
 
  345     mAttributeActionTable->item( row, ActionText )->data( Qt::UserRole ).toString(),
 
  346     mAttributeActionTable->item( row, Capture )->checkState() == Qt::Checked,
 
  347     mAttributeActionTable->item( row, ActionScopes )->data( Qt::UserRole ).value<QSet<QString>>(),
 
  348     mAttributeActionTable->item( row, NotificationMessage )->text(),
 
  349     mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->checkState() == Qt::Checked,
 
  353   actionProperties.setWindowTitle( tr( 
"Edit Action" ) );
 
  355   if ( actionProperties.exec() )
 
  357     mAttributeActionTable->item( row, Type )->setData( Role::ActionType, actionProperties.type() );
 
  358     mAttributeActionTable->item( row, Type )->setText( textForType( actionProperties.type() ) );
 
  359     mAttributeActionTable->item( row, Description )->setText( actionProperties.description() );
 
  360     mAttributeActionTable->item( row, ShortTitle )->setText( actionProperties.shortTitle() );
 
  361     mAttributeActionTable->item( row, ActionText )->setText( actionProperties.actionText().length() > 30 ? actionProperties.actionText().left( 27 ) + 
"…" : actionProperties.actionText() );
 
  362     mAttributeActionTable->item( row, ActionText )->setData( Qt::UserRole, actionProperties.actionText() );
 
  363     mAttributeActionTable->item( row, Capture )->setCheckState( actionProperties.capture() ? Qt::Checked : Qt::Unchecked );
 
  364     mAttributeActionTable->item( row, NotificationMessage )->setText( actionProperties.notificationMessage() );
 
  365     mAttributeActionTable->item( row, EnabledOnlyWhenEditable )->setCheckState( actionProperties.isEnabledOnlyWhenEditable() ? Qt::Checked : Qt::Unchecked );
 
  367     QTableWidgetItem *item = mAttributeActionTable->item( row, ActionScopes );
 
  368     QStringList actionScopes = qgis::setToList( actionProperties.actionScopes() );
 
  369     std::sort( actionScopes.begin(), actionScopes.end() );
 
  370     item->setText( actionScopes.join( QLatin1String( 
", " ) ) );
 
  371     item->setData( Qt::UserRole, QVariant::fromValue<QSet<QString>>( actionProperties.actionScopes() ) );
 
  373     mAttributeActionTable->verticalHeaderItem( row )->setData( Qt::UserRole, actionProperties.iconPath() );
 
  374     mAttributeActionTable->verticalHeaderItem( row )->setIcon( QIcon( actionProperties.iconPath() ) );
 
  378 QString QgsAttributeActionDialog::uniqueName( QString name )
 
  383   const int pos = mAttributeActionTable->rowCount();
 
  386   for ( 
int i = 0; i < pos; ++i )
 
  388     if ( mAttributeActionTable->item( i, Description )->text() == name )
 
  398       const QString suffix = QString::number( suffix_num );
 
  399       new_name = name + 
'_' + suffix;
 
  401       for ( 
int i = 0; i < pos; ++i )
 
  402         if ( mAttributeActionTable->item( i, 0 )->text() == new_name )