[QGIS Commit] r14729 - in trunk/qgis/src: app app/attributetable app/gps core ui

svn_qgis at osgeo.org svn_qgis at osgeo.org
Sun Nov 21 15:09:36 EST 2010


Author: jef
Date: 2010-11-21 12:09:36 -0800 (Sun, 21 Nov 2010)
New Revision: 14729

Modified:
   trunk/qgis/src/app/attributetable/qgsattributetabledelegate.cpp
   trunk/qgis/src/app/attributetable/qgsattributetabledelegate.h
   trunk/qgis/src/app/attributetable/qgsattributetabledialog.cpp
   trunk/qgis/src/app/attributetable/qgsattributetabledialog.h
   trunk/qgis/src/app/attributetable/qgsattributetablememorymodel.cpp
   trunk/qgis/src/app/attributetable/qgsattributetablememorymodel.h
   trunk/qgis/src/app/attributetable/qgsattributetablemodel.cpp
   trunk/qgis/src/app/attributetable/qgsattributetablemodel.h
   trunk/qgis/src/app/attributetable/qgsattributetableview.cpp
   trunk/qgis/src/app/attributetable/qgsattributetableview.h
   trunk/qgis/src/app/gps/qgsgpsinformationwidget.cpp
   trunk/qgis/src/app/qgisappinterface.cpp
   trunk/qgis/src/app/qgsfeatureaction.cpp
   trunk/qgis/src/app/qgsfeatureaction.h
   trunk/qgis/src/app/qgsidentifyresults.cpp
   trunk/qgis/src/app/qgsidentifyresults.h
   trunk/qgis/src/app/qgsmaptooladdfeature.cpp
   trunk/qgis/src/app/qgsmaptooladdfeature.h
   trunk/qgis/src/app/qgsmaptoolidentify.cpp
   trunk/qgis/src/app/qgsmergeattributesdialog.cpp
   trunk/qgis/src/core/qgsvectorlayer.cpp
   trunk/qgis/src/core/qgsvectorlayer.h
   trunk/qgis/src/ui/qgsattributetabledialog.ui
Log:
[FEATURE] attribute editing improvements:
- refactor attribute dialog calls to QgsFeatureAttribute
- add QgsVectorLayer::featureAdded signal
- improve interactive attribute editing in table (adding/deleting
  features, attribute update)
- allow adding of geometryless features
- fix attribute undo/redo

Modified: trunk/qgis/src/app/attributetable/qgsattributetabledelegate.cpp
===================================================================
--- trunk/qgis/src/app/attributetable/qgsattributetabledelegate.cpp	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/attributetable/qgsattributetabledelegate.cpp	2010-11-21 20:09:36 UTC (rev 14729)
@@ -52,7 +52,20 @@
   return -1;
 }
 
+int QgsAttributeTableDelegate::featureId( const QModelIndex &index ) const
+{
+  const QgsAttributeTableModel *tm = qobject_cast<const QgsAttributeTableModel *>( index.model() );
+  if ( tm )
+    return tm->rowToId( index.row() );
 
+  const QgsAttributeTableFilterModel *fm = dynamic_cast<const QgsAttributeTableFilterModel *>( index.model() );
+  if ( fm )
+    return fm->tableModel()->rowToId( index.row() );
+
+  return -1;
+}
+
+
 QWidget *QgsAttributeTableDelegate::createEditor(
   QWidget *parent,
   const QStyleOptionViewItem &option,
@@ -87,11 +100,16 @@
   if ( vl == NULL )
     return;
 
+  int idx = fieldIdx( index );
+  int fid = featureId( index );
+
   QVariant value;
-  if ( !QgsAttributeEditor::retrieveValue( editor, vl, fieldIdx( index ), value ) )
+  if ( !QgsAttributeEditor::retrieveValue( editor, vl, idx, value ) )
     return;
 
-  model->setData( index, value );
+  vl->beginEditCommand( tr( "Attribute changed" ) );
+  vl->changeAttributeValue( fid, idx, value, true );
+  vl->endEditCommand();
 }
 
 void QgsAttributeTableDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const

Modified: trunk/qgis/src/app/attributetable/qgsattributetabledelegate.h
===================================================================
--- trunk/qgis/src/app/attributetable/qgsattributetabledelegate.h	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/attributetable/qgsattributetabledelegate.h	2010-11-21 20:09:36 UTC (rev 14729)
@@ -32,6 +32,7 @@
 
     QgsVectorLayer *layer( const QAbstractItemModel *model ) const;
     int fieldIdx( const QModelIndex &index ) const;
+    int featureId( const QModelIndex &index ) const;
 
   public:
     /** Constructor

Modified: trunk/qgis/src/app/attributetable/qgsattributetabledialog.cpp
===================================================================
--- trunk/qgis/src/app/attributetable/qgsattributetabledialog.cpp	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/attributetable/qgsattributetabledialog.cpp	2010-11-21 20:09:36 UTC (rev 14729)
@@ -34,6 +34,7 @@
 #include "qgslogger.h"
 #include "qgsmapcanvas.h"
 #include "qgsfieldcalculator.h"
+#include "qgsfeatureaction.h"
 
 class QgsAttributeTableDock : public QDockWidget
 {
@@ -99,6 +100,7 @@
   bool canDeleteFeatures = mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::DeleteFeatures;
   bool canAddAttributes = mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::AddAttributes;
   bool canDeleteAttributes = mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::DeleteAttributes;
+  bool canAddFeatures = mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::AddFeatures;
   mToggleEditingButton->setCheckable( true );
   mToggleEditingButton->setChecked( mLayer->isEditable() );
   mToggleEditingButton->setEnabled( canChangeAttributes && !mLayer->isReadOnly() );
@@ -106,6 +108,8 @@
   mDeleteSelectedButton->setEnabled( canDeleteFeatures && mLayer->isEditable() );
   mAddAttribute->setEnabled( canAddAttributes && mLayer->isEditable() );
   mRemoveAttribute->setEnabled( canDeleteAttributes && mLayer->isEditable() );
+  mAddFeature->setEnabled( canAddFeatures && mLayer->isEditable() && mLayer->geometryType() == QGis::NoGeometry );
+  mAddFeature->setHidden( !canAddFeatures || mLayer->geometryType() != QGis::NoGeometry );
 
   // info from table to application
   connect( this, SIGNAL( editingToggled( QgsMapLayer * ) ), QgisApp::instance(), SLOT( toggleEditing( QgsMapLayer * ) ) );
@@ -114,6 +118,7 @@
   connect( mLayer, SIGNAL( editingStopped() ), this, SLOT( editingToggled() ) );
 
   connect( searchButton, SIGNAL( clicked() ), this, SLOT( search() ) );
+  connect( mAddFeature, SIGNAL( clicked() ), this, SLOT( addFeature() ) );
 
   connect( mLayer, SIGNAL( selectionChanged() ), this, SLOT( updateSelectionFromLayer() ) );
   connect( mLayer, SIGNAL( layerDeleted() ), this, SLOT( close() ) );
@@ -674,10 +679,12 @@
   bool canDeleteFeatures = mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::DeleteFeatures;
   bool canAddAttributes = mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::AddAttributes;
   bool canDeleteAttributes = mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::DeleteAttributes;
+  bool canAddFeatures = mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::AddFeatures;
   mOpenFieldCalculator->setEnabled( canChangeAttributes && mLayer->isEditable() );
   mDeleteSelectedButton->setEnabled( canDeleteFeatures && mLayer->isEditable() );
   mAddAttribute->setEnabled( canAddAttributes && mLayer->isEditable() );
   mRemoveAttribute->setEnabled( canDeleteAttributes && mLayer->isEditable() );
+  mAddFeature->setEnabled( canAddFeatures && mLayer->isEditable() && mLayer->geometryType() == QGis::NoGeometry );
 
   // (probably reload data if user stopped editing - possible revert)
   mModel->reload( mModel->index( 0, 0 ), mModel->index( mModel->rowCount(), mModel->columnCount() ) );
@@ -775,9 +782,18 @@
 void QgsAttributeTableDialog::on_mOpenFieldCalculator_clicked()
 {
   QgsFieldCalculator calc( mLayer );
-  if ( calc.exec() == QDialog::Accepted )
+  calc.exec();
+}
+
+void QgsAttributeTableDialog::addFeature()
+{
+  if ( !mLayer->isEditable() )
+    return;
+
+  QgsFeature f;
+  QgsFeatureAction action( tr( "Geometryless feature added" ), f, mLayer, -1, this );
+  if ( action.addFeature() )
   {
-    // update model - a field has been added or updated
     mModel->reload( mModel->index( 0, 0 ), mModel->index( mModel->rowCount(), mModel->columnCount() ) );
   }
 }

Modified: trunk/qgis/src/app/attributetable/qgsattributetabledialog.h
===================================================================
--- trunk/qgis/src/app/attributetable/qgsattributetabledialog.h	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/attributetable/qgsattributetabledialog.h	2010-11-21 20:09:36 UTC (rev 14729)
@@ -151,6 +151,11 @@
      */
     void on_mDeleteSelectedButton_clicked();
 
+    /**
+     * add feature
+     */
+    void addFeature();
+
     void on_mHelpButton_clicked() { QgsContextHelp::run( metaObject()->className() ); }
 
   signals:

Modified: trunk/qgis/src/app/attributetable/qgsattributetablememorymodel.cpp
===================================================================
--- trunk/qgis/src/app/attributetable/qgsattributetablememorymodel.cpp	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/attributetable/qgsattributetablememorymodel.cpp	2010-11-21 20:09:36 UTC (rev 14729)
@@ -60,7 +60,6 @@
   }
 }
 
-#if 0
 void QgsAttributeTableMemoryModel::featureDeleted( int fid )
 {
   QgsDebugMsg( "entered." );
@@ -76,7 +75,6 @@
   mFeatureMap.insert( fid, f );
   QgsAttributeTableModel::featureAdded( fid );
 }
-#endif
 
 void QgsAttributeTableMemoryModel::layerDeleted()
 {
@@ -89,5 +87,5 @@
 {
   QgsDebugMsg( "entered." );
   mFeatureMap[fid].changeAttribute( idx, value );
-  reload( index( 0, 0 ), index( rowCount(), columnCount() ) );
+  QgsAttributeTableModel::attributeValueChanged( fid, idx, value );
 }

Modified: trunk/qgis/src/app/attributetable/qgsattributetablememorymodel.h
===================================================================
--- trunk/qgis/src/app/attributetable/qgsattributetablememorymodel.h	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/attributetable/qgsattributetablememorymodel.h	2010-11-21 20:09:36 UTC (rev 14729)
@@ -40,7 +40,6 @@
     QgsAttributeTableMemoryModel( QgsVectorLayer *theLayer );
 
   protected slots:
-#if 0
     /**
      * Launched when a feature has been deleted
      * @param fid feature id
@@ -51,7 +50,6 @@
      * @param fid feature id
      */
     virtual void featureAdded( int fid );
-#endif
     /**
      * Launched when layer has been deleted
      */

Modified: trunk/qgis/src/app/attributetable/qgsattributetablemodel.cpp
===================================================================
--- trunk/qgis/src/app/attributetable/qgsattributetablemodel.cpp	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/attributetable/qgsattributetablemodel.cpp	2010-11-21 20:09:36 UTC (rev 14729)
@@ -22,14 +22,12 @@
 #include "qgisapp.h"
 #include "qgsattributeaction.h"
 #include "qgsmapcanvas.h"
+#include "qgsfeatureaction.h"
 
 #include <QtGui>
 #include <QVariant>
 #include <limits>
 
-////////////////////////////
-// QgsAttributeTableModel //
-////////////////////////////
 
 QgsAttributeTableModel::QgsAttributeTableModel( QgsVectorLayer *theLayer, QObject *parent )
     : QAbstractTableModel( parent )
@@ -38,67 +36,96 @@
   mLayer = theLayer;
   loadAttributes();
 
-  connect( mLayer, SIGNAL( layerModified( bool ) ), this, SLOT( layerModified( bool ) ) );
-  //connect(mLayer, SIGNAL(attributeValueChanged(int, int, const QVariant&)), this, SLOT( attributeValueChanged(int, int, const QVariant&)));
-  //connect(mLayer, SIGNAL(featureDeleted(int)), this, SLOT( featureDeleted(int)));
-  //connect(mLayer, SIGNAL(featureAdded(int)), this, SLOT( featureAdded(int)));
+  connect( mLayer, SIGNAL( attributeValueChanged( int, int, const QVariant& ) ), this, SLOT( attributeValueChanged( int, int, const QVariant& ) ) );
+  connect( mLayer, SIGNAL( featureAdded( int ) ), this, SLOT( featureAdded( int ) ) );
+  connect( mLayer, SIGNAL( featureDeleted( int ) ), this, SLOT( featureDeleted( int ) ) );
 
   loadLayer();
 }
 
 bool QgsAttributeTableModel::featureAtId( int fid ) const
 {
-  return mLayer->featureAtId( fid, mFeat, false, true );
+  QgsDebugMsg( QString( "loading feature %1" ).arg( fid ) );
+
+  if ( fid == std::numeric_limits<int>::min() )
+    return false;
+  else
+    return mLayer->featureAtId( fid, mFeat, false, true );
 }
 
-#if 0
 void QgsAttributeTableModel::featureDeleted( int fid )
 {
-  QgsDebugMsg( "entered." );
+  QgsDebugMsg( QString( "deleted fid=%1 => row=%2" ).arg( fid ).arg( idToRow( fid ) ) );
 
-#ifdef QGISDEBUG
-  int idx = mIdRowMap[fid];
-  QgsDebugMsg( idx );
-  QgsDebugMsg( fid );
-#endif
+  int row = idToRow( fid );
 
+  beginRemoveRows( QModelIndex(), row, row );
+  removeRow( row );
+  endRemoveRows();
+}
+
+bool QgsAttributeTableModel::removeRows( int row, int count, const QModelIndex &parent )
+{
+  QgsDebugMsg( QString( "remove %2 rows at %1" ).arg( row ).arg( count ) );
+
+  // clean old references
+  for ( int i = row; i < row + count; i++ )
+  {
+    mIdRowMap.remove( mRowIdMap[ row ] );
+    mRowIdMap.remove( row );
+  }
+
+  // update maps
+  int n = mRowIdMap.size() + count;
+  for ( int i = row + count; i < n; i++ )
+  {
+    int id = mRowIdMap[i];
+    mIdRowMap[ id ] -= count;
+    mRowIdMap[ i-count ] = id;
+    mRowIdMap.remove( i );
+  }
+
+#ifdef QGISDEBUG
   QgsDebugMsg( "id->row" );
   QHash<int, int>::iterator it;
   for ( it = mIdRowMap.begin(); it != mIdRowMap.end(); ++it )
     QgsDebugMsg( QString( "%1->%2" ).arg( it.key() ).arg( *it ) );
 
   QgsDebugMsg( "row->id" );
-
   for ( it = mRowIdMap.begin(); it != mRowIdMap.end(); ++it )
     QgsDebugMsg( QString( "%1->%2" ).arg( it.key() ).arg( *it ) );
+#endif
 
+  return true;
 }
 
-void QgsAttributeTableModel::featureAdded( int fid )
+void QgsAttributeTableModel::featureAdded( int fid, bool newOperation )
 {
-  QgsDebugMsg( "BM feature added" );
-  ++mFeatureCount;
-  mIdRowMap.insert( fid, mFeatureCount - 1 );
-  mRowIdMap.insert( mFeatureCount - 1, fid );
-  QgsDebugMsg( QString( "map sizes:%1, %2" ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ) );
-  reload( index( 0, 0 ), index( rowCount(), columnCount() ) );
+  QgsDebugMsg( QString( "feature %1 added (%2, rows %3, ids %4)" ).arg( fid ).arg( newOperation ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ) );
+
+  int n = mRowIdMap.size();
+  if ( newOperation )
+    beginInsertRows( QModelIndex(), n, n );
+
+  mIdRowMap.insert( fid, n );
+  mRowIdMap.insert( n, fid );
+
+  if ( newOperation )
+    endInsertRows();
+
+  reload( index( rowCount() - 1, 0 ), index( rowCount() - 1, columnCount() ) );
 }
-#endif
 
 void QgsAttributeTableModel::attributeAdded( int idx )
 {
-  QgsDebugMsg( "BM attribute added" );
-  loadLayer();
-  QgsDebugMsg( QString( "map sizes:%1, %2" ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ) );
+  QgsDebugMsg( "entered." );
   reload( index( 0, 0 ), index( rowCount(), columnCount() ) );
   emit modelChanged();
 }
 
 void QgsAttributeTableModel::attributeDeleted( int idx )
 {
-  QgsDebugMsg( "BM attribute deleted" );
-  loadLayer();
-  QgsDebugMsg( QString( "map sizes:%1, %2" ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ) );
+  QgsDebugMsg( "entered." );
   reload( index( 0, 0 ), index( rowCount(), columnCount() ) );
   emit modelChanged();
 }
@@ -106,30 +133,17 @@
 void QgsAttributeTableModel::layerDeleted()
 {
   QgsDebugMsg( "entered." );
-  mIdRowMap.clear();
-  mRowIdMap.clear();
-  QgsDebugMsg( QString( "map sizes:%1, %2" ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ) );
-  reload( index( 0, 0 ), index( rowCount(), columnCount() ) );
+
+  beginRemoveRows( QModelIndex(), 0, rowCount() - 1 );
+  removeRows( 0, rowCount() );
+  endRemoveRows();
 }
 
-//TODO: check whether caching in data()/setData() doesn't cache old value
 void QgsAttributeTableModel::attributeValueChanged( int fid, int idx, const QVariant &value )
 {
-  QgsDebugMsg( "entered." );
-  reload( index( 0, 0 ), index( rowCount(), columnCount() ) );
+  setData( index( idToRow( fid ), mAttributes.indexOf( idx ) ), value, Qt::EditRole );
 }
 
-void QgsAttributeTableModel::layerModified( bool onlyGeometry )
-{
-  if ( onlyGeometry )
-    return;
-
-  loadAttributes();
-  loadLayer();
-  emit modelChanged();
-  emit headerDataChanged( Qt::Horizontal, 0, columnCount() - 1 );
-}
-
 void QgsAttributeTableModel::loadAttributes()
 {
   if ( !mLayer )
@@ -187,26 +201,25 @@
 {
   QgsDebugMsg( "entered." );
 
-  QgsFeature f;
-  bool ins = false, rm = false;
+  Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() );
 
-  int previousSize = mRowIdMap.size();
+  beginRemoveRows( QModelIndex(), 0, rowCount() - 1 );
+  removeRows( 0, rowCount() );
+  endRemoveRows();
 
-  mRowIdMap.clear();
-  mIdRowMap.clear();
+  Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() );
 
   QSettings settings;
   int behaviour = settings.value( "/qgis/attributeTableBehaviour", 0 ).toInt();
 
   if ( behaviour == 1 )
   {
-    const QgsFeatureList &features = mLayer->selectedFeatures();
-
-    for ( int i = 0; i < features.size(); ++i )
+    beginInsertRows( QModelIndex(), 0, mLayer->selectedFeatureCount() );
+    foreach( int fid, mLayer->selectedFeaturesIds() )
     {
-      mRowIdMap.insert( i, features[i].id() );
-      mIdRowMap.insert( features[i].id(), i );
+      featureAdded( fid, false );
     }
+    endInsertRows();
   }
   else
   {
@@ -219,39 +232,16 @@
 
     mLayer->select( mAttributes, rect, false );
 
+    QgsFeature f;
     for ( int i = 0; mLayer->nextFeature( f ); ++i )
     {
-      mRowIdMap.insert( i, f.id() );
-      mIdRowMap.insert( f.id(), i );
+      featureAdded( f.id() );
     }
   }
 
-  if ( previousSize < mRowIdMap.size() )
-  {
-    QgsDebugMsg( "ins" );
-    ins = true;
-    beginInsertRows( QModelIndex(), previousSize, mRowIdMap.size() - 1 );
-  }
-  else if ( previousSize > mRowIdMap.size() )
-  {
-    QgsDebugMsg( "rm" );
-    rm = true;
-    beginRemoveRows( QModelIndex(), mRowIdMap.size(), previousSize - 1 );
-  }
+  Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() );
 
-  // not needed when we have featureAdded signal
   mFieldCount = mAttributes.size();
-
-  if ( ins )
-  {
-    endInsertRows();
-    QgsDebugMsg( "end ins" );
-  }
-  else if ( rm )
-  {
-    endRemoveRows();
-    QgsDebugMsg( "end rm" );
-  }
 }
 
 void QgsAttributeTableModel::swapRows( int a, int b )
@@ -442,17 +432,7 @@
   int rowId = rowToId( index.row() );
   if ( mFeat.id() == rowId || featureAtId( rowId ) )
   {
-    int fieldId = mAttributes[ index.column()];
-
-    disconnect( mLayer, SIGNAL( layerModified( bool ) ), this, SLOT( layerModified( bool ) ) );
-
-    mLayer->beginEditCommand( tr( "Attribute changed" ) );
-    mLayer->changeAttributeValue( rowId, fieldId, value, true );
-    mLayer->endEditCommand();
-
-    mFeat.changeAttribute( fieldId, value );
-
-    connect( mLayer, SIGNAL( layerModified( bool ) ), this, SLOT( layerModified( bool ) ) );
+    mFeat.changeAttribute( mAttributes[ index.column()], value );
   }
 
   if ( !mLayer->isModified() )
@@ -509,3 +489,20 @@
 
   mLayer->actions()->doAction( action, attributes, fieldIdx( idx.column() ) );
 }
+
+void QgsAttributeTableModel::featureForm( QModelIndex &idx )
+{
+  QgsFeature f;
+  QgsAttributeMap attributes;
+
+  for ( int i = 0; i < mAttributes.size(); i++ )
+  {
+    f.changeAttribute( i, data( index( idx.row(), i ), Qt::EditRole ) );
+  }
+
+  QgsFeatureAction action( tr( "Attributes changed" ), f, mLayer, -1, this );
+  if ( mLayer->isEditable() )
+    action.editFeature();
+  else
+    action.viewFeatureForm();
+}

Modified: trunk/qgis/src/app/attributetable/qgsattributetablemodel.h
===================================================================
--- trunk/qgis/src/app/attributetable/qgsattributetablemodel.h	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/attributetable/qgsattributetablemodel.h	2010-11-21 20:09:36 UTC (rev 14729)
@@ -82,6 +82,10 @@
      */
     void reload( const QModelIndex &index1, const QModelIndex &index2 );
     /**
+     * Remove rows
+     */
+    bool removeRows( int row, int count, const QModelIndex &parent = QModelIndex() );
+    /**
      * Resets the model
      */
     void resetModel();
@@ -104,9 +108,9 @@
     int fieldIdx( int col ) const;
     /**
      * Maps row to feature id
-     * @param id row id
+     * @param row row number
      */
-    int rowToId( const int id ) const;
+    int rowToId( const int row ) const;
     /**
      * Sorts the model
      * @param column column to sort by
@@ -128,6 +132,8 @@
     /** Execute an action */
     void executeAction( int action, const QModelIndex &idx ) const;
 
+    void featureForm( QModelIndex& );
+
   signals:
     /**
      * Model has been changed
@@ -151,6 +157,7 @@
      * @param idx attribute index
      */
     virtual void attributeDeleted( int idx );
+  protected slots:
     /**
      * Launched when attribute value has been changed
      * @param fid feature id
@@ -159,15 +166,6 @@
      */
     virtual void attributeValueChanged( int fid, int idx, const QVariant &value );
     /**
-     * Launched when layer has been modified
-     * Rebuilds the model
-     * @param onlyGeometry true if only geometry has changed
-     */
-    virtual void layerModified( bool onlyGeometry = false );
-
-  protected slots:
-#if 0
-    /**
      * Launched when a feature has been deleted
      * @param fid feature id
      */
@@ -175,9 +173,9 @@
     /**
      * Launched when a feature has been added
      * @param fid feature id
+     * @parem inOperation guard insertion with beginInsertRows() / endInsertRows()
      */
-    virtual void featureAdded( int fid );
-#endif
+    virtual void featureAdded( int fid, bool inOperation = true );
     /**
      * Launched when layer has been deleted
      */

