QGIS API Documentation  2.2.0-Valmiera
 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 
34 #include <limits>
35 #include "qgsapplication.h"
36 #include "qgsrectangle.h" //just for debugging
37 #include "qgslogger.h"
38 #include "qgssymbollayerv2utils.h" //for pointOnLineWithDistance
39 #include "qgsmaprenderer.h" //for getCompositionMode
40 
41 #include <cmath>
42 
43 #define FONT_WORKAROUND_SCALE 10 //scale factor for upscaling fontsize and downscaling painter
44 
45 #ifndef M_DEG2RAD
46 #define M_DEG2RAD 0.0174532925
47 #endif
48 
49 QgsComposerItem::QgsComposerItem( QgsComposition* composition, bool manageZValue )
50  : QObject( 0 )
51  , QGraphicsRectItem( 0 )
52  , mComposition( composition )
53  , mBoundingResizeRectangle( 0 )
54  , mHAlignSnapItem( 0 )
55  , mVAlignSnapItem( 0 )
56  , mFrame( false )
57  , mBackground( true )
58  , mBackgroundColor( QColor( 255, 255, 255, 255 ) )
59  , mItemPositionLocked( false )
60  , mLastValidViewScaleFactor( -1 )
61  , mItemRotation( 0 )
62  , mBlendMode( QPainter::CompositionMode_SourceOver )
63  , mEffectsEnabled( true )
64  , mTransparency( 0 )
65  , mLastUsedPositionMode( UpperLeft )
66  , mId( "" )
67  , mUuid( QUuid::createUuid().toString() )
68 {
69  init( manageZValue );
70 }
71 
72 QgsComposerItem::QgsComposerItem( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition, bool manageZValue )
73  : QObject( 0 )
74  , QGraphicsRectItem( 0, 0, width, height, 0 )
75  , mComposition( composition )
76  , mBoundingResizeRectangle( 0 )
77  , mHAlignSnapItem( 0 )
78  , mVAlignSnapItem( 0 )
79  , mFrame( false )
80  , mBackground( true )
81  , mBackgroundColor( QColor( 255, 255, 255, 255 ) )
82  , mItemPositionLocked( false )
83  , mLastValidViewScaleFactor( -1 )
84  , mItemRotation( 0 )
85  , mBlendMode( QPainter::CompositionMode_SourceOver )
86  , mEffectsEnabled( true )
87  , mTransparency( 0 )
88  , mLastUsedPositionMode( UpperLeft )
89  , mId( "" )
90  , mUuid( QUuid::createUuid().toString() )
91 {
92  init( manageZValue );
93  setPos( x, y );
94 }
95 
96 void QgsComposerItem::init( bool manageZValue )
97 {
98  setFlag( QGraphicsItem::ItemIsSelectable, true );
99  //set default pen and brush
100  setBrush( QBrush( QColor( 255, 255, 255, 255 ) ) );
101  QPen defaultPen( QColor( 0, 0, 0 ) );
102  defaultPen.setWidthF( 0.3 );
103  defaultPen.setJoinStyle( Qt::MiterJoin );
104  setPen( defaultPen );
105  //let z-Value be managed by composition
106  if ( mComposition && manageZValue )
107  {
108  mComposition->addItemToZList( this );
109  }
110 
111  // Setup composer effect
112  mEffect = new QgsComposerEffect();
113  setGraphicsEffect( mEffect );
114 }
115 
117 {
118  if ( mComposition )
119  {
121  }
122 
124  delete mEffect;
126 }
127 
129 {
130  QgsDebugMsg( "entered." );
131  QGraphicsRectItem::setSelected( s );
132  update(); //to draw selection boxes
133 }
134 
135 bool QgsComposerItem::writeSettings( void ) { return true; }
136 
137 bool QgsComposerItem::readSettings( void ) { return true; }
138 
139 bool QgsComposerItem::removeSettings( void ) { return true; }
140 
141 bool QgsComposerItem::_writeXML( QDomElement& itemElem, QDomDocument& doc ) const
142 {
143  if ( itemElem.isNull() )
144  {
145  return false;
146  }
147 
148  QDomElement composerItemElem = doc.createElement( "ComposerItem" );
149 
150  //frame
151  if ( mFrame )
152  {
153  composerItemElem.setAttribute( "frame", "true" );
154  }
155  else
156  {
157  composerItemElem.setAttribute( "frame", "false" );
158  }
159 
160  //frame
161  if ( mBackground )
162  {
163  composerItemElem.setAttribute( "background", "true" );
164  }
165  else
166  {
167  composerItemElem.setAttribute( "background", "false" );
168  }
169 
170  //scene rect
171  composerItemElem.setAttribute( "x", QString::number( pos().x() ) );
172  composerItemElem.setAttribute( "y", QString::number( pos().y() ) );
173  composerItemElem.setAttribute( "width", QString::number( rect().width() ) );
174  composerItemElem.setAttribute( "height", QString::number( rect().height() ) );
175  composerItemElem.setAttribute( "positionMode", QString::number(( int ) mLastUsedPositionMode ) );
176  composerItemElem.setAttribute( "zValue", QString::number( zValue() ) );
177  composerItemElem.setAttribute( "outlineWidth", QString::number( pen().widthF() ) );
178  composerItemElem.setAttribute( "itemRotation", QString::number( mItemRotation ) );
179  composerItemElem.setAttribute( "uuid", mUuid );
180  composerItemElem.setAttribute( "id", mId );
181  //position lock for mouse moves/resizes
182  if ( mItemPositionLocked )
183  {
184  composerItemElem.setAttribute( "positionLock", "true" );
185  }
186  else
187  {
188  composerItemElem.setAttribute( "positionLock", "false" );
189  }
190 
191  composerItemElem.setAttribute( "lastValidViewScaleFactor", QString::number( mLastValidViewScaleFactor ) );
192 
193 
194  //frame color
195  QDomElement frameColorElem = doc.createElement( "FrameColor" );
196  QColor frameColor = pen().color();
197  frameColorElem.setAttribute( "red", QString::number( frameColor.red() ) );
198  frameColorElem.setAttribute( "green", QString::number( frameColor.green() ) );
199  frameColorElem.setAttribute( "blue", QString::number( frameColor.blue() ) );
200  frameColorElem.setAttribute( "alpha", QString::number( frameColor.alpha() ) );
201  composerItemElem.appendChild( frameColorElem );
202 
203  //background color
204  QDomElement bgColorElem = doc.createElement( "BackgroundColor" );
205  QColor bgColor = brush().color();
206  bgColorElem.setAttribute( "red", QString::number( bgColor.red() ) );
207  bgColorElem.setAttribute( "green", QString::number( bgColor.green() ) );
208  bgColorElem.setAttribute( "blue", QString::number( bgColor.blue() ) );
209  bgColorElem.setAttribute( "alpha", QString::number( bgColor.alpha() ) );
210  composerItemElem.appendChild( bgColorElem );
211 
212  //blend mode
213  composerItemElem.setAttribute( "blendMode", QgsMapRenderer::getBlendModeEnum( mBlendMode ) );
214 
215  //transparency
216  composerItemElem.setAttribute( "transparency", QString::number( mTransparency ) );
217 
218  itemElem.appendChild( composerItemElem );
219 
220  return true;
221 }
222 
223 bool QgsComposerItem::_readXML( const QDomElement& itemElem, const QDomDocument& doc )
224 {
225  Q_UNUSED( doc );
226  if ( itemElem.isNull() )
227  {
228  return false;
229  }
230 
231  //rotation
232  if ( itemElem.attribute( "itemRotation", "0" ).toDouble() != 0 )
233  {
234  setItemRotation( itemElem.attribute( "itemRotation", "0" ).toDouble() );
235  }
236 
237  //uuid
238  mUuid = itemElem.attribute( "uuid", QUuid::createUuid().toString() );
239 
240  // temporary for groups imported from templates
241  mTemplateUuid = itemElem.attribute( "templateUuid" );
242 
243  //id
244  QString id = itemElem.attribute( "id", "" );
245  setId( id );
246 
247  //frame
248  QString frame = itemElem.attribute( "frame" );
249  if ( frame.compare( "true", Qt::CaseInsensitive ) == 0 )
250  {
251  mFrame = true;
252  }
253  else
254  {
255  mFrame = false;
256  }
257 
258  //frame
259  QString background = itemElem.attribute( "background" );
260  if ( background.compare( "true", Qt::CaseInsensitive ) == 0 )
261  {
262  mBackground = true;
263  }
264  else
265  {
266  mBackground = false;
267  }
268 
269  //position lock for mouse moves/resizes
270  QString positionLock = itemElem.attribute( "positionLock" );
271  if ( positionLock.compare( "true", Qt::CaseInsensitive ) == 0 )
272  {
273  setPositionLock( true );
274  }
275  else
276  {
277  setPositionLock( false );
278  }
279 
280  //position
281  double x, y, width, height;
282  bool xOk, yOk, widthOk, heightOk, positionModeOK;
283 
284  x = itemElem.attribute( "x" ).toDouble( &xOk );
285  y = itemElem.attribute( "y" ).toDouble( &yOk );
286  width = itemElem.attribute( "width" ).toDouble( &widthOk );
287  height = itemElem.attribute( "height" ).toDouble( &heightOk );
288  mLastUsedPositionMode = ( ItemPositionMode )itemElem.attribute( "positionMode" ).toInt( &positionModeOK );
289  if ( !positionModeOK )
290  {
292  }
293 
294  if ( !xOk || !yOk || !widthOk || !heightOk )
295  {
296  return false;
297  }
298 
299  mLastValidViewScaleFactor = itemElem.attribute( "lastValidViewScaleFactor", "-1" ).toDouble();
300 
301  setSceneRect( QRectF( x, y, width, height ) );
302  setZValue( itemElem.attribute( "zValue" ).toDouble() );
303 
304  //pen
305  QDomNodeList frameColorList = itemElem.elementsByTagName( "FrameColor" );
306  if ( frameColorList.size() > 0 )
307  {
308  QDomElement frameColorElem = frameColorList.at( 0 ).toElement();
309  bool redOk, greenOk, blueOk, alphaOk, widthOk;
310  int penRed, penGreen, penBlue, penAlpha;
311  double penWidth;
312 
313  penWidth = itemElem.attribute( "outlineWidth" ).toDouble( &widthOk );
314  penRed = frameColorElem.attribute( "red" ).toDouble( &redOk );
315  penGreen = frameColorElem.attribute( "green" ).toDouble( &greenOk );
316  penBlue = frameColorElem.attribute( "blue" ).toDouble( &blueOk );
317  penAlpha = frameColorElem.attribute( "alpha" ).toDouble( &alphaOk );
318  if ( redOk && greenOk && blueOk && alphaOk && widthOk )
319  {
320  QPen framePen( QColor( penRed, penGreen, penBlue, penAlpha ) );
321  framePen.setWidthF( penWidth );
322  framePen.setJoinStyle( Qt::MiterJoin );
323  setPen( framePen );
324  }
325  }
326 
327  //brush
328  QDomNodeList bgColorList = itemElem.elementsByTagName( "BackgroundColor" );
329  if ( bgColorList.size() > 0 )
330  {
331  QDomElement bgColorElem = bgColorList.at( 0 ).toElement();
332  bool redOk, greenOk, blueOk, alphaOk;
333  int bgRed, bgGreen, bgBlue, bgAlpha;
334  bgRed = bgColorElem.attribute( "red" ).toDouble( &redOk );
335  bgGreen = bgColorElem.attribute( "green" ).toDouble( &greenOk );
336  bgBlue = bgColorElem.attribute( "blue" ).toDouble( &blueOk );
337  bgAlpha = bgColorElem.attribute( "alpha" ).toDouble( &alphaOk );
338  if ( redOk && greenOk && blueOk && alphaOk )
339  {
340  QColor brushColor( bgRed, bgGreen, bgBlue, bgAlpha );
341  setBackgroundColor( brushColor );
342  }
343  }
344 
345  //blend mode
346  setBlendMode( QgsMapRenderer::getCompositionMode(( QgsMapRenderer::BlendMode ) itemElem.attribute( "blendMode", "0" ).toUInt() ) );
347 
348  //transparency
349  setTransparency( itemElem.attribute( "transparency" , "0" ).toInt() );
350 
351  return true;
352 }
353 
354 void QgsComposerItem::setFrameEnabled( bool drawFrame )
355 {
356  mFrame = drawFrame;
357  emit frameChanged();
358 }
359 
361 {
362  QPen itemPen = pen();
363  if ( itemPen.widthF() == outlineWidth )
364  {
365  //no change
366  return;
367  }
368  itemPen.setWidthF( outlineWidth );
369  setPen( itemPen );
370  emit frameChanged();
371 }
372 
374 {
375  if ( !hasFrame() )
376  {
377  return 0;
378  }
379 
380  return pen().widthF() / 2.0;
381 }
382 
384 {
385  double frameBleed = estimatedFrameBleed();
386  return rect().adjusted( -frameBleed, -frameBleed, frameBleed, frameBleed );
387 }
388 
390 {
391  if ( mComposition )
392  {
393  mComposition->beginCommand( this, commandText, c );
394  }
395 }
396 
398 {
399  if ( mComposition )
400  {
402  }
403 }
404 
406 {
407  if ( mComposition )
408  {
410  }
411 }
412 
414 {
415 
416  if ( !mComposition )
417  {
418  return;
419  }
420 
422  {
423  double sizeLockSymbol = lockSymbolSize();
424 
425  if ( mItemPositionLocked )
426  {
427  //draw lock symbol at upper left edge. Use QImage to be independent of the graphic system
428  QString lockIconPath = QgsApplication::activeThemePath() + "/mIconLock.png";
429  if ( !QFile::exists( lockIconPath ) )
430  {
431  lockIconPath = QgsApplication::defaultThemePath() + "/mIconLock.png";
432  }
433 
434  QImage lockImage( lockIconPath );
435  if ( !lockImage.isNull() )
436  {
437  p->drawImage( QRectF( 0, 0, sizeLockSymbol, sizeLockSymbol ), lockImage, QRectF( 0, 0, lockImage.width(), lockImage.height() ) );
438  }
439  }
440  }
441 }
442 
443 void QgsComposerItem::drawFrame( QPainter* p )
444 {
445  if ( mFrame && p )
446  {
447  p->setPen( pen() );
448  p->setBrush( Qt::NoBrush );
449  p->setRenderHint( QPainter::Antialiasing, true );
450  p->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) );
451  }
452 }
453 
455 {
456  mItemPositionLocked = lock;
457 }
458 
459 void QgsComposerItem::move( double dx, double dy )
460 {
461  QRectF newSceneRect( pos().x() + dx, pos().y() + dy, rect().width(), rect().height() );
462  setSceneRect( newSceneRect );
463 }
464 
465 void QgsComposerItem::setItemPosition( double x, double y, ItemPositionMode itemPoint )
466 {
467  double width = rect().width();
468  double height = rect().height();
469  setItemPosition( x, y, width, height, itemPoint );
470 }
471 
472 void QgsComposerItem::setItemPosition( double x, double y, double width, double height, ItemPositionMode itemPoint, bool posIncludesFrame )
473 {
474  double upperLeftX = x;
475  double upperLeftY = y;
476 
477  //store the item position mode
478  mLastUsedPositionMode = itemPoint;
479 
480  //adjust x-coordinate if placement is not done to a left point
481  if ( itemPoint == UpperMiddle || itemPoint == Middle || itemPoint == LowerMiddle )
482  {
483  upperLeftX -= width / 2.0;
484  }
485  else if ( itemPoint == UpperRight || itemPoint == MiddleRight || itemPoint == LowerRight )
486  {
487  upperLeftX -= width;
488  }
489 
490  //adjust y-coordinate if placement is not done to an upper point
491  if ( itemPoint == MiddleLeft || itemPoint == Middle || itemPoint == MiddleRight )
492  {
493  upperLeftY -= height / 2.0;
494  }
495  else if ( itemPoint == LowerLeft || itemPoint == LowerMiddle || itemPoint == LowerRight )
496  {
497  upperLeftY -= height;
498  }
499 
500  if ( posIncludesFrame )
501  {
502  //adjust position to account for frame size
503 
504  if ( mItemRotation == 0 )
505  {
506  upperLeftX += estimatedFrameBleed();
507  upperLeftY += estimatedFrameBleed();
508  }
509  else
510  {
511  //adjust position for item rotation
512  QLineF lineToItemOrigin = QLineF( 0, 0, estimatedFrameBleed(), estimatedFrameBleed() );
513  lineToItemOrigin.setAngle( -45 - mItemRotation );
514  upperLeftX += lineToItemOrigin.x2();
515  upperLeftY += lineToItemOrigin.y2();
516  }
517 
518  width -= 2 * estimatedFrameBleed();
519  height -= 2 * estimatedFrameBleed();
520  }
521 
522  setSceneRect( QRectF( upperLeftX, upperLeftY, width, height ) );
523 }
524 
525 void QgsComposerItem::setSceneRect( const QRectF& rectangle )
526 {
527  //setRect in item coordinates
528  double newWidth = rectangle.width();
529  double newHeight = rectangle.height();
530  double xTranslation = rectangle.x();
531  double yTranslation = rectangle.y();
532 
533  //correction if width and/or height are negative
534  if ( rectangle.width() < 0 )
535  {
536  newWidth = - rectangle.width();
537  xTranslation -= newWidth;
538  }
539 
540  if ( rectangle.height() < 0 )
541  {
542  newHeight = - rectangle.height();
543  yTranslation -= newHeight;
544  }
545 
546  QRectF newRect( 0, 0, newWidth, newHeight );
547  QGraphicsRectItem::setRect( newRect );
548  setPos( xTranslation, yTranslation );
549 
550  emit sizeChanged();
551 }
552 
554 {
555  if ( mBackground && p )
556  {
557  p->setBrush( brush() );//this causes a problem in atlas generation
558  p->setPen( Qt::NoPen );
559  p->setRenderHint( QPainter::Antialiasing, true );
560  p->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) );
561  }
562 }
563 
564 void QgsComposerItem::setBackgroundColor( const QColor& backgroundColor )
565 {
567  setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
568 }
569 
570 void QgsComposerItem::setBlendMode( QPainter::CompositionMode blendMode )
571 {
573  // Update the composer effect to use the new blend mode
575 }
576 
577 void QgsComposerItem::setTransparency( int transparency )
578 {
580  // Set the QGraphicItem's opacity
581  setOpacity( 1. - ( transparency / 100. ) );
582 }
583 
584 void QgsComposerItem::setEffectsEnabled( bool effectsEnabled )
585 {
586  //enable or disable the QgsComposerEffect applied to this item
588  mEffect->setEnabled( effectsEnabled );
589 }
590 
591 void QgsComposerItem::drawText( QPainter* p, double x, double y, const QString& text, const QFont& font ) const
592 {
593  QFont textFont = scaledFontPixelSize( font );
594 
595  p->save();
596  p->setFont( textFont );
597  double scaleFactor = 1.0 / FONT_WORKAROUND_SCALE;
598  p->scale( scaleFactor, scaleFactor );
599  p->drawText( QPointF( x * FONT_WORKAROUND_SCALE, y * FONT_WORKAROUND_SCALE ), text );
600  p->restore();
601 }
602 
603 void QgsComposerItem::drawText( QPainter* p, const QRectF& rect, const QString& text, const QFont& font, Qt::AlignmentFlag halignment, Qt::AlignmentFlag valignment ) const
604 {
605  QFont textFont = scaledFontPixelSize( font );
606 
607  QRectF scaledRect( rect.x() * FONT_WORKAROUND_SCALE, rect.y() * FONT_WORKAROUND_SCALE,
608  rect.width() * FONT_WORKAROUND_SCALE, rect.height() * FONT_WORKAROUND_SCALE );
609 
610  p->save();
611  p->setFont( textFont );
612  double scaleFactor = 1.0 / FONT_WORKAROUND_SCALE;
613  p->scale( scaleFactor, scaleFactor );
614  p->drawText( scaledRect, halignment | valignment | Qt::TextWordWrap, text );
615  p->restore();
616 }
617 void QgsComposerItem::drawArrowHead( QPainter* p, double x, double y, double angle, double arrowHeadWidth ) const
618 {
619  if ( !p )
620  {
621  return;
622  }
623  double angleRad = angle / 180.0 * M_PI;
624  QPointF middlePoint( x, y );
625  //rotate both arrow points
626  QPointF p1 = QPointF( -arrowHeadWidth / 2.0, arrowHeadWidth );
627  QPointF p2 = QPointF( arrowHeadWidth / 2.0, arrowHeadWidth );
628 
629  QPointF p1Rotated, p2Rotated;
630  p1Rotated.setX( p1.x() * cos( angleRad ) + p1.y() * -sin( angleRad ) );
631  p1Rotated.setY( p1.x() * sin( angleRad ) + p1.y() * cos( angleRad ) );
632  p2Rotated.setX( p2.x() * cos( angleRad ) + p2.y() * -sin( angleRad ) );
633  p2Rotated.setY( p2.x() * sin( angleRad ) + p2.y() * cos( angleRad ) );
634 
635  QPolygonF arrowHeadPoly;
636  arrowHeadPoly << middlePoint;
637  arrowHeadPoly << QPointF( middlePoint.x() + p1Rotated.x(), middlePoint.y() + p1Rotated.y() );
638  arrowHeadPoly << QPointF( middlePoint.x() + p2Rotated.x(), middlePoint.y() + p2Rotated.y() );
639 
640  p->save();
641 
642  QPen arrowPen = p->pen();
643  arrowPen.setJoinStyle( Qt::RoundJoin );
644  QBrush arrowBrush = p->brush();
645  arrowBrush.setStyle( Qt::SolidPattern );
646  p->setPen( arrowPen );
647  p->setBrush( arrowBrush );
648  arrowBrush.setStyle( Qt::SolidPattern );
649  p->drawPolygon( arrowHeadPoly );
650 
651  p->restore();
652 }
653 
654 double QgsComposerItem::textWidthMillimeters( const QFont& font, const QString& text ) const
655 {
656  QFont metricsFont = scaledFontPixelSize( font );
657  QFontMetricsF fontMetrics( metricsFont );
658  return ( fontMetrics.width( text ) / FONT_WORKAROUND_SCALE );
659 }
660 
661 double QgsComposerItem::fontHeightCharacterMM( const QFont& font, const QChar& c ) const
662 {
663  QFont metricsFont = scaledFontPixelSize( font );
664  QFontMetricsF fontMetrics( metricsFont );
665  return ( fontMetrics.boundingRect( c ).height() / FONT_WORKAROUND_SCALE );
666 }
667 
668 double QgsComposerItem::fontAscentMillimeters( const QFont& font ) const
669 {
670  QFont metricsFont = scaledFontPixelSize( font );
671  QFontMetricsF fontMetrics( metricsFont );
672  return ( fontMetrics.ascent() / FONT_WORKAROUND_SCALE );
673 }
674 
675 double QgsComposerItem::fontDescentMillimeters( const QFont& font ) const
676 {
677  QFont metricsFont = scaledFontPixelSize( font );
678  QFontMetricsF fontMetrics( metricsFont );
679  return ( fontMetrics.descent() / FONT_WORKAROUND_SCALE );
680 }
681 
682 double QgsComposerItem::pixelFontSize( double pointSize ) const
683 {
684  return ( pointSize * 0.3527 );
685 }
686 
687 QFont QgsComposerItem::scaledFontPixelSize( const QFont& font ) const
688 {
689  QFont scaledFont = font;
690  double pixelSize = pixelFontSize( font.pointSizeF() ) * FONT_WORKAROUND_SCALE + 0.5;
691  scaledFont.setPixelSize( pixelSize );
692  return scaledFont;
693 }
694 
695 double QgsComposerItem::angle( const QPointF& p1, const QPointF& p2 ) const
696 {
697  double xDiff = p2.x() - p1.x();
698  double yDiff = p2.y() - p1.y();
699  double length = sqrt( xDiff * xDiff + yDiff * yDiff );
700  if ( length <= 0 )
701  {
702  return 0;
703  }
704 
705  double angle = acos(( -yDiff * length ) / ( length * length ) ) * 180 / M_PI;
706  if ( xDiff < 0 )
707  {
708  return ( 360 - angle );
709  }
710  return angle;
711 }
712 
714 {
715  double result = -1;
716  if ( scene() )
717  {
718  QList<QGraphicsView*> viewList = scene()->views();
719  if ( viewList.size() > 0 ) //if not, probably this function was called from non-gui code
720  {
721  QGraphicsView* currentView = viewList.at( 0 );
722  if ( currentView->isVisible() )
723  {
724  result = currentView->transform().m11();
725  mLastValidViewScaleFactor = result;
726  }
727  }
728  }
729  return result;
730 }
731 
733 {
734  //size of symbol boxes depends on zoom level in composer view
735  double viewScaleFactor = horizontalViewScaleFactor();
736  double rectHandlerSize = 10.0 / viewScaleFactor;
737 
738  //make sure the boxes don't get too large
739  if ( rectHandlerSize > ( rect().width() / 3 ) )
740  {
741  rectHandlerSize = rect().width() / 3;
742  }
743  if ( rectHandlerSize > ( rect().height() / 3 ) )
744  {
745  rectHandlerSize = rect().height() / 3;
746  }
747  return rectHandlerSize;
748 }
749 
751 {
752  double lockSymbolSize = 20.0 / horizontalViewScaleFactor();
753 
754  if ( lockSymbolSize > ( rect().width() / 3 ) )
755  {
756  lockSymbolSize = rect().width() / 3;
757  }
758  if ( lockSymbolSize > ( rect().height() / 3 ) )
759  {
760  lockSymbolSize = rect().height() / 3;
761  }
762  return lockSymbolSize;
763 }
764 
766 {
767  //kept for api compatibility with QGIS 2.0
768  //remove after 2.0 series
769  setItemRotation( r, true );
770 }
771 
772 void QgsComposerItem::setItemRotation( double r, bool adjustPosition )
773 {
774  if ( adjustPosition )
775  {
776  //adjustPosition set, so shift the position of the item so that rotation occurs around item center
777  //create a line from the centrepoint of the rect() to its origin, in scene coordinates
778  QLineF refLine = QLineF( mapToScene( QPointF( rect().width() / 2.0, rect().height() / 2.0 ) ) , mapToScene( QPointF( 0 , 0 ) ) );
779  //rotate this line by the current rotation angle
780  refLine.setAngle( refLine.angle() - r + mItemRotation );
781  //get new end point of line - this is the new item position
782  QPointF rotatedReferencePoint = refLine.p2();
783  setPos( rotatedReferencePoint );
784  emit sizeChanged();
785  }
786 
787  if ( r > 360 )
788  {
789  mItemRotation = (( int )r ) % 360;
790  }
791  else
792  {
793  mItemRotation = r;
794  }
795 
796  setTransformOriginPoint( 0, 0 );
797  QGraphicsItem::setRotation( mItemRotation );
798 
799  emit itemRotationChanged( r );
800  update();
801 }
802 
803 bool QgsComposerItem::imageSizeConsideringRotation( double& width, double& height ) const
804 {
805  //kept for api compatibility with QGIS 2.0, use item rotation
806  return imageSizeConsideringRotation( width, height, mItemRotation );
807 }
808 
809 QRectF QgsComposerItem::largestRotatedRectWithinBounds( QRectF originalRect, QRectF boundsRect, double rotation ) const
810 {
811  double originalWidth = originalRect.width();
812  double originalHeight = originalRect.height();
813  double boundsWidth = boundsRect.width();
814  double boundsHeight = boundsRect.height();
815  double ratioBoundsRect = boundsWidth / boundsHeight;
816 
817  //shortcut for some rotation values
818  if ( rotation == 0 || rotation == 90 || rotation == 180 || rotation == 270 )
819  {
820  double originalRatio = originalWidth / originalHeight;
821  double rectScale = originalRatio > ratioBoundsRect ? boundsWidth / originalWidth : boundsHeight / originalHeight;
822  double rectScaledWidth = rectScale * originalWidth;
823  double rectScaledHeight = rectScale * originalHeight;
824 
825  if ( rotation == 0 || rotation == 180 )
826  {
827  return QRectF(( boundsWidth - rectScaledWidth ) / 2.0, ( boundsHeight - rectScaledHeight ) / 2.0, rectScaledWidth, rectScaledHeight );
828  }
829  else
830  {
831  return QRectF(( boundsWidth - rectScaledHeight ) / 2.0, ( boundsHeight - rectScaledWidth ) / 2.0, rectScaledHeight, rectScaledWidth );
832  }
833  }
834 
835  //convert angle to radians and flip
836  double angleRad = -rotation * M_DEG2RAD;
837  double cosAngle = cos( angleRad );
838  double sinAngle = sin( angleRad );
839 
840  //calculate size of bounds of rotated rectangle
841  double widthBoundsRotatedRect = originalWidth * fabs( cosAngle ) + originalHeight * fabs( sinAngle );
842  double heightBoundsRotatedRect = originalHeight * fabs( cosAngle ) + originalWidth * fabs( sinAngle );
843 
844  //compare ratio of rotated rect with bounds rect and calculate scaling of rotated
845  //rect to fit within bounds
846  double ratioBoundsRotatedRect = widthBoundsRotatedRect / heightBoundsRotatedRect;
847  double rectScale = ratioBoundsRotatedRect > ratioBoundsRect ? boundsWidth / widthBoundsRotatedRect : boundsHeight / heightBoundsRotatedRect;
848  double rectScaledWidth = rectScale * originalWidth;
849  double rectScaledHeight = rectScale * originalHeight;
850 
851  //now calculate offset so that rotated rectangle is centered within bounds
852  //first calculate min x and y coordinates
853  double currentCornerX = 0;
854  double minX = 0;
855  currentCornerX += rectScaledWidth * cosAngle;
856  minX = minX < currentCornerX ? minX : currentCornerX;
857  currentCornerX += rectScaledHeight * sinAngle;
858  minX = minX < currentCornerX ? minX : currentCornerX;
859  currentCornerX -= rectScaledWidth * cosAngle;
860  minX = minX < currentCornerX ? minX : currentCornerX;
861 
862  double currentCornerY = 0;
863  double minY = 0;
864  currentCornerY -= rectScaledWidth * sinAngle;
865  minY = minY < currentCornerY ? minY : currentCornerY;
866  currentCornerY += rectScaledHeight * cosAngle;
867  minY = minY < currentCornerY ? minY : currentCornerY;
868  currentCornerY += rectScaledWidth * sinAngle;
869  minY = minY < currentCornerY ? minY : currentCornerY;
870 
871  //now calculate offset position of rotated rectangle
872  double offsetX = ratioBoundsRotatedRect > ratioBoundsRect ? 0 : ( boundsWidth - rectScale * widthBoundsRotatedRect ) / 2.0;
873  offsetX += fabs( minX );
874  double offsetY = ratioBoundsRotatedRect > ratioBoundsRect ? ( boundsHeight - rectScale * heightBoundsRotatedRect ) / 2.0 : 0;
875  offsetY += fabs( minY );
876 
877  return QRectF( offsetX, offsetY, rectScaledWidth, rectScaledHeight );
878 }
879 
880 bool QgsComposerItem::imageSizeConsideringRotation( double& width, double& height, double rotation ) const
881 {
882  if ( qAbs( rotation ) <= 0.0 ) //width and height stays the same if there is no rotation
883  {
884  return true;
885  }
886 
887  if ( qgsDoubleNear( qAbs( rotation ), 90 ) || qgsDoubleNear( qAbs( rotation ), 270 ) )
888  {
889  double tmp = width;
890  width = height;
891  height = tmp;
892  return true;
893  }
894 
895  double x1 = 0;
896  double y1 = 0;
897  double x2 = width;
898  double y2 = 0;
899  double x3 = width;
900  double y3 = height;
901  double x4 = 0;
902  double y4 = height;
903  double midX = width / 2.0;
904  double midY = height / 2.0;
905 
906  if ( !cornerPointOnRotatedAndScaledRect( x1, y1, width, height, rotation ) )
907  {
908  return false;
909  }
910  if ( !cornerPointOnRotatedAndScaledRect( x2, y2, width, height, rotation ) )
911  {
912  return false;
913  }
914  if ( !cornerPointOnRotatedAndScaledRect( x3, y3, width, height, rotation ) )
915  {
916  return false;
917  }
918  if ( !cornerPointOnRotatedAndScaledRect( x4, y4, width, height, rotation ) )
919  {
920  return false;
921  }
922 
923 
924  //assume points 1 and 3 are on the rectangle boundaries. Calculate 2 and 4.
925  double distM1 = sqrt(( x1 - midX ) * ( x1 - midX ) + ( y1 - midY ) * ( y1 - midY ) );
926  QPointF p2 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x2, y2 ), distM1 );
927 
928  if ( p2.x() < width && p2.x() > 0 && p2.y() < height && p2.y() > 0 )
929  {
930  width = sqrt(( p2.x() - x1 ) * ( p2.x() - x1 ) + ( p2.y() - y1 ) * ( p2.y() - y1 ) );
931  height = sqrt(( x3 - p2.x() ) * ( x3 - p2.x() ) + ( y3 - p2.y() ) * ( y3 - p2.y() ) );
932  return true;
933  }
934 
935  //else assume that points 2 and 4 are on the rectangle boundaries. Calculate 1 and 3
936  double distM2 = sqrt(( x2 - midX ) * ( x2 - midX ) + ( y2 - midY ) * ( y2 - midY ) );
937  QPointF p1 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x1, y1 ), distM2 );
938  QPointF p3 = QgsSymbolLayerV2Utils::pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x3, y3 ), distM2 );
939  width = sqrt(( x2 - p1.x() ) * ( x2 - p1.x() ) + ( y2 - p1.y() ) * ( y2 - p1.y() ) );
940  height = sqrt(( p3.x() - x2 ) * ( p3.x() - x2 ) + ( p3.y() - y2 ) * ( p3.y() - y2 ) );
941  return true;
942 }
943 
944 bool QgsComposerItem::cornerPointOnRotatedAndScaledRect( double& x, double& y, double width, double height ) const
945 {
946  //kept for api compatibility with QGIS 2.0, use item rotation
947  return cornerPointOnRotatedAndScaledRect( x, y, width, height, mItemRotation );
948 }
949 
950 bool QgsComposerItem::cornerPointOnRotatedAndScaledRect( double& x, double& y, double width, double height, double rotation ) const
951 {
952  //first rotate point clockwise
953  double rotToRad = rotation * M_PI / 180.0;
954  QPointF midpoint( width / 2.0, height / 2.0 );
955  double xVector = x - midpoint.x();
956  double yVector = y - midpoint.y();
957  //double xRotated = cos(rotToRad) * xVector + sin(rotToRad) * yVector;
958  //double yRotated = -sin(rotToRad) * xVector + cos(rotToRad) * yVector;
959  double xRotated = cos( rotToRad ) * xVector - sin( rotToRad ) * yVector;
960  double yRotated = sin( rotToRad ) * xVector + cos( rotToRad ) * yVector;
961 
962  //create line from midpoint to rotated point
963  QLineF line( midpoint.x(), midpoint.y(), midpoint.x() + xRotated, midpoint.y() + yRotated );
964 
965  //intersect with all four borders and return result
966  QList<QLineF> borders;
967  borders << QLineF( 0, 0, width, 0 );
968  borders << QLineF( width, 0, width, height );
969  borders << QLineF( width, height, 0, height );
970  borders << QLineF( 0, height, 0, 0 );
971 
972  QList<QLineF>::const_iterator it = borders.constBegin();
973  QPointF intersectionPoint;
974 
975  for ( ; it != borders.constEnd(); ++it )
976  {
977  if ( line.intersect( *it, &intersectionPoint ) == QLineF::BoundedIntersection )
978  {
979  x = intersectionPoint.x();
980  y = intersectionPoint.y();
981  return true;
982  }
983  }
984  return false;
985 }
986 
987 void QgsComposerItem::sizeChangedByRotation( double& width, double& height )
988 {
989  //kept for api compatibility with QGIS 2.0, use item rotation
990  return sizeChangedByRotation( width, height, mItemRotation );
991 }
992 
993 void QgsComposerItem::sizeChangedByRotation( double& width, double& height, double rotation )
994 {
995  if ( rotation == 0.0 )
996  {
997  return;
998  }
999 
1000  //vector to p1
1001  double x1 = -width / 2.0;
1002  double y1 = -height / 2.0;
1003  rotate( rotation, x1, y1 );
1004  //vector to p2
1005  double x2 = width / 2.0;
1006  double y2 = -height / 2.0;
1007  rotate( rotation, x2, y2 );
1008  //vector to p3
1009  double x3 = width / 2.0;
1010  double y3 = height / 2.0;
1011  rotate( rotation, x3, y3 );
1012  //vector to p4
1013  double x4 = -width / 2.0;
1014  double y4 = height / 2.0;
1015  rotate( rotation, x4, y4 );
1016 
1017  //double midpoint
1018  QPointF midpoint( width / 2.0, height / 2.0 );
1019 
1020  QPolygonF rotatedRectPoly;
1021  rotatedRectPoly << QPointF( midpoint.x() + x1, midpoint.y() + y1 );
1022  rotatedRectPoly << QPointF( midpoint.x() + x2, midpoint.y() + y2 );
1023  rotatedRectPoly << QPointF( midpoint.x() + x3, midpoint.y() + y3 );
1024  rotatedRectPoly << QPointF( midpoint.x() + x4, midpoint.y() + y4 );
1025  QRectF boundingRect = rotatedRectPoly.boundingRect();
1026  width = boundingRect.width();
1027  height = boundingRect.height();
1028 }
1029 
1030 void QgsComposerItem::rotate( double angle, double& x, double& y ) const
1031 {
1032  double rotToRad = angle * M_PI / 180.0;
1033  double xRot, yRot;
1034  xRot = x * cos( rotToRad ) - y * sin( rotToRad );
1035  yRot = x * sin( rotToRad ) + y * cos( rotToRad );
1036  x = xRot;
1037  y = yRot;
1038 }
1039 
1041 {
1042  if ( !mHAlignSnapItem )
1043  {
1044  mHAlignSnapItem = new QGraphicsLineItem( 0 );
1045  mHAlignSnapItem->setPen( QPen( QColor( Qt::red ) ) );
1046  scene()->addItem( mHAlignSnapItem );
1047  mHAlignSnapItem->setZValue( 90 );
1048  }
1049  return mHAlignSnapItem;
1050 }
1051 
1053 {
1054  if ( !mVAlignSnapItem )
1055  {
1056  mVAlignSnapItem = new QGraphicsLineItem( 0 );
1057  mVAlignSnapItem->setPen( QPen( QColor( Qt::red ) ) );
1058  scene()->addItem( mVAlignSnapItem );
1059  mVAlignSnapItem->setZValue( 90 );
1060  }
1061  return mVAlignSnapItem;
1062 }
1063 
1065 {
1066  if ( mHAlignSnapItem )
1067  {
1068  scene()->removeItem( mHAlignSnapItem );
1069  delete mHAlignSnapItem;
1070  mHAlignSnapItem = 0;
1071  }
1072 }
1073 
1075 {
1076  if ( mVAlignSnapItem )
1077  {
1078  scene()->removeItem( mVAlignSnapItem );
1079  delete mVAlignSnapItem;
1080  mVAlignSnapItem = 0;
1081  }
1082 }
1083 
1085 {
1088 }
1089 
1091 {
1092  update();
1093 }
1094 
1095 void QgsComposerItem::setId( const QString& id )
1096 {
1097  setToolTip( id );
1098  mId = id;
1099 }
bool positionLock() const
Returns position lock for mouse drags (true means locked)
bool effectsEnabled() const
Returns true if effects (eg blend modes) are enabled for the item.
bool imageSizeConsideringRotation(double &width, double &height, double rotation) const
Calculates width and hight of the picture (in mm) such that it fits into the item frame with the give...
double outlineWidth
Definition: qgssvgcache.cpp:78
int mTransparency
Item transparency.
void setEffectsEnabled(bool effectsEnabled)
Sets whether effects (eg blend modes) are enabled for the item.
double fontHeightCharacterMM(const QFont &font, const QChar &c) const
Returns the font height of a character in millimeters.
void itemRotationChanged(double newRotation)
Is emitted on item rotation change.
static const QString activeThemePath()
Returns the path to the currently active theme directory.
double pixelFontSize(double pointSize) const
Calculates font to from point size to pixel size.
void addItemToZList(QgsComposerItem *item)
Adds item to z list.
virtual void setRotation(double r)
Sets the item rotation.
virtual double estimatedFrameBleed() const
Returns the estimated amount the item's frame bleeds outside the item's actual rectangle.
virtual bool removeSettings()
delete settings from project file
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
double lockSymbolSize() const
Returns the size of the lock symbol depending on the composer zoom level and the item size...
void removeItemFromZList(QgsComposerItem *item)
Removes item from z list.
#define FONT_WORKAROUND_SCALE
double mLastValidViewScaleFactor
Backup to restore item appearance if no view scale factor is available.
ItemPositionMode mLastUsedPositionMode
The item's position mode.
virtual void setSelected(bool s)
Set selected, selected item should be highlighted.
void setItemPosition(double x, double y, ItemPositionMode itemPoint=UpperLeft)
Moves the item to a new position (in canvas coordinates)
virtual void drawFrame(QPainter *p)
Draw black frame around item.
QColor backgroundColor() const
Gets the background color for this item.
QPainter::CompositionMode mBlendMode
Composition blend mode for item.
void setCompositionMode(const QPainter::CompositionMode &compositionMode)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:305
BlendMode
Blending modes enum defining the available composition modes that can be used when rendering a layer...
bool _readXML(const QDomElement &itemElem, const QDomDocument &doc)
Reads parameter that are not subclass specific in document.
double fontDescentMillimeters(const QFont &font) const
Returns the font ascent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCAL...
void setTransparency(int transparency)
Sets the item's transparency.
void frameChanged()
Emitted if the item's frame style changes.
double horizontalViewScaleFactor() const
Returns the zoom factor of the graphics view.
void cancelCommand()
Deletes current command.
QFont scaledFontPixelSize(const QFont &font) const
Returns a font where size is in pixel and font size is upscaled with FONT_WORKAROUND_SCALE.
int transparency() const
Returns the item's transparency.
virtual void drawSelectionBoxes(QPainter *p)
Draw selection boxes around item.
double textWidthMillimeters(const QFont &font, const QString &text) const
Returns the font width in millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCALE...
void rotate(double angle, double &x, double &y) const
Rotates a point / vector.
void endCommand()
Saves end state of item and pushes command to the undo history.
QGraphicsRectItem * mBoundingResizeRectangle
Rectangle used during move and resize actions.
QRectF largestRotatedRectWithinBounds(QRectF originalRect, QRectF boundsRect, double rotation) const
Calculates the largest scaled version of originalRect which fits within boundsRect, when it is rotated by a specified amount.
bool mFrame
True if item fram needs to be painted.
void endCommand()
Finish current command and push it onto the undo stack.
virtual void setFrameOutlineWidth(double outlineWidth)
Sets frame outline width.
#define M_PI
static const QString defaultThemePath()
Returns the path to the default theme directory.
virtual bool writeSettings()
stores state in project
virtual ~QgsComposerItem()
void drawText(QPainter *p, double x, double y, const QString &text, const QFont &font) const
Draws Text.
void beginCommand(const QString &commandText, QgsComposerMergeCommand::Context c=QgsComposerMergeCommand::Unknown)
Starts new composer undo command.
QgsComposition * mComposition
Graphics scene for map printing.
virtual void setItemRotation(double r, bool adjustPosition=false)
Sets the item rotation.
double ANALYSIS_EXPORT angle(Point3D *p1, Point3D *p2, Point3D *p3, Point3D *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
void setPositionLock(bool lock)
Locks / unlocks the item position for mouse drags.
virtual QRectF rectWithFrame() const
Returns the item's rectangular bounds, including any bleed caused by the item's frame.
QGraphicsLineItem * hAlignSnapItem()
Return horizontal align snap item.
static QPainter::CompositionMode getCompositionMode(const QgsMapRenderer::BlendMode &blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode Added in 1.9.
QColor mBackgroundColor
Background color.
QGraphicsLineItem * mVAlignSnapItem
bool cornerPointOnRotatedAndScaledRect(double &x, double &y, double width, double height, double rotation) const
Calculates corner point after rotation and scaling.
QGraphicsLineItem * mHAlignSnapItem
virtual bool readSettings()
read state from project
void setBackgroundColor(const QColor &backgroundColor)
Sets the background color for this item.
bool _writeXML(QDomElement &itemElem, QDomDocument &doc) const
Writes parameter that are not subclass specific in document.
bool mItemPositionLocked
True if item position and size cannot be changed with mouse move.
QPainter::CompositionMode blendMode() const
Returns the item's composition blending mode.
virtual void drawBackground(QPainter *p)
Draw background.
bool hasFrame() const
Whether this item has a frame or not.
virtual void setId(const QString &id)
Set item's id (which is not necessarly unique)
virtual void setSceneRect(const QRectF &rectangle)
Sets this items bound in scene coordinates such that 1 item size units corresponds to 1 scene size un...
double angle(const QPointF &p1, const QPointF &p2) const
Returns angle of the line from p1 to p2 (clockwise, starting at N)
void sizeChanged()
Emitted if the rectangle changes.
void setBlendMode(QPainter::CompositionMode blendMode)
Sets the item's composition blending mode.
static QgsMapRenderer::BlendMode getBlendModeEnum(const QPainter::CompositionMode &blendMode)
Returns a BlendMode corresponding to a QPainter::CompositionMode Added in 1.9.
QgsComposerEffect * mEffect
void drawArrowHead(QPainter *p, double x, double y, double angle, double arrowHeadWidth) const
Draws arrowhead.
QgsComposerItem(QgsComposition *composition, bool manageZValue=true)
Constructor.
void init(bool manageZValue)
QgsComposition::PlotStyle plotStyle() const
double rectHandlerBorderTolerance() const
Returns the current (zoom level dependent) tolerance to decide if mouse position is close enough to t...
#define M_DEG2RAD
void setFrameEnabled(bool drawFrame)
Set whether this item has a frame drawn around it or not.
static QPointF pointOnLineWithDistance(const QPointF &startPoint, const QPointF &directionPoint, double distance)
Returns a point on the line from startPoint to directionPoint that is a certain distance away from th...
bool mBackground
True if item background needs to be painted.
void move(double dx, double dy)
Moves item in canvas coordinates.
double mItemRotation
Item rotation in degrees, clockwise.
QGraphicsLineItem * vAlignSnapItem()
Return vertical align snap item.
double fontAscentMillimeters(const QFont &font) const
Returns the font ascent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCAL...
void sizeChangedByRotation(double &width, double &height, double rotation)
Calculates width / height of the bounding box of a rotated rectangle.
void beginCommand(QgsComposerItem *item, const QString &commandText, QgsComposerMergeCommand::Context c=QgsComposerMergeCommand::Unknown)
Allocates new item command and saves initial state in it.
QString id() const
Get item's id (which is not necessarly unique)