Skip to content
Permalink
Browse files

[FEATURE] Added undo/redo functionality for vector layer editing.

There are undo/redo actions in Edit menu, in Advanced digitizing toolbar
and there is a new dock widget displaying undo stack of active layer.


git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@10921 c8812cc2-4d05-0410-92ff-de0c093fc19c
  • Loading branch information
wonder
wonder committed Jun 14, 2009
1 parent 9156371 commit 0da0e7deab33e40dde62ec0858e42c78136ae5f8
Binary file not shown.
Binary file not shown.
@@ -60,6 +60,7 @@ SET(QGIS_APP_SRCS
qgsshortcutsmanager.cpp
qgssinglesymboldialog.cpp
qgssnappingdialog.cpp
qgsundowidget.cpp
qgsuniquevaluedialog.cpp
qgsvectorlayerproperties.cpp

@@ -148,6 +149,7 @@ SET (QGIS_APP_MOC_HDRS
qgsvectorlayerproperties.h
qgsdbtablemodel.h
qgsspatialitetablemodel.h
qgsundowidget.h

composer/qgscomposer.h
composer/qgscomposeritemwidget.h
@@ -120,7 +120,9 @@ bool QgsAttributeTableMemoryModel::setData( const QModelIndex &index, const QVar
// QgsDebugMsg(mFeatureMap[rowToId(index.row())].id());
mFeatureMap[rowToId( index.row() )].changeAttribute( mAttributes[ index.column()], value );
// propagate back to the layer
mLayer->beginEditCommand( tr("Attribute changed") );
mLayer->changeAttributeValue( rowToId( index.row() ), mAttributes[ index.column()], value, true );
mLayer->endEditCommand();
}

if ( !mLayer->isModified() )
@@ -393,8 +393,9 @@ bool QgsAttributeTableModel::setData( const QModelIndex &index, const QVariant &
{
mLastRowId = rowToId( index.row() );
mLastRow = ( QgsAttributeMap * )( &( mFeat.attributeMap() ) );

mLayer->beginEditCommand( tr("Attribute changed") );
mLayer->changeAttributeValue( rowToId( index.row() ), mAttributes[ index.column()], value, true );
mLayer->endEditCommand();
}

if ( !mLayer->isModified() )
@@ -132,6 +132,7 @@
#include "qgsrenderer.h"
#include "qgsserversourceselect.h"
#include "qgsshortcutsmanager.h"
#include "qgsundowidget.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"
#include "ogr/qgsopenvectorlayerdialog.h"
@@ -363,6 +364,11 @@ QgisApp::QgisApp( QSplashScreen *splash, QWidget * parent, Qt::WFlags fl )
mInternalClipboard = new QgsClipboard; // create clipboard
mQgisInterface = new QgisAppInterface( this ); // create the interfce

// create undo widget
mUndoWidget = new QgsUndoWidget( NULL, mMapCanvas);
addDockWidget(Qt::LeftDockWidgetArea, mUndoWidget);
mUndoWidget->hide();

// set application's icon
setWindowIcon( QPixmap( qgis_xpm ) );

@@ -583,11 +589,6 @@ void QgisApp::createActions()
// Edit Menu Items

#if 0
mActionUndo = new QAction( tr( "&Undo" ), this );
shortcuts->registerAction( mActionUndo, tr( "Ctrl+Z" ) );
mActionUndo->setStatusTip( tr( "Undo the last operation" ) );
connect( mActionUndo, SIGNAL( triggered ), this, SLOT( undo() ) );

mActionCut = new QAction( tr( "Cu&t" ), this );
shortcuts->registerAction( mActionCut, tr( "Ctrl+X" ) );
mActionCut->setStatusTip( tr( "Cut the current selection's contents to the clipboard" ) );
@@ -604,6 +605,18 @@ void QgisApp::createActions()
connect( mActionPaste, SIGNAL( triggered ), this, SLOT( paste() ) );
#endif

mActionUndo = new QAction( getThemeIcon( "mActionUndo.png"), tr( "&Undo" ), this );
shortcuts->registerAction( mActionUndo, tr( "Ctrl+Z" ) );
mActionUndo->setStatusTip( tr( "Undo the last operation" ) );
mActionUndo->setEnabled( false );
// action connected to mUndoWidget::undo slot in setupConnections()

mActionRedo = new QAction( getThemeIcon( "mActionRedo.png"), tr( "&Redo" ), this );
shortcuts->registerAction( mActionRedo, tr( "Ctrl+Shift+Z" ) );
mActionRedo->setStatusTip( tr( "Redo the last operation" ) );
mActionRedo->setEnabled( false );
// action connected to mUndoWidget::redo slot in setupConnections()

mActionCutFeatures = new QAction( getThemeIcon( "mActionEditCut.png" ), tr( "Cut Features" ), this );
shortcuts->registerAction( mActionCutFeatures, tr( "Ctrl+X" ) );
mActionCutFeatures->setStatusTip( tr( "Cut selected features" ) );
@@ -1132,11 +1145,14 @@ void QgisApp::createMenus()
mEditMenu = menuBar()->addMenu( tr( "&Edit" ) );

#if 0
mEditMenu->addAction( mActionUndo );
mEditMenu->addAction( mActionCut );
mEditMenu->addAction( mActionCopy );
mEditMenu->addAction( mActionPaste );
#endif
mEditMenu->addAction( mActionUndo );
mEditMenu->addAction( mActionRedo );
mActionEditSeparator0 = mEditMenu->addSeparator();

mEditMenu->addAction( mActionCutFeatures );
mEditMenu->addAction( mActionCopyFeatures );
mEditMenu->addAction( mActionPasteFeatures );
@@ -1360,6 +1376,8 @@ void QgisApp::createToolBars()
mAdvancedDigitizeToolBar = addToolBar( tr( "Advanced Digitizing" ) );
mAdvancedDigitizeToolBar->setIconSize( myIconSize );
mAdvancedDigitizeToolBar->setObjectName( "Advanced Digitizing" );
mAdvancedDigitizeToolBar->addAction( mActionUndo );
mAdvancedDigitizeToolBar->addAction( mActionRedo );
mAdvancedDigitizeToolBar->addAction( mActionSimplifyFeature );
mAdvancedDigitizeToolBar->addAction( mActionAddRing );
mAdvancedDigitizeToolBar->addAction( mActionAddIsland );
@@ -1638,6 +1656,8 @@ void QgisApp::setupConnections()
mMapLegend, SLOT( addLayer( QgsMapLayer * ) ) );
connect( mMapLegend, SIGNAL( currentLayerChanged( QgsMapLayer* ) ),
this, SLOT( activateDeactivateLayerRelatedActions( QgsMapLayer* ) ) );
connect( mMapLegend, SIGNAL( currentLayerChanged( QgsMapLayer* ) ),
mUndoWidget, SLOT( layerChanged( QgsMapLayer* ) ) );


//signal when mouse moved over window (coords display in status bar)
@@ -1664,7 +1684,12 @@ void QgisApp::setupConnections()

connect( QgsProject::instance(), SIGNAL( layerLoaded( int, int ) ), this, SLOT( showProgress( int, int ) ) );

// setup undo/redo actions
connect( mActionUndo, SIGNAL( triggered() ), mUndoWidget, SLOT( undo() ) );
connect( mActionRedo, SIGNAL( triggered() ), mUndoWidget, SLOT( redo() ) );
connect( mUndoWidget, SIGNAL( undoStackChanged() ), this, SLOT(updateUndoActions()) );
}

void QgisApp::createCanvas()
{
// "theMapCanvas" used to find this canonical instance later
@@ -4083,13 +4108,14 @@ void QgisApp::deleteSelected()
return;
}


vlayer->beginEditCommand( tr("Features deleted") );
if ( !vlayer->deleteSelectedFeatures() )
{
QMessageBox::information( this, tr( "Problem deleting features" ),
tr( "A problem occured during deletion of features" ) );
}

vlayer->endEditCommand();
// notify the project we've made a change
QgsProject::instance()->dirty( true );
}
@@ -4228,6 +4254,8 @@ void QgisApp::mergeSelectedFeatures()
}
}

vl->beginEditCommand( tr("Merged features") );

//create new feature
QgsFeature newFeature;
newFeature.setGeometry(unionGeom);
@@ -4241,6 +4269,8 @@ void QgisApp::mergeSelectedFeatures()

vl->addFeature(newFeature, false);

vl->endEditCommand();;

if(mapCanvas())
{
mapCanvas()->refresh();
@@ -4361,7 +4391,9 @@ void QgisApp::editCut( QgsMapLayer * layerContainingSelection )
{
QgsFeatureList features = selectionVectorLayer->selectedFeatures();
clipboard()->replaceWithCopyOf( selectionVectorLayer->dataProvider()->fields(), features );
selectionVectorLayer->beginEditCommand( tr("Features cut") );
selectionVectorLayer->deleteSelectedFeatures();
selectionVectorLayer->endEditCommand();
}
}
}
@@ -4410,7 +4442,9 @@ void QgisApp::editPaste( QgsMapLayer * destinationLayer )

if ( pasteVectorLayer != 0 )
{
pasteVectorLayer->beginEditCommand( tr("Features pasted") );
pasteVectorLayer->addFeatures( clipboard()->copyOf() );
pasteVectorLayer->endEditCommand();
mMapCanvas->refresh();
}
}
@@ -5420,6 +5454,8 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionLayerProperties->setEnabled( false );
mActionAddToOverview->setEnabled( false );
mActionCopyFeatures->setEnabled( false );
mActionUndo->setEnabled( false );
mActionRedo->setEnabled( false );
return;
}