Modified: trunk/qgis/src/app/attributetable/qgsattributetableview.cpp
===================================================================
--- trunk/qgis/src/app/attributetable/qgsattributetableview.cpp	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/attributetable/qgsattributetableview.cpp	2010-11-21 20:09:36 UTC (rev 14729)
@@ -88,27 +88,32 @@
   }
 
   QgsVectorLayer *vlayer = mModel->layer();
-  if ( !vlayer || vlayer->actions()->size() == 0 )
-  {
+  if ( !vlayer )
     return;
-  }
 
   mActionPopup = new QMenu();
 
-  QAction *a = mActionPopup->addAction( tr( "Run action" ) );
-  a->setEnabled( false );
-
-  for ( int i = 0; i < vlayer->actions()->size(); i++ )
+  if ( vlayer->actions()->size() == 0 )
   {
-    const QgsAction &action = vlayer->actions()->at( i );
 
-    if ( !action.runable() )
-      continue;
+    QAction *a = mActionPopup->addAction( tr( "Run action" ) );
+    a->setEnabled( false );
 
-    QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, mModel, i, idx );
-    mActionPopup->addAction( action.name(), a, SLOT( execute() ) );
+    for ( int i = 0; i < vlayer->actions()->size(); i++ )
+    {
+      const QgsAction &action = vlayer->actions()->at( i );
+
+      if ( !action.runable() )
+        continue;
+
+      QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, mModel, i, idx );
+      mActionPopup->addAction( action.name(), a, SLOT( execute() ) );
+    }
   }
 
+  QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open form" ), this, mModel, -1, idx );
+  mActionPopup->addAction( tr( "Open form" ), a, SLOT( featureForm() ) );
+
   mActionPopup->popup( event->globalPos() );
 }
 
@@ -116,3 +121,8 @@
 {
   mModel->executeAction( mAction, mFieldIdx );
 }
+
+void QgsAttributeTableAction::featureForm()
+{
+  mModel->featureForm( mFieldIdx );
+}

Modified: trunk/qgis/src/app/attributetable/qgsattributetableview.h
===================================================================
--- trunk/qgis/src/app/attributetable/qgsattributetableview.h	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/attributetable/qgsattributetableview.h	2010-11-21 20:09:36 UTC (rev 14729)
@@ -26,7 +26,7 @@
 class QMenu;
 
 
-class QgsAttributeTableView: public QTableView
+class QgsAttributeTableView : public QTableView
 {
     Q_OBJECT
 
@@ -65,6 +65,7 @@
 
   public slots:
     void execute();
+    void featureForm();
 
   private:
     QgsAttributeTableModel *mModel;

Modified: trunk/qgis/src/app/gps/qgsgpsinformationwidget.cpp
===================================================================
--- trunk/qgis/src/app/gps/qgsgpsinformationwidget.cpp	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/gps/qgsgpsinformationwidget.cpp	2010-11-21 20:09:36 UTC (rev 14729)
@@ -29,7 +29,7 @@
 #include "qgsproject.h"
 #include "qgsapplication.h"
 #include "qgslogger.h"
-#include "qgsattributedialog.h"
+#include "qgsfeatureaction.h"
 #include "qgsgeometry.h"
 
 //for avoid intersections static method
@@ -674,45 +674,13 @@
       memcpy( &wkb[5] + sizeof( double ), &y, sizeof( double ) );
 
       f->setGeometryAndOwnership( &wkb[0], size );
-      // add the fields to the QgsFeature
-      const QgsFieldMap fields = vlayer->pendingFields();
-      for ( QgsFieldMap::const_iterator it = fields.constBegin(); it != fields.constEnd(); ++it )
-      {
-        f->addAttribute( it.key(), provider->defaultValue( it.key() ) );
-      }
 
-      vlayer->beginEditCommand( tr( "Feature added" ) );
+      QgsFeatureAction action( tr( "Feature added" ), *f, vlayer, -1, this );
+      if ( action.addFeature() )
+        mpCanvas->refresh();
 
-      // show the dialog to enter attribute values
-      QSettings settings;
-      bool isDisabledAttributeValuesDlg = settings.value( "/qgis/digitizing/disable_enter_attribute_values_dialog", false ).toBool();
-      if ( isDisabledAttributeValuesDlg )
-      {
-        QgsDebugMsg( "Adding feature to layer" );
-        vlayer->addFeature( *f );
-        vlayer->endEditCommand();
-      }
-      else
-      {
-        QgsAttributeDialog *mypDialog = new QgsAttributeDialog( vlayer, f );
-        if ( mypDialog->exec() )
-        {
-          QgsDebugMsg( "Adding feature to layer" );
-          vlayer->addFeature( *f );
-          vlayer->endEditCommand();
-        }
-        else
-        {
-          vlayer->destroyEditCommand();
-          QgsDebugMsg( "Adding feature to layer failed" );
-          delete f;
-        }
-        delete mypDialog;
-      }
-
-      mpCanvas->refresh();
+      delete f;
     }
-
   }
   else // Line or poly
   {
@@ -819,48 +787,10 @@
       return; //unknown wkbtype
     }
 
-    // add the fields to the QgsFeature
-    const QgsFieldMap fields = vlayer->pendingFields();
-    for ( QgsFieldMap::const_iterator it = fields.begin(); it != fields.end(); ++it )
-    {
-      f->addAttribute( it.key(), provider->defaultValue( it.key() ) );
-    }
+    QgsFeatureAction action( tr( "Feature added" ), *f, vlayer, -1, this );
+    if ( action.addFeature() )
+      mpCanvas->refresh();
 
-    QSettings settings;
-    bool isDisabledAttributeValuesDlg = settings.value( "/qgis/digitizing/disable_enter_attribute_values_dialog", false ).toBool();
-    if ( isDisabledAttributeValuesDlg )
-    {
-      vlayer->beginEditCommand( tr( "Feature added" ) );
-      if ( vlayer->addFeature( *f ) )
-      {
-        //add points to other features to keep topology up-to-date
-        int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 );
-        if ( topologicalEditing )
-        {
-          vlayer->addTopologicalPoints( f->geometry() );
-        }
-      }
-      vlayer->endEditCommand();
-    }
-    else
-    {
-      QgsAttributeDialog * mypDialog = new QgsAttributeDialog( vlayer, f );
-      if ( mypDialog->exec() )
-      {
-        vlayer->beginEditCommand( tr( "Feature added" ) );
-        if ( vlayer->addFeature( *f ) )
-        {
-          //add points to other features to keep topology up-to-date
-          int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 );
-          if ( topologicalEditing )
-          {
-            vlayer->addTopologicalPoints( f->geometry() );
-          }
-        }
-        vlayer->endEditCommand();
-      }
-      mypDialog->deleteLater();
-    }
     delete f;
 
     delete mpRubberBand;

Modified: trunk/qgis/src/app/qgisappinterface.cpp
===================================================================
--- trunk/qgis/src/app/qgisappinterface.cpp	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/qgisappinterface.cpp	2010-11-21 20:09:36 UTC (rev 14729)
@@ -380,73 +380,6 @@
   if ( !vlayer )
     return false;
 
-  QgsVectorDataProvider *dp = vlayer->dataProvider();
-  if ( dp )
-  {
-    // add the fields to the QgsFeature
-    const QgsFieldMap fields = vlayer->pendingFields();
-    for ( QgsFieldMap::const_iterator it = fields.constBegin(); it != fields.constEnd(); ++it )
-    {
-      if ( !f.attributeMap().contains( it.key() ) )
-        f.addAttribute( it.key(), dp->defaultValue( it.key() ) );
-    }
-  }
-
-  QgsAttributeMap src = f.attributeMap();
-
-  if ( !updateFeatureOnly && vlayer->isEditable() )
-    vlayer->beginEditCommand( tr( "Feature form edit" ) );
-
-  QgsAttributeDialog *ad = new QgsAttributeDialog( vlayer, &f );
-
-  if ( 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(), f, vlayer, i, ad->dialog() );
-      ad->dialog()->addAction( a );
-      connect( a, SIGNAL( triggered() ), a, SLOT( execute() ) );
-
-      QAbstractButton *pb = ad->dialog()->findChild<QAbstractButton *>( action.name() );
-      if ( pb )
-        connect( pb, SIGNAL( clicked() ), a, SLOT( execute() ) );
-    }
-  }
-
-  bool res = ad->exec();
-
-  if ( !updateFeatureOnly && vlayer->isEditable() )
-  {
-    if ( res )
-    {
-      const QgsAttributeMap &dst = f.attributeMap();
-      for ( QgsAttributeMap::const_iterator it = dst.begin(); it != dst.end(); it++ )
-      {
-        if ( !src.contains( it.key() ) || it.value() != src[it.key()] )
-        {
-          vlayer->changeAttributeValue( f.id(), it.key(), it.value() );
-        }
-      }
-      vlayer->endEditCommand();
-    }
-    else
-    {
-      vlayer->destroyEditCommand();
-    }
-  }
-
-  delete ad;
-
-  return res;
+  QgsFeatureAction action( tr( "Attributes changed" ), f, vlayer, -1, QgisApp::instance() );
+  return action.editFeature();
 }

Modified: trunk/qgis/src/app/qgsfeatureaction.cpp
===================================================================
--- trunk/qgis/src/app/qgsfeatureaction.cpp	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/qgsfeatureaction.cpp	2010-11-21 20:09:36 UTC (rev 14729)
@@ -18,25 +18,189 @@
 
 #include "qgsfeatureaction.h"
 #include "qgsvectorlayer.h"
+#include "qgsvectordataprovider.h"
 #include "qgsidentifyresults.h"
+#include "qgsattributedialog.h"
+#include "qgslogger.h"
 
-QgsFeatureAction::QgsFeatureAction( const QString &name, QgsIdentifyResults *results, QgsVectorLayer *vl, int action, QTreeWidgetItem *featItem )
-    : QAction( name, results )
-    , mLayer( vl )
-    , mAction( action )
-{
-  results->retrieveAttributes( featItem, mAttributes, mIdx );
-}
+#include <QPushButton>
+#include <QSettings>
 
 QgsFeatureAction::QgsFeatureAction( const QString &name, QgsFeature &f, QgsVectorLayer *layer, int action, QObject *parent )
     : QAction( name, parent )
     , mLayer( layer )
+    , mFeature( f )
     , mAction( action )
 {
-  mAttributes = f.attributeMap();
 }
 
 void QgsFeatureAction::execute()
 {
-  mLayer->actions()->doAction( mAction, mAttributes, mIdx );
+  mLayer->actions()->doAction( mAction, mFeature.attributeMap(), mIdx );
 }
+
+QgsAttributeDialog *QgsFeatureAction::newDialog()
+{
+  QgsAttributeDialog *dialog = new QgsAttributeDialog( mLayer, &mFeature );
+
+  if ( mLayer->actions()->size() == 0 )
+  {
+    dialog->dialog()->setContextMenuPolicy( Qt::ActionsContextMenu );
+
+    QAction *a = new QAction( tr( "Run actions" ), dialog->dialog() );
+    a->setEnabled( false );
+    dialog->dialog()->addAction( a );
+
+    for ( int i = 0; i < mLayer->actions()->size(); i++ )
+    {
+      const QgsAction &action = mLayer->actions()->at( i );
+
+      if ( !action.runable() )
+        continue;
+
+      QgsFeatureAction *a = new QgsFeatureAction( action.name(), mFeature, mLayer, i, dialog->dialog() );
+      dialog->dialog()->addAction( a );
+      connect( a, SIGNAL( triggered() ), a, SLOT( execute() ) );
+
+      QAbstractButton *pb = dialog->dialog()->findChild<QAbstractButton *>( action.name() );
+      if ( pb )
+        connect( pb, SIGNAL( clicked() ), a, SLOT( execute() ) );
+    }
+  }
+
+  return dialog;
+}
+
+bool QgsFeatureAction::viewFeatureForm( QgsRubberBand *rb )
+{
+  if ( !mLayer )
+    return false;
+
+  QgsAttributeDialog *dialog = newDialog();
+  dialog->setHighlight( rb );
+  dialog->show();
+
+  return true;
+}
+
+bool QgsFeatureAction::editFeature()
+{
+  bool res = false;
+
+  if ( !mLayer )
+    return res;
+
+  QgsAttributeDialog *dialog = newDialog();
+
+  if ( !mLayer->isEditable() )
+  {
+    res = dialog->exec();
+  }
+  else
+  {
+    QgsAttributeMap src = mFeature.attributeMap();
+
+    if ( dialog->exec() )
+    {
+      mLayer->beginEditCommand( text() );
+
+      const QgsAttributeMap &dst = mFeature.attributeMap();
+      for ( QgsAttributeMap::const_iterator it = dst.begin(); it != dst.end(); it++ )
+      {
+        if ( !src.contains( it.key() ) || it.value() != src[it.key()] )
+        {
+          mLayer->changeAttributeValue( mFeature.id(), it.key(), it.value() );
+        }
+      }
+
+      mLayer->endEditCommand();
+      res = true;
+    }
+    else
+    {
+      res = false;
+    }
+  }
+
+  delete dialog;
+  return res;
+}
+
+bool QgsFeatureAction::addFeature()
+{
+  if ( !mLayer || !mLayer->isEditable() )
+    return false;
+
+  QgsVectorDataProvider *provider = mLayer->dataProvider();
+
+  QSettings settings;
+  bool reuseLastValues = settings.value( "/qgis/digitizing/reuseLastValues", false ).toBool();
+  QgsDebugMsg( QString( "reuseLastValues: %1" ).arg( reuseLastValues ) );
+
+  // add the fields to the QgsFeature
+  const QgsFieldMap fields = mLayer->pendingFields();
+  for ( QgsFieldMap::const_iterator it = fields.constBegin(); it != fields.constEnd(); ++it )
+  {
+    if ( reuseLastValues && mLastUsedValues.contains( mLayer ) && mLastUsedValues[ mLayer ].contains( it.key() ) )
+    {
+      QgsDebugMsg( QString( "reusing %1 for %2" ).arg( mLastUsedValues[ mLayer ][ it.key()].toString() ).arg( it.key() ) );
+      mFeature.addAttribute( it.key(), mLastUsedValues[ mLayer ][ it.key()] );
+    }
+    else
+    {
+      mFeature.addAttribute( it.key(), provider->defaultValue( it.key() ) );
+    }
+  }
+
+  bool res = false;
+
+  mLayer->beginEditCommand( text() );
+
+  // show the dialog to enter attribute values
+  bool isDisabledAttributeValuesDlg = settings.value( "/qgis/digitizing/disable_enter_attribute_values_dialog", true ).toBool();
+  if ( isDisabledAttributeValuesDlg )
+  {
+    res = mLayer->addFeature( mFeature );
+  }
+  else
+  {
+    QgsAttributeMap origValues;
+    if ( reuseLastValues )
+      origValues = mFeature.attributeMap();
+
+    QgsAttributeDialog *dialog = newDialog();
+    if ( dialog->exec() )
+    {
+      if ( reuseLastValues )
+      {
+        for ( QgsFieldMap::const_iterator it = fields.constBegin(); it != fields.constEnd(); ++it )
+        {
+          const QgsAttributeMap &newValues = mFeature.attributeMap();
+          if ( newValues.contains( it.key() )
+               && origValues.contains( it.key() )
+               && origValues[ it.key()] != newValues[ it.key()] )
+          {
+            QgsDebugMsg( QString( "saving %1 for %2" ).arg( mLastUsedValues[ mLayer ][ it.key()].toString() ).arg( it.key() ) );
+            mLastUsedValues[ mLayer ][ it.key()] = newValues[ it.key()];
+          }
+        }
+      }
+
+      res = mLayer->addFeature( mFeature );
+    }
+    else
+    {
+      QgsDebugMsg( "Adding feature to layer failed" );
+      res = false;
+    }
+  }
+
+  if ( res )
+    mLayer->endEditCommand();
+  else
+    mLayer->destroyEditCommand();
+
+  return res;
+}
+
+QMap<QgsVectorLayer *, QgsAttributeMap> QgsFeatureAction::mLastUsedValues;

Modified: trunk/qgis/src/app/qgsfeatureaction.h
===================================================================
--- trunk/qgis/src/app/qgsfeatureaction.h	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/qgsfeatureaction.h	2010-11-21 20:09:36 UTC (rev 14729)
@@ -26,7 +26,8 @@
 
 class QgsIdentifyResults;
 class QgsVectorLayer;
