Quantum GIS API Documentation  1.7.5-Wroclaw
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator 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 <QGraphicsScene>
21 #include <QGraphicsSceneMouseEvent>
22 #include <QGraphicsView>
23 #include <QPainter>
24 
25 #include "qgscomposition.h"
26 #include "qgscomposeritem.h"
27 
28 
29 #include <limits>
30 #include "qgsapplication.h"
31 #include "qgsrectangle.h" //just for debugging
32 #include "qgslogger.h"
33 
34 #include <cmath>
35 
36 #define FONT_WORKAROUND_SCALE 10 //scale factor for upscaling fontsize and downscaling painter
37 
38 QgsComposerItem::QgsComposerItem( QgsComposition* composition, bool manageZValue )
39  : QObject( 0 )
40  , QGraphicsRectItem( 0 )
41  , mComposition( composition )
42  , mBoundingResizeRectangle( 0 )
43  , mFrame( true )
44  , mItemPositionLocked( false )
45  , mLastValidViewScaleFactor( -1 )
46  , mRotation( 0 )
47 {
48  setFlag( QGraphicsItem::ItemIsSelectable, true );
49  setAcceptsHoverEvents( true );
50 
51  //set default pen and brush
52  setBrush( QBrush( QColor( 255, 255, 255, 255 ) ) );
53  QPen defaultPen( QColor( 0, 0, 0 ) );
54  defaultPen.setWidthF( 0.3 );
55  setPen( defaultPen );
56 
57  //let z-Value be managed by composition
58  if ( mComposition && manageZValue )
59  {
61  }
62 }
63 
64 QgsComposerItem::QgsComposerItem( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition, bool manageZValue )
65  : QObject( 0 )
66  , QGraphicsRectItem( 0, 0, width, height, 0 )
67  , mComposition( composition )
68  , mBoundingResizeRectangle( 0 )
69  , mFrame( true )
70  , mItemPositionLocked( false )
71  , mLastValidViewScaleFactor( -1 )
72  , mRotation( 0 )
73 {
74  setFlag( QGraphicsItem::ItemIsSelectable, true );
75  setAcceptsHoverEvents( true );
76 
77  QTransform t;
78  t.translate( x, y );
79  setTransform( t );
80 
81  //set default pen and brush
82  setBrush( QBrush( QColor( 255, 255, 255, 255 ) ) );
83  QPen defaultPen( QColor( 0, 0, 0 ) );
84  defaultPen.setWidthF( 0.3 );
85  setPen( defaultPen );
86 
87 //let z-Value be managed by composition
88  if ( mComposition && manageZValue )
89  {
91  }
92 }
93 
95 {
96  if ( mComposition )
97  {
99  }
100 
102 }
103 
105 {
106  QgsDebugMsg( "entered." );
108  update(); //to draw selection boxes
109 }
110 
111 bool QgsComposerItem::writeSettings( void ) { return true; }
112 
113 bool QgsComposerItem::readSettings( void ) { return true; }
114 
115 bool QgsComposerItem::removeSettings( void ) { return true; }
116 
117 bool QgsComposerItem::_writeXML( QDomElement& itemElem, QDomDocument& doc ) const
118 {
119  if ( itemElem.isNull() )
120  {
121  return false;
122  }
123 
124  QDomElement composerItemElem = doc.createElement( "ComposerItem" );
125 
126  //frame
127  if ( mFrame )
128  {
129  composerItemElem.setAttribute( "frame", "true" );
130  }
131  else
132  {
133  composerItemElem.setAttribute( "frame", "false" );
134  }
135 
136  //scene rect
137  composerItemElem.setAttribute( "x", transform().dx() );
138  composerItemElem.setAttribute( "y", transform().dy() );
139  composerItemElem.setAttribute( "width", rect().width() );
140  composerItemElem.setAttribute( "height", rect().height() );
141  composerItemElem.setAttribute( "zValue", QString::number( zValue() ) );
142  composerItemElem.setAttribute( "outlineWidth", QString::number( pen().widthF() ) );
143  composerItemElem.setAttribute( "rotation", mRotation );
144 
145  //position lock for mouse moves/resizes
146  if ( mItemPositionLocked )
147  {
148  composerItemElem.setAttribute( "positionLock", "true" );
149  }
150  else
151  {
152  composerItemElem.setAttribute( "positionLock", "false" );
153  }
154 
155  composerItemElem.setAttribute( "lastValidViewScaleFactor", mLastValidViewScaleFactor );
156 
157 
158  //frame color
159  QDomElement frameColorElem = doc.createElement( "FrameColor" );
160  QColor frameColor = pen().color();
161  frameColorElem.setAttribute( "red", QString::number( frameColor.red() ) );
162  frameColorElem.setAttribute( "green", QString::number( frameColor.green() ) );
163  frameColorElem.setAttribute( "blue", QString::number( frameColor.blue() ) );
164  frameColorElem.setAttribute( "alpha", QString::number( frameColor.alpha() ) );
165  composerItemElem.appendChild( frameColorElem );
166 
167  //background color
168  QDomElement bgColorElem = doc.createElement( "BackgroundColor" );
169  QColor bgColor = brush().color();
170  bgColorElem.setAttribute( "red", QString::number( bgColor.red() ) );
171  bgColorElem.setAttribute( "green", QString::number( bgColor.green() ) );
172  bgColorElem.setAttribute( "blue", QString::number( bgColor.blue() ) );
173  bgColorElem.setAttribute( "alpha", QString::number( bgColor.alpha() ) );
174  composerItemElem.appendChild( bgColorElem );
175 
176  itemElem.appendChild( composerItemElem );
177 
178  return true;
179 }
180 
181 bool QgsComposerItem::_readXML( const QDomElement& itemElem, const QDomDocument& doc )
182 {
183  if ( itemElem.isNull() )
184  {
185  return false;
186  }
187 
188  //rotation
189  mRotation = itemElem.attribute( "rotation", "0" ).toDouble();
190 
191  //frame
192  QString frame = itemElem.attribute( "frame" );
193  if ( frame.compare( "true", Qt::CaseInsensitive ) == 0 )
194  {
195  mFrame = true;
196  }
197  else
198  {
199  mFrame = false;
200  }
201 
202  //position lock for mouse moves/resizes
203  QString positionLock = itemElem.attribute( "positionLock" );
204  if ( positionLock.compare( "true", Qt::CaseInsensitive ) == 0 )
205  {
206  mItemPositionLocked = true;
207  }
208  else
209  {
210  mItemPositionLocked = false;
211  }
212 
213  //position
214  double x, y, width, height;
215  bool xOk, yOk, widthOk, heightOk;
216 
217  x = itemElem.attribute( "x" ).toDouble( &xOk );
218  y = itemElem.attribute( "y" ).toDouble( &yOk );
219  width = itemElem.attribute( "width" ).toDouble( &widthOk );
220  height = itemElem.attribute( "height" ).toDouble( &heightOk );
221 
222  if ( !xOk || !yOk || !widthOk || !heightOk )
223  {
224  return false;
225  }
226 
227  mLastValidViewScaleFactor = itemElem.attribute( "lastValidViewScaleFactor", "-1" ).toDouble();
228 
229  setSceneRect( QRectF( x, y, width, height ) );
230  setZValue( itemElem.attribute( "zValue" ).toDouble() );
231 
232  //pen
233  QDomNodeList frameColorList = itemElem.elementsByTagName( "FrameColor" );
234  if ( frameColorList.size() > 0 )
235  {
236  QDomElement frameColorElem = frameColorList.at( 0 ).toElement();
237  bool redOk, greenOk, blueOk, alphaOk, widthOk;
238  int penRed, penGreen, penBlue, penAlpha;
239  double penWidth;
240 
241  penWidth = itemElem.attribute( "outlineWidth" ).toDouble( &widthOk );
242  penRed = frameColorElem.attribute( "red" ).toDouble( &redOk );
243  penGreen = frameColorElem.attribute( "green" ).toDouble( &greenOk );
244  penBlue = frameColorElem.attribute( "blue" ).toDouble( &blueOk );
245  penAlpha = frameColorElem.attribute( "alpha" ).toDouble( &alphaOk );
246  if ( redOk && greenOk && blueOk && alphaOk && widthOk )
247  {
248  QPen framePen( QColor( penRed, penGreen, penBlue, penAlpha ) );
249  framePen.setWidthF( penWidth );
250  setPen( framePen );
251  }
252  }
253 
254  //brush
255  QDomNodeList bgColorList = itemElem.elementsByTagName( "BackgroundColor" );
256  if ( bgColorList.size() > 0 )
257  {
258  QDomElement bgColorElem = bgColorList.at( 0 ).toElement();
259  bool redOk, greenOk, blueOk, alphaOk;
260  int bgRed, bgGreen, bgBlue, bgAlpha;
261  bgRed = bgColorElem.attribute( "red" ).toDouble( &redOk );
262  bgGreen = bgColorElem.attribute( "green" ).toDouble( &greenOk );
263  bgBlue = bgColorElem.attribute( "blue" ).toDouble( &blueOk );
264  bgAlpha = bgColorElem.attribute( "alpha" ).toDouble( &alphaOk );
265  if ( redOk && greenOk && blueOk && alphaOk )
266  {
267  QColor brushColor( bgRed, bgGreen, bgBlue, bgAlpha );
268  setBrush( QBrush( brushColor ) );
269  }
270  }
271  return true;
272 }
273 
275 {
276  if ( mComposition )
277  {
278  mComposition->beginCommand( this, commandText, c );
279  }
280 }
281 
283 {
284  if ( mComposition )
285  {
287  }
288 }
289 
291 {
292  if ( mComposition )
293  {
295  }
296 }
297 
298 void QgsComposerItem::mouseMoveEvent( QGraphicsSceneMouseEvent * event )
299 {
300  if ( mItemPositionLocked )
301  {
302  return;
303  }
304 
306  {
307  double diffX = event->lastScenePos().x() - mLastMouseEventPos.x();
308  double diffY = event->lastScenePos().y() - mLastMouseEventPos.y();
309 
310  changeItemRectangle( event->lastScenePos(), mMouseMoveStartPos, this, diffX, diffY, mBoundingResizeRectangle );
311  }
312  mLastMouseEventPos = event->lastScenePos();
313 }
314 
315 void QgsComposerItem::mousePressEvent( QGraphicsSceneMouseEvent * event )
316 {
317  if ( mItemPositionLocked )
318  {
319  return;
320  }
321 
322  //set current position and type of mouse move action
323  mMouseMoveStartPos = event->lastScenePos();
324  mLastMouseEventPos = event->lastScenePos();
326 
327  //remove the old rubber band item if it is still there
329  {
330  scene()->removeItem( mBoundingResizeRectangle );
333  }
334  //create and show bounding rectangle
335  mBoundingResizeRectangle = new QGraphicsRectItem( 0 );
336  scene()->addItem( mBoundingResizeRectangle );
337  mBoundingResizeRectangle->setRect( QRectF( 0, 0, rect().width(), rect().height() ) );
338  QTransform resizeTransform;
339  resizeTransform.translate( transform().dx(), transform().dy() );
340  mBoundingResizeRectangle->setTransform( resizeTransform );
341 
342  mBoundingResizeRectangle->setBrush( Qt::NoBrush );
343  mBoundingResizeRectangle->setPen( QPen( QColor( 0, 0, 0 ), 0 ) );
344  mBoundingResizeRectangle->setZValue( 90 );
345  mBoundingResizeRectangle->show();
346 }
347 
348 void QgsComposerItem::mouseReleaseEvent( QGraphicsSceneMouseEvent * event )
349 {
350 
351  if ( mItemPositionLocked )
352  {
353  return;
354  }
355 
356  //delete frame rectangle
358  {
359  scene()->removeItem( mBoundingResizeRectangle );
362  }
363 
364  QPointF mouseMoveStopPoint = event->lastScenePos();
365  double diffX = mouseMoveStopPoint.x() - mMouseMoveStartPos.x();
366  double diffY = mouseMoveStopPoint.y() - mMouseMoveStartPos.y();
367 
368  //it was only a click
369  if ( qAbs( diffX ) < std::numeric_limits<double>::min() && qAbs( diffY ) < std::numeric_limits<double>::min() )
370  {
371  return;
372  }
373 
374  beginCommand( tr( "Change item position" ) );
375  changeItemRectangle( mouseMoveStopPoint, mMouseMoveStartPos, this, diffX, diffY, this );
376  endCommand();
377 
378  //reset default action
380  setCursor( Qt::ArrowCursor );
381 }
382 
383 Qt::CursorShape QgsComposerItem::cursorForPosition( const QPointF& itemCoordPos )
384 {
385  QgsComposerItem::MouseMoveAction mouseAction = mouseMoveActionForPosition( itemCoordPos );
386 
387  if ( mouseAction == QgsComposerItem::NoAction )
388  {
389  return Qt::ForbiddenCursor;
390  }
391  if ( mouseAction == QgsComposerItem::MoveItem )
392  {
393  return Qt::ClosedHandCursor;
394  }
395  else if ( mouseAction == QgsComposerItem::ResizeLeftUp || mouseAction == QgsComposerItem::ResizeRightDown )
396  {
397  return Qt::SizeFDiagCursor;
398  }
399  else if ( mouseAction == QgsComposerItem::ResizeLeftDown || mouseAction == QgsComposerItem::ResizeRightUp )
400  {
401  return Qt::SizeBDiagCursor;
402  }
403  else if ( mouseAction == QgsComposerItem::ResizeUp || mouseAction == QgsComposerItem::ResizeDown )
404  {
405  return Qt::SizeVerCursor;
406  }
407  else //if(mouseAction == QgsComposerItem::ResizeLeft || mouseAction == QgsComposerItem::ResizeRight)
408  {
409  return Qt::SizeHorCursor;
410  }
411 }
412 
414 {
415 
416  //no action at all if item position is locked for mouse
417  if ( mItemPositionLocked )
418  {
420  }
421 
422  bool nearLeftBorder = false;
423  bool nearRightBorder = false;
424  bool nearLowerBorder = false;
425  bool nearUpperBorder = false;
426 
427  double borderTolerance = rectHandlerBorderTolerance();
428 
429  if ( itemCoordPos.x() < borderTolerance )
430  {
431  nearLeftBorder = true;
432  }
433  if ( itemCoordPos.y() < borderTolerance )
434  {
435  nearUpperBorder = true;
436  }
437  if ( itemCoordPos.x() > ( rect().width() - borderTolerance ) )
438  {
439  nearRightBorder = true;
440  }
441  if ( itemCoordPos.y() > ( rect().height() - borderTolerance ) )
442  {
443  nearLowerBorder = true;
444  }
445 
446  if ( nearLeftBorder && nearUpperBorder )
447  {
449  }
450  else if ( nearLeftBorder && nearLowerBorder )
451  {
453  }
454  else if ( nearRightBorder && nearUpperBorder )
455  {
457  }
458  else if ( nearRightBorder && nearLowerBorder )
459  {
461  }
462  else if ( nearLeftBorder )
463  {
465  }
466  else if ( nearRightBorder )
467  {
469  }
470  else if ( nearUpperBorder )
471  {
473  }
474  else if ( nearLowerBorder )
475  {
477  }
478 
479  return QgsComposerItem::MoveItem; //default
480 }
481 
482 void QgsComposerItem::changeItemRectangle( const QPointF& currentPosition, const QPointF& mouseMoveStartPos, const QGraphicsRectItem* originalItem, double dx, double dy, QGraphicsRectItem* changeItem )
483 {
484  if ( !changeItem || !originalItem || !mComposition )
485  {
486  return;
487  }
488 
489  //test if change item is a composer item. If so, prefer call to setSceneRect() instead of setTransform() and setRect()
490  QgsComposerItem* changeComposerItem = dynamic_cast<QgsComposerItem *>( changeItem );
491 
492  double mx = 0.0, my = 0.0, rx = 0.0, ry = 0.0;
493  QPointF snappedPosition = mComposition->snapPointToGrid( currentPosition );
494  //double diffX = snappedPosition.x() - mouseMoveStartPos.x();
495  //double diffY = snappedPosition.y() - mouseMoveStartPos.y();
496  double diffX = 0;
497  double diffY = 0;
498 
499  switch ( mCurrentMouseMoveAction )
500  {
501  //vertical resize
503  diffY = snappedPosition.y() - originalItem->transform().dy();
504  mx = 0; my = diffY; rx = 0; ry = -diffY;
505  break;
506 
508  diffY = snappedPosition.y() - ( originalItem->transform().dy() + originalItem->rect().height() );
509  mx = 0; my = 0; rx = 0; ry = diffY;
510  break;
511 
512  //horizontal resize
514  diffX = snappedPosition.x() - originalItem->transform().dx();
515  mx = diffX, my = 0; rx = -diffX; ry = 0;
516  break;
517 
519  diffX = snappedPosition.x() - ( originalItem->transform().dx() + originalItem->rect().width() );
520  mx = 0; my = 0; rx = diffX, ry = 0;
521  break;
522 
523  //diagonal resize
525  diffX = snappedPosition.x() - originalItem->transform().dx();
526  diffY = snappedPosition.y() - originalItem->transform().dy();
527  mx = diffX, my = diffY; rx = -diffX; ry = -diffY;
528  break;
529 
531  diffX = snappedPosition.x() - ( originalItem->transform().dx() + originalItem->rect().width() );
532  diffY = snappedPosition.y() - ( originalItem->transform().dy() + originalItem->rect().height() );
533  mx = 0; my = 0; rx = diffX, ry = diffY;
534  break;
535 
537  diffX = snappedPosition.x() - ( originalItem->transform().dx() + originalItem->rect().width() );
538  diffY = snappedPosition.y() - originalItem->transform().dy();
539  mx = 0; my = diffY, rx = diffX, ry = -diffY;
540  break;
541 
543  diffX = snappedPosition.x() - originalItem->transform().dx();
544  diffY = snappedPosition.y() - ( originalItem->transform().dy() + originalItem->rect().height() );
545  mx = diffX, my = 0; rx = -diffX; ry = diffY;
546  break;
547 
549  {
550  //calculate total move difference
551  double moveX = currentPosition.x() - mouseMoveStartPos.x();
552  double moveY = currentPosition.y() - mouseMoveStartPos.y();
553 
554  QPointF upperLeftPoint( originalItem->transform().dx() + moveX, originalItem->transform().dy() + moveY );
555  QPointF snappedLeftPoint = mComposition->snapPointToGrid( upperLeftPoint );
556 
557  double moveRectX = snappedLeftPoint.x() - originalItem->transform().dx();
558  double moveRectY = snappedLeftPoint.y() - originalItem->transform().dy();
559 
560  if ( !changeComposerItem )
561  {
562  QTransform moveTransform;
563  moveTransform.translate( originalItem->transform().dx() + moveRectX, originalItem->transform().dy() + moveRectY );
564  changeItem->setTransform( moveTransform );
565  }
566  else //for composer items, we prefer setSceneRect as subclasses can implement custom behaviour (e.g. item group)
567  {
568  changeComposerItem->setSceneRect( QRectF( originalItem->transform().dx() + moveRectX,
569  originalItem->transform().dy() + moveRectY,
570  originalItem->rect().width(), originalItem->rect().height() ) );
571  }
572  }
573  return;
575  break;
576  }
577 
578  if ( !changeComposerItem )
579  {
580  QTransform itemTransform;
581  itemTransform.translate( originalItem->transform().dx() + mx, originalItem->transform().dy() + my );
582  changeItem->setTransform( itemTransform );
583  QRectF itemRect( 0, 0, originalItem->rect().width() + rx, originalItem->rect().height() + ry );
584  changeItem->setRect( itemRect );
585  }
586  else //for composer items, we prefer setSceneRect as subclasses can implement custom behaviour (e.g. item group)
587  {
588  changeComposerItem->setSceneRect( QRectF( originalItem->transform().dx() + mx, originalItem->transform().dy() + my,
589  originalItem->rect().width() + rx, originalItem->rect().height() + ry ) );
590  }
591 }
592 
594 {
595  if ( !mComposition )
596  {
597  return;
598  }
599 
601  {
602  //size of symbol boxes depends on zoom level in composer view
603  double rectHandlerSize = rectHandlerBorderTolerance();
604  double sizeLockSymbol = lockSymbolSize();
605 
606  if ( mItemPositionLocked )
607  {
608  //draw lock symbol at upper left edge. Use QImage to be independent of the graphic system
609  QString lockIconPath = QgsApplication::activeThemePath() + "/mIconLock.png";
610  if ( !QFile::exists( lockIconPath ) )
611  {
612  lockIconPath = QgsApplication::defaultThemePath() + "/mIconLock.png";
613  }
614 
615  QImage lockImage( lockIconPath );
616  if ( !lockImage.isNull() )
617  {
618  p->drawImage( QRectF( 0, 0, sizeLockSymbol, sizeLockSymbol ), lockImage, QRectF( 0, 0, lockImage.width(), lockImage.height() ) );
619  }
620  }
621  else //draw blue squares
622  {
623  p->setPen( QColor( 50, 100, 120, 200 ) );
624  p->setBrush( QColor( 200, 200, 210, 120 ) );
625  p->drawRect( QRectF( 0, 0, rectHandlerSize, rectHandlerSize ) );
626  p->drawRect( QRectF( rect().width() - rectHandlerSize, 0, rectHandlerSize, rectHandlerSize ) );
627  p->drawRect( QRectF( rect().width() - rectHandlerSize, rect().height() - rectHandlerSize, rectHandlerSize, rectHandlerSize ) );
628  p->drawRect( QRectF( 0, rect().height() - rectHandlerSize, rectHandlerSize, rectHandlerSize ) );
629  }
630  }
631 }
632 
633 void QgsComposerItem::drawFrame( QPainter* p )
634 {
635  if ( mFrame && p )
636  {
637  p->setPen( pen() );
638  p->setBrush( Qt::NoBrush );
639  p->setRenderHint( QPainter::Antialiasing, true );
640  p->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) );
641  }
642 }
643 
644 void QgsComposerItem::move( double dx, double dy )
645 {
646  QTransform t = transform();
647  QRectF newSceneRect( t.dx() + dx, t.dy() + dy, rect().width(), rect().height() );
648  setSceneRect( newSceneRect );
649 }
650 
651 void QgsComposerItem::setItemPosition( double x, double y, ItemPositionMode itemPoint )
652 {
653  double width = rect().width();
654  double height = rect().height();
655  setItemPosition( x, y, width, height, itemPoint );
656 }
657 
658 void QgsComposerItem::setItemPosition( double x, double y, double width, double height, ItemPositionMode itemPoint )
659 {
660  double upperLeftX = x;
661  double upperLeftY = y;
662 
663  //adjust x-coordinate if placement is not done to a left point
664  if ( itemPoint == UpperMiddle || itemPoint == Middle || itemPoint == LowerMiddle )
665  {
666  upperLeftX -= width / 2.0;
667  }
668  else if ( itemPoint == UpperRight || itemPoint == MiddleRight || itemPoint == LowerRight )
669  {
670  upperLeftX -= width;
671  }
672 
673  //adjust y-coordinate if placement is not done to an upper point
674  if ( itemPoint == MiddleLeft || itemPoint == Middle || itemPoint == MiddleRight )
675  {
676  upperLeftY -= height / 2.0;
677  }
678  else if ( itemPoint == LowerLeft || itemPoint == LowerMiddle || itemPoint == LowerRight )
679  {
680  upperLeftY -= height;
681  }
682 
683  setSceneRect( QRectF( upperLeftX, upperLeftY, width, height ) );
684 }
685 
686 void QgsComposerItem::setSceneRect( const QRectF& rectangle )
687 {
688  //setRect in item coordinates
689  double newWidth = rectangle.width();
690  double newHeight = rectangle.height();
691  double xTranslation = rectangle.x();
692  double yTranslation = rectangle.y();
693 
694  //correction if width and/or height are negative
695  if ( rectangle.width() < 0 )
696  {
697  newWidth = - rectangle.width();
698  xTranslation -= newWidth;
699  }
700 
701  if ( rectangle.height() < 0 )
702  {
703  newHeight = - rectangle.height();
704  yTranslation -= newHeight;
705  }
706 
707  QRectF newRect( 0, 0, newWidth, newHeight );
708  QGraphicsRectItem::setRect( newRect );
709 
710  //set up transformation matrix for item coordinates
711  QTransform t;
712  t.translate( xTranslation, yTranslation );
713  setTransform( t );
714 }
715 
717 {
718  if ( p )
719  {
720  p->setBrush( brush() );
721  p->setPen( Qt::NoPen );
722  p->setRenderHint( QPainter::Antialiasing, true );
723  p->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) );
724  }
725 }
726 
727 void QgsComposerItem::hoverMoveEvent( QGraphicsSceneHoverEvent * event )
728 {
729  if ( isSelected() )
730  {
731  setCursor( cursorForPosition( event->pos() ) );
732  }
733 }
734 
735 void QgsComposerItem::drawText( QPainter* p, double x, double y, const QString& text, const QFont& font ) const
736 {
737  QFont textFont = scaledFontPixelSize( font );
738 
739  p->save();
740  p->setFont( textFont );
741  p->setPen( QColor( 0, 0, 0 ) ); //draw text always in black
742  double scaleFactor = 1.0 / FONT_WORKAROUND_SCALE;
743  p->scale( scaleFactor, scaleFactor );
744  p->drawText( QPointF( x * FONT_WORKAROUND_SCALE, y * FONT_WORKAROUND_SCALE ), text );
745  p->restore();
746 }
747 
748 void QgsComposerItem::drawText( QPainter* p, const QRectF& rect, const QString& text, const QFont& font, Qt::AlignmentFlag halignement, Qt::AlignmentFlag valignment ) const
749 {
750  QFont textFont = scaledFontPixelSize( font );
751 
752  QRectF scaledRect( rect.x() * FONT_WORKAROUND_SCALE, rect.y() * FONT_WORKAROUND_SCALE,
753  rect.width() * FONT_WORKAROUND_SCALE, rect.height() * FONT_WORKAROUND_SCALE );
754 
755  p->save();
756  p->setFont( textFont );
757  double scaleFactor = 1.0 / FONT_WORKAROUND_SCALE;
758  p->scale( scaleFactor, scaleFactor );
759  p->drawText( scaledRect, halignement | valignment | Qt::TextWordWrap, text );
760  p->restore();
761 }
762 void QgsComposerItem::drawArrowHead( QPainter* p, double x, double y, double angle, double arrowHeadWidth ) const
763 {
764  if ( !p )
765  {
766  return;
767  }
768  double angleRad = angle / 180.0 * M_PI;
769  QPointF middlePoint( x, y );
770  //rotate both arrow points
771  QPointF p1 = QPointF( -arrowHeadWidth / 2.0, arrowHeadWidth );
772  QPointF p2 = QPointF( arrowHeadWidth / 2.0, arrowHeadWidth );
773 
774  QPointF p1Rotated, p2Rotated;
775  p1Rotated.setX( p1.x() * cos( angleRad ) + p1.y() * -sin( angleRad ) );
776  p1Rotated.setY( p1.x() * sin( angleRad ) + p1.y() * cos( angleRad ) );
777  p2Rotated.setX( p2.x() * cos( angleRad ) + p2.y() * -sin( angleRad ) );
778  p2Rotated.setY( p2.x() * sin( angleRad ) + p2.y() * cos( angleRad ) );
779 
780  QPolygonF arrowHeadPoly;
781  arrowHeadPoly << middlePoint;
782  arrowHeadPoly << QPointF( middlePoint.x() + p1Rotated.x(), middlePoint.y() + p1Rotated.y() );
783  arrowHeadPoly << QPointF( middlePoint.x() + p2Rotated.x(), middlePoint.y() + p2Rotated.y() );
784 
785  p->save();
786 
787  QPen arrowPen = p->pen();
788  arrowPen.setJoinStyle( Qt::RoundJoin );
789  QBrush arrowBrush = p->brush();
790  arrowBrush.setStyle( Qt::SolidPattern );
791  p->setPen( arrowPen );
792  p->setBrush( arrowBrush );
793  arrowBrush.setStyle( Qt::SolidPattern );
794  p->drawPolygon( arrowHeadPoly );
795 
796  p->restore();
797 }
798 
799 double QgsComposerItem::textWidthMillimeters( const QFont& font, const QString& text ) const
800 {
801  QFont metricsFont = scaledFontPixelSize( font );
802  QFontMetrics fontMetrics( metricsFont );
803  return ( fontMetrics.width( text ) / FONT_WORKAROUND_SCALE );
804 }
805 
806 double QgsComposerItem::fontHeightCharacterMM( const QFont& font, const QChar& c ) const
807 {
808  QFont metricsFont = scaledFontPixelSize( font );
809  QFontMetricsF fontMetrics( metricsFont );
810  return ( fontMetrics.boundingRect( c ).height() / FONT_WORKAROUND_SCALE );
811 }
812 
813 double QgsComposerItem::fontAscentMillimeters( const QFont& font ) const
814 {
815  QFont metricsFont = scaledFontPixelSize( font );
816  QFontMetricsF fontMetrics( metricsFont );
817  return ( fontMetrics.ascent() / FONT_WORKAROUND_SCALE );
818 }
819 
820 double QgsComposerItem::pixelFontSize( double pointSize ) const
821 {
822  return ( pointSize * 0.3527 );
823 }
824 
825 QFont QgsComposerItem::scaledFontPixelSize( const QFont& font ) const
826 {
827  QFont scaledFont = font;
828  double pixelSize = pixelFontSize( font.pointSizeF() ) * FONT_WORKAROUND_SCALE + 0.5;
829  scaledFont.setPixelSize( pixelSize );
830  return scaledFont;
831 }
832 
833 double QgsComposerItem::angle( const QPointF& p1, const QPointF& p2 ) const
834 {
835  double xDiff = p2.x() - p1.x();
836  double yDiff = p2.y() - p1.y();
837  double length = sqrt( xDiff * xDiff + yDiff * yDiff );
838  if ( length <= 0 )
839  {
840  return 0;
841  }
842 
843  double angle = acos(( -yDiff * length ) / ( length * length ) ) * 180 / M_PI;
844  if ( xDiff < 0 )
845  {
846  return ( 360 - angle );
847  }
848  return angle;
849 }
850 
852 {
853  double result = -1;
854  if ( scene() )
855  {
856  QList<QGraphicsView*> viewList = scene()->views();
857  if ( viewList.size() > 0 ) //if not, probably this function was called from non-gui code
858  {
859  QGraphicsView* currentView = viewList.at( 0 );
860  if ( currentView->isVisible() )
861  {
862  result = currentView->transform().m11();
863  mLastValidViewScaleFactor = result;
864  }
865  }
866  }
867  return result;
868 }
869 
871 {
872  //size of symbol boxes depends on zoom level in composer view
873  double viewScaleFactor = horizontalViewScaleFactor();
874  double rectHandlerSize = 10.0 / viewScaleFactor;
875 
876  //make sure the boxes don't get too large
877  if ( rectHandlerSize > ( rect().width() / 3 ) )
878  {
879  rectHandlerSize = rect().width() / 3;
880  }
881  if ( rectHandlerSize > ( rect().height() / 3 ) )
882  {
883  rectHandlerSize = rect().height() / 3;
884  }
885  return rectHandlerSize;
886 }
887 
889 {
890  double lockSymbolSize = 20.0 / horizontalViewScaleFactor();
891 
892  if ( lockSymbolSize > ( rect().width() / 3 ) )
893  {
894  lockSymbolSize = rect().width() / 3;
895  }
896  if ( lockSymbolSize > ( rect().height() / 3 ) )
897  {
898  lockSymbolSize = rect().height() / 3;
899  }
900  return lockSymbolSize;
901 }
902 
903 void QgsComposerItem::updateCursor( const QPointF& itemPos )
904 {
905  setCursor( cursorForPosition( itemPos ) );
906 }
907 
909 {
910  if ( r > 360 )
911  {
912  mRotation = (( int )r ) % 360;
913  }
914  else
915  {
916  mRotation = r;
917  }
918  emit rotationChanged( r );
919  update();
920 }
921 
922 bool QgsComposerItem::imageSizeConsideringRotation( double& width, double& height ) const
923 {
924  if ( qAbs( mRotation ) <= 0.0 ) //width and height stays the same if there is no rotation
925  {
926  return true;
927  }
928 
929  double x1 = 0;
930  double y1 = 0;
931  double x2 = width;
932  double y2 = 0;
933  double x3 = width;
934  double y3 = height;
935  double x4 = 0;
936  double y4 = height;
937  double midX = width / 2.0;
938  double midY = height / 2.0;
939 
940  if ( !cornerPointOnRotatedAndScaledRect( x1, y1, width, height ) )
941  {
942  return false;
943  }
944  if ( !cornerPointOnRotatedAndScaledRect( x2, y2, width, height ) )
945  {
946  return false;
947  }
948  if ( !cornerPointOnRotatedAndScaledRect( x3, y3, width, height ) )
949  {
950  return false;
951  }
952  if ( !cornerPointOnRotatedAndScaledRect( x4, y4, width, height ) )
953  {
954  return false;
955  }
956 
957 
958  //assume points 1 and 3 are on the rectangle boundaries. Calculate 2 and 4.
959  double distM1 = sqrt(( x1 - midX ) * ( x1 - midX ) + ( y1 - midY ) * ( y1 - midY ) );
960  QPointF p2 = pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x2, y2 ), distM1 );
961 
962  if ( p2.x() < width && p2.x() > 0 && p2.y() < height && p2.y() > 0 )
963  {
964  width = sqrt(( p2.x() - x1 ) * ( p2.x() - x1 ) + ( p2.y() - y1 ) * ( p2.y() - y1 ) );
965  height = sqrt(( x3 - p2.x() ) * ( x3 - p2.x() ) + ( y3 - p2.y() ) * ( y3 - p2.y() ) );
966  return true;
967  }
968 
969  //else assume that points 2 and 4 are on the rectangle boundaries. Calculate 1 and 3
970  double distM2 = sqrt(( x2 - midX ) * ( x2 - midX ) + ( y2 - midY ) * ( y2 - midY ) );
971  QPointF p1 = pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x1, y1 ), distM2 );
972  QPointF p3 = pointOnLineWithDistance( QPointF( midX, midY ), QPointF( x3, y3 ), distM2 );
973  width = sqrt(( x2 - p1.x() ) * ( x2 - p1.x() ) + ( y2 - p1.y() ) * ( y2 - p1.y() ) );
974  height = sqrt(( p3.x() - x2 ) * ( p3.x() - x2 ) + ( p3.y() - y2 ) * ( p3.y() - y2 ) );
975  return true;
976 
977 
978 #if 0
979  double x1 = 0;
980  double y1 = 0;
981  double x2 = width;
982  double y2 = 0;
983  double x3 = width;
984  double y3 = height;
985 
986  if ( !cornerPointOnRotatedAndScaledRect( x1, y1, width, height ) )
987  {
988  return false;
989  }
990  if ( !cornerPointOnRotatedAndScaledRect( x2, y2, width, height ) )
991  {
992  return false;
993  }
994  if ( !cornerPointOnRotatedAndScaledRect( x3, y3, width, height ) )
995  {
996  return false;
997  }
998 
999  width = sqrt(( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 ) );
1000  height = sqrt(( x3 - x2 ) * ( x3 - x2 ) + ( y3 - y2 ) * ( y3 - y2 ) );
1001  return true;
1002 #endif //0
1003 }
1004 
1005 bool QgsComposerItem::cornerPointOnRotatedAndScaledRect( double& x, double& y, double width, double height ) const
1006 {
1007  //first rotate point clockwise
1008  double rotToRad = mRotation * M_PI / 180.0;
1009  QPointF midpoint( width / 2.0, height / 2.0 );
1010  double xVector = x - midpoint.x();
1011  double yVector = y - midpoint.y();
1012  //double xRotated = cos(rotToRad) * xVector + sin(rotToRad) * yVector;
1013  //double yRotated = -sin(rotToRad) * xVector + cos(rotToRad) * yVector;
1014  double xRotated = cos( rotToRad ) * xVector - sin( rotToRad ) * yVector;
1015  double yRotated = sin( rotToRad ) * xVector + cos( rotToRad ) * yVector;
1016 
1017  //create line from midpoint to rotated point
1018  QLineF line( midpoint.x(), midpoint.y(), midpoint.x() + xRotated, midpoint.y() + yRotated );
1019 
1020  //intersect with all four borders and return result
1021  QList<QLineF> borders;
1022  borders << QLineF( 0, 0, width, 0 );
1023  borders << QLineF( width, 0, width, height );
1024  borders << QLineF( width, height, 0, height );
1025  borders << QLineF( 0, height, 0, 0 );
1026 
1027  QList<QLineF>::const_iterator it = borders.constBegin();
1028  QPointF intersectionPoint;
1029 
1030  for ( ; it != borders.constEnd(); ++it )
1031  {
1032  if ( line.intersect( *it, &intersectionPoint ) == QLineF::BoundedIntersection )
1033  {
1034  x = intersectionPoint.x();
1035  y = intersectionPoint.y();
1036  return true;
1037  }
1038  }
1039  return false;
1040 }
1041 
1042 QPointF QgsComposerItem::pointOnLineWithDistance( const QPointF& startPoint, const QPointF& directionPoint, double distance ) const
1043 {
1044  double dx = directionPoint.x() - startPoint.x();
1045  double dy = directionPoint.y() - startPoint.y();
1046  double length = sqrt( dx * dx + dy * dy );
1047  double scaleFactor = distance / length;
1048  return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
1049 }
1050 
1051 void QgsComposerItem::sizeChangedByRotation( double& width, double& height )
1052 {
1053  if ( mRotation == 0.0 )
1054  {
1055  return;
1056  }
1057 
1058  //vector to p1
1059  double x1 = -width / 2.0;
1060  double y1 = -height / 2.0;
1061  rotate( mRotation, x1, y1 );
1062  //vector to p2
1063  double x2 = width / 2.0;
1064  double y2 = -height / 2.0;
1065  rotate( mRotation, x2, y2 );
1066  //vector to p3
1067  double x3 = width / 2.0;
1068  double y3 = height / 2.0;
1069  rotate( mRotation, x3, y3 );
1070  //vector to p4
1071  double x4 = -width / 2.0;
1072  double y4 = height / 2.0;
1073  rotate( mRotation, x4, y4 );
1074 
1075  //double midpoint
1076  QPointF midpoint( width / 2.0, height / 2.0 );
1077 
1078  QPolygonF rotatedRectPoly;
1079  rotatedRectPoly << QPointF( midpoint.x() + x1, midpoint.y() + y1 );
1080  rotatedRectPoly << QPointF( midpoint.x() + x2, midpoint.y() + y2 );
1081  rotatedRectPoly << QPointF( midpoint.x() + x3, midpoint.y() + y3 );
1082  rotatedRectPoly << QPointF( midpoint.x() + x4, midpoint.y() + y4 );
1083  QRectF boundingRect = rotatedRectPoly.boundingRect();
1084  width = boundingRect.width();
1085  height = boundingRect.height();
1086 }
1087 
1088 void QgsComposerItem::rotate( double angle, double& x, double& y ) const
1089 {
1090  double rotToRad = angle * M_PI / 180.0;
1091  double xRot, yRot;
1092  xRot = x * cos( rotToRad ) - y * sin( rotToRad );
1093  yRot = x * sin( rotToRad ) + y * cos( rotToRad );
1094  x = xRot;
1095  y = yRot;
1096 }
1097 
1099 {
1100  update();
1101 }