[QGIS Commit] r14786 - in trunk/qgis: python/gui src/app/composer src/core src/core/composer src/gui

svn_qgis at osgeo.org svn_qgis at osgeo.org
Mon Nov 29 10:30:19 EST 2010


Author: mhugent
Date: 2010-11-29 07:30:19 -0800 (Mon, 29 Nov 2010)
New Revision: 14786

Added:
   trunk/qgis/src/core/composer/qgsaddremoveitemcommand.cpp
   trunk/qgis/src/core/composer/qgsaddremoveitemcommand.h
   trunk/qgis/src/core/composer/qgscomposeritemcommand.cpp
   trunk/qgis/src/core/composer/qgscomposeritemcommand.h
Modified:
   trunk/qgis/python/gui/qgscomposerview.sip
   trunk/qgis/src/app/composer/qgscomposer.cpp
   trunk/qgis/src/app/composer/qgscomposer.h
   trunk/qgis/src/app/composer/qgscomposerarrowwidget.cpp
   trunk/qgis/src/app/composer/qgscomposerarrowwidget.h
   trunk/qgis/src/app/composer/qgscomposeritemwidget.cpp
   trunk/qgis/src/app/composer/qgscomposerlabelwidget.cpp
   trunk/qgis/src/app/composer/qgscomposerlabelwidget.h
   trunk/qgis/src/app/composer/qgscomposerlegendwidget.cpp
   trunk/qgis/src/app/composer/qgscomposerlegendwidget.h
   trunk/qgis/src/app/composer/qgscomposermapwidget.cpp
   trunk/qgis/src/app/composer/qgscomposermapwidget.h
   trunk/qgis/src/app/composer/qgscomposerpicturewidget.cpp
   trunk/qgis/src/app/composer/qgscomposerpicturewidget.h
   trunk/qgis/src/app/composer/qgscomposerscalebarwidget.cpp
   trunk/qgis/src/app/composer/qgscomposershapewidget.cpp
   trunk/qgis/src/app/composer/qgscomposershapewidget.h
   trunk/qgis/src/app/composer/qgscomposertablewidget.cpp
   trunk/qgis/src/app/composer/qgscomposertablewidget.h
   trunk/qgis/src/core/CMakeLists.txt
   trunk/qgis/src/core/composer/qgscomposerarrow.cpp
   trunk/qgis/src/core/composer/qgscomposerattributetable.cpp
   trunk/qgis/src/core/composer/qgscomposeritem.cpp
   trunk/qgis/src/core/composer/qgscomposeritem.h
   trunk/qgis/src/core/composer/qgscomposeritemgroup.cpp
   trunk/qgis/src/core/composer/qgscomposerlabel.cpp
   trunk/qgis/src/core/composer/qgscomposerlegend.cpp
   trunk/qgis/src/core/composer/qgscomposermap.cpp
   trunk/qgis/src/core/composer/qgscomposermap.h
   trunk/qgis/src/core/composer/qgscomposerpicture.cpp
   trunk/qgis/src/core/composer/qgscomposerpicture.h
   trunk/qgis/src/core/composer/qgscomposershape.cpp
   trunk/qgis/src/core/composer/qgscomposition.cpp
   trunk/qgis/src/core/composer/qgscomposition.h
   trunk/qgis/src/gui/qgscomposerview.cpp
   trunk/qgis/src/gui/qgscomposerview.h
Log:
Initial implementation of composer undo / redo

Modified: trunk/qgis/python/gui/qgscomposerview.sip
===================================================================
--- trunk/qgis/python/gui/qgscomposerview.sip	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/python/gui/qgscomposerview.sip	2010-11-29 15:30:19 UTC (rev 14786)
@@ -80,8 +80,8 @@
     
 
   public slots:
-    /**For QgsComposerItemGroup to send its signals to QgsComposer (or other classes that keep track of input widgets)*/
-    void sendItemRemovedSignal( QgsComposerItem* item );
+    /**Casts object to the proper subclass type and calls corresponding itemAdded signal*/
+    void sendItemAddedSignal( QgsComposerItem* item );
 
   signals:
     /**Is emitted when selected item changed. If 0, no item is selected*/

Modified: trunk/qgis/src/app/composer/qgscomposer.cpp
===================================================================
--- trunk/qgis/src/app/composer/qgscomposer.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposer.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -65,13 +65,13 @@
 #include <QSvgGenerator>
 #include <QToolBar>
 #include <QToolButton>
-//#include <QUndoView>
+#include <QUndoView>
 
 
 
 
 
-QgsComposer::QgsComposer( QgisApp *qgis, const QString& title ): QMainWindow(), mTitle( title )
+QgsComposer::QgsComposer( QgisApp *qgis, const QString& title ): QMainWindow(), mTitle( title ), mUndoView( 0 )
 {
   setupUi( this );
   setWindowTitle( mTitle );
@@ -217,8 +217,8 @@
   mCompositionNameComboBox->insertItem( 0, tr( "Map 1" ) );
 
   //undo widget
-  /*QUndoView* undoWidget = new QUndoView( mComposition->undoStack(), this );
-  mOptionsTabWidget->addTab( undoWidget, tr( "Command history" ) );*/
+  mUndoView = new QUndoView( mComposition->undoStack(), this );
+  mOptionsTabWidget->addTab( mUndoView, tr( "Command history" ) );
 
   // Create size grip (needed by Mac OS X for QMainWindow if QStatusBar is not visible)
   mSizeGrip = new QSizeGrip( this );
@@ -1330,6 +1330,11 @@
   mComposition->sortZList();
   mView->setComposition( mComposition );
 
+  if ( mUndoView )
+  {
+    mUndoView->setStack( mComposition->undoStack() );
+  }
+
   setSelectionTool();
 }
 
@@ -1446,7 +1451,7 @@
     return;
   }
 
-  delete( it.key() );
+  //the item itself is not deleted here (usually, this is done in the destructor of QgsAddRemoveItemCommand)
   delete( it.value() );
   mItemWidgetMap.remove( it.key() );
 }

Modified: trunk/qgis/src/app/composer/qgscomposer.h
===================================================================
--- trunk/qgis/src/app/composer/qgscomposer.h	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposer.h	2010-11-29 15:30:19 UTC (rev 14786)
@@ -42,6 +42,7 @@
 class QResizeEvent;
 class QFile;
 class QSizeGrip;
+class QUndoView;
 
 /** \ingroup MapComposer
  * \brief A gui for composing a printable map.
@@ -229,7 +230,7 @@
     /**Adds a composer table to the item/widget map and creates a configuration widget*/
     void addComposerTable( QgsComposerAttributeTable* table );
 
-    /**Removes item from the item/widget map and deletes the configuration widget*/
+    /**Removes item from the item/widget map and deletes the configuration widget. Does not delete the item itself*/
     void deleteItem( QgsComposerItem* item );
 
     /**Shows the configuration widget for a composer item*/
@@ -306,6 +307,8 @@
 
     //! Page & Printer Setup
     QPrinter mPrinter;
+
+    QUndoView* mUndoView;
 };
 
 #endif

Modified: trunk/qgis/src/app/composer/qgscomposerarrowwidget.cpp
===================================================================
--- trunk/qgis/src/app/composer/qgscomposerarrowwidget.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposerarrowwidget.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -39,6 +39,11 @@
   toolBox->addItem( itemPropertiesWidget, tr( "General options" ) );
 
   setGuiElementValues();
+
+  if ( arrow )
+  {
+    connect( arrow, SIGNAL( itemChanged() ), this, SLOT( setGuiElementValues() ) );
+  }
 }
 
 QgsComposerArrowWidget::~QgsComposerArrowWidget()
@@ -53,8 +58,10 @@
     return;
   }
 
+  mArrow->beginCommand( tr( "Arrow outline width" ), QgsComposerMergeCommand::ArrowOutlineWidth );
   mArrow->setOutlineWidth( d );
   mArrow->update();
+  mArrow->endCommand();
 }
 
 void QgsComposerArrowWidget::on_mArrowHeadWidthSpinBox_valueChanged( double d )
@@ -64,8 +71,10 @@
     return;
   }
 
+  mArrow->beginCommand( tr( "Arrowhead width" ), QgsComposerMergeCommand::ArrowHeadWidth );
   mArrow->setArrowHeadWidth( d );
   mArrow->update();
+  mArrow->endCommand();
 }
 
 void QgsComposerArrowWidget::on_mArrowColorButton_clicked()
@@ -82,8 +91,10 @@
 #endif
   if ( newColor.isValid() )
   {
+    mArrow->beginCommand( tr( "Arrow color changed" ) );
     mArrow->setArrowColor( newColor );
     mArrow->update();
+    mArrow->endCommand();
   }
 }
 
@@ -143,8 +154,10 @@
 {
   if ( mArrow && toggled )
   {
+    mArrow->beginCommand( tr( "Arrow marker changed" ) );
     mArrow->setMarkerMode( QgsComposerArrow::DefaultMarker );
     mArrow->update();
+    mArrow->endCommand();
   }
 }
 
@@ -152,8 +165,10 @@
 {
   if ( mArrow && toggled )
   {
+    mArrow->beginCommand( tr( "Arrow marker changed" ) );
     mArrow->setMarkerMode( QgsComposerArrow::NoMarker );
     mArrow->update();
+    mArrow->endCommand();
   }
 }
 
@@ -162,15 +177,18 @@
   enableSvgInputElements( toggled );
   if ( mArrow && toggled )
   {
+    mArrow->beginCommand( tr( "Arrow marker changed" ) );
     mArrow->setMarkerMode( QgsComposerArrow::SVGMarker );
     mArrow->update();
+    mArrow->endCommand();
   }
 }
 
-void QgsComposerArrowWidget::on_mStartMarkerLineEdit_textChanged( const QString & text )
+void QgsComposerArrowWidget::on_mStartMarkerLineEdit_editingFinished( const QString & text )
 {
   if ( mArrow )
   {
+    mArrow->beginCommand( tr( "Arrow start marker" ) );
     QFileInfo fi( text );
     if ( fi.exists() )
     {
@@ -181,13 +199,15 @@
       mArrow->setStartMarker( "" );
     }
     mArrow->update();
+    mArrow->endCommand();
   }
 }
 
-void QgsComposerArrowWidget::on_mEndMarkerLineEdit_textChanged( const QString & text )
+void QgsComposerArrowWidget::on_mEndMarkerLineEdit_editingFinished( const QString & text )
 {
   if ( mArrow )
   {
+    mArrow->beginCommand( tr( "Arrow start marker" ) );
     QFileInfo fi( text );
     if ( fi.exists() )
     {
@@ -198,6 +218,7 @@
       mArrow->setEndMarker( "" );
     }
     mArrow->update();
+    mArrow->endCommand();
   }
 }
 
@@ -207,7 +228,9 @@
   QString svgFileName = QFileDialog::getOpenFileName( 0, tr( "Start marker svg file" ), fi.dir().absolutePath() );
   if ( !svgFileName.isNull() )
   {
+    mArrow->beginCommand( tr( "Arrow start marker" ) );
     mStartMarkerLineEdit->setText( svgFileName );
+    mArrow->endCommand();
   }
 }
 
@@ -217,6 +240,8 @@
   QString svgFileName = QFileDialog::getOpenFileName( 0, tr( "End marker svg file" ), fi.dir().absolutePath() );
   if ( !svgFileName.isNull() )
   {
+    mArrow->beginCommand( tr( "Arrow end marker" ) );
     mEndMarkerLineEdit ->setText( svgFileName );
+    mArrow->endCommand();
   }
 }

Modified: trunk/qgis/src/app/composer/qgscomposerarrowwidget.h
===================================================================
--- trunk/qgis/src/app/composer/qgscomposerarrowwidget.h	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposerarrowwidget.h	2010-11-29 15:30:19 UTC (rev 14786)
@@ -33,7 +33,7 @@
     QgsComposerArrow* mArrow;
 
     void blockAllSignals( bool block );
-    void setGuiElementValues();
+
     QButtonGroup* mRadioButtonGroup;
 
     /**Enables / disables the SVG line inputs*/
@@ -46,10 +46,12 @@
     void on_mDefaultMarkerRadioButton_toggled( bool toggled );
     void on_mNoMarkerRadioButton_toggled( bool toggled );
     void on_mSvgMarkerRadioButton_toggled( bool toggled );
-    void on_mStartMarkerLineEdit_textChanged( const QString & text );
-    void on_mEndMarkerLineEdit_textChanged( const QString & text );
+    void on_mStartMarkerLineEdit_editingFinished( const QString & text );
+    void on_mEndMarkerLineEdit_editingFinished( const QString & text );
     void on_mStartMarkerToolButton_clicked();
     void on_mEndMarkerToolButton_clicked();
+
+    void setGuiElementValues();
 };
 
 #endif // QGSCOMPOSERARROWWIDGET_H

Modified: trunk/qgis/src/app/composer/qgscomposeritemwidget.cpp
===================================================================
--- trunk/qgis/src/app/composer/qgscomposeritemwidget.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposeritemwidget.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -52,12 +52,14 @@
     return; //dialog canceled
   }
 
+  mItem->beginCommand( tr( "Frame color changed" ) );
   QPen thePen;
   thePen.setColor( newFrameColor );
   thePen.setWidthF( mOutlineWidthSpinBox->value() );
 
   mItem->setPen( thePen );
   mItem->update();
+  mItem->endCommand();
 }
 
 void QgsComposerItemWidget::on_mBackgroundColorButton_clicked()
@@ -73,6 +75,7 @@
     return; //dialog canceled
   }
 
+  mItem->beginCommand( tr( "Background color changed" ) );
   newBackgroundColor.setAlpha( mOpacitySlider->value() );
   mItem->setBrush( QBrush( QColor( newBackgroundColor ), Qt::SolidPattern ) );
   //if the item is a composer map, we need to regenerate the map image
@@ -83,6 +86,7 @@
     cm->cache();
   }
   mItem->update();
+  mItem->endCommand();
 }
 
 void QgsComposerItemWidget::on_mOpacitySlider_sliderReleased()
@@ -93,11 +97,13 @@
   }
   int value = mOpacitySlider->value();
 
+  mItem->beginCommand( tr( "Item opacity changed" ) );
   QBrush itemBrush = mItem->brush();
   QColor brushColor = itemBrush.color();
   brushColor.setAlpha( value );
   mItem->setBrush( QBrush( brushColor ) );
   mItem->update();
+  mItem->endCommand();
 }
 
 void QgsComposerItemWidget::on_mOutlineWidthSpinBox_valueChanged( double d )
@@ -107,9 +113,11 @@
     return;
   }
 
+  mItem->beginCommand( tr( "Item outline width" ), QgsComposerMergeCommand::ItemOutlineWidth );
   QPen itemPen = mItem->pen();
   itemPen.setWidthF( d );
   mItem->setPen( itemPen );
+  mItem->endCommand();
 }
 
 void QgsComposerItemWidget::on_mFrameCheckBox_stateChanged( int state )
@@ -119,6 +127,7 @@
     return;
   }
 
+  mItem->beginCommand( tr( "Item frame toggled" ) );
   if ( state == Qt::Checked )
   {
     mItem->setFrame( true );
@@ -128,6 +137,7 @@
     mItem->setFrame( false );
   }
   mItem->update();
+  mItem->endCommand();
 }
 
 void QgsComposerItemWidget::setValuesForGuiElements()
@@ -165,6 +175,14 @@
     return;
   }
 
+  mItem->beginCommand( tr( "Item position changed" ) );
   QgsItemPositionDialog d( mItem, 0 );
-  d.exec();
+  if ( d.exec() == QDialog::Accepted )
+  {
+    mItem->endCommand();
+  }
+  else
+  {
+    mItem->cancelCommand();
+  }
 }

Modified: trunk/qgis/src/app/composer/qgscomposerlabelwidget.cpp
===================================================================
--- trunk/qgis/src/app/composer/qgscomposerlabelwidget.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposerlabelwidget.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -32,14 +32,8 @@
 
   if ( mComposerLabel )
   {
-    mTextEdit->setText( mComposerLabel->text() );
-    mMarginDoubleSpinBox->setValue( mComposerLabel->margin() );
-    mTopRadioButton->setChecked( mComposerLabel->vAlign() == Qt::AlignTop );
-    mMiddleRadioButton->setChecked( mComposerLabel->vAlign() == Qt::AlignVCenter );
-    mBottomRadioButton->setChecked( mComposerLabel->vAlign() == Qt::AlignBottom );
-    mLeftRadioButton->setChecked( mComposerLabel->hAlign() == Qt::AlignLeft );
-    mCenterRadioButton->setChecked( mComposerLabel->hAlign() == Qt::AlignHCenter );
-    mRightRadioButton->setChecked( mComposerLabel->hAlign() == Qt::AlignRight );
+    setGuiElementValues();
+    connect( mComposerLabel, SIGNAL( itemChanged() ), this, SLOT( setGuiElementValues() ) );
   }
 }
 
@@ -47,8 +41,10 @@
 {
   if ( mComposerLabel )
   {
+    mComposerLabel->beginCommand( tr( "Label text changed" ), QgsComposerMergeCommand::ComposerLabelSetText );
     mComposerLabel->setText( mTextEdit->toPlainText() );
     mComposerLabel->update();
+    mComposerLabel->endCommand();
   }
 }
 
@@ -65,8 +61,10 @@
 #endif
     if ( ok )
     {
+      mComposerLabel->beginCommand( tr( "Label font changed" ) );
       mComposerLabel->setFont( newFont );
       mComposerLabel->update();
+      mComposerLabel->endCommand();
     }
   }
 }
@@ -75,8 +73,10 @@
 {
   if ( mComposerLabel )
   {
+    mComposerLabel->beginCommand( tr( "Label margin changed" ) );
     mComposerLabel->setMargin( d );
     mComposerLabel->update();
+    mComposerLabel->endCommand();
   }
 }
 
@@ -91,15 +91,19 @@
   {
     return;
   }
+  mComposerLabel->beginCommand( tr( "Label font changed" ) );
   mComposerLabel->setFontColor( newColor );
+  mComposerLabel->endCommand();
 }
 
 void QgsComposerLabelWidget::on_mCenterRadioButton_clicked()
 {
   if ( mComposerLabel )
   {
+    mComposerLabel->beginCommand( tr( "Label alignment changed" ) );
     mComposerLabel->setHAlign( Qt::AlignHCenter );
     mComposerLabel->update();
+    mComposerLabel->endCommand();
   }
 }
 
@@ -107,8 +111,10 @@
 {
   if ( mComposerLabel )
   {
+    mComposerLabel->beginCommand( tr( "Label alignment changed" ) );
     mComposerLabel->setHAlign( Qt::AlignRight );
     mComposerLabel->update();
+    mComposerLabel->endCommand();
   }
 }
 
@@ -116,8 +122,10 @@
 {
   if ( mComposerLabel )
   {
+    mComposerLabel->beginCommand( tr( "Label alignment changed" ) );
     mComposerLabel->setHAlign( Qt::AlignLeft );
     mComposerLabel->update();
+    mComposerLabel->endCommand();
   }
 }
 
@@ -125,8 +133,10 @@
 {
   if ( mComposerLabel )
   {
+    mComposerLabel->beginCommand( tr( "Label alignment changed" ) );
     mComposerLabel->setVAlign( Qt::AlignTop );
     mComposerLabel->update();
+    mComposerLabel->endCommand();
   }
 }
 
@@ -134,8 +144,10 @@
 {
   if ( mComposerLabel )
   {
+    mComposerLabel->beginCommand( tr( "Label alignment changed" ) );
     mComposerLabel->setVAlign( Qt::AlignBottom );
     mComposerLabel->update();
+    mComposerLabel->endCommand();
   }
 }
 
@@ -143,7 +155,35 @@
 {
   if ( mComposerLabel )
   {
+    mComposerLabel->beginCommand( tr( "Label alignment changed" ) );
     mComposerLabel->setVAlign( Qt::AlignVCenter );
     mComposerLabel->update();
+    mComposerLabel->endCommand();
   }
 }
+
+void QgsComposerLabelWidget::setGuiElementValues()
+{
+  blockAllSignals( true );
+  mTextEdit->setText( mComposerLabel->text() );
+  mMarginDoubleSpinBox->setValue( mComposerLabel->margin() );
+  mTopRadioButton->setChecked( mComposerLabel->vAlign() == Qt::AlignTop );
+  mMiddleRadioButton->setChecked( mComposerLabel->vAlign() == Qt::AlignVCenter );
+  mBottomRadioButton->setChecked( mComposerLabel->vAlign() == Qt::AlignBottom );
+  mLeftRadioButton->setChecked( mComposerLabel->hAlign() == Qt::AlignLeft );
+  mCenterRadioButton->setChecked( mComposerLabel->hAlign() == Qt::AlignHCenter );
+  mRightRadioButton->setChecked( mComposerLabel->hAlign() == Qt::AlignRight );
+  blockAllSignals( false );
+}
+
+void QgsComposerLabelWidget::blockAllSignals( bool block )
+{
+  mTextEdit->blockSignals( block );
+  mMarginDoubleSpinBox->blockSignals( block );
+  mTopRadioButton->blockSignals( block );
+  mMiddleRadioButton->blockSignals( block );
+  mBottomRadioButton->blockSignals( block );
+  mLeftRadioButton->blockSignals( block );
+  mCenterRadioButton->blockSignals( block );
+  mRightRadioButton->blockSignals( block );
+}

Modified: trunk/qgis/src/app/composer/qgscomposerlabelwidget.h
===================================================================
--- trunk/qgis/src/app/composer/qgscomposerlabelwidget.h	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposerlabelwidget.h	2010-11-29 15:30:19 UTC (rev 14786)
@@ -43,8 +43,13 @@
     void on_mBottomRadioButton_clicked();
     void on_mMiddleRadioButton_clicked();
 
+  private slots:
+    void setGuiElementValues();
+
   private:
     QgsComposerLabel* mComposerLabel;
+
+    void blockAllSignals( bool block );
 };
 
 #endif //QGSCOMPOSERLABELWIDGET

Modified: trunk/qgis/src/app/composer/qgscomposerlegendwidget.cpp
===================================================================
--- trunk/qgis/src/app/composer/qgscomposerlegendwidget.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposerlegendwidget.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -45,6 +45,7 @@
   mItemTreeView->setDragDropMode( QAbstractItemView::InternalMove );
 
   setGuiElements();
+  connect( mItemTreeView, SIGNAL( itemChanged() ), this, SLOT( setGuiElements() ) );
 }
 
 QgsComposerLegendWidget::QgsComposerLegendWidget(): mLegend( 0 )
@@ -81,9 +82,11 @@
 {
   if ( mLegend )
   {
+    mLegend->beginCommand( tr( "Legend title changed" ), QgsComposerMergeCommand::ComposerLegendText );
     mLegend->setTitle( text );
     mLegend->adjustBoxSize();
     mLegend->update();
+    mLegend->endCommand();
   }
 }
 
@@ -91,9 +94,11 @@
 {
   if ( mLegend )
   {
+    mLegend->beginCommand( tr( "Legend symbol width" ), QgsComposerMergeCommand::LegendSymbolWidth );
     mLegend->setSymbolWidth( d );
     mLegend->adjustBoxSize();
     mLegend->update();
+    mLegend->endCommand();
   }
 }
 
@@ -101,9 +106,11 @@
 {
   if ( mLegend )
   {
+    mLegend->beginCommand( tr( "Legend symbol height" ), QgsComposerMergeCommand::LegendSymbolHeight );
     mLegend->setSymbolHeight( d );
     mLegend->adjustBoxSize();
     mLegend->update();
+    mLegend->endCommand();
   }
 }
 
@@ -111,9 +118,11 @@
 {
   if ( mLegend )
   {
+    mLegend->beginCommand( tr( "Legend layer space" ), QgsComposerMergeCommand::LegendLayerSpace );
     mLegend->setLayerSpace( d );
     mLegend->adjustBoxSize();
     mLegend->update();
+    mLegend->endCommand();
   }
 }
 
@@ -121,9 +130,11 @@
 {
   if ( mLegend )
   {
+    mLegend->beginCommand( tr( "Legend symbol space" ), QgsComposerMergeCommand::LegendSymbolSpace );
     mLegend->setSymbolSpace( d );
     mLegend->adjustBoxSize();
     mLegend->update();
+    mLegend->endCommand();
   }
 }
 
@@ -131,9 +142,11 @@
 {
   if ( mLegend )
   {
+    mLegend->beginCommand( tr( "Legend icon label space" ), QgsComposerMergeCommand::LegendIconSymbolSpace );
     mLegend->setIconLabelSpace( d );
     mLegend->adjustBoxSize();
     mLegend->update();
+    mLegend->endCommand();
   }
 }
 
@@ -150,9 +163,11 @@
 #endif
     if ( ok )
     {
+      mLegend->beginCommand( tr( "Title font changed" ) );
       mLegend->setTitleFont( newFont );
       mLegend->adjustBoxSize();
       mLegend->update();
+      mLegend->endCommand();
     }
   }
 }
@@ -170,9 +185,11 @@
 #endif
     if ( ok )
     {
+      mLegend->beginCommand( tr( "Legend group font changed" ) );
       mLegend->setGroupFont( newFont );
       mLegend->adjustBoxSize();
       mLegend->update();
+      mLegend->endCommand();
     }
   }
 }
@@ -190,9 +207,11 @@
 #endif
     if ( ok )
     {
+      mLegend->beginCommand( tr( "Legend layer font changed" ) );
       mLegend->setLayerFont( newFont );
       mLegend->adjustBoxSize();
       mLegend->update();
+      mLegend->endCommand();
     }
   }
 }
@@ -210,9 +229,11 @@
 #endif
     if ( ok )
     {
+      mLegend->beginCommand( tr( "Legend item font changed" ) );
       mLegend->setItemFont( newFont );
       mLegend->adjustBoxSize();
       mLegend->update();
+      mLegend->endCommand();
     }
   }
 }
@@ -222,14 +243,21 @@
 {
   if ( mLegend )
   {
+    mLegend->beginCommand( tr( "Legend box space" ), QgsComposerMergeCommand::LegendBoxSpace );
     mLegend->setBoxSpace( d );
     mLegend->adjustBoxSize();
     mLegend->update();
+    mLegend->endCommand();
   }
 }
 
 void QgsComposerLegendWidget::on_mMoveDownToolButton_clicked()
 {
+  if ( !mLegend )
+  {
+    return;
+  }
+
   QStandardItemModel* itemModel = qobject_cast<QStandardItemModel *>( mItemTreeView->model() );
   if ( !itemModel )
   {
@@ -251,6 +279,7 @@
     return;
   }
 
+  mLegend->beginCommand( "Moved legend item down" );
   QModelIndex parentIndex = currentIndex.parent();
   QList<QStandardItem*> itemToMove;
   QList<QStandardItem*> youngerSiblingItem;
@@ -272,14 +301,17 @@
   }
 
   mItemTreeView->setCurrentIndex( itemModel->indexFromItem( itemToMove.at( 0 ) ) );