-class QTreeWidgetItem;
+class QgsRubberBand;
+class QgsAttributeDialog;
 
 class QgsFeatureAction : public QAction
 {
@@ -34,16 +35,22 @@
 
   public:
     QgsFeatureAction( const QString &name, QgsFeature &f, QgsVectorLayer *vl, int action, QObject *parent );
-    QgsFeatureAction( const QString &name, QgsIdentifyResults *results, QgsVectorLayer *vl, int action, QTreeWidgetItem *featItem );
 
   public slots:
     void execute();
+    bool viewFeatureForm( QgsRubberBand *rb = 0 );
+    bool editFeature();
+    bool addFeature();
 
   private:
+    QgsAttributeDialog *newDialog();
+
     QgsVectorLayer *mLayer;
+    QgsFeature &mFeature;
     int mAction;
     int mIdx;
-    QgsAttributeMap mAttributes;
+
+    static QMap<QgsVectorLayer *, QgsAttributeMap> mLastUsedValues;
 };
 
 #endif

Modified: trunk/qgis/src/app/qgsidentifyresults.cpp
===================================================================
--- trunk/qgis/src/app/qgsidentifyresults.cpp	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/qgsidentifyresults.cpp	2010-11-21 20:09:36 UTC (rev 14729)
@@ -63,7 +63,7 @@
 // Tree hierarchy
 //
 // layer [userrole: QgsMapLayer]
-//   feature: displayfield|displayvalue [userrole: fid]
+//   feature: displayfield|displayvalue [userrole: fid, index in feature list]
 //     derived attributes (if any) [userrole: "derived"]
 //       name value
 //     actions (if any) [userrole: "actions"]
@@ -132,8 +132,8 @@
   return 0;
 }
 
-void QgsIdentifyResults::addFeature( QgsVectorLayer *vlayer, int fid,
-                                     const QgsAttributeMap &attributes,
+void QgsIdentifyResults::addFeature( QgsVectorLayer *vlayer,
+                                     const QgsFeature &f,
                                      const QMap<QString, QString> &derivedAttributes )
 {
   QTreeWidgetItem *layItem = layerItem( vlayer );
@@ -153,10 +153,12 @@
   }
 
   QTreeWidgetItem *featItem = new QTreeWidgetItem;
-  featItem->setData( 0, Qt::UserRole, fid );
+  featItem->setData( 0, Qt::UserRole, f.id() );
+  featItem->setData( 0, Qt::UserRole + 1, mFeatures.size() );
+  mFeatures << f;
   layItem->addChild( featItem );
 
-  for ( QgsAttributeMap::const_iterator it = attributes.begin(); it != attributes.end(); it++ )
+  for ( QgsAttributeMap::const_iterator it = f.attributeMap().begin(); it != f.attributeMap().end(); it++ )
   {
     QTreeWidgetItem *attrItem = new QTreeWidgetItem( QStringList() << QString::number( it.key() ) << it.value().toString() );
 
@@ -461,7 +463,8 @@
       if ( !action.runable() )
         continue;
 
-      QgsFeatureAction *a = new QgsFeatureAction( action.name(), this, vlayer, i, featItem );
+      int idx = featItem->data( 0, Qt::UserRole + 1 ).toInt();
+      QgsFeatureAction *a = new QgsFeatureAction( action.name(), mFeatures[ idx ], vlayer, i, this );
       mActionPopup->addAction( QgisApp::getThemeIcon( "/mAction.png" ), action.name(), a, SLOT( execute() ) );
     }
   }
@@ -869,71 +872,23 @@
     return;
 
   int fid = featItem->data( 0, Qt::UserRole ).toInt();
+  int idx = featItem->data( 0, Qt::UserRole + 1 ).toInt();
 
   QgsFeature f;
   if ( !vlayer->featureAtId( fid, f ) )
     return;
 
-  QgsAttributeMap src = f.attributeMap();
-
+  QgsFeatureAction action( tr( "Attribute changes" ), f, vlayer, idx, this );
   if ( vlayer->isEditable() )
-    vlayer->beginEditCommand( tr( "Attribute changed" ) );
-
-  QgsAttributeDialog *ad = new QgsAttributeDialog( vlayer, &f );
-
-  if ( 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++ )
+    if ( action.editFeature() )
     {
-      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() ) );
-
-      QAbstractButton *pb = ad->dialog()->findChild<QAbstractButton *>( action.name() );
-      if ( pb )
-        connect( pb, SIGNAL( clicked() ), a, SLOT( execute() ) );
+      mCanvas->refresh();
     }
   }
-
-  if ( vlayer->isEditable() )
-  {
-    if ( ad->exec() )
-    {
-      const QgsAttributeMap &dst = f.attributeMap();
-      for ( QgsAttributeMap::const_iterator it = dst.begin(); it != dst.end(); it++ )
-      {
-        if ( !src.contains( it.key() ) || it.value() != src[it.key()] )
-        {
-          vlayer->changeAttributeValue( f.id(), it.key(), it.value() );
-        }
-      }
-      vlayer->endEditCommand();
-    }
-    else
-    {
-      vlayer->destroyEditCommand();
-    }
-
-    delete ad;
-
-    mCanvas->refresh();
-  }
   else
   {
-    QgsRubberBand *rb = mRubberBands.take( featItem );
-    ad->setHighlight( rb );
-    ad->show();
+    action.viewFeatureForm( mRubberBands.take( featItem ) );
   }
 }
 

Modified: trunk/qgis/src/app/qgsidentifyresults.h
===================================================================
--- trunk/qgis/src/app/qgsidentifyresults.h	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/qgsidentifyresults.h	2010-11-21 20:09:36 UTC (rev 14729)
@@ -55,8 +55,8 @@
     ~QgsIdentifyResults();
 
     /** Add add feature from vector layer */
