[QGIS Commit] r12248 - in trunk/qgis: images/themes/default src/app src/app/composer src/core src/core/composer src/gui src/ui

svn_qgis at osgeo.org svn_qgis at osgeo.org
Wed Nov 25 05:17:29 EST 2009


Author: mhugent
Date: 2009-11-25 05:17:28 -0500 (Wed, 25 Nov 2009)
New Revision: 12248

Added:
   trunk/qgis/images/themes/default/mActionAddArrow.png
   trunk/qgis/src/app/composer/qgscomposerarrowwidget.cpp
   trunk/qgis/src/app/composer/qgscomposerarrowwidget.h
   trunk/qgis/src/core/composer/qgscomposerarrow.cpp
   trunk/qgis/src/core/composer/qgscomposerarrow.h
Modified:
   trunk/qgis/src/app/CMakeLists.txt
   trunk/qgis/src/app/composer/qgscomposer.cpp
   trunk/qgis/src/app/composer/qgscomposer.h
   trunk/qgis/src/core/CMakeLists.txt
   trunk/qgis/src/gui/qgscomposerview.cpp
   trunk/qgis/src/gui/qgscomposerview.h
   trunk/qgis/src/ui/qgscomposerbase.ui
Log:
[FEATURE]: arrow item for composer. Custom arrow heads from svg files are planned for the near future

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


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

Modified: trunk/qgis/src/app/CMakeLists.txt
===================================================================
--- trunk/qgis/src/app/CMakeLists.txt	2009-11-25 10:00:05 UTC (rev 12247)
+++ trunk/qgis/src/app/CMakeLists.txt	2009-11-25 10:17:28 UTC (rev 12248)
@@ -73,6 +73,7 @@
   qgsquerybuilder.cpp
 
   composer/qgscomposer.cpp
+  composer/qgscomposerarrowwidget.cpp
   composer/qgscomposeritemwidget.cpp
   composer/qgscomposerlabelwidget.cpp
   composer/qgscomposerpicturewidget.cpp
@@ -171,6 +172,7 @@
   qgsquerybuilder.h
 
   composer/qgscomposer.h
+  composer/qgscomposerarrowwidget.h
   composer/qgscomposeritemwidget.h
   composer/qgscomposerlabelwidget.h
   composer/qgscomposerlegendwidget.h

Modified: trunk/qgis/src/app/composer/qgscomposer.cpp
===================================================================
--- trunk/qgis/src/app/composer/qgscomposer.cpp	2009-11-25 10:00:05 UTC (rev 12247)
+++ trunk/qgis/src/app/composer/qgscomposer.cpp	2009-11-25 10:17:28 UTC (rev 12248)
@@ -21,6 +21,8 @@
 #include "qgscomposerview.h"
 #include "qgscomposition.h"
 #include "qgscompositionwidget.h"
+#include "qgscomposerarrow.h"
+#include "qgscomposerarrowwidget.h"
 #include "qgscomposerlabel.h"
 #include "qgscomposerlabelwidget.h"
 #include "qgscomposerlegend.h"
@@ -109,6 +111,7 @@
   toggleActionGroup->addAction( mActionAddImage );
   toggleActionGroup->addAction( mActionSelectMoveItem );
   toggleActionGroup->addAction( mActionAddBasicShape );
+  toggleActionGroup->addAction( mActionAddArrow );
   toggleActionGroup->setExclusive( true );
 
   mActionAddNewMap->setCheckable( true );
@@ -119,6 +122,7 @@
   mActionAddImage->setCheckable( true );
   mActionMoveItemContent->setCheckable( true );
   mActionAddBasicShape->setCheckable( true );
+  mActionAddArrow->setCheckable( true );
 
 #ifdef Q_WS_MAC
   QMenu *appMenu = menuBar()->addMenu( tr( "QGIS" ) );
@@ -155,6 +159,7 @@
   layoutMenu->addAction( mActionSelectMoveItem );
   layoutMenu->addAction( mActionMoveItemContent );
   layoutMenu->addAction( mActionAddBasicShape );
+  layoutMenu->addAction( mActionAddArrow );
   layoutMenu->addSeparator();
   layoutMenu->addAction( mActionGroupItems );
   layoutMenu->addAction( mActionUngroupItems );
@@ -249,6 +254,7 @@
   mActionAddNewLegend->setIcon( QgisApp::getThemeIcon( "/mActionAddLegend.png" ) );
   mActionAddNewScalebar->setIcon( QgisApp::getThemeIcon( "/mActionScaleBar.png" ) );
   mActionAddBasicShape->setIcon( QgisApp::getThemeIcon( "/mActionAddBasicShape.png" ) );
+  mActionAddArrow->setIcon( QgisApp::getThemeIcon( "/mActionAddArrow.png" ) );
   mActionSelectMoveItem->setIcon( QgisApp::getThemeIcon( "/mActionSelectPan.png" ) );
   mActionMoveItemContent->setIcon( QgisApp::getThemeIcon( "/mActionMoveItemContent.png" ) );
   mActionGroupItems->setIcon( QgisApp::getThemeIcon( "/mActionGroupItems.png" ) );
