QGIS API Documentation  2.12.0-Lyon
qgscomposermousehandles.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposermousehandles.cpp
3  -------------------
4  begin : September 2013
5  copyright : (C) 2013 by Nyall Dawson, Radim Blazek
6  email : [email protected]
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 #include <QGraphicsView>
18 #include <QGraphicsSceneHoverEvent>
19 #include <QPainter>
20 #include <QWidget>
21 
22 #include <limits>
23 
25 #include "qgscomposeritem.h"
26 #include "qgscomposition.h"
27 #include "qgscomposerutils.h"
28 #include "qgspaperitem.h"
29 #include "qgis.h"
30 #include "qgslogger.h"
31 #include "qgsproject.h"
32 
34  : QObject( 0 )
35  , QGraphicsRectItem( 0 )
36  , mComposition( composition )
37  , mGraphicsView( 0 )
38  , mCurrentMouseMoveAction( NoAction )
39  , mBeginHandleWidth( 0 )
40  , mBeginHandleHeight( 0 )
41  , mResizeMoveX( 0 )
42  , mResizeMoveY( 0 )
43  , mIsDragging( false )
44  , mIsResizing( false )
45  , mHAlignSnapItem( 0 )
46  , mVAlignSnapItem( 0 )
47 {
48  //listen for selection changes, and update handles accordingly
49  QObject::connect( mComposition, SIGNAL( selectionChanged() ), this, SLOT( selectionChanged() ) );
50 
51  //accept hover events, required for changing cursor to resize cursors
52  setAcceptHoverEvents( true );
53 }
54 
56 {
57 
58 }
59 
60 QGraphicsView* QgsComposerMouseHandles::graphicsView()
61 {
62  //have we already found the current view?
63  if ( mGraphicsView )
64  {
65  return mGraphicsView;
66  }
67 
68  //otherwise, try and find current view attached to composition
69  if ( scene() )
70  {
71  QList<QGraphicsView*> viewList = scene()->views();
72  if ( viewList.size() > 0 )
73  {
74  mGraphicsView = viewList.at( 0 );
75  return mGraphicsView;
76  }
77  }
78 
79  //no view attached to composition
80  return 0;
81 }
82 
83 void QgsComposerMouseHandles::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
84 {
85  Q_UNUSED( itemStyle );
86  Q_UNUSED( pWidget );
87 
88  if ( mComposition->plotStyle() != QgsComposition::Preview )
89  {
90  //don't draw selection handles in composition outputs
91  return;
92  }
93 
94  if ( mComposition->boundingBoxesVisible() )
95  {
96  //draw resize handles around bounds of entire selection
97  double rectHandlerSize = rectHandlerBorderTolerance();
98  drawHandles( painter, rectHandlerSize );
99  }
100 
101  if ( mIsResizing || mIsDragging || mComposition->boundingBoxesVisible() )
102  {
103  //draw dotted boxes around selected items
104  drawSelectedItemBounds( painter );
105  }
106 }
107 
108 void QgsComposerMouseHandles::drawHandles( QPainter* painter, double rectHandlerSize )
109 {
110  //blue, zero width cosmetic pen for outline
111  QPen handlePen = QPen( QColor( 55, 140, 195, 255 ) );
112  handlePen.setWidth( 0 );
113  painter->setPen( handlePen );
114 
115  //draw box around entire selection bounds
116  painter->setBrush( Qt::NoBrush );
117  painter->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) );
118 
119  //draw resize handles, using a filled white box
120  painter->setBrush( QColor( 255, 255, 255, 255 ) );
121  //top left
122  painter->drawRect( QRectF( 0, 0, rectHandlerSize, rectHandlerSize ) );
123  //mid top
124  painter->drawRect( QRectF(( rect().width() - rectHandlerSize ) / 2, 0, rectHandlerSize, rectHandlerSize ) );
125  //top right
126  painter->drawRect( QRectF( rect().width() - rectHandlerSize, 0, rectHandlerSize, rectHandlerSize ) );
127  //mid left
128  painter->drawRect( QRectF( 0, ( rect().height() - rectHandlerSize ) / 2, rectHandlerSize, rectHandlerSize ) );
129  //mid right
130  painter->drawRect( QRectF( rect().width() - rectHandlerSize, ( rect().height() - rectHandlerSize ) / 2, rectHandlerSize, rectHandlerSize ) );
131  //bottom left
132  painter->drawRect( QRectF( 0, rect().height() - rectHandlerSize, rectHandlerSize, rectHandlerSize ) );
133  //mid bottom
134  painter->drawRect( QRectF(( rect().width() - rectHandlerSize ) / 2, rect().height() - rectHandlerSize, rectHandlerSize, rectHandlerSize ) );
135  //bottom right
136  painter->drawRect( QRectF( rect().width() - rectHandlerSize, rect().height() - rectHandlerSize, rectHandlerSize, rectHandlerSize ) );
137 }
138 
139 void QgsComposerMouseHandles::drawSelectedItemBounds( QPainter* painter )
140 {
141  //draw dotted border around selected items to give visual feedback which items are selected
142  QList<QgsComposerItem*> selectedItems = mComposition->selectedComposerItems( false );
143  if ( selectedItems.size() == 0 )
144  {
145  return;
146  }
147 
148  //use difference mode so that they are visible regardless of item colors
149  painter->save();
150  painter->setCompositionMode( QPainter::CompositionMode_Difference );
151 
152  // use a grey dashed pen - in difference mode this should always be visible
153  QPen selectedItemPen = QPen( QColor( 144, 144, 144, 255 ) );
154  selectedItemPen.setStyle( Qt::DashLine );
155  selectedItemPen.setWidth( 0 );
156  painter->setPen( selectedItemPen );
157  painter->setBrush( Qt::NoBrush );
158 
159  QList<QgsComposerItem*>::iterator itemIter = selectedItems.begin();
160  for ( ; itemIter != selectedItems.end(); ++itemIter )
161  {
162  //get bounds of selected item
163  QPolygonF itemBounds;
164  if ( mIsDragging && !( *itemIter )->positionLock() )
165  {
166  //if currently dragging, draw selected item bounds relative to current mouse position
167  //first, get bounds of current item in scene coordinates
168  QPolygonF itemSceneBounds = ( *itemIter )->mapToScene(( *itemIter )->rectWithFrame() );
169  //now, translate it by the current movement amount
170  //IMPORTANT - this is done in scene coordinates, since we don't want any rotation/non-translation transforms to affect the movement
171  itemSceneBounds.translate( transform().dx(), transform().dy() );
172  //finally, remap it to the mouse handle item's coordinate system so it's ready for drawing
173  itemBounds = mapFromScene( itemSceneBounds );
174  }
175  else if ( mIsResizing && !( *itemIter )->positionLock() )
176  {
177  //if currently resizing, calculate relative resize of this item
178  if ( selectedItems.size() > 1 )
179  {
180  //get item bounds in mouse handle item's coordinate system
181  QRectF itemRect = mapRectFromItem(( *itemIter ), ( *itemIter )->rectWithFrame() );
182  //now, resize it relative to the current resized dimensions of the mouse handles
183  QgsComposerUtils::relativeResizeRect( itemRect, QRectF( -mResizeMoveX, -mResizeMoveY, mBeginHandleWidth, mBeginHandleHeight ), mResizeRect );
184  itemBounds = QPolygonF( itemRect );
185  }
186  else
187  {
188  //single item selected
189  itemBounds = rect();
190  }
191  }
192  else
193  {
194  //not resizing or moving, so just map from scene bounds
195  itemBounds = mapRectFromItem(( *itemIter ), ( *itemIter )->rectWithFrame() );
196  }
197  painter->drawPolygon( itemBounds );
198  }
199  painter->restore();
200 }
201 
203 {
204  //listen out for selected items' size and rotation changed signals
205  QList<QGraphicsItem *> itemList = composition()->items();
206  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
207  for ( ; itemIt != itemList.end(); ++itemIt )
208  {
209  QgsComposerItem* item = dynamic_cast<QgsComposerItem *>( *itemIt );
210  if ( item )
211  {
212  if ( item->selected() )
213  {
214  QObject::connect( item, SIGNAL( sizeChanged() ), this, SLOT( selectedItemSizeChanged() ) );
215  QObject::connect( item, SIGNAL( itemRotationChanged( double ) ), this, SLOT( selectedItemRotationChanged() ) );
216  QObject::connect( item, SIGNAL( frameChanged() ), this, SLOT( selectedItemSizeChanged() ) );
217  QObject::connect( item, SIGNAL( lockChanged() ), this, SLOT( selectedItemSizeChanged() ) );
218  }
219  else
220  {
221  QObject::disconnect( item, SIGNAL( sizeChanged() ), this, 0 );
222  QObject::disconnect( item, SIGNAL( itemRotationChanged( double ) ), this, 0 );
223  QObject::disconnect( item, SIGNAL( frameChanged() ), this, 0 );
224  QObject::disconnect( item, SIGNAL( lockChanged() ), this, 0 );
225  }
226  }
227  }
228 
229  resetStatusBar();
230  updateHandles();
231 }
232 
234 {
235  if ( !mIsDragging && !mIsResizing )
236  {
237  //only required for non-mouse initiated size changes
238  updateHandles();
239  }
240 }
241 
243 {
244  if ( !mIsDragging && !mIsResizing )
245  {
246  //only required for non-mouse initiated rotation changes
247  updateHandles();
248  }
249 }
250 
251 void QgsComposerMouseHandles::updateHandles()
252 {
253  //recalculate size and position of handle item
254 
255  //first check to see if any items are selected
256  QList<QgsComposerItem*> selectedItems = mComposition->selectedComposerItems( false );
257  if ( selectedItems.size() > 0 )
258  {
259  //one or more items are selected, get bounds of all selected items
260 
261  //update rotation of handle object
262  double rotation;
263  if ( selectionRotation( rotation ) )
264  {
265  //all items share a common rotation value, so we rotate the mouse handles to match
266  setRotation( rotation );
267  }
268  else
269  {
270  //items have varying rotation values - we can't rotate the mouse handles to match
271  setRotation( 0 );
272  }
273 
274  //get bounds of all selected items
275  QRectF newHandleBounds = selectionBounds();
276 
277  //update size and position of handle object
278  setRect( 0, 0, newHandleBounds.width(), newHandleBounds.height() );
279  setPos( mapToScene( newHandleBounds.topLeft() ) );
280 
281  show();
282  }
283  else
284  {
285  //no items selected, hide handles
286  hide();
287  }
288  //force redraw
289  update();
290 }
291 
292 QRectF QgsComposerMouseHandles::selectionBounds() const
293 {
294  //calculate bounds of all currently selected items in mouse handle coordinate system
295  QList<QgsComposerItem*> selectedItems = mComposition->selectedComposerItems( false );
296  QList<QgsComposerItem*>::iterator itemIter = selectedItems.begin();
297 
298  //start with handle bounds of first selected item
299  QRectF bounds = mapFromItem(( *itemIter ), ( *itemIter )->rectWithFrame() ).boundingRect();
300 
301  //iterate through remaining items, expanding the bounds as required
302  for ( ++itemIter; itemIter != selectedItems.end(); ++itemIter )
303  {
304  bounds = bounds.united( mapFromItem(( *itemIter ), ( *itemIter )->rectWithFrame() ).boundingRect() );
305  }
306 
307  return bounds;
308 }
309 
310 bool QgsComposerMouseHandles::selectionRotation( double & rotation ) const
311 {
312  //check if all selected items have same rotation
313  QList<QgsComposerItem*> selectedItems = mComposition->selectedComposerItems( false );
314  QList<QgsComposerItem*>::iterator itemIter = selectedItems.begin();
315 
316  //start with rotation of first selected item
317  double firstItemRotation = ( *itemIter )->itemRotation();
318 
319  //iterate through remaining items, checking if they have same rotation
320  for ( ++itemIter; itemIter != selectedItems.end(); ++itemIter )
321  {
322  if (( *itemIter )->itemRotation() != firstItemRotation )
323  {
324  //item has a different rotation, so return false
325  return false;
326  }
327  }
328 
329  //all items have the same rotation, so set the rotation variable and return true
330  rotation = firstItemRotation;
331  return true;
332 }
333 
334 double QgsComposerMouseHandles::rectHandlerBorderTolerance()
335 {
336  //calculate size for resize handles
337  //get view scale factor
338  double viewScaleFactor = graphicsView()->transform().m11();
339 
340  //size of handle boxes depends on zoom level in composer view
341  double rectHandlerSize = 10.0 / viewScaleFactor;
342 
343  //make sure the boxes don't get too large
344  if ( rectHandlerSize > ( rect().width() / 3 ) )
345  {
346  rectHandlerSize = rect().width() / 3;
347  }
348  if ( rectHandlerSize > ( rect().height() / 3 ) )
349  {
350  rectHandlerSize = rect().height() / 3;
351  }
352  return rectHandlerSize;
353 }
354 
355 Qt::CursorShape QgsComposerMouseHandles::cursorForPosition( const QPointF& itemCoordPos )
356 {
357  QgsComposerMouseHandles::MouseAction mouseAction = mouseActionForPosition( itemCoordPos );
358  switch ( mouseAction )
359  {
360  case NoAction:
361  return Qt::ForbiddenCursor;
362  case MoveItem:
363  return Qt::SizeAllCursor;
364  case ResizeUp:
365  case ResizeDown:
366  //account for rotation
367  if (( rotation() <= 22.5 || rotation() >= 337.5 ) || ( rotation() >= 157.5 && rotation() <= 202.5 ) )
368  {
369  return Qt::SizeVerCursor;
370  }
371  else if (( rotation() >= 22.5 && rotation() <= 67.5 ) || ( rotation() >= 202.5 && rotation() <= 247.5 ) )
372  {
373  return Qt::SizeBDiagCursor;
374  }
375  else if (( rotation() >= 67.5 && rotation() <= 112.5 ) || ( rotation() >= 247.5 && rotation() <= 292.5 ) )
376  {
377  return Qt::SizeHorCursor;
378  }
379  else
380  {
381  return Qt::SizeFDiagCursor;
382  }
383  case ResizeLeft:
384  case ResizeRight:
385  //account for rotation
386  if (( rotation() <= 22.5 || rotation() >= 337.5 ) || ( rotation() >= 157.5 && rotation() <= 202.5 ) )
387  {
388  return Qt::SizeHorCursor;
389  }
390  else if (( rotation() >= 22.5 && rotation() <= 67.5 ) || ( rotation() >= 202.5 && rotation() <= 247.5 ) )
391  {
392  return Qt::SizeFDiagCursor;
393  }
394  else if (( rotation() >= 67.5 && rotation() <= 112.5 ) || ( rotation() >= 247.5 && rotation() <= 292.5 ) )
395  {
396  return Qt::SizeVerCursor;
397  }
398  else
399  {
400  return Qt::SizeBDiagCursor;
401  }
402 
403  case ResizeLeftUp:
404  case ResizeRightDown:
405  //account for rotation
406  if (( rotation() <= 22.5 || rotation() >= 337.5 ) || ( rotation() >= 157.5 && rotation() <= 202.5 ) )
407  {
408  return Qt::SizeFDiagCursor;
409  }
410  else if (( rotation() >= 22.5 && rotation() <= 67.5 ) || ( rotation() >= 202.5 && rotation() <= 247.5 ) )
411  {
412  return Qt::SizeVerCursor;
413  }
414  else if (( rotation() >= 67.5 && rotation() <= 112.5 ) || ( rotation() >= 247.5 && rotation() <= 292.5 ) )
415  {
416  return Qt::SizeBDiagCursor;
417  }
418  else
419  {
420  return Qt::SizeHorCursor;
421  }
422  case ResizeRightUp:
423  case ResizeLeftDown:
424  //account for rotation
425  if (( rotation() <= 22.5 || rotation() >= 337.5 ) || ( rotation() >= 157.5 && rotation() <= 202.5 ) )
426  {
427  return Qt::SizeBDiagCursor;
428  }
429  else if (( rotation() >= 22.5 && rotation() <= 67.5 ) || ( rotation() >= 202.5 && rotation() <= 247.5 ) )
430  {
431  return Qt::SizeHorCursor;
432  }
433  else if (( rotation() >= 67.5 && rotation() <= 112.5 ) || ( rotation() >= 247.5 && rotation() <= 292.5 ) )
434  {
435  return Qt::SizeFDiagCursor;
436  }
437  else
438  {
439  return Qt::SizeVerCursor;
440  }
441  case SelectItem:
442  default:
443  return Qt::ArrowCursor;
444  }
445 }
446 
447 QgsComposerMouseHandles::MouseAction QgsComposerMouseHandles::mouseActionForPosition( const QPointF& itemCoordPos )
448 {
449  bool nearLeftBorder = false;
450  bool nearRightBorder = false;
451  bool nearLowerBorder = false;
452  bool nearUpperBorder = false;
453 
454  bool withinWidth = false;
455  bool withinHeight = false;
456  if ( itemCoordPos.x() >= 0 && itemCoordPos.x() <= rect().width() )
457  {
458  withinWidth = true;
459  }
460  if ( itemCoordPos.y() >= 0 && itemCoordPos.y() <= rect().height() )
461  {
462  withinHeight = true;
463  }
464 
465  double borderTolerance = rectHandlerBorderTolerance();
466 
467  if ( itemCoordPos.x() >= 0 && itemCoordPos.x() < borderTolerance )
468  {
469  nearLeftBorder = true;
470  }
471  if ( itemCoordPos.y() >= 0 && itemCoordPos.y() < borderTolerance )
472  {
473  nearUpperBorder = true;
474  }
475  if ( itemCoordPos.x() <= rect().width() && itemCoordPos.x() > ( rect().width() - borderTolerance ) )
476  {
477  nearRightBorder = true;
478  }
479  if ( itemCoordPos.y() <= rect().height() && itemCoordPos.y() > ( rect().height() - borderTolerance ) )
480  {
481  nearLowerBorder = true;
482  }
483 
484  if ( nearLeftBorder && nearUpperBorder )
485  {
487  }
488  else if ( nearLeftBorder && nearLowerBorder )
489  {
491  }
492  else if ( nearRightBorder && nearUpperBorder )
493  {
495  }
496  else if ( nearRightBorder && nearLowerBorder )
497  {
499  }
500  else if ( nearLeftBorder && withinHeight )
501  {
503  }
504  else if ( nearRightBorder && withinHeight )
505  {
507  }
508  else if ( nearUpperBorder && withinWidth )
509  {
511  }
512  else if ( nearLowerBorder && withinWidth )
513  {
515  }
516 
517  //find out if cursor position is over a selected item
518  QPointF scenePoint = mapToScene( itemCoordPos );
519  QList<QGraphicsItem *> itemsAtCursorPos = mComposition->items( scenePoint );
520  if ( itemsAtCursorPos.size() == 0 )
521  {
522  //no items at cursor position
524  }
525  QList<QGraphicsItem*>::iterator itemIter = itemsAtCursorPos.begin();
526  for ( ; itemIter != itemsAtCursorPos.end(); ++itemIter )
527  {
528  QgsComposerItem* item = dynamic_cast<QgsComposerItem *>(( *itemIter ) );
529  if ( item && item->selected() )
530  {
531  //cursor is over a selected composer item
533  }
534  }
535 
536  //default
538 }
539 
541 {
542  // convert sceneCoordPos to item coordinates
543  QPointF itemPos = mapFromScene( sceneCoordPos );
544  return mouseActionForPosition( itemPos );
545 }
546 
548 {
549  setViewportCursor( cursorForPosition( event->pos() ) );
550 }
551 
553 {
554  Q_UNUSED( event );
555  setViewportCursor( Qt::ArrowCursor );
556 }
557 
558 void QgsComposerMouseHandles::setViewportCursor( Qt::CursorShape cursor )
559 {
560  //workaround qt bug #3732 by setting cursor for QGraphicsView viewport,
561  //rather then setting it directly here
562 
563  if ( !mComposition->preventCursorChange() )
564  {
565  graphicsView()->viewport()->setCursor( cursor );
566  }
567 }
568 
570 {
571  if ( mIsDragging )
572  {
573  //currently dragging a selection
574  //if shift depressed, constrain movement to horizontal/vertical
575  //if control depressed, ignore snapping
576  dragMouseMove( event->lastScenePos(), event->modifiers() & Qt::ShiftModifier, event->modifiers() & Qt::ControlModifier );
577  }
578  else if ( mIsResizing )
579  {
580  //currently resizing a selection
581  //lock aspect ratio if shift depressed
582  //resize from center if alt depressed
583  resizeMouseMove( event->lastScenePos(), event->modifiers() & Qt::ShiftModifier, event->modifiers() & Qt::AltModifier );
584  }
585 
586  mLastMouseEventPos = event->lastScenePos();
587 }
588 
590 {
591  QPointF mouseMoveStopPoint = event->lastScenePos();
592  double diffX = mouseMoveStopPoint.x() - mMouseMoveStartPos.x();
593  double diffY = mouseMoveStopPoint.y() - mMouseMoveStartPos.y();
594 
595  //it was only a click
596  if ( qAbs( diffX ) < std::numeric_limits<double>::min() && qAbs( diffY ) < std::numeric_limits<double>::min() )
597  {
598  mIsDragging = false;
599  mIsResizing = false;
600  update();
601  return;
602  }
603 
604  if ( mCurrentMouseMoveAction == QgsComposerMouseHandles::MoveItem )
605  {
606  //move selected items
607  QUndoCommand* parentCommand = new QUndoCommand( tr( "Change item position" ) );
608 
609  QPointF mEndHandleMovePos = scenePos();
610 
611  //move all selected items
612  QList<QgsComposerItem*> selectedItems = mComposition->selectedComposerItems( false );
613  QList<QgsComposerItem*>::iterator itemIter = selectedItems.begin();
614  for ( ; itemIter != selectedItems.end(); ++itemIter )
615  {
616  if (( *itemIter )->positionLock() || (( *itemIter )->flags() & QGraphicsItem::ItemIsSelectable ) == 0 )
617  {
618  //don't move locked items
619  continue;
620  }
621  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *itemIter, "", parentCommand );
622  subcommand->savePreviousState();
623  ( *itemIter )->move( mEndHandleMovePos.x() - mBeginHandlePos.x(), mEndHandleMovePos.y() - mBeginHandlePos.y() );
624  subcommand->saveAfterState();
625  }
626  mComposition->undoStack()->push( parentCommand );
627  QgsProject::instance()->dirty( true );
628  }
629  else if ( mCurrentMouseMoveAction != QgsComposerMouseHandles::NoAction )
630  {
631  //resize selected items
632  QUndoCommand* parentCommand = new QUndoCommand( tr( "Change item size" ) );
633 
634  //resize all selected items
635  QList<QgsComposerItem*> selectedItems = mComposition->selectedComposerItems( false );
636  QList<QgsComposerItem*>::iterator itemIter = selectedItems.begin();
637  for ( ; itemIter != selectedItems.end(); ++itemIter )
638  {
639  if (( *itemIter )->positionLock() || (( *itemIter )->flags() & QGraphicsItem::ItemIsSelectable ) == 0 )
640  {
641  //don't resize locked items or unselectable items (eg, items which make up an item group)
642  continue;
643  }
644  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *itemIter, "", parentCommand );
645  subcommand->savePreviousState();
646 
647  QRectF itemRect;
648  if ( selectedItems.size() == 1 )
649  {
650  //only a single item is selected, so set its size to the final resized mouse handle size
651  itemRect = mResizeRect;
652  }
653  else
654  {
655  //multiple items selected, so each needs to be scaled relatively to the final size of the mouse handles
656  itemRect = mapRectFromItem(( *itemIter ), ( *itemIter )->rectWithFrame() );
657  QgsComposerUtils::relativeResizeRect( itemRect, QRectF( -mResizeMoveX, -mResizeMoveY, mBeginHandleWidth, mBeginHandleHeight ), mResizeRect );
658  }
659 
660  itemRect = itemRect.normalized();
661  QPointF newPos = mapToScene( itemRect.topLeft() );
662  ( *itemIter )->setItemPosition( newPos.x(), newPos.y(), itemRect.width(), itemRect.height(), QgsComposerItem::UpperLeft, true );
663 
664  subcommand->saveAfterState();
665  }
666  mComposition->undoStack()->push( parentCommand );
667  QgsProject::instance()->dirty( true );
668  }
669 
670  deleteAlignItems();
671 
672  if ( mIsDragging )
673  {
674  mIsDragging = false;
675  }
676  if ( mIsResizing )
677  {
678  mIsResizing = false;
679  }
680 
681  //reset default action
682  mCurrentMouseMoveAction = QgsComposerMouseHandles::MoveItem;
683  setViewportCursor( Qt::ArrowCursor );
684  //redraw handles
685  resetTransform();
686  updateHandles();
687  //reset status bar message
688  resetStatusBar();
689 }
690 
691 void QgsComposerMouseHandles::resetStatusBar()
692 {
693  QList<QgsComposerItem*> selectedItems = mComposition->selectedComposerItems( false );
694  int selectedCount = selectedItems.size();
695  if ( selectedCount > 1 )
696  {
697  //set status bar message to count of selected items
698  mComposition->setStatusMessage( QString( tr( "%1 items selected" ) ).arg( selectedCount ) );
699  }
700  else if ( selectedCount == 1 )
701  {
702  //set status bar message to count of selected items
703  mComposition->setStatusMessage( tr( "1 item selected" ) );
704  }
705  else
706  {
707  //clear status bar message
708  mComposition->setStatusMessage( QString() );
709  }
710 }
711 
713 {
714  //save current cursor position
715  mMouseMoveStartPos = event->lastScenePos();
716  mLastMouseEventPos = event->lastScenePos();
717  //save current item geometry
718  mBeginMouseEventPos = event->lastScenePos();
719  mBeginHandlePos = scenePos();
720  mBeginHandleWidth = rect().width();
721  mBeginHandleHeight = rect().height();
722  //type of mouse move action
723  mCurrentMouseMoveAction = mouseActionForPosition( event->pos() );
724 
725  deleteAlignItems();
726 
727  if ( mCurrentMouseMoveAction == QgsComposerMouseHandles::MoveItem )
728  {
729  //moving items
730  mIsDragging = true;
731  }
732  else if ( mCurrentMouseMoveAction != QgsComposerMouseHandles::SelectItem &&
733  mCurrentMouseMoveAction != QgsComposerMouseHandles::NoAction )
734  {
735  //resizing items
736  mIsResizing = true;
737  mResizeRect = QRectF( 0, 0, mBeginHandleWidth, mBeginHandleHeight );
738  mResizeMoveX = 0;
739  mResizeMoveY = 0;
740  mCursorOffset = calcCursorEdgeOffset( mMouseMoveStartPos );
741 
742  }
743 
744 }
745 
747 {
748  Q_UNUSED( event );
749 }
750 
751 QSizeF QgsComposerMouseHandles::calcCursorEdgeOffset( const QPointF &cursorPos )
752 {
753  //find offset between cursor position and actual edge of item
754  QPointF sceneMousePos = mapFromScene( cursorPos );
755 
756  switch ( mCurrentMouseMoveAction )
757  {
758  //vertical resize
760  return QSizeF( 0, sceneMousePos.y() );
761 
763  return QSizeF( 0, sceneMousePos.y() - rect().height() );
764 
765  //horizontal resize
767  return QSizeF( sceneMousePos.x(), 0 );
768 
770  return QSizeF( sceneMousePos.x() - rect().width(), 0 );
771 
772  //diagonal resize
774  return QSizeF( sceneMousePos.x(), sceneMousePos.y() );
775 
777  return QSizeF( sceneMousePos.x() - rect().width(), sceneMousePos.y() - rect().height() );
778 
780  return QSizeF( sceneMousePos.x() - rect().width(), sceneMousePos.y() );
781 
783  return QSizeF( sceneMousePos.x(), sceneMousePos.y() - rect().height() );
784 
785  default:
786  return QSizeF( 0, 0 );
787  }
788 
789 }
790 
791 void QgsComposerMouseHandles::dragMouseMove( const QPointF& currentPosition, bool lockMovement, bool preventSnap )
792 {
793  if ( !mComposition )
794  {
795  return;
796  }
797 
798  //calculate total amount of mouse movement since drag began
799  double moveX = currentPosition.x() - mBeginMouseEventPos.x();
800  double moveY = currentPosition.y() - mBeginMouseEventPos.y();
801 
802  //find target position before snapping (in scene coordinates)
803  QPointF upperLeftPoint( mBeginHandlePos.x() + moveX, mBeginHandlePos.y() + moveY );
804 
805  QPointF snappedLeftPoint;
806  //no snapping for rotated items for now
807  if ( !preventSnap && rotation() == 0 )
808  {
809  //snap to grid and guides
810  snappedLeftPoint = snapPoint( upperLeftPoint, QgsComposerMouseHandles::Item );
811  }
812  else
813  {
814  //no snapping
815  snappedLeftPoint = upperLeftPoint;
816  deleteAlignItems();
817  }
818 
819  //calculate total shift for item from beginning of drag operation to current position
820  double moveRectX = snappedLeftPoint.x() - mBeginHandlePos.x();
821  double moveRectY = snappedLeftPoint.y() - mBeginHandlePos.y();
822 
823  if ( lockMovement )
824  {
825  //constrained (shift) moving should lock to horizontal/vertical movement
826  //reset the smaller of the x/y movements
827  if ( qAbs( moveRectX ) <= qAbs( moveRectY ) )
828  {
829  moveRectX = 0;
830  }
831  else
832  {
833  moveRectY = 0;
834  }
835  }
836 
837  //shift handle item to new position
838  QTransform moveTransform;
839  moveTransform.translate( moveRectX, moveRectY );
840  setTransform( moveTransform );
841  //show current displacement of selection in status bar
842  mComposition->setStatusMessage( QString( tr( "dx: %1 mm dy: %2 mm" ) ).arg( moveRectX ).arg( moveRectY ) );
843 }
844 
845 void QgsComposerMouseHandles::resizeMouseMove( const QPointF& currentPosition, bool lockRatio, bool fromCenter )
846 {
847 
848  if ( !mComposition )
849  {
850  return;
851  }
852 
853  double mx = 0.0, my = 0.0, rx = 0.0, ry = 0.0;
854 
855  QPointF beginMousePos;
856  QPointF finalPosition;
857  if ( rotation() == 0 )
858  {
859  //snapping only occurs if handles are not rotated for now
860 
861  //subtract cursor edge offset from begin mouse event and current cursor position, so that snapping occurs to edge of mouse handles
862  //rather then cursor position
863  beginMousePos = mapFromScene( QPointF( mBeginMouseEventPos.x() - mCursorOffset.width(), mBeginMouseEventPos.y() - mCursorOffset.height() ) );
864  QPointF snappedPosition = snapPoint( QPointF( currentPosition.x() - mCursorOffset.width(), currentPosition.y() - mCursorOffset.height() ), QgsComposerMouseHandles::Point );
865  finalPosition = mapFromScene( snappedPosition );
866  }
867  else
868  {
869  //no snapping for rotated items for now
870  beginMousePos = mapFromScene( mBeginMouseEventPos );
871  finalPosition = mapFromScene( currentPosition );
872  }
873 
874  double diffX = finalPosition.x() - beginMousePos.x();
875  double diffY = finalPosition.y() - beginMousePos.y();
876 
877  double ratio = 0;
878  if ( lockRatio && mBeginHandleHeight != 0 )
879  {
880  ratio = mBeginHandleWidth / mBeginHandleHeight;
881  }
882 
883  switch ( mCurrentMouseMoveAction )
884  {
885  //vertical resize
887  {
888  if ( ratio )
889  {
890  diffX = (( mBeginHandleHeight - diffY ) * ratio ) - mBeginHandleWidth;
891  mx = -diffX / 2; my = diffY; rx = diffX; ry = -diffY;
892  }
893  else
894  {
895  mx = 0; my = diffY; rx = 0; ry = -diffY;
896  }
897  break;
898  }
899 
901  {
902  if ( ratio )
903  {
904  diffX = (( mBeginHandleHeight + diffY ) * ratio ) - mBeginHandleWidth;
905  mx = -diffX / 2; my = 0; rx = diffX; ry = diffY;
906  }
907  else
908  {
909  mx = 0; my = 0; rx = 0; ry = diffY;
910  }
911  break;
912  }
913 
914  //horizontal resize
916  {
917  if ( ratio )
918  {
919  diffY = (( mBeginHandleWidth - diffX ) / ratio ) - mBeginHandleHeight;
920  mx = diffX; my = -diffY / 2; rx = -diffX; ry = diffY;
921  }
922  else
923  {
924  mx = diffX, my = 0; rx = -diffX; ry = 0;
925  }
926  break;
927  }
928 
930  {
931  if ( ratio )
932  {
933  diffY = (( mBeginHandleWidth + diffX ) / ratio ) - mBeginHandleHeight;
934  mx = 0; my = -diffY / 2; rx = diffX; ry = diffY;
935  }
936  else
937  {
938  mx = 0; my = 0; rx = diffX, ry = 0;
939  }
940  break;
941  }
942 
943  //diagonal resize
945  {
946  if ( ratio )
947  {
948  //ratio locked resize
949  if (( mBeginHandleWidth - diffX ) / ( mBeginHandleHeight - diffY ) > ratio )
950  {
951  diffX = mBeginHandleWidth - (( mBeginHandleHeight - diffY ) * ratio );
952  }
953  else
954  {
955  diffY = mBeginHandleHeight - (( mBeginHandleWidth - diffX ) / ratio );
956  }
957  }
958  mx = diffX, my = diffY; rx = -diffX; ry = -diffY;
959  break;
960  }
961 
963  {
964  if ( ratio )
965  {
966  //ratio locked resize
967  if (( mBeginHandleWidth + diffX ) / ( mBeginHandleHeight + diffY ) > ratio )
968  {
969  diffX = (( mBeginHandleHeight + diffY ) * ratio ) - mBeginHandleWidth;
970  }
971  else
972  {
973  diffY = (( mBeginHandleWidth + diffX ) / ratio ) - mBeginHandleHeight;
974  }
975  }
976  mx = 0; my = 0; rx = diffX, ry = diffY;
977  break;
978  }
979 
981  {
982  if ( ratio )
983  {
984  //ratio locked resize
985  if (( mBeginHandleWidth + diffX ) / ( mBeginHandleHeight - diffY ) > ratio )
986  {
987  diffX = (( mBeginHandleHeight - diffY ) * ratio ) - mBeginHandleWidth;
988  }
989  else
990  {
991  diffY = mBeginHandleHeight - (( mBeginHandleWidth + diffX ) / ratio );
992  }
993  }
994  mx = 0; my = diffY, rx = diffX, ry = -diffY;
995  break;
996  }
997 
999  {
1000  if ( ratio )
1001  {
1002  //ratio locked resize
1003  if (( mBeginHandleWidth - diffX ) / ( mBeginHandleHeight + diffY ) > ratio )
1004  {
1005  diffX = mBeginHandleWidth - (( mBeginHandleHeight + diffY ) * ratio );
1006  }
1007  else
1008  {
1009  diffY = (( mBeginHandleWidth - diffX ) / ratio ) - mBeginHandleHeight;
1010  }
1011  }
1012  mx = diffX, my = 0; rx = -diffX; ry = diffY;
1013  break;
1014  }
1015 
1019  break;
1020  }
1021 
1022  //resizing from center of objects?
1023  if ( fromCenter )
1024  {
1025  my = -ry;
1026  mx = -rx;
1027  ry = 2 * ry;
1028  rx = 2 * rx;
1029  }
1030 
1031  //update selection handle rectangle
1032 
1033  //make sure selection handle size rectangle is normalized (ie, left coord < right coord)
1034  mResizeMoveX = mBeginHandleWidth + rx > 0 ? mx : mx + mBeginHandleWidth + rx;
1035  mResizeMoveY = mBeginHandleHeight + ry > 0 ? my : my + mBeginHandleHeight + ry;
1036 
1037  //calculate movement in scene coordinates
1038  QLineF translateLine = QLineF( 0, 0, mResizeMoveX, mResizeMoveY );
1039  translateLine.setAngle( translateLine.angle() - rotation() );
1040  QPointF sceneTranslate = translateLine.p2();
1041 
1042  //move selection handles
1044  itemTransform.translate( sceneTranslate.x(), sceneTranslate.y() );
1045  setTransform( itemTransform );
1046 
1047  //handle non-normalised resizes - eg, dragging the left handle so far to the right that it's past the right handle
1048  if ( mBeginHandleWidth + rx >= 0 && mBeginHandleHeight + ry >= 0 )
1049  {
1050  mResizeRect = QRectF( 0, 0, mBeginHandleWidth + rx, mBeginHandleHeight + ry );
1051  }
1052  else if ( mBeginHandleHeight + ry >= 0 )
1053  {
1054  mResizeRect = QRectF( QPointF( -( mBeginHandleWidth + rx ), 0 ), QPointF( 0, mBeginHandleHeight + ry ) );
1055  }
1056  else if ( mBeginHandleWidth + rx >= 0 )
1057  {
1058  mResizeRect = QRectF( QPointF( 0, -( mBeginHandleHeight + ry ) ), QPointF( mBeginHandleWidth + rx, 0 ) );
1059  }
1060  else
1061  {
1062  mResizeRect = QRectF( QPointF( -( mBeginHandleWidth + rx ), -( mBeginHandleHeight + ry ) ), QPointF( 0, 0 ) );
1063  }
1064 
1065  setRect( 0, 0, fabs( mBeginHandleWidth + rx ), fabs( mBeginHandleHeight + ry ) );
1066 
1067  //show current size of selection in status bar
1068  mComposition->setStatusMessage( QString( tr( "width: %1 mm height: %2 mm" ) ).arg( rect().width() ).arg( rect().height() ) );
1069 }
1070 
1071 QPointF QgsComposerMouseHandles::snapPoint( const QPointF& point, QgsComposerMouseHandles::SnapGuideMode mode )
1072 {
1073  //snap to grid
1074  QPointF snappedPoint = mComposition->snapPointToGrid( point );
1075 
1076  if ( snappedPoint != point ) //don't do align snap if grid snap has been done
1077  {
1078  deleteAlignItems();
1079  return snappedPoint;
1080  }
1081 
1082  //align item
1083  if ( !mComposition->alignmentSnap() && !mComposition->smartGuidesEnabled() )
1084  {
1085  return point;
1086  }
1087 
1088  double alignX = 0;
1089  double alignY = 0;
1090 
1091  //depending on the mode, we either snap just the single point, or all the bounds of the selection
1092  switch ( mode )
1093  {
1095  snappedPoint = alignItem( alignX, alignY, point.x(), point.y() );
1096  break;
1098  snappedPoint = alignPos( point, alignX, alignY );
1099  break;
1100  }
1101 
1102  if ( alignX != -1 )
1103  {
1104  QGraphicsLineItem* item = hAlignSnapItem();
1105  int numPages = mComposition->numPages();
1106  double yLineCoord = 300; //default in case there is no single page
1107  if ( numPages > 0 )
1108  {
1109  yLineCoord = mComposition->paperHeight() * numPages + mComposition->spaceBetweenPages() * ( numPages - 1 );
1110  }
1111  item->setLine( QLineF( alignX, 0, alignX, yLineCoord ) );
1112  item->show();
1113  }
1114  else
1115  {
1116  deleteHAlignSnapItem();
1117  }
1118  if ( alignY != -1 )
1119  {
1120  QGraphicsLineItem* item = vAlignSnapItem();
1121  item->setLine( QLineF( 0, alignY, mComposition->paperWidth(), alignY ) );
1122  item->show();
1123  }
1124  else
1125  {
1126  deleteVAlignSnapItem();
1127  }
1128  return snappedPoint;
1129 }
1130 
1131 QGraphicsLineItem* QgsComposerMouseHandles::hAlignSnapItem()
1132 {
1133  if ( !mHAlignSnapItem )
1134  {
1135  mHAlignSnapItem = new QGraphicsLineItem( 0 );
1136  mHAlignSnapItem->setPen( QPen( QColor( Qt::red ) ) );
1137  scene()->addItem( mHAlignSnapItem );
1138  mHAlignSnapItem->setZValue( 90 );
1139  }
1140  return mHAlignSnapItem;
1141 }
1142 
1143 QGraphicsLineItem* QgsComposerMouseHandles::vAlignSnapItem()
1144 {
1145  if ( !mVAlignSnapItem )
1146  {
1147  mVAlignSnapItem = new QGraphicsLineItem( 0 );
1148  mVAlignSnapItem->setPen( QPen( QColor( Qt::red ) ) );
1149  scene()->addItem( mVAlignSnapItem );
1150  mVAlignSnapItem->setZValue( 90 );
1151  }
1152  return mVAlignSnapItem;
1153 }
1154 
1155 void QgsComposerMouseHandles::deleteHAlignSnapItem()
1156 {
1157  if ( mHAlignSnapItem )
1158  {
1159  scene()->removeItem( mHAlignSnapItem );
1160  delete mHAlignSnapItem;
1161  mHAlignSnapItem = 0;
1162  }
1163 }
1164 
1165 void QgsComposerMouseHandles::deleteVAlignSnapItem()
1166 {
1167  if ( mVAlignSnapItem )
1168  {
1169  scene()->removeItem( mVAlignSnapItem );
1170  delete mVAlignSnapItem;
1171  mVAlignSnapItem = 0;
1172  }
1173 }
1174 
1175 void QgsComposerMouseHandles::deleteAlignItems()
1176 {
1177  deleteHAlignSnapItem();
1178  deleteVAlignSnapItem();
1179 }
1180 
1181 QPointF QgsComposerMouseHandles::alignItem( double& alignX, double& alignY, double unalignedX, double unalignedY )
1182 {
1183  double left = unalignedX;
1184  double right = left + rect().width();
1185  double midH = ( left + right ) / 2.0;
1186  double top = unalignedY;
1187  double bottom = top + rect().height();
1188  double midV = ( top + bottom ) / 2.0;
1189 
1190  QMap<double, const QgsComposerItem* > xAlignCoordinates;
1191  QMap<double, const QgsComposerItem* > yAlignCoordinates;
1192  collectAlignCoordinates( xAlignCoordinates, yAlignCoordinates );
1193 
1194  //find nearest matches x
1195  double xItemLeft = left; //new left coordinate of the item
1196  double xAlignCoord = 0;
1197  double smallestDiffX = DBL_MAX;
1198 
1199  checkNearestItem( left, xAlignCoordinates, smallestDiffX, 0, xItemLeft, xAlignCoord );
1200  checkNearestItem( midH, xAlignCoordinates, smallestDiffX, ( left - right ) / 2.0, xItemLeft, xAlignCoord );
1201  checkNearestItem( right, xAlignCoordinates, smallestDiffX, left - right, xItemLeft, xAlignCoord );
1202 
1203  //find nearest matches y
1204  double yItemTop = top; //new top coordinate of the item
1205  double yAlignCoord = 0;
1206  double smallestDiffY = DBL_MAX;
1207 
1208  checkNearestItem( top, yAlignCoordinates, smallestDiffY, 0, yItemTop, yAlignCoord );
1209  checkNearestItem( midV, yAlignCoordinates, smallestDiffY, ( top - bottom ) / 2.0, yItemTop, yAlignCoord );
1210  checkNearestItem( bottom, yAlignCoordinates, smallestDiffY, top - bottom, yItemTop, yAlignCoord );
1211 
1212  double xCoord = ( smallestDiffX < 5 ) ? xItemLeft : unalignedX;
1213  alignX = ( smallestDiffX < 5 ) ? xAlignCoord : -1;
1214  double yCoord = ( smallestDiffY < 5 ) ? yItemTop : unalignedY;
1215  alignY = ( smallestDiffY < 5 ) ? yAlignCoord : -1;
1216  return QPointF( xCoord, yCoord );
1217 }
1218 
1219 QPointF QgsComposerMouseHandles::alignPos( const QPointF& pos, double& alignX, double& alignY )
1220 {
1221  QMap<double, const QgsComposerItem* > xAlignCoordinates;
1222  QMap<double, const QgsComposerItem* > yAlignCoordinates;
1223  collectAlignCoordinates( xAlignCoordinates, yAlignCoordinates );
1224 
1225  double nearestX = pos.x();
1226  double nearestY = pos.y();
1227  if ( !nearestItem( xAlignCoordinates, pos.x(), nearestX )
1228  || !nearestItem( yAlignCoordinates, pos.y(), nearestY ) )
1229  {
1230  alignX = -1;
1231  alignY = -1;
1232  return pos;
1233  }
1234 
1235  //convert snap tolerance from pixels to mm
1236  double viewScaleFactor = graphicsView()->transform().m11();
1237  double alignThreshold = mComposition->snapTolerance() / viewScaleFactor;
1238 
1239  QPointF result( pos.x(), pos.y() );
1240  if ( fabs( nearestX - pos.x() ) < alignThreshold )
1241  {
1242  result.setX( nearestX );
1243  alignX = nearestX;
1244  }
1245  else
1246  {
1247  alignX = -1;
1248  }
1249 
1250  if ( fabs( nearestY - pos.y() ) < alignThreshold )
1251  {
1252  result.setY( nearestY );
1253  alignY = nearestY;
1254  }
1255  else
1256  {
1257  alignY = -1;
1258  }
1259  return result;
1260 }
1261 
1262 void QgsComposerMouseHandles::collectAlignCoordinates( QMap< double, const QgsComposerItem* >& alignCoordsX, QMap< double, const QgsComposerItem* >& alignCoordsY )
1263 {
1264  alignCoordsX.clear();
1265  alignCoordsY.clear();
1266 
1267  if ( mComposition->smartGuidesEnabled() )
1268  {
1269  QList<QGraphicsItem *> itemList = mComposition->items();
1270  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
1271  for ( ; itemIt != itemList.end(); ++itemIt )
1272  {
1273  const QgsComposerItem* currentItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
1274  //don't snap to selected items, since they're the ones that will be snapping to something else
1275  //also ignore group members - only snap to bounds of group itself
1276  //also ignore hidden items
1277  if ( !currentItem || currentItem->selected() || currentItem->isGroupMember() || !currentItem->isVisible() )
1278  {
1279  continue;
1280  }
1281  QRectF itemRect;
1282  if ( dynamic_cast<const QgsPaperItem *>( *itemIt ) )
1283  {
1284  //if snapping to paper use the paper item's rect rather then the bounding rect,
1285  //since we want to snap to the page edge and not any outlines drawn around the page
1286  itemRect = currentItem->mapRectToScene( currentItem->rect() );
1287  }
1288  else
1289  {
1290  itemRect = currentItem->mapRectToScene( currentItem->rectWithFrame() );
1291  }
1292  alignCoordsX.insert( itemRect.left(), currentItem );
1293  alignCoordsX.insert( itemRect.right(), currentItem );
1294  alignCoordsX.insert( itemRect.center().x(), currentItem );
1295  alignCoordsY.insert( itemRect.top(), currentItem );
1296  alignCoordsY.insert( itemRect.center().y(), currentItem );
1297  alignCoordsY.insert( itemRect.bottom(), currentItem );
1298  }
1299  }
1300 
1301  //arbitrary snap lines
1302  if ( mComposition->alignmentSnap() )
1303  {
1305  for ( ; sIt != mComposition->snapLines()->constEnd(); ++sIt )
1306  {
1307  double x = ( *sIt )->line().x1();
1308  double y = ( *sIt )->line().y1();
1309  if ( qgsDoubleNear( y, 0.0 ) )
1310  {
1311  alignCoordsX.insert( x, 0 );
1312  }
1313  else
1314  {
1315  alignCoordsY.insert( y, 0 );
1316  }
1317  }
1318  }
1319 }
1320 
1321 void QgsComposerMouseHandles::checkNearestItem( double checkCoord, const QMap< double, const QgsComposerItem* >& alignCoords, double& smallestDiff, double itemCoordOffset, double& itemCoord, double& alignCoord )
1322 {
1323  double currentCoord = 0;
1324  if ( !nearestItem( alignCoords, checkCoord, currentCoord ) )
1325  {
1326  return;
1327  }
1328 
1329  double currentDiff = fabs( checkCoord - currentCoord );
1330  //convert snap tolerance from pixels to mm
1331  double viewScaleFactor = graphicsView()->transform().m11();
1332  double alignThreshold = mComposition->snapTolerance() / viewScaleFactor;
1333 
1334  if ( currentDiff < alignThreshold && currentDiff < smallestDiff )
1335  {
1336  itemCoord = currentCoord + itemCoordOffset;
1337  alignCoord = currentCoord;
1338  smallestDiff = currentDiff;
1339  }
1340 }
1341 
1342 bool QgsComposerMouseHandles::nearestItem( const QMap< double, const QgsComposerItem* >& coords, double value, double& nearestValue ) const
1343 {
1344  if ( coords.size() < 1 )
1345  {
1346  return false;
1347  }
1348 
1350  if ( it == coords.constBegin() ) //value smaller than first map value
1351  {
1352  nearestValue = it.key();
1353  return true;
1354  }
1355  else if ( it == coords.constEnd() ) //value larger than last map value
1356  {
1357  --it;
1358  nearestValue = it.key();
1359  return true;
1360  }
1361  else
1362  {
1363  //get smaller value and larger value and return the closer one
1364  double upperVal = it.key();
1365  --it;
1366  double lowerVal = it.key();
1367 
1368  double lowerDiff = value - lowerVal;
1369  double upperDiff = upperVal - value;
1370  if ( lowerDiff < upperDiff )
1371  {
1372  nearestValue = lowerVal;
1373  return true;
1374  }
1375  else
1376  {
1377  nearestValue = upperVal;
1378  return true;
1379  }
1380  }
1381 }
1382 
QRectF mapRectFromItem(const QGraphicsItem *item, const QRectF &rect) const
static void relativeResizeRect(QRectF &rectToResize, const QRectF &boundsBefore, const QRectF &boundsAfter)
Resizes a QRectF relative to a resized bounding rectangle.
QUndoStack * undoStack()
Returns pointer to undo/redo command storage.
qreal x() const
qreal y() const
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override
double paperWidth() const
Width of paper item.
void setStyle(Qt::PenStyle style)
bool boundingBoxesVisible() const
Returns whether selection bounding boxes should be shown in the composition.
void setCursor(const QCursor &)
void setCompositionMode(CompositionMode mode)
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
void selectedItemRotationChanged()
Redraws handles when selected item rotation changes.
virtual bool selected() const
Is selected.
QList< QGraphicsItem * > items() const
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override
MouseAction
Describes the action (move or resize in different directon) to be done during mouse move...
void setAcceptHoverEvents(bool enabled)
const_iterator constBegin() const
bool smartGuidesEnabled() const
qreal rotation() const
const T & at(int i) const
A item that forms part of a map composition.
void save()
bool preventCursorChange() const
QPointF snapPointToGrid(const QPointF &scenePoint) const
Snaps a scene coordinate point to grid.
void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
QWidget * viewport() const
qreal top() const
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override
void savePreviousState()
Saves current item state as previous state.
void selectedItemSizeChanged()
Redraws handles when selected item size changes.
QRectF mapRectToScene(const QRectF &rect) const
void clear()
QGraphicsScene * scene() const
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
double spaceBetweenPages() const
Returns the vertical space between pages in a composer view.
bool alignmentSnap() const
void setStatusMessage(const QString &message)
Sets the status bar message for the composer window.
QString tr(const char *sourceText, const char *disambiguation, int n)
int numPages() const
Returns the number of pages in the composition.
qreal left() const
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:268
void update(const QRectF &rect)
int size() const
void setRect(const QRectF &rectangle)
qreal bottom() const
void drawRect(const QRectF &rectangle)
virtual QRectF boundingRect() const
QTransform transform() const
QPointF pos() const
QTransform & translate(qreal dx, qreal dy)
qreal x() const
qreal y() const
QPointF p2() const
const Key & key() const
void removeItem(QGraphicsItem *item)
void setPen(const QColor &color)
void setPos(const QPointF &pos)
QList< QGraphicsView * > views() const
QRectF normalized() const
qreal m11() const
void setAngle(qreal angle)
QPointF topLeft() const
const_iterator constEnd() const
void setLine(const QLineF &line)
QPointF mapFromScene(const QPointF &point) const
QTransform itemTransform(const QGraphicsItem *other, bool *ok) const
void translate(qreal dx, qreal dy)
void setBrush(const QBrush &brush)
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override
QPointF center() const
QRectF united(const QRectF &rectangle) const
iterator lowerBound(const Key &key)
void selectionChanged()
Sets up listeners to sizeChanged signal for all selected items.
Graphics scene for map printing.
void setPen(const QPen &pen)
iterator end()
qreal right() const
void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override
virtual QRectF rectWithFrame() const
Returns the item's rectangular bounds, including any bleed caused by the item's frame.
void saveAfterState()
Saves current item state as after state.
void restore()
bool isVisible() const
QPointF mapToScene(const QPointF &point) const
qreal width() const
QPointF mapFromItem(const QGraphicsItem *item, const QPointF &point) const
Undo command to undo/redo all composer item related changes.
int snapTolerance() const
Returns the snap tolerance to use when automatically snapping items during movement and resizing to g...
void setWidth(int width)
void resetTransform()
QTransform transform() const
void setX(qreal x)
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:353
void setTransform(const QTransform &matrix, bool combine)
double paperHeight() const
Height of paper item.
qreal angle() const
void setRotation(qreal angle)
qreal height() const
iterator insert(const Key &key, const T &value)
double ANALYSIS_EXPORT min(double x, double y)
Returns the minimum of two doubles or the first argument if both are equal.
const_iterator constEnd() const
const_iterator constBegin() const
QgsComposerMouseHandles(QgsComposition *composition)
void addItem(QGraphicsItem *item)
qreal height() const
QgsComposition::PlotStyle plotStyle() const
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void mousePressEvent(QGraphicsSceneMouseEvent *event) override
bool isGroupMember() const
Returns whether this item is part of a group.
void setZValue(qreal z)
QgsComposerMouseHandles::MouseAction mouseActionForScenePos(const QPointF &sceneCoordPos)
Finds out which mouse move action to choose depending on the scene cursor position.
iterator begin()
int size() const
QPointF scenePos() const
qreal width() const
void push(QUndoCommand *cmd)
QPointF lastScenePos() const
QRectF rect() const
QList< QgsComposerItem * > selectedComposerItems(const bool includeLockedItems=true)
Returns list of selected composer items.
void dirty(bool b)
Definition: qgsproject.cpp:382
QList< QGraphicsLineItem * > * snapLines()
Returns pointer to snap lines collection.