-    void addFeature( QgsVectorLayer *layer, int fid,
-                     const QgsAttributeMap &attributes,
+    void addFeature( QgsVectorLayer *layer,
+                     const QgsFeature &f,
                      const QMap< QString, QString > &derivedAttributes );
 
     /** Add add feature from other layer */
@@ -117,6 +117,7 @@
     QMenu *mActionPopup;
     QMap<QTreeWidgetItem *, QgsRubberBand * > mRubberBands;
     QgsMapCanvas *mCanvas;
+    QList<QgsFeature> mFeatures;
 
     QgsVectorLayer *vectorLayer( QTreeWidgetItem *item );
     QTreeWidgetItem *featureItem( QTreeWidgetItem *item );

Modified: trunk/qgis/src/app/qgsmaptooladdfeature.cpp
===================================================================
--- trunk/qgis/src/app/qgsmaptooladdfeature.cpp	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/qgsmaptooladdfeature.cpp	2010-11-21 20:09:36 UTC (rev 14729)
@@ -27,87 +27,24 @@
 #include "qgsvectordataprovider.h"
 #include "qgsvectorlayer.h"
 #include "qgslogger.h"
+#include "qgsfeatureaction.h"
+
 #include <QMessageBox>
 #include <QMouseEvent>
 #include <QSettings>
 
 QgsMapToolAddFeature::QgsMapToolAddFeature( QgsMapCanvas* canvas, CaptureMode tool ): QgsMapToolCapture( canvas, tool )
 {
-
 }
 
 QgsMapToolAddFeature::~QgsMapToolAddFeature()
 {
-
 }
 
 bool QgsMapToolAddFeature::addFeature( QgsVectorLayer *vlayer, QgsFeature *f )
 {
-  bool res = false;
-  QgsVectorDataProvider* provider = vlayer->dataProvider();
-
-  QSettings settings;
-  bool reuseLastValues = settings.value( "/qgis/digitizing/reuseLastValues", false ).toBool();
-  QgsDebugMsg( QString( "reuseLastValues: %1" ).arg( reuseLastValues ) );
-
-  // add the fields to the QgsFeature
-  const QgsFieldMap fields = vlayer->pendingFields();
-  for ( QgsFieldMap::const_iterator it = fields.constBegin(); it != fields.constEnd(); ++it )
-  {
-    if ( reuseLastValues && mLastUsedValues.contains( vlayer ) && mLastUsedValues[ vlayer ].contains( it.key() ) )
-    {
-      QgsDebugMsg( QString( "reusing %1 for %2" ).arg( mLastUsedValues[ vlayer ][ it.key()].toString() ).arg( it.key() ) );
-      f->addAttribute( it.key(), mLastUsedValues[ vlayer ][ it.key()] );
-    }
-    else
-    {
-      f->addAttribute( it.key(), provider->defaultValue( it.key() ) );
-    }
-  }
-
-  // show the dialog to enter attribute values
-  bool isDisabledAttributeValuesDlg = settings.value( "/qgis/digitizing/disable_enter_attribute_values_dialog", true ).toBool();
-  if ( isDisabledAttributeValuesDlg )
-  {
-    res = vlayer->addFeature( *f );
-  }
-  else
-  {
-    QgsAttributeDialog *mypDialog = new QgsAttributeDialog( vlayer, f );
-
-    QgsAttributeMap origValues;
-    if ( reuseLastValues )
-      origValues = f->attributeMap();
-
-    if ( mypDialog->exec() )
-    {
-      if ( reuseLastValues )
-      {
-        for ( QgsFieldMap::const_iterator it = fields.constBegin(); it != fields.constEnd(); ++it )
-        {
-          const QgsAttributeMap &newValues = f->attributeMap();
-          if ( newValues.contains( it.key() )
-               && origValues.contains( it.key() )
-               && origValues[ it.key()] != newValues[ it.key()] )
-          {
-            QgsDebugMsg( QString( "saving %1 for %2" ).arg( mLastUsedValues[ vlayer ][ it.key()].toString() ).arg( it.key() ) );
-            mLastUsedValues[ vlayer ][ it.key()] = newValues[ it.key()];
-          }
-        }
-      }
-
-      res = vlayer->addFeature( *f );
-    }
-    else
-    {
-      QgsDebugMsg( "Adding feature to layer failed" );
-      res = false;
-    }
-
-    mypDialog->deleteLater();
-  }
-
-  return res;
+  QgsFeatureAction action( tr( "add feature" ), *f, vlayer, -1, this );
+  return action.addFeature();
 }
 
 void QgsMapToolAddFeature::canvasReleaseEvent( QMouseEvent * e )

Modified: trunk/qgis/src/app/qgsmaptooladdfeature.h
===================================================================
--- trunk/qgis/src/app/qgsmaptooladdfeature.h	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/qgsmaptooladdfeature.h	2010-11-21 20:09:36 UTC (rev 14729)
@@ -18,7 +18,7 @@
 #include "qgsfeature.h"
 
 /**This tool adds new point/line/polygon features to already existing vector layers*/
-class QgsMapToolAddFeature: public QgsMapToolCapture
+class QgsMapToolAddFeature : public QgsMapToolCapture
 {
     Q_OBJECT
   public:
@@ -26,7 +26,5 @@
     virtual ~QgsMapToolAddFeature();
     void canvasReleaseEvent( QMouseEvent * e );
 
-  private:
     bool addFeature( QgsVectorLayer *vlayer, QgsFeature *f );
-    QMap<QgsVectorLayer *, QgsAttributeMap> mLastUsedValues;
 };

Modified: trunk/qgis/src/app/qgsmaptoolidentify.cpp
===================================================================
--- trunk/qgis/src/app/qgsmaptoolidentify.cpp	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/qgsmaptoolidentify.cpp	2010-11-21 20:09:36 UTC (rev 14729)
@@ -300,7 +300,7 @@
 
     derivedAttributes.insert( tr( "feature id" ), fid < 0 ? tr( "new feature" ) : QString::number( fid ) );
 
-    results()->addFeature( layer, fid, f_it->attributeMap(), derivedAttributes );
+    results()->addFeature( layer, *f_it, derivedAttributes );
   }
 
   QgsDebugMsg( "Feature count on identify: " + QString::number( featureCount ) );

Modified: trunk/qgis/src/app/qgsmergeattributesdialog.cpp
===================================================================
--- trunk/qgis/src/app/qgsmergeattributesdialog.cpp	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/app/qgsmergeattributesdialog.cpp	2010-11-21 20:09:36 UTC (rev 14729)
@@ -26,7 +26,12 @@
 #include <limits>
 #include <QComboBox>
 
