diff --git a/src/app/qgsattributeactiondialog.cpp b/src/app/qgsattributeactiondialog.cpp index 54e68a8a45b..2a7ac1897c4 100644 --- a/src/app/qgsattributeactiondialog.cpp +++ b/src/app/qgsattributeactiondialog.cpp @@ -22,11 +22,12 @@ back to QgsVectorLayer. #include "qgsattributeactiondialog.h" #include "qgsattributeaction.h" +#include "qgsexpressionbuilderdialog.h" #include #include #include - +#include QgsAttributeActionDialog::QgsAttributeActionDialog( QgsAttributeAction* actions, const QgsFieldMap& fields, @@ -50,6 +51,7 @@ QgsAttributeActionDialog::QgsAttributeActionDialog( QgsAttributeAction* actions, connect( insertButton, SIGNAL( clicked() ), this, SLOT( insert() ) ); connect( updateButton, SIGNAL( clicked() ), this, SLOT( update() ) ); connect( insertFieldButton, SIGNAL( clicked() ), this, SLOT( insertField() ) ); + connect( insertExpressionButton, SIGNAL( clicked() ), this, SLOT( insertExpression() ) ); init(); // Populate the combo box with the field names. Will the field names @@ -143,9 +145,7 @@ void QgsAttributeActionDialog::swapRows( int row1, int row2 ) void QgsAttributeActionDialog::browse() { - // Popup a file browser and place the results into the actionName - // widget - + // Popup a file browser and place the results into the action widget QString action = QFileDialog::getOpenFileName( this, tr( "Select an action", "File dialog window title" ) ); @@ -153,6 +153,28 @@ void QgsAttributeActionDialog::browse() actionAction->insert( action ); } +void QgsAttributeActionDialog::insertExpression() +{ + QString selText = actionAction->selectedText(); + + // edit the selected expression if there's one + if ( selText.startsWith( "[%" ) && selText.endsWith( "%]" ) ) + selText = selText.mid( 2, selText.size() - 3 ); + + // display the expression builder + QgsExpressionBuilderDialog dlg( mActions->layer(), selText, this ); + dlg.setWindowTitle( tr( "Insert expression" ) ); + if ( dlg.exec() == QDialog::Accepted ) + { + QString expression = dlg.expressionBuilder()->getExpressionString(); + //Only add the expression if the user has entered some text. + if ( !expression.isEmpty() ) + { + actionAction->insert( "[%" + expression + "%]" ); + } + } +} + void QgsAttributeActionDialog::remove() { QList selection = attributeActionTable->selectedItems(); @@ -234,13 +256,14 @@ void QgsAttributeActionDialog::update() void QgsAttributeActionDialog::insertField() { - // Take the selected field, preprend a % and insert into the action - // field at the cursor position + // Convert the selected field to an expression and + // insert it into the action at the cursor position if ( !fieldComboBox->currentText().isNull() ) { - QString field( "%" ); + QString field = "[% \""; field += fieldComboBox->currentText(); + field += "\" %]"; actionAction->insert( field ); } } diff --git a/src/app/qgsattributeactiondialog.h b/src/app/qgsattributeactiondialog.h index 33475f2aa0c..f1d43088f20 100644 --- a/src/app/qgsattributeactiondialog.h +++ b/src/app/qgsattributeactiondialog.h @@ -50,6 +50,7 @@ class QgsAttributeActionDialog: public QWidget, private Ui::QgsAttributeActionDi void remove(); void insert(); void insertField(); + void insertExpression(); void apply(); void update(); void itemSelectionChanged(); diff --git a/src/app/qgsfeatureaction.cpp b/src/app/qgsfeatureaction.cpp index f631ca8bfcb..5c891473a5e 100644 --- a/src/app/qgsfeatureaction.cpp +++ b/src/app/qgsfeatureaction.cpp @@ -36,7 +36,7 @@ QgsFeatureAction::QgsFeatureAction( const QString &name, QgsFeature &f, QgsVecto void QgsFeatureAction::execute() { - mLayer->actions()->doAction( mAction, mFeature.attributeMap(), mIdx ); + mLayer->actions()->doAction( mAction, mFeature, mIdx ); } QgsAttributeDialog *QgsFeatureAction::newDialog( bool cloneFeature ) diff --git a/src/app/qgsidentifyresults.cpp b/src/app/qgsidentifyresults.cpp index bdf4a7f6607..b37da19c19a 100644 --- a/src/app/qgsidentifyresults.cpp +++ b/src/app/qgsidentifyresults.cpp @@ -429,7 +429,7 @@ void QgsIdentifyResults::contextMenuEvent( QContextMenuEvent* event ) mActionPopup = new QMenu(); - int idx = 0; + int idx = -1; QTreeWidgetItem *featItem = featureItem( item ); if ( featItem ) { @@ -549,9 +549,7 @@ void QgsIdentifyResults::deactivate() void QgsIdentifyResults::doAction( QTreeWidgetItem *item, int action ) { - int idx; - QgsAttributeMap attributes; - QTreeWidgetItem *featItem = retrieveAttributes( item, attributes, idx ); + QTreeWidgetItem *featItem = featureItem( item ); if ( !featItem ) return; @@ -559,7 +557,7 @@ void QgsIdentifyResults::doAction( QTreeWidgetItem *item, int action ) if ( !layer ) return; - idx = -1; + int idx = -1; if ( item->parent() == featItem ) { QString fieldName = item->data( 0, Qt::DisplayRole ).toString(); @@ -574,7 +572,8 @@ void QgsIdentifyResults::doAction( QTreeWidgetItem *item, int action ) } } - layer->actions()->doAction( action, attributes, idx ); + int featIdx = featItem->data( 0, Qt::UserRole + 1 ).toInt(); + layer->actions()->doAction( action, mFeatures[ featIdx ], idx ); } QTreeWidgetItem *QgsIdentifyResults::featureItem( QTreeWidgetItem *item ) diff --git a/src/core/qgsattributeaction.cpp b/src/core/qgsattributeaction.cpp index c077cdd8e38..3c20b918a7e 100644 --- a/src/core/qgsattributeaction.cpp +++ b/src/core/qgsattributeaction.cpp @@ -22,15 +22,23 @@ * * ***************************************************************************/ -#include - -#include -#include - #include "qgsattributeaction.h" #include "qgspythonrunner.h" #include "qgsrunprocess.h" #include "qgsvectorlayer.h" +#include "qgsproject.h" +#include +#include "qgsexpression.h" + +#include +#include +#include +#include +#include +#include +#include +#include + void QgsAttributeAction::addAction( QgsAction::ActionType type, QString name, QString action, bool capture ) { @@ -44,7 +52,6 @@ void QgsAttributeAction::doAction( int index, const QgsAttributeMap &attributes, return; const QgsAction &action = at( index ); - if ( !action.runable() ) return; @@ -58,29 +65,67 @@ void QgsAttributeAction::doAction( int index, const QgsAttributeMap &attributes, // the UI and the code in this function to select on the // action.capture() return value. - // The QgsRunProcess instance created by this static function - // deletes itself when no longer needed. QString expandedAction = expandAction( action.action(), attributes, defaultValueIndex ); + if ( expandedAction.isEmpty() ) + return; + + QgsAction newAction( action.type(), action.name(), expandedAction, action.capture() ); + runAction( newAction, executePython ); +} + +void QgsAttributeAction::doAction( int index, QgsFeature &feat, int defaultValueIndex ) +{ + QMap substitutionMap; + if ( defaultValueIndex >= 0 ) + substitutionMap.insert( "$currfield", QVariant( defaultValueIndex ) ); + + doAction( index, feat, &substitutionMap ); +} + +void QgsAttributeAction::doAction( int index, QgsFeature &feat, + const QMap *substitutionMap ) +{ + if ( index < 0 || index >= size() ) + return; + + const QgsAction &action = at( index ); + if ( !action.runable() ) + return; + + // search for expressions while expanding actions + QString expandedAction = expandAction( action.action(), feat, substitutionMap ); + if ( expandedAction.isEmpty() ) + return; + + QgsAction newAction( action.type(), action.name(), expandedAction, action.capture() ); + runAction( newAction ); +} + +void QgsAttributeAction::runAction( const QgsAction &action, void ( *executePython )( const QString & ) ) +{ if ( action.type() == QgsAction::GenericPython ) { if ( executePython ) { // deprecated - executePython( expandedAction ); + executePython( action.action() ); } else if ( smPythonExecute ) { // deprecated - smPythonExecute( expandedAction ); + smPythonExecute( action.action() ); } else { - QgsPythonRunner::run( expandedAction ); + // TODO: capture output from QgsPythonRunner + QgsPythonRunner::run( action.action() ); } } else { - QgsRunProcess::create( expandedAction, action.capture() ); + // The QgsRunProcess instance created by this static function + // deletes itself when no longer needed. + QgsRunProcess::create( action.action(), action.capture() ); } } @@ -89,7 +134,7 @@ QString QgsAttributeAction::expandAction( QString action, const QgsAttributeMap { // This function currently replaces all %% characters in the action // with the value from values[clickedOnValue].second, and then - // searches for all strings that go %attribite_name, where + // searches for all strings that go %attribute_name, where // attribute_name is found in values[x].first, and replaces any that // it finds by values[s].second. @@ -134,6 +179,63 @@ QString QgsAttributeAction::expandAction( QString action, const QgsAttributeMap return expanded_action; } +QString QgsAttributeAction::expandAction( QString action, QgsFeature &feat, const QMap *substitutionMap ) +{ + // This function currently replaces each expression between [% and %] + // in the action with the result of its evaluation on the feature + // passed as argument. + + // Additional substitutions can be passed through the substitutionMap + // parameter + + QString expr_action; + + int index = 0; + while ( index < action.size() ) + { + QRegExp rx = QRegExp( "\\[%([^\\]]+)%\\]" ); + + int pos = rx.indexIn( action, index ); + if ( pos < 0 ) + break; + + int start = index; + index = pos + rx.matchedLength(); + + QString to_replace = rx.cap(1).trimmed(); + QgsDebugMsg( "Found expression:" + to_replace ); + + if ( substitutionMap && substitutionMap->contains( to_replace ) ) + { + expr_action += action.mid( start, pos - start ) + substitutionMap->value( to_replace ).toString(); + continue; + } + + QgsExpression* exp = new QgsExpression( to_replace ); + if ( exp->hasParserError() ) + { + QgsDebugMsg( "Expression parser error:" + exp->parserErrorString() ); + expr_action += action.mid( start, index - start ); + continue; + } + + QVariant result = exp->evaluate( &feat, mLayer->pendingFields() ); + if ( exp->hasEvalError() ) + { + QgsDebugMsg( "Expression parser eval error:" + exp->evalErrorString() ); + expr_action += action.mid( start, index - start ); + continue; + } + + QgsDebugMsg( "Expression result is: " + result.toString() ); + expr_action += action.mid( start, pos - start ) + result.toString(); + } + + expr_action += action.mid( index ); + return expr_action; +} + + bool QgsAttributeAction::writeXML( QDomNode& layer_node, QDomDocument& doc ) const { QDomElement aActions = doc.createElement( "attributeactions" ); diff --git a/src/core/qgsattributeaction.h b/src/core/qgsattributeaction.h index e5e0ae20bed..02ad8fa8bf8 100644 --- a/src/core/qgsattributeaction.h +++ b/src/core/qgsattributeaction.h @@ -107,23 +107,63 @@ class CORE_EXPORT QgsAttributeAction // dialog box. void addAction( QgsAction::ActionType type, QString name, QString action, bool capture = false ); - //! Does the action using the given values. defaultValueIndex is an - // index into values which indicates which value in the values vector - // is to be used if the action has a default placeholder. - // @note parameter executePython deprecated (and missing in python binding) + /*! Does the action using the given values. defaultValueIndex is an + * index into values which indicates which value in the values vector + * is to be used if the action has a default placeholder. + * @note parameter executePython deprecated (and missing in python binding) + * @deprecated + */ + Q_DECL_DEPRECATED void doAction( int index, + const QgsAttributeMap &attributes, + int defaultValueIndex = 0, + void ( *executePython )( const QString & ) = 0 ); + + /*! Does the given values. defaultValueIndex is the index of the + * field to be used if the action has a $currfield placeholder. + * @note added in 1.9 + */ void doAction( int index, - const QgsAttributeMap &attributes, - int defaultValueIndex = 0, - void ( *executePython )( const QString & ) = 0 ); + QgsFeature &feat, + int defaultValueIndex = 0 ); + + /*! Does the action using the expression builder to expand it + * and getting values from the passed feature attribute map. + * substitutionMap is used to pass custom substitutions, to replace + * each key in the map with the associated value + * @note added in 1.9 + */ + void doAction( int index, + QgsFeature &feat, + const QMap *substitutionMap = 0 ); //! Removes all actions void clearActions() { mActions.clear(); } - //! Expands the given action, replacing all %'s with the value as - // given. + //! Return the layer + QgsVectorLayer *layer() { return mLayer; } + + /*! Expands the given action, replacing all %'s with the value as + * given. + * @deprecated + */ + Q_DECL_DEPRECATED QString expandAction( QString action, + const QgsAttributeMap &attributes, + uint defaultValueIndex ); + + /*! Expands the given action using the expression builder + * This function currently replaces each expression between [% and %] + * placeholders in the action with the result of its evaluation on + * the feature passed as argument. + * + * Additional substitutions can be passed through the substitutionMap + * parameter + * + * @note added in 1.9 + */ QString expandAction( QString action, - const QgsAttributeMap &attributes, - uint defaultValueIndex ); + QgsFeature &feat, + const QMap *substitutionMap = 0 ); + //! Writes the actions out in XML format bool writeXML( QDomNode& layer_node, QDomDocument& doc ) const; @@ -142,6 +182,9 @@ class CORE_EXPORT QgsAttributeAction QList mActions; QgsVectorLayer *mLayer; static void ( *smPythonExecute )( const QString & ); + + void runAction( const QgsAction &action, + void ( *executePython )( const QString & ) = 0 ); }; #endif diff --git a/src/gui/attributetable/qgsattributetablemodel.cpp b/src/gui/attributetable/qgsattributetablemodel.cpp index 636c7d0fcb7..1c6521dc835 100644 --- a/src/gui/attributetable/qgsattributetablemodel.cpp +++ b/src/gui/attributetable/qgsattributetablemodel.cpp @@ -555,21 +555,13 @@ void QgsAttributeTableModel::incomingChangeLayout() void QgsAttributeTableModel::executeAction( int action, const QModelIndex &idx ) const { - QgsAttributeMap attributes; - - for ( int i = 0; i < mAttributes.size(); i++ ) - { - attributes.insert( mAttributes[i], data( index( idx.row(), i ), Qt::EditRole ) ); - } - - mLayer->actions()->doAction( action, attributes, fieldIdx( idx.column() ) ); + QgsFeature f = feature( idx ); + mLayer->actions()->doAction( action, f, fieldIdx( idx.column() ) ); } -QgsFeature QgsAttributeTableModel::feature( QModelIndex &idx ) +QgsFeature QgsAttributeTableModel::feature( const QModelIndex &idx ) const { QgsFeature f; - QgsAttributeMap attributes; - f.setFeatureId( rowToId( idx.row() ) ); for ( int i = 0; i < mAttributes.size(); i++ ) { diff --git a/src/gui/attributetable/qgsattributetablemodel.h b/src/gui/attributetable/qgsattributetablemodel.h index 4a9e5cad372..3fc20385c65 100644 --- a/src/gui/attributetable/qgsattributetablemodel.h +++ b/src/gui/attributetable/qgsattributetablemodel.h @@ -138,7 +138,7 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel void executeAction( int action, const QModelIndex &idx ) const; /** return feature attributes at given index */ - QgsFeature feature( QModelIndex &idx ); + QgsFeature feature( const QModelIndex &idx ) const; signals: /** diff --git a/src/ui/qgsattributeactiondialogbase.ui b/src/ui/qgsattributeactiondialogbase.ui index 63f6dddac64..63b2eb9ff94 100644 --- a/src/ui/qgsattributeactiondialogbase.ui +++ b/src/ui/qgsattributeactiondialogbase.ui @@ -253,6 +253,38 @@ + + + + + 0 + 0 + + + + Inserts an expression into the action + + + Insert expression... + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + +