[QGIS Commit] r12113 - in trunk/qgis: python/core src/app src/core src/ui

svn_qgis at osgeo.org svn_qgis at osgeo.org
Sat Nov 14 19:15:57 EST 2009


Author: jef
Date: 2009-11-14 19:15:55 -0500 (Sat, 14 Nov 2009)
New Revision: 12113

Added:
   trunk/qgis/python/core/qgsattributeaction.sip
Modified:
   trunk/qgis/python/core/core.sip
   trunk/qgis/python/core/qgsvectorlayer.sip
   trunk/qgis/src/app/qgisapp.cpp
   trunk/qgis/src/app/qgisapp.h
   trunk/qgis/src/app/qgsattributeactiondialog.cpp
   trunk/qgis/src/app/qgsattributeactiondialog.h
   trunk/qgis/src/app/qgsattributedialog.h
   trunk/qgis/src/app/qgsidentifyresults.cpp
   trunk/qgis/src/app/qgsidentifyresults.h
   trunk/qgis/src/core/qgsattributeaction.cpp
   trunk/qgis/src/core/qgsattributeaction.h
   trunk/qgis/src/core/qgsmaplayer.cpp
   trunk/qgis/src/core/qgsvectorlayer.cpp
   trunk/qgis/src/ui/qgsattributeactiondialogbase.ui
Log:
[FEATHRE] attribute action improvements
- add python bindings for attribute actions
- support actions as context menu in feature form (ui)
- add action types:
  * generic: commands that should work on all platforms
  * windows, mac, unix: commands that should work and are only shown on one platform respectively
    (eg. vim on unix, notepad on windows and textmate on Mac)
  * python: python strings to be executed instead of a command
    (eg. qgis.utils.plugins['apluginname'].amethod('[%someattribute%]'))

[BUGFIXES]
- ogr support for relative filenames fixed
- relative filename support also for feature form uis


Modified: trunk/qgis/python/core/core.sip
===================================================================
--- trunk/qgis/python/core/core.sip	2009-11-14 23:13:11 UTC (rev 12112)
+++ trunk/qgis/python/core/core.sip	2009-11-15 00:15:55 UTC (rev 12113)
@@ -68,6 +68,7 @@
 %Include qgssymbologyutils.sip
 %Include qgstolerance.sip
 %Include qgsuniquevaluerenderer.sip
+%Include qgsattributeaction.sip
 %Include qgsvectordataprovider.sip
 %Include qgsvectorfilewriter.sip
 %Include qgsvectorlayer.sip

Added: trunk/qgis/python/core/qgsattributeaction.sip
===================================================================
--- trunk/qgis/python/core/qgsattributeaction.sip	                        (rev 0)
+++ trunk/qgis/python/core/qgsattributeaction.sip	2009-11-15 00:15:55 UTC (rev 12113)
@@ -0,0 +1,77 @@
+class QgsAction
+{
+%TypeHeaderCode
+#include "qgsattributeaction.h"
+%End
+
+  public:
+    enum ActionType
+    {
+      Generic,
+      GenericPython,
+      Mac,
+      Windows,
+      Unix,
+    };
+
+    QgsAction( ActionType type, QString name, QString action, bool capture );
+
+    //! The name of the action
+    QString name() const;
+
+    //! The action
+    QString action() const;
+
+    //! The action type
+    ActionType type() const;
+
+    //! Whether to capture output for display when this action is run
+    bool capture() const;
+
+    bool runable() const;
+};
+
+class QgsAttributeAction
+{
+%TypeHeaderCode
+#include "qgsattributeaction.h"
+%End
+  public:
+    QgsAttributeAction();
+
+    //! Destructor
+    virtual ~QgsAttributeAction();
+
+    //! Add an action with the given name and action details.
+    // Will happily have duplicate names and actions. If
+    // capture is true, when running the action using doAction(),
+    // any stdout from the process will be captured and displayed in a
+    // 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.
+    void doAction( int index, const QList< QPair<QString, QString> > &values,
+                   int defaultValueIndex = 0, void *executePython = 0 );
+ */
+
+    //! Removes all actions
+    void clearActions();
+
+    //! Expands the given action, replacing all %'s with the value as
+    // given.
+    static QString expandAction( QString action, const QList< QPair<QString, QString> > &values,
+                                 uint defaultValueIndex );
+
+    //! Writes the actions out in XML format
+    bool writeXML( QDomNode& layer_node, QDomDocument& doc ) const;
+
+    //! Reads the actions in in XML format
+    bool readXML( const QDomNode& layer_node );
+
+    //! interface to inherited methods from QList<QgsAction>
+    const QgsAction &at( int idx );
+    const int size();
+};

Modified: trunk/qgis/python/core/qgsvectorlayer.sip
===================================================================
--- trunk/qgis/python/core/qgsvectorlayer.sip	2009-11-14 23:13:11 UTC (rev 12112)
+++ trunk/qgis/python/core/qgsvectorlayer.sip	2009-11-15 00:15:55 UTC (rev 12113)
@@ -63,7 +63,7 @@
 
   QgsLabel *label();
 
-  // TODO: wrap QgsAttributeAction* actions();
+  QgsAttributeAction *actions();
 
   /** The number of features that are selected in this layer */
   int selectedFeatureCount();

Modified: trunk/qgis/src/app/qgisapp.cpp
===================================================================
--- trunk/qgis/src/app/qgisapp.cpp	2009-11-14 23:13:11 UTC (rev 12112)
+++ trunk/qgis/src/app/qgisapp.cpp	2009-11-15 00:15:55 UTC (rev 12113)
@@ -6397,3 +6397,11 @@
   mActionUndo->setEnabled( canUndo );
   mActionRedo->setEnabled( canRedo );
 }
+
+void QgisApp::runPythonString( const QString &expr )
+{
+  if ( mPythonUtils )
+  {
+    mPythonUtils->runStringUnsafe( expr );
+  }
+}

Modified: trunk/qgis/src/app/qgisapp.h
===================================================================
--- trunk/qgis/src/app/qgisapp.h	2009-11-14 23:13:11 UTC (rev 12112)
+++ trunk/qgis/src/app/qgisapp.h	2009-11-15 00:15:55 UTC (rev 12113)
@@ -340,6 +340,9 @@
     QToolBar *pluginToolBar() { return mPluginToolBar; }
     QToolBar *helpToolBar() { return mHelpToolBar; }
 
+    //! run python
+    void runPythonString( const QString &expr );
+
   public slots:
     //! Zoom to full extent
     void zoomFull();

Modified: trunk/qgis/src/app/qgsattributeactiondialog.cpp
===================================================================
--- trunk/qgis/src/app/qgsattributeactiondialog.cpp	2009-11-14 23:13:11 UTC (rev 12112)
+++ trunk/qgis/src/app/qgsattributeactiondialog.cpp	2009-11-15 00:15:55 UTC (rev 12113)
@@ -68,24 +68,26 @@
   attributeActionTable->setRowCount( 0 );
 
   // Populate with our actions.
-  QgsAttributeAction::AttributeActions::const_iterator
-  iter = mActions->begin();
-  int i = 0;
-  for ( ; iter != mActions->end(); ++iter, ++i )
+  for ( int i = 0; i < mActions->size(); i++ )
   {
-    insertRow( i, iter->name(), iter->action(), iter->capture() );
+    const QgsAction action = ( *mActions )[i];
+    insertRow( i, action.type(), action.name(), action.action(), action.capture() );
   }
 }
 
-void QgsAttributeActionDialog::insertRow( int row, const QString &name, const QString &action, bool capture )
+void QgsAttributeActionDialog::insertRow( int row, QgsAction::ActionType type, const QString &name, const QString &action, bool capture )
 {
+  QTableWidgetItem* item;
   attributeActionTable->insertRow( row );
-  attributeActionTable->setItem( row, 0, new QTableWidgetItem( name ) );
-  attributeActionTable->setItem( row, 1, new QTableWidgetItem( action ) );
-  QTableWidgetItem* item = new QTableWidgetItem();
+  item = new QTableWidgetItem( actionType->itemText( type ) );
+  item->setFlags( item->flags() & ~Qt::ItemIsEditable );
+  attributeActionTable->setItem( row, 0, item );
+  attributeActionTable->setItem( row, 1, new QTableWidgetItem( name ) );
+  attributeActionTable->setItem( row, 2, new QTableWidgetItem( action ) );
+  item = new QTableWidgetItem();
   item->setFlags( item->flags() & ~( Qt::ItemIsEditable | Qt::ItemIsUserCheckable ) );
   item->setCheckState( capture ? Qt::Checked : Qt::Unchecked );
-  attributeActionTable->setItem( row, 2, item );
+  attributeActionTable->setItem( row, 3, item );
 }
 
 void QgsAttributeActionDialog::moveUp()
@@ -208,15 +210,15 @@
     if ( pos >= numRows )
     {
       // Expand the table to have a row with index pos
-      insertRow( pos, name, actionAction->text(), captureCB->isChecked() );
+      insertRow( pos, ( QgsAction::ActionType ) actionType->currentIndex(), name, actionAction->text(), captureCB->isChecked() );
     }
     else
     {
       // Update existing row
-      attributeActionTable->item( pos, 0 )->setText( name );
-      attributeActionTable->item( pos, 1 )->setText( actionAction->text() );
-      attributeActionTable->item( pos, 2 )->setCheckState(
-        captureCB->isChecked() ? Qt::Checked : Qt::Unchecked );
+      attributeActionTable->item( pos, 0 )->setText( actionType->currentText() );
+      attributeActionTable->item( pos, 1 )->setText( name );
+      attributeActionTable->item( pos, 2 )->setText( actionAction->text() );
+      attributeActionTable->item( pos, 3 )->setCheckState( captureCB->isChecked() ? Qt::Checked : Qt::Unchecked );
     }
   }
 }
@@ -253,12 +255,13 @@
   mActions->clearActions();
   for ( int i = 0; i < attributeActionTable->rowCount(); ++i )
   {
-    const QString &name = attributeActionTable->item( i, 0 )->text();
-    const QString &action = attributeActionTable->item( i, 1 )->text();
+    const QgsAction::ActionType type = ( QgsAction::ActionType ) actionType->findText( attributeActionTable->item( i, 0 )->text() );
+    const QString &name = attributeActionTable->item( i, 1 )->text();
+    const QString &action = attributeActionTable->item( i, 2 )->text();
     if ( !name.isEmpty() && !action.isEmpty() )
     {
-      QTableWidgetItem *item = attributeActionTable->item( i, 2 );
-      mActions->addAction( name, action, item->checkState() == Qt::Checked );
+      QTableWidgetItem *item = attributeActionTable->item( i, 3 );
+      mActions->addAction( type, name, action, item->checkState() == Qt::Checked );
     }
   }
 }
@@ -292,8 +295,9 @@
   if ( item )
   {
     // Only if a populated row was selected
-    actionName->setText( attributeActionTable->item( row, 0 )->text() );
-    actionAction->setText( attributeActionTable->item( row, 1 )->text() );
+    actionType->setCurrentIndex( actionType->findText( attributeActionTable->item( row, 0 )->text() ) );
+    actionName->setText( attributeActionTable->item( row, 1 )->text() );
+    actionAction->setText( attributeActionTable->item( row, 2 )->text() );
     captureCB->setChecked( item->checkState() == Qt::Checked );
   }
 }

Modified: trunk/qgis/src/app/qgsattributeactiondialog.h
===================================================================
--- trunk/qgis/src/app/qgsattributeactiondialog.h	2009-11-14 23:13:11 UTC (rev 12112)
+++ trunk/qgis/src/app/qgsattributeactiondialog.h	2009-11-15 00:15:55 UTC (rev 12113)
@@ -25,6 +25,7 @@
 #define QGSATTRIBUTEACTIONDIALOG_H
 
 #include "ui_qgsattributeactiondialogbase.h"
+#include "qgsattributeaction.h"
 #include "qgsfield.h"
 #include <QMap>
 
@@ -56,7 +57,7 @@
 
   private:
 
-    void insertRow( int row, const QString &name, const QString &action, bool capture );
+    void insertRow( int row, QgsAction::ActionType type, const QString &name, const QString &action, bool capture );
     void swapRows( int row1, int row2 );
 
     void insert( int pos );
@@ -66,7 +67,7 @@
     QString uniqueName( QString name );
 
     // Pointer to the QgsAttributeAction in the class that created us.
-    QgsAttributeAction* mActions;
+    QgsAttributeAction *mActions;
 };
 
 #endif

Modified: trunk/qgis/src/app/qgsattributedialog.h
===================================================================
--- trunk/qgis/src/app/qgsattributedialog.h	2009-11-14 23:13:11 UTC (rev 12112)
+++ trunk/qgis/src/app/qgsattributedialog.h	2009-11-15 00:15:55 UTC (rev 12113)
@@ -45,6 +45,8 @@
      */
     void restoreGeometry();
 
+    QDialog *dialog() { return mDialog; }
+
   public slots:
     /** Overloaded accept method which will write the feature field
      * values, then delegate to QDialog::accept()

Modified: trunk/qgis/src/app/qgsidentifyresults.cpp
===================================================================
--- trunk/qgis/src/app/qgsidentifyresults.cpp	2009-11-14 23:13:11 UTC (rev 12112)
+++ trunk/qgis/src/app/qgsidentifyresults.cpp	2009-11-15 00:15:55 UTC (rev 12113)
@@ -38,9 +38,22 @@
 #include <QMenu>
 #include <QClipboard>
 #include <QDockWidget>
+#include <QMenuBar>
 
 #include "qgslogger.h"
 
+static void _runPythonString( const QString &expr )
+{
+  QgisApp::instance()->runPythonString( expr );
+}
+
+void QgsFeatureAction::execute()
+{
+  QList< QPair<QString, QString> > attributes;
+  mResults->retrieveAttributes( mFeatItem, attributes );
+  mLayer->actions()->doAction( mAction, attributes, 0, _runPythonString );
+}
+
 class QgsIdentifyResultsDock : public QDockWidget
 {
   public:
@@ -96,7 +109,7 @@
   setColumnText( 0, tr( "Feature" ) );
   setColumnText( 1, tr( "Value" ) );
 
-  connect( buttonBox,SIGNAL( helpRequested() ), this, SLOT( helpClicked() ) );
+  connect( buttonBox, SIGNAL( helpRequested() ), this, SLOT( helpClicked() ) );
 
   connect( buttonBox, SIGNAL( clicked() ), this, SLOT( close() ) );
 
@@ -182,16 +195,19 @@
     actionItem->setData( 0, Qt::UserRole, "actions" );
     featItem->addChild( actionItem );
 
-    QTreeWidgetItem *editItem = new QTreeWidgetItem( QStringList() << "" << (vlayer->isEditable() ? tr( "Edit feature form" ) : tr( "View feature form" ) ) );
+    QTreeWidgetItem *editItem = new QTreeWidgetItem( QStringList() << "" << ( vlayer->isEditable() ? tr( "Edit feature form" ) : tr( "View feature form" ) ) );
     editItem->setIcon( 0, QgisApp::getThemeIcon( vlayer->isEditable() ? "/mIconEditable.png" : "/mIconEditable.png" ) );
     editItem->setData( 0, Qt::UserRole, "edit" );
     actionItem->addChild( editItem );
 
     for ( int i = 0; i < vlayer->actions()->size(); i++ )
     {
-      QgsAttributeAction::aIter iter = vlayer->actions()->retrieveAction( i );
+      const QgsAction &action = vlayer->actions()->at( i );
 
-      QTreeWidgetItem *twi = new QTreeWidgetItem( QStringList() << "" << iter->name() );
+      if ( !action.runable() )
+        continue;
+
+      QTreeWidgetItem *twi = new QTreeWidgetItem( QStringList() << "" << action.name() );
       twi->setIcon( 0, QgisApp::getThemeIcon( "/mAction.png" ) );
       twi->setData( 0, Qt::UserRole, "action" );
       twi->setData( 0, Qt::UserRole + 1, QVariant::fromValue( i ) );
@@ -206,31 +222,31 @@
 {
   QTreeWidgetItem *layItem = layerItem( sender() );
   QgsVectorLayer *vlayer = vectorLayer( layItem );
-  if( !layItem || !vlayer )
+  if ( !layItem || !vlayer )
     return;
 
   // iterate features
   int i;
-  for( i=0; i<layItem->childCount(); i++ )
+  for ( i = 0; i < layItem->childCount(); i++ )
   {
-    QTreeWidgetItem *featItem = layItem->child(i);
+    QTreeWidgetItem *featItem = layItem->child( i );
 
     int j;
-    for( j=0; j<featItem->childCount() && featItem->child(j)->data( 0, Qt::UserRole ).toString() != "actions"; j++ )
-      QgsDebugMsg( QString("%1: skipped %2").arg( featItem->child(j)->data( 0, Qt::UserRole ).toString() ) );
+    for ( j = 0; j < featItem->childCount() && featItem->child( j )->data( 0, Qt::UserRole ).toString() != "actions"; j++ )
+      QgsDebugMsg( QString( "%1: skipped %2" ).arg( featItem->child( j )->data( 0, Qt::UserRole ).toString() ) );
 
-    if( j==featItem->childCount() || featItem->child(j)->childCount()<1 )
+    if ( j == featItem->childCount() || featItem->child( j )->childCount() < 1 )
       continue;
 
-    QTreeWidgetItem *actions = featItem->child(j);
+    QTreeWidgetItem *actions = featItem->child( j );
 
-    for( j=0; i<actions->childCount() && actions->child(j)->data( 0, Qt::UserRole ).toString() != "edit"; j++ )  
+    for ( j = 0; i < actions->childCount() && actions->child( j )->data( 0, Qt::UserRole ).toString() != "edit"; j++ )
       ;
 
-    if( j==actions->childCount() )
+    if ( j == actions->childCount() )
       continue;
 
-    QTreeWidgetItem *editItem = actions->child(j);
+    QTreeWidgetItem *editItem = actions->child( j );
     editItem->setIcon( 0, QgisApp::getThemeIcon( vlayer->isEditable() ? "/mIconEditable.png" : "/mIconEditable.png" ) );
     editItem->setText( 1, vlayer->isEditable() ? tr( "Edit feature form" ) : tr( "View feature form" ) );
   }
@@ -257,7 +273,8 @@
 
         // if this is the only feature and it's on a vector layer
         // don't show the form dialog instead of the results window
-        featureForm( featItem );
+        lstResults->setCurrentItem( featItem );
+        featureForm();
         return;
       }
     }
@@ -291,7 +308,8 @@
 {
   if ( item->data( 0, Qt::UserRole ).toString() == "edit" )
   {
-    featureForm( item );
+    lstResults->setCurrentItem( item );
+    featureForm();
   }
   else if ( item->data( 0, Qt::UserRole ).toString() == "action" )
   {
@@ -319,36 +337,15 @@
 
   mActionPopup = new QMenu();
 
-  QAction *a;
-
-  a = mActionPopup->addAction( vlayer->isEditable() ? tr( "Edit feature form" ) : tr( "View feature form" ) );
-  a->setEnabled( true );
-  a->setData( QVariant::fromValue( -6 ) );
-
-  a = mActionPopup->addAction( tr( "Zoom to feature" ) );
-  a->setEnabled( true );
-  a->setData( QVariant::fromValue( -5 ) );
-
-  a = mActionPopup->addAction( tr( "Copy attribute value" ) );
-  a->setEnabled( true );
-  a->setData( QVariant::fromValue( -4 ) );
-
-  a = mActionPopup->addAction( tr( "Copy feature attributes" ) );
-  a->setEnabled( true );
-  a->setData( QVariant::fromValue( -3 ) );
-
+  mActionPopup->addAction( vlayer->isEditable() ? tr( "Edit feature form" ) : tr( "View feature form" ), this, SLOT( featureForm() ) );
+  mActionPopup->addAction( tr( "Zoom to feature" ), this, SLOT( zoomToFeature() ) );
+  mActionPopup->addAction( tr( "Copy attribute value" ), this, SLOT( copyAttributeValue() ) );
+  mActionPopup->addAction( tr( "Copy feature attributes" ), this, SLOT( copyFeatureAttributes() ) );
   mActionPopup->addSeparator();
+  mActionPopup->addAction( tr( "Expand all" ), this, SLOT( expandAll() ) );
+  mActionPopup->addAction( tr( "Collapse all" ), this, SLOT( collapseAll() ) );
 
-  a = mActionPopup->addAction( tr( "Expand all" ) );
-  a->setEnabled( true );
-  a->setData( QVariant::fromValue( -2 ) );
-
-  a = mActionPopup->addAction( tr( "Collapse all" ) );
-  a->setEnabled( true );
-  a->setData( QVariant::fromValue( -1 ) );
-
-  QgsAttributeAction *actions = vlayer->actions();
-  if ( actions && actions->size() > 0 )
+  if ( vlayer->actions()->size() > 0 )
   {
     mActionPopup->addSeparator();
 
@@ -356,22 +353,21 @@
     // created for each new Identify Results dialog box, and that the
     // contents of the popup menu doesn't change during the time that
     // such a dialog box is around.
-    a = mActionPopup->addAction( tr( "Run action" ) );
+    QAction *a = mActionPopup->addAction( tr( "Run action" ) );
     a->setEnabled( false );
 
-    QgsAttributeAction::aIter iter = actions->begin();
-    for ( int i = 0; iter != actions->end(); ++iter, ++i )
+    for ( int i = 0; i < vlayer->actions()->size(); i++ )
     {
-      QAction* a = mActionPopup->addAction( iter->name() );
-      a->setEnabled( true );
-      // The menu action stores an integer that is used later on to
-      // associate an menu action with an actual qgis action.
-      a->setData( QVariant::fromValue( i ) );
+      const QgsAction &action = vlayer->actions()->at( i );
+
+      if ( !action.runable() )
+        continue;
+
+      QgsFeatureAction *a = new QgsFeatureAction( action.name(), this, vlayer, i, featureItem( item ) );
+      mActionPopup->addAction( action.name(), a, SLOT( execute() ) );
     }
   }
 
-  connect( mActionPopup, SIGNAL( triggered( QAction* ) ), this, SLOT( popupItemSelected( QAction* ) ) );
-
   mActionPopup->popup( event->globalPos() );
 }
 
@@ -396,69 +392,6 @@
   header->setText( column, label );
 }
 
-// Run the action that was selected in the popup menu
-void QgsIdentifyResults::popupItemSelected( QAction* menuAction )
-{
-  QTreeWidgetItem *item = lstResults->currentItem();
-  if ( item == 0 )
-    return;
-
-  int action = menuAction->data().toInt();
-
-  if ( action < 0 )
-  {
-    switch ( action )
-    {
-      case -6:
-        featureForm( item );
-        break;
-
-      case -5:
-        zoomToFeature( item );
-        break;
-
-      case -4:
-      case -3:
-      {
-        QClipboard *clipboard = QApplication::clipboard();
-        QString text;
-
-        if ( action == -4 )
-        {
-          text = item->data( 1, Qt::DisplayRole ).toString();
-        }
-        else
-        {
-          std::vector< std::pair<QString, QString> > attributes;
-          retrieveAttributes( item, attributes );
-
-          for ( std::vector< std::pair<QString, QString> >::iterator it = attributes.begin(); it != attributes.end(); it++ )
-          {
-            text += QString( "%1: %2\n" ).arg( it->first ).arg( it->second );
-          }
-        }
-
-        QgsDebugMsg( QString( "set clipboard: %1" ).arg( text ) );
-        clipboard->setText( text );
-      }
-      break;
-
-      case -2:
-        lstResults->expandAll();
-        break;
-
-      case -1:
-        lstResults->collapseAll();
-        break;
-
-    }
-  }
-  else
-  {
-    doAction( item, action );
-  }
-}
-
 void QgsIdentifyResults::expandColumnsToFit()
 {
   lstResults->resizeColumnToContents( 0 );
@@ -511,7 +444,7 @@
 
 void QgsIdentifyResults::doAction( QTreeWidgetItem *item, int action )
 {
-  std::vector< std::pair<QString, QString> > attributes;
+  QList< QPair<QString, QString> > attributes;
   QTreeWidgetItem *featItem = retrieveAttributes( item, attributes );
   if ( !featItem )
     return;
@@ -585,7 +518,7 @@
 }
 
 
-QTreeWidgetItem *QgsIdentifyResults::retrieveAttributes( QTreeWidgetItem *item, std::vector< std::pair<QString, QString> > &attributes )
+QTreeWidgetItem *QgsIdentifyResults::retrieveAttributes( QTreeWidgetItem *item, QList< QPair<QString, QString> > &attributes )
 {
   QTreeWidgetItem *featItem = featureItem( item );
 
@@ -595,7 +528,7 @@
     QTreeWidgetItem *item = featItem->child( i );
     if ( item->childCount() > 0 )
       continue;
-    attributes.push_back( std::make_pair( item->data( 0, Qt::DisplayRole ).toString(), item->data( 1, Qt::DisplayRole ).toString() ) );
+    attributes << QPair<QString, QString>( item->data( 0, Qt::DisplayRole ).toString(), item->data( 1, Qt::DisplayRole ).toString() );
   }
 
   return featItem;
@@ -729,8 +662,10 @@
   }
 }
 
-void QgsIdentifyResults::zoomToFeature( QTreeWidgetItem *item )
+void QgsIdentifyResults::zoomToFeature()
 {
+  QTreeWidgetItem *item = lstResults->currentItem();
+
   QgsVectorLayer *layer = vectorLayer( item );
   if ( !layer )
     return;
@@ -765,11 +700,12 @@
   mCanvas->refresh();
 }
 
-
-void QgsIdentifyResults::featureForm( QTreeWidgetItem *item )
+void QgsIdentifyResults::featureForm()
 {
-  QgsVectorLayer *layer = vectorLayer( item );
-  if ( !layer )
+  QTreeWidgetItem *item = lstResults->currentItem();
+
+  QgsVectorLayer *vlayer = vectorLayer( item );
+  if ( !vlayer )
     return;
 
   QTreeWidgetItem *featItem = featureItem( item );
@@ -779,13 +715,35 @@
   int fid = featItem->data( 0, Qt::UserRole ).toInt();
 
   QgsFeature f;
-  if ( ! layer->featureAtId( fid, f ) )
+  if ( !vlayer->featureAtId( fid, f ) )
     return;
 
   QgsAttributeMap src = f.attributeMap();
 
-  layer->beginEditCommand( tr( "Attribute changed" ) );
-  QgsAttributeDialog *ad = new QgsAttributeDialog( layer, &f );
+  vlayer->beginEditCommand( tr( "Attribute changed" ) );
+  QgsAttributeDialog *ad = new QgsAttributeDialog( vlayer, &f );
+
+  if ( !vlayer->isEditable() && vlayer->actions()->size() > 0 )
+  {
+    ad->dialog()->setContextMenuPolicy( Qt::ActionsContextMenu );
+
+    QAction *a = new QAction( tr( "Run actions" ), ad->dialog() );
+    a->setEnabled( false );
+    ad->dialog()->addAction( a );
+
+    for ( int i = 0; i < vlayer->actions()->size(); i++ )
+    {
+      const QgsAction &action = vlayer->actions()->at( i );
+
+      if ( !action.runable() )
+        continue;
+
+      QgsFeatureAction *a = new QgsFeatureAction( action.name(), this, vlayer, i, featItem );
+      ad->dialog()->addAction( a );
+      connect( a, SIGNAL( triggered() ), a, SLOT( execute() ) );
+    }
+  }
+
   if ( ad->exec() )
   {
     const QgsAttributeMap &dst = f.attributeMap();
@@ -793,16 +751,51 @@
     {
       if ( !src.contains( it.key() ) || it.value() != src[it.key()] )
       {
-        layer->changeAttributeValue( f.id(), it.key(), it.value() );
+        vlayer->changeAttributeValue( f.id(), it.key(), it.value() );
       }
     }
-    layer->endEditCommand();
+    vlayer->endEditCommand();
   }
   else
   {
-    layer->destroyEditCommand();
+    vlayer->destroyEditCommand();
   }
 
   delete ad;
   mCanvas->refresh();
 }
+
+void QgsIdentifyResults::expandAll()
+{
+  lstResults->expandAll();
+}
+
+void QgsIdentifyResults::collapseAll()
+{
+  lstResults->collapseAll();
+}
+
+void QgsIdentifyResults::copyAttributeValue()
+{
+  QClipboard *clipboard = QApplication::clipboard();
+  QString text = lstResults->currentItem()->data( 1, Qt::DisplayRole ).toString();
+  QgsDebugMsg( QString( "set clipboard: %1" ).arg( text ) );
+  clipboard->setText( text );
+}
+
+void QgsIdentifyResults::copyFeatureAttributes()
+{
+  QClipboard *clipboard = QApplication::clipboard();
+  QString text;
+
+  QList< QPair<QString, QString> > attributes;
+  retrieveAttributes( lstResults->currentItem(), attributes );
+
+  for ( QList< QPair<QString, QString> >::iterator it = attributes.begin(); it != attributes.end(); it++ )
+  {
+    text += QString( "%1: %2\n" ).arg( it->first ).arg( it->second );
+  }
+
+  QgsDebugMsg( QString( "set clipboard: %1" ).arg( text ) );
+  clipboard->setText( text );
+}

Modified: trunk/qgis/src/app/qgsidentifyresults.h
===================================================================
--- trunk/qgis/src/app/qgsidentifyresults.h	2009-11-14 23:13:11 UTC (rev 12112)
+++ trunk/qgis/src/app/qgsidentifyresults.h	2009-11-15 00:15:55 UTC (rev 12113)
@@ -21,9 +21,9 @@
 
 #include "ui_qgsidentifyresultsbase.h"
 #include "qgsattributeaction.h"
+
 #include <QWidget>
-#include <vector>
-#include <map>
+#include <QList>
 
 class QCloseEvent;
 class QTreeWidgetItem;
@@ -77,12 +77,18 @@
 
     void close();
     void contextMenuEvent( QContextMenuEvent* );
-    void popupItemSelected( QAction* menuAction );
 
     void layerDestroyed();
     void editingToggled();
     void featureDeleted( int fid );
 
+    void featureForm();
+    void zoomToFeature();
+    void copyAttributeValue();
+    void copyFeatureAttributes();
+    void expandAll();
+    void collapseAll();
+
     //! Context help
     void helpClicked();
 
@@ -96,6 +102,8 @@
     /* Item in tree was clicked */
     void itemClicked( QTreeWidgetItem *lvi, int column );
 
+    QTreeWidgetItem *retrieveAttributes( QTreeWidgetItem *item, QList< QPair<QString, QString> > &attributes );
+
   private:
     QMenu *mActionPopup;
     QgsVectorLayer *mRubberBandLayer;
@@ -108,7 +116,7 @@
     QgsVectorLayer *vectorLayer( QTreeWidgetItem *item );
     QTreeWidgetItem *featureItem( QTreeWidgetItem *item );
     QTreeWidgetItem *layerItem( QObject *layer );
-    QTreeWidgetItem *retrieveAttributes( QTreeWidgetItem *item, std::vector< std::pair<QString, QString> > &attributes );
+
     void clearRubberBand();
     void disconnectLayer( QObject *object );
 
@@ -118,12 +126,29 @@
     void restorePosition();
 
     void highlightFeature( QTreeWidgetItem *item );
-    void zoomToFeature( QTreeWidgetItem *item );
-    void featureForm( QTreeWidgetItem *item );
 
     void doAction( QTreeWidgetItem *item, int action );
 
     QDockWidget *mDock;
 };
 
+class QgsFeatureAction : public QAction
+{
+    Q_OBJECT
+
+  public:
+    QgsFeatureAction( const QString &name, QgsIdentifyResults *results, QgsVectorLayer *vl, int action, QTreeWidgetItem *featItem ) :
+        QAction( name, results ), mResults( results ), mLayer( vl ), mAction( action ), mFeatItem( featItem )
+    {}
+
+  public slots:
+    void execute();
+
+  private:
+    QgsIdentifyResults *mResults;
+    QgsVectorLayer *mLayer;
+    int mAction;
+    QTreeWidgetItem *mFeatItem;
+};
+
 #endif

Modified: trunk/qgis/src/core/qgsattributeaction.cpp
===================================================================
--- trunk/qgis/src/core/qgsattributeaction.cpp	2009-11-14 23:13:11 UTC (rev 12112)
+++ trunk/qgis/src/core/qgsattributeaction.cpp	2009-11-15 00:15:55 UTC (rev 12113)
@@ -23,7 +23,7 @@
  ***************************************************************************/
 /*  $Id$ */
 
-#include <vector>
+#include <QList>
 
 #include <QStringList>
 #include <QDomElement>
@@ -33,17 +33,22 @@
 
 static const char * const ident_ = "$Id$";
 
-void QgsAttributeAction::addAction( QString name, QString action,
-                                    bool capture )
+void QgsAttributeAction::addAction( QgsAction::ActionType type, QString name, QString action, bool capture )
 {
-  mActions.push_back( QgsAction( name, action, capture ) );
+  *this << QgsAction( type, name, action, capture );
 }
 
-void QgsAttributeAction::doAction( unsigned int index, const std::vector< std::pair<QString, QString> > &values,
-                                   uint defaultValueIndex )
+void QgsAttributeAction::doAction( int index, const QList< QPair<QString, QString> > &values,
+                                   int defaultValueIndex, void ( *executePython )( const QString & ) )
 {
-  aIter action = retrieveAction( index );
+  if ( index < 0 || index >= size() )
+    return;
 
+  const QgsAction &action = at( index );
+
+  if ( !action.runable() )
+    return;
+
   // A couple of extra options for running the action may be
   // useful. For example,
   // - run the action inside a terminal (on unix)
@@ -54,31 +59,23 @@
   // the UI and the code in this function to select on the
   // action.capture() return value.
 
-  if ( action != end() )
+  // The QgsRunProcess instance created by this static function
+  // deletes itself when no longer needed.
+  QString expandedAction = expandAction( action.action(), values, defaultValueIndex );
+  if ( action.type() == QgsAction::GenericPython )
   {
-    // The QgsRunProcess instance created by this static function
-    // deletes itself when no longer needed.
-    QString expandedAction = expandAction( action->action(), values, defaultValueIndex );
-    QgsRunProcess::create( expandedAction, action->capture() );
+    if ( executePython )
+    {
+      executePython( expandedAction );
+    }
   }
-}
-
-QgsAttributeAction::aIter QgsAttributeAction::retrieveAction( unsigned int index ) const
-{
-  // This function returns an iterator so that it's easy to deal with
-  // an invalid index being given.
-  aIter a_iter = end();
-
-  if ( index >= 0 && index < mActions.size() )
+  else
   {
-    a_iter = mActions.begin();
-    for ( unsigned int i = 0; i < index; ++i, ++a_iter )
-      {}
+    QgsRunProcess::create( expandedAction, action.capture() );
   }
-  return a_iter;
 }
 
-QString QgsAttributeAction::expandAction( QString action, const std::vector< std::pair<QString, QString> > &values,
+QString QgsAttributeAction::expandAction( QString action, const QList< QPair<QString, QString> > &values,
     uint clickedOnValue )
 {
   // This function currently replaces all %% characters in the action
@@ -102,7 +99,7 @@
   else
     expanded_action = action;
 
-  for ( unsigned int i = 0; i < values.size(); ++i )
+  for ( int i = 0; i < values.size(); ++i )
   {
     // Check for a replace a quoted version and a non-quoted version.
     QString to_replace_1 = "[%" + values[i].first + "]";
@@ -119,14 +116,13 @@
 {
   QDomElement aActions = doc.createElement( "attributeactions" );
 
-  aIter a_iter = begin();
-
-  for ( ; a_iter != end(); ++a_iter )
+  for ( int i = 0; i < size(); i++ )
   {
     QDomElement actionSetting = doc.createElement( "actionsetting" );
-    actionSetting.setAttribute( "name", a_iter->name() );
-    actionSetting.setAttribute( "action", a_iter->action() );
-    actionSetting.setAttribute( "capture", a_iter->capture() );
+    actionSetting.setAttribute( "type", at( i ).type() );
+    actionSetting.setAttribute( "name", at( i ).name() );
+    actionSetting.setAttribute( "action", at( i ).action() );
+    actionSetting.setAttribute( "capture", at( i ).capture() );
     aActions.appendChild( actionSetting );
   }
   layer_node.appendChild( aActions );
@@ -136,7 +132,7 @@
 
 bool QgsAttributeAction::readXML( const QDomNode& layer_node )
 {
-  mActions.clear();
+  clear();
 
   QDomNode aaNode = layer_node.namedItem( "attributeactions" );
 
@@ -146,10 +142,10 @@
     for ( unsigned int i = 0; i < actionsettings.length(); ++i )
     {
       QDomElement setting = actionsettings.item( i ).toElement();
-      int capture = setting.attributeNode( "capture" ).value().toInt();
-      addAction( setting.attributeNode( "name" ).value(),
-                 setting.attributeNode( "action" ).value(),
-                 capture == 0 ? false : true );
+      addAction(( QgsAction::ActionType ) setting.attributeNode( "type" ).value().toInt(),
+                setting.attributeNode( "name" ).value(),
+                setting.attributeNode( "action" ).value(),
+                setting.attributeNode( "capture" ).value().toInt() != 0 );
     }
   }
   return true;

Modified: trunk/qgis/src/core/qgsattributeaction.h
===================================================================
--- trunk/qgis/src/core/qgsattributeaction.h	2009-11-14 23:13:11 UTC (rev 12112)
+++ trunk/qgis/src/core/qgsattributeaction.h	2009-11-15 00:15:55 UTC (rev 12113)
@@ -27,34 +27,59 @@
 
 #include <QString>
 #include <QObject>
+#include <QList>
+#include <QPair>
 
-#include <list>
-#include <vector>
-#include <utility>
-
 class QDomNode;
 class QDomDocument;
 
-
 /** \ingroup core
  * Utility class that encapsulates an action based on vector attributes.
  */
 class CORE_EXPORT QgsAction
 {
   public:
-    QgsAction( QString name, QString action, bool capture ) :
-        mName( name ), mAction( action ), mCaptureOutput( capture ) {}
+    enum ActionType
+    {
+      Generic,
+      GenericPython,
+      Mac,
+      Windows,
+      Unix,
+    };
 
+    QgsAction( ActionType type, QString name, QString action, bool capture ) :
+        mType( type ), mName( name ), mAction( action ), mCaptureOutput( capture ) {}
+
     //! The name of the action
     QString name() const { return mName; }
 
     //! The action
     QString action() const { return mAction; }
 
+    //! The action type
+    ActionType type() const { return mType; }
+
     //! Whether to capture output for display when this action is run
     bool capture() const { return mCaptureOutput; }
 
+    //!
+    bool runable() const
+    {
+      return mType == Generic ||
+             mType == GenericPython ||
+#if defined(Q_OS_WIN)
+             mType == Windows
+#elif defined(Q_OS_MAC)
+             mType == Mac
+#else
+             mType == Unix
+#endif
+             ;
+    }
+
   private:
+    ActionType mType;
     QString mName;
     QString mAction;
     bool mCaptureOutput;
@@ -65,13 +90,9 @@
  * attributes.
  */
 
-class  CORE_EXPORT QgsAttributeAction
+class  CORE_EXPORT QgsAttributeAction : public QList<QgsAction>
 {
   public:
-
-    typedef std::list<QgsAction> AttributeActions;
-    typedef AttributeActions::const_iterator aIter;
-
     //! Constructor
     QgsAttributeAction() {};
 
@@ -83,38 +104,20 @@
     // capture is true, when running the action using doAction(),
     // any stdout from the process will be captured and displayed in a
     // dialog box.
-    void addAction( QString name, QString action, bool capture = false );
+    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.
-    void doAction( unsigned int index, const std::vector< std::pair<QString, QString> > &values,
-                   uint defaultValueIndex = 0 );
+    void doAction( int index, const QList< QPair<QString, QString> > &values,
+                   int defaultValueIndex = 0, void ( *executePython )( const QString & ) = 0 );
 
-    //! Returns a const_iterator that points to the QgsAction at the
-    // given position in the data collection. The insertion order is
-    // preserved. The index starts at 0 and goes to one less than the
-    // number of actions. An index outside that range will return an
-    // a const_iterator equal to that returned by end().
-    aIter retrieveAction( unsigned int index ) const;
-
     //! Removes all actions
-    void clearActions() { mActions.clear(); }
+    void clearActions() { clear(); }
 
-    //! A const iterator to the start of the action pairs
-    const AttributeActions::const_iterator begin() const
-    { return mActions.begin(); }
-
-    //! A const iterator to the one past the end of the action pairs
-    const AttributeActions::const_iterator end() const
-    { return mActions.end(); }
-
-    //! Returns the number of stored actions
-    int size() const { return mActions.size(); }
-
     //! Expands the given action, replacing all %'s with the value as
     // given.
-    static QString expandAction( QString action, const std::vector< std::pair<QString, QString> > &values,
+    static QString expandAction( QString action, const QList< QPair<QString, QString> > &values,
                                  uint defaultValueIndex );
 
     //! Writes the actions out in XML format
@@ -122,11 +125,6 @@
 
     //! Reads the actions in in XML format
     bool readXML( const QDomNode& layer_node );
-
-  private:
-
-    // Stores the name/action pairs.
-    AttributeActions mActions;
 };
 
 #endif

Modified: trunk/qgis/src/core/qgsmaplayer.cpp
===================================================================
--- trunk/qgis/src/core/qgsmaplayer.cpp	2009-11-14 23:13:11 UTC (rev 12112)
+++ trunk/qgis/src/core/qgsmaplayer.cpp	2009-11-15 00:15:55 UTC (rev 12113)
@@ -292,7 +292,7 @@
   else if ( vlayer && vlayer->providerType() == "ogr" )
   {
     QStringList theURIParts = src.split( "|" );
-    theURIParts[0] = QgsProject::instance()->readPath( theURIParts[0] );
+    theURIParts[0] = QgsProject::instance()->writePath( theURIParts[0] );
     src = theURIParts.join( "|" );
   }
   else

Modified: trunk/qgis/src/core/qgsvectorlayer.cpp
===================================================================
--- trunk/qgis/src/core/qgsvectorlayer.cpp	2009-11-14 23:13:11 UTC (rev 12112)
+++ trunk/qgis/src/core/qgsvectorlayer.cpp	2009-11-15 00:15:55 UTC (rev 12113)
@@ -72,6 +72,7 @@
 #include "qgslogger.h"
 #include "qgsmaplayerregistry.h"
 #include "qgsclipper.h"
+#include "qgsproject.h"
 
 #ifdef TESTPROVIDERLIB
 #include <dlfcn.h>
@@ -2357,7 +2358,7 @@
 
         mRanges[ name ] = RangeData( min, max, step );
       }
-      else if( editType == CheckBox )
+      else if ( editType == CheckBox )
       {
         mCheckedStates[ name ] = QPair<QString, QString>( editTypeElement.attribute( "checked" ), editTypeElement.attribute( "unchecked" ) );
       }
@@ -2368,7 +2369,7 @@
   if ( !editFormNode.isNull() )
   {
     QDomElement e = editFormNode.toElement();
-    mEditForm = e.text();
+    mEditForm = QgsProject::instance()->readPath( e.text() );
   }
 
   mAttributeAliasMap.clear();
@@ -2531,7 +2532,7 @@
   }
 
   QDomElement efField  = doc.createElement( "editform" );
-  QDomText efText = doc.createTextNode( mEditForm );
+  QDomText efText = doc.createTextNode( QgsProject::instance()->writePath( mEditForm ) );
   efField.appendChild( efText );
   node.appendChild( efField );
 
@@ -4130,14 +4131,14 @@
 {
   const QgsFieldMap &fields = pendingFields();
   if ( fields.contains( idx ) )
-    mCheckedStates[ fields[idx].name() ] = QPair<QString, QString>( checked, unchecked );
+    mCheckedStates[ fields[idx].name()] = QPair<QString, QString>( checked, unchecked );
 }
 
 QPair<QString, QString> QgsVectorLayer::checkedState( int idx )
 {
   const QgsFieldMap &fields = pendingFields();
   if ( fields.contains( idx ) && mCheckedStates.contains( fields[idx].name() ) )
-    return mCheckedStates[ fields[idx].name() ];
+    return mCheckedStates[ fields[idx].name()];
   else
-    return QPair<QString,QString>( "1", "0" );
+    return QPair<QString, QString>( "1", "0" );
 }

Modified: trunk/qgis/src/ui/qgsattributeactiondialogbase.ui
===================================================================
--- trunk/qgis/src/ui/qgsattributeactiondialogbase.ui	2009-11-14 23:13:11 UTC (rev 12112)
+++ trunk/qgis/src/ui/qgsattributeactiondialogbase.ui	2009-11-15 00:15:55 UTC (rev 12113)
@@ -47,16 +47,21 @@
          <enum>QAbstractItemView::SelectRows</enum>
         </property>
         <property name="columnCount">
-         <number>3</number>
+         <number>4</number>
         </property>
         <column>
          <property name="text">
+          <string>New Column</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
           <string>Name</string>
          </property>
         </column>
         <column>
          <property name="text">
-          <string>Action</string>
+          <string>Type</string>
          </property>
         </column>
         <column>
@@ -125,7 +130,7 @@
       <string>Action properties</string>
      </property>
      <layout class="QGridLayout" name="gridLayout">
-      <item row="0" column="0">
+      <item row="1" column="0">
        <widget class="QLabel" name="textLabel1">
         <property name="whatsThis">
          <string>Enter the name of an action here. The name should be unique (qgis will make it unique if necessary).</string>
@@ -138,7 +143,7 @@
         </property>
        </widget>
       </item>
-      <item row="0" column="1">
+      <item row="1" column="1">
        <widget class="QLineEdit" name="actionName">
         <property name="toolTip">
          <string>Enter the action name here</string>
@@ -148,7 +153,7 @@
         </property>
        </widget>
       </item>
-      <item row="1" column="0">
+      <item row="2" column="0">
        <widget class="QLabel" name="textLabel2">
         <property name="whatsThis">
          <string>Enter the action here. This can be any program, script or command that is available on your system. When the action is invoked any set of characters that start with a % and then have the name of a field will be replaced by the value of that field. The special characters %% will be replaced by the value of the field that was selected. Double quote marks group text into single arguments to the program, script or command. Double quotes will be ignored if prefixed with a backslash</string>
@@ -161,7 +166,7 @@
         </property>
        </widget>
       </item>
-      <item row="1" column="1">
+      <item row="2" column="1">
        <layout class="QHBoxLayout" name="horizontalLayout">
         <item>
          <widget class="QLineEdit" name="actionAction">
@@ -191,7 +196,7 @@
         </item>
        </layout>
       </item>
-      <item row="2" column="0">
+      <item row="3" column="0">
        <widget class="QCheckBox" name="captureCB">
         <property name="toolTip">
          <string>Captures any output from the action</string>
@@ -204,7 +209,7 @@
         </property>
        </widget>
       </item>
-      <item row="2" column="1">
+      <item row="3" column="1">
        <layout class="QHBoxLayout" name="horizontalLayout_2">
         <item>
          <widget class="QComboBox" name="fieldComboBox">
@@ -225,6 +230,45 @@
         </item>
        </layout>
       </item>
+      <item row="0" column="1">
+       <widget class="QComboBox" name="actionType">
+        <item>
+         <property name="text">
+          <string>Generic</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Python</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Windows</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Mac</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Unix</string>
+         </property>
+        </item>
+       </widget>
+      </item>
+      <item row="0" column="0">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Type</string>
+        </property>
+        <property name="buddy">
+         <cstring>actionType</cstring>
+        </property>
+       </widget>
+      </item>
      </layout>
     </widget>
    </item>



More information about the QGIS-commit mailing list