@@ -6139,3 +6175,20 @@ QPixmap QgisApp::getThemePixmap( const QString theName )
return QPixmap( myDefaultPath );
}
}

void QgisApp::updateUndoActions()
{
bool canUndo = false, canRedo = false;
QgsMapLayer* layer = this->activeLayer();
if (layer)
{
QgsVectorLayer* vlayer = dynamic_cast<QgsVectorLayer*>( layer );
if ( vlayer && vlayer->isEditable() )
{
canUndo = vlayer->undoStack()->canUndo();
canRedo = vlayer->undoStack()->canRedo();
}
}
mActionUndo->setEnabled( canUndo );
mActionRedo->setEnabled( canRedo );
}
@@ -56,6 +56,7 @@ class QgsPythonDialog;
class QgsPythonUtils;
class QgsRasterLayer;
class QgsRectangle;
class QgsUndoWidget;
class QgsVectorLayer;

#include <QMainWindow>
@@ -341,6 +342,8 @@ class QgisApp : public QMainWindow
//! Zoom to selected features
void zoomToSelected();

void updateUndoActions();

//! cuts selected features on the active layer to the clipboard
/**
\param layerContainingSelection The layer that the selection will be taken from
@@ -710,6 +713,9 @@ class QgisApp : public QMainWindow
QAction *mActionFileSeparator4;
QAction *mActionExit;

QAction *mActionUndo;
QAction *mActionRedo;
QAction *mActionEditSeparator0;
QAction *mActionCutFeatures;
QAction *mActionCopyFeatures;
QAction *mActionPasteFeatures;
@@ -958,6 +964,8 @@ class QgisApp : public QMainWindow

static QgisApp *smInstance;

QgsUndoWidget* mUndoWidget;

};

#endif
@@ -459,6 +459,7 @@ void QgsMapToolAddFeature::canvasReleaseEvent( QMouseEvent * e )
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
@@ -468,12 +469,14 @@ void QgsMapToolAddFeature::canvasReleaseEvent( QMouseEvent * e )
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
@@ -483,6 +486,7 @@ void QgsMapToolAddFeature::canvasReleaseEvent( QMouseEvent * e )
vlayer->addTopologicalPoints( f->geometry() );
}
}
vlayer->endEditCommand();
}
delete mypDialog;
}
@@ -101,8 +101,9 @@ void QgsMapToolAddIsland::canvasReleaseEvent( QMouseEvent * e )

//close polygon
mCaptureList.push_back( *mCaptureList.begin() );

vlayer->beginEditCommand( tr("Island added") );
int errorCode = vlayer->addIsland( mCaptureList );
vlayer->endEditCommand();
QString errorMessage;

if ( errorCode != 0 )
@@ -81,7 +81,9 @@ void QgsMapToolAddRing::canvasReleaseEvent( QMouseEvent * e )
//close polygon
mCaptureList.push_back( *mCaptureList.begin() );

vlayer->beginEditCommand( tr("Ring added") );
int addRingReturnCode = vlayer->addRing( mCaptureList );
vlayer->endEditCommand();
if ( addRingReturnCode != 0 )
{
QString errorMessage;
@@ -122,10 +122,12 @@ void QgsMapToolAddVertex::canvasReleaseEvent( QMouseEvent * e )

//and change the feature points
QList<QgsSnappingResult>::iterator sr_it = mRecentSnappingResults.begin();
vlayer->beginEditCommand( tr("Added vertex") );
for ( ; sr_it != mRecentSnappingResults.end(); ++sr_it )
{
vlayer->insertVertex( snappedPointLayerCoord.x(), snappedPointLayerCoord.y(), sr_it->snappedAtGeometry, sr_it->afterVertexNr );
}
vlayer->endEditCommand();
}
}

@@ -106,7 +106,9 @@ void QgsMapToolDeletePart::deletePart( int fId, int beforeVertexNr, QgsVectorLay

if ( g->deletePart( partNum ) )
{
vlayer->beginEditCommand( tr("Part of multipart feature deleted") );
vlayer->changeGeometry( fId, g );
vlayer->endEditCommand();
mCanvas->refresh();
}
else
@@ -111,7 +111,9 @@ void QgsMapToolDeleteRing::deleteRing( int fId, int beforeVertexNr, QgsVectorLay

if ( g->deleteRing( ringNum, partNum ) )
{
vlayer->beginEditCommand( tr("Ring deleted") );
vlayer->changeGeometry( fId, g );
vlayer->endEditCommand();
mCanvas->refresh();
}

@@ -79,10 +79,12 @@ void QgsMapToolDeleteVertex::canvasReleaseEvent( QMouseEvent * e )
if ( vlayer && mRecentSnappingResults.size() > 0 )
{
QList<QgsSnappingResult>::iterator sr_it = mRecentSnappingResults.begin();
vlayer->beginEditCommand( tr("Vertex deleted") );
for ( ; sr_it != mRecentSnappingResults.end(); ++sr_it )
{
vlayer->deleteVertex( sr_it->snappedAtGeometry, sr_it->snappedVertexNr );
}
vlayer->endEditCommand();
}

mCanvas->refresh();
@@ -489,17 +489,25 @@ void QgsMapToolIdentify::editFeature( QgsFeature &f )

QgsAttributeMap src = f.attributeMap();

layer->beginEditCommand( tr("Attribute changed") );
QgsAttributeDialog *ad = new QgsAttributeDialog( layer, &f );
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()] )
{
layer->changeAttributeValue( f.id(), it.key(), it.value() );
}
}
layer->endEditCommand();
}
else
{
layer->destroyEditCommand();
}

delete ad;
mCanvas->refresh();
}
@@ -137,12 +137,12 @@ void QgsMapToolMoveFeature::canvasReleaseEvent( QMouseEvent * e )

double dx = stopPointLayerCoords.x() - startPointLayerCoords.x();
double dy = stopPointLayerCoords.y() - startPointLayerCoords.y();

vlayer->beginEditCommand( tr("Feature moved") );
vlayer->translateFeature( mMovedFeature, dx, dy );

delete mRubberBand;
mRubberBand = 0;
mCanvas->refresh();
vlayer->endEditCommand();
}

//! called when map tool is being deactivated

0 comments on commit 0da0e7d

Please sign in to comment.
You can’t perform that action at this time.