QGIS API Documentation  2.2.0-Valmiera
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgscomposershape.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposershape.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 "qgscomposershape.h"
19 #include "qgscomposition.h"
20 #include "qgssymbolv2.h"
21 #include "qgssymbollayerv2utils.h"
22 #include <QPainter>
23 
25  mShape( Ellipse ),
26  mCornerRadius( 0 ),
27  mUseSymbolV2( false ), //default to not using SymbolV2 for shapes, to preserve 2.0 api
28  mShapeStyleSymbol( 0 )
29 {
30  setFrameEnabled( true );
32 }
33 
34 QgsComposerShape::QgsComposerShape( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition ):
35  QgsComposerItem( x, y, width, height, composition ),
36  mShape( Ellipse ),
37  mCornerRadius( 0 ),
38  mUseSymbolV2( false ), //default to not using SymbolV2 for shapes, to preserve 2.0 api
39  mShapeStyleSymbol( 0 )
40 {
41  setSceneRect( QRectF( x, y, width, height ) );
42  setFrameEnabled( true );
44 }
45 
47 {
48  delete mShapeStyleSymbol;
49 }
50 
51 void QgsComposerShape::setUseSymbolV2( bool useSymbolV2 )
52 {
53  mUseSymbolV2 = useSymbolV2;
54  setFrameEnabled( !useSymbolV2 );
55 }
56 
58 {
59  delete mShapeStyleSymbol;
60  mShapeStyleSymbol = symbol;
61  update();
62  emit frameChanged();
63 }
64 
66 {
67  update();
68  emit frameChanged();
69 }
70 
72 {
73  delete mShapeStyleSymbol;
74  QgsStringMap properties;
75  properties.insert( "color", "white" );
76  properties.insert( "style", "solid" );
77  properties.insert( "style_border", "solid" );
78  properties.insert( "color_border", "black" );
79  properties.insert( "width_border", "0.3" );
81 }
82 
83 void QgsComposerShape::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
84 {
85  Q_UNUSED( itemStyle );
86  Q_UNUSED( pWidget );
87  if ( !painter )
88  {
89  return;
90  }
91  drawBackground( painter );
92  drawFrame( painter );
93 
94  if ( isSelected() )
95  {
96  drawSelectionBoxes( painter );
97  }
98 }
99 
100 
101 void QgsComposerShape::drawShape( QPainter* p )
102 {
103  if ( mUseSymbolV2 )
104  {
106  return;
107  }
108 
109  //draw using QPainter brush and pen to keep 2.0 api compatibility
110  p->save();
111  p->setRenderHint( QPainter::Antialiasing );
112 
113  switch ( mShape )
114  {
115  case Ellipse:
116  p->drawEllipse( QRectF( 0, 0 , rect().width(), rect().height() ) );
117  break;
118  case Rectangle:
119  //if corner radius set, then draw a rounded rectangle
120  if ( mCornerRadius > 0 )
121  {
122  p->drawRoundedRect( QRectF( 0, 0 , rect().width(), rect().height() ), mCornerRadius, mCornerRadius );
123  }
124  else
125  {
126  p->drawRect( QRectF( 0, 0 , rect().width(), rect().height() ) );
127  }
128  break;
129  case Triangle:
130  QPolygonF triangle;
131  triangle << QPointF( 0, rect().height() );
132  triangle << QPointF( rect().width() , rect().height() );
133  triangle << QPointF( rect().width() / 2.0, 0 );
134  p->drawPolygon( triangle );
135  break;
136  }
137  p->restore();
138 }
139 
141 {
142  p->save();
143  p->setRenderHint( QPainter::Antialiasing );
144 
145  QgsRenderContext context;
146  context.setPainter( p );
147  context.setScaleFactor( 1.0 );
149  {
151  }
152  else
153  {
155  }
156 
157  //generate polygon to draw
158  QList<QPolygonF> rings; //empty list
159  QPolygonF shapePolygon;
160 
161  //shapes with curves must be enlarged before conversion to QPolygonF, or
162  //the curves are approximated too much and appear jaggy
163  QTransform t = QTransform::fromScale( 100, 100 );
164  //inverse transform used to scale created polygons back to expected size
165  QTransform ti = t.inverted();
166 
167  switch ( mShape )
168  {
169  case Ellipse:
170  {
171  //create an ellipse
172  QPainterPath ellipsePath;
173  ellipsePath.addEllipse( QRectF( 0, 0 , rect().width(), rect().height() ) );
174  QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
175  shapePolygon = ti.map( ellipsePoly );
176  break;
177  }
178  case Rectangle:
179  {
180  //if corner radius set, then draw a rounded rectangle
181  if ( mCornerRadius > 0 )
182  {
183  QPainterPath roundedRectPath;
184  roundedRectPath.addRoundedRect( QRectF( 0, 0 , rect().width(), rect().height() ), mCornerRadius, mCornerRadius );
185  QPolygonF roundedPoly = roundedRectPath.toFillPolygon( t );
186  shapePolygon = ti.map( roundedPoly );
187  }
188  else
189  {
190  shapePolygon = QPolygonF( QRectF( 0, 0, rect().width(), rect().height() ) );
191  }
192  break;
193  }
194  case Triangle:
195  {
196  shapePolygon << QPointF( 0, rect().height() );
197  shapePolygon << QPointF( rect().width() , rect().height() );
198  shapePolygon << QPointF( rect().width() / 2.0, 0 );
199  shapePolygon << QPointF( 0, rect().height() );
200  break;
201  }
202  }
203 
204  mShapeStyleSymbol->startRender( context );
205 
207 
208  //even though we aren't going to use it to draw the shape, set the pen width as 2 * symbol bleed
209  //so that the item is fully rendered within it's scene rect
210  //(QGraphicsRectItem considers the pen width when calculating an item's scene rect)
211  setPen( QPen( QBrush( Qt::NoBrush ), maxBleed * 2.0 ) );
212 
213  //need to render using atlas feature properties?
215  {
216  //using an atlas, so render using current atlas feature
217  //since there may be data defined symbols using atlas feature properties
218  mShapeStyleSymbol->renderPolygon( shapePolygon, &rings, mComposition->atlasComposition().currentFeature(), context );
219  }
220  else
221  {
222  mShapeStyleSymbol->renderPolygon( shapePolygon, &rings, 0, context );
223  }
224 
225  mShapeStyleSymbol->stopRender( context );
226  p->restore();
227 }
228 
229 
230 void QgsComposerShape::drawFrame( QPainter* p )
231 {
232  if ( mFrame && p && !mUseSymbolV2 )
233  {
234  p->setPen( pen() );
235  p->setBrush( Qt::NoBrush );
236  p->setRenderHint( QPainter::Antialiasing, true );
237  drawShape( p );
238  }
239 }
240 
242 {
243  if ( p && ( mBackground || mUseSymbolV2 ) )
244  {
245  p->setBrush( brush() );//this causes a problem in atlas generation
246  p->setPen( Qt::NoPen );
247  p->setRenderHint( QPainter::Antialiasing, true );
248  drawShape( p );
249  }
250 }
251 
253 {
255 }
256 
257 bool QgsComposerShape::writeXML( QDomElement& elem, QDomDocument & doc ) const
258 {
259  QDomElement composerShapeElem = doc.createElement( "ComposerShape" );
260  composerShapeElem.setAttribute( "shapeType", mShape );
261  composerShapeElem.setAttribute( "cornerRadius", mCornerRadius );
262 
263  QDomElement shapeStyleElem = QgsSymbolLayerV2Utils::saveSymbol( QString(), mShapeStyleSymbol, doc );
264  composerShapeElem.appendChild( shapeStyleElem );
265 
266  elem.appendChild( composerShapeElem );
267  return _writeXML( composerShapeElem, doc );
268 }
269 
270 bool QgsComposerShape::readXML( const QDomElement& itemElem, const QDomDocument& doc )
271 {
272  mShape = QgsComposerShape::Shape( itemElem.attribute( "shapeType", "0" ).toInt() );
273  mCornerRadius = itemElem.attribute( "cornerRadius", "0" ).toDouble();
274 
275  //restore general composer item properties
276  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
277  if ( composerItemList.size() > 0 )
278  {
279  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
280 
281  //rotation
282  if ( composerItemElem.attribute( "rotation", "0" ).toDouble() != 0 )
283  {
284  //check for old (pre 2.1) rotation attribute
285  setItemRotation( composerItemElem.attribute( "rotation", "0" ).toDouble() );
286  }
287 
288  _readXML( composerItemElem, doc );
289  }
290 
291  QDomElement shapeStyleSymbolElem = itemElem.firstChildElement( "symbol" );
292  if ( !shapeStyleSymbolElem.isNull() )
293  {
294  delete mShapeStyleSymbol;
295  mShapeStyleSymbol = dynamic_cast<QgsFillSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol( shapeStyleSymbolElem ) );
296  }
297  else
298  {
299  //upgrade project file from 2.0 to use symbolV2 styling
300  delete mShapeStyleSymbol;
301  QgsStringMap properties;
302  properties.insert( "color", QgsSymbolLayerV2Utils::encodeColor( brush().color() ) );
303  if ( hasBackground() )
304  {
305  properties.insert( "style", "solid" );
306  }
307  else
308  {
309  properties.insert( "style", "no" );
310  }
311  if ( hasFrame() )
312  {
313  properties.insert( "style_border", "solid" );
314  }
315  else
316  {
317  properties.insert( "style_border", "no" );
318  }
319  properties.insert( "color_border", QgsSymbolLayerV2Utils::encodeColor( pen().color() ) );
320  properties.insert( "width_border", QString::number( pen().widthF() ) );
322  }
323  emit itemChanged();
324  return true;
325 }
326 
328 {
329  mCornerRadius = radius;
330 }