-QgsMergeAttributesDialog::QgsMergeAttributesDialog( const QgsFeatureList& features, QgsVectorLayer* vl, QgsMapCanvas* canvas, QWidget * parent, Qt::WindowFlags f ): QDialog( parent, f ), mFeatureList( features ), mVectorLayer( vl ), mMapCanvas( canvas ), mSelectionRubberBand( 0 )
+QgsMergeAttributesDialog::QgsMergeAttributesDialog( const QgsFeatureList &features, QgsVectorLayer *vl, QgsMapCanvas *canvas, QWidget *parent, Qt::WindowFlags f )
+    : QDialog( parent, f )
+    , mFeatureList( features )
+    , mVectorLayer( vl )
+    , mMapCanvas( canvas )
+    , mSelectionRubberBand( 0 )
 {
   setupUi( this );
   createTableWidgetContents();
@@ -60,48 +65,50 @@
   {
     return;
   }
-  const QgsFieldMap& fieldMap = mVectorLayer->pendingFields();
 
   //combo box row, attributes titles, feature values and current merge results
   mTableWidget->setRowCount( mFeatureList.size() + 2 );
-  mTableWidget->setColumnCount( fieldMap.size() );
 
-  //create combo boxes
-  for ( int i = 0; i < fieldMap.size(); ++i )
+  //create combo boxes and insert attribute names
+  const QgsFieldMap& fieldMap = mVectorLayer->pendingFields();
+
+  int col = 0;
+  for ( QgsFieldMap::const_iterator fieldIt = fieldMap.constBegin();
+        fieldIt != fieldMap.constEnd();
+        ++fieldIt )
   {
-    mTableWidget->setCellWidget( 0, i, createMergeComboBox( fieldMap[i].type() ) );
-  }
+    if ( mVectorLayer->editType( fieldIt.key() ) == QgsVectorLayer::Hidden ||
+         mVectorLayer->editType( fieldIt.key() ) == QgsVectorLayer::Immutable )
+      continue;
 
-  QgsFieldMap::const_iterator fieldIt = fieldMap.constBegin();
+    mTableWidget->setColumnCount( col + 1 );
 
-  //insert attribute names
-  int col = 0;
-  for ( ; fieldIt != fieldMap.constEnd(); ++fieldIt )
-  {
+    mTableWidget->setCellWidget( 0, col, createMergeComboBox( fieldIt->type() ) );
+
     QTableWidgetItem *item = new QTableWidgetItem( fieldIt.value().name() );
     item->setData( Qt::UserRole, fieldIt.key() );
     mTableWidget->setHorizontalHeaderItem( col++, item );
   }
 
   //insert the attribute values
-  int currentRow = 1;
   QStringList verticalHeaderLabels; //the id column is in the
   verticalHeaderLabels << tr( "Id" );
 
   for ( int i = 0; i < mFeatureList.size(); ++i )
   {
     verticalHeaderLabels << QString::number( mFeatureList[i].id() );
-    QgsAttributeMap currentAttributeMap = mFeatureList[i].attributeMap();
-    QgsAttributeMap::const_iterator currentMapIt = currentAttributeMap.constBegin();
-    int col = 0;
-    for ( ; currentMapIt != currentAttributeMap.constEnd(); ++currentMapIt )
+
+    const QgsAttributeMap &attrs = mFeatureList[i].attributeMap();
+
+    for ( int j = 0; j < mTableWidget->columnCount(); j++ )
     {
-      QTableWidgetItem* attributeValItem = new QTableWidgetItem( currentMapIt.value().toString() );
+      int idx = mTableWidget->horizontalHeaderItem( j )->data( Qt::UserRole ).toInt();
+
+      QTableWidgetItem* attributeValItem = new QTableWidgetItem( attrs[idx].toString() );
       attributeValItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
-      mTableWidget->setItem( currentRow, col, attributeValItem );
-      mTableWidget->setCellWidget( currentRow, col++, QgsAttributeEditor::createAttributeEditor( mTableWidget, NULL, mVectorLayer, currentMapIt.key(), currentMapIt.value() ) );
+      mTableWidget->setItem( i + 1, j, attributeValItem );
+      mTableWidget->setCellWidget( i + 1, j, QgsAttributeEditor::createAttributeEditor( mTableWidget, NULL, mVectorLayer, idx, attrs[idx] ) );
     }
-    ++currentRow;
   }
 
   //merge
@@ -109,7 +116,7 @@
   mTableWidget->setVerticalHeaderLabels( verticalHeaderLabels );
 
   //insert currently merged values
-  for ( int i = 0; i < fieldMap.size(); ++i )
+  for ( int i = 0; i < mTableWidget->columnCount(); ++i )
   {
     refreshMergedValue( i );
   }
@@ -180,7 +187,7 @@
 
   int row = selectionList[0]->row();
 
-  if ( !mTableWidget || !mMapCanvas || !mVectorLayer || row < 1 || row >= ( mTableWidget->rowCount() ) )
+  if ( !mTableWidget || !mMapCanvas || !mVectorLayer || row < 1 || row >= mTableWidget->rowCount() )
   {
     return;
   }
@@ -476,17 +483,18 @@
   for ( int i = 0; i < mTableWidget->columnCount(); ++i )
   {
     QComboBox* currentComboBox = qobject_cast<QComboBox *>( mTableWidget->cellWidget( 0, i ) );
-    if ( currentComboBox )
-    {
-      currentComboBox->blockSignals( true );
-      currentComboBox->removeItem( currentComboBox->findText( tr( "feature %1" ).arg( featureId ) ) );
-      currentComboBox->blockSignals( false );
-    }
+    if ( !currentComboBox )
+      continue;
+
+    currentComboBox->blockSignals( true );
+    currentComboBox->removeItem( currentComboBox->findText( tr( "feature %1" ).arg( featureId ) ) );
+    currentComboBox->blockSignals( false );
   }
 
   //finally remove the feature from mFeatureList
-  QgsFeatureList::iterator f_it = mFeatureList.begin();
-  for ( ; f_it != mFeatureList.end(); ++f_it )
+  for ( QgsFeatureList::iterator f_it = mFeatureList.begin();
+        f_it != mFeatureList.end();
+        ++f_it )
   {
     if ( f_it->id() == featureId )
     {
@@ -509,29 +517,22 @@
 
 QgsAttributeMap QgsMergeAttributesDialog::mergedAttributesMap() const
 {
-  QgsAttributeMap resultMap;
   if ( mFeatureList.size() < 1 )
   {
-    return resultMap; //return empty map
+    return QgsAttributeMap();
   }
 
-  resultMap = mFeatureList[0].attributeMap();
-  int index = 0;
-  QgsAttributeMap::iterator it = resultMap.begin();
+  QgsAttributeMap resultMap;
+  for ( int i = 0; i < mTableWidget->columnCount(); i++ )
+  {
+    int idx = mTableWidget->horizontalHeaderItem( i )->data( Qt::UserRole ).toInt();
 
-  for ( ; it != resultMap.end(); ++it )
-  {
-    QTableWidgetItem* currentItem = mTableWidget->item( mFeatureList.size() + 1, index );
+    QTableWidgetItem* currentItem = mTableWidget->item( mFeatureList.size() + 1, i );
     if ( !currentItem )
-    {
       continue;
-    }
-    QString mergedString = currentItem->text();
-    QVariant newValue( mergedString );
-    resultMap.insert( it.key(), newValue );
-    ++index;
+
+    resultMap.insert( idx, currentItem->text() );
   }
 
   return resultMap;
 }
-

Modified: trunk/qgis/src/core/qgsvectorlayer.cpp
===================================================================
--- trunk/qgis/src/core/qgsvectorlayer.cpp	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/core/qgsvectorlayer.cpp	2010-11-21 20:09:36 UTC (rev 14729)
@@ -169,7 +169,7 @@
 
 QgsVectorLayer::~QgsVectorLayer()
 {
-  QgsDebugMsg( "In QgsVectorLayer destructor" );
+  QgsDebugMsg( "entered." );
 
   emit layerDeleted();
 
@@ -1813,6 +1813,8 @@
     updateExtents();
   }
 
+  emit featureAdded( f.id() );
+
   return true;
 }
 
@@ -4509,6 +4511,7 @@
   for ( ; delIt != deletedFeatureIdChange.end(); ++delIt )
   {
     mDeletedFeatureIds.insert( *delIt );
+    emit featureDeleted( *delIt );
   }
 
   // added features
@@ -4516,6 +4519,7 @@
   for ( ; addIt != addedFeatures.end(); ++addIt )
   {
     mAddedFeatures.append( *addIt );
+    emit featureAdded( addIt->id() );
   }
 
   // changed attributes
@@ -4553,9 +4557,8 @@
             break;
           }
         }
-
       }
-
+      emit attributeValueChanged( fid, attrChIt.key(), attrChIt.value().target );
     }
   }
 
@@ -4566,6 +4569,7 @@
     int attrIndex = attrIt.key();
     mAddedAttributeIds.insert( attrIndex );
     mUpdatedFields.insert( attrIndex, attrIt.value() );
+    emit attributeAdded( attrIndex );
   }
 
   // deleted attributes
@@ -4575,6 +4579,7 @@
     int attrIndex = dAttrIt.key();
     mDeletedAttributeIds.insert( attrIndex );
     mUpdatedFields.remove( attrIndex );
+    emit attributeDeleted( attrIndex );
   }
   setModified( true );
 
@@ -4598,6 +4603,7 @@
     int attrIndex = dAttrIt.key();
     mDeletedAttributeIds.remove( attrIndex );
     mUpdatedFields.insert( attrIndex, dAttrIt.value() );
+    emit attributeAdded( attrIndex );
   }
 
   // added attributes
@@ -4607,6 +4613,7 @@
     int attrIndex = attrIt.key();
     mAddedAttributeIds.remove( attrIndex );
     mUpdatedFields.remove( attrIndex );
+    emit attributeDeleted( attrIndex );
   }
 
   // geometry changes
@@ -4628,6 +4635,7 @@
   for ( ; delIt != deletedFeatureIdChange.end(); ++delIt )
   {
     mDeletedFeatureIds.remove( *delIt );
+    emit featureAdded( *delIt );
   }
 
   // added features
@@ -4640,6 +4648,7 @@
       if ( addedIt->id() == addIt->id() )
       {
         mAddedFeatures.erase( addedIt );
+        emit featureDeleted( addIt->id() );
         break; // feature was found so move to next one
       }
     }
@@ -4675,9 +4684,15 @@
             break;
           }
         }
-
       }
-      emit attributeValueChanged( fid, attrChIt.key(), attrChIt.value().original );
+      QVariant original = attrChIt.value().original;
+      if ( attrChIt.value().isFirstChange )
+      {
+        QgsFeature tmp;
+        mDataProvider->featureAtId( fid, tmp, false, QgsAttributeList() << attrChIt.key() );
+        original = tmp.attributeMap()[ attrChIt.key()];
+      }
+      emit attributeValueChanged( fid, attrChIt.key(), original );
     }
   }
   setModified( true );

Modified: trunk/qgis/src/core/qgsvectorlayer.h
===================================================================
--- trunk/qgis/src/core/qgsvectorlayer.h	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/core/qgsvectorlayer.h	2010-11-21 20:09:36 UTC (rev 14729)
@@ -623,6 +623,7 @@
     void editingStopped();
     void attributeAdded( int idx );
     void attributeDeleted( int idx );
+    void featureAdded( int fid );  // added in 1.7
     void featureDeleted( int fid );
     void layerDeleted();
 

Modified: trunk/qgis/src/ui/qgsattributetabledialog.ui
===================================================================
--- trunk/qgis/src/ui/qgsattributetabledialog.ui	2010-11-21 20:04:36 UTC (rev 14728)
+++ trunk/qgis/src/ui/qgsattributetabledialog.ui	2010-11-21 20:09:36 UTC (rev 14729)
@@ -6,7 +6,7 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>646</width>
+    <width>763</width>
     <height>570</height>
    </rect>
   </property>
@@ -313,6 +313,19 @@
       </widget>
      </item>
      <item>
+      <widget class="QToolButton" name="mAddFeature">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="toolTip">
+        <string>Add feature</string>
+       </property>
+       <property name="text">
+        <string>+</string>
+       </property>
+      </widget>
+     </item>
+     <item>
       <widget class="QToolButton" name="mOpenFieldCalculator">
        <property name="toolTip">
         <string>Open field calculator (Ctrl+I)</string>



More information about the QGIS-commit mailing list