-  if ( mLegend )
-  {
-    mLegend->update();
-  }
+  mLegend->update();
+  mLegend->endCommand();
 }
 
 void QgsComposerLegendWidget::on_mMoveUpToolButton_clicked()
 {
+  if ( !mLegend )
+  {
+    return;
+  }
+
   QStandardItemModel* itemModel = qobject_cast<QStandardItemModel *>( mItemTreeView->model() );
   if ( !itemModel )
   {
@@ -292,6 +324,7 @@
     return;
   }
 
+  mLegend->beginCommand( "Moved legend item up" );
   //is there an older sibling?
   int row = currentIndex.row();
   QModelIndex olderSibling = currentIndex.sibling( row - 1, 0 );
@@ -323,14 +356,17 @@
   }
 
   mItemTreeView->setCurrentIndex( itemModel->indexFromItem( itemToMove.at( 0 ) ) );
-  if ( mLegend )
-  {
-    mLegend->update();
-  }
+  mLegend->update();
+  mLegend->endCommand();
 }
 
 void QgsComposerLegendWidget::on_mRemoveToolButton_clicked()
 {
+  if ( !mLegend )
+  {
+    return;
+  }
+
   QStandardItemModel* itemModel = qobject_cast<QStandardItemModel *>( mItemTreeView->model() );
   if ( !itemModel )
   {
@@ -345,16 +381,20 @@
 
   QModelIndex parentIndex = currentIndex.parent();
 
+  mLegend->beginCommand( "Legend item removed" );
   itemModel->removeRow( currentIndex.row(), parentIndex );
-  if ( mLegend )
-  {
-    mLegend->adjustBoxSize();
-    mLegend->update();
-  }
+  mLegend->adjustBoxSize();
+  mLegend->update();
+  mLegend->endCommand();
 }
 
 void QgsComposerLegendWidget::on_mEditPushButton_clicked()
 {
+  if ( !mLegend )
+  {
+    return;
+  }
+
   QStandardItemModel* itemModel = qobject_cast<QStandardItemModel *>( mItemTreeView->model() );
   if ( !itemModel )
   {
@@ -379,15 +419,20 @@
   {
     currentItem->setText( itemDialog.itemText() );
   }
-  if ( mLegend )
-  {
-    mLegend->adjustBoxSize();
-    mLegend->update();
-  }
+
+  mLegend->beginCommand( tr( "Legend item edited" ) );
+  mLegend->adjustBoxSize();
+  mLegend->update();
+  mLegend->endCommand();
 }
 
 void QgsComposerLegendWidget::on_mUpdatePushButton_clicked()
 {
+  if ( !mLegend )
+  {
+    return;
+  }
+
   //get current item
   QStandardItemModel* itemModel = qobject_cast<QStandardItemModel *>( mItemTreeView->model() );
   if ( !itemModel )
@@ -408,12 +453,14 @@
     return;
   }
 
+  mLegend->beginCommand( tr( "Legend updated" ) );
   if ( mLegend->model() )
   {
     mLegend->model()->updateItem( currentItem );
   }
   mLegend->update();
   mLegend->adjustBoxSize();
+  mLegend->endCommand();
 }
 
 void QgsComposerLegendWidget::on_mUpdateAllPushButton_clicked()
@@ -425,8 +472,10 @@
 {
   if ( mLegend && mLegend->model() )
   {
+    mLegend->beginCommand( tr( "Legend group added" ) );
     mLegend->model()->addGroup();
     mLegend->update();
+    mLegend->endCommand();
   }
 }
 
@@ -434,6 +483,7 @@
 {
   if ( mLegend )
   {
+    mLegend->beginCommand( tr( "Legend updated" ) );
     QgisApp* app = QgisApp::instance();
     if ( !app )
     {
@@ -456,5 +506,6 @@
     QgsAppLegendInterface legendIface( app->legend() );
     QList< GroupLayerInfo > groupInfo = legendIface.groupLayerRelationship();
     mLegend->model()->setLayerSetAndGroups( layerIdList, groupInfo );
+    mLegend->endCommand();
   }
 }

Modified: trunk/qgis/src/app/composer/qgscomposerlegendwidget.h
===================================================================
--- trunk/qgis/src/app/composer/qgscomposerlegendwidget.h	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposerlegendwidget.h	2010-11-29 15:30:19 UTC (rev 14786)
@@ -60,11 +60,14 @@
     void on_mUpdateAllPushButton_clicked();
     void on_mAddGroupButton_clicked();
 
-  private:
-    QgsComposerLegendWidget();
+  private slots:
     /**Sets GUI according to state of mLegend*/
     void setGuiElements();
 
+  private:
+    QgsComposerLegendWidget();
+
+
     QgsComposerLegend* mLegend;
 };
 

Modified: trunk/qgis/src/app/composer/qgscomposermapwidget.cpp
===================================================================
--- trunk/qgis/src/app/composer/qgscomposermapwidget.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposermapwidget.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -56,7 +56,7 @@
   mAnnotationDirectionComboBox->insertItem( 2, tr( "Boundary direction" ) );
   if ( composerMap )
   {
-    connect( composerMap, SIGNAL( extentChanged() ), this, SLOT( updateSettingsNoSignals() ) );
+    connect( composerMap, SIGNAL( itemChanged() ), this, SLOT( setGuiElementValues() ) );
   }
 
   updateGuiElements();
@@ -81,7 +81,10 @@
     QTransform composerMapTransform = mComposerMap->transform();
 
     QRectF newRect( composerMapTransform.dx(), composerMapTransform.dy(), newWidth, composerMapRect.height() );
+
+    mComposerMap->beginCommand( tr( "Change item width" ) );
     mComposerMap->setSceneRect( newRect );
+    mComposerMap->endCommand();
   }
 }
 
@@ -99,7 +102,9 @@
     QTransform composerMapTransform = mComposerMap->transform();
 
     QRectF newRect( composerMapTransform.dx(), composerMapTransform.dy(), composerMapRect.width(), newHeight );
+    mComposerMap->beginCommand( tr( "Change item height" ) );
     mComposerMap->setSceneRect( newRect );
+    mComposerMap->endCommand();
   }
 }
 
@@ -151,7 +156,9 @@
     return;
   }
 
+  mComposerMap->beginCommand( tr( "Map scale changed" ) );
   mComposerMap->setNewScale( scaleDenominator );
+  mComposerMap->endCommand();
 }
 
 void QgsComposerMapWidget::on_mRotationSpinBox_valueChanged( int value )
@@ -161,7 +168,9 @@
     return;
   }
 
+  mComposerMap->beginCommand( tr( "Map rotation changed" ), QgsComposerMergeCommand::ComposerMapRotation );
   mComposerMap->setMapRotation( value );
+  mComposerMap->endCommand();
   mComposerMap->cache();
   mComposerMap->update();
 }
@@ -200,7 +209,9 @@
       mYMinLineEdit->setText( QString::number( newExtent.yMinimum() ) );
       mYMaxLineEdit->setText( QString::number( newExtent.yMaximum() ) );
 
+      mComposerMap->beginCommand( tr( "Map extent changed" ) );
       mComposerMap->setNewExtent( newExtent );
+      mComposerMap->endCommand();
     }
   }
 }
@@ -225,7 +236,7 @@
   updateComposerExtentFromGui();
 }
 
-void QgsComposerMapWidget::updateSettingsNoSignals()
+void QgsComposerMapWidget::setGuiElementValues()
 {
   mHeightLineEdit->blockSignals( true );
   mWidthLineEdit->blockSignals( true );
@@ -398,7 +409,9 @@
   if ( !conversionSuccess ) {return;}
 
   QgsRectangle newExtent( xmin, ymin, xmax, ymax );
+  mComposerMap->beginCommand( tr( "Map extent changed" ) );
   mComposerMap->setNewExtent( newExtent );
+  mComposerMap->endCommand();
 }
 
 void QgsComposerMapWidget::blockAllSignals( bool b )
@@ -479,12 +492,14 @@
     return;
   }
 
+  mComposerMap->beginCommand( tr( "Canvas items toggled" ) );
   mComposerMap->setDrawCanvasItems( state == Qt::Checked );
   mUpdatePreviewButton->setEnabled( false ); //prevent crashes because of many button clicks
   mComposerMap->setCacheUpdated( false );
   mComposerMap->cache();
   mComposerMap->update();
   mUpdatePreviewButton->setEnabled( true );
+  mComposerMap->endCommand();
 }
 
 void QgsComposerMapWidget::on_mGridCheckBox_toggled( bool state )
@@ -494,6 +509,7 @@
     return;
   }
 
+  mComposerMap->beginCommand( tr( "Grid checkbox toggled" ) );
   if ( state )
   {
     mComposerMap->setGridEnabled( true );
@@ -504,6 +520,7 @@
   }
   mComposerMap->updateBoundingRect();
   mComposerMap->update();
+  mComposerMap->endCommand();
 }
 
 void QgsComposerMapWidget::on_mIntervalXSpinBox_editingFinished()
@@ -512,9 +529,12 @@
   {
     return;
   }
+
+  mComposerMap->beginCommand( tr( "Grid interval changed" ) );
   mComposerMap->setGridIntervalX( mIntervalXSpinBox->value() );
   mComposerMap->updateBoundingRect();
   mComposerMap->update();
+  mComposerMap->endCommand();
 }
 
 void QgsComposerMapWidget::on_mIntervalYSpinBox_editingFinished()
@@ -523,9 +543,11 @@
   {
     return;
   }
+  mComposerMap->beginCommand( tr( "Grid interval changed" ) );
   mComposerMap->setGridIntervalY( mIntervalYSpinBox->value() );
   mComposerMap->updateBoundingRect();
   mComposerMap->update();
+  mComposerMap->endCommand();
 }
 
 void QgsComposerMapWidget::on_mOffsetXSpinBox_editingFinished()
@@ -534,9 +556,11 @@
   {
     return;
   }
+  mComposerMap->beginCommand( tr( "Grid offset changed" ) );
   mComposerMap->setGridOffsetX( mOffsetXSpinBox->value() );
   mComposerMap->updateBoundingRect();
   mComposerMap->update();
+  mComposerMap->endCommand();
 }
 
 void QgsComposerMapWidget::on_mOffsetYSpinBox_editingFinished()
@@ -545,9 +569,11 @@
   {
     return;
   }
+  mComposerMap->beginCommand( tr( "Grid offset changed" ) );
   mComposerMap->setGridOffsetY( mOffsetYSpinBox->value() );
   mComposerMap->updateBoundingRect();
   mComposerMap->update();
+  mComposerMap->endCommand();
 }
 
 void QgsComposerMapWidget::on_mLineWidthSpinBox_valueChanged( double d )
@@ -556,8 +582,10 @@
   {
     return;
   }
+  mComposerMap->beginCommand( tr( "Grid pen changed" ) );
   mComposerMap->setGridPenWidth( d );
   mComposerMap->update();
+  mComposerMap->endCommand();
 }
 
 void QgsComposerMapWidget::on_mLineColorButton_clicked()
@@ -569,8 +597,10 @@
   QColor newColor = QColorDialog::getColor( mLineColorButton->color() );
   if ( newColor.isValid() )
   {
+    mComposerMap->beginCommand( tr( "Grid pen changed" ) );
     mLineColorButton->setColor( newColor );
     mComposerMap->setGridPenColor( newColor );
+    mComposerMap->endCommand();
   }
   mComposerMap->update();
 }
@@ -590,7 +620,9 @@
   {
     mComposerMap->setGridStyle( QgsComposerMap::Solid );
   }
+  mComposerMap->beginCommand( tr( "Grid type changed" ) );
   mComposerMap->update();
+  mComposerMap->endCommand();
 }
 
 void QgsComposerMapWidget::on_mCrossWidthSpinBox_valueChanged( double d )
@@ -600,8 +632,10 @@
     return;
   }
 
+  mComposerMap->beginCommand( tr( "Grid cross width changed" ) );
   mComposerMap->setCrossLength( d );
   mComposerMap->update();
+  mComposerMap->endCommand();
 }
 
 void QgsComposerMapWidget::on_mAnnotationFontButton_clicked()
@@ -620,9 +654,11 @@
 #endif
   if ( ok )
   {
+    mComposerMap->beginCommand( tr( "Annotation font changed" ) );
     mComposerMap->setGridAnnotationFont( newFont );
     mComposerMap->updateBoundingRect();
     mComposerMap->update();
+    mComposerMap->endCommand();
   }
 }
 
@@ -632,9 +668,11 @@
   {
     return;
   }
+  mComposerMap->beginCommand( tr( "Annotation distance changed" ), QgsComposerMergeCommand::ComposerMapAnnotationDistance );
   mComposerMap->setAnnotationFrameDistance( d );
   mComposerMap->updateBoundingRect();
   mComposerMap->update();
+  mComposerMap->endCommand();
 }
 
 void QgsComposerMapWidget::on_mAnnotationPositionComboBox_currentIndexChanged( const QString& text )
@@ -644,6 +682,7 @@
     return;
   }
 
+  mComposerMap->beginCommand( tr( "Annotation position changed" ) );
   if ( text == tr( "Inside frame" ) )
   {
     mComposerMap->setGridAnnotationPosition( QgsComposerMap::InsideMapFrame );
@@ -654,6 +693,7 @@
   }
   mComposerMap->updateBoundingRect();
   mComposerMap->update();
+  mComposerMap->endCommand();
 }
 
 void QgsComposerMapWidget::on_mDrawAnnotationCheckBox_stateChanged( int state )
@@ -663,6 +703,7 @@
     return;
   }
 
+  mComposerMap->beginCommand( tr( "Annotation toggled" ) );
   if ( state == Qt::Checked )
   {
     mComposerMap->setShowGridAnnotation( true );
@@ -673,6 +714,7 @@
   }
   mComposerMap->updateBoundingRect();
   mComposerMap->update();
+  mComposerMap->endCommand();
 }
 
 void QgsComposerMapWidget::on_mAnnotationDirectionComboBox_currentIndexChanged( const QString& text )
@@ -682,6 +724,7 @@
     return;
   }
 
+  mComposerMap->beginCommand( tr( "Changed annotation direction" ) );
   if ( text == tr( "Horizontal" ) )
   {
     mComposerMap->setGridAnnotationDirection( QgsComposerMap::Horizontal );
@@ -700,6 +743,7 @@
   }
   mComposerMap->updateBoundingRect();
   mComposerMap->update();
+  mComposerMap->endCommand();
 }
 
 void QgsComposerMapWidget::on_mCoordinatePrecisionSpinBox_valueChanged( int value )
@@ -708,7 +752,9 @@
   {
     return;
   }
+  mComposerMap->beginCommand( tr( "Changed annotation precision" ) );
   mComposerMap->setGridAnnotationPrecision( value );
   mComposerMap->updateBoundingRect();
   mComposerMap->update();
+  mComposerMap->endCommand();
 }

Modified: trunk/qgis/src/app/composer/qgscomposermapwidget.h
===================================================================
--- trunk/qgis/src/app/composer/qgscomposermapwidget.h	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposermapwidget.h	2010-11-29 15:30:19 UTC (rev 14786)
@@ -66,9 +66,11 @@
     void on_mAnnotationDirectionComboBox_currentIndexChanged( const QString& text );
     void on_mCoordinatePrecisionSpinBox_valueChanged( int value );
 
-    /**Updates width and height without notify the composer map (to avoid infinite recursion)*/
-    void updateSettingsNoSignals();
+  private slots:
 
+    /**Sets the GUI elements to the values of mPicture*/
+    void setGuiElementValues();
+
   private:
     QgsComposerMap* mComposerMap;
 

Modified: trunk/qgis/src/app/composer/qgscomposerpicturewidget.cpp
===================================================================
--- trunk/qgis/src/app/composer/qgscomposerpicturewidget.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposerpicturewidget.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -46,7 +46,7 @@
 
   //add preview icons
   addStandardDirectoriesToPreview();
-  connect( mPicture, SIGNAL( settingsChanged() ), this, SLOT( setGuiElementValues() ) );
+  connect( mPicture, SIGNAL( itemChanged() ), this, SLOT( setGuiElementValues() ) );
   connect( mPicture, SIGNAL( rotationChanged( double ) ), this, SLOT( setGuiElementValues() ) );
 }
 
@@ -88,8 +88,10 @@
   //pass file path to QgsComposerPicture
   if ( mPicture )
   {
+    mPicture->beginCommand( tr( "Picture changed" ) );
     mPicture->setPictureFile( filePath );
     mPicture->update();
+    mPicture->endCommand();
   }
 }
 
@@ -108,8 +110,10 @@
       return;
     }
 
+    mPicture->beginCommand( tr( "Picture changed" ) );
     mPicture->setPictureFile( filePath );
     mPicture->update();
+    mPicture->endCommand();
   }
 }
 
@@ -123,8 +127,10 @@
     double newWidth = mWidthLineEdit->text().toDouble( &conversionOk );
     if ( conversionOk )
     {
+      mPicture->beginCommand( tr( "Picture width changed" ) );
       QRectF newSceneRect( mPicture->transform().dx(), mPicture->transform().dy(), newWidth, pictureRect.height() );
       mPicture->setSceneRect( newSceneRect );
+      mPicture->endCommand();
     }
   }
 }
@@ -139,8 +145,10 @@
     double newHeight = mHeightLineEdit->text().toDouble( &conversionOk );
     if ( conversionOk )
     {
+      mPicture->beginCommand( tr( "Picture height changed" ) );
       QRectF newSceneRect( mPicture->transform().dx(), mPicture->transform().dy(), pictureRect.width(), newHeight );
       mPicture->setSceneRect( newSceneRect );
+      mPicture->endCommand();
     }
   }
 }
@@ -149,8 +157,10 @@
 {
   if ( mPicture )
   {
+    mPicture->beginCommand( tr( "Picture rotation changed" ), QgsComposerMergeCommand::ComposerPictureRotation );
     mPicture->setRotation( d );
     mPicture->update();
+    mPicture->endCommand();
   }
 }
 
@@ -162,9 +172,11 @@
   }
 
   QString absoluteFilePath = current->data( Qt::UserRole ).toString();
+  mPicture->beginCommand( tr( "Picture changed" ) );
   mPicture->setPictureFile( absoluteFilePath );
   mPictureLineEdit->setText( absoluteFilePath );
   mPicture->update();
+  mPicture->endCommand();
 }
 
 void QgsComposerPictureWidget::on_mAddDirectoryButton_clicked()
@@ -225,6 +237,7 @@
     return;
   }
 
+  mPicture->beginCommand( tr( "Rotation synchronisatione toggled" ) );
   if ( state == Qt::Unchecked )
   {
     mPicture->setRotationMap( -1 );
@@ -239,10 +252,12 @@
       return;
     }
     int composerId = mComposerMapComboBox->itemData( currentItemIndex, Qt::UserRole ).toInt();
+
     mPicture->setRotationMap( composerId );
     mRotationSpinBox->setEnabled( false );
     mComposerMapComboBox->setEnabled( true );
   }
+  mPicture->endCommand();
 }
 
 void QgsComposerPictureWidget::showEvent( QShowEvent * event )
@@ -287,8 +302,10 @@
   {
     return;
   }
+  mPicture->beginCommand( tr( "Rotation map changed" ) );
   mPicture->setRotationMap( id );
   mPicture->update();
+  mPicture->endCommand();
 }
 
 void QgsComposerPictureWidget::refreshMapComboBox()

Modified: trunk/qgis/src/app/composer/qgscomposerpicturewidget.h
===================================================================
--- trunk/qgis/src/app/composer/qgscomposerpicturewidget.h	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposerpicturewidget.h	2010-11-29 15:30:19 UTC (rev 14786)
@@ -45,6 +45,7 @@
     void on_mRotationFromComposerMapCheckBox_stateChanged( int state );
     void on_mComposerMapComboBox_activated( const QString & text );
 
+  private slots:
     /**Sets the GUI elements to the values of mPicture*/
     void setGuiElementValues();
 

Modified: trunk/qgis/src/app/composer/qgscomposerscalebarwidget.cpp
===================================================================
--- trunk/qgis/src/app/composer/qgscomposerscalebarwidget.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposerscalebarwidget.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -124,10 +124,10 @@
   }
 
   //set it to scale bar
+  mComposerScaleBar->beginCommand( tr( "Scalebar map changed" ) );
   mComposerScaleBar->setComposerMap( composerMap );
-
-  //update scale bar
   mComposerScaleBar->update();
+  mComposerScaleBar->endCommand();
 }
 
 void QgsComposerScaleBarWidget::setGuiElements()
@@ -172,10 +172,12 @@
     return;
   }
 
+  mComposerScaleBar->beginCommand( tr( "Scalebar line width" ), QgsComposerMergeCommand::ScaleBarLineWidth );
   QPen newPen( QColor( 0, 0, 0 ) );
   newPen.setWidthF( d );
   mComposerScaleBar->setPen( newPen );
   mComposerScaleBar->update();
+  mComposerScaleBar->endCommand();
 }
 
 void QgsComposerScaleBarWidget::on_mSegmentSizeSpinBox_valueChanged( double d )
@@ -185,8 +187,10 @@
     return;
   }
 
+  mComposerScaleBar->beginCommand( tr( "Scalebar segment size" ), QgsComposerMergeCommand::ScaleBarSegmentSize );
   mComposerScaleBar->setNumUnitsPerSegment( d );
   mComposerScaleBar->update();
+  mComposerScaleBar->endCommand();
 }
 
 void QgsComposerScaleBarWidget::on_mSegmentsLeftSpinBox_valueChanged( int i )
@@ -196,8 +200,10 @@
     return;
   }
 
+  mComposerScaleBar->beginCommand( tr( "Scalebar segments left" ), QgsComposerMergeCommand::ScaleBarSegmentsLeft );
   mComposerScaleBar->setNumSegmentsLeft( i );
   mComposerScaleBar->update();
+  mComposerScaleBar->endCommand();
 }
 
 void QgsComposerScaleBarWidget::on_mNumberOfSegmentsSpinBox_valueChanged( int i )
@@ -207,8 +213,10 @@
     return;
   }
 
+  mComposerScaleBar->beginCommand( tr( "Scalebar n segments" ), QgsComposerMergeCommand::ScaleBarNSegments );
   mComposerScaleBar->setNumSegments( i );
   mComposerScaleBar->update();
+  mComposerScaleBar->endCommand();
 }
 
 void QgsComposerScaleBarWidget::on_mHeightSpinBox_valueChanged( int i )
@@ -217,8 +225,10 @@
   {
     return;
   }
+  mComposerScaleBar->beginCommand( tr( "Scalebar height changed" ), QgsComposerMergeCommand::ScaleBarHeight );
   mComposerScaleBar->setHeight( i );
   mComposerScaleBar->update();
+  mComposerScaleBar->endCommand();
 }
 
 void QgsComposerScaleBarWidget::on_mFontButton_clicked()
@@ -233,7 +243,9 @@
   QFont newFont = QFontDialog::getFont( &dialogAccepted, oldFont, 0 );
   if ( dialogAccepted )
   {
+    mComposerScaleBar->beginCommand( tr( "Scalebar font changed" ) );
     mComposerScaleBar->setFont( newFont );
+    mComposerScaleBar->endCommand();
   }
   mComposerScaleBar->update();
 }
@@ -253,9 +265,11 @@
     return;
   }
 
+  mComposerScaleBar->beginCommand( tr( "Scalebar color changed" ) );
   QBrush newBrush( newColor );
   mComposerScaleBar->setBrush( newBrush );
   mComposerScaleBar->update();
+  mComposerScaleBar->endCommand();
 }
 
 void QgsComposerScaleBarWidget::on_mUnitLabelLineEdit_textChanged( const QString& text )
@@ -265,8 +279,10 @@
     return;
   }
 
+  mComposerScaleBar->beginCommand( tr( "Scalebar unit text" ), QgsComposerMergeCommand::ScaleBarUnitText );
   mComposerScaleBar->setUnitLabeling( text );
   mComposerScaleBar->update();
+  mComposerScaleBar->endCommand();
 }
 
 void QgsComposerScaleBarWidget::on_mMapUnitsPerBarUnitSpinBox_valueChanged( double d )
@@ -276,8 +292,10 @@
     return;
   }
 
+  mComposerScaleBar->beginCommand( tr( "Scalebar map units per segment" ), QgsComposerMergeCommand::ScaleBarMapUnitsSegment );
   mComposerScaleBar->setNumMapUnitsPerScaleBarUnit( d );
   mComposerScaleBar->update();
+  mComposerScaleBar->endCommand();
 }
 
 void QgsComposerScaleBarWidget::on_mStyleComboBox_currentIndexChanged( const QString& text )
@@ -287,6 +305,7 @@
     return;
   }
 
+  mComposerScaleBar->beginCommand( tr( "Scalebar style changed" ) );
   QString untranslatedStyleName;
   if ( text == tr( "Single Box" ) )
   {
@@ -318,6 +337,7 @@
   }
   mComposerScaleBar->setStyle( untranslatedStyleName );
   mComposerScaleBar->update();
+  mComposerScaleBar->endCommand();
 }
 
 void QgsComposerScaleBarWidget::on_mLabelBarSpaceSpinBox_valueChanged( double d )
@@ -327,8 +347,10 @@
     return;
   }
 
+  mComposerScaleBar->beginCommand( tr( "Scalebar label bar space" ), QgsComposerMergeCommand::ScaleBarLabelBarSize );
   mComposerScaleBar->setLabelBarSpace( d );
   mComposerScaleBar->update();
+  mComposerScaleBar->endCommand();
 }
 
 void QgsComposerScaleBarWidget::on_mBoxSizeSpinBox_valueChanged( double d )
@@ -338,8 +360,10 @@
     return;
   }
 
+  mComposerScaleBar->beginCommand( tr( "Scalebar box content space" ), QgsComposerMergeCommand::ScaleBarBoxContentSpace );
   mComposerScaleBar->setBoxContentSpace( d );
   mComposerScaleBar->update();
+  mComposerScaleBar->endCommand();
 }
 
 void QgsComposerScaleBarWidget::blockMemberSignals( bool block )

Modified: trunk/qgis/src/app/composer/qgscomposershapewidget.cpp
===================================================================
--- trunk/qgis/src/app/composer/qgscomposershapewidget.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposershapewidget.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -38,6 +38,8 @@
   setGuiElementValues();
 
   blockAllSignals( false );
+
+  connect( mShapeComboBox, SIGNAL( itemChanged() ), this, SLOT( setGuiElementValues() ) );
 }
 
 QgsComposerShapeWidget::~QgsComposerShapeWidget()
@@ -61,6 +63,8 @@
   {
     return;
   }
+
+  blockAllSignals( true );
   mOutlineWidthSpinBox->setValue( mComposerShape->lineWidth() );
   mRotationSpinBox->setValue( mComposerShape->rotation() );
   if ( mComposerShape->shapeType() == QgsComposerShape::Ellipse )
@@ -86,14 +90,17 @@
     mTransparentCheckBox->setCheckState( Qt::Unchecked );
     mFillColorButton->setEnabled( true );
   }
+  blockAllSignals( false );
 }
 
 void QgsComposerShapeWidget::on_mRotationSpinBox_valueChanged( int val )
 {
   if ( mComposerShape )
   {
+    mComposerShape->beginCommand( tr( "Shape rotation changed" ), QgsComposerMergeCommand::ShapeRotation );
     mComposerShape->setRotation( val );
     mComposerShape->update();
+    mComposerShape->endCommand();
   }
 }
 
@@ -104,6 +111,7 @@
     return;
   }
 
+  mComposerShape->beginCommand( tr( "Shape type changed" ) );
   if ( text == tr( "Ellipse" ) )
   {
     mComposerShape->setShapeType( QgsComposerShape::Ellipse );
@@ -117,6 +125,7 @@
     mComposerShape->setShapeType( QgsComposerShape::Triangle );
   }
   mComposerShape->update();
+  mComposerShape->endCommand();
 }
 
 void QgsComposerShapeWidget::on_mOutlineColorButton_clicked()
@@ -133,8 +142,10 @@
 #endif
   if ( newColor.isValid() )
   {
+    mComposerShape->beginCommand( tr( "Shape outline color" ) );
     mComposerShape->setOutlineColor( newColor );
     mComposerShape->update();
+    mComposerShape->endCommand();
   }
 }
 
@@ -144,8 +155,10 @@
   {
     return;
   }
+  mComposerShape->beginCommand( tr( "Shape outline width" ), QgsComposerMergeCommand::ShapeOutlineWidth );
   mComposerShape->setLineWidth( d );
   mComposerShape->update();
+  mComposerShape->endCommand();
 }
 
 void QgsComposerShapeWidget::on_mTransparentCheckBox_stateChanged( int state )
@@ -155,6 +168,7 @@
     return;
   }
 
+  mComposerShape->beginCommand( tr( "Shape transparency toggled" ) );
   if ( state == Qt::Checked )
   {
     mComposerShape->setTransparentFill( true );
@@ -166,6 +180,7 @@
     mFillColorButton->setEnabled( true );
   }
   mComposerShape->update();
+  mComposerShape->endCommand();
 }
 
 void QgsComposerShapeWidget::on_mFillColorButton_clicked()
@@ -182,7 +197,9 @@
 #endif
   if ( newColor.isValid() )
   {
+    mComposerShape->beginCommand( tr( "Shape fill color" ) );
     mComposerShape->setFillColor( newColor );
     mComposerShape->update();
+    mComposerShape->endCommand();
   }
 }

Modified: trunk/qgis/src/app/composer/qgscomposershapewidget.h
===================================================================
--- trunk/qgis/src/app/composer/qgscomposershapewidget.h	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposershapewidget.h	2010-11-29 15:30:19 UTC (rev 14786)
@@ -35,8 +35,6 @@
 
     /**Blocks / unblocks the signal of all GUI elements*/
     void blockAllSignals( bool block );
-    /**Sets the GUI elements to the currentValues of mComposerShape*/
-    void setGuiElementValues();
 
   private slots:
     void on_mShapeComboBox_currentIndexChanged( const QString& text );
@@ -45,6 +43,9 @@
     void on_mTransparentCheckBox_stateChanged( int state );
     void on_mFillColorButton_clicked();
     void on_mRotationSpinBox_valueChanged( int val );
+
+    /**Sets the GUI elements to the currentValues of mComposerShape*/
+    void setGuiElementValues();
 };
 
 #endif // QGSCOMPOSERSHAPEWIDGET_H

Modified: trunk/qgis/src/app/composer/qgscomposertablewidget.cpp
===================================================================
--- trunk/qgis/src/app/composer/qgscomposertablewidget.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposertablewidget.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -71,6 +71,7 @@
   if ( mComposerTable )
   {
     QObject::connect( mComposerTable, SIGNAL( maximumNumerOfFeaturesChanged( int ) ), this, SLOT( setMaximumNumberOfFeatures( int ) ) );
+    QObject::connect( mComposerTable, SIGNAL( itemChanged ), this, SLOT( updateGuiElements() ) );
   }
 }
 
@@ -100,8 +101,10 @@
     QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( ml );
     if ( vl )
     {
+      mComposerTable->beginCommand( tr( "Table layer changed" ) );
       mComposerTable->setVectorLayer( vl );
       mComposerTable->update();
+      mComposerTable->endCommand();
     }
   }
 }
@@ -117,10 +120,12 @@
   if ( d.exec() == QDialog::Accepted )
   {
     //change displayAttributes and aliases
+    mComposerTable->beginCommand( tr( "Table attribute settings" ) );
     mComposerTable->setDisplayAttributes( d.enabledAttributes() );
     mComposerTable->setFieldAliasMap( d.aliasMap() );
     mComposerTable->setSortAttributes( d.attributeSorting() );
     mComposerTable->update();
+    mComposerTable->endCommand();
   }
 }
 
@@ -141,8 +146,10 @@
   const QgsComposition* tableComposition = mComposerTable->composition();
   if ( tableComposition )
   {
+    mComposerTable->beginCommand( tr( "Table map changed" ) );
     mComposerTable->setComposerMap( tableComposition->getComposerMapById( mapId ) );
     mComposerTable->update();
+    mComposerTable->endCommand();
   }
 }
 
@@ -153,8 +160,10 @@
     return;
   }
 
+  mComposerTable->beginCommand( tr( "Table maximum columns" ), QgsComposerMergeCommand::TableMaximumFeatures );
   mComposerTable->setMaximumNumberOfFeatures( i );
   mComposerTable->update();
+  mComposerTable->endCommand();
 }
 
 void QgsComposerTableWidget::on_mMarginSpinBox_valueChanged( double d )
@@ -163,8 +172,11 @@
   {
     return;
   }
+
+  mComposerTable->beginCommand( tr( "Table maximum columns" ), QgsComposerMergeCommand::TableMargin );
   mComposerTable->setLineTextDistance( d );
   mComposerTable->update();
+  mComposerTable->endCommand();
 }
 
 void QgsComposerTableWidget::on_mHeaderFontPushButton_clicked()
@@ -178,7 +190,9 @@
   QFont newFont = QFontDialog::getFont( &ok, mComposerTable->headerFont(), 0, tr( "Select Font" ) );
   if ( ok )
   {
+    mComposerTable->beginCommand( tr( "Table header font" ) );
     mComposerTable->setHeaderFont( newFont );
+    mComposerTable->endCommand();
   }
 }
 
@@ -193,7 +207,9 @@
   QFont newFont = QFontDialog::getFont( &ok, mComposerTable->contentFont(), 0, tr( "Select Font" ) );
   if ( ok )
   {
+    mComposerTable->beginCommand( tr( "Table content font" ) );
     mComposerTable->setContentFont( newFont );
+    mComposerTable->endCommand();
   }
 }
 
@@ -203,8 +219,10 @@
   {
     return;
   }
+  mComposerTable->beginCommand( tr( "Table grid stroke" ), QgsComposerMergeCommand::TableGridStrokeWidth );
   mComposerTable->setGridStrokeWidth( d );
   mComposerTable->update();
+  mComposerTable->endCommand();
 }
 
 void QgsComposerTableWidget::on_mGridColorButton_clicked()
@@ -223,9 +241,11 @@
   {
     return;
   }
+  mComposerTable->beginCommand( tr( "Table grid color" ) );
   mGridColorButton->setColor( newColor );
   mComposerTable->setGridColor( newColor );
   mComposerTable->update();
+  mComposerTable->endCommand();
 }
 
 void QgsComposerTableWidget::on_mShowGridCheckBox_stateChanged( int state )
@@ -240,8 +260,10 @@
   {
     showGrid = true;
   }
+  mComposerTable->beginCommand( tr( "Table grid toggled" ) );
   mComposerTable->setShowGrid( showGrid );
   mComposerTable->update();
+  mComposerTable->endCommand();
 }
 
 
@@ -325,9 +347,11 @@
     return;
   }
 
+  mComposerTable->beginCommand( tr( "Table visible only toggled" ) );
   bool showOnlyVisibleFeatures = ( state == Qt::Checked );
   mComposerTable->setDisplayOnlyVisibleFeatures( showOnlyVisibleFeatures );
   mComposerTable->update();
+  mComposerTable->endCommand();
 }
 
 

Modified: trunk/qgis/src/app/composer/qgscomposertablewidget.h
===================================================================
--- trunk/qgis/src/app/composer/qgscomposertablewidget.h	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/app/composer/qgscomposertablewidget.h	2010-11-29 15:30:19 UTC (rev 14786)
@@ -32,8 +32,6 @@
   private:
     QgsComposerAttributeTable* mComposerTable;
 
-    /**Sets the GUI elements to the values of mComposerTable*/
-    void updateGuiElements();
     /**Blocks / unblocks the signals of all GUI elements*/
     void blockAllSignals( bool b );
 
@@ -52,6 +50,9 @@
 
     /**Inserts a new maximum number of features into the spin box (without the spinbox emitting a signal)*/
     void setMaximumNumberOfFeatures( int n );
+
+    /**Sets the GUI elements to the values of mComposerTable*/
+    void updateGuiElements();
 };
 
 #endif // QGSCOMPOSERTABLEWIDGET_H

Modified: trunk/qgis/src/core/CMakeLists.txt
===================================================================
--- trunk/qgis/src/core/CMakeLists.txt	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/core/CMakeLists.txt	2010-11-29 15:30:19 UTC (rev 14786)
@@ -94,8 +94,10 @@
 
   qgsnetworkaccessmanager.cpp
 
+  composer/qgsaddremoveitemcommand.cpp
   composer/qgscomposerarrow.cpp
   composer/qgscomposeritem.cpp
+  composer/qgscomposeritemcommand.cpp
   composer/qgscomposeritemgroup.cpp
   composer/qgscomposerlabel.cpp
   composer/qgscomposerlegenditem.cpp
@@ -242,6 +244,7 @@
   qgsnetworkaccessmanager.h
   qgsvectordataprovider.h
 
+  composer/qgsaddremoveitemcommand.h
   composer/qgscomposerlegend.h
   composer/qgscomposermap.h
   composer/qgscomposerpicture.h

Added: trunk/qgis/src/core/composer/qgsaddremoveitemcommand.cpp
===================================================================
--- trunk/qgis/src/core/composer/qgsaddremoveitemcommand.cpp	                        (rev 0)
+++ trunk/qgis/src/core/composer/qgsaddremoveitemcommand.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -0,0 +1,74 @@
+/***************************************************************************
+                          qgsaddremoveitemcommand.cpp
+                          ---------------------------
+    begin                : 2010-11-27
+    copyright            : (C) 2010 by Marco Hugentobler
+    email                : marco dot hugentobler at sourcepole dot ch
+***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "qgsaddremoveitemcommand.h"
+#include "qgscomposeritem.h"
+
+QgsAddRemoveItemCommand::QgsAddRemoveItemCommand( State s, QgsComposerItem* item, QgsComposition* c, const QString& text, QUndoCommand* parent ):
+    QUndoCommand( text, parent ), mItem( item ), mComposition( c ), mState( s ), mFirstRun( true )
+{
+}
+
+QgsAddRemoveItemCommand::~QgsAddRemoveItemCommand()
+{
+  if ( mState == Removed ) //command class stores the item if removed from the composition
+  {
+    delete mItem;
+  }
+}
+
+void QgsAddRemoveItemCommand::redo()
+{
+  if ( mFirstRun )
+  {
+    mFirstRun = false;
+    return;
+  }
+  switchState();
+}
+
+void QgsAddRemoveItemCommand::undo()
+{
+  if ( mFirstRun )
+  {
+    mFirstRun = false;
+    return;
+  }
+  switchState();
+}
+
+void QgsAddRemoveItemCommand::switchState()
+{
+  if ( mState == Added )
+  {
+    if ( mComposition )
+    {
+      mComposition->removeItem( mItem );
+    }
+    emit itemRemoved( mItem );
+    mState = Removed;
+  }
+  else //Removed
+  {
+    if ( mComposition )
+    {
+      mComposition->addItem( mItem );
+    }
+    emit itemAdded( mItem );
+    mState = Added;
+  }
+}

Added: trunk/qgis/src/core/composer/qgsaddremoveitemcommand.h
===================================================================
--- trunk/qgis/src/core/composer/qgsaddremoveitemcommand.h	                        (rev 0)
+++ trunk/qgis/src/core/composer/qgsaddremoveitemcommand.h	2010-11-29 15:30:19 UTC (rev 14786)
@@ -0,0 +1,58 @@
+/***************************************************************************
+                          qgsaddremoveitemcommand.h
+                          ------------------------
+    begin                : 2010-11-27
+    copyright            : (C) 2010 by Marco Hugentobler
+    email                : marco dot hugentobler at sourcepole dot ch
+***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef QGSADDREMOVEITEMCOMMAND_H
+#define QGSADDREMOVEITEMCOMMAND_H
+
+#include <QUndoCommand>
+class QgsComposerItem;
+class QgsComposition;
+
+/**A composer command class for adding / removing composer items. If mState == Removed, the command owns the item*/
+class CORE_EXPORT QgsAddRemoveItemCommand: public QObject, public QUndoCommand
+{
+    Q_OBJECT
+
+  public:
+
+    enum State
+    {
+      Added = 0,
+      Removed
+    };
+
+    QgsAddRemoveItemCommand( State s, QgsComposerItem* item, QgsComposition* c, const QString& text, QUndoCommand* parent = 0 );
+    ~QgsAddRemoveItemCommand();
+
+    void redo();
+    void undo();
+
+  signals:
+    void itemAdded( QgsComposerItem* item );
+    void itemRemoved( QgsComposerItem* item );
+
+  private:
+    QgsComposerItem* mItem;
+    QgsComposition* mComposition;
+    State mState;
+    bool mFirstRun; //flag to prevent execution when the command is pushed to the QUndoStack
+
+    //changes between added / removed state
+    void switchState();
+};
+
+#endif // QGSADDREMOVEITEMCOMMAND_H

Modified: trunk/qgis/src/core/composer/qgscomposerarrow.cpp
===================================================================
--- trunk/qgis/src/core/composer/qgscomposerarrow.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/core/composer/qgscomposerarrow.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -358,5 +358,6 @@
   }
 
   adaptItemSceneRect();
+  emit itemChanged();
   return true;
 }

Modified: trunk/qgis/src/core/composer/qgscomposerattributetable.cpp
===================================================================
--- trunk/qgis/src/core/composer/qgscomposerattributetable.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/core/composer/qgscomposerattributetable.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -362,5 +362,7 @@
       mSortInformation.push_back( qMakePair( attribute, ascending ) );
     }
   }
-  return tableReadXML( itemElem, doc );
+  bool success = tableReadXML( itemElem, doc );
+  emit itemChanged();
+  return success;
 }

Modified: trunk/qgis/src/core/composer/qgscomposeritem.cpp
===================================================================
--- trunk/qgis/src/core/composer/qgscomposeritem.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/core/composer/qgscomposeritem.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -271,6 +271,30 @@
   return true;
 }
 
+void QgsComposerItem::beginCommand( const QString& commandText, QgsComposerMergeCommand::Context c )
+{
+  if ( mComposition )
+  {
+    mComposition->beginCommand( this, commandText, c );
+  }
+}
+
+void QgsComposerItem::endCommand()
+{
+  if ( mComposition )
+  {
+    mComposition->endCommand();
+  }
+}
+
+void QgsComposerItem::cancelCommand()
+{
+  if ( mComposition )
+  {
+    mComposition->cancelCommand();
+  }
+}
+
 void QgsComposerItem::mouseMoveEvent( QGraphicsSceneMouseEvent * event )
 {
   if ( mItemPositionLocked )
@@ -347,7 +371,9 @@
     return;
   }
 
+  beginCommand( tr( "Change item position" ) );
   changeItemRectangle( mouseMoveStopPoint, mMouseMoveStartPos, this, diffX, diffY, this );
+  endCommand();
 
   //reset default action
   mCurrentMouseMoveAction = QgsComposerItem::MoveItem;

Modified: trunk/qgis/src/core/composer/qgscomposeritem.h
===================================================================
--- trunk/qgis/src/core/composer/qgscomposeritem.h	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/core/composer/qgscomposeritem.h	2010-11-29 15:30:19 UTC (rev 14786)
@@ -169,6 +169,14 @@
 
     const QgsComposition* composition() const {return mComposition;}
 
+    /**Starts new composer undo command
+      @param commandText command title
+      @param c context for mergeable commands (unknown for non-mergeable commands*/
+    void beginCommand( const QString& commandText, QgsComposerMergeCommand::Context c = QgsComposerMergeCommand::Unknown );
+    /**Finish current command and push it onto the undo stack */
+    void endCommand();
+    void cancelCommand();
+
     //functions that encapsulate the workaround for the Qt font bug (that is to scale the font size up and then scale the
     //painter down by the same factor for drawing
 
@@ -305,6 +313,8 @@
   signals:
     /**Is emitted on rotation change to notify north arrow pictures*/
     void rotationChanged( double newRotation );
+    /**Used e.g. by the item widgets to update the gui elements*/
+    void itemChanged();
 };
 
 #endif

Added: trunk/qgis/src/core/composer/qgscomposeritemcommand.cpp
===================================================================
--- trunk/qgis/src/core/composer/qgscomposeritemcommand.cpp	                        (rev 0)
+++ trunk/qgis/src/core/composer/qgscomposeritemcommand.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -0,0 +1,98 @@
+/***************************************************************************
+                          qgscomposeritemcommand.cpp
+                          --------------------------
+    begin                : 2010-11-18
+    copyright            : (C) 2010 by Marco Hugentobler
+    email                : marco dot hugentobler at sourcepole dot ch
+***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "qgscomposeritemcommand.h"
+#include "qgscomposeritem.h"
+
+QgsComposerItemCommand::QgsComposerItemCommand( QgsComposerItem* item, const QString& text, QUndoCommand* parent ):
+    QUndoCommand( text, parent ), mItem( item ), mFirstRun( true )
+{
+}
+
+QgsComposerItemCommand::~QgsComposerItemCommand()
+{
+}
+
+void QgsComposerItemCommand::undo()
+{
+  restoreState( mPreviousState );
+}
+
+void QgsComposerItemCommand::redo()
+{
+  if ( mFirstRun )
+  {
+    mFirstRun = false;
+    return;
+  }
+  restoreState( mAfterState );
+}
+
+bool QgsComposerItemCommand::containsChange() const
+{
+  return !( mPreviousState.isNull() || mAfterState.isNull() || mPreviousState.toString() == mAfterState.toString() );
+}
+
+void QgsComposerItemCommand::savePreviousState()
+{
+  saveState( mPreviousState );
+}
+
+void QgsComposerItemCommand::saveAfterState()
+{
+  saveState( mAfterState );
+}
+
+void QgsComposerItemCommand::saveState( QDomDocument& stateDoc ) const
+{
+  if ( mItem )
+  {
+    stateDoc.clear();
+    QDomElement documentElement = stateDoc.createElement( "ComposerItemState" );
+    mItem->writeXML( documentElement, stateDoc );
+    stateDoc.appendChild( documentElement );
+  }
+}
+
+void QgsComposerItemCommand::restoreState( QDomDocument& stateDoc ) const
+{
+  if ( mItem )
+  {
+    mItem->readXML( stateDoc.documentElement().firstChild().toElement(), stateDoc );
+    mItem->repaint();
+  }
+}
+
+QgsComposerMergeCommand::QgsComposerMergeCommand( Context c, QgsComposerItem* item, const QString& text ): QgsComposerItemCommand( item, text ), mContext( c )
+{
+}
+
+QgsComposerMergeCommand::~QgsComposerMergeCommand()
+{
+}
+
+bool QgsComposerMergeCommand::mergeWith( const QUndoCommand * command )
+{
+  const QgsComposerItemCommand* c = dynamic_cast<const QgsComposerItemCommand*>( command );
+  if ( !c )
+  {
+    return false;
+  }
+  mAfterState = c->afterState();
+  return true;
+}
+

Added: trunk/qgis/src/core/composer/qgscomposeritemcommand.h
===================================================================
--- trunk/qgis/src/core/composer/qgscomposeritemcommand.h	                        (rev 0)
+++ trunk/qgis/src/core/composer/qgscomposeritemcommand.h	2010-11-29 15:30:19 UTC (rev 14786)
@@ -0,0 +1,121 @@
+/***************************************************************************
+                          qgscomposeritemcommand.h
+                          ------------------------
+    begin                : 2010-11-18
+    copyright            : (C) 2010 by Marco Hugentobler
+    email                : marco dot hugentobler at sourcepole dot ch
+***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef QGSCOMPOSERITEMCOMMAND_H
+#define QGSCOMPOSERITEMCOMMAND_H
+
+#include <QUndoCommand>
+#include <QDomDocument>
+
+class QgsComposerItem;
+
+/**Undo command to undo/redo all composer item related changes*/
+class CORE_EXPORT QgsComposerItemCommand: public QUndoCommand
+{
+  public:
+    QgsComposerItemCommand( QgsComposerItem* item, const QString& text, QUndoCommand* parent = 0 );
+    virtual ~QgsComposerItemCommand();
+
+    /**Reverses the command*/
+    void undo();
+    /**Replays the command*/
+    void redo();
+
+    /**Saves current item state as previous state*/
+    void savePreviousState();
+    /**Saves current item state as after state*/
+    void saveAfterState();
+
+    QDomDocument previousState() const { return mPreviousState.cloneNode().toDocument(); }
+    QDomDocument afterState() const { return mAfterState.cloneNode().toDocument(); }
+
+    /**Returns true if previous state and after state are valid and different*/
+    bool containsChange() const;
+
+  protected:
+    /**Target item of the command*/
+    QgsComposerItem* mItem;
+    /**XML that saves the state before executing the command*/
+    QDomDocument mPreviousState;
+    /**XML containing the state after executing the command*/
+    QDomDocument mAfterState;
+
+    /**Flag to prevent the first redo() if the command is pushed to the undo stack*/
+    bool mFirstRun;
+
+    void saveState( QDomDocument& stateDoc ) const;
+    void restoreState( QDomDocument& stateDoc ) const;
+};
+
+/**A composer command that merges together with other commands having the same context (=id). Keeps the oldest previous state and uses the
+  newest after state. The purpose is to avoid too many micro changes in the history*/
+class CORE_EXPORT QgsComposerMergeCommand: public QgsComposerItemCommand
+{
+  public:
+    enum Context
+    {
+      Unknown = 0,
+      //composer label
+      ComposerLabelSetText,
+      //composer map
+      ComposerMapRotation,
+      ComposerMapAnnotationDistance,
+      //composer legend
+      ComposerLegendText,
+      LegendSymbolWidth,
+      LegendSymbolHeight,
+      LegendLayerSpace,
+      LegendSymbolSpace,
+      LegendIconSymbolSpace,
+      LegendBoxSpace,
+      //composer picture
+      ComposerPictureRotation,
+      // composer scalebar
+      ScaleBarLineWidth,
+      ScaleBarHeight,
+      ScaleBarSegmentSize,
+      ScaleBarSegmentsLeft,
+      ScaleBarNSegments,
+      ScaleBarUnitText,
+      ScaleBarMapUnitsSegment,
+      ScaleBarLabelBarSize,
+      ScaleBarBoxContentSpace,
+      // composer table
+      TableMaximumFeatures,
+      TableMargin,
+      TableGridStrokeWidth,
+      //composer shape
+      ShapeRotation,
+      ShapeOutlineWidth,
+      //composer arrow
+      ArrowOutlineWidth,
+      ArrowHeadWidth,
+      //item
+      ItemOutlineWidth
+    };
+
+    QgsComposerMergeCommand( Context c, QgsComposerItem* item, const QString& text );
+    ~QgsComposerMergeCommand();
+
+    bool mergeWith( const QUndoCommand * command );
+    int id() const { return ( int )mContext; }
+
+  private:
+    Context mContext;
+};
+
+#endif // QGSCOMPOSERITEMCOMMAND_H

Modified: trunk/qgis/src/core/composer/qgscomposeritemgroup.cpp
===================================================================
--- trunk/qgis/src/core/composer/qgscomposeritemgroup.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/core/composer/qgscomposeritemgroup.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -32,7 +32,6 @@
   for ( ; itemIt != mItems.end(); ++itemIt )
   {
     emit childItemDeleted( *itemIt );
-    delete( *itemIt );
   }
 }
 

Modified: trunk/qgis/src/core/composer/qgscomposerlabel.cpp
===================================================================
--- trunk/qgis/src/core/composer/qgscomposerlabel.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/core/composer/qgscomposerlabel.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -198,5 +198,6 @@
     QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
     _readXML( composerItemElem, doc );
   }
+  emit itemChanged();
   return true;
 }

Modified: trunk/qgis/src/core/composer/qgscomposerlegend.cpp
===================================================================
--- trunk/qgis/src/core/composer/qgscomposerlegend.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/core/composer/qgscomposerlegend.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -623,5 +623,6 @@
     _readXML( composerItemElem, doc );
   }
 
+  emit itemChanged();
   return true;
 }

Modified: trunk/qgis/src/core/composer/qgscomposermap.cpp
===================================================================
--- trunk/qgis/src/core/composer/qgscomposermap.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/core/composer/qgscomposermap.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -390,6 +390,7 @@
     mExtent.setYMaximum( mExtent.yMaximum() + dy );
     cache();
     update();
+    emit itemChanged();
     emit extentChanged();
   }
 }
@@ -458,6 +459,7 @@
 
   cache();
   update();
+  emit itemChanged();
   emit extentChanged();
 }
 
@@ -480,6 +482,7 @@
   }
   updateBoundingRect();
   update();
+  emit itemChanged();
   emit extentChanged();
 }
 
@@ -513,6 +516,7 @@
   mCacheUpdated = false;
   cache();
   update();
+  emit itemChanged();
   emit extentChanged();
 }
 
@@ -786,6 +790,7 @@
   }
 
   updateBoundingRect();
+  emit itemChanged();
   return true;
 }
 

Modified: trunk/qgis/src/core/composer/qgscomposermap.h
===================================================================
--- trunk/qgis/src/core/composer/qgscomposermap.h	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/core/composer/qgscomposermap.h	2010-11-29 15:30:19 UTC (rev 14786)
@@ -260,6 +260,9 @@
     void setDrawCanvasItems( bool b ) { mDrawCanvasItems = b; }
     bool drawCanvasItems() const { return mDrawCanvasItems; }
 
+  signals:
+    void extentChanged();
+
   public slots:
 
     /**Called if map canvas has changed*/
@@ -267,10 +270,6 @@
     /**Call updateCachedImage if item is in render mode*/
     void renderModeUpdateCachedImage();
 
-  signals:
-    /**Is emitted when width/height is changed as a result of user interaction*/
-    void extentChanged();
-
   private:
 
     /**Enum for different frame borders*/

Modified: trunk/qgis/src/core/composer/qgscomposerpicture.cpp
===================================================================
--- trunk/qgis/src/core/composer/qgscomposerpicture.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/core/composer/qgscomposerpicture.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -161,7 +161,7 @@
   {
     setSceneRect( QRectF( transform().dx(), transform().dy(), rect().width(), rect().height() ) );
   }
-  emit settingsChanged();
+  emit itemChanged();
 }
 
 QRectF QgsComposerPicture::boundedImageRect( double deviceWidth, double deviceHeight )
@@ -241,7 +241,7 @@
   mPictureWidth = newPictureWidth;
   mPictureHeight = newPictureHeight;
 
-  emit settingsChanged();
+  emit itemChanged();
 }
 
 void QgsComposerPicture::setRotation( double r )
@@ -357,6 +357,7 @@
     QObject::connect( mRotationMap, SIGNAL( rotationChanged( double ) ), this, SLOT( setRotation( double ) ) );
   }
 
+  emit itemChanged();
   return true;
 }
 

Modified: trunk/qgis/src/core/composer/qgscomposerpicture.h
===================================================================
--- trunk/qgis/src/core/composer/qgscomposerpicture.h	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/core/composer/qgscomposerpicture.h	2010-11-29 15:30:19 UTC (rev 14786)
@@ -104,10 +104,6 @@
     double mPictureWidth;
     /**Height of the picture (in mm)*/
     double mPictureHeight;
-
-  signals:
-    /**Tell the configuration widget that the settings need to be updated*/
-    void settingsChanged();
 };
 
 #endif

Modified: trunk/qgis/src/core/composer/qgscomposershape.cpp
===================================================================
--- trunk/qgis/src/core/composer/qgscomposershape.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/core/composer/qgscomposershape.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -155,6 +155,7 @@
     QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
     _readXML( composerItemElem, doc );
   }
+  emit itemChanged();
   return true;
 }
 

Modified: trunk/qgis/src/core/composer/qgscomposition.cpp
===================================================================
--- trunk/qgis/src/core/composer/qgscomposition.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/core/composer/qgscomposition.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -24,7 +24,9 @@
 #include <QGraphicsRectItem>
 #include <QSettings>
 
-QgsComposition::QgsComposition( QgsMapRenderer* mapRenderer ): QGraphicsScene( 0 ), mMapRenderer( mapRenderer ), mPlotStyle( QgsComposition::Preview ), mPaperItem( 0 ), mSnapToGrid( false ), mSnapGridResolution( 0.0 ), mSnapGridOffsetX( 0.0 ), mSnapGridOffsetY( 0.0 )
+QgsComposition::QgsComposition( QgsMapRenderer* mapRenderer ):
+    QGraphicsScene( 0 ), mMapRenderer( mapRenderer ), mPlotStyle( QgsComposition::Preview ), mPaperItem( 0 ), mSnapToGrid( false ),
+    mSnapGridResolution( 0.0 ), mSnapGridOffsetX( 0.0 ), mSnapGridOffsetY( 0.0 ), mActiveCommand( 0 )
 {
   setBackgroundBrush( Qt::gray );
 
@@ -41,7 +43,9 @@
   mPrintAsRaster = s.value( "/qgis/composerPrintAsRaster", false ).toBool();
 }
 
-QgsComposition::QgsComposition(): QGraphicsScene( 0 ), mMapRenderer( 0 ), mPlotStyle( QgsComposition::Preview ), mPaperItem( 0 ), mPrintAsRaster( false ), mSnapToGrid( false ), mSnapGridResolution( 0.0 ), mSnapGridOffsetX( 0.0 ), mSnapGridOffsetY( 0.0 )
+QgsComposition::QgsComposition():
+    QGraphicsScene( 0 ), mMapRenderer( 0 ), mPlotStyle( QgsComposition::Preview ), mPaperItem( 0 ), mPrintAsRaster( false ),
+    mSnapToGrid( false ), mSnapGridResolution( 0.0 ), mSnapGridOffsetX( 0.0 ), mSnapGridOffsetY( 0.0 ), mActiveCommand( 0 )
 {
   loadGridAppearanceSettings();
   //mPrintAsRaster
@@ -326,6 +330,7 @@
 {
   QList<QgsComposerItem*> selectedItems = selectedComposerItems();
   QList<QgsComposerItem*>::iterator it = selectedItems.begin();
+
   for ( ; it != selectedItems.end(); ++it )
   {
     moveItemToTop( *it );
@@ -389,13 +394,18 @@
   double minXCoordinate = selectedItemBBox.left();
 
   //align items left to minimum x coordinate
+  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items left" ) );
   QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
   for ( ; align_it != selectedItems.end(); ++align_it )
   {
+    QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
+    subcommand->savePreviousState();
     QTransform itemTransform = ( *align_it )->transform();
     itemTransform.translate( minXCoordinate - itemTransform.dx(), 0 );
     ( *align_it )->setTransform( itemTransform );
+    subcommand->saveAfterState();
   }
+  mUndoStack.push( parentCommand );
 }
 
 void QgsComposition::alignSelectedItemsHCenter()
@@ -415,13 +425,18 @@
   double averageXCoord = ( selectedItemBBox.left() + selectedItemBBox.right() ) / 2.0;
 
   //place items
+  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items hcenter" ) );
   QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
   for ( ; align_it != selectedItems.end(); ++align_it )
   {
+    QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
+    subcommand->savePreviousState();
     QTransform itemTransform = ( *align_it )->transform();
     itemTransform.translate( averageXCoord - itemTransform.dx() - ( *align_it )->rect().width() / 2.0, 0 );
     ( *align_it )->setTransform( itemTransform );
+    subcommand->saveAfterState();
   }
+  mUndoStack.push( parentCommand );
 }
 
 void QgsComposition::alignSelectedItemsRight()
@@ -441,13 +456,18 @@
   double maxXCoordinate = selectedItemBBox.right();
 
   //align items right to maximum x coordinate
+  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items right" ) );
   QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
   for ( ; align_it != selectedItems.end(); ++align_it )
   {
+    QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
+    subcommand->savePreviousState();
     QTransform itemTransform = ( *align_it )->transform();
     itemTransform.translate( maxXCoordinate - itemTransform.dx() - ( *align_it )->rect().width(), 0 );
     ( *align_it )->setTransform( itemTransform );
+    subcommand->saveAfterState();
   }
+  mUndoStack.push( parentCommand );
 }
 
 void QgsComposition::alignSelectedItemsTop()
@@ -465,13 +485,19 @@
   }
 
   double minYCoordinate = selectedItemBBox.top();
+
+  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items top" ) );
   QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
   for ( ; align_it != selectedItems.end(); ++align_it )
   {
+    QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
+    subcommand->savePreviousState();
     QTransform itemTransform = ( *align_it )->transform();
     itemTransform.translate( 0, minYCoordinate - itemTransform.dy() );
     ( *align_it )->setTransform( itemTransform );
+    subcommand->saveAfterState();
   }
+  mUndoStack.push( parentCommand );
 }
 
 void QgsComposition::alignSelectedItemsVCenter()
@@ -489,13 +515,18 @@
   }
 
   double averageYCoord = ( selectedItemBBox.top() + selectedItemBBox.bottom() ) / 2.0;
+  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items vcenter" ) );
   QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
   for ( ; align_it != selectedItems.end(); ++align_it )
   {
+    QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
+    subcommand->savePreviousState();
     QTransform itemTransform = ( *align_it )->transform();
     itemTransform.translate( 0, averageYCoord - itemTransform.dy() - ( *align_it )->rect().height() / 2 );
     ( *align_it )->setTransform( itemTransform );
+    subcommand->saveAfterState();
   }
+  mUndoStack.push( parentCommand );
 }
 
 void QgsComposition::alignSelectedItemsBottom()
@@ -513,13 +544,18 @@
   }
 
   double maxYCoord = selectedItemBBox.bottom();
+  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items bottom" ) );
   QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
   for ( ; align_it != selectedItems.end(); ++align_it )
   {
+    QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
+    subcommand->savePreviousState();
     QTransform itemTransform = ( *align_it )->transform();
     itemTransform.translate( 0, maxYCoord - itemTransform.dy() - ( *align_it )->rect().height() );
     ( *align_it )->setTransform( itemTransform );
+    subcommand->saveAfterState();
   }
+  mUndoStack.push( parentCommand );
 }
 
 void QgsComposition::updateZValues()
@@ -528,16 +564,20 @@
   QLinkedList<QgsComposerItem*>::iterator it = mItemZList.begin();
   QgsComposerItem* currentItem = 0;
 
+  QUndoCommand* parentCommand = new QUndoCommand( tr( "Item z-order changed" ) );
   for ( ; it != mItemZList.end(); ++it )
   {
     currentItem = *it;
     if ( currentItem )
     {
-      QgsDebugMsg( QString::number( counter ) );
+      QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *it, "", parentCommand );
+      subcommand->savePreviousState();
       currentItem->setZValue( counter );
+      subcommand->saveAfterState();
     }
     ++counter;
   }
+  mUndoStack.push( parentCommand );
 }
 
 void QgsComposition::sortZList()
@@ -547,16 +587,6 @@
     return;
   }
 
-#ifdef QGISDEBUG
-  //debug: list before sorting
-  QgsDebugMsg( "before sorting" );
-  QLinkedList<QgsComposerItem*>::iterator before_it = mItemZList.begin();
-  for ( ; before_it != mItemZList.end(); ++before_it )
-  {
-    QgsDebugMsg( QString( "%1" ).arg(( *before_it )->zValue() ) );
-  }
-#endif
-
   QLinkedList<QgsComposerItem*>::const_iterator lIt = mItemZList.constBegin();
   QLinkedList<QgsComposerItem*> sortedList;
 
@@ -574,17 +604,6 @@
   }
 
   mItemZList = sortedList;
-
-#ifdef QGISDEBUG
-  //debug: list after sorting
-  //debug: list before sorting
-  QgsDebugMsg( "after sorting" );
-  QLinkedList<QgsComposerItem*>::iterator after_it = mItemZList.begin();
-  for ( ; after_it != mItemZList.end(); ++after_it )
-  {
-    QgsDebugMsg( QString( "%1" ).arg(( *after_it )->zValue() ) );
-  }
-#endif
 }
 
 QPointF QgsComposition::snapPointToGrid( const QPointF& scenePoint ) const
@@ -758,3 +777,46 @@
     s.setValue( "/qgis/composerGridStyle", "Crosses" );
   }
 }
+
+void QgsComposition::beginCommand( QgsComposerItem* item, const QString& commandText, QgsComposerMergeCommand::Context c )
+{
+  delete mActiveCommand;
+  if ( !item )
+  {
+    mActiveCommand = 0;
+    return;
+  }
+
+  if ( c == QgsComposerMergeCommand::Unknown )
+  {
+    mActiveCommand = new QgsComposerItemCommand( item, commandText );
+  }
+  else
+  {
+    mActiveCommand = new QgsComposerMergeCommand( c, item, commandText );
+  }
+  mActiveCommand->savePreviousState();
+}
+
+void QgsComposition::endCommand()
+{
+  if ( mActiveCommand )
+  {
+    mActiveCommand->saveAfterState();
+    if ( mActiveCommand->containsChange() ) //protect against empty commands
+    {
+      mUndoStack.push( mActiveCommand );
+    }
+    else
+    {
+      delete mActiveCommand;
+    }
+    mActiveCommand = 0;
+  }
+}
+
+void QgsComposition::cancelCommand()
+{
+  delete mActiveCommand;
+  mActiveCommand = 0;
+}

Modified: trunk/qgis/src/core/composer/qgscomposition.h
===================================================================
--- trunk/qgis/src/core/composer/qgscomposition.h	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/core/composer/qgscomposition.h	2010-11-29 15:30:19 UTC (rev 14786)
@@ -19,7 +19,10 @@
 #include <QDomDocument>
 #include <QGraphicsScene>
 #include <QLinkedList>
