QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgscomposeritem.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposeritem.cpp
3  -------------------
4  begin : January 2005
5  copyright : (C) 2005 by Radim Blazek
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 #include <QWidget>
18 #include <QDomNode>
19 #include <QFile>
20 #include <QGraphicsLineItem>
21 #include <QGraphicsScene>
22 #include <QGraphicsSceneMouseEvent>
23 #include <QGraphicsView>
24 #include <QPainter>
25 #include <QUuid>
26 #include <QGraphicsEffect>
27 
28 #include "qgsproject.h"
29 
30 #include "qgscomposition.h"
31 #include "qgscomposeritem.h"
32 #include "qgscomposerframe.h"
33 #include "qgsdatadefined.h"
34 #include "qgscomposerutils.h"
35 #include "qgscomposermodel.h"
36 
37 #include <limits>
38 #include "qgsapplication.h"
39 #include "qgsrectangle.h" //just for debugging
40 #include "qgslogger.h"
41 #include "qgssymbollayerv2utils.h" //for pointOnLineWithDistance
42 #include "qgsmaprenderer.h" //for getCompositionMode
43 
44 #include <cmath>
45 
46 #define FONT_WORKAROUND_SCALE 10 //scale factor for upscaling fontsize and downscaling painter
47 
48 #ifndef M_DEG2RAD
49 #define M_DEG2RAD 0.0174532925
50 #endif
51 
52 QgsComposerItem::QgsComposerItem( QgsComposition* composition, bool manageZValue )
53  : QgsComposerObject( composition )
54  , QGraphicsRectItem( 0 )
55  , mRemovedFromComposition( false )
56  , mBoundingResizeRectangle( 0 )
57  , mHAlignSnapItem( 0 )
58  , mVAlignSnapItem( 0 )
59  , mFrame( false )
60  , mBackground( true )
61  , mBackgroundColor( QColor( 255, 255, 255, 255 ) )
62  , mFrameJoinStyle( Qt::MiterJoin )
63  , mItemPositionLocked( false )
64  , mLastValidViewScaleFactor( -1 )
65  , mItemRotation( 0 )
66  , mEvaluatedItemRotation( 0 )
67  , mBlendMode( QPainter::CompositionMode_SourceOver )
68  , mEffectsEnabled( true )
69  , mTransparency( 0 )
70  , mExcludeFromExports( false )
71  , mEvaluatedExcludeFromExports( false )
72  , mLastUsedPositionMode( UpperLeft )
73  , mIsGroupMember( false )
74  , mCurrentExportLayer( -1 )
75  , mId( "" )
76  , mUuid( QUuid::createUuid().toString() )
77 {
78  init( manageZValue );
79 }
80 
81 QgsComposerItem::QgsComposerItem( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition, bool manageZValue )
82  : QgsComposerObject( composition )
83  , QGraphicsRectItem( 0, 0, width, height, 0 )
84  , mRemovedFromComposition( false )
85  , mBoundingResizeRectangle( 0 )
86  , mHAlignSnapItem( 0 )
87  , mVAlignSnapItem( 0 )
88  , mFrame( false )
89  , mBackground( true )
90  , mBackgroundColor( QColor( 255, 255, 255, 255 ) )
91  , mFrameJoinStyle( Qt::MiterJoin )
92  , mItemPositionLocked( false )
93  , mLastValidViewScaleFactor( -1 )
94  , mItemRotation( 0 )
95  , mEvaluatedItemRotation( 0 )
96  , mBlendMode( QPainter::CompositionMode_SourceOver )
97  , mEffectsEnabled( true )
98  , mTransparency( 0 )
99  , mExcludeFromExports( false )
100  , mEvaluatedExcludeFromExports( false )
101  , mLastUsedPositionMode( UpperLeft )
102  , mIsGroupMember( false )
103  , mCurrentExportLayer( -1 )
104  , mId( "" )
105  , mUuid( QUuid::createUuid().toString() )
106 {
107  init( manageZValue );
108  setPos( x, y );
109 }
110 
111 void QgsComposerItem::init( const bool manageZValue )
112 {
113  setFlag( QGraphicsItem::ItemIsSelectable, true );
114  //set default pen and brush
115  setBrush( QBrush( QColor( 255, 255, 255, 255 ) ) );
116  QPen defaultPen( QColor( 0, 0, 0 ) );
117  defaultPen.setWidthF( 0.3 );
118  defaultPen.setJoinStyle( mFrameJoinStyle );
119  setPen( defaultPen );
120  //let z-Value be managed by composition
121  if ( mComposition && manageZValue )
122  {
123  mCompositionManagesZValue = true;
124  mComposition->addItemToZList( this );
125  }
126  else
127  {
128  mCompositionManagesZValue = false;
129  }
130 
131  // Setup composer effect
132  mEffect = new QgsComposerEffect();
133  setGraphicsEffect( mEffect );
134 
135  // data defined strings
136  mDataDefinedNames.insert( QgsComposerObject::PageNumber, QString( "dataDefinedPageNumber" ) );
137  mDataDefinedNames.insert( QgsComposerObject::PositionX, QString( "dataDefinedPositionX" ) );
138  mDataDefinedNames.insert( QgsComposerObject::PositionY, QString( "dataDefinedPositionY" ) );
139  mDataDefinedNames.insert( QgsComposerObject::ItemWidth, QString( "dataDefinedWidth" ) );
140  mDataDefinedNames.insert( QgsComposerObject::ItemHeight, QString( "dataDefinedHeight" ) );
141  mDataDefinedNames.insert( QgsComposerObject::ItemRotation, QString( "dataDefinedRotation" ) );
142  mDataDefinedNames.insert( QgsComposerObject::Transparency, QString( "dataDefinedTransparency" ) );
143  mDataDefinedNames.insert( QgsComposerObject::BlendMode, QString( "dataDefinedBlendMode" ) );
144  mDataDefinedNames.insert( QgsComposerObject::ExcludeFromExports, QString( "dataDefinedExcludeExports" ) );
145 }
146 
148 {
149  if ( mComposition && mCompositionManagesZValue )
150  {
152  }
153 
155  delete mEffect;
156 
158 }
159 
161 {
162  QgsDebugMsg( "entered." );
164  //inform model that id data has changed
165  if ( mComposition )
166  {
168  }
169  update(); //to draw selection boxes
170 }
171 
172 bool QgsComposerItem::_writeXML( QDomElement& itemElem, QDomDocument& doc ) const
173 {
174  if ( itemElem.isNull() )
175  {
176  return false;
177  }
178 
179  QDomElement composerItemElem = doc.createElement( "ComposerItem" );
180 
181  //frame
182  if ( mFrame )
183  {
184  composerItemElem.setAttribute( "frame", "true" );
185  }
186  else
187  {
188  composerItemElem.setAttribute( "frame", "false" );
189  }
190 
191  //background
192  if ( mBackground )
193  {
194  composerItemElem.setAttribute( "background", "true" );
195  }
196  else
197  {
198  composerItemElem.setAttribute( "background", "false" );
199  }
200 
201  //scene rect
202  QPointF pagepos = pagePos();
203  composerItemElem.setAttribute( "x", QString::number( pos().x() ) );
204  composerItemElem.setAttribute( "y", QString::number( pos().y() ) );
205  composerItemElem.setAttribute( "page", page() );
206  composerItemElem.setAttribute( "pagex", QString::number( pagepos.x() ) );
207  composerItemElem.setAttribute( "pagey", QString::number( pagepos.y() ) );
208  composerItemElem.setAttribute( "width", QString::number( rect().width() ) );
209  composerItemElem.setAttribute( "height", QString::number( rect().height() ) );
210  composerItemElem.setAttribute( "positionMode", QString::number(( int ) mLastUsedPositionMode ) );
211  composerItemElem.setAttribute( "zValue", QString::number( zValue() ) );
212  composerItemElem.setAttribute( "outlineWidth", QString::number( pen().widthF() ) );
213  composerItemElem.setAttribute( "frameJoinStyle", QgsSymbolLayerV2Utils::encodePenJoinStyle( mFrameJoinStyle ) );
214  composerItemElem.setAttribute( "itemRotation", QString::number( mItemRotation ) );
215  composerItemElem.setAttribute( "uuid", mUuid );
216  composerItemElem.setAttribute( "id", mId );
217  composerItemElem.setAttribute( "visibility", isVisible() );
218  //position lock for mouse moves/resizes
219  if ( mItemPositionLocked )
220  {
221  composerItemElem.setAttribute( "positionLock", "true" );
222  }
223  else
224  {
225  composerItemElem.setAttribute( "positionLock", "false" );
226  }
227 
228  composerItemElem.setAttribute( "lastValidViewScaleFactor", QString::number( mLastValidViewScaleFactor ) );
229 
230  //frame color
231  QDomElement frameColorElem = doc.createElement( "FrameColor" );
232  QColor frameColor = pen().color();
233  frameColorElem.setAttribute( "red", QString::number( frameColor.red() ) );
234  frameColorElem.setAttribute( "green", QString::number( frameColor.green() ) );
235  frameColorElem.setAttribute( "blue", QString::number( frameColor.blue() ) );
236  frameColorElem.setAttribute( "alpha", QString::number( frameColor.alpha() ) );
237  composerItemElem.appendChild( frameColorElem );
238 
239  //background color
240  QDomElement bgColorElem = doc.createElement( "BackgroundColor" );
241  QColor bgColor = brush().color();
242  bgColorElem.setAttribute( "red", QString::number( bgColor.red() ) );
243  bgColorElem.setAttribute( "green", QString::number( bgColor.green() ) );
244  bgColorElem.setAttribute( "blue", QString::number( bgColor.blue() ) );
245  bgColorElem.setAttribute( "alpha", QString::number( bgColor.alpha() ) );
246  composerItemElem.appendChild( bgColorElem );
247 
248  //blend mode
249  composerItemElem.setAttribute( "blendMode", QgsMapRenderer::getBlendModeEnum( mBlendMode ) );
250 
251  //transparency
252  composerItemElem.setAttribute( "transparency", QString::number( mTransparency ) );
253 
254  composerItemElem.setAttribute( "excludeFromExports", mExcludeFromExports );
255 
256  QgsComposerObject::writeXML( composerItemElem, doc );
257  itemElem.appendChild( composerItemElem );
258 
259  return true;
260 }
261 
262 bool QgsComposerItem::_readXML( const QDomElement& itemElem, const QDomDocument& doc )
263 {
264  Q_UNUSED( doc );
265  if ( itemElem.isNull() )
266  {
267  return false;
268  }
269 
270  QgsComposerObject::readXML( itemElem, doc );
271 
272  //rotation
273  setItemRotation( itemElem.attribute( "itemRotation", "0" ).toDouble() );
274 
275  //uuid
276  mUuid = itemElem.attribute( "uuid", QUuid::createUuid().toString() );
277 
278  // temporary for groups imported from templates
279  mTemplateUuid = itemElem.attribute( "templateUuid" );
280 
281  //id
282  QString id = itemElem.attribute( "id", "" );
283  setId( id );
284 
285  //frame
286  QString frame = itemElem.attribute( "frame" );
287  if ( frame.compare( "true", Qt::CaseInsensitive ) == 0 )
288  {
289  mFrame = true;
290  }
291  else
292  {
293  mFrame = false;
294  }
295 
296  //frame
297  QString background = itemElem.attribute( "background" );
298  if ( background.compare( "true", Qt::CaseInsensitive ) == 0 )
299  {
300  mBackground = true;
301  }
302  else
303  {
304  mBackground = false;
305  }
306 
307  //position lock for mouse moves/resizes
308  QString positionLock = itemElem.attribute( "positionLock" );
309  if ( positionLock.compare( "true", Qt::CaseInsensitive ) == 0 )
310  {
311  setPositionLock( true );
312  }
313  else
314  {
315  setPositionLock( false );
316  }
317 
318  //visibility
319  setVisibility( itemElem.attribute( "visibility", "1" ) != "0" );
320 
321  //position
322  int page;
323  double x, y, pagex, pagey, width, height;
324  bool xOk, yOk, pageOk, pagexOk, pageyOk, widthOk, heightOk, positionModeOK;
325 
326  x = itemElem.attribute( "x" ).toDouble( &xOk );
327  y = itemElem.attribute( "y" ).toDouble( &yOk );
328  page = itemElem.attribute( "page" ).toInt( &pageOk );
329  pagex = itemElem.attribute( "pagex" ).toDouble( &pagexOk );
330  pagey = itemElem.attribute( "pagey" ).toDouble( &pageyOk );
331  width = itemElem.attribute( "width" ).toDouble( &widthOk );
332  height = itemElem.attribute( "height" ).toDouble( &heightOk );
333  mLastUsedPositionMode = ( ItemPositionMode )itemElem.attribute( "positionMode" ).toInt( &positionModeOK );
334  if ( !positionModeOK )
335  {
337  }
338  if ( pageOk && pagexOk && pageyOk )
339  {
340  xOk = true;
341  yOk = true;
342  x = pagex;
343  y = ( page - 1 ) * ( mComposition->paperHeight() + composition()->spaceBetweenPages() ) + pagey;
344  }
345 
346  if ( !xOk || !yOk || !widthOk || !heightOk )
347  {
348  return false;
349  }
350 
351  mLastValidViewScaleFactor = itemElem.attribute( "lastValidViewScaleFactor", "-1" ).toDouble();
352 
353  setZValue( itemElem.attribute( "zValue" ).toDouble() );
354 
355  //pen
356  QDomNodeList frameColorList = itemElem.elementsByTagName( "FrameColor" );
357  if ( frameColorList.size() > 0 )
358  {
359  QDomElement frameColorElem = frameColorList.at( 0 ).toElement();
360  bool redOk, greenOk, blueOk, alphaOk, widthOk;
361  int penRed, penGreen, penBlue, penAlpha;
362  double penWidth;
363 
364  penWidth = itemElem.attribute( "outlineWidth" ).toDouble( &widthOk );
365  penRed = frameColorElem.attribute( "red" ).toDouble( &redOk );
366  penGreen = frameColorElem.attribute( "green" ).toDouble( &greenOk );
367  penBlue = frameColorElem.attribute( "blue" ).toDouble( &blueOk );
368  penAlpha = frameColorElem.attribute( "alpha" ).toDouble( &alphaOk );
369  mFrameJoinStyle = QgsSymbolLayerV2Utils::decodePenJoinStyle( itemElem.attribute( "frameJoinStyle", "miter" ) );
370 
371  if ( redOk && greenOk && blueOk && alphaOk && widthOk )
372  {
373  QPen framePen( QColor( penRed, penGreen, penBlue, penAlpha ) );
374  framePen.setWidthF( penWidth );
375  framePen.setJoinStyle( mFrameJoinStyle );
376  setPen( framePen );
377  }
378  }
379 
380  //brush
381  QDomNodeList bgColorList = itemElem.elementsByTagName( "BackgroundColor" );
382  if ( bgColorList.size() > 0 )
383  {
384  QDomElement bgColorElem = bgColorList.at( 0 ).toElement();
385  bool redOk, greenOk, blueOk, alphaOk;
386  int bgRed, bgGreen, bgBlue, bgAlpha;
387  bgRed = bgColorElem.attribute( "red" ).toDouble( &redOk );
388  bgGreen = bgColorElem.attribute( "green" ).toDouble( &greenOk );
389  bgBlue = bgColorElem.attribute( "blue" ).toDouble( &blueOk );
390  bgAlpha = bgColorElem.attribute( "alpha" ).toDouble( &alphaOk );
391  if ( redOk && greenOk && blueOk && alphaOk )
392  {
393  QColor brushColor( bgRed, bgGreen, bgBlue, bgAlpha );
394  setBackgroundColor( brushColor );
395  }
396  }
397 
398  //blend mode
399  setBlendMode( QgsMapRenderer::getCompositionMode(( QgsMapRenderer::BlendMode ) itemElem.attribute( "blendMode", "0" ).toUInt() ) );
400 
401  //transparency
402  setTransparency( itemElem.attribute( "transparency", "0" ).toInt() );
403 
404  mExcludeFromExports = itemElem.attribute( "excludeFromExports", "0" ).toInt();
406 
407  QRectF evaluatedRect = evalItemRect( QRectF( x, y, width, height ) );
408  setSceneRect( evaluatedRect );
409 
410  return true;
411 }
412 
413 void QgsComposerItem::setFrameEnabled( const bool drawFrame )
414 {
415  if ( drawFrame == mFrame )
416  {
417  //no change
418  return;
419  }
420 
421  mFrame = drawFrame;
422  emit frameChanged();
423 }
424 
425 void QgsComposerItem::setFrameOutlineColor( const QColor &color )
426 {
427  QPen itemPen = pen();
428  if ( itemPen.color() == color )
429  {
430  //no change
431  return;
432  }
433  itemPen.setColor( color );
434  setPen( itemPen );
435  emit frameChanged();
436 }
437 
439 {
440  QPen itemPen = pen();
441  if ( itemPen.widthF() == outlineWidth )
442  {
443  //no change
444  return;
445  }
446  itemPen.setWidthF( outlineWidth );
447  setPen( itemPen );
448  emit frameChanged();
449 }
450 
451 void QgsComposerItem::setFrameJoinStyle( const Qt::PenJoinStyle style )
452 {
453  if ( mFrameJoinStyle == style )
454  {
455  //no change
456  return;
457  }
458  mFrameJoinStyle = style;
459 
460  QPen itemPen = pen();
461  itemPen.setJoinStyle( mFrameJoinStyle );
462  setPen( itemPen );
463  emit frameChanged();
464 }
465 
467 {
468  if ( !hasFrame() )
469  {
470  return 0;
471  }
472 
473  return pen().widthF() / 2.0;
474 }
475 
477 {
478  double frameBleed = estimatedFrameBleed();
479  return rect().adjusted( -frameBleed, -frameBleed, frameBleed, frameBleed );
480 }
481 
483 {
484  if ( mComposition )
485  {
486  mComposition->beginCommand( this, commandText, c );
487  }
488 }
489 
491 {
492  if ( mComposition )
493  {
495  }
496 }
497 
499 {
500  if ( mComposition )
501  {
503  }
504 }
505 
507 {
508  Q_UNUSED( p );
510  {
511  return;
512  }
513 
514  if ( !isSelected() )
515  {
516  return;
517  }
518 
519  //logic for drawing additional graphics on selected items here (if required)
520 
521  //draw dotted border around locked, selected items
522  if ( positionLock() )
523  {
524  p->save();
525  p->setCompositionMode( QPainter::CompositionMode_Difference );
526 
527  // use a grey dashed pen - in difference mode this should always be visible
528  QPen selectedItemPen = QPen( QColor( 144, 144, 144, 255 ) );
529  selectedItemPen.setStyle( Qt::DotLine );
530  selectedItemPen.setWidth( 0 );
531  p->setPen( selectedItemPen );
532  p->setBrush( Qt::NoBrush );
533  p->drawPolygon( rect() );
534  p->restore();
535  }
536 
537 }
538 
539 void QgsComposerItem::drawFrame( QPainter* p )
540 {
541  if ( mFrame && p )
542  {
543  p->save();
544  p->setPen( pen() );
545  p->setBrush( Qt::NoBrush );
546  p->setRenderHint( QPainter::Antialiasing, true );
547  p->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) );
548  p->restore();
549  }
550 }
551 
552 void QgsComposerItem::setPositionLock( const bool lock )
553 {
554  if ( lock == mItemPositionLocked )
555  {
556  return;
557  }
558 
559  mItemPositionLocked = lock;
560 
561  //inform model that id data has changed
562  if ( mComposition )
563  {
565  }
566  update();
567  emit lockChanged();
568 }
569 
570 double QgsComposerItem::itemRotation( const PropertyValueType valueType ) const
571 {
573 }
574 
575 void QgsComposerItem::move( double dx, double dy )
576 {
577  QRectF newSceneRect( pos().x() + dx, pos().y() + dy, rect().width(), rect().height() );
578  setSceneRect( evalItemRect( newSceneRect ) );
579 }
580 
582 {
583  double y = pos().y();
584  double h = composition()->paperHeight() + composition()->spaceBetweenPages();
585  int page = 1;
586  while ( y - h >= 0. )
587  {
588  y -= h;
589  ++page;
590  }
591  return page;
592 }
593 
595 {
596  QPointF p = pos();
597  double h = composition()->paperHeight() + composition()->spaceBetweenPages();
598  p.ry() -= ( page() - 1 ) * h;
599  return p;
600 }
601 
602 void QgsComposerItem::updatePagePos( double newPageWidth, double newPageHeight )
603 {
604  Q_UNUSED( newPageWidth )
605  QPointF curPagePos = pagePos();
606  int curPage = page() - 1;
607 
608  double y = curPage * ( newPageHeight + composition()->spaceBetweenPages() ) + curPagePos.y();
609  QRectF newSceneRect( pos().x(), y, rect().width(), rect().height() );
610 
611  setSceneRect( evalItemRect( newSceneRect ) );
612  emit sizeChanged();
613 }
614 
615 void QgsComposerItem::setItemPosition( double x, double y, ItemPositionMode itemPoint, int page )
616 {
617  double width = rect().width();
618  double height = rect().height();
619  setItemPosition( x, y, width, height, itemPoint, false, page );
620 }
621 
622 void QgsComposerItem::setItemPosition( double x, double y, double width, double height, ItemPositionMode itemPoint, bool posIncludesFrame, int page )
623 {
624  double upperLeftX = x;
625  double upperLeftY = y;
626 
627  if ( page > 0 )
628  {
629  double h = composition()->paperHeight() + composition()->spaceBetweenPages();
630  upperLeftY += ( page - 1 ) * h;
631  }
632 
633  //store the item position mode
634  mLastUsedPositionMode = itemPoint;
635 
636  //adjust x-coordinate if placement is not done to a left point
637  if ( itemPoint == UpperMiddle || itemPoint == Middle || itemPoint == LowerMiddle )
638  {
639  upperLeftX -= width / 2.0;
640  }
641  else if ( itemPoint == UpperRight || itemPoint == MiddleRight || itemPoint == LowerRight )
642  {
643  upperLeftX -= width;
644  }
645 
646  //adjust y-coordinate if placement is not done to an upper point
647  if ( itemPoint == MiddleLeft || itemPoint == Middle || itemPoint == MiddleRight )
648  {
649  upperLeftY -= height / 2.0;
650  }
651  else if ( itemPoint == LowerLeft || itemPoint == LowerMiddle || itemPoint == LowerRight )
652  {
653  upperLeftY -= height;
654  }
655 
656  if ( posIncludesFrame )
657  {
658  //adjust position to account for frame size
659 
660  if ( mEvaluatedItemRotation == 0 )
661  {
662  upperLeftX += estimatedFrameBleed();
663  upperLeftY += estimatedFrameBleed();
664  }
665  else
666  {
667  //adjust position for item rotation
668  QLineF lineToItemOrigin = QLineF( 0, 0, estimatedFrameBleed(), estimatedFrameBleed() );
669  lineToItemOrigin.setAngle( -45 - mEvaluatedItemRotation );
670  upperLeftX += lineToItemOrigin.x2();
671  upperLeftY += lineToItemOrigin.y2();
672  }
673 
674  width -= 2 * estimatedFrameBleed();
675  height -= 2 * estimatedFrameBleed();
676  }
677 
678  //consider data defined item size and position before finalising rect
679  QRectF newRect = evalItemRect( QRectF( upperLeftX, upperLeftY, width, height ) );
680 
681  setSceneRect( newRect );
682 }
683 
684 void QgsComposerItem::setSceneRect( const QRectF& rectangle )
685 {
686  //setRect in item coordinates
687  double newWidth = rectangle.width();
688  double newHeight = rectangle.height();
689  double xTranslation = rectangle.x();
690  double yTranslation = rectangle.y();
691 
692  //correction if width and/or height are negative
693  if ( rectangle.width() < 0 )
694  {
695  newWidth = - rectangle.width();
696  xTranslation -= newWidth;
697  }
698 
699  if ( rectangle.height() < 0 )
700  {
701  newHeight = - rectangle.height();
702  yTranslation -= newHeight;
703  }
704 
705  QGraphicsRectItem::setRect( QRectF( 0, 0, newWidth, newHeight ) );
706  setPos( QPointF( xTranslation, yTranslation ) );
707 
708  emit sizeChanged();
709 }
710 
711 QRectF QgsComposerItem::evalItemRect( const QRectF &newRect, const bool resizeOnly )
712 {
713  QRectF result = newRect;
714 
715  //data defined position or size set? if so, update rect with data defined values
716  QVariant exprVal;
717  //evaulate width and height first, since they may affect position if non-top-left reference point set
719  {
720  bool ok;
721  double width = exprVal.toDouble( &ok );
722  QgsDebugMsg( QString( "exprVal Width:%1" ).arg( width ) );
723  if ( ok && !exprVal.isNull() )
724  {
725  result.setWidth( width );
726  }
727  }
729  {
730  bool ok;
731  double height = exprVal.toDouble( &ok );
732  QgsDebugMsg( QString( "exprVal Height:%1" ).arg( height ) );
733  if ( ok && !exprVal.isNull() )
734  {
735  result.setHeight( height );
736  }
737  }
738 
739  double x = result.left();
740  //initially adjust for position mode to get x coordinate
741  if ( !resizeOnly )
742  {
743  //adjust x-coordinate if placement is not done to a left point
745  {
746  x += newRect.width() / 2.0;
747  }
749  {
750  x += newRect.width();
751  }
752  }
753  else
754  {
756  {
757  x += rect().width() / 2.0;
758  }
760  {
761  x += rect().width();
762  }
763  }
765  {
766  bool ok;
767  double positionX = exprVal.toDouble( &ok );
768  QgsDebugMsg( QString( "exprVal Position X:%1" ).arg( positionX ) );
769  if ( ok && !exprVal.isNull() )
770  {
771  x = positionX;
772  }
773  }
774 
775  double y = result.top();
776  //initially adjust for position mode to get y coordinate
777  if ( !resizeOnly )
778  {
779  //adjust y-coordinate if placement is not done to an upper point
781  {
782  y += newRect.height() / 2.0;
783  }
785  {
786  y += newRect.height();
787  }
788  }
789  else
790  {
792  {
793  y += rect().height() / 2.0;
794  }
796  {
797  y += rect().height();
798  }
799  }
801  {
802  bool ok;
803  double positionY = exprVal.toDouble( &ok );
804  QgsDebugMsg( QString( "exprVal Position Y:%1" ).arg( positionY ) );
805  if ( ok && !exprVal.isNull() )
806  {
807  y = positionY;
808  }
809  }
810 
811  //adjust x-coordinate if placement is not done to a left point
813  {
814  x -= result.width() / 2.0;
815  }
817  {
818  x -= result.width();
819  }
820 
821  //adjust y-coordinate if placement is not done to an upper point
823  {
824  y -= result.height() / 2.0;
825  }
827  {
828  y -= result.height();
829  }
830 
831  result.moveLeft( x );
832  result.moveTop( y );
833 
834  return result;
835 }
836 
838 {
840  {
841  //preview mode or no composition, so ok to draw item
842  return true;
843  }
844 
845  //exporting composition, so check if item is excluded from exports
847 }
848 
850 {
851  if ( mBackground && p )
852  {
853  p->save();
854  p->setBrush( brush() );//this causes a problem in atlas generation
855  p->setPen( Qt::NoPen );
856  p->setRenderHint( QPainter::Antialiasing, true );
857  p->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) );
858  p->restore();
859  }
860 }
861 
862 void QgsComposerItem::drawArrowHead( QPainter *p, double x, double y, double angle, double arrowHeadWidth ) const
863 {
864  QgsComposerUtils::drawArrowHead( p, x, y, angle, arrowHeadWidth );
865 }
866 
867 double QgsComposerItem::angle( const QPointF &p1, const QPointF &p2 ) const
868 {
869  return QgsComposerUtils::angle( p1, p2 );
870 }
871 
872 void QgsComposerItem::setBackgroundColor( const QColor& backgroundColor )
873 {
875  setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
876 }
877 
878 void QgsComposerItem::setBlendMode( const QPainter::CompositionMode blendMode )
879 {
881  // Update the composer effect to use the new blend mode
882  refreshBlendMode();
883 }
884 
885 void QgsComposerItem::refreshBlendMode()
886 {
887  QPainter::CompositionMode blendMode = mBlendMode;
888 
889  //data defined blend mode set?
890  QVariant exprVal;
891  if ( dataDefinedEvaluate( QgsComposerObject::BlendMode, exprVal ) && !exprVal.isNull() )
892  {
893  QString blendstr = exprVal.toString().trimmed();
894  QPainter::CompositionMode blendModeD = QgsSymbolLayerV2Utils::decodeBlendMode( blendstr );
895 
896  QgsDebugMsg( QString( "exprVal BlendMode:%1" ).arg( blendModeD ) );
897  blendMode = blendModeD;
898  }
899 
900  // Update the composer effect to use the new blend mode
901  mEffect->setCompositionMode( blendMode );
902 }
903 
904 void QgsComposerItem::setTransparency( const int transparency )
905 {
907  refreshTransparency( true );
908 }
909 
910 void QgsComposerItem::refreshTransparency( const bool updateItem )
911 {
913 
914  //data defined transparency set?
915  QVariant exprVal;
917  {
918  bool ok;
919  int transparencyD = exprVal.toInt( &ok );
920  QgsDebugMsg( QString( "exprVal Transparency:%1" ).arg( transparencyD ) );
921  if ( ok && !exprVal.isNull() )
922  {
923  transparency = transparencyD;
924  }
925  }
926 
927  // Set the QGraphicItem's opacity
928  setOpacity( 1. - ( transparency / 100. ) );
929 
930  if ( updateItem )
931  {
932  update();
933  }
934 }
935 
936 void QgsComposerItem::setEffectsEnabled( const bool effectsEnabled )
937 {
938  //enable or disable the QgsComposerEffect applied to this item
940  mEffect->setEnabled( effectsEnabled );
941 }
942 
943 void QgsComposerItem::drawText( QPainter* p, double x, double y, const QString& text, const QFont& font, const QColor& c ) const
944 {
945  QgsComposerUtils::drawText( p, QPointF( x, y ), text, font, c );
946 }
947 
948 void QgsComposerItem::drawText( QPainter* p, const QRectF& rect, const QString& text, const QFont& font, Qt::AlignmentFlag halignment, Qt::AlignmentFlag valignment, int flags ) const
949 {
950  QgsComposerUtils::drawText( p, rect, text, font, QColor(), halignment, valignment, flags );
951 }
952 double QgsComposerItem::textWidthMillimeters( const QFont& font, const QString& text ) const
953 {
954  return QgsComposerUtils::textWidthMM( font, text );
955 }
956 
957 double QgsComposerItem::fontHeightCharacterMM( const QFont& font, const QChar& c ) const
958 {
959  return QgsComposerUtils::fontHeightCharacterMM( font, c );
960 }
961 
962 double QgsComposerItem::fontAscentMillimeters( const QFont& font ) const
963 {
964  return QgsComposerUtils::fontAscentMM( font );
965 }
966 
967 double QgsComposerItem::fontDescentMillimeters( const QFont& font ) const
968 {
969  return QgsComposerUtils::fontDescentMM( font );
970 }
971 
972 double QgsComposerItem::fontHeightMillimeters( const QFont& font ) const
973 {
974  return QgsComposerUtils::fontHeightMM( font );
975 }
976 
977 double QgsComposerItem::pixelFontSize( double pointSize ) const
978 {
979  return QgsComposerUtils::pointsToMM( pointSize );
980 }
981 
982 QFont QgsComposerItem::scaledFontPixelSize( const QFont& font ) const
983 {
985 }
986 
988 {
989  double result = -1;
990  if ( scene() )
991  {
992  QList<QGraphicsView*> viewList = scene()->views();
993  if ( viewList.size() > 0 ) //if not, probably this function was called from non-gui code
994  {
995  QGraphicsView* currentView = viewList.at( 0 );
996  if ( currentView->isVisible() )
997  {
998  result = currentView->transform().m11();
999  mLastValidViewScaleFactor = result;
1000  }
1001  }
1002  }
1003  return result;
1004 }
1005 
1007 {
1008  //size of symbol boxes depends on zoom level in composer view
1009  double viewScaleFactor = horizontalViewScaleFactor();
1010  double rectHandlerSize = 10.0 / viewScaleFactor;
1011 
1012  //make sure the boxes don't get too large
1013  if ( rectHandlerSize > ( rect().width() / 3 ) )
1014  {
1015  rectHandlerSize = rect().width() / 3;
1016  }
1017  if ( rectHandlerSize > ( rect().height() / 3 ) )
1018  {
1019  rectHandlerSize = rect().height() / 3;
1020  }
1021  return rectHandlerSize;
1022 }
1023 
1025 {
1026  double lockSymbolSize = 20.0 / horizontalViewScaleFactor();
1027 
1028  if ( lockSymbolSize > ( rect().width() / 3 ) )
1029  {
1030  lockSymbolSize = rect().width() / 3;
1031  }
1032  if ( lockSymbolSize > ( rect().height() / 3 ) )
1033  {
1034  lockSymbolSize = rect().height() / 3;
1035  }
1036  return lockSymbolSize;
1037 }
1038 
1039 void QgsComposerItem::setRotation( const double r )
1040 {
1041  //kept for api compatibility with QGIS 2.0
1042  //remove after 2.0 series
1043  setItemRotation( r, true );
1044 }
1045 
1046 void QgsComposerItem::setItemRotation( const double r, const bool adjustPosition )
1047 {
1048  if ( r >= 360 )
1049  {
1050  mItemRotation = (( int )r ) % 360;
1051  }
1052  else
1053  {
1054  mItemRotation = r;
1055  }
1056 
1057  refreshRotation( true, adjustPosition );
1058 }
1059 
1060 void QgsComposerItem::refreshRotation( const bool updateItem, const bool adjustPosition )
1061 {
1062  double rotation = mItemRotation;
1063 
1064  //data defined rotation set?
1065  QVariant exprVal;
1067  {
1068  bool ok;
1069  double rotD = exprVal.toDouble( &ok );
1070  QgsDebugMsg( QString( "exprVal Rotation:%1" ).arg( rotD ) );
1071  if ( ok && !exprVal.isNull() )
1072  {
1073  rotation = rotD;
1074  }
1075  }
1076 
1077  if ( rotation == mEvaluatedItemRotation )
1078  {
1079  return;
1080  }
1081 
1082  if ( adjustPosition )
1083  {
1084  //adjustPosition set, so shift the position of the item so that rotation occurs around item center
1085  //create a line from the centrepoint of the rect() to its origin, in scene coordinates
1086  QLineF refLine = QLineF( mapToScene( QPointF( rect().width() / 2.0, rect().height() / 2.0 ) ), mapToScene( QPointF( 0, 0 ) ) );
1087  //rotate this line by the current rotation angle
1088  refLine.setAngle( refLine.angle() - rotation + mEvaluatedItemRotation );
1089  //get new end point of line - this is the new item position
1090  QPointF rotatedReferencePoint = refLine.p2();
1091  setPos( rotatedReferencePoint );
1092  emit sizeChanged();
1093  }
1094 
1095  setTransformOriginPoint( 0, 0 );
1096  QGraphicsItem::setRotation( rotation );
1097 
1099 
1100  emit itemRotationChanged( rotation );
1101 
1102  //update bounds of scene, since rotation may affect this
1104 
1105  if ( updateItem )
1106  {
1107  update();
1108  }
1109 }
1110 
1111 bool QgsComposerItem::imageSizeConsideringRotation( double& width, double& height ) const
1112 {
1113  //kept for api compatibility with QGIS 2.0, use item rotation
1115  return imageSizeConsideringRotation( width, height, mEvaluatedItemRotation );
1117 }
1118 
1119 bool QgsComposerItem::imageSizeConsideringRotation( double& width, double& height, double rotation ) const
1120 {
1121  if ( qAbs( rotation ) <= 0.0 ) //width and height stays the same if there is no rotation
1122  {
1123  return true;
1124  }
1125 
1126  if ( qgsDoubleNear( qAbs( rotation ), 90 ) || qgsDoubleNear( qAbs( rotation ), 270 ) )
1127  {
1128  double tmp = width;
1129  width = height;
1130  height = tmp;
1131  return true;
1132  }
1133 
1134  double x1 = 0;
1135  double y1 = 0;
1136  double x2 = width;
1137  double y2 = 0;
1138  double x3 = width;
1139  double y3 = height;
1140  double x4 = 0;
1141  double y4 = height;
1142  double midX = width / 2.0;
1143  double midY = height / 2.0;
1144 
1146  if ( !cornerPointOnRotatedAndScaledRect( x1, y1, width, height, rotation ) )
1147  {
1148  return false;
1149  }
1150  if ( !cornerPointOnRotatedAndScaledRect( x2, y2, width, height, rotation ) )
1151  {
1152  return false;
1153  }
1154  if ( !cornerPointOnRotatedAndScaledRect( x3, y3, width, height, rotation ) )
1155  {
1156  return false;
1157  }
1158  if ( !cornerPointOnRotatedAndScaledRect( x4, y4, width, height, rotation ) )
1159  {
1160  return false;
1161  }
1163 
1164 
1165  //assume points 1 and 3 are on the rectangle boundaries. Calculate 2 and 4.
1166  double distM1 = sqrt(( x1 - midX ) * ( x1 - midX ) + ( y1 - midY ) * ( y1 - midY ) );
1167  QPointF p2 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x2, y2 ), distM1 );
1168 
1169  if ( p2.x() < width && p2.x() > 0 && p2.y() < height && p2.y() > 0 )
1170  {
1171  width = sqrt(( p2.x() - x1 ) * ( p2.x() - x1 ) + ( p2.y() - y1 ) * ( p2.y() - y1 ) );
1172  height = sqrt(( x3 - p2.x() ) * ( x3 - p2.x() ) + ( y3 - p2.y() ) * ( y3 - p2.y() ) );
1173  return true;
1174  }
1175 
1176  //else assume that points 2 and 4 are on the rectangle boundaries. Calculate 1 and 3
1177  double distM2 = sqrt(( x2 - midX ) * ( x2 - midX ) + ( y2 - midY ) * ( y2 - midY ) );
1178  QPointF p1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x1, y1 ), distM2 );
1179  QPointF p3 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x3, y3 ), distM2 );
1180  width = sqrt(( x2 - p1.x() ) * ( x2 - p1.x() ) + ( y2 - p1.y() ) * ( y2 - p1.y() ) );
1181  height = sqrt(( p3.x() - x2 ) * ( p3.x() - x2 ) + ( p3.y() - y2 ) * ( p3.y() - y2 ) );
1182  return true;
1183 }
1184 
1185 QRectF QgsComposerItem::largestRotatedRectWithinBounds( QRectF originalRect, QRectF boundsRect, double rotation ) const
1186 {
1187  return QgsComposerUtils::largestRotatedRectWithinBounds( originalRect, boundsRect, rotation );
1188 }
1189 
1190 bool QgsComposerItem::cornerPointOnRotatedAndScaledRect( double& x, double& y, double width, double height ) const
1191 {
1192  //kept for api compatibility with QGIS 2.0, use item rotation
1194  return cornerPointOnRotatedAndScaledRect( x, y, width, height, mEvaluatedItemRotation );
1196 }
1197 
1198 bool QgsComposerItem::cornerPointOnRotatedAndScaledRect( double& x, double& y, double width, double height, double rotation ) const
1199 {
1200  //first rotate point clockwise
1201  double rotToRad = rotation * M_PI / 180.0;
1202  QPointF midpoint( width / 2.0, height / 2.0 );
1203  double xVector = x - midpoint.x();
1204  double yVector = y - midpoint.y();
1205  //double xRotated = cos(rotToRad) * xVector + sin(rotToRad) * yVector;
1206  //double yRotated = -sin(rotToRad) * xVector + cos(rotToRad) * yVector;
1207  double xRotated = cos( rotToRad ) * xVector - sin( rotToRad ) * yVector;
1208  double yRotated = sin( rotToRad ) * xVector + cos( rotToRad ) * yVector;
1209 
1210  //create line from midpoint to rotated point
1211  QLineF line( midpoint.x(), midpoint.y(), midpoint.x() + xRotated, midpoint.y() + yRotated );
1212 
1213  //intersect with all four borders and return result
1214  QList<QLineF> borders;
1215  borders << QLineF( 0, 0, width, 0 );
1216  borders << QLineF( width, 0, width, height );
1217  borders << QLineF( width, height, 0, height );
1218  borders << QLineF( 0, height, 0, 0 );
1219 
1220  QList<QLineF>::const_iterator it = borders.constBegin();
1221  QPointF intersectionPoint;
1222 
1223  for ( ; it != borders.constEnd(); ++it )
1224  {
1225  if ( line.intersect( *it, &intersectionPoint ) == QLineF::BoundedIntersection )
1226  {
1227  x = intersectionPoint.x();
1228  y = intersectionPoint.y();
1229  return true;
1230  }
1231  }
1232  return false;
1233 }
1234 
1235 void QgsComposerItem::sizeChangedByRotation( double& width, double& height )
1236 {
1237  //kept for api compatibility with QGIS 2.0, use item rotation
1239  return sizeChangedByRotation( width, height, mEvaluatedItemRotation );
1241 }
1242 
1243 void QgsComposerItem::sizeChangedByRotation( double& width, double& height, double rotation )
1244 {
1245  if ( rotation == 0.0 )
1246  {
1247  return;
1248  }
1249 
1250  //vector to p1
1251  double x1 = -width / 2.0;
1252  double y1 = -height / 2.0;
1253  QgsComposerUtils::rotate( rotation, x1, y1 );
1254  //vector to p2
1255  double x2 = width / 2.0;
1256  double y2 = -height / 2.0;
1257  QgsComposerUtils::rotate( rotation, x2, y2 );
1258  //vector to p3
1259  double x3 = width / 2.0;
1260  double y3 = height / 2.0;
1261  QgsComposerUtils::rotate( rotation, x3, y3 );
1262  //vector to p4
1263  double x4 = -width / 2.0;
1264  double y4 = height / 2.0;
1265  QgsComposerUtils::rotate( rotation, x4, y4 );
1266 
1267  //double midpoint
1268  QPointF midpoint( width / 2.0, height / 2.0 );
1269 
1270  QPolygonF rotatedRectPoly;
1271  rotatedRectPoly << QPointF( midpoint.x() + x1, midpoint.y() + y1 );
1272  rotatedRectPoly << QPointF( midpoint.x() + x2, midpoint.y() + y2 );
1273  rotatedRectPoly << QPointF( midpoint.x() + x3, midpoint.y() + y3 );
1274  rotatedRectPoly << QPointF( midpoint.x() + x4, midpoint.y() + y4 );
1275  QRectF boundingRect = rotatedRectPoly.boundingRect();
1276  width = boundingRect.width();
1277  height = boundingRect.height();
1278 }
1279 
1280 void QgsComposerItem::rotate( double angle, double& x, double& y ) const
1281 {
1282  QgsComposerUtils::rotate( angle, x, y );
1283 }
1284 
1286 {
1287  if ( !mHAlignSnapItem )
1288  {
1289  mHAlignSnapItem = new QGraphicsLineItem( 0 );
1290  mHAlignSnapItem->setPen( QPen( QColor( Qt::red ) ) );
1291  scene()->addItem( mHAlignSnapItem );
1292  mHAlignSnapItem->setZValue( 90 );
1293  }
1294  return mHAlignSnapItem;
1295 }
1296 
1298 {
1299  if ( !mVAlignSnapItem )
1300  {
1301  mVAlignSnapItem = new QGraphicsLineItem( 0 );
1302  mVAlignSnapItem->setPen( QPen( QColor( Qt::red ) ) );
1303  scene()->addItem( mVAlignSnapItem );
1304  mVAlignSnapItem->setZValue( 90 );
1305  }
1306  return mVAlignSnapItem;
1307 }
1308 
1310 {
1311  if ( mHAlignSnapItem )
1312  {
1313  scene()->removeItem( mHAlignSnapItem );
1314  delete mHAlignSnapItem;
1315  mHAlignSnapItem = 0;
1316  }
1317 }
1318 
1320 {
1321  if ( mVAlignSnapItem )
1322  {
1323  scene()->removeItem( mVAlignSnapItem );
1324  delete mVAlignSnapItem;
1325  mVAlignSnapItem = 0;
1326  }
1327 }
1328 
1330 {
1333 }
1334 
1336 {
1337  updateItem();
1338 }
1339 
1341 {
1342  //update data defined properties and redraw item to match
1343  if ( property == QgsComposerObject::PositionX || property == QgsComposerObject::PositionY ||
1344  property == QgsComposerObject::ItemWidth || property == QgsComposerObject::ItemHeight ||
1345  property == QgsComposerObject::AllProperties )
1346  {
1347  QRectF beforeRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
1348  QRectF evaluatedRect = evalItemRect( beforeRect );
1349  if ( evaluatedRect != beforeRect )
1350  {
1351  setSceneRect( evaluatedRect );
1352  }
1353  }
1354  if ( property == QgsComposerObject::ItemRotation || property == QgsComposerObject::AllProperties )
1355  {
1356  refreshRotation( false, true );
1357  }
1358  if ( property == QgsComposerObject::Transparency || property == QgsComposerObject::AllProperties )
1359  {
1360  refreshTransparency( false );
1361  }
1362  if ( property == QgsComposerObject::BlendMode || property == QgsComposerObject::AllProperties )
1363  {
1364  refreshBlendMode();
1365  }
1367  {
1368  bool exclude = mExcludeFromExports;
1369  //data defined exclude from exports set?
1370  QVariant exprVal;
1371  if ( dataDefinedEvaluate( QgsComposerObject::ExcludeFromExports, exprVal ) && !exprVal.isNull() )
1372  {
1373  exclude = exprVal.toBool();
1374  }
1375  mEvaluatedExcludeFromExports = exclude;
1376  }
1377 
1378  update();
1379 }
1380 
1381 void QgsComposerItem::setId( const QString& id )
1382 {
1383  if ( id == mId )
1384  {
1385  return;
1386  }
1387 
1388  setToolTip( id );
1389  mId = id;
1390 
1391  //inform model that id data has changed
1392  if ( mComposition )
1393  {
1395  }
1396 
1397  emit itemChanged();
1398 }
1399 
1400 void QgsComposerItem::setIsGroupMember( const bool isGroupMember )
1401 {
1403  setFlag( QGraphicsItem::ItemIsSelectable, !isGroupMember ); //item in groups cannot be selected
1404 }
1405 
1407 {
1408  //return id, if it's not empty
1409  if ( ! id().isEmpty() )
1410  {
1411  return id();
1412  }
1413 
1414  //for unnamed items, default to item type
1415  //(note some item types override this method to provide their own defaults)
1416  switch ( type() )
1417  {
1418  case ComposerArrow:
1419  return tr( "<arrow>" );
1420  case ComposerItemGroup:
1421  return tr( "<group>" );
1422  case ComposerLabel:
1423  return tr( "<label>" );
1424  case ComposerLegend:
1425  return tr( "<legend>" );
1426  case ComposerMap:
1427  return tr( "<map>" );
1428  case ComposerPicture:
1429  return tr( "<picture>" );
1430  case ComposerScaleBar:
1431  return tr( "<scale bar>" );
1432  case ComposerShape:
1433  return tr( "<shape>" );
1434  case ComposerTable:
1435  return tr( "<table>" );
1437  return tr( "<attribute table>" );
1438  case ComposerTextTable:
1439  return tr( "<text table>" );
1440  case ComposerFrame:
1441  return tr( "<frame>" );
1442  }
1443 
1444  return tr( "<item>" );
1445 }
1446 
1447 void QgsComposerItem::setVisibility( const bool visible )
1448 {
1449  if ( visible == isVisible() )
1450  {
1451  //nothing to do
1452  return;
1453  }
1454 
1455  QGraphicsItem::setVisible( visible );
1456 
1457  //inform model that id data has changed
1458  if ( mComposition )
1459  {
1461  }
1462 }
1463 
1465 {
1467 }
1468 
1469 void QgsComposerItem::setExcludeFromExports( const bool exclude )
1470 {
1471  mExcludeFromExports = exclude;
1473 }