Quantum GIS API Documentation  1.8
src/core/composer/qgscomposerarrow.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                          qgscomposerarrow.cpp
00003                          ----------------------
00004     begin                : November 2009
00005     copyright            : (C) 2009 by Marco Hugentobler
00006     email                : marco@hugis.net
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "qgscomposerarrow.h"
00019 #include <QPainter>
00020 #include <QSvgRenderer>
00021 
00022 #include <cmath>
00023 
00024 QgsComposerArrow::QgsComposerArrow( QgsComposition* c )
00025     : QgsComposerItem( c )
00026     , mStartPoint( 0, 0 )
00027     , mStopPoint( 0, 0 )
00028     , mMarkerMode( DefaultMarker )
00029     , mArrowColor( QColor( 0, 0, 0 ) )
00030 {
00031   initGraphicsSettings();
00032 }
00033 
00034 QgsComposerArrow::QgsComposerArrow( const QPointF& startPoint, const QPointF& stopPoint, QgsComposition* c )
00035     : QgsComposerItem( c )
00036     , mStartPoint( startPoint )
00037     , mStopPoint( stopPoint )
00038     , mMarkerMode( DefaultMarker )
00039     , mArrowColor( QColor( 0, 0, 0 ) )
00040 {
00041   initGraphicsSettings();
00042   adaptItemSceneRect();
00043 }
00044 
00045 QgsComposerArrow::~QgsComposerArrow()
00046 {
00047 
00048 }
00049 
00050 void QgsComposerArrow::initGraphicsSettings()
00051 {
00052   setArrowHeadWidth( 4 );
00053   mPen.setColor( QColor( 0, 0, 0 ) );
00054   mPen.setWidthF( 1 );
00055 
00056   //set composer item brush and pen to transparent white by default
00057   setPen( QPen( QColor( 255, 255, 255, 0 ) ) );
00058   setBrush( QBrush( QColor( 255, 255, 255, 0 ) ) );
00059 }
00060 
00061 void QgsComposerArrow::paint( QPainter* painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
00062 {
00063   Q_UNUSED( itemStyle );
00064   Q_UNUSED( pWidget );
00065   if ( !painter )
00066   {
00067     return;
00068   }
00069 
00070   drawBackground( painter );
00071 
00072   //draw arrow
00073   QPen arrowPen = mPen;
00074   arrowPen.setCapStyle( Qt::FlatCap );
00075   arrowPen.setColor( mArrowColor );
00076   painter->setPen( arrowPen );
00077   painter->setBrush( QBrush( mArrowColor ) );
00078   painter->drawLine( QPointF( mStartPoint.x() - transform().dx(), mStartPoint.y() - transform().dy() ), QPointF( mStopPoint.x() - transform().dx(), mStopPoint.y() - transform().dy() ) );
00079 
00080   if ( mMarkerMode == DefaultMarker )
00081   {
00082     drawHardcodedMarker( painter, EndMarker );
00083   }
00084   else if ( mMarkerMode == SVGMarker )
00085   {
00086     drawSVGMarker( painter, StartMarker, mStartMarkerFile );
00087     drawSVGMarker( painter, EndMarker, mEndMarkerFile );
00088   }
00089 
00090   drawFrame( painter );
00091   if ( isSelected() )
00092   {
00093     drawSelectionBoxes( painter );
00094   }
00095 }
00096 
00097 void QgsComposerArrow::setSceneRect( const QRectF& rectangle )
00098 {
00099   //maintain the relative position of start and stop point in the rectangle
00100   double startPointXPos = ( mStartPoint.x() - transform().dx() ) / rect().width();
00101   double startPointYPos = ( mStartPoint.y() - transform().dy() ) / rect().height();
00102   double stopPointXPos = ( mStopPoint.x() - transform().dx() ) / rect().width();
00103   double stopPointYPos = ( mStopPoint.y() - transform().dy() ) / rect().height();
00104 
00105   mStartPoint.setX( rectangle.left() + startPointXPos * rectangle.width() );
00106   mStartPoint.setY( rectangle.top() + startPointYPos * rectangle.height() );
00107   mStopPoint.setX( rectangle.left() + stopPointXPos * rectangle.width() );
00108   mStopPoint.setY( rectangle.top() + stopPointYPos * rectangle.height() );
00109 
00110   adaptItemSceneRect();
00111 }
00112 
00113 void QgsComposerArrow::drawHardcodedMarker( QPainter *p, MarkerType type )
00114 {
00115   Q_UNUSED( type );
00116   QBrush arrowBrush = p->brush();
00117   arrowBrush.setColor( mArrowColor );
00118   p->setBrush( arrowBrush );
00119   drawArrowHead( p, mStopPoint.x() - transform().dx(), mStopPoint.y() - transform().dy(), angle( mStartPoint, mStopPoint ), mArrowHeadWidth );
00120 }
00121 
00122 void QgsComposerArrow::drawSVGMarker( QPainter* p, MarkerType type, const QString &markerPath )
00123 {
00124   Q_UNUSED( markerPath );
00125   double ang = angle( mStartPoint, mStopPoint );
00126 
00127   double arrowHeadHeight;
00128   if ( type == StartMarker )
00129   {
00130     arrowHeadHeight = mStartArrowHeadHeight;
00131   }
00132   else
00133   {
00134     arrowHeadHeight = mStopArrowHeadHeight;
00135   }
00136 
00137   //prepare paint device
00138   int dpi = ( p->device()->logicalDpiX() + p->device()->logicalDpiY() ) / 2;
00139   double viewScaleFactor = horizontalViewScaleFactor();
00140   int imageWidth = mArrowHeadWidth / 25.4 * dpi;
00141   int imageHeight = arrowHeadHeight / 25.4 * dpi;
00142 
00143   //make nicer preview
00144   if ( mComposition && mComposition->plotStyle() == QgsComposition::Preview )
00145   {
00146     imageWidth *= qMin( viewScaleFactor, 10.0 );
00147     imageHeight *= qMin( viewScaleFactor, 10.0 );
00148   }
00149   QImage markerImage( imageWidth, imageHeight, QImage::Format_ARGB32 );
00150   QColor markerBG( 255, 255, 255, 0 ); //transparent white background
00151   markerImage.fill( markerBG.rgba() );
00152 
00153   QPointF imageFixPoint;
00154   imageFixPoint.setX( mArrowHeadWidth / 2.0 );
00155   QPointF canvasPoint;
00156   if ( type == StartMarker )
00157   {
00158     canvasPoint = QPointF( mStartPoint.x() - transform().dx(), mStartPoint.y() - transform().dy() );
00159     imageFixPoint.setY( mStartArrowHeadHeight );
00160   }
00161   else //end marker
00162   {
00163     canvasPoint = QPointF( mStopPoint.x() - transform().dx(), mStopPoint.y() - transform().dy() );
00164     imageFixPoint.setY( 0 );
00165   }
00166 
00167   //rasterize svg
00168   QSvgRenderer r;
00169   if ( type == StartMarker )
00170   {
00171     if ( !r.load( mStartMarkerFile ) )
00172     {
00173       return;
00174     }
00175   }
00176   else //end marker
00177   {
00178     if ( !r.load( mEndMarkerFile ) )
00179     {
00180       return;
00181     }
00182   }
00183 
00184   //rotate image fix point for backtransform
00185   QPointF fixPoint;
00186   if ( type == StartMarker )
00187   {
00188     fixPoint.setX( 0 ); fixPoint.setY( arrowHeadHeight / 2.0 );
00189   }
00190   else
00191   {
00192     fixPoint.setX( 0 ); fixPoint.setY( -arrowHeadHeight / 2.0 );
00193   }
00194   QPointF rotatedFixPoint;
00195   double angleRad = ang / 180 * M_PI;
00196   rotatedFixPoint.setX( fixPoint.x() * cos( angleRad ) + fixPoint.y() * -sin( angleRad ) );
00197   rotatedFixPoint.setY( fixPoint.x() * sin( angleRad ) + fixPoint.y() * cos( angleRad ) );
00198 
00199 
00200   QPainter imagePainter( &markerImage );
00201   r.render( &imagePainter );
00202 
00203   p->save();
00204   p->translate( canvasPoint.x() - rotatedFixPoint.x() , canvasPoint.y() - rotatedFixPoint.y() );
00205   p->rotate( ang );
00206   p->translate( -mArrowHeadWidth / 2.0, -arrowHeadHeight / 2.0 );
00207 
00208   p->drawImage( QRectF( 0, 0, mArrowHeadWidth, arrowHeadHeight ), markerImage, QRectF( 0, 0, imageWidth, imageHeight ) );
00209   p->restore();
00210 
00211   return;
00212 }
00213 
00214 void QgsComposerArrow::setStartMarker( const QString& svgPath )
00215 {
00216   QSvgRenderer r;
00217   if ( !r.load( svgPath ) )
00218   {
00219     return;
00220     // mStartArrowHeadHeight = 0;
00221   }
00222   mStartMarkerFile = svgPath;
00223 
00224   //calculate mArrowHeadHeight from svg file and mArrowHeadWidth
00225   QRect viewBox = r.viewBox();
00226   mStartArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
00227   adaptItemSceneRect();
00228 }
00229 
00230 void QgsComposerArrow::setEndMarker( const QString& svgPath )
00231 {
00232   QSvgRenderer r;
00233   if ( !r.load( svgPath ) )
00234   {
00235     return;
00236     // mStopArrowHeadHeight = 0;
00237   }
00238   mEndMarkerFile = svgPath;
00239 
00240   //calculate mArrowHeadHeight from svg file and mArrowHeadWidth
00241   QRect viewBox = r.viewBox();
00242   mStopArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
00243   adaptItemSceneRect();
00244 }
00245 
00246 void QgsComposerArrow::setOutlineWidth( double width )
00247 {
00248   mPen.setWidthF( width );
00249   adaptItemSceneRect();
00250 }
00251 
00252 void QgsComposerArrow::setArrowHeadWidth( double width )
00253 {
00254   mArrowHeadWidth = width;
00255   setStartMarker( mStartMarkerFile );
00256   setEndMarker( mEndMarkerFile );
00257   adaptItemSceneRect();
00258 }
00259 
00260 void QgsComposerArrow::adaptItemSceneRect()
00261 {
00262   //rectangle containing start and end point
00263   QRectF rect = QRectF( qMin( mStartPoint.x(), mStopPoint.x() ), qMin( mStartPoint.y(), mStopPoint.y() ),
00264                         qAbs( mStopPoint.x() - mStartPoint.x() ), qAbs( mStopPoint.y() - mStartPoint.y() ) );
00265   double enlarge = 0;
00266   if ( mMarkerMode == DefaultMarker )
00267   {
00268     enlarge = mPen.widthF() / 2.0 + mArrowHeadWidth / 2.0;
00269   }
00270   else if ( mMarkerMode == NoMarker )
00271   {
00272     enlarge = mPen.widthF() / 2.0;
00273   }
00274   else if ( mMarkerMode == SVGMarker )
00275   {
00276     double maxArrowHeight = qMax( mStartArrowHeadHeight, mStopArrowHeadHeight );
00277     enlarge = mPen.widthF() / 2 + qMax( mArrowHeadWidth / 2.0, maxArrowHeight / 2.0 );
00278   }
00279 
00280   rect.adjust( -enlarge, -enlarge, enlarge, enlarge );
00281   QgsComposerItem::setSceneRect( rect );
00282 }
00283 
00284 bool QgsComposerArrow::writeXML( QDomElement& elem, QDomDocument & doc ) const
00285 {
00286   QDomElement composerArrowElem = doc.createElement( "ComposerArrow" );
00287   composerArrowElem.setAttribute( "outlineWidth", QString::number( outlineWidth() ) );
00288   composerArrowElem.setAttribute( "arrowHeadWidth", QString::number( mArrowHeadWidth ) );
00289   composerArrowElem.setAttribute( "markerMode", mMarkerMode );
00290   composerArrowElem.setAttribute( "startMarkerFile", mStartMarkerFile );
00291   composerArrowElem.setAttribute( "endMarkerFile", mEndMarkerFile );
00292 
00293   //arrow color
00294   QDomElement arrowColorElem = doc.createElement( "ArrowColor" );
00295   arrowColorElem.setAttribute( "red", mArrowColor.red() );
00296   arrowColorElem.setAttribute( "green", mArrowColor.green() );
00297   arrowColorElem.setAttribute( "blue", mArrowColor.blue() );
00298   arrowColorElem.setAttribute( "alpha", mArrowColor.alpha() );
00299   composerArrowElem.appendChild( arrowColorElem );
00300 
00301   //start point
00302   QDomElement startPointElem = doc.createElement( "StartPoint" );
00303   startPointElem.setAttribute( "x", QString::number( mStartPoint.x() ) );
00304   startPointElem.setAttribute( "y", QString::number( mStartPoint.y() ) );
00305   composerArrowElem.appendChild( startPointElem );
00306 
00307   //stop point
00308   QDomElement stopPointElem = doc.createElement( "StopPoint" );
00309   stopPointElem.setAttribute( "x", QString::number( mStopPoint.x() ) );
00310   stopPointElem.setAttribute( "y", QString::number( mStopPoint.y() ) );
00311   composerArrowElem.appendChild( stopPointElem );
00312 
00313   elem.appendChild( composerArrowElem );
00314   return _writeXML( composerArrowElem, doc );
00315 }
00316 
00317 bool QgsComposerArrow::readXML( const QDomElement& itemElem, const QDomDocument& doc )
00318 {
00319   mArrowHeadWidth = itemElem.attribute( "arrowHeadWidth", "2.0" ).toDouble();
00320   mPen.setWidthF( itemElem.attribute( "outlineWidth", "1.0" ).toDouble() );
00321   setStartMarker( itemElem.attribute( "startMarkerFile", "" ) );
00322   setEndMarker( itemElem.attribute( "endMarkerFile", "" ) );
00323   mMarkerMode = QgsComposerArrow::MarkerMode( itemElem.attribute( "markerMode", "0" ).toInt() );
00324 
00325   //arrow color
00326   QDomNodeList arrowColorList = itemElem.elementsByTagName( "ArrowColor" );
00327   if ( arrowColorList.size() > 0 )
00328   {
00329     QDomElement arrowColorElem = arrowColorList.at( 0 ).toElement();
00330     int red = arrowColorElem.attribute( "red", "0" ).toInt();
00331     int green = arrowColorElem.attribute( "green", "0" ).toInt();
00332     int blue = arrowColorElem.attribute( "blue", "0" ).toInt();
00333     int alpha = arrowColorElem.attribute( "alpha", "255" ).toInt();
00334     mArrowColor = QColor( red, green, blue, alpha );
00335   }
00336 
00337   //restore general composer item properties
00338   //needs to be before start point / stop point because setSceneRect()
00339   QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
00340   if ( composerItemList.size() > 0 )
00341   {
00342     QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
00343     _readXML( composerItemElem, doc );
00344   }
00345 
00346   //start point
00347   QDomNodeList startPointList = itemElem.elementsByTagName( "StartPoint" );
00348   if ( startPointList.size() > 0 )
00349   {
00350     QDomElement startPointElem = startPointList.at( 0 ).toElement();
00351     mStartPoint.setX( startPointElem.attribute( "x", "0.0" ).toDouble() );
00352     mStartPoint.setY( startPointElem.attribute( "y", "0.0" ).toDouble() );
00353   }
00354 
00355   //stop point
00356   QDomNodeList stopPointList = itemElem.elementsByTagName( "StopPoint" );
00357   if ( stopPointList.size() > 0 )
00358   {
00359     QDomElement stopPointElem = stopPointList.at( 0 ).toElement();
00360     mStopPoint.setX( stopPointElem.attribute( "x", "0.0" ).toDouble() );
00361     mStopPoint.setY( stopPointElem.attribute( "y", "0.0" ).toDouble() );
00362   }
00363 
00364   adaptItemSceneRect();
00365   emit itemChanged();
00366   return true;
00367 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines