[QGIS Commit] r14697 - in trunk/qgis: images images/themes/default python/core src/app src/core src/core/pal src/ui

svn_qgis at osgeo.org svn_qgis at osgeo.org
Wed Nov 17 10:01:15 EST 2010


Author: mhugent
Date: 2010-11-17 07:01:15 -0800 (Wed, 17 Nov 2010)
New Revision: 14697

Added:
   trunk/qgis/images/themes/default/mActionChangeLabelProperties.png
   trunk/qgis/images/themes/default/mActionMoveLabel.png
   trunk/qgis/images/themes/default/mActionRotateLabel.png
   trunk/qgis/src/app/qgsmaptoolchangelabelproperties.cpp
   trunk/qgis/src/app/qgsmaptoolchangelabelproperties.h
   trunk/qgis/src/app/qgsmaptoollabel.cpp
   trunk/qgis/src/app/qgsmaptoollabel.h
   trunk/qgis/src/app/qgsmaptoolmovelabel.cpp
   trunk/qgis/src/app/qgsmaptoolmovelabel.h
   trunk/qgis/src/app/qgsmaptoolrotatelabel.cpp
   trunk/qgis/src/app/qgsmaptoolrotatelabel.h
   trunk/qgis/src/core/qgslabelsearchtree.cpp
   trunk/qgis/src/core/qgslabelsearchtree.h
   trunk/qgis/src/ui/qgslabelpropertydialogbase.ui
Modified:
   trunk/qgis/images/images.qrc
   trunk/qgis/python/core/qgsmaprenderer.sip
   trunk/qgis/python/core/qgspoint.sip
   trunk/qgis/src/app/CMakeLists.txt
   trunk/qgis/src/app/qgisapp.cpp
   trunk/qgis/src/app/qgisapp.h
   trunk/qgis/src/app/qgspointrotationitem.cpp
   trunk/qgis/src/app/qgspointrotationitem.h
   trunk/qgis/src/core/CMakeLists.txt
   trunk/qgis/src/core/pal/labelposition.cpp
   trunk/qgis/src/core/pal/labelposition.h
   trunk/qgis/src/core/qgsmaprenderer.h
   trunk/qgis/src/core/qgspallabeling.cpp
   trunk/qgis/src/core/qgspallabeling.h
   trunk/qgis/src/core/qgspoint.cpp
   trunk/qgis/src/core/qgspoint.h
Log:
[FEATURE]: move/rotate/change label edit tools to interactively change data defined label properties

Modified: trunk/qgis/images/images.qrc
===================================================================
--- trunk/qgis/images/images.qrc	2010-11-17 09:57:44 UTC (rev 14696)
+++ trunk/qgis/images/images.qrc	2010-11-17 15:01:15 UTC (rev 14697)
@@ -123,6 +123,7 @@
     <file>themes/default/mActionCaptureLine.png</file>
     <file>themes/default/mActionCapturePoint.png</file>
     <file>themes/default/mActionCapturePolygon.png</file>
+    <file>themes/default/mActionChangeLabelProperties.png</file>
     <file>themes/default/mActionCheckQgisVersion.png</file>
     <file>themes/default/mActionCollapseTree.png</file>
     <file>themes/default/mActionComposerManager.png</file>
@@ -168,6 +169,7 @@
     <file>themes/default/mActionMergeFeatures.png</file>
     <file>themes/default/mActionMergeFeatureAttributes.png</file>
     <file>themes/default/mActionMoveFeature.png</file>
+    <file>themes/default/mActionMoveLabel.png</file>
     <file>themes/default/mActionMoveItemContent.png</file>
     <file>themes/default/mActionMoveItemsToBottom.png</file>
     <file>themes/default/mActionMoveItemsToTop.png</file>
@@ -191,6 +193,7 @@
     <file>themes/default/mActionRemoveLayer.png</file>
     <file>themes/default/mActionRemoveSelectedFeature.png</file>
     <file>themes/default/mActionReshape.png</file>
+    <file>themes/default/mActionRotateLabel.png</file>
     <file>themes/default/mActionRotatePointSymbols.png</file>
     <file>themes/default/mActionSaveAsPDF.png</file>
     <file>themes/default/mActionSaveAsSVG.png</file>

Added: trunk/qgis/images/themes/default/mActionChangeLabelProperties.png
===================================================================
(Binary files differ)


Property changes on: trunk/qgis/images/themes/default/mActionChangeLabelProperties.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/qgis/images/themes/default/mActionMoveLabel.png
===================================================================
(Binary files differ)


Property changes on: trunk/qgis/images/themes/default/mActionMoveLabel.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/qgis/images/themes/default/mActionRotateLabel.png
===================================================================
(Binary files differ)


Property changes on: trunk/qgis/images/themes/default/mActionRotateLabel.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Modified: trunk/qgis/python/core/qgsmaprenderer.sip
===================================================================
--- trunk/qgis/python/core/qgsmaprenderer.sip	2010-11-17 09:57:44 UTC (rev 14696)
+++ trunk/qgis/python/core/qgsmaprenderer.sip	2010-11-17 15:01:15 UTC (rev 14697)
@@ -25,12 +25,33 @@
   virtual void drawLabeling( QgsRenderContext& context ) = 0;
   //! called when we're done with rendering
   virtual void exit() = 0;
+  //! return infos about labels at a given (map) position
+  //! @note: this method was added in version 1.7
+  virtual QList<QgsLabelPosition> labelsAtPosition( const QgsPoint& p )= 0;
 
+
   //! called when passing engine among map renderers
   virtual QgsLabelingEngineInterface* clone() = 0;
 };
 
+struct QgsLabelPosition
+{
+%TypeHeaderCode
+#include <qgsmaprenderer.h>
+%End
+  QgsLabelPosition( int id, double r, const QVector< QgsPoint >& corners, const QgsRectangle& rect, double w, double h, const QString& layer, bool upside_down );
+  QgsLabelPosition();
+  int featureId;
+  double rotation;
+  QVector< QgsPoint > cornerPoints;
+  QgsRectangle labelRect;
+  double width;
+  double height;
+  QString layerID;
+  bool upsideDown;
+};
 
+
 /**
  * \class QgsMapRenderer
  * \brief Class for rendering map layer set

Modified: trunk/qgis/python/core/qgspoint.sip
===================================================================
--- trunk/qgis/python/core/qgspoint.sip	2010-11-17 09:57:44 UTC (rev 14696)
+++ trunk/qgis/python/core/qgspoint.sip	2010-11-17 15:01:15 UTC (rev 14697)
@@ -77,6 +77,10 @@
     @note added in QGIS 1.5*/
   double sqrDistToSegment( double x1, double y1, double x2, double y2, QgsPoint& minDistPoint /Out/) const;
 
+  /**Calculates azimut between this point and other one (clockwise in degree, starting from north)
+    @note: this function has been added in version 1.7*/
+  double azimuth( const QgsPoint& other );
+
   //! equality operator
   bool operator==(const QgsPoint &other);
     

Modified: trunk/qgis/src/app/CMakeLists.txt
===================================================================
--- trunk/qgis/src/app/CMakeLists.txt	2010-11-17 09:57:44 UTC (rev 14696)
+++ trunk/qgis/src/app/CMakeLists.txt	2010-11-17 15:01:15 UTC (rev 14697)
@@ -28,6 +28,7 @@
   qgsidentifyresults.cpp
   qgsfeatureaction.cpp
   qgslabeldialog.cpp
+  qgslabelpropertydialog.cpp
   qgslabelengineconfigdialog.cpp
   qgslabelinggui.cpp
   qgslabelpreview.cpp
@@ -37,17 +38,21 @@
   qgsmaptooladdring.cpp
   qgsmaptoolannotation.cpp
   qgsmaptoolcapture.cpp
+  qgsmaptoolchangelabelproperties.cpp
   qgsmaptooldeletering.cpp
   qgsmaptooldeletepart.cpp
   qgsmaptooldeletevertex.cpp
   qgsmaptooledit.cpp
   qgsmaptoolformannotation.cpp
   qgsmaptoolidentify.cpp
+  qgsmaptoollabel.cpp
   qgsmaptoolmeasureangle.cpp
   qgsmaptoolmovefeature.cpp
+  qgsmaptoolmovelabel.cpp
   qgsmaptoolmovevertex.cpp
   qgsmaptoolnodetool.cpp
   qgsmaptoolreshape.cpp
+  qgsmaptoolrotatelabel.cpp
   qgsmaptoolrotatepointsymbols.cpp
   qgsmaptoolselect.cpp
   qgsmaptoolselectrectangle.cpp
@@ -175,6 +180,7 @@
   qgsidentifyresults.h
   qgsfeatureaction.h
   qgslabeldialog.h
+  qgslabelpropertydialog.h
   qgsmanageconnectionsdialog.h
   qgsmaptoolidentify.h
   qgsmaptoolsplitfeatures.h

Modified: trunk/qgis/src/app/qgisapp.cpp
===================================================================
--- trunk/qgis/src/app/qgisapp.cpp	2010-11-17 09:57:44 UTC (rev 14696)
+++ trunk/qgis/src/app/qgisapp.cpp	2010-11-17 15:01:15 UTC (rev 14697)
@@ -212,6 +212,9 @@
 #include "qgsmaptoolzoom.h"
 #include "qgsmaptoolsimplify.h"
 #include "qgsmeasuretool.h"
+#include "qgsmaptoolmovelabel.h"
+#include "qgsmaptoolrotatelabel.h"
+#include "qgsmaptoolchangelabelproperties.h"
 
 //
 // Conditional Includes
@@ -580,6 +583,8 @@
   delete mMapTools.mDeletePart;
   delete mMapTools.mAddIsland;
   delete mMapTools.mNodeTool;
+  delete mMapTools.mMoveLabel;
+  delete mMapTools.mChangeLabelProperties;
 
   delete mPythonUtils;
 
@@ -1229,6 +1234,18 @@
   mActionAbout->setMenuRole( QAction::AboutRole ); // put in application menu on Mac OS X
   connect( mActionAbout, SIGNAL( triggered() ), this, SLOT( about() ) );
 
+  mActionMoveLabel = new QAction( getThemeIcon( "mActionMoveLabel.png" ), tr( "Move Label" ), this );
+  mActionMoveLabel->setStatusTip( tr( "Move labels interactively" ) );
+  connect( mActionMoveLabel, SIGNAL( triggered() ), this, SLOT( moveLabel() ) );
+
+  mActionRotateLabel = new QAction( getThemeIcon( "mActionRotateLabel.png" ), tr( "Rotate Label" ), this );
+  mActionRotateLabel->setStatusTip( tr( "Rotate labels interactively" ) );
+  connect( mActionRotateLabel, SIGNAL( triggered() ), this, SLOT( rotateLabel() ) );
+
+  mActionChangeLabelProperties = new QAction( getThemeIcon( "mActionChangeLabelProperties.png" ), tr( "Change label" ), this );
+  mActionChangeLabelProperties->setStatusTip( tr( "Change label properties" ) );
+  connect( mActionChangeLabelProperties, SIGNAL( triggered() ), this, SLOT( changeLabelProperties() ) );
+
   mActionStyleManagerV2 = new QAction( tr( "Style manager..." ), this );
   shortcuts->registerAction( mActionStyleManagerV2 );
   mActionStyleManagerV2->setStatusTip( tr( "Show style manager V2" ) );
@@ -1349,6 +1366,12 @@
   mMapToolGroup->addAction( mActionNodeTool );
   mActionRotatePointSymbols->setCheckable( true );
   mMapToolGroup->addAction( mActionRotatePointSymbols );
+  mActionMoveLabel->setCheckable( true );
+  mMapToolGroup->addAction( mActionMoveLabel );
+  mActionRotateLabel->setCheckable( true );
+  mMapToolGroup->addAction( mActionRotateLabel );
+  mActionChangeLabelProperties->setCheckable( true );
+  mMapToolGroup->addAction( mActionChangeLabelProperties );
 }
 
 void QgisApp::createMenus()
@@ -1606,7 +1629,7 @@
   // don't add it yet, wait for a plugin
   mDatabaseMenu = new QMenu( tr( "&Database" ) );
 
-  
+
   // Raster Menu
 
   mRasterMenu = menuBar()->addMenu( tr( "&Raster" ) );
@@ -1843,6 +1866,15 @@
   mHelpToolBar->addAction( mActionHelpContents );
   mHelpToolBar->addAction( QWhatsThis::createAction() );
   mToolbarMenu->addAction( mHelpToolBar->toggleViewAction() );
+
+  //Label Toolbar
+  mLabelToolBar = addToolBar( tr( "Label" ) );
+  mLabelToolBar->setIconSize( myIconSize );
+  mLabelToolBar->setObjectName( "Label" );
+  mLabelToolBar->addAction( mActionMoveLabel );
+  mLabelToolBar->addAction( mActionRotateLabel );
+  mLabelToolBar->addAction( mActionChangeLabelProperties );
+  mToolbarMenu->addAction( mLabelToolBar->toggleViewAction() );
 }
 
 void QgisApp::createStatusBar()
@@ -2245,6 +2277,12 @@
   mMapTools.mNodeTool->setAction( mActionNodeTool );
   mMapTools.mRotatePointSymbolsTool = new QgsMapToolRotatePointSymbols( mMapCanvas );
   mMapTools.mRotatePointSymbolsTool->setAction( mActionRotatePointSymbols );
+  mMapTools.mMoveLabel = new QgsMapToolMoveLabel( mMapCanvas );
+  mMapTools.mMoveLabel->setAction( mActionMoveLabel );
+  mMapTools.mRotateLabel = new QgsMapToolRotateLabel( mMapCanvas );
+  mMapTools.mRotateLabel->setAction( mActionRotateLabel );
+  mMapTools.mChangeLabelProperties = new QgsMapToolChangeLabelProperties( mMapCanvas );
+  mMapTools.mChangeLabelProperties->setAction( mActionChangeLabelProperties );
   //ensure that non edit tool is initialised or we will get crashes in some situations
   mNonEditMapTool = mMapTools.mPan;
 }
@@ -4250,6 +4288,21 @@
   return true;
 }
 
+void QgisApp::moveLabel()
+{
+  mMapCanvas->setMapTool( mMapTools.mMoveLabel );
+}
+
+void QgisApp::rotateLabel()
+{
+  mMapCanvas->setMapTool( mMapTools.mRotateLabel );
+}
+
+void QgisApp::changeLabelProperties()
+{
+  mMapCanvas->setMapTool( mMapTools.mChangeLabelProperties );
+}
+
 QList<QgsAnnotationItem*> QgisApp::annotationItems()
 {
   QList<QgsAnnotationItem*> itemList;

Modified: trunk/qgis/src/app/qgisapp.h
===================================================================
--- trunk/qgis/src/app/qgisapp.h	2010-11-17 09:57:44 UTC (rev 14696)
+++ trunk/qgis/src/app/qgisapp.h	2010-11-17 15:01:15 UTC (rev 14697)
@@ -757,6 +757,13 @@
 
     bool loadAnnotationItemsFromProject( const QDomDocument& doc );
 
+    //! Activates the move label tool
+    void moveLabel();
+    //! Activates rotate label tool
+    void rotateLabel();
+    //! Activates label property tool
+    void changeLabelProperties();
+
   signals:
     /** emitted when a key is pressed and we want non widget sublasses to be able
       to pick up on this (e.g. maplayer) */
@@ -866,6 +873,7 @@
     QToolBar *mAttributesToolBar;
     QToolBar *mPluginToolBar;
     QToolBar *mHelpToolBar;
+    QToolBar *mLabelToolBar;
 
     // actions for menus and toolbars -----------------
 
@@ -1002,6 +1010,10 @@
     QAction *mActionHelpSeparator2;
     QAction *mActionAbout;
 
+    QAction *mActionMoveLabel;
+    QAction *mActionRotateLabel;
+    QAction *mActionChangeLabelProperties;
+
     QAction *mActionUseRendererV2;
     QAction *mActionStyleManagerV2;
 
@@ -1069,6 +1081,9 @@
         QgsMapTool* mAnnotation;
         QgsMapTool* mFormAnnotation;
         QgsMapTool* mTextAnnotation;
+        QgsMapTool* mMoveLabel;
+        QgsMapTool* mRotateLabel;
+        QgsMapTool* mChangeLabelProperties;
     } mMapTools;
 
     QgsMapTool *mNonEditMapTool;

