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