@@ -275,6 +281,7 @@
   connect( mView, SIGNAL( composerLegendAdded( QgsComposerLegend* ) ), this, SLOT( addComposerLegend( QgsComposerLegend* ) ) );
   connect( mView, SIGNAL( composerPictureAdded( QgsComposerPicture* ) ), this, SLOT( addComposerPicture( QgsComposerPicture* ) ) );
   connect( mView, SIGNAL( composerShapeAdded( QgsComposerShape* ) ), this, SLOT( addComposerShape( QgsComposerShape* ) ) );
+  connect( mView, SIGNAL( composerArrowAdded( QgsComposerArrow* ) ), this, SLOT( addComposerArrow( QgsComposerArrow* ) ) );
   connect( mView, SIGNAL( actionFinished() ), this, SLOT( setSelectionTool() ) );
 }
 
@@ -805,6 +812,14 @@
   }
 }
 
+void QgsComposer::on_mActionAddArrow_triggered()
+{
+  if ( mView )
+  {
+    mView->setCurrentTool( QgsComposerView::AddArrow );
+  }
+}
+
 void QgsComposer::on_mActionSaveAsTemplate_triggered()
 {
   //show file dialog
@@ -1217,6 +1232,21 @@
     showItemOptions( newShape );
   }
 
+  //composer arrows
+  QDomNodeList composerArrowList = composerElem.elementsByTagName( "ComposerArrow" );
+  for ( int i = 0; i < composerArrowList.size(); ++i )
+  {
+    QDomElement currentArrowElem = composerArrowList.at( i ).toElement();
+    QgsComposerArrow* newArrow = new QgsComposerArrow( mComposition );
+    newArrow->readXML( currentArrowElem, doc );
+    addComposerArrow( newArrow );
+    mComposition->addItem( newArrow );
+    mComposition->update();
+    mComposition->clearSelection();
+    newArrow->setSelected( true );
+    showItemOptions( newArrow );
+  }
+
   mComposition->sortZList();
   mView->setComposition( mComposition );
 
@@ -1235,6 +1265,17 @@
   mItemWidgetMap.clear();
 }
 
+void QgsComposer::addComposerArrow( QgsComposerArrow* arrow )
+{
+  if ( !arrow )
+  {
+    return;
+  }
+
+  QgsComposerArrowWidget* arrowWidget = new QgsComposerArrowWidget( arrow );
+  mItemWidgetMap.insert( arrow, arrowWidget );
+}
+
 void QgsComposer::addComposerMap( QgsComposerMap* map )
 {
   if ( !map )

Modified: trunk/qgis/src/app/composer/qgscomposer.h
===================================================================
--- trunk/qgis/src/app/composer/qgscomposer.h	2009-11-25 10:00:05 UTC (rev 12247)
+++ trunk/qgis/src/app/composer/qgscomposer.h	2009-11-25 10:17:28 UTC (rev 12248)
@@ -22,6 +22,7 @@
 #include "qgscontexthelp.h"
 
 class QgisApp;
+class QgsComposerArrow;
 class QgsComposerLabel;
 class QgsComposerLegend;
 class QgsComposerMap;
@@ -128,6 +129,9 @@
     //! Select item
     void on_mActionSelectMoveItem_triggered();
 
+    //! Add arrow
+    void on_mActionAddArrow_triggered();
+
     //! Add new map
     void on_mActionAddNewMap_triggered();
 
@@ -193,6 +197,9 @@
     //! Save window state
     void saveWindowState();
 
+    /**Add a composer arrow to the item/widget map and crete a configuration widget for it*/
+    void addComposerArrow( QgsComposerArrow* arrow );
+
     /**Add a composer map to the item/widget map and creates a configuration widget for it*/
     void addComposerMap( QgsComposerMap* map );
 

Added: trunk/qgis/src/app/composer/qgscomposerarrowwidget.cpp
===================================================================
--- trunk/qgis/src/app/composer/qgscomposerarrowwidget.cpp	                        (rev 0)
+++ trunk/qgis/src/app/composer/qgscomposerarrowwidget.cpp	2009-11-25 10:17:28 UTC (rev 12248)
@@ -0,0 +1,121 @@
+/***************************************************************************
+                         qgscomposerarrowwidget.cpp
+                         --------------------------
+    begin                : November 2009
+    copyright            : (C) 2009 by Marco Hugentobler
+    email                : marco at hugis.net
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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 "qgscomposerarrowwidget.h"
+#include "qgscomposerarrow.h"
+#include "qgscomposeritemwidget.h"
+#include <QColorDialog>
+
+QgsComposerArrowWidget::QgsComposerArrowWidget( QgsComposerArrow* arrow ): QWidget( 0 ), mArrow( arrow )
+{
+  setupUi( this );
+
+  //add widget for general composer item properties
+  QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, mArrow );
+  toolBox->addItem( itemPropertiesWidget, tr( "General options" ) );
+
+  setGuiElementValues();
+}
+
+QgsComposerArrowWidget::~QgsComposerArrowWidget()
+{
+
+}
+
+void QgsComposerArrowWidget::on_mOutlineWidthSpinBox_valueChanged( double d )
+{
+  if ( !mArrow )
+  {
+    return;
+  }
+
+  mArrow->setOutlineWidth( d );
+  mArrow->update();
+}
+
+void QgsComposerArrowWidget::on_mArrowHeadWidthSpinBox_valueChanged( double d )
+{
+  if ( !mArrow )
+  {
+    return;
+  }
+
+  mArrow->setArrowHeadWidth( d );
+  mArrow->update();
+}
+
+void QgsComposerArrowWidget::on_mShowArrowHeadCheckBox_stateChanged( int state )
+{
+  if ( !mArrow )
+  {
+    return;
+  }
+
+  if ( state == Qt::Checked )
+  {
+    mArrow->setShowArrowMarker( true );
+  }
+  else
+  {
+    mArrow->setShowArrowMarker( false );
+  }
+  mArrow->update();
+}
+
+void QgsComposerArrowWidget::on_mArrowColorButton_clicked()
+{
+  if ( !mArrow )
+  {
+    return;
+  }
+
+  QColor newColor = QColorDialog::getColor( mArrow->arrowColor(), 0, tr( "Arrow color" ), QColorDialog::ShowAlphaChannel );
+  if ( newColor.isValid() )
+  {
+    mArrow->setArrowColor( newColor );
+    mArrow->update();
+  }
+}
+
+void QgsComposerArrowWidget::blockAllSignals( bool block )
+{
+  mArrowColorButton->blockSignals( block );
+  mShowArrowHeadCheckBox->blockSignals( block );
+  mOutlineWidthSpinBox->blockSignals( block );
+  mArrowHeadWidthSpinBox->blockSignals( block );
+}
+
+void QgsComposerArrowWidget::setGuiElementValues()
+{
+  if ( !mArrow )
+  {
+    return;
+  }
+
+  blockAllSignals( true );
+  mOutlineWidthSpinBox->setValue( mArrow->outlineWidth() );
+  mArrowHeadWidthSpinBox->setValue( mArrow->arrowHeadWidth() );
+  if ( mArrow->showArrowMarker() )
+  {
+    mShowArrowHeadCheckBox->setCheckState( Qt::Checked );
+  }
+  else
+  {
+    mShowArrowHeadCheckBox->setCheckState( Qt::Unchecked );
+  }
+  blockAllSignals( false );
+}

Added: trunk/qgis/src/app/composer/qgscomposerarrowwidget.h
===================================================================
--- trunk/qgis/src/app/composer/qgscomposerarrowwidget.h	                        (rev 0)
+++ trunk/qgis/src/app/composer/qgscomposerarrowwidget.h	2009-11-25 10:17:28 UTC (rev 12248)
@@ -0,0 +1,45 @@
+/***************************************************************************
+                         qgscomposerarrowwidget.h
+                         ------------------------
+    begin                : November 2009
+    copyright            : (C) 2009 by Marco Hugentobler
+    email                : marco at hugis.net
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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 QGSCOMPOSERARROWWIDGET_H
+#define QGSCOMPOSERARROWWIDGET_H
+
+#include "ui_qgscomposerarrowwidgetbase.h"
+
+class QgsComposerArrow;
+
+class QgsComposerArrowWidget: public QWidget, private Ui::QgsComposerArrowWidgetBase
+{
+    Q_OBJECT
+  public:
+    QgsComposerArrowWidget( QgsComposerArrow* arrow );
+    ~QgsComposerArrowWidget();
+
+  private:
+    QgsComposerArrow* mArrow;
+
+    void blockAllSignals( bool block );
+    void setGuiElementValues();
+
+  private slots:
+    void on_mOutlineWidthSpinBox_valueChanged( double d );
+    void on_mArrowHeadWidthSpinBox_valueChanged( double d );
+    void on_mShowArrowHeadCheckBox_stateChanged( int state );
+    void on_mArrowColorButton_clicked();
+};
+
+#endif // QGSCOMPOSERARROWWIDGET_H

Modified: trunk/qgis/src/core/CMakeLists.txt
===================================================================
--- trunk/qgis/src/core/CMakeLists.txt	2009-11-25 10:00:05 UTC (rev 12247)
+++ trunk/qgis/src/core/CMakeLists.txt	2009-11-25 10:17:28 UTC (rev 12248)
@@ -70,6 +70,7 @@
   qgsvectorlayerundocommand.cpp
   qgsvectoroverlay.cpp
 
+  composer/qgscomposerarrow.cpp
   composer/qgscomposeritem.cpp
   composer/qgscomposeritemgroup.cpp
   composer/qgscomposerlabel.cpp

Added: trunk/qgis/src/core/composer/qgscomposerarrow.cpp
===================================================================
--- trunk/qgis/src/core/composer/qgscomposerarrow.cpp	                        (rev 0)
+++ trunk/qgis/src/core/composer/qgscomposerarrow.cpp	2009-11-25 10:17:28 UTC (rev 12248)
@@ -0,0 +1,384 @@
+/***************************************************************************
+                         qgscomposerarrow.cpp
+                         ----------------------
+    begin                : November 2009
+    copyright            : (C) 2009 by Marco Hugentobler
+    email                : marco at hugis.net
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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 "qgscomposerarrow.h"
+#include <QPainter>
+#include <QSvgRenderer>
+
+#ifndef Q_OS_MACX
+#include <cmath>
+#else
+#include <math.h>
+#endif
+
+QgsComposerArrow::QgsComposerArrow( QgsComposition* c ): QgsComposerItem( c ), mStartPoint( 0, 0 ), mStopPoint( 0, 0 ), mArrowColor( QColor( 0, 0, 0 ) )
+{
+  initGraphicsSettings();
+}
+
+QgsComposerArrow::QgsComposerArrow( const QPointF& startPoint, const QPointF& stopPoint, QgsComposition* c ): QgsComposerItem( c ), mStartPoint( startPoint ), \
+    mStopPoint( stopPoint ), mArrowColor( QColor( 0, 0, 0 ) )
+{
+  //setStartMarker( "/home/marco/src/qgis/images/svg/north_arrows/NorthArrow11.svg" );
+  //setEndMarker( "/home/marco/src/qgis/images/svg/north_arrows/NorthArrow11.svg" );
+  initGraphicsSettings();
+  adaptItemSceneRect();
+}
+
+QgsComposerArrow::~QgsComposerArrow()
+{
+
+}
+
+void QgsComposerArrow::initGraphicsSettings()
+{
+  setArrowHeadWidth( 4 );
+  mPen.setColor( QColor( 0, 0, 0 ) );
+  mPen.setWidthF( 1 );
+  mShowArrowMarker = true;
+
+  //set composer item brush and pen to transparent white by default
+  setPen( QPen( QColor( 255, 255, 255, 0 ) ) );
+  setBrush( QBrush( QColor( 255, 255, 255, 0 ) ) );
+}
+
+void QgsComposerArrow::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
+{
+  if ( !painter )
+  {
+    return;
+  }
+
+  drawBackground( painter );
+
+  //draw arrow
+  QPen arrowPen = mPen;
+  arrowPen.setCapStyle( Qt::FlatCap );
+  arrowPen.setColor( mArrowColor );
+  painter->setPen( arrowPen );
+  painter->setBrush( QBrush( mArrowColor ) );
+  painter->drawLine( QPointF( mStartPoint.x() - transform().dx(), mStartPoint.y() - transform().dy() ), QPointF( mStopPoint.x() - transform().dx(), mStopPoint.y() - transform().dy() ) );
+
+  if ( mShowArrowMarker )
+  {
+    drawHardcodedMarker( painter, EndMarker );
+    //drawSVGMarker( painter, StartMarker, mStartMarkerFile );
+    //drawSVGMarker( painter, EndMarker, mEndMarkerFile );
+  }
+
+  drawFrame( painter );
+  if ( isSelected() )
+  {
+    drawSelectionBoxes( painter );
+  }
+}
+
+void QgsComposerArrow::QgsComposerArrow::setSceneRect( const QRectF& rectangle )
+{
+  //maintain the relative position of start and stop point in the rectangle
+  double startPointXPos = ( mStartPoint.x() - transform().dx() ) / rect().width();
+  double startPointYPos = ( mStartPoint.y() - transform().dy() ) / rect().height();
+  double stopPointXPos = ( mStopPoint.x() - transform().dx() ) / rect().width();
+  double stopPointYPos = ( mStopPoint.y() - transform().dy() ) / rect().height();
+
+  mStartPoint.setX( rectangle.left() + startPointXPos * rectangle.width() );
+  mStartPoint.setY( rectangle.top() + startPointYPos * rectangle.height() );
+  mStopPoint.setX( rectangle.left() + stopPointXPos * rectangle.width() );
+  mStopPoint.setY( rectangle.top() + stopPointYPos * rectangle.height() );
+
+  adaptItemSceneRect();
+}
+
+void QgsComposerArrow::drawHardcodedMarker( QPainter* p, MarkerType type )
+{
+  double angle = arrowAngle();
+  //qWarning(QString::number(angle).toLocal8Bit().data());
+  double angleRad = angle / 180.0 * M_PI;
+  QPointF middlePoint = QPointF( mStopPoint.x() - transform().dx(), mStopPoint.y() - transform().dy() );
+
+  //rotate both arrow points
+  QPointF p1 = QPointF( -mArrowHeadWidth / 2.0, mArrowHeadWidth );
+  QPointF p2 = QPointF( mArrowHeadWidth / 2.0, mArrowHeadWidth );
+
+  QPointF p1Rotated, p2Rotated;
+  p1Rotated.setX( p1.x() * cos( angleRad ) + p1.y() * -sin( angleRad ) );
+  p1Rotated.setY( p1.x() * sin( angleRad ) + p1.y() * cos( angleRad ) );
+  p2Rotated.setX( p2.x() * cos( angleRad ) + p2.y() * -sin( angleRad ) );
+  p2Rotated.setY( p2.x() * sin( angleRad ) + p2.y() * cos( angleRad ) );
+
+  QPolygonF arrowHeadPoly;
+  arrowHeadPoly << middlePoint;
+  arrowHeadPoly << QPointF( middlePoint.x() + p1Rotated.x(), middlePoint.y() + p1Rotated.y() );
+  arrowHeadPoly << QPointF( middlePoint.x() + p2Rotated.x(), middlePoint.y() + p2Rotated.y() );
+
+  p->save();
+
+  QPen arrowPen = p->pen();
+  arrowPen.setJoinStyle( Qt::RoundJoin );
+  QBrush arrowBrush = p->brush();
+  arrowBrush.setColor( mArrowColor );
+  arrowBrush.setStyle( Qt::SolidPattern );
+  p->setPen( arrowPen );
+  p->setBrush( arrowBrush );
+  p->drawPolygon( arrowHeadPoly );
+
+  p->restore();
+}
+
+void QgsComposerArrow::drawSVGMarker( QPainter* p, MarkerType type, const QString& markerPath )
+{
+  double angle = arrowAngle();
+
+  double arrowHeadHeight;
+  if ( type == StartMarker )
+  {
+    arrowHeadHeight = mStartArrowHeadHeight;
+  }
+  else
+  {
+    arrowHeadHeight = mStopArrowHeadHeight;
+  }
+
+  //prepare paint device
+  int dpi = ( p->device()->logicalDpiX() + p->device()->logicalDpiY() ) / 2;
+  int imageWidth = mArrowHeadWidth / 25.4 * dpi;
+  int imageHeight = arrowHeadHeight / 25.4 * dpi;
+  QImage markerImage( imageWidth, imageHeight, QImage::Format_ARGB32 );
+
+  QPointF imageFixPoint;
+  imageFixPoint.setX( mArrowHeadWidth / 2.0 );
+  QPointF canvasPoint;
+  if ( type == StartMarker )
+  {
+    canvasPoint = QPointF( mStartPoint.x() - transform().dx(), mStartPoint.y() - transform().dy() );
+    imageFixPoint.setY( mStartArrowHeadHeight );
+  }
+  else //end marker
+  {
+    canvasPoint = QPointF( mStopPoint.x() - transform().dx(), mStopPoint.y() - transform().dy() );
+    imageFixPoint.setY( 0 );
+  }
+
+  //rasterize svg
+  QSvgRenderer r;
+  if ( type == StartMarker )
+  {
+    if ( !r.load( mStartMarkerFile ) )
+    {
+      return;
+    }
+  }
+  else //end marker
+  {
+    if ( !r.load( mEndMarkerFile ) )
+    {
+      return;
+    }
+  }
+
+  //rotate image fix point for backtransform
+  QPointF fixPoint;
+  if ( type == StartMarker )
+  {
+    fixPoint.setX( 0 ); fixPoint.setY( arrowHeadHeight / 2.0 );
+  }
+  else
+  {
+    fixPoint.setX( 0 ); fixPoint.setY( -arrowHeadHeight / 2.0 );
+  }
+  QPointF rotatedFixPoint;
+  double angleRad = angle / 180 * M_PI;
+  rotatedFixPoint.setX( fixPoint.x() * cos( angleRad ) + fixPoint.y() * sin( angleRad ) );
+  rotatedFixPoint.setY( fixPoint.x() * -sin( angleRad ) + fixPoint.y() * cos( angleRad ) );
+
+
+  QPainter imagePainter( &markerImage );
+  r.render( &imagePainter );
+
+  p->save();
+  p->translate( canvasPoint.x() - rotatedFixPoint.x() , canvasPoint.y() - rotatedFixPoint.y() );
+  p->rotate( angle );
+  p->translate( -mArrowHeadWidth / 2.0, -arrowHeadHeight / 2.0 );
+
+  p->drawImage( QRectF( 0, 0, mArrowHeadWidth, arrowHeadHeight ), markerImage, QRectF( 0, 0, imageWidth, imageHeight ) );
+  p->restore();
+
+  return;
+}
+
+double QgsComposerArrow::arrowAngle() const
+{
+  double xDiff = mStopPoint.x() - mStartPoint.x();
+  double yDiff = mStopPoint.y() - mStartPoint.y();
+  double length = sqrt( xDiff * xDiff + yDiff * yDiff );
+
+  double angle = acos(( -yDiff * length ) / ( length * length ) ) * 180 / M_PI;
+  if ( xDiff < 0 )
+  {
+    return ( 360 - angle );
+  }
+  return angle;
+}
+
+void QgsComposerArrow::setStartMarker( const QString& svgPath )
+{
+  QSvgRenderer r;
+  if ( !r.load( svgPath ) )
+  {
+    return;
+    mStartArrowHeadHeight = 0;
+  }
+  mStartMarkerFile = svgPath;
+
+  //calculate mArrowHeadHeight from svg file and mArrowHeadWidth
+  QRect viewBox = r.viewBox();
+  mStartArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
+  adaptItemSceneRect();
+}
+
+void QgsComposerArrow::setEndMarker( const QString& svgPath )
+{
+  QSvgRenderer r;
+  if ( !r.load( svgPath ) )
+  {
+    return;
+    mStopArrowHeadHeight = 0;
+  }
+  mEndMarkerFile = svgPath;
+
+  //calculate mArrowHeadHeight from svg file and mArrowHeadWidth
+  QRect viewBox = r.viewBox();
+  mStopArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
+  adaptItemSceneRect();
+}
+
+void QgsComposerArrow::setOutlineWidth( double width )
+{
+  mPen.setWidthF( width );
+  adaptItemSceneRect();
+}
+
+void QgsComposerArrow::setArrowHeadWidth( double width )
+{
+  mArrowHeadWidth = width;
+  adaptItemSceneRect();
+}
+
+void QgsComposerArrow::setShowArrowMarker( bool show )
+{
+  mShowArrowMarker = show;
+  adaptItemSceneRect();
+}
+
+void QgsComposerArrow::adaptItemSceneRect()
+{
+  //rectangle containing start and end point
+  QRectF rect = QRectF( std::min( mStartPoint.x(), mStopPoint.x() ), std::min( mStartPoint.y(), mStopPoint.y() ), \
+                        std::abs( mStopPoint.x() - mStartPoint.x() ), std::abs( mStopPoint.y() - mStartPoint.y() ) );
+  double enlarge;
+  if ( mShowArrowMarker )
+  {
+    double maxArrowHeight = std::max( mStartArrowHeadHeight, mStopArrowHeadHeight );
+    enlarge = mPen.widthF() / 2 + std::max( mArrowHeadWidth / 2.0, maxArrowHeight / 2.0 );
+  }
+  else
+  {
+    enlarge = mPen.widthF() / 2.0;
+  }
+  rect.adjust( -enlarge, -enlarge, enlarge, enlarge );
+  QgsComposerItem::setSceneRect( rect );
+}
+
+bool QgsComposerArrow::writeXML( QDomElement& elem, QDomDocument & doc ) const
+{
+  QDomElement composerArrowElem = doc.createElement( "ComposerArrow" );
+  composerArrowElem.setAttribute( "outlineWidth", outlineWidth() );
+  composerArrowElem.setAttribute( "showArrowMarker", mShowArrowMarker );
+  composerArrowElem.setAttribute( "arrowHeadWidth", mArrowHeadWidth );
+
+  //arrow color
+  QDomElement arrowColorElem = doc.createElement( "ArrowColor" );
+  arrowColorElem.setAttribute( "red", mArrowColor.red() );
+  arrowColorElem.setAttribute( "green", mArrowColor.green() );
+  arrowColorElem.setAttribute( "blue", mArrowColor.blue() );
+  arrowColorElem.setAttribute( "alpha", mArrowColor.alpha() );
+  composerArrowElem.appendChild( arrowColorElem );
+
+  //start point
+  QDomElement startPointElem = doc.createElement( "StartPoint" );
+  startPointElem.setAttribute( "x", mStartPoint.x() );
+  startPointElem.setAttribute( "y", mStartPoint.y() );
+  composerArrowElem.appendChild( startPointElem );
+
+  //stop point
+  QDomElement stopPointElem = doc.createElement( "StopPoint" );
+  stopPointElem.setAttribute( "x", mStopPoint.x() );
+  stopPointElem.setAttribute( "y", mStopPoint.y() );
+  composerArrowElem.appendChild( stopPointElem );
+
+  elem.appendChild( composerArrowElem );
+  return true;
+}
+
+bool QgsComposerArrow::readXML( const QDomElement& itemElem, const QDomDocument& doc )
+{
+  mShowArrowMarker = itemElem.attribute( "showArrowMarker", "1" ).toInt();
+  mArrowHeadWidth = itemElem.attribute( "arrowHeadWidth", "2.0" ).toDouble();
+  mPen.setWidthF( itemElem.attribute( "outlineWidth", "1.0" ).toDouble() );
+
+  //arrow color
+  QDomNodeList arrowColorList = itemElem.elementsByTagName( "ArrowColor" );
+  if ( arrowColorList.size() > 0 )
+  {
+    QDomElement arrowColorElem = arrowColorList.at( 0 ).toElement();
+    int red = arrowColorElem.attribute( "red", "0" ).toInt();
+    int green = arrowColorElem.attribute( "green", "0" ).toInt();
+    int blue = arrowColorElem.attribute( "blue", "0" ).toInt();
+    int alpha = arrowColorElem.attribute( "alpha", "255" ).toInt();
+    mArrowColor = QColor( red, green, blue, alpha );
+  }
+
+  //start point
+  QDomNodeList startPointList = itemElem.elementsByTagName( "StartPoint" );
+  if ( startPointList.size() > 0 )
+  {
+    QDomElement startPointElem = startPointList.at( 0 ).toElement();
+    mStartPoint.setX( startPointElem.attribute( "x", "0.0" ).toDouble() );
+    mStartPoint.setY( startPointElem.attribute( "y", "0.0" ).toDouble() );
+  }
+
+  //stop point
+  QDomNodeList stopPointList = itemElem.elementsByTagName( "StopPoint" );
+  if ( stopPointList.size() > 0 )
+  {
+    QDomElement stopPointElem = stopPointList.at( 0 ).toElement();
+    mStopPoint.setX( stopPointElem.attribute( "x", "0.0" ).toDouble() );
+    mStopPoint.setY( stopPointElem.attribute( "y", "0.0" ).toDouble() );
+  }
+
+
+  //restore general composer item properties
+  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
+  if ( composerItemList.size() > 0 )
+  {
+    QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
+    _readXML( composerItemElem, doc );
+  }
+
+  adaptItemSceneRect();
+  return true;
+}

Added: trunk/qgis/src/core/composer/qgscomposerarrow.h
===================================================================
--- trunk/qgis/src/core/composer/qgscomposerarrow.h	                        (rev 0)
+++ trunk/qgis/src/core/composer/qgscomposerarrow.h	2009-11-25 10:17:28 UTC (rev 12248)
@@ -0,0 +1,105 @@
+/***************************************************************************
+                         qgscomposerarrow.h
+                         ----------------------
+    begin                : November 2009
+    copyright            : (C) 2009 by Marco Hugentobler
+    email                : marco at hugis.net
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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 QGSCOMPOSERARROW_H
+#define QGSCOMPOSERARROW_H
+
+#include "qgscomposeritem.h"
+
+/**An item that draws an arrow between to points*/
+class QgsComposerArrow: public QgsComposerItem
+{
+  public:
+    QgsComposerArrow( QgsComposition* c );
+    QgsComposerArrow( const QPointF& startPoint, const QPointF& stopPoint, QgsComposition* c );
+    ~QgsComposerArrow();
+
+    /** \brief Reimplementation of QCanvasItem::paint - draw on canvas */
+    void paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget );
+
+    /**Modifies position of start and endpoint and calls QgsComposerItem::setSceneRect*/
+    void setSceneRect( const QRectF& rectangle );
+
+    /**Sets the width of the arrow head in mm*/
+    void setArrowHeadWidth( double width );
+    double arrowHeadWidth() const {return mArrowHeadWidth;}
+
+    void setOutlineWidth( double width );
+    double outlineWidth() const {return mPen.widthF();}
+
+    void setStartMarker( const QString& svgPath );
+    void setEndMarker( const QString& svgPath );
+
+    bool showArrowMarker() const { return mShowArrowMarker;}
+    void setShowArrowMarker( bool show );
+
+    QColor arrowColor() const { return mArrowColor; }
+    void setArrowColor( const QColor& c ) { mArrowColor = c; }
+
+    /** stores state in Dom node
+    * @param node is Dom node corresponding to 'Composer' tag
+    * @param temp write template file
+    */
+    bool writeXML( QDomElement& elem, QDomDocument & doc ) const;
+
+    /** sets state from Dom document
+    * @param itemElem is Dom node corresponding to item tag
+    */
+    bool readXML( const QDomElement& itemElem, const QDomDocument& doc );
+
+  private:
+
+    enum MarkerType
+    {
+      StartMarker,
+      EndMarker
+    };
+
+    QPointF mStartPoint;
+    QPointF mStopPoint;
+
+    QPen mPen;
+    QBrush mBrush;
+
+    /**Width of the arrow marker in mm. May be specified by the user. The height is automatically adapted*/
+    double mArrowHeadWidth;
+    /**Height of the arrow marker in mm. Is calculated from arrow marker width and apsect ratio of svg*/
+    double mStartArrowHeadHeight;
+    double mStopArrowHeadHeight;
+
+    /**Path to the start marker file*/
+    QString mStartMarkerFile;
+    /**Path to the end marker file*/
+    QString mEndMarkerFile;
+
+    /**True if arrow head marker is drawn*/
+    bool mShowArrowMarker;
+    QColor mArrowColor;
+
+    /**Adapts the item scene rect to contain the start point, the stop point including the arrow marker and the outline.
+        Needs to be called whenever the arrow width/height, the outline with or the endpoints are changed*/
+    void adaptItemSceneRect();
+
+    void drawHardcodedMarker( QPainter* p, MarkerType type );
+    void drawSVGMarker( QPainter* p, MarkerType type, const QString& markerPath );
+    /**Calculates arrow angle (for marker rotation)*/
+    double arrowAngle() const;
+    /**Apply default graphics settings*/
+    void initGraphicsSettings();
+};
+
+#endif // QGSCOMPOSERARROW_H

Modified: trunk/qgis/src/gui/qgscomposerview.cpp
===================================================================
--- trunk/qgis/src/gui/qgscomposerview.cpp	2009-11-25 10:00:05 UTC (rev 12247)
+++ trunk/qgis/src/gui/qgscomposerview.cpp	2009-11-25 10:17:28 UTC (rev 12248)
@@ -20,6 +20,7 @@
 #include <QKeyEvent>
 
 #include "qgscomposerview.h"
+#include "qgscomposerarrow.h"
 #include "qgscomposerlabel.h"
 #include "qgscomposerlegend.h"
 #include "qgscomposermap.h"
@@ -29,7 +30,7 @@
 #include "qgscomposershape.h"
 
 QgsComposerView::QgsComposerView( QWidget* parent, const char* name, Qt::WFlags f ) :
-    QGraphicsView( parent ), mShiftKeyPressed( false ), mRubberBandItem( 0 ), mMoveContentItem( 0 )
+    QGraphicsView( parent ), mShiftKeyPressed( false ), mRubberBandItem( 0 ), mRubberBandLineItem( 0 ), mMoveContentItem( 0 )
 {
   setResizeAnchor( QGraphicsView::AnchorViewCenter );
   setMouseTracking( true );
@@ -97,6 +98,15 @@
       break;
     }
 
