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