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