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