Added: trunk/qgis/src/app/qgsmaptoolchangelabelproperties.cpp
===================================================================
--- trunk/qgis/src/app/qgsmaptoolchangelabelproperties.cpp	                        (rev 0)
+++ trunk/qgis/src/app/qgsmaptoolchangelabelproperties.cpp	2010-11-17 15:01:15 UTC (rev 14697)
@@ -0,0 +1,76 @@
+/***************************************************************************
+                          qgsmaptoolchangelabelproperties.cpp
+                          ---------------------------------
+    begin                : 2010-11-11
+    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 "qgsmaptoolchangelabelproperties.h"
+#include "qgslabelpropertydialog.h"
+#include "qgsmapcanvas.h"
+#include "qgsrubberband.h"
+#include "qgsvectorlayer.h"
+
+QgsMapToolChangeLabelProperties::QgsMapToolChangeLabelProperties( QgsMapCanvas* canvas ): QgsMapToolLabel( canvas )
+{
+}
+
+QgsMapToolChangeLabelProperties::~QgsMapToolChangeLabelProperties()
+{
+}
+
+void QgsMapToolChangeLabelProperties::canvasPressEvent( QMouseEvent * e )
+{
+  deleteRubberBands();
+
+  if ( !labelAtPosition( e, mCurrentLabelPos ) )
+  {
+    return;
+  }
+
+  QgsVectorLayer* vlayer = currentLayer();
+  if ( !vlayer || !vlayer->isEditable() )
+  {
+    return;
+  }
+
+  createRubberBands();
+}
+
+void QgsMapToolChangeLabelProperties::canvasReleaseEvent( QMouseEvent * e )
+{
+  QgsVectorLayer* vlayer = currentLayer();
+  if ( mLabelRubberBand && mCanvas && vlayer )
+  {
+    QgsLabelPropertyDialog d( mCurrentLabelPos.layerID, mCurrentLabelPos.featureId, mCanvas->mapRenderer() );
+    if ( d.exec() == QDialog::Accepted )
+    {
+      const QgsAttributeMap& changes = d.changedProperties();
+      if ( changes.size() > 0 )
+      {
+        vlayer->beginEditCommand( tr( "Label properties changed" ) );
+
+        QgsAttributeMap::const_iterator changeIt = changes.constBegin();
+        for ( ; changeIt != changes.constEnd(); ++changeIt )
+        {
+          vlayer->changeAttributeValue( mCurrentLabelPos.featureId, changeIt.key(), changeIt.value(), false );
+        }
+
+        vlayer->endEditCommand();
+        mCanvas->refresh();
+      }
+    }
+    deleteRubberBands();
+  }
+}
+

Added: trunk/qgis/src/app/qgsmaptoolchangelabelproperties.h
===================================================================
--- trunk/qgis/src/app/qgsmaptoolchangelabelproperties.h	                        (rev 0)
+++ trunk/qgis/src/app/qgsmaptoolchangelabelproperties.h	2010-11-17 15:01:15 UTC (rev 14697)
@@ -0,0 +1,34 @@
+/***************************************************************************
+                          qgsmaptoolchangelabelproperties.h
+                          ---------------------------------
+    begin                : 2010-11-11
+    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 QGSMAPTOOLCHANGELABELPROPERTIES_H
+#define QGSMAPTOOLCHANGELABELPROPERTIES_H
+
+#include "qgsmaptoollabel.h"
+
+class QgsMapToolChangeLabelProperties: public QgsMapToolLabel
+{
+  public:
+    QgsMapToolChangeLabelProperties( QgsMapCanvas* canvas );
+    ~QgsMapToolChangeLabelProperties();
+
+    virtual void canvasPressEvent( QMouseEvent * e );
+    virtual void canvasReleaseEvent( QMouseEvent * e );
+
+};
+
+#endif // QGSMAPTOOLCHANGELABEL_H

Added: trunk/qgis/src/app/qgsmaptoollabel.cpp
===================================================================
--- trunk/qgis/src/app/qgsmaptoollabel.cpp	                        (rev 0)
+++ trunk/qgis/src/app/qgsmaptoollabel.cpp	2010-11-17 15:01:15 UTC (rev 14697)
@@ -0,0 +1,347 @@
+/***************************************************************************
+                          qgsmaptoollabel.cpp
+                          --------------------
+    begin                : 2010-11-03
+    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 "qgsmaptoollabel.h"
+#include "qgsmapcanvas.h"
+#include "qgsmaplayerregistry.h"
+#include "qgsrubberband.h"
+#include "qgsvectorlayer.h"
+#include <QMouseEvent>
+
+QgsMapToolLabel::QgsMapToolLabel( QgsMapCanvas* canvas ): QgsMapTool( canvas ), mLabelRubberBand( 0 ), mFeatureRubberBand( 0 ), mFixPointRubberBand( 0 )
+{
+}
+
+QgsMapToolLabel::~QgsMapToolLabel()
+{
+  delete mLabelRubberBand;
+  delete mFeatureRubberBand;
+  delete mFixPointRubberBand;
+}
+
+bool QgsMapToolLabel::labelAtPosition( QMouseEvent* e, QgsLabelPosition& p )
+{
+  QgsPoint pt = toMapCoordinates( e->pos() );
+  QgsLabelingEngineInterface* labelingEngine = mCanvas->mapRenderer()->labelingEngine();
+  if ( labelingEngine )
+  {
+    QList<QgsLabelPosition> labelPosList = labelingEngine->labelsAtPosition( pt );
+    QList<QgsLabelPosition>::const_iterator posIt = labelPosList.constBegin();
+    if ( posIt != labelPosList.constEnd() )
+    {
+      p = *posIt;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void QgsMapToolLabel::createRubberBands( )
+{
+  delete mLabelRubberBand;
+  delete mFeatureRubberBand;
+
+  //label rubber band
+  QgsRectangle rect = mCurrentLabelPos.labelRect;
+  mLabelRubberBand = new QgsRubberBand( mCanvas, false );
+  mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMinimum() ) );
+  mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMaximum() ) );
+  mLabelRubberBand->addPoint( QgsPoint( rect.xMaximum(), rect.yMaximum() ) );
+  mLabelRubberBand->addPoint( QgsPoint( rect.xMaximum(), rect.yMinimum() ) );
+  mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMinimum() ) );
+  mLabelRubberBand->setColor( Qt::green );
+  mLabelRubberBand->setWidth( 3 );
+  mLabelRubberBand->show();
+
+  //feature rubber band
+  QgsVectorLayer* vlayer = currentLayer();
+  if ( vlayer )
+  {
+    QgsFeature f;
+    if ( currentFeature( f, true ) )
+    {
+      QgsGeometry* geom = f.geometry();
+      if ( geom )
+      {
+        mFeatureRubberBand = new QgsRubberBand( mCanvas, geom->type() == QGis::Polygon );
+        mFeatureRubberBand->setColor( Qt::red );
+        mFeatureRubberBand->setToGeometry( geom, vlayer );
+        mFeatureRubberBand->show();
+      }
+    }
+
+    //fixpoint rubber band
+    QgsPoint fixPoint;
+    if ( rotationPoint( fixPoint ) )
+    {
+      QgsGeometry* pointGeom = QgsGeometry::fromPoint( fixPoint );
+      mFixPointRubberBand = new QgsRubberBand( mCanvas, false );
+      mFixPointRubberBand->setColor( Qt::blue );
+      mFixPointRubberBand->setToGeometry( pointGeom, vlayer );
+      mFixPointRubberBand->show();
+      delete pointGeom;
+    }
+  }
+}
+
+void QgsMapToolLabel::deleteRubberBands()
+{
+  delete mLabelRubberBand; mLabelRubberBand = 0;
+  delete mFeatureRubberBand; mFeatureRubberBand = 0;
+  delete mFixPointRubberBand; mFixPointRubberBand = 0;
+}
+
+QgsVectorLayer* QgsMapToolLabel::currentLayer()
+{
+  QgsVectorLayer* vlayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( mCurrentLabelPos.layerID ) );
+  return vlayer;
+}
+
+QgsPalLayerSettings& QgsMapToolLabel::currentLabelSettings( bool* ok )
+{
+  QgsVectorLayer* vlayer = currentLayer();
+  if ( vlayer )
+  {
+    QgsPalLabeling* labelEngine = dynamic_cast<QgsPalLabeling*>( mCanvas->mapRenderer()->labelingEngine() );
+    if ( labelEngine )
+    {
+      if ( ok )
+      {
+        *ok = true;
+      }
+      return labelEngine->layer( mCurrentLabelPos.layerID );
+    }
+  }
+
+  if ( ok )
+  {
+    *ok = false;
+  }
+  return mInvalidLabelSettings;
+}
+
+QString QgsMapToolLabel::currentLabelText()
+{
+  QgsVectorLayer* vlayer = currentLayer();
+  if ( !vlayer )
+  {
+    return "";
+  }
+
+  QString labelField = vlayer->customProperty( "labeling/fieldName" ).toString();
+  if ( !labelField.isEmpty() )
+  {
+    int labelFieldId = vlayer->fieldNameIndex( labelField );
+    QgsFeature f;
+    if ( vlayer->featureAtId( mCurrentLabelPos.featureId, f, false, true ) )
+    {
+      return f.attributeMap()[labelFieldId].toString();
+    }
+  }
+  return "";
+}
+
+void QgsMapToolLabel::currentAlignment( QString& hali, QString& vali )
+{
+  hali = "Left";
+  vali = "Bottom";
+
+  QgsFeature f;
+  if ( !currentFeature( f ) )
+  {
+    return;
+  }
+  const QgsAttributeMap& featureAttributes = f.attributeMap();
+
+  bool settingsOk;
+  QgsPalLayerSettings& labelSettings = currentLabelSettings( &settingsOk );
+  if ( settingsOk )
+  {
+    QMap< QgsPalLayerSettings::DataDefinedProperties, int > ddProperties = labelSettings.dataDefinedProperties;
+
+    QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator haliIter = ddProperties.find( QgsPalLayerSettings::Hali );
+    if ( haliIter != ddProperties.constEnd() )
+    {
+      hali = featureAttributes[*haliIter].toString();
+    }
+
+    QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator valiIter = ddProperties.find( QgsPalLayerSettings::Vali );
+    if ( valiIter != ddProperties.constEnd() )
+    {
+      vali = featureAttributes[*valiIter].toString();
+    }
+  }
+}
+
+bool QgsMapToolLabel::currentFeature( QgsFeature& f, bool fetchGeom )
+{
+  QgsVectorLayer* vlayer = currentLayer();
+  if ( !vlayer )
+  {
+    return false;
+  }
+  return vlayer->featureAtId( mCurrentLabelPos.featureId, f, fetchGeom, true );
+}
+
+QFont QgsMapToolLabel::labelFontCurrentFeature()
+{
+  QFont font;
+  QgsVectorLayer* vlayer = currentLayer();
+
+  bool labelSettingsOk;
+  QgsPalLayerSettings& layerSettings = currentLabelSettings( &labelSettingsOk );
+
+  if ( labelSettingsOk && vlayer )
+  {
+    font = layerSettings.textFont;
+
+    QgsFeature f;
+    if ( vlayer->featureAtId( mCurrentLabelPos.featureId, f, false, true ) )
+    {
+      const QgsAttributeMap& attributes = f.attributeMap();
+      QMap< QgsPalLayerSettings::DataDefinedProperties, int > ddProperties = layerSettings.dataDefinedProperties;
+
+      //size
+      QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator sizeIt = ddProperties.find( QgsPalLayerSettings::Size );
+      if ( sizeIt != ddProperties.constEnd() )
+      {
+        font.setPointSizeF( attributes[*sizeIt].toDouble() );
+      }
+
+      //family
+      QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator familyIt = ddProperties.find( QgsPalLayerSettings::Family );
+      if ( familyIt != ddProperties.constEnd() )
+      {
+        font.setFamily( attributes[*sizeIt].toString() );
+      }
+
+      //underline
+      QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator underlineIt = ddProperties.find( QgsPalLayerSettings::Underline );
+      if ( familyIt != ddProperties.constEnd() )
+      {
+        font.setUnderline( attributes[*underlineIt].toBool() );
+      }
+
+      //strikeout
+      QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator strikeoutIt = ddProperties.find( QgsPalLayerSettings::Strikeout );
+      if ( strikeoutIt != ddProperties.constEnd() )
+      {
+        font.setStrikeOut( attributes[*strikeoutIt].toBool() );
+      }
+
+      //bold
+      QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator boldIt = ddProperties.find( QgsPalLayerSettings::Bold );
+      if ( boldIt != ddProperties.constEnd() )
+      {
+        font.setBold( attributes[*boldIt].toBool() );
+      }
+
+      //italic
+      QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator italicIt = ddProperties.find( QgsPalLayerSettings::Italic );
+      if ( italicIt != ddProperties.constEnd() )
+      {
+        font.setItalic( attributes[*italicIt].toBool() );
+      }
+    }
+  }
+
+  return font;
+}
+
+bool QgsMapToolLabel::rotationPoint( QgsPoint& pos )
+{
+  QVector<QgsPoint> cornerPoints = mCurrentLabelPos.cornerPoints;
+  if ( cornerPoints.size() < 4 )
+  {
+    return false;
+  }
+
+  if ( mCurrentLabelPos.upsideDown )
+  {
+    pos = mCurrentLabelPos.cornerPoints.at( 2 );
+  }
+  else
+  {
+    pos = mCurrentLabelPos.cornerPoints.at( 0 );
+  }
+
+  //adapt pos depending on data defined alignment
+  QString haliString, valiString;
+  currentAlignment( haliString, valiString );
+
+  QFont labelFont = labelFontCurrentFeature();
+  QFontMetricsF labelFontMetrics( labelFont );
+
+  //label text?
+  QString labelText = currentLabelText();
+
+  bool labelSettingsOk;
+  QgsPalLayerSettings& labelSettings = currentLabelSettings( &labelSettingsOk );
+  if ( !labelSettingsOk )
+  {
+    return false;
+  }
+
+  double labelSizeX, labelSizeY;
+  labelSettings.calculateLabelSize( &labelFontMetrics, labelText, labelSizeX, labelSizeY );
+
+  double xdiff = 0;
+  double ydiff = 0;
+
+  if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
+  {
+    xdiff = labelSizeX / 2.0;
+  }
+  else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
+  {
+    xdiff = labelSizeX;
+  }
+
+  if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 || valiString.compare( "Cap", Qt::CaseInsensitive ) == 0 )
+  {
+    ydiff = labelSizeY;
+  }
+  else
+  {
+    double descentRatio = labelFontMetrics.descent() / labelFontMetrics.height();
+    if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
+    {
+      ydiff = labelSizeY * descentRatio;
+    }
+    else if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
+    {
+      ydiff = labelSizeY * descentRatio;
+      ydiff = labelSizeY * 0.5 * ( 1 - descentRatio );
+    }
+  }
+
+  double angle = mCurrentLabelPos.rotation;
+  double xd = xdiff * cos( angle ) - ydiff * sin( angle );
+  double yd = xdiff * sin( angle ) + ydiff * cos( angle );
+  if ( mCurrentLabelPos.upsideDown )
+  {
+    pos.setX( pos.x() - xd );
+    pos.setY( pos.y() - yd );
+  }
+  else
+  {
+    pos.setX( pos.x() + xd );
+    pos.setY( pos.y() + yd );
+  }
+  return true;
+}

Added: trunk/qgis/src/app/qgsmaptoollabel.h
===================================================================
--- trunk/qgis/src/app/qgsmaptoollabel.h	                        (rev 0)
+++ trunk/qgis/src/app/qgsmaptoollabel.h	2010-11-17 15:01:15 UTC (rev 14697)
@@ -0,0 +1,80 @@
+/***************************************************************************
+                          qgsmaptoollabel.h
+                          --------------------
+    begin                : 2010-11-03
+    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 QGSMAPTOOLLABEL_H
+#define QGSMAPTOOLLABEL_H
+
+#include "qgsmaptool.h"
+#include "qgsmaprenderer.h"
+#include "qgspallabeling.h"
+#include "qgspoint.h"
+class QgsRubberBand;
+
+/**Base class for map tools that modify label properties*/
+class QgsMapToolLabel: public QgsMapTool
+{
+  public:
+    QgsMapToolLabel( QgsMapCanvas* canvas );
+    ~QgsMapToolLabel();
+
+  protected:
+    QgsRubberBand* mLabelRubberBand;
+    QgsRubberBand* mFeatureRubberBand;
+    /**Shows label fixpoint (left/bottom by default)*/
+    QgsRubberBand* mFixPointRubberBand;
+
+    /**Currently dragged label position*/
+    QgsLabelPosition mCurrentLabelPos;
+
+    /**Returns label position for mouse click location
+      @param e mouse event
+      @param p out: label position
+      @return true in case of success, false if no label at this location*/
+    bool labelAtPosition( QMouseEvent* e, QgsLabelPosition& p );
+
+    /**Finds out rotation point of current label position
+      @return true in case of success*/
+    bool rotationPoint( QgsPoint& pos );
+
+    /**Creates label / feature / fixpoint rubber bands for the current label position*/
+    void createRubberBands();
+
+    /**Removes label / feature / fixpoint rubber bands*/
+    void deleteRubberBands();
+
+    /**Returns vector layer for current label position*/
+    QgsVectorLayer* currentLayer();
+
+    /**Returns layer settings of current label position*/
+    QgsPalLayerSettings& currentLabelSettings( bool* ok );
+
+    QString currentLabelText();
+
+    void currentAlignment( QString& hali, QString& vali );
+
+    /**Gets vector feature for current label pos
+      @return true in case of success*/
+    bool currentFeature( QgsFeature& f, bool fetchGeom = false );
+
+    /**Returns the font for the current feature (considering default font and data defined properties*/
+    QFont labelFontCurrentFeature();
+
+  private:
+    QgsPalLayerSettings mInvalidLabelSettings;
+};
+
+#endif // QGSMAPTOOLLABEL_H

Added: trunk/qgis/src/app/qgsmaptoolmovelabel.cpp
===================================================================
--- trunk/qgis/src/app/qgsmaptoolmovelabel.cpp	                        (rev 0)
+++ trunk/qgis/src/app/qgsmaptoolmovelabel.cpp	2010-11-17 15:01:15 UTC (rev 14697)
@@ -0,0 +1,200 @@
+/***************************************************************************
+                          qgsmaptoolmovelabel.cpp
+                          -----------------------
+    begin                : 2010-11-03
+    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 "qgsmaptoolmovelabel.h"
+#include "qgsmapcanvas.h"
+#include "qgsmaplayerregistry.h"
+#include "qgsrubberband.h"
+#include "qgsvectorlayer.h"
+#include <QMouseEvent>
+
+QgsMapToolMoveLabel::QgsMapToolMoveLabel( QgsMapCanvas* canvas ): QgsMapToolLabel( canvas )
+{
+}
+
+QgsMapToolMoveLabel::~QgsMapToolMoveLabel()
+{
+}
+
+void QgsMapToolMoveLabel::canvasPressEvent( QMouseEvent * e )
+{
+  deleteRubberBands();
+
+  if ( !labelAtPosition( e, mCurrentLabelPos ) )
+  {
+    return;
+  }
+
+  QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( mCurrentLabelPos.layerID );
+  if ( !layer )
+  {
+    return;
+  }
+
+  int xCol, yCol;
+  if ( layerIsMoveable( layer, xCol, yCol ) )
+  {
+    mStartPointMapCoords = toMapCoordinates( e->pos() );
+    mClickOffsetX = mStartPointMapCoords.x() - mCurrentLabelPos.labelRect.xMinimum();
+    mClickOffsetY = mStartPointMapCoords.y() - mCurrentLabelPos.labelRect.yMinimum();
+    createRubberBands();
+  }
+}
+
+void QgsMapToolMoveLabel::canvasMoveEvent( QMouseEvent * e )
+{
+  if ( mLabelRubberBand )
+  {
+    QgsPoint pointCanvasCoords = toMapCoordinates( e->pos() );
+    double offsetX = pointCanvasCoords.x() - mStartPointMapCoords.x();
+    double offsetY = pointCanvasCoords.y() - mStartPointMapCoords.y();
+    mLabelRubberBand->setTranslationOffset( offsetX, offsetY );
+    mLabelRubberBand->updatePosition();
+    mLabelRubberBand->update();
+    mFixPointRubberBand->setTranslationOffset( offsetX, offsetY );
+    mFixPointRubberBand->updatePosition();
+    mFixPointRubberBand->update();
+  }
+}
+
+void QgsMapToolMoveLabel::canvasReleaseEvent( QMouseEvent * e )
+{
+  if ( !mLabelRubberBand )
+  {
+    return;
+  }
+
+  deleteRubberBands();
+
+  QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( mCurrentLabelPos.layerID );
+  if ( !layer )
+  {
+    return;
+  }
+
+  QgsVectorLayer* vlayer = dynamic_cast<QgsVectorLayer*>( layer );
+  if ( !vlayer )
+  {
+    return;
+  }
+
+  if ( !vlayer->isEditable() )
+  {
+    return;
+  }
+
+  QgsPoint releaseCoords = toMapCoordinates( e->pos() );
+  double xdiff = releaseCoords.x() - mStartPointMapCoords.x();
+  double ydiff = releaseCoords.y() - mStartPointMapCoords.y();
+
+  int xCol, yCol;
+  double xPosOrig, yPosOrig;
+  bool xSuccess, ySuccess;
+
+  if ( !dataDefinedPosition( vlayer, mCurrentLabelPos.featureId, xPosOrig, xSuccess, yPosOrig, ySuccess, xCol, yCol ) )
+  {
+    return;
+  }
+
+  double xPosNew, yPosNew;
+
+  if ( !xSuccess || !ySuccess )
+  {
+    xPosNew = releaseCoords.x() - mClickOffsetX;
+    yPosNew = releaseCoords.y() - mClickOffsetY;
+
+    //todo: consider hali/vali if there
+  }
+  else
+  {
+    xPosNew = xPosOrig + xdiff;
+    yPosNew = yPosOrig + ydiff;
+  }
+
+  vlayer->beginEditCommand( tr( "Label moved" ) );
+  vlayer->changeAttributeValue( mCurrentLabelPos.featureId, xCol, xPosNew, false );
+  vlayer->changeAttributeValue( mCurrentLabelPos.featureId, yCol, yPosNew, false );
+  vlayer->endEditCommand();
+
+  mCanvas->refresh();
+}
+
+bool QgsMapToolMoveLabel::dataDefinedPosition( QgsVectorLayer* vlayer, int featureId, double& x, bool& xSuccess, double& y, bool& ySuccess, int& xCol, int& yCol ) const
+{
+  xSuccess = false;
+  ySuccess = false;
+
+  if ( !vlayer )
+  {
+    return false;
+  }
+
+  if ( !layerIsMoveable( vlayer, xCol, yCol ) )
+  {
+    return false;
+  }
+
+  QgsFeature f;
+  if ( !vlayer->featureAtId( featureId, f, false, true ) )
+  {
+    return false;
+  }
+
+  QgsAttributeMap attributes = f.attributeMap();
+  x = attributes[xCol].toDouble( &xSuccess );
+  y = attributes[yCol].toDouble( &ySuccess );
+
+  return true;
+}
+
+bool QgsMapToolMoveLabel::layerIsMoveable( const QgsMapLayer* ml, int& xCol, int& yCol ) const
+{
+  const QgsVectorLayer* vlayer = dynamic_cast<const QgsVectorLayer*>( ml );
+  if ( !vlayer || !vlayer->isEditable() )
+  {
+    return false;
+  }
+
+  bool xColOk, yColOk;
+
+  QVariant xColumn = ml->customProperty( "labeling/dataDefinedProperty9" );
+  if ( !xColumn.isValid() )
+  {
+    return false;
+  }
+  xCol = xColumn.toInt( &xColOk );
+  if ( !xColOk )
+  {
+    return false;
+  }
+
+  QVariant yColumn = ml->customProperty( "labeling/dataDefinedProperty10" );
+  if ( !yColumn.isValid() )
+  {
+    return false;
+  }
+  yCol = yColumn.toInt( &yColOk );
+  if ( !yColOk )
+  {
+    return false;
+  }
+
+  return true;
+}
+
+
+

Added: trunk/qgis/src/app/qgsmaptoolmovelabel.h
===================================================================
--- trunk/qgis/src/app/qgsmaptoolmovelabel.h	                        (rev 0)
+++ trunk/qgis/src/app/qgsmaptoolmovelabel.h	2010-11-17 15:01:15 UTC (rev 14697)
@@ -0,0 +1,61 @@
+/***************************************************************************
+                          qgsmaptoolmovelabel.h
+                          --------------------
+    begin                : 2010-11-03
+    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 QGSMAPTOOLMOVELABEL_H
+#define QGSMAPTOOLMOVELABEL_H
+
+#include "qgsmaptoollabel.h"
+
+/**A map tool for dragging label positions*/
+class QgsMapToolMoveLabel: public QgsMapToolLabel
+{
+  public:
+    QgsMapToolMoveLabel( QgsMapCanvas* canvas );
+    ~QgsMapToolMoveLabel();
+
+    virtual void canvasPressEvent( QMouseEvent * e );
+
+    virtual void canvasMoveEvent( QMouseEvent * e );
+
+    virtual void canvasReleaseEvent( QMouseEvent * e );
+
+  protected:
+    /**Get data defined position of a feature
+      @param layerId layer identification string
+      @param x out: data defined x-coordinate
+      @param xSuccess out: false if attribute value is NULL
+      @param y out: data defined y-coordinate
+      @param ySuccess out: false if attribute value is NULL
+      @param xCol out: index of the x position column
+      @param yCol out: index of the y position column
+      @return false if layer does not have data defined label position enabled*/
+    bool dataDefinedPosition( QgsVectorLayer* vlayer, int featureId, double& x, bool& xSuccess, double& y, bool& ySuccess, int& xCol, int& yCol ) const;
+
+    /**Returns true if layer move can be applied to a layer
+      @param xCol out: index of the attribute for data defined x coordinate
+      @param yCol out: index of the attribute for data defined y coordinate
+      @return true if labels of layer can be moved*/
+    bool layerIsMoveable( const QgsMapLayer* ml, int& xCol, int& yCol ) const;
+
+    /**Start point of the move in map coordinates*/
+    QgsPoint mStartPointMapCoords;
+
+    double mClickOffsetX;
+    double mClickOffsetY;
+};
+
+#endif // QGSMAPTOOLMOVELABEL_H

Added: trunk/qgis/src/app/qgsmaptoolrotatelabel.cpp
===================================================================
--- trunk/qgis/src/app/qgsmaptoolrotatelabel.cpp	                        (rev 0)
+++ trunk/qgis/src/app/qgsmaptoolrotatelabel.cpp	2010-11-17 15:01:15 UTC (rev 14697)
@@ -0,0 +1,207 @@
+/***************************************************************************
+                          qgsmaptoolrotatelabel.cpp
+                          -------------------------
+    begin                : 2010-11-09
+    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 "qgsmaptoolrotatelabel.h"
+#include "qgsmapcanvas.h"
+#include "qgsmaplayerregistry.h"
+#include "qgspallabeling.h"
+#include "qgspointrotationitem.h"
+#include "qgsrubberband.h"
+#include "qgsvectorlayer.h"
+#include <QMouseEvent>
+
+#include "qgisapp.h"
+
+QgsMapToolRotateLabel::QgsMapToolRotateLabel( QgsMapCanvas* canvas ): QgsMapToolLabel( canvas ), mRotationItem( 0 )
+{
+}
+
+QgsMapToolRotateLabel::~QgsMapToolRotateLabel()
+{
+  delete mRotationItem;
+}
+
+void QgsMapToolRotateLabel::canvasPressEvent( QMouseEvent * e )
+{
+  deleteRubberBands();
+
+  if ( !labelAtPosition( e, mCurrentLabelPos ) )
+  {
+    return;
+  }
+
+  QgsVectorLayer* vlayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( mCurrentLabelPos.layerID ) );
+  if ( !vlayer )
+  {
+    return;
+  }
+
+  if ( !rotationPoint( mRotationPoint ) )
+  {
+    return;
+  }
+
+  int rotationCol;
+  if ( layerIsRotatable( vlayer, rotationCol ) )
+  {
+    mCurrentMouseAzimuth = azimuthToCCW( mRotationPoint.azimuth( toMapCoordinates( e->pos() ) ) );
+
+
+    bool hasRotationValue;
+    if ( dataDefinedRotation( vlayer, mCurrentLabelPos.featureId, mCurrentRotation, hasRotationValue ) )
+    {
+      if ( !hasRotationValue )
+      {
+        mCurrentRotation = 0;
+      }
+      createRubberBands();
+
+      mRotationItem = new QgsPointRotationItem( mCanvas );
+      mRotationItem->setOrientation( QgsPointRotationItem::Counterclockwise );
+      mRotationItem->setSymbol( QgisApp::instance()->getThemePixmap( "mActionRotatePointSymbols.png" ).toImage() );
+      mRotationItem->setPointLocation( mRotationPoint );
+      mRotationItem->setSymbolRotation( mCurrentRotation );
+    }
+  }
+}
+
+void QgsMapToolRotateLabel::canvasMoveEvent( QMouseEvent * e )
+{
+  if ( mLabelRubberBand )
+  {
+    QgsPoint currentPoint = toMapCoordinates( e->pos() );
+    double azimuth = azimuthToCCW( mRotationPoint.azimuth( currentPoint ) );
+    double azimuthDiff = azimuth - mCurrentMouseAzimuth;
+    azimuthDiff = azimuthDiff > 180 ? azimuthDiff - 360 : azimuthDiff;
+
+    mCurrentRotation += azimuthDiff;
+    mCurrentRotation = mCurrentRotation - static_cast<float>( static_cast<int>( mCurrentRotation / 360 ) ) * 360; //mCurrentRotation % 360;
+    mCurrentRotation = mCurrentRotation < 0 ? 360 - mCurrentRotation : mCurrentRotation;
+
+    mCurrentMouseAzimuth = azimuth - static_cast<float>( static_cast<int>( azimuth / 360 ) ) * 360;
+
+    //if shift-modifier is pressed, round to 15 degrees
+    int displayValue;
+    if ( e->modifiers() & Qt::ControlModifier )
+    {
+      displayValue = roundTo15Degrees( mCurrentRotation );
+      mCtrlPressed = true;
+    }
+    else
+    {
+      displayValue = ( int )( mCurrentRotation );
+      mCtrlPressed = false;
+    }
+
+    if ( mRotationItem )
+    {
+      mRotationItem->setSymbolRotation( displayValue );
+      mRotationItem->update();
+    }
+  }
+}
+
+void QgsMapToolRotateLabel::canvasReleaseEvent( QMouseEvent * e )
+{
+  deleteRubberBands();
+  delete mRotationItem;
+  mRotationItem = 0;
+
+  QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( mCurrentLabelPos.layerID );
+  if ( !layer )
+  {
+    return;
+  }
+
+  QgsVectorLayer* vlayer = dynamic_cast<QgsVectorLayer*>( layer );
+  if ( !vlayer )
+  {
+    return;
+  }
+
+  int rotationCol;
+  if ( !layerIsRotatable( vlayer, rotationCol ) )
+  {
+    return;
+  }
+
+  double rotation = mCtrlPressed ? roundTo15Degrees( mCurrentRotation ) : mCurrentRotation;
+
+  vlayer->beginEditCommand( tr( "Label rotated" ) );
+  vlayer->changeAttributeValue( mCurrentLabelPos.featureId, rotationCol, mCurrentRotation, false );
+  vlayer->endEditCommand();
+  mCanvas->refresh();
+}
+
+bool QgsMapToolRotateLabel::layerIsRotatable( const QgsMapLayer* layer, int& rotationCol ) const
+{
+  const QgsVectorLayer* vlayer = dynamic_cast<const QgsVectorLayer*>( layer );
+  if ( !vlayer || !vlayer->isEditable() )
+  {
+    return false;
+  }
+
+  QVariant rotation = layer->customProperty( "labeling/dataDefinedProperty14" );
+  if ( !rotation.isValid() )
+  {
+    return false;
+  }
+
+  bool rotationOk;
+  rotationCol = rotation.toInt( &rotationOk );
+  if ( !rotationOk )
+  {
+    return false;
+  }
+  return true;
+}
+
+bool QgsMapToolRotateLabel::dataDefinedRotation( QgsVectorLayer* vlayer, int featureId, double& rotation, bool& rotationSuccess )
+{
+  rotationSuccess = false;
+  if ( !vlayer )
+  {
+    return false;
+  }
+
+  int rotationCol;
+  if ( !layerIsRotatable( vlayer, rotationCol ) )
+  {
+    return false;
+  }
+
+  QgsFeature f;
+  if ( !vlayer->featureAtId( featureId, f, false, true ) )
+  {
+    return false;
+  }
+
+  QgsAttributeMap attributes = f.attributeMap();
+  rotation = attributes[rotationCol].toDouble( &rotationSuccess );
+  return true;
+}
+
+int QgsMapToolRotateLabel::roundTo15Degrees( double n )
+{
+  int m = ( int )( n / 15.0 + 0.5 );
+  return ( m * 15 );
+}
+
+double QgsMapToolRotateLabel::azimuthToCCW( double a )
+{
+  return ( a > 0 ? 360 - a : -a );
+}

Added: trunk/qgis/src/app/qgsmaptoolrotatelabel.h
===================================================================
--- trunk/qgis/src/app/qgsmaptoolrotatelabel.h	                        (rev 0)
+++ trunk/qgis/src/app/qgsmaptoolrotatelabel.h	2010-11-17 15:01:15 UTC (rev 14697)
@@ -0,0 +1,59 @@
+/***************************************************************************
+                          qgsmaptoolrotatelabel.h
+                          -----------------------
+    begin                : 2010-11-09
+    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 QGSMAPTOOLROTATELABEL_H
+#define QGSMAPTOOLROTATELABEL_H
+
+#include "qgsmaptoollabel.h"
+class QgsPointRotationItem;
+
+class QgsMapToolRotateLabel: public QgsMapToolLabel
+{
+  public:
+    QgsMapToolRotateLabel( QgsMapCanvas* canvas );
+    ~QgsMapToolRotateLabel();
+
+    virtual void canvasPressEvent( QMouseEvent * e );
+    virtual void canvasMoveEvent( QMouseEvent * e );
+    virtual void canvasReleaseEvent( QMouseEvent * e );
+
+  protected:
+    /**Checks if labels in a layer can be rotated
+      @param rotationCol out: attribute column for data defined label rotation*/
+    bool layerIsRotatable( const QgsMapLayer* layer, int& rotationCol ) const;
+    /**Returns data defined rotation of a feature.
+      @param rotation out: rotation value
+      @param rotationSuccess out: false if rotation value is NULL
+      @return true if data defined rotation is enabled on the layer
+      */
+    bool dataDefinedRotation( QgsVectorLayer* vlayer, int featureId, double& rotation, bool& rotationSuccess );
+
+    static int roundTo15Degrees( double n );
+    /**Converts azimuth value to counterclockwise 0 - 360*/
+    static double azimuthToCCW( double a );
+
+
+    double mCurrentRotation;
+    double mCurrentMouseAzimuth;
+    QgsPoint mRotationPoint;
+    QgsPointRotationItem* mRotationItem;
+
+    /**True if ctrl was pressed during the last mouse move event*/
+    bool mCtrlPressed;
+};
+
+#endif // QGSMAPTOOLROTATELABEL_H

Modified: trunk/qgis/src/app/qgspointrotationitem.cpp
===================================================================
--- trunk/qgis/src/app/qgspointrotationitem.cpp	2010-11-17 09:57:44 UTC (rev 14696)
+++ trunk/qgis/src/app/qgspointrotationitem.cpp	2010-11-17 15:01:15 UTC (rev 14697)
@@ -22,7 +22,7 @@
 #include <math.h>
 #endif
 
-QgsPointRotationItem::QgsPointRotationItem( QgsMapCanvas* canvas ): QgsMapCanvasItem( canvas ), mRotation( 0.0 )
+QgsPointRotationItem::QgsPointRotationItem( QgsMapCanvas* canvas ): QgsMapCanvasItem( canvas ), mOrientation( Clockwise ), mRotation( 0.0 )
 {
   //setup font
   mFont.setPointSize( 12 );
@@ -55,12 +55,11 @@
   {
     h = sqrt(( double ) mPixmap.width() * mPixmap.width() + mPixmap.height() * mPixmap.height() ) / 2; //the half of the item diagonal
     dAngel = acos( mPixmap.width() / ( h * 2 ) ) * 180 / M_PI; //the diagonal angel of the original rect
-    x = h * cos(( mRotation - dAngel ) * M_PI / 180 );
-    y = h * sin(( mRotation - dAngel ) * M_PI / 180 );
+    x = h * cos(( painterRotation( mRotation ) - dAngel ) * M_PI / 180 );
+    y = h * sin(( painterRotation( mRotation ) - dAngel ) * M_PI / 180 );
   }
 
-  //painter->translate(-mPixmap.width() / 2.0, -mPixmap.width() / 2.0);
-  painter->rotate( mRotation );
+  painter->rotate( painterRotation( mRotation ) );
   painter->translate( x - mPixmap.width() / 2.0, -y - mPixmap.height() / 2.0 );
   painter->drawPixmap( 0, 0, mPixmap );
 
@@ -109,3 +108,13 @@
   }
 }
 
+int QgsPointRotationItem::painterRotation( int rotation ) const
+{
+  if ( mOrientation == Clockwise )
+  {
+    return rotation;
+  }
+
+  return 360 - ( rotation % 360 );
+}
+

Modified: trunk/qgis/src/app/qgspointrotationitem.h
===================================================================
--- trunk/qgis/src/app/qgspointrotationitem.h	2010-11-17 09:57:44 UTC (rev 14696)
+++ trunk/qgis/src/app/qgspointrotationitem.h	2010-11-17 15:01:15 UTC (rev 14697)
@@ -24,6 +24,13 @@
 class QgsPointRotationItem: public QgsMapCanvasItem
 {
   public:
+
+    enum Orientation
+    {
+      Clockwise = 0,
+      Counterclockwise
+    };
+
     QgsPointRotationItem( QgsMapCanvas* canvas );
     ~QgsPointRotationItem();
 
@@ -39,8 +46,15 @@
     /**Sets rotation symbol from image (takes ownership)*/
     void setSymbol( const QImage& symbolImage );
 
+    void setOrientation( Orientation o ) { mOrientation = o; }
+    Orientation orientation() const { return mOrientation; }
+
   private:
     QgsPointRotationItem();
+    /**Converts rotation into QPainter rotation considering mOrientation*/
+    int painterRotation( int rotation ) const;
+    /**Clockwise (default) or counterclockwise*/
+    Orientation mOrientation;
     /**Font to display the numerical rotation values*/
     QFont mFont;
     /**Symboll pixmap*/

Modified: trunk/qgis/src/core/CMakeLists.txt
===================================================================
--- trunk/qgis/src/core/CMakeLists.txt	2010-11-17 09:57:44 UTC (rev 14696)
+++ trunk/qgis/src/core/CMakeLists.txt	2010-11-17 15:01:15 UTC (rev 14697)
@@ -53,6 +53,7 @@
   qgshttptransaction.cpp
   qgslabel.cpp
   qgslabelattributes.cpp
+  qgslabelsearchtree.cpp
   qgslogger.cpp
   qgsmaplayer.cpp
   qgsmaplayerregistry.cpp

Modified: trunk/qgis/src/core/pal/labelposition.cpp
===================================================================
--- trunk/qgis/src/core/pal/labelposition.cpp	2010-11-17 09:57:44 UTC (rev 14696)
+++ trunk/qgis/src/core/pal/labelposition.cpp	2010-11-17 15:01:15 UTC (rev 14697)
@@ -55,7 +55,7 @@
 namespace pal
 {
   LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h, double alpha, double cost, FeaturePart *feature, bool isReversed )
-      : id( id ), cost( cost ), feature( feature ), nbOverlap( 0 ), alpha( alpha ), w( w ), h( h ), nextPart( NULL ), partId( -1 ), reversed( isReversed )
+      : id( id ), cost( cost ), feature( feature ), nbOverlap( 0 ), alpha( alpha ), w( w ), h( h ), nextPart( NULL ), partId( -1 ), reversed( isReversed ), upsideDown( false )
   {
 
     // alpha take his value bw 0 and 2*pi rad
@@ -111,6 +111,8 @@
       x[3] = tx;
       y[3] = ty;
 
+      upsideDown = true;
+
       if ( this->alpha < M_PI )
         this->alpha += M_PI;
       else
@@ -137,6 +139,7 @@
     else
       nextPart = NULL;
     partId = other.partId;
+    upsideDown = other.upsideDown;
   }
 
   bool LabelPosition::isIn( double *bbox )

Modified: trunk/qgis/src/core/pal/labelposition.h
===================================================================
--- trunk/qgis/src/core/pal/labelposition.h	2010-11-17 09:57:44 UTC (rev 14696)
+++ trunk/qgis/src/core/pal/labelposition.h	2010-11-17 15:01:15 UTC (rev 14697)
@@ -79,6 +79,8 @@
       //if the layer arrangement is P_LINE
       bool reversed;
 
+      bool upsideDown;
+
       bool isInConflictSinglePart( LabelPosition* lp );
       bool isInConflictMultiPart( LabelPosition* lp );
 
@@ -196,6 +198,7 @@
        */
       double getAlpha() const;
       bool getReversed() const { return reversed; }
+      bool getUpsideDown() const { return upsideDown; }
 
       void print();
 

Added: trunk/qgis/src/core/qgslabelsearchtree.cpp
===================================================================
--- trunk/qgis/src/core/qgslabelsearchtree.cpp	                        (rev 0)
+++ trunk/qgis/src/core/qgslabelsearchtree.cpp	2010-11-17 15:01:15 UTC (rev 14697)
@@ -0,0 +1,62 @@
+#include "qgslabelsearchtree.h"
+#include "labelposition.h"
+
+bool searchCallback( QgsLabelPosition* pos, void* context )
+{
+  QList<QgsLabelPosition*>* list = static_cast< QList<QgsLabelPosition*>* >( context );
+  list->push_back( pos );
+  return true;
+}
+
+QgsLabelSearchTree::QgsLabelSearchTree()
+{
+}
+
+QgsLabelSearchTree::~QgsLabelSearchTree()
+{
+  clear();
+}
+
+void QgsLabelSearchTree::label( const QgsPoint& p, QList<QgsLabelPosition*>& posList )
+{
+  double c_min[2]; c_min[0] = p.x() - 1; c_min[1] = p.y() - 1;
+  double c_max[2]; c_max[0] = p.x() + 1; c_max[1] = p.y() + 1;
+
+  mSearchResults.clear();
+  mSpatialIndex.Search( c_min, c_max, searchCallback, &mSearchResults );
+  posList = mSearchResults;
+}
+
+bool QgsLabelSearchTree::insertLabel( LabelPosition* labelPos, int featureId, const QString& layerName )
+{
+  if ( !labelPos )
+  {
+    return false;
+  }
+
+  double c_min[2];
+  double c_max[2];
+  labelPos->getBoundingBox( c_min, c_max );
+
+  QVector<QgsPoint> cornerPoints;
+  for ( int i = 0; i < 4; ++i )
+  {
+    cornerPoints.push_back( QgsPoint( labelPos->getX( i ), labelPos->getY( i ) ) );
+  }
+  QgsLabelPosition* newEntry = new QgsLabelPosition( featureId, labelPos->getAlpha(), cornerPoints, QgsRectangle( c_min[0], c_min[1], c_max[0], c_max[1] ),
+      labelPos->getWidth(), labelPos->getHeight(), layerName, labelPos->getUpsideDown() );
+  mSpatialIndex.Insert( c_min, c_max, newEntry );
+  return true;
+}
+
+void QgsLabelSearchTree::clear()
+{
+  RTree<QgsLabelPosition*, double, 2, double>::Iterator indexIt;
+  mSpatialIndex.GetFirst( indexIt );
+  while ( !mSpatialIndex.IsNull( indexIt ) )
+  {
+    delete mSpatialIndex.GetAt( indexIt );
+    mSpatialIndex.GetNext( indexIt );
+  }
+  mSpatialIndex.RemoveAll();
+}

Added: trunk/qgis/src/core/qgslabelsearchtree.h
===================================================================
--- trunk/qgis/src/core/qgslabelsearchtree.h	                        (rev 0)
+++ trunk/qgis/src/core/qgslabelsearchtree.h	2010-11-17 15:01:15 UTC (rev 14697)
@@ -0,0 +1,54 @@
+/***************************************************************************
+                          qgslabelsearchtree.h
+            Node for raster calculator tree
+                          --------------------
+    begin                : 2010-11-02
+    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 QGSLABELSEARCHTREE_H
+#define QGSLABELSEARCHTREE_H
+
+#include "qgspoint.h"
+#include "qgsmaprenderer.h"
+#include <QList>
+#include <QVector>
+#include <pointset.h>
+#include <labelposition.h>
+#include "qgsrectangle.h"
+
+using namespace pal;
+
+/**A class to query the labeling structure at a given point (small wraper around pal RTree class)*/
+class QgsLabelSearchTree
+{
+  public:
+    QgsLabelSearchTree();
+    ~QgsLabelSearchTree();
+
+    /**Removes and deletes all the entries*/
+    void clear();
+
+    /**Returns label position(s) at a given point. QgsLabelSearchTree keeps ownership, don't delete the LabelPositions*/
+    void label( const QgsPoint& p, QList<QgsLabelPosition*>& posList );
+
+    /**Inserts label position. Does not take ownership of labelPos
+      @return true in case of success*/
+    bool insertLabel( LabelPosition* labelPos, int featureId, const QString& layerName );
+
+  private:
+    RTree<QgsLabelPosition*, double, 2, double> mSpatialIndex;
+    QList<QgsLabelPosition*> mSearchResults;
+};
+
+#endif // QGSLABELTREE_H

Modified: trunk/qgis/src/core/qgsmaprenderer.h
===================================================================
--- trunk/qgis/src/core/qgsmaprenderer.h	2010-11-17 09:57:44 UTC (rev 14696)
+++ trunk/qgis/src/core/qgsmaprenderer.h	2010-11-17 15:01:15 UTC (rev 14697)
@@ -19,6 +19,7 @@
 
 #include <QSize>
 #include <QStringList>
+#include <QVector>
 
 #include "qgis.h"
 #include "qgsrectangle.h"
@@ -38,12 +39,28 @@
 class QgsVectorLayer;
 class QgsFeature;
 
+struct QgsLabelPosition
+{
+  QgsLabelPosition( int id, double r, const QVector< QgsPoint >& corners, const QgsRectangle& rect, double w, double h, const QString& layer, bool upside_down ):
+      featureId( id ), rotation( r ), cornerPoints( corners ), labelRect( rect ), width( w ), height( h ), layerID( layer ), upsideDown( upside_down ) {}
+  QgsLabelPosition(): featureId( -1 ), rotation( 0 ), labelRect( QgsRectangle() ), width( 0 ), height( 0 ), layerID( "" ), upsideDown( false ) {}
+  int featureId;
+  double rotation;
+  QVector< QgsPoint > cornerPoints;
+  QgsRectangle labelRect;
+  double width;
+  double height;
+  QString layerID;
+  bool upsideDown;
+};
+
 /** Labeling engine interface.
  * \note Added in QGIS v1.4
  */
 class QgsLabelingEngineInterface
 {
   public:
+
     virtual ~QgsLabelingEngineInterface() {}
 
     //! called when we're going to start with rendering
@@ -59,6 +76,9 @@
     virtual void drawLabeling( QgsRenderContext& context ) = 0;
     //! called when we're done with rendering
     virtual void exit() = 0;
+    //! return infos about labels at a given (map) position
+    //! @note: this method was added in version 1.7
+    virtual QList<QgsLabelPosition> labelsAtPosition( const QgsPoint& p ) = 0;
 
     //! called when passing engine among map renderers
     virtual QgsLabelingEngineInterface* clone() = 0;

Modified: trunk/qgis/src/core/qgspallabeling.cpp
===================================================================
--- trunk/qgis/src/core/qgspallabeling.cpp	2010-11-17 09:57:44 UTC (rev 14696)
+++ trunk/qgis/src/core/qgspallabeling.cpp	2010-11-17 15:01:15 UTC (rev 14697)
@@ -38,6 +38,7 @@
 #include <QTime>
 #include <QPainter>
 
+#include "qgslabelsearchtree.h"
 #include <qgslogger.h>
 #include <qgsvectorlayer.h>
 #include <qgsmaplayerregistry.h>
@@ -453,6 +454,7 @@
   bool dataDefinedPosition = false;
   bool dataDefinedRotation = false;
   double xPos, yPos, angle;
+  bool ddXPos, ddYPos;
 
   QMap< DataDefinedProperties, int >::const_iterator dPosXIt = dataDefinedProperties.find( QgsPalLayerSettings::PositionX );
   if ( dPosXIt != dataDefinedProperties.constEnd() )
@@ -460,74 +462,77 @@
     QMap< DataDefinedProperties, int >::const_iterator dPosYIt = dataDefinedProperties.find( QgsPalLayerSettings::PositionY );
     if ( dPosYIt != dataDefinedProperties.constEnd() )
     {
-      //data defined position
-      dataDefinedPosition = true;
-      xPos = f.attributeMap().value( *dPosXIt ).toDouble();
-      yPos = f.attributeMap().value( *dPosYIt ).toDouble();
+      //data defined position. But field values could be NULL -> positions will be generated by PAL
+      xPos = f.attributeMap().value( *dPosXIt ).toDouble( &ddXPos );
+      yPos = f.attributeMap().value( *dPosYIt ).toDouble( &ddYPos );
 
-      //x/y shift in case of alignment
-      double xdiff = 0;
-      double ydiff = 0;
-
-      //horizontal alignment
-      QMap< DataDefinedProperties, int >::const_iterator haliIt = dataDefinedProperties.find( QgsPalLayerSettings::Hali );
-      if ( haliIt != dataDefinedProperties.end() )
+      if ( ddXPos && ddYPos )
       {
-        QString haliString = f.attributeMap().value( *haliIt ).toString();
-        if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
-        {
-          xdiff -= labelX / 2.0;
-        }
-        else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
-        {
-          xdiff -= labelX;
-        }
-      }
+        dataDefinedPosition = true;
+        //x/y shift in case of alignment
+        double xdiff = 0;
+        double ydiff = 0;
 
-      //vertical alignment
-      QMap< DataDefinedProperties, int >::const_iterator valiIt = dataDefinedProperties.find( QgsPalLayerSettings::Vali );
-      if ( valiIt != dataDefinedProperties.constEnd() )
-      {
-        QString valiString = f.attributeMap().value( *valiIt ).toString();
-        if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 )
+        //horizontal alignment
+        QMap< DataDefinedProperties, int >::const_iterator haliIt = dataDefinedProperties.find( QgsPalLayerSettings::Hali );
+        if ( haliIt != dataDefinedProperties.end() )
         {
-          if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 || valiString.compare( "Cap", Qt::CaseInsensitive ) == 0 )
+          QString haliString = f.attributeMap().value( *haliIt ).toString();
+          if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
           {
-            ydiff -= labelY;
+            xdiff -= labelX / 2.0;
           }
-          else
+          else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
           {
-            QFontMetrics labelFontMetrics( labelFont );
-            double descentRatio = labelFontMetrics.descent() / labelFontMetrics.height();
+            xdiff -= labelX;
+          }
+        }
 
-            if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
+        //vertical alignment
+        QMap< DataDefinedProperties, int >::const_iterator valiIt = dataDefinedProperties.find( QgsPalLayerSettings::Vali );
+        if ( valiIt != dataDefinedProperties.constEnd() )
+        {
+          QString valiString = f.attributeMap().value( *valiIt ).toString();
+          if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 )
+          {
+            if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 || valiString.compare( "Cap", Qt::CaseInsensitive ) == 0 )
             {
-              ydiff -= labelY * descentRatio;
+              ydiff -= labelY;
             }
-            else if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
+            else
             {
-              ydiff -= labelY * descentRatio;
-              ydiff -= labelY * 0.5 * ( 1 - descentRatio );
+              QFontMetrics labelFontMetrics( labelFont );
+              double descentRatio = labelFontMetrics.descent() / labelFontMetrics.height();
+
+              if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
+              {
+                ydiff -= labelY * descentRatio;
+              }
+              else if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
+              {
+                ydiff -= labelY * descentRatio;
+                ydiff -= labelY * 0.5 * ( 1 - descentRatio );
+              }
             }
           }
         }
-      }
 
-      //data defined rotation?
-      QMap< DataDefinedProperties, int >::const_iterator rotIt = dataDefinedProperties.find( QgsPalLayerSettings::Rotation );
-      if ( rotIt != dataDefinedProperties.constEnd() )
-      {
-        dataDefinedRotation = true;
-        angle = f.attributeMap().value( *rotIt ).toDouble() * M_PI / 180;
-        //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
-        double xd = xdiff * cos( angle ) - ydiff * sin( angle );
-        double yd = xdiff * sin( angle ) + ydiff * cos( angle );
-        xdiff = xd;
-        ydiff = yd;
+        //data defined rotation?
+        QMap< DataDefinedProperties, int >::const_iterator rotIt = dataDefinedProperties.find( QgsPalLayerSettings::Rotation );
+        if ( rotIt != dataDefinedProperties.constEnd() )
+        {
+          dataDefinedRotation = true;
+          angle = f.attributeMap().value( *rotIt ).toDouble() * M_PI / 180;
+          //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
+          double xd = xdiff * cos( angle ) - ydiff * sin( angle );
+          double yd = xdiff * sin( angle ) + ydiff * cos( angle );
+          xdiff = xd;
+          ydiff = yd;
+        }
+
+        yPos += ydiff;
+        xPos += xdiff;
       }
-
-      yPos += ydiff;
-      xPos += xdiff;
     }
   }
 
@@ -615,6 +620,8 @@
 
   mShowingCandidates = false;
   mShowingAllLabels = false;
+
+  mLabelSearchTree = new QgsLabelSearchTree();
 }
 
 
@@ -622,6 +629,8 @@
 {
   // make sure we've freed everything
   exit();
+  delete mLabelSearchTree;
+  mLabelSearchTree = NULL;
 }
 
 
@@ -752,6 +761,8 @@
   mPal->setPointP( mCandPoint );
   mPal->setLineP( mCandLine );
   mPal->setPolyP( mCandPolygon );
+
+  mActiveLayers.clear();
 }
 
 void QgsPalLabeling::exit()
@@ -761,14 +772,15 @@
   mMapRenderer = NULL;
 }
 
-QgsPalLayerSettings& QgsPalLabeling::layer( const char* layerName )
+QgsPalLayerSettings& QgsPalLabeling::layer( const QString& layerName )
 {
   QHash<QgsVectorLayer*, QgsPalLayerSettings>::iterator lit;
   for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
   {
-    QgsPalLayerSettings& lyr = lit.value();
-    if ( lyr.palLayer->getName() == layerName )
-      return lyr;
+    if ( lit.key() && lit.key()->getLayerID() == layerName )
+    {
+      return lit.value();
+    }
   }
   return mInvalidLayerSettings;
 }
@@ -780,6 +792,11 @@
   QPainter* painter = context.painter();
   QgsRectangle extent = context.extent();
 
+  if ( mLabelSearchTree )
+  {
+    mLabelSearchTree->clear();
+  }
+
   QTime t;
   t.start();
 
@@ -797,7 +814,7 @@
   catch ( std::exception& e )
   {
     QgsDebugMsg( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ) );
-    mActiveLayers.clear(); // clean up
+    //mActiveLayers.clear(); // clean up
     return;
   }
 
@@ -915,6 +932,11 @@
       drawLabel( *it, painter, fontForLabel, fontColor, xform, bufferSize, bufferColor, true );
 
     drawLabel( *it, painter, fontForLabel, fontColor, xform );
+
+    if ( mLabelSearchTree )
+    {
+      mLabelSearchTree->insertLabel( *it,  QString( palGeometry->strId() ).toInt(), ( *it )->getLayerName() );
+    }
   }
 
   QgsDebugMsg( QString( "LABELING draw:  %1 ms" ).arg( t.elapsed() ) );
@@ -932,10 +954,28 @@
     lyr.geometries.clear();
   }
   // labeling is done: clear the active layers hashtable
