QGIS API Documentation  2.2.0-Valmiera
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgscomposerarrow.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposerarrow.cpp
3  ----------------------
4  begin : November 2009
5  copyright : (C) 2009 by Marco Hugentobler
6  email : [email protected]
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgscomposerarrow.h"
19 #include "qgscomposition.h"
20 #include <QPainter>
21 #include <QSvgRenderer>
22 
23 #include <cmath>
24 
26  : QgsComposerItem( c )
27  , mStartPoint( 0, 0 )
28  , mStopPoint( 0, 0 )
29  , mMarkerMode( DefaultMarker )
30  , mArrowColor( QColor( 0, 0, 0 ) )
31 {
33 }
34 
35 QgsComposerArrow::QgsComposerArrow( const QPointF& startPoint, const QPointF& stopPoint, QgsComposition* c )
36  : QgsComposerItem( c )
37  , mStartPoint( startPoint )
38  , mStopPoint( stopPoint )
39  , mMarkerMode( DefaultMarker )
40  , mArrowColor( QColor( 0, 0, 0 ) )
41 {
44 }
45 
47 {
48 
49 }
50 
52 {
53  setArrowHeadWidth( 4 );
54  mPen.setColor( QColor( 0, 0, 0 ) );
55  mPen.setWidthF( 1 );
56 
57  //set composer item brush and pen to transparent white by default
58  setPen( QPen( QColor( 255, 255, 255, 0 ) ) );
59  setBrush( QBrush( QColor( 255, 255, 255, 0 ) ) );
60 }
61 
62 void QgsComposerArrow::paint( QPainter* painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
63 {
64  Q_UNUSED( itemStyle );
65  Q_UNUSED( pWidget );
66  if ( !painter )
67  {
68  return;
69  }
70 
71  drawBackground( painter );
72 
73  //draw arrow
74  QPen arrowPen = mPen;
75  arrowPen.setCapStyle( Qt::FlatCap );
76  arrowPen.setColor( mArrowColor );
77  painter->setPen( arrowPen );
78  painter->setBrush( QBrush( mArrowColor ) );
79  painter->drawLine( QPointF( mStartPoint.x() - pos().x(), mStartPoint.y() - pos().y() ), QPointF( mStopPoint.x() - pos().x(), mStopPoint.y() - pos().y() ) );
80 
81  if ( mMarkerMode == DefaultMarker )
82  {
83  drawHardcodedMarker( painter, EndMarker );
84  }
85  else if ( mMarkerMode == SVGMarker )
86  {
89  }
90 
91  drawFrame( painter );
92  if ( isSelected() )
93  {
94  drawSelectionBoxes( painter );
95  }
96 }
97 
98 void QgsComposerArrow::setSceneRect( const QRectF& rectangle )
99 {
100  //maintain the relative position of start and stop point in the rectangle
101  double startPointXPos = ( mStartPoint.x() - pos().x() ) / rect().width();
102  double startPointYPos = ( mStartPoint.y() - pos().y() ) / rect().height();
103  double stopPointXPos = ( mStopPoint.x() - pos().x() ) / rect().width();
104  double stopPointYPos = ( mStopPoint.y() - pos().y() ) / rect().height();
105 
106  mStartPoint.setX( rectangle.left() + startPointXPos * rectangle.width() );
107  mStartPoint.setY( rectangle.top() + startPointYPos * rectangle.height() );
108  mStopPoint.setX( rectangle.left() + stopPointXPos * rectangle.width() );
109  mStopPoint.setY( rectangle.top() + stopPointYPos * rectangle.height() );
110 
112 }
113 
115 {
116  Q_UNUSED( type );
117  QBrush arrowBrush = p->brush();
118  arrowBrush.setColor( mArrowColor );
119  p->setBrush( arrowBrush );
120  drawArrowHead( p, mStopPoint.x() - pos().x(), mStopPoint.y() - pos().y(), angle( mStartPoint, mStopPoint ), mArrowHeadWidth );
121 }
122 
123 void QgsComposerArrow::drawSVGMarker( QPainter* p, MarkerType type, const QString &markerPath )
124 {
125  Q_UNUSED( markerPath );
126  double ang = angle( mStartPoint, mStopPoint );
127 
128  double arrowHeadHeight;
129  if ( type == StartMarker )
130  {
131  arrowHeadHeight = mStartArrowHeadHeight;
132  }
133  else
134  {
135  arrowHeadHeight = mStopArrowHeadHeight;
136  }
137 
138  //prepare paint device
139  int dpi = ( p->device()->logicalDpiX() + p->device()->logicalDpiY() ) / 2;
140  double viewScaleFactor = horizontalViewScaleFactor();
141  int imageWidth = mArrowHeadWidth / 25.4 * dpi;
142  int imageHeight = arrowHeadHeight / 25.4 * dpi;
143 
144  //make nicer preview
146  {
147  imageWidth *= qMin( viewScaleFactor, 10.0 );
148  imageHeight *= qMin( viewScaleFactor, 10.0 );
149  }
150  QImage markerImage( imageWidth, imageHeight, QImage::Format_ARGB32 );
151  QColor markerBG( 255, 255, 255, 0 ); //transparent white background
152  markerImage.fill( markerBG.rgba() );
153 
154  QPointF imageFixPoint;
155  imageFixPoint.setX( mArrowHeadWidth / 2.0 );
156  QPointF canvasPoint;
157  if ( type == StartMarker )
158  {
159  canvasPoint = QPointF( mStartPoint.x() - pos().x(), mStartPoint.y() - pos().y() );
160  imageFixPoint.setY( mStartArrowHeadHeight );
161  }
162  else //end marker
163  {
164  canvasPoint = QPointF( mStopPoint.x() - pos().x(), mStopPoint.y() - pos().y() );
165  imageFixPoint.setY( 0 );
166  }
167 
168  //rasterize svg
169  QSvgRenderer r;
170  if ( type == StartMarker )
171  {
172  if ( !r.load( mStartMarkerFile ) )
173  {
174  return;
175  }
176  }
177  else //end marker
178  {
179  if ( !r.load( mEndMarkerFile ) )
180  {
181  return;
182  }
183  }
184 
185  //rotate image fix point for backtransform
186  QPointF fixPoint;
187  if ( type == StartMarker )
188  {
189  fixPoint.setX( 0 ); fixPoint.setY( arrowHeadHeight / 2.0 );
190  }
191  else
192  {
193  fixPoint.setX( 0 ); fixPoint.setY( -arrowHeadHeight / 2.0 );
194  }
195  QPointF rotatedFixPoint;
196  double angleRad = ang / 180 * M_PI;
197  rotatedFixPoint.setX( fixPoint.x() * cos( angleRad ) + fixPoint.y() * -sin( angleRad ) );
198  rotatedFixPoint.setY( fixPoint.x() * sin( angleRad ) + fixPoint.y() * cos( angleRad ) );
199 
200 
201  QPainter imagePainter( &markerImage );
202  r.render( &imagePainter );
203 
204  p->save();
205  p->translate( canvasPoint.x() - rotatedFixPoint.x() , canvasPoint.y() - rotatedFixPoint.y() );
206  p->rotate( ang );
207  p->translate( -mArrowHeadWidth / 2.0, -arrowHeadHeight / 2.0 );
208 
209  p->drawImage( QRectF( 0, 0, mArrowHeadWidth, arrowHeadHeight ), markerImage, QRectF( 0, 0, imageWidth, imageHeight ) );
210  p->restore();
211 
212  return;
213 }
214 
215 void QgsComposerArrow::setStartMarker( const QString& svgPath )
216 {
217  QSvgRenderer r;
218  if ( !r.load( svgPath ) )
219  {
220  return;
221  // mStartArrowHeadHeight = 0;
222  }
223  mStartMarkerFile = svgPath;
224 
225  //calculate mArrowHeadHeight from svg file and mArrowHeadWidth
226  QRect viewBox = r.viewBox();
227  mStartArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
229 }
230 
231 void QgsComposerArrow::setEndMarker( const QString& svgPath )
232 {
233  QSvgRenderer r;
234  if ( !r.load( svgPath ) )
235  {
236  return;
237  // mStopArrowHeadHeight = 0;
238  }
239  mEndMarkerFile = svgPath;
240 
241  //calculate mArrowHeadHeight from svg file and mArrowHeadWidth
242  QRect viewBox = r.viewBox();
243  mStopArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
245 }
246 
248 {
249  mPen.setWidthF( width );
251 }
252 
254 {
255  mArrowHeadWidth = width;
259 }
260 
262 {
263  //rectangle containing start and end point
264  QRectF rect = QRectF( qMin( mStartPoint.x(), mStopPoint.x() ), qMin( mStartPoint.y(), mStopPoint.y() ),
265  qAbs( mStopPoint.x() - mStartPoint.x() ), qAbs( mStopPoint.y() - mStartPoint.y() ) );
266  double enlarge = 0;
267  if ( mMarkerMode == DefaultMarker )
268  {
269  enlarge = mPen.widthF() / 2.0 + mArrowHeadWidth / 2.0;
270  }
271  else if ( mMarkerMode == NoMarker )
272  {
273  enlarge = mPen.widthF() / 2.0;
274  }
275  else if ( mMarkerMode == SVGMarker )
276  {
277  double maxArrowHeight = qMax( mStartArrowHeadHeight, mStopArrowHeadHeight );
278  enlarge = mPen.widthF() / 2 + qMax( mArrowHeadWidth / 2.0, maxArrowHeight / 2.0 );
279  }
280 
281  rect.adjust( -enlarge, -enlarge, enlarge, enlarge );
283 }
284 
285 bool QgsComposerArrow::writeXML( QDomElement& elem, QDomDocument & doc ) const
286 {
287  QDomElement composerArrowElem = doc.createElement( "ComposerArrow" );
288  composerArrowElem.setAttribute( "outlineWidth", QString::number( outlineWidth() ) );
289  composerArrowElem.setAttribute( "arrowHeadWidth", QString::number( mArrowHeadWidth ) );
290  composerArrowElem.setAttribute( "markerMode", mMarkerMode );
291  composerArrowElem.setAttribute( "startMarkerFile", mStartMarkerFile );
292  composerArrowElem.setAttribute( "endMarkerFile", mEndMarkerFile );
293 
294  //arrow color
295  QDomElement arrowColorElem = doc.createElement( "ArrowColor" );
296  arrowColorElem.setAttribute( "red", mArrowColor.red() );
297  arrowColorElem.setAttribute( "green", mArrowColor.green() );
298  arrowColorElem.setAttribute( "blue", mArrowColor.blue() );
299  arrowColorElem.setAttribute( "alpha", mArrowColor.alpha() );
300  composerArrowElem.appendChild( arrowColorElem );
301 
302  //start point
303  QDomElement startPointElem = doc.createElement( "StartPoint" );
304  startPointElem.setAttribute( "x", QString::number( mStartPoint.x() ) );
305  startPointElem.setAttribute( "y", QString::number( mStartPoint.y() ) );
306  composerArrowElem.appendChild( startPointElem );
307 
308  //stop point
309  QDomElement stopPointElem = doc.createElement( "StopPoint" );
310  stopPointElem.setAttribute( "x", QString::number( mStopPoint.x() ) );
311  stopPointElem.setAttribute( "y", QString::number( mStopPoint.y() ) );
312  composerArrowElem.appendChild( stopPointElem );
313 
314  elem.appendChild( composerArrowElem );
315  return _writeXML( composerArrowElem, doc );
316 }
317 
318 bool QgsComposerArrow::readXML( const QDomElement& itemElem, const QDomDocument& doc )
319 {
320  mArrowHeadWidth = itemElem.attribute( "arrowHeadWidth", "2.0" ).toDouble();
321  mPen.setWidthF( itemElem.attribute( "outlineWidth", "1.0" ).toDouble() );
322  setStartMarker( itemElem.attribute( "startMarkerFile", "" ) );
323  setEndMarker( itemElem.attribute( "endMarkerFile", "" ) );
324  mMarkerMode = QgsComposerArrow::MarkerMode( itemElem.attribute( "markerMode", "0" ).toInt() );
325 
326  //arrow color
327  QDomNodeList arrowColorList = itemElem.elementsByTagName( "ArrowColor" );
328  if ( arrowColorList.size() > 0 )
329  {
330  QDomElement arrowColorElem = arrowColorList.at( 0 ).toElement();
331  int red = arrowColorElem.attribute( "red", "0" ).toInt();
332  int green = arrowColorElem.attribute( "green", "0" ).toInt();
333  int blue = arrowColorElem.attribute( "blue", "0" ).toInt();
334  int alpha = arrowColorElem.attribute( "alpha", "255" ).toInt();
335  mArrowColor = QColor( red, green, blue, alpha );
336  }
337 
338  //restore general composer item properties
339  //needs to be before start point / stop point because setSceneRect()
340  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
341  if ( composerItemList.size() > 0 )
342  {
343  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
344  _readXML( composerItemElem, doc );
345  }
346 
347  //start point
348  QDomNodeList startPointList = itemElem.elementsByTagName( "StartPoint" );
349  if ( startPointList.size() > 0 )
350  {
351  QDomElement startPointElem = startPointList.at( 0 ).toElement();
352  mStartPoint.setX( startPointElem.attribute( "x", "0.0" ).toDouble() );
353  mStartPoint.setY( startPointElem.attribute( "y", "0.0" ).toDouble() );
354  }
355 
356  //stop point
357  QDomNodeList stopPointList = itemElem.elementsByTagName( "StopPoint" );
358  if ( stopPointList.size() > 0 )
359  {
360  QDomElement stopPointElem = stopPointList.at( 0 ).toElement();
361  mStopPoint.setX( stopPointElem.attribute( "x", "0.0" ).toDouble() );
362  mStopPoint.setY( stopPointElem.attribute( "y", "0.0" ).toDouble() );
363  }
364 
366  emit itemChanged();
367  return true;
368 }