+    case AddArrow:
+    {
+      mRubberBandStartPos = QPointF( snappedScenePoint.x(), snappedScenePoint.y() );
+      mRubberBandLineItem = new QGraphicsLineItem( snappedScenePoint.x(), snappedScenePoint.y(), snappedScenePoint.x(), snappedScenePoint.y() );
+      mRubberBandLineItem->setZValue( 100 );
+      scene()->addItem( mRubberBandLineItem );
+      scene()->update();
+    }
+
     //create rubber band for map and ellipse items
     case AddMap:
     case AddShape:
@@ -187,6 +197,18 @@
       }
       break;
     }
+    case AddArrow:
+    {
+      QPointF scenePoint = mapToScene( e->pos() );
+      QPointF snappedScenePoint = composition()->snapPointToGrid( scenePoint );
+      QgsComposerArrow* composerArrow = new QgsComposerArrow( mRubberBandStartPos, QPointF( snappedScenePoint.x(), snappedScenePoint.y() ), composition() );
+      addComposerArrow( composerArrow );
+      scene()->removeItem( mRubberBandLineItem );
+      delete mRubberBandLineItem;
+      mRubberBandLineItem = 0;
+      emit actionFinished();
+      break;
+    }
 
     case AddShape:
     {
@@ -194,6 +216,7 @@
       {
         scene()->removeItem( mRubberBandItem );
         delete mRubberBandItem;
+        mRubberBandItem = 0;
         return;
       }
 
@@ -218,6 +241,7 @@
       addComposerMap( composerMap );
       scene()->removeItem( mRubberBandItem );
       delete mRubberBandItem;
+      mRubberBandItem = 0;
       emit actionFinished();
     }
     break;