-  mActiveLayers.clear();
+// mActiveLayers.clear();
 
 }
 
+QList<QgsLabelPosition> QgsPalLabeling::labelsAtPosition( const QgsPoint& p )
+{
+  QList<QgsLabelPosition> positions;
+
+  QList<QgsLabelPosition*> positionPointers;
+  if ( mLabelSearchTree )
+  {
+    mLabelSearchTree->label( p, positionPointers );
+    QList<QgsLabelPosition*>::const_iterator pointerIt = positionPointers.constBegin();
+    for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
+    {
+      positions.push_back( QgsLabelPosition( **pointerIt ) );
+    }
+  }
+
+  return positions;
+}
+
 void QgsPalLabeling::numCandidatePositions( int& candPoint, int& candLine, int& candPolygon )
 {
   candPoint = mCandPoint;
@@ -1055,14 +1095,6 @@
 
 void QgsPalLabeling::drawLabelBuffer( QPainter* p, QString text, const QFont& font, double size, QColor color )
 {
-  /*
-  p->setFont( font );
-  p->setPen( color );
-  for (int x = -size; x <= size; x++)
-    for (int y = -size; y <= size; y++)
-      p->drawText(x,y, text);
-  */
-
   QPainterPath path;
   path.addText( 0, 0, font, text );
   QPen pen( color );

Modified: trunk/qgis/src/core/qgspallabeling.h
===================================================================
--- trunk/qgis/src/core/qgspallabeling.h	2010-11-17 09:57:44 UTC (rev 14696)
+++ trunk/qgis/src/core/qgspallabeling.h	2010-11-17 15:01:15 UTC (rev 14697)
@@ -26,6 +26,7 @@
 class QgsMapRenderer;
 class QgsRectangle;
 class QgsCoordinateTransform;
+class QgsLabelSearchTree;
 
 #include <QString>
 #include <QFont>
@@ -168,7 +169,7 @@
     QgsPalLabeling();
     ~QgsPalLabeling();
 
-    QgsPalLayerSettings& layer( const char* layerName );
+    QgsPalLayerSettings& layer( const QString& layerName );
 
     void numCandidatePositions( int& candPoint, int& candLine, int& candPolygon );
     void setNumCandidatePositions( int candPoint, int candLine, int candPolygon );
@@ -199,6 +200,8 @@
     virtual void drawLabeling( QgsRenderContext& context );
     //! called when we're done with rendering
     virtual void exit();
+    //! return infos about labels at a given (map) position
+    virtual QList<QgsLabelPosition> labelsAtPosition( const QgsPoint& p );
 
     //! called when passing engine among map renderers
     virtual QgsLabelingEngineInterface* clone();
@@ -214,7 +217,7 @@
     void initPal();
 
   protected:
-    // temporary hashtable of layer settings, being filled during labeling, cleared once labeling's done
+    // hashtable of layer settings, being filled during labeling
     QHash<QgsVectorLayer*, QgsPalLayerSettings> mActiveLayers;
     QgsPalLayerSettings mInvalidLayerSettings;
 
@@ -229,6 +232,8 @@
     bool mShowingCandidates;
 
     bool mShowingAllLabels; // whether to avoid collisions or not
+
+    QgsLabelSearchTree* mLabelSearchTree;
 };
 
 #endif // QGSPALLABELING_H

Modified: trunk/qgis/src/core/qgspoint.cpp
===================================================================
--- trunk/qgis/src/core/qgspoint.cpp	2010-11-17 09:57:44 UTC (rev 14696)
+++ trunk/qgis/src/core/qgspoint.cpp	2010-11-17 15:01:15 UTC (rev 14697)
@@ -43,7 +43,7 @@
 
 QgsVector QgsVector::operator*( double scalar ) const
 {
-  return QgsVector( m_x*scalar, m_y*scalar );
+  return QgsVector( m_x * scalar, m_y * scalar );
 }
 
 QgsVector QgsVector::operator/( double scalar ) const
@@ -53,12 +53,12 @@
 
 double QgsVector::operator*( QgsVector v ) const
 {
-  return m_x*v.m_x + m_y*v.m_y;
+  return m_x * v.m_x + m_y * v.m_y;
 }
 
 double QgsVector::length() const
 {
-  return sqrt( m_x*m_x + m_y*m_y );
+  return sqrt( m_x * m_x + m_y * m_y );
 }
 
 double QgsVector::x() const
@@ -80,7 +80,7 @@
 double QgsVector::angle( void ) const
 {
   double ang = atan2( m_y, m_x );
-  return ang < 0.0 ? ang + 2.0*M_PI : ang;
+  return ang < 0.0 ? ang + 2.0 * M_PI : ang;
 }
 
 double QgsVector::angle( QgsVector v ) const
@@ -92,7 +92,7 @@
 {
   double ang = atan2( m_y, m_x ) + rot;
   double len = length();
-  return QgsVector( len*cos( ang ), len*sin( ang ) );
+  return QgsVector( len * cos( ang ), len * sin( ang ) );
 }
 
 QgsVector QgsVector::normal() const
@@ -167,7 +167,7 @@
 
 double QgsPoint::sqrDist( double x, double y ) const
 {
-  return ( m_x - x )*( m_x - x ) + ( m_y - y )*( m_y - y );
+  return ( m_x - x ) * ( m_x - x ) + ( m_y - y ) * ( m_y - y );
 }
 
 double QgsPoint::sqrDist( const QgsPoint& other ) const
@@ -175,6 +175,13 @@
   return sqrDist( other.x(), other.y() );
 }
 
+double QgsPoint::azimuth( const QgsPoint& other )
+{
+  double dx = other.x() - m_x;
+  double dy = other.y() - m_y;
+  return ( atan2( dx, dy ) * 180.0 / M_PI );
+}
+
 // operators
 bool QgsPoint::operator==( const QgsPoint & other )
 {

Modified: trunk/qgis/src/core/qgspoint.h
===================================================================
--- trunk/qgis/src/core/qgspoint.h	2010-11-17 09:57:44 UTC (rev 14696)
+++ trunk/qgis/src/core/qgspoint.h	2010-11-17 15:01:15 UTC (rev 14697)
@@ -148,6 +148,10 @@
     @note added in QGIS 1.5*/
     double sqrDistToSegment( double x1, double y1, double x2, double y2, QgsPoint& minDistPoint ) const;
 
+    /**Calculates azimut between this point and other one (clockwise in degree, starting from north)
+      @note: this function has been added in version 1.7*/
+    double azimuth( const QgsPoint& other );
+
     //! equality operator
     bool operator==( const QgsPoint &other );
 

Added: trunk/qgis/src/ui/qgslabelpropertydialogbase.ui
===================================================================
--- trunk/qgis/src/ui/qgslabelpropertydialogbase.ui	                        (rev 0)
+++ trunk/qgis/src/ui/qgslabelpropertydialogbase.ui	2010-11-17 15:01:15 UTC (rev 14697)
@@ -0,0 +1,259 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QgsLabelPropertyDialogBase</class>
+ <widget class="QDialog" name="QgsLabelPropertyDialogBase">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>284</width>
+    <height>411</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Label properties</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout_4">
+   <item row="0" column="0">
+    <layout class="QHBoxLayout" name="horizontalLayout_3">
+     <item>
+      <widget class="QLabel" name="mLabelTextLabel">
+       <property name="text">
+        <string>Text</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="mLabelTextLineEdit"/>
+     </item>
+    </layout>
+   </item>
+   <item row="1" column="0">
+    <widget class="QGroupBox" name="mFontGroupBox">
+     <property name="title">
+      <string>Font</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QLabel" name="mFontSizeLabel">
+          <property name="text">
+           <string>Size</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QDoubleSpinBox" name="mFontSizeSpinBox">
+          <property name="maximum">
+           <double>999999.000000000000000</double>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item row="0" column="2">
+       <widget class="QPushButton" name="mFontPushButton">
+        <property name="text">
+         <string>Font</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QgsColorButton" name="mFontColorButton">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string/>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="2" column="0">
+    <widget class="QGroupBox" name="mBufferGroupBox">
+     <property name="title">
+      <string>Buffer</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <item row="0" column="0">
+       <layout class="QHBoxLayout" name="horizontalLayout_2">
+        <item>
+         <widget class="QLabel" name="mBufferSizeLabel">
+          <property name="text">
+           <string>Size</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QDoubleSpinBox" name="mBufferSizeSpinBox">
+          <property name="maximum">
+           <double>999999.000000000000000</double>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item row="0" column="1">
+       <widget class="QgsColorButton" name="mBufferColorButton">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string/>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="3" column="0">
+    <widget class="QGroupBox" name="mPositionGroupBlox">
+     <property name="title">
+      <string>Position</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_3">
+      <item row="0" column="0">
+       <widget class="QLabel" name="mLabelDistanceLabel">
+        <property name="text">
+         <string>Label distance</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QDoubleSpinBox" name="mLabelDistanceSpinBox"/>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="mXCoordLabel">
+        <property name="text">
+         <string>X Coordinate</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QDoubleSpinBox" name="mXCoordSpinBox">
+        <property name="minimum">
+         <double>-999999999.000000000000000</double>
+        </property>
+        <property name="maximum">
+         <double>999999999.000000000000000</double>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="mYCoordLabel">
+        <property name="text">
+         <string>Y Coordinate</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QDoubleSpinBox" name="mYCoordSpinBox">
+        <property name="minimum">
+         <double>-999999999.000000000000000</double>
+        </property>
+        <property name="maximum">
+         <double>999999999.000000000000000</double>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0">
+       <widget class="QLabel" name="mHaliLabel">
+        <property name="text">
+         <string>Horizontal alignment</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1">
+       <widget class="QComboBox" name="mHaliComboBox"/>
+      </item>
+      <item row="4" column="0">
+       <widget class="QLabel" name="mValiLabel">
+        <property name="text">
+         <string>Vertical alignment</string>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="1">
+       <widget class="QComboBox" name="mValiComboBox"/>
+      </item>
+      <item row="5" column="0">
+       <widget class="QLabel" name="mRotationLabel">
+        <property name="text">
+         <string>Rotation</string>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="1">
+       <widget class="QDoubleSpinBox" name="mRotationSpinBox">
+        <property name="maximum">
+         <double>360.000000000000000</double>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="4" column="0">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QgsColorButton</class>
+   <extends>QToolButton</extends>
+   <header>qgscolorbutton.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>QgsLabelPropertyDialogBase</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>QgsLabelPropertyDialogBase</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>



More information about the QGIS-commit mailing list