+#include <QUndoStack>
 
+#include "qgscomposeritemcommand.h"
+
 class QgsComposerItem;
 class QgsComposerMap;
 class QgsPaperItem;
@@ -85,6 +88,9 @@
     void setGridStyle( GridStyle s );
     GridStyle gridStyle() const {return mGridStyle;}
 
+    /**Returns pointer to undo/redo command storage*/
+    QUndoStack* undoStack() { return &mUndoStack; }
+
     /**Returns the topmost composer item. Ignores mPaperItem*/
     QgsComposerItem* composerItemAt( const QPointF & position );
 
@@ -153,7 +159,18 @@
     /**Snaps a scene coordinate point to grid*/
     QPointF snapPointToGrid( const QPointF& scenePoint ) const;
 
+    /**Allocates new item command and saves initial state in it
+      @param item target item
+      @param commandText descriptive command text
+      @param c context for merge commands (unknown for non-mergeable commands)*/
+    void beginCommand( QgsComposerItem* item, const QString& commandText, QgsComposerMergeCommand::Context c = QgsComposerMergeCommand::Unknown );
 
+    /**Saves end state of item and pushes command to the undo history*/
+    void endCommand();
+    /**Deletes current command*/
+    void cancelCommand();
+
+
   private:
     /**Pointer to map renderer of QGIS main map*/
     QgsMapRenderer* mMapRenderer;
@@ -177,6 +194,10 @@
     QPen mGridPen;
     GridStyle mGridStyle;
 
+    QUndoStack mUndoStack;
+
+    QgsComposerItemCommand* mActiveCommand;
+
     QgsComposition(); //default constructor is forbidden
 
     /**Reset z-values of items based on position in z list*/

Modified: trunk/qgis/src/gui/qgscomposerview.cpp
===================================================================
--- trunk/qgis/src/gui/qgscomposerview.cpp	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/gui/qgscomposerview.cpp	2010-11-29 15:30:19 UTC (rev 14786)
@@ -138,8 +138,8 @@
     case AddScalebar:
     {
       QgsComposerScaleBar* newScaleBar = new QgsComposerScaleBar( composition() );
+      newScaleBar->setSceneRect( QRectF( snappedScenePoint.x(), snappedScenePoint.y(), 20, 20 ) );
       addComposerScaleBar( newScaleBar );
-      newScaleBar->setSceneRect( QRectF( snappedScenePoint.x(), snappedScenePoint.y(), 20, 20 ) );
       emit actionFinished();
     }
     break;
@@ -147,24 +147,24 @@
     case AddLegend:
     {
       QgsComposerLegend* newLegend = new QgsComposerLegend( composition() );
+      newLegend->setSceneRect( QRectF( snappedScenePoint.x(), snappedScenePoint.y(), newLegend->rect().width(), newLegend->rect().height() ) );
       addComposerLegend( newLegend );
-      newLegend->setSceneRect( QRectF( snappedScenePoint.x(), snappedScenePoint.y(), newLegend->rect().width(), newLegend->rect().height() ) );
       emit actionFinished();
       break;
     }
     case AddPicture:
     {
       QgsComposerPicture* newPicture = new QgsComposerPicture( composition() );
+      newPicture->setSceneRect( QRectF( snappedScenePoint.x(), snappedScenePoint.y(), 30, 30 ) );
       addComposerPicture( newPicture );
-      newPicture->setSceneRect( QRectF( snappedScenePoint.x(), snappedScenePoint.y(), 30, 30 ) );
       emit actionFinished();
       break;
     }
     case AddTable:
     {
       QgsComposerAttributeTable* newTable = new QgsComposerAttributeTable( composition() );
+      newTable->setSceneRect( QRectF( snappedScenePoint.x(), snappedScenePoint.y(), 50, 50 ) );
       addComposerTable( newTable );
-      newTable->setSceneRect( QRectF( snappedScenePoint.x(), snappedScenePoint.y(), 50, 50 ) );
       emit actionFinished();
       break;
     }
@@ -205,7 +205,9 @@
         double moveX = scenePoint.x() - mMoveContentStartPos.x();
         double moveY = scenePoint.y() - mMoveContentStartPos.y();
 
+        composition()->beginCommand( mMoveContentItem, tr( "Move item content" ) );
         mMoveContentItem->moveContent( -moveX, -moveY );
+        composition()->endCommand();
         mMoveContentItem = 0;
       }
       break;
@@ -393,6 +395,8 @@
       {
         composition()->removeItem( *itemIt );
         emit itemRemoved( *itemIt );
+
+        pushAddRemoveCommand( *itemIt, tr( "Item deleted" ), QgsAddRemoveItemCommand::Removed );
       }
     }
   }
@@ -446,7 +450,9 @@
     if ( theItem->isSelected() )
     {
       QPointF itemPoint = theItem->mapFromScene( scenePoint );
+      theItem->beginCommand( tr( "Zoom item content" ) );
       theItem->zoomContent( event->delta(), itemPoint.x(), itemPoint.y() );
+      theItem->endCommand();
     }
   }
 }
@@ -489,6 +495,7 @@
   scene()->clearSelection();
   arrow->setSelected( true );
   emit selectedItemChanged( arrow );
+  pushAddRemoveCommand( arrow, tr( "Arrow added" ) );
 }
 
 void QgsComposerView::addComposerLabel( QgsComposerLabel* label )
@@ -498,6 +505,7 @@
   scene()->clearSelection();
   label->setSelected( true );
   emit selectedItemChanged( label );
+  pushAddRemoveCommand( label, tr( "Label added" ) );
 }
 
 void QgsComposerView::addComposerMap( QgsComposerMap* map )
@@ -510,6 +518,7 @@
   scene()->clearSelection();
   map->setSelected( true );
   emit selectedItemChanged( map );
+  pushAddRemoveCommand( map, tr( "Map added" ) );
 }
 
 void QgsComposerView::addComposerScaleBar( QgsComposerScaleBar* scaleBar )
@@ -526,6 +535,7 @@
   scene()->clearSelection();
   scaleBar->setSelected( true );
   emit selectedItemChanged( scaleBar );
+  pushAddRemoveCommand( scaleBar, tr( "Scale bar added" ) );
 }
 
 void QgsComposerView::addComposerLegend( QgsComposerLegend* legend )
@@ -535,6 +545,7 @@
   scene()->clearSelection();
   legend->setSelected( true );
   emit selectedItemChanged( legend );
+  pushAddRemoveCommand( legend, tr( "Legend added" ) );
 }
 
 void QgsComposerView::addComposerPicture( QgsComposerPicture* picture )
@@ -544,6 +555,7 @@
   scene()->clearSelection();
   picture->setSelected( true );
   emit selectedItemChanged( picture );
+  pushAddRemoveCommand( picture, tr( "Picture added" ) );
 }
 
 void QgsComposerView::addComposerShape( QgsComposerShape* shape )
@@ -553,6 +565,7 @@
   scene()->clearSelection();
   shape->setSelected( true );
   emit selectedItemChanged( shape );
+  pushAddRemoveCommand( shape, tr( "Shape added" ) );
 }
 
 void QgsComposerView::addComposerTable( QgsComposerAttributeTable* table )
@@ -562,6 +575,7 @@
   scene()->clearSelection();
   table->setSelected( true );
   emit selectedItemChanged( table );
+  pushAddRemoveCommand( table, tr( "Table added" ) );
 }
 
 void QgsComposerView::groupItems()
@@ -576,16 +590,16 @@
   {
     return; //not enough items for a group
   }
-
   QgsComposerItemGroup* itemGroup = new QgsComposerItemGroup( composition() );
   //connect signal/slot to let item group tell if child items get removed
-  connect( itemGroup, SIGNAL( childItemDeleted( QgsComposerItem* ) ), this, SLOT( sendItemRemovedSignal( QgsComposerItem* ) ) );
+  connect( itemGroup, SIGNAL( childItemDeleted( QgsComposerItem* ) ), this, SIGNAL( itemRemoved( QgsComposerItem* ) ) );
 
   QList<QgsComposerItem*>::iterator itemIter = selectionList.begin();
   for ( ; itemIter != selectionList.end(); ++itemIter )
   {
     itemGroup->addItem( *itemIter );
   }
+
   composition()->addItem( itemGroup );
   itemGroup->setSelected( true );
   emit selectedItemChanged( itemGroup );
@@ -611,12 +625,68 @@
       emit itemRemoved( *itemIter );
     }
   }
-
 }
 
-void QgsComposerView::sendItemRemovedSignal( QgsComposerItem* item )
+void QgsComposerView::sendItemAddedSignal( QgsComposerItem* item )
 {
-  emit itemRemoved( item );
+  //cast and send proper signal
+  item->setSelected( true );
+  QgsComposerArrow* arrow = dynamic_cast<QgsComposerArrow*>( item );
+  if ( arrow )
+  {
+    emit composerArrowAdded( arrow );
+    emit selectedItemChanged( arrow );
+    return;
+  }
+  QgsComposerLabel* label = dynamic_cast<QgsComposerLabel*>( item );
+  if ( label )
+  {
+    emit composerLabelAdded( label );
+    emit selectedItemChanged( label );
+    return;
+  }
+  QgsComposerMap* map = dynamic_cast<QgsComposerMap*>( item );
+  if ( map )
+  {
+    emit composerMapAdded( map );
+    emit selectedItemChanged( map );
+    return;
+  }
+  QgsComposerScaleBar* scalebar = dynamic_cast<QgsComposerScaleBar*>( item );
+  if ( scalebar )
+  {
+    emit composerScaleBarAdded( scalebar );
+    emit selectedItemChanged( scalebar );
+    return;
+  }
+  QgsComposerLegend* legend = dynamic_cast<QgsComposerLegend*>( item );
+  if ( legend )
+  {
+    emit composerLegendAdded( legend );
+    emit selectedItemChanged( legend );
+    return;
+  }
+  QgsComposerPicture* picture = dynamic_cast<QgsComposerPicture*>( item );
+  if ( picture )
+  {
+    emit composerPictureAdded( picture );
+    emit selectedItemChanged( picture );
+    return;
+  }
+  QgsComposerShape* shape = dynamic_cast<QgsComposerShape*>( item );
+  if ( shape )
+  {
+    emit composerShapeAdded( shape );
+    emit selectedItemChanged( shape );
+    return;
+  }
+  QgsComposerAttributeTable* table = dynamic_cast<QgsComposerAttributeTable*>( item );
+  if ( table )
+  {
+    emit composerTableAdded( table );
+    emit selectedItemChanged( table );
+    return;
+  }
 }
 
 QMainWindow* QgsComposerView::composerWindow()
@@ -641,3 +711,24 @@
   return 0;
 }
 
+void QgsComposerView::connectAddRemoveCommandSignals( QgsAddRemoveItemCommand* c )
+{
+  if ( !c )
+  {
+    return;
+  }
+  QObject::connect( c, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SIGNAL( itemRemoved( QgsComposerItem* ) ) );
+  QObject::connect( c, SIGNAL( itemAdded( QgsComposerItem* ) ), this, SLOT( sendItemAddedSignal( QgsComposerItem* ) ) );
+}
+
+void QgsComposerView::pushAddRemoveCommand( QgsComposerItem* item, const QString& text, QgsAddRemoveItemCommand::State state )
+{
+  if ( !composition() )
+  {
+    return;
+  }
+
+  QgsAddRemoveItemCommand* c = new QgsAddRemoveItemCommand( state, item, composition(), text );
+  connectAddRemoveCommandSignals( c );
+  composition()->undoStack()->push( c );
+}

Modified: trunk/qgis/src/gui/qgscomposerview.h
===================================================================
--- trunk/qgis/src/gui/qgscomposerview.h	2010-11-29 15:25:31 UTC (rev 14785)
+++ trunk/qgis/src/gui/qgscomposerview.h	2010-11-29 15:30:19 UTC (rev 14786)
@@ -19,6 +19,7 @@
 #define QGSCOMPOSERVIEW_H
 
 #include <QGraphicsView>
+#include "qgsaddremoveitemcommand.h"
 
 class QKeyEvent;
 class QMainWindow;
@@ -114,6 +115,10 @@
 
     void paintEvent( QPaintEvent* event );
 
+    /**Convenience function to create a QgsAddRemoveItemCommand, connect its signals and push it to the undo stack*/
+    void pushAddRemoveCommand( QgsComposerItem* item, const QString& text, QgsAddRemoveItemCommand::State state = QgsAddRemoveItemCommand::Added );
+
+
   private:
     /**Status of shift key (used for multiple selection)*/
     bool mShiftKeyPressed;
@@ -132,9 +137,12 @@
 
     bool mPaintingEnabled;
 
+    void connectAddRemoveCommandSignals( QgsAddRemoveItemCommand* c );
+
+
   public slots:
-    /**For QgsComposerItemGroup to send its signals to QgsComposer (or other classes that keep track of input widgets)*/
-    void sendItemRemovedSignal( QgsComposerItem* item );
+    /**Casts object to the proper subclass type and calls corresponding itemAdded signal*/
+    void sendItemAddedSignal( QgsComposerItem* item );
 
   signals:
     /**Is emitted when selected item changed. If 0, no item is selected*/



More information about the QGIS-commit mailing list