@@ -251,6 +275,15 @@
         QGraphicsView::mouseMoveEvent( e );
         break;
 
+      case AddArrow:
+      {
+        if ( mRubberBandLineItem )
+        {
+          mRubberBandLineItem->setLine( mRubberBandStartPos.x(), mRubberBandStartPos.y(),  scenePoint.x(),  scenePoint.y() );
+        }
+        break;
+      }
+
       case AddMap:
       case AddShape:
         //adjust rubber band item
@@ -285,10 +318,13 @@
           height = dy;
         }
 
-        mRubberBandItem->setRect( 0, 0, width, height );
-        QTransform t;
-        t.translate( x, y );
-        mRubberBandItem->setTransform( t );
+        if ( mRubberBandItem )
+        {
+          mRubberBandItem->setRect( 0, 0, width, height );
+          QTransform t;
+          t.translate( x, y );
+          mRubberBandItem->setTransform( t );
+        }
         break;
       }
 
@@ -410,6 +446,15 @@
   return 0;
 }
 
+void QgsComposerView::addComposerArrow( QgsComposerArrow* arrow )
+{
+  composition()->addItem( arrow );
+  emit composerArrowAdded( arrow );
+  scene()->clearSelection();
+  arrow->setSelected( true );
+  emit selectedItemChanged( arrow );
+}
+
 void QgsComposerView::addComposerLabel( QgsComposerLabel* label )
 {
   composition()->addItem( label );

Modified: trunk/qgis/src/gui/qgscomposerview.h
===================================================================
--- trunk/qgis/src/gui/qgscomposerview.h	2009-11-25 10:00:05 UTC (rev 12247)
+++ trunk/qgis/src/gui/qgscomposerview.h	2009-11-25 10:17:28 UTC (rev 12248)
@@ -24,6 +24,7 @@
 class QMainWindow;
 class QMouseEvent;
 class QgsComposition;
+class QgsComposerArrow;
 class QgsComposerItem;
 class QgsComposerLabel;
 class QgsComposerLegend;
@@ -49,6 +50,7 @@
     enum Tool
     {
       Select = 0,      // Select/Move item
+      AddArrow,         //add arrow
       AddMap,          // add new map
       AddLegend, // add vector legend
       AddLabel,        // add label
@@ -74,6 +76,8 @@
     /**Returns the composition or 0 in case of error*/
     QgsComposition* composition();
 
+    /**Adds an arrow item to the graphics scene and advices composer to create a widget for it (through signal)*/
+    void addComposerArrow( QgsComposerArrow* arrow );
     /**Adds label to the graphics scene and advices composer to create a widget for it (through signal)*/
     void addComposerLabel( QgsComposerLabel* label );
     /**Adds map to the graphics scene and advices composer to create a widget for it (through signal)*/
@@ -107,6 +111,8 @@
     QgsComposerView::Tool mCurrentTool;
     /**Rubber band item*/
     QGraphicsRectItem* mRubberBandItem;
+    /**Rubber band item for arrows*/
+    QGraphicsLineItem* mRubberBandLineItem;
     /**Item to move content*/
     QgsComposerItem* mMoveContentItem;
     /**Start position of content move*/
@@ -121,7 +127,9 @@
   signals:
     /**Is emitted when selected item changed. If 0, no item is selected*/
     void selectedItemChanged( const QgsComposerItem* selected );
-    /**Ist emittted when new composer label has been added to the view*/
+    /**Is emitted when new composer arrow has been added to the view*/
+    void composerArrowAdded( QgsComposerArrow* arrow );
+    /**Is emitted when new composer label has been added to the view*/
     void composerLabelAdded( QgsComposerLabel* label );
     /**Is emitted when new composer map has been added to the view*/
     void composerMapAdded( QgsComposerMap* map );

Modified: trunk/qgis/src/ui/qgscomposerbase.ui
===================================================================
--- trunk/qgis/src/ui/qgscomposerbase.ui	2009-11-25 10:00:05 UTC (rev 12247)
+++ trunk/qgis/src/ui/qgscomposerbase.ui	2009-11-25 10:17:28 UTC (rev 12248)
@@ -170,6 +170,7 @@
    <addaction name="mActionAddNewLegend"/>
    <addaction name="mActionAddNewScalebar"/>
    <addaction name="mActionAddBasicShape"/>
+   <addaction name="mActionAddArrow"/>
    <addaction name="mActionSelectMoveItem"/>
    <addaction name="mActionMoveItemContent"/>
    <addaction name="mActionGroupItems"/>
@@ -452,6 +453,14 @@
     <string>Add Basic Shape</string>
    </property>
   </action>
+  <action name="mActionAddArrow">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Add arrow</string>
+   </property>
+  </action>
  </widget>
  <tabstops>
   <tabstop>mCompositionNameComboBox</tabstop>



More information about the QGIS-commit mailing list