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@10921 c8812cc2-4d05-0410-92ff-de0c093fc19c
  • Loading branch information
wonder committed Jun 14, 2009
1 parent 7fd5ba8 commit 5752dc5
Show file tree
Hide file tree
Showing 23 changed files with 383 additions and 13 deletions.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -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 5752dc5

Please sign in to comment.