QGIS API Documentation  2.99.0-Master (a18066b)
qgscomposerview.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposerview.cpp
3  -------------------
4  begin : January 2005
5  copyright : (C) 2005 by Radim Blazek
6  email : [email protected]
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include <QApplication>
19 #include <QMainWindow>
20 #include <QMouseEvent>
21 #include <QKeyEvent>
22 #include <QClipboard>
23 #include <QMimeData>
24 #include <QGridLayout>
25 #include <QScrollBar>
26 #include <QDesktopWidget>
27 
28 #include "qgsapplication.h"
29 #include "qgscomposerview.h"
30 #include "qgscomposerarrow.h"
31 #include "qgscomposerframe.h"
32 #include "qgscomposerpolygon.h"
33 #include "qgscomposerpolyline.h"
34 #include "qgscomposerhtml.h"
35 #include "qgscomposerlabel.h"
36 #include "qgscomposerlegend.h"
37 #include "qgscomposermap.h"
39 #include "qgscomposeritemgroup.h"
40 #include "qgscomposerpicture.h"
41 #include "qgscomposerruler.h"
42 #include "qgscomposerscalebar.h"
43 #include "qgscomposershape.h"
46 #include "qgspaperitem.h"
47 #include "qgsmapcanvas.h"
48 #include "qgscursors.h"
49 #include "qgscomposerutils.h"
50 
51 #define MIN_VIEW_SCALE 0.05
52 #define MAX_VIEW_SCALE 1000.0
53 
54 QgsComposerView::QgsComposerView( QWidget* parent, const char* name, Qt::WindowFlags f )
55  : QGraphicsView( parent )
56  , mCurrentTool( Select )
57  , mPreviousTool( Select )
58  , mRubberBandItem( nullptr )
59  , mRubberBandLineItem( nullptr )
60  , mMoveContentItem( nullptr )
61  , mMarqueeSelect( false )
62  , mMarqueeZoom( false )
63  , mTemporaryZoomStatus( QgsComposerView::Inactive )
64  , mPaintingEnabled( true )
65  , mHorizontalRuler( nullptr )
66  , mVerticalRuler( nullptr )
67  , mMoveContentSearchRadius( 25 )
68  , mNodesItem( nullptr )
69  , mNodesItemIndex( -1 )
70  , mToolPanning( false )
71  , mMousePanning( false )
72  , mKeyPanning( false )
73  , mMovingItemContent( false )
74  , mPreviewEffect( nullptr )
75 {
76  Q_UNUSED( f );
77  Q_UNUSED( name );
78 
79  setResizeAnchor( QGraphicsView::AnchorViewCenter );
80  setMouseTracking( true );
81  viewport()->setMouseTracking( true );
82  setFrameShape( QFrame::NoFrame );
83 
84  mPreviewEffect = new QgsPreviewEffect( this );
85  viewport()->setGraphicsEffect( mPreviewEffect );
86 }
87 
89 {
90  mCurrentTool = t;
91 
92  //update mouse cursor for current tool
93  if ( !composition() )
94  {
95  return;
96  }
97 
98  // do not display points of NodesItem by default
99  mNodesItemIndex = -1;
100  mNodesItem = nullptr;
101  mPolygonItem.reset();
102  mPolylineItem.reset();
103  displayNodes( false );
104  deselectNode();
105 
106  switch ( t )
107  {
109  {
110  //lock cursor to prevent composer items changing it
112  viewport()->setCursor( defaultCursorForTool( Pan ) );
113  break;
114  }
116  {
117  //lock cursor to prevent composer items changing it
119  //set the cursor to zoom in
120  viewport()->setCursor( defaultCursorForTool( Zoom ) );
121  break;
122  }
137  {
138  //using a drawing tool
139  //lock cursor to prevent composer items changing it
141  viewport()->setCursor( defaultCursorForTool( mCurrentTool ) );
142  break;
143  }
144 
146  {
148  viewport()->setCursor( defaultCursorForTool( mCurrentTool ) );
149 
150  displayNodes();
151 
152  break;
153  }
154  default:
155  {
156  //not using pan tool, composer items can change cursor
158  viewport()->setCursor( Qt::ArrowCursor );
159  }
160  }
161 }
162 
163 void QgsComposerView::mousePressEvent( QMouseEvent* e )
164 {
165  if ( !composition() )
166  {
167  return;
168  }
169 
170  if ( mRubberBandItem || mRubberBandLineItem || mKeyPanning || mMousePanning || mToolPanning || mMovingItemContent )
171  {
172  //ignore clicks during certain operations
173  return;
174  }
175 
176  if ( composition()->selectionHandles()->isDragging() || composition()->selectionHandles()->isResizing() )
177  {
178  //ignore clicks while dragging/resizing items
179  return;
180  }
181 
182  QPointF scenePoint = mapToScene( e->pos() );
183  QPointF snappedScenePoint = composition()->snapPointToGrid( scenePoint );
184  mMousePressStartPos = e->pos();
185 
186  if ( e->button() == Qt::RightButton )
187  {
188  //ignore right clicks for now
189  //TODO - show context menu
190  return;
191  }
192  else if ( e->button() == Qt::MidButton )
193  {
194  //pan composer with middle button
195  mMousePanning = true;
196  mMouseLastXY = e->pos();
197  if ( composition() )
198  {
199  //lock cursor to closed hand cursor
201  }
202  viewport()->setCursor( Qt::ClosedHandCursor );
203  return;
204  }
205 
206  switch ( mCurrentTool )
207  {
208  //select/deselect items and pass mouse event further
209  case Select:
210  {
211  //check if we are clicking on a selection handle
212  if ( composition()->selectionHandles()->isVisible() )
213  {
214  //selection handles are being shown, get mouse action for current cursor position
216 
218  {
219  //mouse is over a resize handle, so propagate event onward
220  QGraphicsView::mousePressEvent( e );
221  return;
222  }
223  }
224 
225  QgsComposerItem* selectedItem = nullptr;
226  QgsComposerItem* previousSelectedItem = nullptr;
227 
228  if ( e->modifiers() & Qt::ControlModifier )
229  {
230  //CTRL modifier, so we are trying to select the next item below the current one
231  //first, find currently selected item
232  QList<QgsComposerItem*> selectedItems = composition()->selectedComposerItems();
233  if ( !selectedItems.isEmpty() )
234  {
235  previousSelectedItem = selectedItems.at( 0 );
236  }
237  }
238 
239  if ( previousSelectedItem )
240  {
241  //select highest item just below previously selected item at position of event
242  selectedItem = composition()->composerItemAt( scenePoint, previousSelectedItem, true );
243 
244  //if we didn't find a lower item we'll use the top-most as fall-back
245  //this duplicates mapinfo/illustrator/etc behavior where ctrl-clicks are "cyclic"
246  if ( !selectedItem )
247  {
248  selectedItem = composition()->composerItemAt( scenePoint, true );
249  }
250  }
251  else
252  {
253  //select topmost item at position of event
254  selectedItem = composition()->composerItemAt( scenePoint, true );
255  }
256 
257  if ( !selectedItem )
258  {
259  //not clicking over an item, so start marquee selection
260  startMarqueeSelect( scenePoint );
261  break;
262  }
263 
264  if (( !selectedItem->selected() ) && //keep selection if an already selected item pressed
265  !( e->modifiers() & Qt::ShiftModifier ) ) //keep selection if shift key pressed
266  {
268  }
269 
270  if (( e->modifiers() & Qt::ShiftModifier ) && ( selectedItem->selected() ) )
271  {
272  //SHIFT-clicking a selected item deselects it
273  selectedItem->setSelected( false );
274 
275  //Check if we have any remaining selected items, and if so, update the item panel
276  QList<QgsComposerItem*> selectedItems = composition()->selectedComposerItems();
277  if ( !selectedItems.isEmpty() )
278  {
279  emit selectedItemChanged( selectedItems.at( 0 ) );
280  }
281  }
282  else
283  {
284  selectedItem->setSelected( true );
285  QGraphicsView::mousePressEvent( e );
286  emit selectedItemChanged( selectedItem );
287  }
288  break;
289  }
290 
291  case Zoom:
292  {
293  if ( !( e->modifiers() & Qt::AltModifier ) )
294  {
295  //zoom in action
296  startMarqueeZoom( scenePoint );
297  }
298  else
299  {
300  //zoom out action, so zoom out and recenter on clicked point
301  double scaleFactor = 2;
302  //get current visible part of scene
303  QRect viewportRect( 0, 0, viewport()->width(), viewport()->height() );
304  QgsRectangle visibleRect = QgsRectangle( mapToScene( viewportRect ).boundingRect() );
305 
306  //transform the mouse pos to scene coordinates
307  QPointF scenePoint = mapToScene( e->pos() );
308 
309  visibleRect.scale( scaleFactor, scenePoint.x(), scenePoint.y() );
310  QRectF boundsRect = visibleRect.toRectF();
311 
312  //zoom view to fit desired bounds
313  fitInView( boundsRect, Qt::KeepAspectRatio );
314  }
315  break;
316  }
317 
318  case Pan:
319  {
320  //pan action
321  mToolPanning = true;
322  mMouseLastXY = e->pos();
323  viewport()->setCursor( Qt::ClosedHandCursor );
324  break;
325  }
326 
327  case MoveItemContent:
328  {
329  //get a list of items at clicked position
330  QList<QGraphicsItem *> itemsAtCursorPos = items( e->pos() );
331  if ( itemsAtCursorPos.isEmpty() )
332  {
333  //no items at clicked position
334  return;
335  }
336 
337  //find highest non-locked QgsComposerItem at clicked position
338  //(other graphics items may be higher, e.g., selection handles)
339  QList<QGraphicsItem*>::iterator itemIter = itemsAtCursorPos.begin();
340  for ( ; itemIter != itemsAtCursorPos.end(); ++itemIter )
341  {
342  QgsComposerItem* item = dynamic_cast<QgsComposerItem *>(( *itemIter ) );
343  if ( item && !item->positionLock() )
344  {
345  //we've found the highest QgsComposerItem
346  mMoveContentStartPos = scenePoint;
347  mMoveContentItem = item;
348  mMovingItemContent = true;
349  break;
350  }
351  }
352 
353  //no QgsComposerItem at clicked position
354  return;
355  }
356 
357  case EditNodesItem:
358  {
359  QList<QGraphicsItem *> itemsAtCursorPos = items( e->pos().x(), e->pos().y(),
360  mMoveContentSearchRadius,
361  mMoveContentSearchRadius );
362  if ( itemsAtCursorPos.isEmpty() )
363  return;
364 
365  mNodesItemIndex = -1;
366  mNodesItem = nullptr;
367 
368  QList<QGraphicsItem*>::iterator itemIter = itemsAtCursorPos.begin();
369  for ( ; itemIter != itemsAtCursorPos.end(); ++itemIter )
370  {
371  QgsComposerItem* item = dynamic_cast<QgsComposerItem *>(( *itemIter ) );
372 
373  if ( item && !item->positionLock() )
374  {
375  if (( item->type() == QgsComposerItem::ComposerPolygon
376  || item->type() == QgsComposerItem::ComposerPolyline ) )
377  {
378  QgsComposerNodesItem* itemP = static_cast<QgsComposerNodesItem *>( item );
379  int index = itemP->nodeAtPosition( scenePoint );
380  if ( index != -1 )
381  {
382  mNodesItemIndex = index;
383  mNodesItem = itemP;
384  mMoveContentStartPos = scenePoint;
385  }
386  }
387  }
388 
389  if ( mNodesItemIndex != -1 )
390  {
391  composition()->beginCommand( mNodesItem, tr( "Move item node" ) );
392  setSelectedNode( mNodesItem, mNodesItemIndex );
393  break;
394  }
395  }
396 
397  break;
398  }
399 
400  //create rubber band for adding line items
401  case AddArrow:
402  {
403  mRubberBandStartPos = QPointF( snappedScenePoint.x(), snappedScenePoint.y() );
404  mRubberBandLineItem = new QGraphicsLineItem( snappedScenePoint.x(), snappedScenePoint.y(), snappedScenePoint.x(), snappedScenePoint.y() );
405  mRubberBandLineItem->setPen( QPen( QBrush( QColor( 227, 22, 22, 200 ) ), 0 ) );
406  mRubberBandLineItem->setZValue( 1000 );
407  scene()->addItem( mRubberBandLineItem );
408  scene()->update();
409  break;
410  }
411 
412  //create rubber band for adding rectangular items
413  case AddMap:
414  case AddRectangle:
415  case AddTriangle:
416  case AddEllipse:
417  case AddHtml:
418  case AddPicture:
419  case AddLabel:
420  case AddLegend:
421  case AddTable:
422  case AddAttributeTable:
423  {
424  QTransform t;
425  mRubberBandItem = new QGraphicsRectItem( 0, 0, 0, 0 );
426  mRubberBandItem->setBrush( Qt::NoBrush );
427  mRubberBandItem->setPen( QPen( QBrush( QColor( 227, 22, 22, 200 ) ), 0 ) );
428  mRubberBandStartPos = QPointF( snappedScenePoint.x(), snappedScenePoint.y() );
429  t.translate( snappedScenePoint.x(), snappedScenePoint.y() );
430  mRubberBandItem->setTransform( t );
431  mRubberBandItem->setZValue( 1000 );
432  scene()->addItem( mRubberBandItem );
433  scene()->update();
434  }
435  break;
436 
437  case AddScalebar:
438  if ( composition() )
439  {
440  QgsComposerScaleBar* newScaleBar = new QgsComposerScaleBar( composition() );
441  newScaleBar->setSceneRect( QRectF( snappedScenePoint.x(), snappedScenePoint.y(), 20, 20 ) );
442  composition()->addComposerScaleBar( newScaleBar );
443  QList<const QgsComposerMap*> mapItemList = composition()->composerMapItems();
444  if ( !mapItemList.isEmpty() )
445  {
446  newScaleBar->setComposerMap( mapItemList.at( 0 ) );
447  }
448  newScaleBar->applyDefaultSize(); //4 segments, 1/5 of composer map width
449 
451  newScaleBar->setSelected( true );
452  emit selectedItemChanged( newScaleBar );
453 
454  emit actionFinished();
455  composition()->pushAddRemoveCommand( newScaleBar, tr( "Scale bar added" ) );
456  }
457  break;
458 
459  case AddPolygon:
460  {
461  if ( !mPolygonItem )
462  {
463  mPolygonItem.reset( new QGraphicsPolygonItem() );
464  mPolygonItem->setBrush( Qt::NoBrush );
465  mPolygonItem->setPen( QPen( QBrush( QColor( 227, 22, 22, 200 ) ), 0 ) );
466  mPolygonItem->setZValue( 1000 );
467 
468  scene()->addItem( mPolygonItem.get() );
469  scene()->update();
470  }
471 
472  break;
473  }
474 
475  case AddPolyline:
476  {
477  if ( !mPolylineItem && !mPolygonItem )
478  {
479  mPolygonItem.reset( new QGraphicsPolygonItem() );
480 
481  mPolylineItem.reset( new QGraphicsPathItem() );
482  mPolylineItem->setPen( QPen( QBrush( QColor( 227, 22, 22, 200 ) ), 0 ) );
483  mPolylineItem->setZValue( 1000 );
484  }
485 
486  break;
487  }
488 
489  default:
490  break;
491  }
492 }
493 
494 QCursor QgsComposerView::defaultCursorForTool( Tool currentTool )
495 {
496  switch ( currentTool )
497  {
498  case Select:
499  return Qt::ArrowCursor;
500 
501  case Zoom:
502  {
503  QPixmap myZoomQPixmap = QPixmap(( const char ** )( zoom_in ) );
504  return QCursor( myZoomQPixmap, 7, 7 );
505  }
506 
507  case Pan:
508  return Qt::OpenHandCursor;
509 
510  case MoveItemContent:
511  return Qt::ArrowCursor;
512 
513  case EditNodesItem:
514  return Qt::CrossCursor;
515 
516  case AddArrow:
517  case AddMap:
518  case AddRectangle:
519  case AddTriangle:
520  case AddEllipse:
521  case AddPolygon:
522  case AddPolyline:
523  case AddHtml:
524  case AddLabel:
525  case AddScalebar:
526  case AddLegend:
527  case AddPicture:
528  case AddTable:
529  case AddAttributeTable:
530  {
531  QPixmap myCrosshairQPixmap = QPixmap(( const char ** )( cross_hair_cursor ) );
532  return QCursor( myCrosshairQPixmap, 8, 8 );
533  }
534  }
535  return Qt::ArrowCursor;
536 }
537 
538 void QgsComposerView::addShape( Tool currentTool )
539 {
541 
542  if ( currentTool == AddRectangle )
544  else if ( currentTool == AddTriangle )
546 
547  if ( !mRubberBandItem || ( mRubberBandItem->rect().width() < 0.1 && mRubberBandItem->rect().height() < 0.1 ) )
548  {
549  removeRubberBand();
550  return;
551  }
552  if ( composition() )
553  {
554  QgsComposerShape* composerShape = new QgsComposerShape( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height(), composition() );
555  composerShape->setShapeType( shape );
556  //new shapes use symbol v2 by default
557  composerShape->setUseSymbol( true );
558  composition()->addComposerShape( composerShape );
559  removeRubberBand();
560 
562  composerShape->setSelected( true );
563  emit selectedItemChanged( composerShape );
564 
565  emit actionFinished();
566  composition()->pushAddRemoveCommand( composerShape, tr( "Shape added" ) );
567  }
568 }
569 
571 {
572  if ( mHorizontalRuler )
573  {
574  mHorizontalRuler->setSceneTransform( viewportTransform() );
575  }
576  if ( mVerticalRuler )
577  {
578  mVerticalRuler->setSceneTransform( viewportTransform() );
579  }
580 }
581 
582 void QgsComposerView::removeRubberBand()
583 {
584  if ( mRubberBandItem )
585  {
586  scene()->removeItem( mRubberBandItem );
587  delete mRubberBandItem;
588  mRubberBandItem = nullptr;
589  }
590 }
591 
592 void QgsComposerView::startMarqueeSelect( QPointF & scenePoint )
593 {
594  mMarqueeSelect = true;
595 
596  QTransform t;
597  mRubberBandItem = new QGraphicsRectItem( 0, 0, 0, 0 );
598  mRubberBandItem->setBrush( QBrush( QColor( 224, 178, 76, 63 ) ) );
599  mRubberBandItem->setPen( QPen( QBrush( QColor( 254, 58, 29, 100 ) ), 0, Qt::DotLine ) );
600  mRubberBandStartPos = QPointF( scenePoint.x(), scenePoint.y() );
601  t.translate( scenePoint.x(), scenePoint.y() );
602  mRubberBandItem->setTransform( t );
603  mRubberBandItem->setZValue( 1000 );
604  scene()->addItem( mRubberBandItem );
605  scene()->update();
606 }
607 
608 void QgsComposerView::endMarqueeSelect( QMouseEvent* e )
609 {
610  mMarqueeSelect = false;
611 
612  bool subtractingSelection = false;
613  if ( e->modifiers() & Qt::ShiftModifier )
614  {
615  //shift modifer means adding to selection, nothing required here
616  }
617  else if ( e->modifiers() & Qt::ControlModifier )
618  {
619  //control modifier means subtract from current selection
620  subtractingSelection = true;
621  }
622  else
623  {
624  //not adding to or removing from selection, so clear current selection
626  }
627 
628  if ( !mRubberBandItem || ( mRubberBandItem->rect().width() < 0.1 && mRubberBandItem->rect().height() < 0.1 ) )
629  {
630  //just a click, do nothing
631  removeRubberBand();
632  return;
633  }
634 
635  QRectF boundsRect = QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(),
636  mRubberBandItem->rect().width(), mRubberBandItem->rect().height() );
637 
638  //determine item selection mode, default to intersection
639  Qt::ItemSelectionMode selectionMode = Qt::IntersectsItemShape;
640  if ( e->modifiers() & Qt::AltModifier )
641  {
642  //alt modifier switches to contains selection mode
643  selectionMode = Qt::ContainsItemShape;
644  }
645 
646  //find all items in rubber band
647  QList<QGraphicsItem *> itemList = composition()->items( boundsRect, selectionMode );
648  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
649  for ( ; itemIt != itemList.end(); ++itemIt )
650  {
651  QgsComposerItem* mypItem = dynamic_cast<QgsComposerItem *>( *itemIt );
652  QgsPaperItem* paperItem = dynamic_cast<QgsPaperItem*>( *itemIt );
653  if ( mypItem && !paperItem )
654  {
655  if ( !mypItem->positionLock() )
656  {
657  if ( subtractingSelection )
658  {
659  mypItem->setSelected( false );
660  }
661  else
662  {
663  mypItem->setSelected( true );
664  }
665  }
666  }
667  }
668  removeRubberBand();
669 
670  //update item panel
671  QList<QgsComposerItem*> selectedItemList = composition()->selectedComposerItems();
672  if ( !selectedItemList.isEmpty() )
673  {
674  emit selectedItemChanged( selectedItemList[0] );
675  }
676 }
677 
678 void QgsComposerView::startMarqueeZoom( QPointF & scenePoint )
679 {
680  mMarqueeZoom = true;
681 
682  QTransform t;
683  mRubberBandItem = new QGraphicsRectItem( 0, 0, 0, 0 );
684  mRubberBandItem->setBrush( QBrush( QColor( 70, 50, 255, 25 ) ) );
685  mRubberBandItem->setPen( QPen( QColor( 70, 50, 255, 100 ) ) );
686  mRubberBandStartPos = QPointF( scenePoint.x(), scenePoint.y() );
687  t.translate( scenePoint.x(), scenePoint.y() );
688  mRubberBandItem->setTransform( t );
689  mRubberBandItem->setZValue( 1000 );
690  scene()->addItem( mRubberBandItem );
691  scene()->update();
692 }
693 
694 void QgsComposerView::endMarqueeZoom( QMouseEvent* e )
695 {
696  mMarqueeZoom = false;
697 
698  QRectF boundsRect;
699 
700  if ( !mRubberBandItem || ( mRubberBandItem->rect().width() < 0.1 && mRubberBandItem->rect().height() < 0.1 ) )
701  {
702  //just a click, so zoom to clicked point and recenter
703  double scaleFactor = 0.5;
704  //get current visible part of scene
705  QRect viewportRect( 0, 0, viewport()->width(), viewport()->height() );
706  QgsRectangle visibleRect = QgsRectangle( mapToScene( viewportRect ).boundingRect() );
707 
708  //transform the mouse pos to scene coordinates
709  QPointF scenePoint = mapToScene( e->pos() );
710 
711  visibleRect.scale( scaleFactor, scenePoint.x(), scenePoint.y() );
712  boundsRect = visibleRect.toRectF();
713  }
714  else
715  {
716  //marquee zoom
717  //zoom bounds are size marquee object
718  boundsRect = QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(),
719  mRubberBandItem->rect().width(), mRubberBandItem->rect().height() );
720  }
721 
722  removeRubberBand();
723  //zoom view to fit desired bounds
724  fitInView( boundsRect, Qt::KeepAspectRatio );
725 
726  if ( mTemporaryZoomStatus == QgsComposerView::ActiveUntilMouseRelease )
727  {
728  //user was using the temporary keyboard activated zoom tool
729  //and the control or space key was released before mouse button, so end temporary zoom
730  mTemporaryZoomStatus = QgsComposerView::Inactive;
731  setCurrentTool( mPreviousTool );
732  }
733 }
734 
736 {
737  if ( !composition() )
738  {
739  return;
740  }
741 
742  if ( e->button() != Qt::LeftButton &&
744  {
745  //ignore clicks while dragging/resizing items
746  return;
747  }
748 
749  QPoint mousePressStopPoint = e->pos();
750  int diffX = mousePressStopPoint.x() - mMousePressStartPos.x();
751  int diffY = mousePressStopPoint.y() - mMousePressStartPos.y();
752 
753  //was this just a click? or a click and drag?
754  bool clickOnly = false;
755  if ( qAbs( diffX ) < 2 && qAbs( diffY ) < 2 )
756  {
757  clickOnly = true;
758  }
759 
760  QPointF scenePoint = mapToScene( e->pos() );
761 
762  if ( mMousePanning || mToolPanning )
763  {
764  mMousePanning = false;
765  mToolPanning = false;
766 
767  if ( clickOnly && e->button() == Qt::MidButton )
768  {
769  //middle mouse button click = recenter on point
770 
771  //get current visible part of scene
772  QRect viewportRect( 0, 0, viewport()->width(), viewport()->height() );
773  QgsRectangle visibleRect = QgsRectangle( mapToScene( viewportRect ).boundingRect() );
774  visibleRect.scale( 1, scenePoint.x(), scenePoint.y() );
775  QRectF boundsRect = visibleRect.toRectF();
776 
777  //zoom view to fit desired bounds
778  fitInView( boundsRect, Qt::KeepAspectRatio );
779  }
780 
781  //set new cursor
782  if ( mCurrentTool != Pan )
783  {
784  if ( composition() )
785  {
786  //allow composer items to change cursor
788  }
789  }
790  viewport()->setCursor( defaultCursorForTool( mCurrentTool ) );
791  }
792 
793  if ( e->button() == Qt::RightButton )
794  {
795  switch ( mCurrentTool )
796  {
797  case AddPolygon:
798  {
799  if ( mPolygonItem )
800  {
801  QPolygonF poly = mPolygonItem->polygon();
802 
803  // last (temporary) point is removed
804  poly.remove( poly.count() - 1 );
805  if ( poly.size() >= 3 )
806  {
807  mPolygonItem->setPolygon( poly );
808 
809  // add polygon in composition
810  QgsComposerPolygon *composerPolygon = new QgsComposerPolygon( mPolygonItem->polygon(), composition() );
811  composition()->addComposerPolygon( composerPolygon );
812 
813  // select the polygon
815  composerPolygon->setSelected( true );
816  emit selectedItemChanged( composerPolygon );
817 
818  composition()->pushAddRemoveCommand( composerPolygon, tr( "Polygon added" ) );
819  }
820 
821  // clean
822  scene()->removeItem( mPolygonItem.get() );
823  mPolygonItem.reset();
824  emit actionFinished();
825  }
826  break;
827  }
828 
829  case AddPolyline:
830  {
831  if ( mPolygonItem && mPolylineItem )
832  {
833  // ignore the last point due to release event before doubleClick event
834  QPolygonF poly = mPolygonItem->polygon();
835 
836  // last (temporary) point is removed
837  poly.remove( poly.count() - 1 );
838  if ( poly.size() >= 2 )
839  {
840  mPolygonItem->setPolygon( poly );
841 
842  // add polygon in composition
843  QgsComposerPolyline *composerPolyline = new QgsComposerPolyline( mPolygonItem->polygon(), composition() );
844  composition()->addComposerPolyline( composerPolyline );
845 
846  // select the polygon
848  composerPolyline->setSelected( true );
849  emit selectedItemChanged( composerPolyline );
850 
851  composition()->pushAddRemoveCommand( composerPolyline, tr( "Polyline added" ) );
852  }
853 
854  // clean
855  scene()->removeItem( mPolylineItem.get() );
856  mPolygonItem.reset();
857  mPolylineItem.reset();
858  emit actionFinished();
859  }
860 
861  break;
862  }
863 
864  default:
865  e->ignore();
866  }
867  }
868 
869  //for every other tool, ignore clicks of non-left button
870  if ( e->button() != Qt::LeftButton )
871  {
872  return;
873  }
874 
875  if ( mMarqueeSelect )
876  {
877  endMarqueeSelect( e );
878  return;
879  }
880 
881  switch ( mCurrentTool )
882  {
883  case Select:
884  {
885  QGraphicsView::mouseReleaseEvent( e );
886  break;
887  }
888 
889  case Zoom:
890  {
891  if ( mMarqueeZoom )
892  {
893  endMarqueeZoom( e );
894  }
895  break;
896  }
897 
898  case MoveItemContent:
899  {
900  if ( mMoveContentItem )
901  {
902  //update map preview if composer map
903  QgsComposerMap* composerMap = dynamic_cast<QgsComposerMap *>( mMoveContentItem );
904  if ( composerMap )
905  {
906  composerMap->setOffset( 0, 0 );
907  }
908 
909  double moveX = scenePoint.x() - mMoveContentStartPos.x();
910  double moveY = scenePoint.y() - mMoveContentStartPos.y();
911 
912  composition()->beginCommand( mMoveContentItem, tr( "Move item content" ) );
913  mMoveContentItem->moveContent( -moveX, -moveY );
914  composition()->endCommand();
915  mMoveContentItem = nullptr;
916  mMovingItemContent = false;
917  }
918  break;
919  }
920 
921  case EditNodesItem:
922  {
923  if ( mNodesItemIndex != -1 )
924  {
925  if ( scenePoint != mMoveContentStartPos )
926  composition()->endCommand();
927  else
929  }
930 
931  break;
932  }
933 
934  case AddArrow:
935  if ( !composition() || !mRubberBandLineItem )
936  {
937  scene()->removeItem( mRubberBandLineItem );
938  delete mRubberBandLineItem;
939  mRubberBandLineItem = nullptr;
940  return;
941  }
942  else
943  {
944  QgsComposerArrow* composerArrow = new QgsComposerArrow( mRubberBandLineItem->line().p1(), mRubberBandLineItem->line().p2(), composition() );
945  composition()->addComposerArrow( composerArrow );
946 
948  composerArrow->setSelected( true );
949  emit selectedItemChanged( composerArrow );
950 
951  scene()->removeItem( mRubberBandLineItem );
952  delete mRubberBandLineItem;
953  mRubberBandLineItem = nullptr;
954  emit actionFinished();
955  composition()->pushAddRemoveCommand( composerArrow, tr( "Arrow added" ) );
956  }
957  break;
958 
959  case AddRectangle:
960  case AddTriangle:
961  case AddEllipse:
962  addShape( mCurrentTool );
963  break;
964 
965  case AddPolygon:
966  {
967  if ( mPolygonItem )
968  addPolygonNode( scenePoint );
969 
970  break;
971  }
972 
973  case AddPolyline:
974  {
975  if ( mPolygonItem && mPolylineItem )
976  {
977  addPolygonNode( scenePoint );
978 
979  // rebuild a new qpainter path
980  QPainterPath path;
981  path.addPolygon( mPolygonItem->polygon() );
982  mPolylineItem->setPath( path );
983 
984  // add it to the scene
985  scene()->addItem( mPolylineItem.get() );
986  scene()->update();
987  }
988  break;
989  }
990 
991  case AddMap:
992  if ( !composition() || !mRubberBandItem || ( mRubberBandItem->rect().width() < 0.1 && mRubberBandItem->rect().height() < 0.1 ) )
993  {
994  removeRubberBand();
995  return;
996  }
997  else
998  {
999  QgsComposerMap* composerMap = new QgsComposerMap( composition(), mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height() );
1000  if ( mCanvas )
1001  {
1002  composerMap->zoomToExtent( mCanvas->mapSettings().visibleExtent() );
1003  composerMap->setLayers( mCanvas->mapSettings().layers() );
1004  }
1005 
1006  composition()->addComposerMap( composerMap );
1007 
1009  composerMap->setSelected( true );
1010  emit selectedItemChanged( composerMap );
1011 
1012  removeRubberBand();
1013  emit actionFinished();
1014  composition()->pushAddRemoveCommand( composerMap, tr( "Map added" ) );
1015  }
1016  break;
1017 
1018  case AddPicture:
1019  if ( !composition() || !mRubberBandItem || ( mRubberBandItem->rect().width() < 0.1 && mRubberBandItem->rect().height() < 0.1 ) )
1020  {
1021  removeRubberBand();
1022  return;
1023  }
1024  else
1025  {
1026  QgsComposerPicture* newPicture = new QgsComposerPicture( composition() );
1027  newPicture->setSceneRect( QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height() ) );
1028  composition()->addComposerPicture( newPicture );
1029 
1031  newPicture->setSelected( true );
1032  emit selectedItemChanged( newPicture );
1033 
1034  removeRubberBand();
1035  emit actionFinished();
1036  composition()->pushAddRemoveCommand( newPicture, tr( "Picture added" ) );
1037  }
1038  break;
1039 
1040  case AddLabel:
1041  if ( !composition() || !mRubberBandItem )
1042  {
1043  removeRubberBand();
1044  return;
1045  }
1046  else
1047  {
1048  QgsComposerLabel* newLabelItem = new QgsComposerLabel( composition() );
1049  newLabelItem->setText( tr( "QGIS" ) );
1050  newLabelItem->adjustSizeToText();
1051 
1052  //make sure label size is sufficient to fit text
1053  double labelWidth = qMax( mRubberBandItem->rect().width(), newLabelItem->rect().width() );
1054  double labelHeight = qMax( mRubberBandItem->rect().height(), newLabelItem->rect().height() );
1055  newLabelItem->setSceneRect( QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), labelWidth, labelHeight ) );
1056 
1057  composition()->addComposerLabel( newLabelItem );
1058 
1060  newLabelItem->setSelected( true );
1061  emit selectedItemChanged( newLabelItem );
1062 
1063  removeRubberBand();
1064  emit actionFinished();
1065  composition()->pushAddRemoveCommand( newLabelItem, tr( "Label added" ) );
1066  }
1067  break;
1068 
1069  case AddLegend:
1070  if ( !composition() || !mRubberBandItem )
1071  {
1072  removeRubberBand();
1073  return;
1074  }
1075  else
1076  {
1077  QgsComposerLegend* newLegend = new QgsComposerLegend( composition() );
1078  QList<const QgsComposerMap*> mapItemList = composition()->composerMapItems();
1079  if ( !mapItemList.isEmpty() )
1080  {
1081  newLegend->setComposerMap( mapItemList.at( 0 ) );
1082  }
1083  newLegend->setSceneRect( QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height() ) );
1084  composition()->addComposerLegend( newLegend );
1085  newLegend->updateLegend();
1086 
1088  newLegend->setSelected( true );
1089  emit selectedItemChanged( newLegend );
1090 
1091  removeRubberBand();
1092  emit actionFinished();
1093  composition()->pushAddRemoveCommand( newLegend, tr( "Legend added" ) );
1094  }
1095  break;
1096 
1097  case AddAttributeTable:
1098  if ( !composition() || !mRubberBandItem )
1099  {
1100  removeRubberBand();
1101  return;
1102  }
1103  else
1104  {
1106  QList<const QgsComposerMap*> mapItemList = composition()->composerMapItems();
1107  if ( !mapItemList.isEmpty() )
1108  {
1109  newTable->setComposerMap( mapItemList.at( 0 ) );
1110  }
1112  newTable, composition(), tr( "Attribute table added" ) );
1113  composition()->undoStack()->push( command );
1114  QgsComposerFrame* frame = new QgsComposerFrame( composition(), newTable, mRubberBandItem->transform().dx(),
1115  mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(),
1116  mRubberBandItem->rect().height() );
1117  composition()->beginMultiFrameCommand( newTable, tr( "Attribute table frame added" ) );
1118  newTable->addFrame( frame );
1120 
1122  frame->setSelected( true );
1123  emit selectedItemChanged( frame );
1124 
1125  removeRubberBand();
1126  emit actionFinished();
1127  }
1128  break;
1129 
1130  case AddHtml:
1131  if ( !composition() || !mRubberBandItem || ( mRubberBandItem->rect().width() < 0.1 && mRubberBandItem->rect().height() < 0.1 ) )
1132  {
1133  removeRubberBand();
1134  return;
1135  }
1136  else
1137  {
1138  QgsComposerHtml* composerHtml = new QgsComposerHtml( composition(), true );
1140  composerHtml, composition(), tr( "HTML item added" ) );
1141  composition()->undoStack()->push( command );
1142  QgsComposerFrame* frame = new QgsComposerFrame( composition(), composerHtml, mRubberBandItem->transform().dx(),
1143  mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(),
1144  mRubberBandItem->rect().height() );
1145  composition()->beginMultiFrameCommand( composerHtml, tr( "HTML frame added" ) );
1146  composerHtml->addFrame( frame );
1148 
1150  frame->setSelected( true );
1151  emit selectedItemChanged( frame );
1152 
1153  removeRubberBand();
1154  emit actionFinished();
1155  }
1156  break;
1157  default:
1158  break;
1159  }
1160 }
1161 
1162 void QgsComposerView::mouseMoveEvent( QMouseEvent* e )
1163 {
1164  if ( !composition() )
1165  {
1166  return;
1167  }
1168 
1169  bool shiftModifier = false;
1170  bool altModifier = false;
1171  if ( e->modifiers() & Qt::ShiftModifier )
1172  {
1173  //shift key depressed
1174  shiftModifier = true;
1175  }
1176  if ( e->modifiers() & Qt::AltModifier )
1177  {
1178  //alt key depressed
1179  altModifier = true;
1180  }
1181 
1182  mMouseCurrentXY = e->pos();
1183  //update cursor position in composer status bar
1184  emit cursorPosChanged( mapToScene( e->pos() ) );
1185 
1186  updateRulers();
1187  if ( mHorizontalRuler )
1188  {
1189  mHorizontalRuler->updateMarker( e->posF() );
1190  }
1191  if ( mVerticalRuler )
1192  {
1193  mVerticalRuler->updateMarker( e->posF() );
1194  }
1195 
1196  if ( mToolPanning || mMousePanning || mKeyPanning )
1197  {
1198  //panning, so scroll view
1199  horizontalScrollBar()->setValue( horizontalScrollBar()->value() - ( e->x() - mMouseLastXY.x() ) );
1200  verticalScrollBar()->setValue( verticalScrollBar()->value() - ( e->y() - mMouseLastXY.y() ) );
1201  mMouseLastXY = e->pos();
1202  return;
1203  }
1204  else if (( e->buttons() == Qt::NoButton ) && ( !mPolygonItem ) )
1205  {
1206  if ( mCurrentTool == Select )
1207  {
1208  QGraphicsView::mouseMoveEvent( e );
1209  }
1210  }
1211  else
1212  {
1213  QPointF scenePoint = mapToScene( e->pos() );
1214 
1215  if ( mMarqueeSelect || mMarqueeZoom )
1216  {
1217  updateRubberBandRect( scenePoint );
1218  return;
1219  }
1220 
1221  switch ( mCurrentTool )
1222  {
1223  case Select:
1224  QGraphicsView::mouseMoveEvent( e );
1225  break;
1226 
1227  case AddArrow:
1228  {
1229  updateRubberBandLine( scenePoint, shiftModifier );
1230  break;
1231  }
1232 
1233  case AddMap:
1234  case AddRectangle:
1235  case AddTriangle:
1236  case AddEllipse:
1237  case AddHtml:
1238  case AddPicture:
1239  case AddLabel:
1240  case AddLegend:
1241  case AddTable:
1242  case AddAttributeTable:
1243  //adjust rubber band item
1244  {
1245  updateRubberBandRect( scenePoint, shiftModifier, altModifier );
1246  break;
1247  }
1248 
1249  case AddPolygon:
1250  {
1251  if ( mPolygonItem )
1252  movePolygonNode( scenePoint, shiftModifier );
1253 
1254  break;
1255  }
1256 
1257  case AddPolyline:
1258  {
1259  if ( mPolygonItem && mPolylineItem )
1260  {
1261  movePolygonNode( scenePoint, shiftModifier );
1262 
1263  // rebuild a new qpainter path
1264  QPainterPath path;
1265  path.addPolygon( mPolygonItem->polygon() );
1266  mPolylineItem->setPath( path );
1267  }
1268 
1269  break;
1270  }
1271 
1272  case MoveItemContent:
1273  {
1274  //update map preview if composer map
1275  QgsComposerMap* composerMap = dynamic_cast<QgsComposerMap *>( mMoveContentItem );
1276  if ( composerMap )
1277  {
1278  composerMap->setOffset( scenePoint.x() - mMoveContentStartPos.x(), scenePoint.y() - mMoveContentStartPos.y() );
1279  composerMap->update();
1280  }
1281  break;
1282  }
1283 
1284  case EditNodesItem:
1285  {
1286  if ( mNodesItemIndex != -1 )
1287  {
1288  QPointF scenePoint = mapToScene( e->pos() );
1289  mNodesItem->moveNode( mNodesItemIndex, scenePoint );
1290  scene()->update();
1291  }
1292 
1293  break;
1294  }
1295 
1296  default:
1297  break;
1298  }
1299  }
1300 }
1301 
1302 void QgsComposerView::updateRubberBandRect( QPointF & pos, const bool constrainSquare, const bool fromCenter )
1303 {
1304  if ( !mRubberBandItem )
1305  {
1306  return;
1307  }
1308 
1309  double x = 0;
1310  double y = 0;
1311  double width = 0;
1312  double height = 0;
1313 
1314  double dx = pos.x() - mRubberBandStartPos.x();
1315  double dy = pos.y() - mRubberBandStartPos.y();
1316 
1317  if ( constrainSquare )
1318  {
1319  if ( fabs( dx ) > fabs( dy ) )
1320  {
1321  width = fabs( dx );
1322  height = width;
1323  }
1324  else
1325  {
1326  height = fabs( dy );
1327  width = height;
1328  }
1329 
1330  x = mRubberBandStartPos.x() - (( dx < 0 ) ? width : 0 );
1331  y = mRubberBandStartPos.y() - (( dy < 0 ) ? height : 0 );
1332  }
1333  else
1334  {
1335  //not constraining
1336  if ( dx < 0 )
1337  {
1338  x = pos.x();
1339  width = -dx;
1340  }
1341  else
1342  {
1343  x = mRubberBandStartPos.x();
1344  width = dx;
1345  }
1346 
1347  if ( dy < 0 )
1348  {
1349  y = pos.y();
1350  height = -dy;
1351  }
1352  else
1353  {
1354  y = mRubberBandStartPos.y();
1355  height = dy;
1356  }
1357  }
1358 
1359  if ( fromCenter )
1360  {
1361  x = mRubberBandStartPos.x() - width;
1362  y = mRubberBandStartPos.y() - height;
1363  width *= 2.0;
1364  height *= 2.0;
1365  }
1366 
1367  mRubberBandItem->setRect( 0, 0, width, height );
1368  QTransform t;
1369  t.translate( x, y );
1370  mRubberBandItem->setTransform( t );
1371 }
1372 
1373 void QgsComposerView::updateRubberBandLine( QPointF pos, const bool constrainAngles )
1374 {
1375  if ( !mRubberBandLineItem )
1376  {
1377  return;
1378  }
1379 
1380  //snap to grid
1381  QPointF snappedScenePoint = composition()->snapPointToGrid( pos );
1382 
1383  QLineF newLine = QLineF( mRubberBandStartPos, snappedScenePoint );
1384 
1385  if ( constrainAngles )
1386  {
1387  //movement is contrained to 45 degree angles
1388  double angle = QgsComposerUtils::snappedAngle( newLine.angle() );
1389  newLine.setAngle( angle );
1390  }
1391 
1392  mRubberBandLineItem->setLine( newLine );
1393 }
1394 
1396 {
1397  QPointF scenePoint = mapToScene( e->pos() );
1398 
1399  switch ( mCurrentTool )
1400  {
1401  case EditNodesItem:
1402  {
1403  // erase status previously set by the mousePressEvent method
1404  if ( mNodesItemIndex != -1 )
1405  {
1406  mNodesItem = nullptr;
1407  mNodesItemIndex = -1;
1408  deselectNode();
1409  }
1410 
1411  // search items in composer
1412  QList<QGraphicsItem *> itemsAtCursorPos = items( e->pos().x(), e->pos().y(),
1413  mMoveContentSearchRadius,
1414  mMoveContentSearchRadius );
1415  if ( itemsAtCursorPos.isEmpty() )
1416  return;
1417 
1418  bool rc = false;
1419  QList<QGraphicsItem*>::iterator itemIter = itemsAtCursorPos.begin();
1420  for ( ; itemIter != itemsAtCursorPos.end(); ++itemIter )
1421  {
1422  QgsComposerItem* item = dynamic_cast<QgsComposerItem *>(( *itemIter ) );
1423 
1424  if ( item && !item->positionLock() )
1425  {
1426  if (( item->type() == QgsComposerItem::ComposerPolygon
1427  || item->type() == QgsComposerItem::ComposerPolyline ) )
1428  {
1429  QgsComposerNodesItem* itemP = dynamic_cast<QgsComposerNodesItem *>( item );
1430 
1431  composition()->beginCommand( itemP, tr( "Add item node" ) );
1432  rc = itemP->addNode( scenePoint );
1433 
1434  if ( rc )
1435  {
1436  composition()->endCommand();
1437  mNodesItem = itemP;
1438  mNodesItemIndex = mNodesItem->nodeAtPosition( scenePoint );
1439  }
1440  else
1442  }
1443  }
1444 
1445  if ( rc )
1446  break;
1447  }
1448 
1449  if ( rc )
1450  {
1451  setSelectedNode( mNodesItem, mNodesItemIndex );
1452  scene()->update();
1453  }
1454 
1455  break;
1456  }
1457 
1458  default:
1459  break;
1460  }
1461 }
1462 
1464 {
1465  if ( !composition() )
1466  {
1467  return;
1468  }
1469 
1470  QList<QgsComposerItem*> composerItemList = composition()->selectedComposerItems();
1471  QList<QgsComposerItem*>::iterator itemIt = composerItemList.begin();
1472 
1473  QDomDocument doc;
1474  QDomElement documentElement = doc.createElement( QStringLiteral( "ComposerItemClipboard" ) );
1475  for ( ; itemIt != composerItemList.end(); ++itemIt )
1476  {
1477  // copy each item in a group
1478  QgsComposerItemGroup* itemGroup = dynamic_cast<QgsComposerItemGroup*>( *itemIt );
1479  if ( itemGroup && composition() )
1480  {
1481  QSet<QgsComposerItem*> groupedItems = itemGroup->items();
1482  QSet<QgsComposerItem*>::iterator it = groupedItems.begin();
1483  for ( ; it != groupedItems.end(); ++it )
1484  {
1485  ( *it )->writeXml( documentElement, doc );
1486  }
1487  }
1488  ( *itemIt )->writeXml( documentElement, doc );
1489  if ( mode == ClipboardModeCut )
1490  {
1491  composition()->removeComposerItem( *itemIt );
1492  }
1493  }
1494  doc.appendChild( documentElement );
1495 
1496  //if it's a copy, we have to remove the UUIDs since we don't want any duplicate UUID
1497  if ( mode == ClipboardModeCopy )
1498  {
1499  // remove all uuid attributes
1500  QDomNodeList composerItemsNodes = doc.elementsByTagName( QStringLiteral( "ComposerItem" ) );
1501  for ( int i = 0; i < composerItemsNodes.count(); ++i )
1502  {
1503  QDomNode composerItemNode = composerItemsNodes.at( i );
1504  if ( composerItemNode.isElement() )
1505  {
1506  composerItemNode.toElement().removeAttribute( QStringLiteral( "uuid" ) );
1507  }
1508  }
1509  }
1510 
1511  QMimeData *mimeData = new QMimeData;
1512  mimeData->setData( QStringLiteral( "text/xml" ), doc.toByteArray() );
1513  QClipboard *clipboard = QApplication::clipboard();
1514  clipboard->setMimeData( mimeData );
1515 }
1516 
1518 {
1519  if ( !composition() )
1520  {
1521  return;
1522  }
1523 
1524  QDomDocument doc;
1525  QClipboard *clipboard = QApplication::clipboard();
1526  if ( doc.setContent( clipboard->mimeData()->data( QStringLiteral( "text/xml" ) ) ) )
1527  {
1528  QDomElement docElem = doc.documentElement();
1529  if ( docElem.tagName() == QLatin1String( "ComposerItemClipboard" ) )
1530  {
1531  if ( composition() )
1532  {
1533  QPointF pt;
1535  {
1536  // place items at cursor position
1537  pt = mapToScene( mapFromGlobal( QCursor::pos() ) );
1538  }
1539  else
1540  {
1541  // place items in center of viewport
1542  pt = mapToScene( viewport()->rect().center() );
1543  }
1544  bool pasteInPlace = ( mode == PasteModeInPlace );
1545  composition()->addItemsFromXml( docElem, doc, nullptr, true, &pt, pasteInPlace );
1546  }
1547  }
1548  }
1549 
1550  //switch back to select tool so that pasted items can be moved/resized (#8958)
1552 }
1553 
1555 {
1556  if ( !composition() )
1557  {
1558  return;
1559  }
1560 
1561  if ( mCurrentTool == QgsComposerView::EditNodesItem )
1562  {
1563  if ( mNodesItemIndex != -1 )
1564  {
1565  composition()->beginCommand( mNodesItem, tr( "Remove item node" ) );
1566  if ( mNodesItem->removeNode( mNodesItemIndex ) )
1567  {
1568  composition()->endCommand();
1569  if ( mNodesItem->nodesSize() > 0 )
1570  {
1571  mNodesItemIndex = mNodesItem->selectedNode();
1572  // setSelectedNode( mNodesItem, mNodesItemIndex );
1573  }
1574  else
1575  {
1576  mNodesItemIndex = -1;
1577  mNodesItem = nullptr;
1578  }
1579  scene()->update();
1580  }
1581  else
1582  {
1584  }
1585  }
1586  }
1587  else
1588  {
1589  QList<QgsComposerItem*> composerItemList = composition()->selectedComposerItems();
1590  QList<QgsComposerItem*>::iterator itemIt = composerItemList.begin();
1591 
1592  //delete selected items
1593  for ( ; itemIt != composerItemList.end(); ++itemIt )
1594  {
1595  if ( composition() )
1596  {
1597  composition()->removeComposerItem( *itemIt );
1598  }
1599  }
1600  }
1601 }
1602 
1604 {
1605  if ( !composition() )
1606  {
1607  return;
1608  }
1609 
1610  //select all items in composer
1611  QList<QGraphicsItem *> itemList = composition()->items();
1612  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
1613  for ( ; itemIt != itemList.end(); ++itemIt )
1614  {
1615  QgsComposerItem* mypItem = dynamic_cast<QgsComposerItem *>( *itemIt );
1616  QgsPaperItem* paperItem = dynamic_cast<QgsPaperItem*>( *itemIt );
1617  if ( mypItem && !paperItem )
1618  {
1619  if ( !mypItem->positionLock() )
1620  {
1621  mypItem->setSelected( true );
1622  }
1623  else
1624  {
1625  //deselect all locked items
1626  mypItem->setSelected( false );
1627  }
1628  emit selectedItemChanged( mypItem );
1629  }
1630  }
1631 }
1632 
1634 {
1635  if ( !composition() )
1636  {
1637  return;
1638  }
1639 
1641 }
1642 
1644 {
1645  if ( !composition() )
1646  {
1647  return;
1648  }
1649 
1650  //check all items in composer
1651  QList<QGraphicsItem *> itemList = composition()->items();
1652  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
1653  for ( ; itemIt != itemList.end(); ++itemIt )
1654  {
1655  QgsComposerItem* mypItem = dynamic_cast<QgsComposerItem *>( *itemIt );
1656  QgsPaperItem* paperItem = dynamic_cast<QgsPaperItem*>( *itemIt );
1657  if ( mypItem && !paperItem )
1658  {
1659  //flip selected state for items (and deselect any locked items)
1660  if ( mypItem->selected() || mypItem->positionLock() )
1661  {
1662 
1663  mypItem->setSelected( false );
1664  }
1665  else
1666  {
1667  mypItem->setSelected( true );
1668  emit selectedItemChanged( mypItem );
1669  }
1670  }
1671  }
1672 }
1673 
1674 void QgsComposerView::keyPressEvent( QKeyEvent * e )
1675 {
1676  if ( !composition() )
1677  {
1678  return;
1679  }
1680 
1681  if ( mKeyPanning || mMousePanning || mToolPanning || mMovingItemContent ||
1682  composition()->selectionHandles()->isDragging() || composition()->selectionHandles()->isResizing() )
1683  {
1684  return;
1685  }
1686 
1687  if ( mTemporaryZoomStatus != QgsComposerView::Inactive )
1688  {
1689  //temporary keyboard based zoom is active
1690  if ( e->isAutoRepeat() )
1691  {
1692  return;
1693  }
1694 
1695  //respond to changes in ctrl key status
1696  if ( !( e->modifiers() & Qt::ControlModifier ) && !mMarqueeZoom )
1697  {
1698  //space pressed, but control key was released, end of temporary zoom tool
1699  mTemporaryZoomStatus = QgsComposerView::Inactive;
1700  setCurrentTool( mPreviousTool );
1701  }
1702  else if ( !( e->modifiers() & Qt::ControlModifier ) && mMarqueeZoom )
1703  {
1704  //control key released, but user is mid-way through a marquee zoom
1705  //so end temporary zoom when user releases the mouse button
1706  mTemporaryZoomStatus = QgsComposerView::ActiveUntilMouseRelease;
1707  }
1708  else
1709  {
1710  //both control and space pressed
1711  //set cursor to zoom in/out depending on shift key status
1712  QPixmap myZoomQPixmap = QPixmap(( const char ** )(( e->modifiers() & Qt::AltModifier ) ? zoom_out : zoom_in ) );
1713  QCursor zoomCursor = QCursor( myZoomQPixmap, 7, 7 );
1714  viewport()->setCursor( zoomCursor );
1715  }
1716  return;
1717  }
1718 
1719  if ( mCurrentTool != QgsComposerView::Zoom && ( mRubberBandItem || mRubberBandLineItem ) )
1720  {
1721  //disable keystrokes while drawing a box
1722  return;
1723  }
1724 
1725  if ( e->key() == Qt::Key_Space && ! e->isAutoRepeat() )
1726  {
1727  if ( !( e->modifiers() & Qt::ControlModifier ) )
1728  {
1729  // Pan composer with space bar
1730  mKeyPanning = true;
1731  mMouseLastXY = mMouseCurrentXY;
1732  if ( composition() )
1733  {
1734  //prevent cursor changes while panning
1736  }
1737  viewport()->setCursor( Qt::ClosedHandCursor );
1738  return;
1739  }
1740  else
1741  {
1742  //ctrl+space pressed, so switch to temporary keyboard based zoom tool
1743  mTemporaryZoomStatus = QgsComposerView::Active;
1744  mPreviousTool = mCurrentTool;
1745  setCurrentTool( Zoom );
1746  //set cursor to zoom in/out depending on alt key status
1747  QPixmap myZoomQPixmap = QPixmap(( const char ** )(( e->modifiers() & Qt::AltModifier ) ? zoom_out : zoom_in ) );
1748  QCursor zoomCursor = QCursor( myZoomQPixmap, 7, 7 );
1749  viewport()->setCursor( zoomCursor );
1750  return;
1751  }
1752  }
1753 
1754  if ( mCurrentTool == QgsComposerView::Zoom )
1755  {
1756  //using the zoom tool, respond to changes in alt key status and update mouse cursor accordingly
1757  if ( ! e->isAutoRepeat() )
1758  {
1759  QPixmap myZoomQPixmap = QPixmap(( const char ** )(( e->modifiers() & Qt::AltModifier ) ? zoom_out : zoom_in ) );
1760  QCursor zoomCursor = QCursor( myZoomQPixmap, 7, 7 );
1761  viewport()->setCursor( zoomCursor );
1762  }
1763  return;
1764  }
1765 
1766  QList<QgsComposerItem*> composerItemList = composition()->selectedComposerItems();
1767  QList<QgsComposerItem*>::iterator itemIt = composerItemList.begin();
1768 
1769  // increment used for cursor key item movement
1770  double increment = 1.0;
1771  if ( e->modifiers() & Qt::ShiftModifier )
1772  {
1773  //holding shift while pressing cursor keys results in a big step
1774  increment = 10.0;
1775  }
1776  else if ( e->modifiers() & Qt::AltModifier )
1777  {
1778  //holding alt while pressing cursor keys results in a 1 pixel step
1779  double viewScale = transform().m11();
1780  if ( viewScale > 0 )
1781  {
1782  increment = 1 / viewScale;
1783  }
1784  }
1785 
1786  if ( e->key() == Qt::Key_Left )
1787  {
1788  if ( mCurrentTool == EditNodesItem )
1789  {
1790  if ( mNodesItemIndex != -1 )
1791  {
1792  QPointF currentPos;
1793 
1794  if ( mNodesItem->nodePosition( mNodesItemIndex, currentPos ) )
1795  {
1796  currentPos.setX( currentPos.x() - increment );
1797 
1798  composition()->beginCommand( mNodesItem, tr( "Move item node" ) );
1799  mNodesItem->moveNode( mNodesItemIndex, currentPos );
1800  composition()->endCommand();
1801 
1802  scene()->update();
1803  }
1804  }
1805  }
1806  else
1807  {
1808  for ( ; itemIt != composerItemList.end(); ++itemIt )
1809  {
1810  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
1811  ( *itemIt )->move( -1 * increment, 0.0 );
1812  ( *itemIt )->endCommand();
1813  }
1814  }
1815  }
1816  else if ( e->key() == Qt::Key_Right )
1817  {
1818  if ( mCurrentTool == EditNodesItem )
1819  {
1820  if ( mNodesItemIndex != -1 )
1821  {
1822  QPointF currentPos;
1823 
1824  if ( mNodesItem->nodePosition( mNodesItemIndex, currentPos ) )
1825  {
1826  currentPos.setX( currentPos.x() + increment );
1827 
1828  composition()->beginCommand( mNodesItem, tr( "Move item node" ) );
1829  mNodesItem->moveNode( mNodesItemIndex, currentPos );
1830  composition()->endCommand();
1831 
1832  scene()->update();
1833  }
1834  }
1835  }
1836  else
1837  {
1838  for ( ; itemIt != composerItemList.end(); ++itemIt )
1839  {
1840  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
1841  ( *itemIt )->move( increment, 0.0 );
1842  ( *itemIt )->endCommand();
1843  }
1844  }
1845  }
1846  else if ( e->key() == Qt::Key_Down )
1847  {
1848  if ( mCurrentTool == EditNodesItem )
1849  {
1850  if ( mNodesItemIndex != -1 )
1851  {
1852  QPointF currentPos;
1853 
1854  if ( mNodesItem->nodePosition( mNodesItemIndex, currentPos ) )
1855  {
1856  currentPos.setY( currentPos.y() + increment );
1857 
1858  composition()->beginCommand( mNodesItem, tr( "Move item node" ) );
1859  mNodesItem->moveNode( mNodesItemIndex, currentPos );
1860  composition()->endCommand();
1861 
1862  scene()->update();
1863  }
1864  }
1865  }
1866  else
1867  {
1868  for ( ; itemIt != composerItemList.end(); ++itemIt )
1869  {
1870  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
1871  ( *itemIt )->move( 0.0, increment );
1872  ( *itemIt )->endCommand();
1873  }
1874  }
1875  }
1876  else if ( e->key() == Qt::Key_Up )
1877  {
1878  if ( mCurrentTool == EditNodesItem )
1879  {
1880  if ( mNodesItemIndex != -1 )
1881  {
1882  QPointF currentPos;
1883 
1884  if ( mNodesItem->nodePosition( mNodesItemIndex, currentPos ) )
1885  {
1886  currentPos.setY( currentPos.y() - increment );
1887 
1888  composition()->beginCommand( mNodesItem, tr( "Move item node" ) );
1889  mNodesItem->moveNode( mNodesItemIndex, currentPos );
1890  composition()->endCommand();
1891 
1892  scene()->update();
1893  }
1894  }
1895  }
1896  else
1897  {
1898  for ( ; itemIt != composerItemList.end(); ++itemIt )
1899  {
1900  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
1901  ( *itemIt )->move( 0.0, -1 * increment );
1902  ( *itemIt )->endCommand();
1903  }
1904  }
1905  }
1906 }
1907 
1909 {
1910  if ( e->key() == Qt::Key_Space && !e->isAutoRepeat() && mKeyPanning )
1911  {
1912  //end of panning with space key
1913  mKeyPanning = false;
1914 
1915  //reset cursor
1916  if ( mCurrentTool != Pan )
1917  {
1918  if ( composition() )
1919  {
1920  //allow cursor changes again
1921  composition()->setPreventCursorChange( false );
1922  }
1923  }
1924  viewport()->setCursor( defaultCursorForTool( mCurrentTool ) );
1925  return;
1926  }
1927  else if ( e->key() == Qt::Key_Space && !e->isAutoRepeat() && mTemporaryZoomStatus != QgsComposerView::Inactive )
1928  {
1929  //temporary keyboard-based zoom tool is active and space key has been released
1930  if ( mMarqueeZoom )
1931  {
1932  //currently in the middle of a marquee operation, so don't switch tool back immediately
1933  //instead, wait until mouse button has been released before switching tool back
1934  mTemporaryZoomStatus = QgsComposerView::ActiveUntilMouseRelease;
1935  }
1936  else
1937  {
1938  //switch tool back
1939  mTemporaryZoomStatus = QgsComposerView::Inactive;
1940  setCurrentTool( mPreviousTool );
1941  }
1942  }
1943  else if ( mCurrentTool == QgsComposerView::Zoom )
1944  {
1945  //if zoom tool is active, respond to changes in the alt key status and update cursor accordingly
1946  if ( ! e->isAutoRepeat() )
1947  {
1948  QPixmap myZoomQPixmap = QPixmap(( const char ** )(( e->modifiers() & Qt::AltModifier ) ? zoom_out : zoom_in ) );
1949  QCursor zoomCursor = QCursor( myZoomQPixmap, 7, 7 );
1950  viewport()->setCursor( zoomCursor );
1951  }
1952  return;
1953  }
1954 }
1955 
1956 void QgsComposerView::wheelEvent( QWheelEvent* event )
1957 {
1958  if ( mRubberBandItem || mRubberBandLineItem )
1959  {
1960  //ignore wheel events while marquee operations are active (e.g., creating new item)
1961  return;
1962  }
1963 
1964  if ( composition()->selectionHandles()->isDragging() || composition()->selectionHandles()->isResizing() )
1965  {
1966  //ignore wheel events while dragging/resizing items
1967  return;
1968  }
1969 
1970  if ( currentTool() == MoveItemContent )
1971  {
1972  //move item content tool, so scroll events get handled by the selected composer item
1973 
1974  QPointF scenePoint = mapToScene( event->pos() );
1975  //select topmost item at position of event
1976  QgsComposerItem* item = composition()->composerItemAt( scenePoint, true );
1977  if ( item )
1978  {
1979  if ( item->isSelected() )
1980  {
1981  QSettings settings;
1982  //read zoom mode
1983  QgsComposerItem::ZoomMode zoomMode = ( QgsComposerItem::ZoomMode )settings.value( QStringLiteral( "/qgis/wheel_action" ), 2 ).toInt();
1984  if ( zoomMode == QgsComposerItem::NoZoom )
1985  {
1986  //do nothing
1987  return;
1988  }
1989 
1990  double zoomFactor = settings.value( QStringLiteral( "/qgis/zoom_factor" ), 2.0 ).toDouble();
1991  if ( event->modifiers() & Qt::ControlModifier )
1992  {
1993  //holding ctrl while wheel zooming results in a finer zoom
1994  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
1995  }
1996  zoomFactor = event->delta() > 0 ? zoomFactor : 1 / zoomFactor;
1997 
1998  QPointF itemPoint = item->mapFromScene( scenePoint );
1999  item->beginCommand( tr( "Zoom item content" ), QgsComposerMergeCommand::ItemZoomContent );
2000  item->zoomContent( zoomFactor, itemPoint, zoomMode );
2001  item->endCommand();
2002  }
2003  }
2004  }
2005  else
2006  {
2007  //not using move item content tool, so zoom whole composition
2008  wheelZoom( event );
2009  }
2010 }
2011 
2012 void QgsComposerView::wheelZoom( QWheelEvent * event )
2013 {
2014  //get mouse wheel zoom behavior settings
2015  QSettings mySettings;
2016  double zoomFactor = mySettings.value( QStringLiteral( "/qgis/zoom_factor" ), 2 ).toDouble();
2017 
2018  if ( event->modifiers() & Qt::ControlModifier )
2019  {
2020  //holding ctrl while wheel zooming results in a finer zoom
2021  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 10.0;
2022  }
2023 
2024  //calculate zoom scale factor
2025  bool zoomIn = event->delta() > 0;
2026  double scaleFactor = ( zoomIn ? 1 / zoomFactor : zoomFactor );
2027 
2028  //get current visible part of scene
2029  QRect viewportRect( 0, 0, viewport()->width(), viewport()->height() );
2030  QgsRectangle visibleRect = QgsRectangle( mapToScene( viewportRect ).boundingRect() );
2031 
2032  //transform the mouse pos to scene coordinates
2033  QPointF scenePoint = mapToScene( event->pos() );
2034 
2035  //adjust view center
2036  QgsPoint oldCenter( visibleRect.center() );
2037  QgsPoint newCenter( scenePoint.x() + (( oldCenter.x() - scenePoint.x() ) * scaleFactor ),
2038  scenePoint.y() + (( oldCenter.y() - scenePoint.y() ) * scaleFactor ) );
2039  centerOn( newCenter.x(), newCenter.y() );
2040 
2041  //zoom composition
2042  if ( zoomIn )
2043  {
2044  scaleSafe( zoomFactor );
2045  }
2046  else
2047  {
2048  scaleSafe( 1 / zoomFactor );
2049  }
2050 
2051  //update composition for new zoom
2052  emit zoomLevelChanged();
2053  updateRulers();
2054  update();
2055  //redraw cached map items
2056  QList<QGraphicsItem *> itemList = composition()->items();
2057  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
2058  for ( ; itemIt != itemList.end(); ++itemIt )
2059  {
2060  QgsComposerMap* mypItem = dynamic_cast<QgsComposerMap *>( *itemIt );
2061  if (( mypItem ) && ( mypItem->previewMode() == QgsComposerMap::Render ) )
2062  {
2063  mypItem->updateCachedImage();
2064  }
2065  }
2066 }
2067 
2068 void QgsComposerView::setZoomLevel( double zoomLevel )
2069 {
2070  double dpi = QgsApplication::desktop()->logicalDpiX();
2071  //monitor dpi is not always correct - so make sure the value is sane
2072  if (( dpi < 60 ) || ( dpi > 250 ) )
2073  dpi = 72;
2074 
2075  //desired pixel width for 1mm on screen
2076  double scale = qBound( MIN_VIEW_SCALE, zoomLevel * dpi / 25.4, MAX_VIEW_SCALE );
2077  setTransform( QTransform::fromScale( scale, scale ) );
2078 
2079  updateRulers();
2080  update();
2081  emit zoomLevelChanged();
2082 }
2083 
2084 void QgsComposerView::scaleSafe( double scale )
2085 {
2086  double currentScale = transform().m11();
2087  scale *= currentScale;
2088  scale = qBound( MIN_VIEW_SCALE, scale, MAX_VIEW_SCALE );
2089  setTransform( QTransform::fromScale( scale, scale ) );
2090 }
2091 
2093 {
2094  if ( !mPreviewEffect )
2095  {
2096  return;
2097  }
2098 
2099  mPreviewEffect->setEnabled( enabled );
2100 }
2101 
2103 {
2104  if ( !mPreviewEffect )
2105  {
2106  return;
2107  }
2108 
2109  mPreviewEffect->setMode( mode );
2110 }
2111 
2113 {
2114  mCanvas = canvas;
2115 }
2116 
2118 {
2119  return mCanvas;
2120 }
2121 
2122 void QgsComposerView::paintEvent( QPaintEvent* event )
2123 {
2124  if ( mPaintingEnabled )
2125  {
2126  QGraphicsView::paintEvent( event );
2127  event->accept();
2128  }
2129  else
2130  {
2131  event->ignore();
2132  }
2133 }
2134 
2135 void QgsComposerView::hideEvent( QHideEvent* e )
2136 {
2137  emit composerViewHide( this );
2138  e->ignore();
2139 }
2140 
2141 void QgsComposerView::showEvent( QShowEvent* e )
2142 {
2143  emit composerViewShow( this );
2144  e->ignore();
2145 }
2146 
2147 void QgsComposerView::resizeEvent( QResizeEvent* event )
2148 {
2149  QGraphicsView::resizeEvent( event );
2150  emit zoomLevelChanged();
2151  updateRulers();
2152 }
2153 
2155 {
2156  QGraphicsView::scrollContentsBy( dx, dy );
2157  updateRulers();
2158 }
2159 
2161 {
2162  setScene( c );
2163  if ( mHorizontalRuler )
2164  {
2165  mHorizontalRuler->setComposition( c );
2166  }
2167  if ( mVerticalRuler )
2168  {
2169  mVerticalRuler->setComposition( c );
2170  }
2171 
2172  //emit compositionSet, so that composer windows can update for the new composition
2173  emit compositionSet( c );
2174 }
2175 
2177 {
2178  if ( scene() )
2179  {
2180  QgsComposition* c = dynamic_cast<QgsComposition *>( scene() );
2181  if ( c )
2182  {
2183  return c;
2184  }
2185  }
2186  return nullptr;
2187 }
2188 
2190 {
2191  if ( !composition() )
2192  {
2193  return;
2194  }
2195 
2196  //group selected items
2197  QList<QgsComposerItem*> selectionList = composition()->selectedComposerItems();
2198  QgsComposerItemGroup* itemGroup = composition()->groupItems( selectionList );
2199 
2200  if ( !itemGroup )
2201  {
2202  //group could not be created
2203  return;
2204  }
2205 
2206  itemGroup->setSelected( true );
2207  emit selectedItemChanged( itemGroup );
2208 }
2209 
2211 {
2212  if ( !composition() )
2213  {
2214  return;
2215  }
2216 
2217  //hunt through selection for any groups, and ungroup them
2218  QList<QgsComposerItem*> selectionList = composition()->selectedComposerItems();
2219  QList<QgsComposerItem*>::iterator itemIter = selectionList.begin();
2220  for ( ; itemIter != selectionList.end(); ++itemIter )
2221  {
2222  QgsComposerItemGroup* itemGroup = dynamic_cast<QgsComposerItemGroup *>( *itemIter );
2223  if ( itemGroup )
2224  {
2225  composition()->ungroupItems( itemGroup );
2226  }
2227  }
2228 }
2229 
2231 {
2232  QMainWindow* composerObject = nullptr;
2233  QObject* currentObject = parent();
2234  if ( !currentObject )
2235  {
2236  return qobject_cast<QMainWindow *>( currentObject );
2237  }
2238 
2239  while ( true )
2240  {
2241  composerObject = qobject_cast<QMainWindow*>( currentObject );
2242  if ( composerObject || !currentObject->parent() )
2243  {
2244  return composerObject;
2245  }
2246  currentObject = currentObject->parent();
2247  }
2248 
2249  return nullptr;
2250 }
2251 
2252 void QgsComposerView::addPolygonNode( QPointF scenePoint )
2253 {
2254  QPolygonF polygon = mPolygonItem->polygon();
2255  polygon.append( QPointF( scenePoint.x(), scenePoint.y() ) );
2256 
2257  if ( polygon.size() == 1 )
2258  polygon.append( QPointF( scenePoint.x(), scenePoint.y() ) );
2259 
2260  mPolygonItem->setPolygon( polygon );
2261 }
2262 
2263 void QgsComposerView::movePolygonNode( QPointF scenePoint, bool constrainAngle )
2264 {
2265  QPolygonF polygon = mPolygonItem->polygon();
2266 
2267  if ( polygon.isEmpty() )
2268  return;
2269 
2270  if ( polygon.size() > 1 && constrainAngle )
2271  {
2272  QPointF start = polygon.at( polygon.size() - 2 );
2273  QLineF newLine = QLineF( start, scenePoint );
2274 
2275  //movement is contrained to 45 degree angles
2276  double angle = QgsComposerUtils::snappedAngle( newLine.angle() );
2277  newLine.setAngle( angle );
2278  scenePoint = newLine.p2();
2279  }
2280 
2281  polygon.replace( polygon.size() - 1, scenePoint );
2282  mPolygonItem->setPolygon( polygon );
2283 }
2284 
2285 void QgsComposerView::displayNodes( const bool display )
2286 {
2287  QList<QgsComposerNodesItem*> nodesShapes;
2288  composition()->composerItems( nodesShapes );
2289 
2290  QList<QgsComposerNodesItem*>::iterator it = nodesShapes.begin();
2291  for ( ; it != nodesShapes.end(); ++it )
2292  ( *it )->setDisplayNodes( display );
2293 
2294  scene()->update();
2295 }
2296 
2297 void QgsComposerView::setSelectedNode( QgsComposerNodesItem *shape,
2298  const int index )
2299 {
2300  QList<QgsComposerNodesItem*> nodesShapes;
2301  composition()->composerItems( nodesShapes );
2302 
2303  QList<QgsComposerNodesItem*>::iterator it = nodesShapes.begin();
2304  for ( ; it != nodesShapes.end(); ++it )
2305  {
2306  if (( *it ) == shape )
2307  {
2308  ( *it )->setSelectedNode( index );
2309  selectNone();
2310  ( *it )->setSelected( true );
2311  emit selectedItemChanged(( *it ) );
2312  }
2313  else
2314  ( *it )->deselectNode();
2315  }
2316 
2317  scene()->update();
2318 }
2319 
2320 void QgsComposerView::deselectNode()
2321 {
2322  QList<QgsComposerNodesItem*> nodesShapes;
2323  composition()->composerItems( nodesShapes );
2324 
2325  QList<QgsComposerNodesItem*>::iterator it = nodesShapes.begin();
2326  for ( ; it != nodesShapes.end(); ++it )
2327  ( *it )->deselectNode();
2328 
2329  scene()->update();
2330 }
QgsComposerMouseHandles::MouseAction mouseActionForScenePos(QPointF sceneCoordPos)
Finds out which mouse move action to choose depending on the scene cursor position.
int selectedNode()
Returns the currently selected node.
void setShapeType(QgsComposerShape::Shape s)
Item representing the paper.
Definition: qgspaperitem.h:42
An abstract composer item that provides generic methods for nodes based shapes such as polygon or pol...
A scale bar item that can be added to a map composition.
QUndoStack * undoStack()
Returns pointer to undo/redo command storage.
QgsComposerItemGroup * groupItems(QList< QgsComposerItem *> items)
Creates a new group from a list of composer items and adds it to the composition. ...
static unsigned index
A rectangle specified with double values.
Definition: qgsrectangle.h:36
QgsPoint center() const
Center point of the rectangle.
Definition: qgsrectangle.h:222
Composer item for polylines.
QMainWindow * composerWindow()
Returns the composer main window.
void composerItems(QList< T *> &itemList)
Return composer items of a specific type.
void setAllDeselected()
Clears any selected items in the composition.
bool removeNode(const int index)
Remove a node from the shape.
int nodeAtPosition(QPointF node, const bool searchInRadius=true, const double radius=10)
Search the nearest node in shape within a maximal area.
An item that draws an arrow between two points.
const char * zoom_in[]
Bitmap cursors for map operations.
Definition: qgscursors.cpp:21
void keyPressEvent(QKeyEvent *e) override
void zoomLevelChanged()
Is emitted when the view zoom changes.
void selectAll()
Selects all items.
void setOffset(double xOffset, double yOffset)
Sets offset values to shift image (useful for live updates when moving item content) ...
#define MIN_VIEW_SCALE
void setLayers(const QList< QgsMapLayer *> &layers)
Setter for stored layer set.
void selectInvert()
Inverts current selection.
ZoomMode
Modes for zooming item content.
bool moveNode(const int index, QPointF node)
Move a node to a new position.
void updateRulers()
Update rulers with current scene rect.
void applyDefaultSize(ScaleBarUnits u=Meters)
Apply default size (scale bar 1/5 of map item width)
QgsComposerMouseHandles * selectionHandles()
Returns pointer to selection handles.
void mousePressEvent(QMouseEvent *) override
void mouseReleaseEvent(QMouseEvent *) override
void addComposerScaleBar(QgsComposerScaleBar *scaleBar)
Adds scale bar to the graphics scene and advises composer to create a widget for it (through signal) ...
A item that forms part of a map composition.
void scale(double scaleFactor, const QgsPoint *c=nullptr)
Scale the rectangle around its center point.
void pushAddRemoveCommand(QgsComposerItem *item, const QString &text, const QgsAddRemoveItemCommand::State state=QgsAddRemoveItemCommand::Added)
Convenience function to create a QgsAddRemoveItemCommand, connect its signals and push it to the undo...
void setZoomLevel(double zoomLevel)
Set zoom level, where a zoom level of 1.0 corresponds to 100%.
A container for grouping several QgsComposerItems.
void deleteSelectedItems()
Deletes selected items.
virtual void setSelected(bool s)
Set selected, selected item should be highlighted.
bool isDragging()
Returns true is user is currently dragging the handles.
void setComposition(QgsComposition *c)
MouseAction
Describes the action (move or resize in different directon) to be done during mouse move...
void updateCachedImage()
Forces an update of the cached map image.
void setComposerMap(const QgsComposerMap *map)
Sets the composer map to use to limit the extent of features shown in the attribute table...
void setCurrentTool(QgsComposerView::Tool t)
void groupItems()
Add an item group containing the selected items.
QList< QgsMapLayer * > layers() const
Get list of layers for map rendering The layers are stored in the reverse order of how they are rende...
void updateLegend()
Updates the model and all legend entries.
void selectNone()
Deselects all items.
QList< const QgsComposerMap * > composerMapItems() const
Returns pointers to all composer maps in the scene.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:72
QgsRectangle visibleExtent() const
Return the actual extent derived from requested extent that takes takes output image size into accoun...
A table that displays attributes from a vector layer.
void composerViewHide(QgsComposerView *)
Emitted before composerview is hidden.
A graphics effect which can be applied to a widget to simulate various printing and color blindness m...
void compositionSet(QgsComposition *)
Emitted when the composition is set for the view.
A composer class that displays svg files or raster format (jpg, png, ...)
bool isResizing()
Returns true is user is currently resizing with the handles.
QSet< QgsComposerItem * > items()
bool addNode(QPointF pt, const bool checkArea=true, const double radius=10)
Add a node in current shape.
QgsComposerView(QWidget *parent=nullptr, const char *name=nullptr, Qt::WindowFlags f=0)
void addComposerShape(QgsComposerShape *shape)
Adds a composer shape to the graphics scene and advises composer to create a widget for it (through s...
void zoomToExtent(const QgsRectangle &extent)
Zooms the map so that the specified extent is fully visible within the map item.
virtual void moveContent(double dx, double dy)
Move Content of item.
void setUseSymbol(bool useSymbol)
Controls whether the shape should be drawn using a QgsFillSymbol.
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the view.
void showEvent(QShowEvent *e) override
void wheelEvent(QWheelEvent *event) override
const char * zoom_out[]
Definition: qgscursors.cpp:45
void paintEvent(QPaintEvent *event) override
void cancelCommand()
Deletes current command.
void setPreventCursorChange(const bool preventChange)
If true, prevents any mouse cursor changes by the composition or by any composer items Used by QgsCom...
void endCommand()
Saves end state of item and pushes command to the undo history.
virtual bool selected() const
Is selected.
bool nodePosition(const int index, QPointF &position)
Gets the position of a node in scene coordinate.
void addFrame(QgsComposerFrame *frame, bool recalcFrameSizes=true) override
Adds a frame to the multiframe.
void selectedItemChanged(QgsComposerItem *selected)
Is emitted when selected item changed. If 0, no item is selected.
void scaleSafe(double scale)
Scales the view in a safe way, by limiting the acceptable range of the scale applied.
void setSceneTransform(const QTransform &transform)
void addItemsFromXml(const QDomElement &elem, const QDomDocument &doc, QMap< QgsComposerMap *, int > *mapsToRestore=nullptr, bool addUndoCommands=false, QPointF *pos=nullptr, bool pasteInPlace=false)
Add items from XML representation to the graphics scene (for project file reading, pasting items from clipboard)
void setComposerMap(const QgsComposerMap *map)
QList< QgsComposerItem * > ungroupItems(QgsComposerItemGroup *group)
Ungroups items by removing them from an item group and removing the group from the composition...
#define MAX_VIEW_SCALE
Widget to display the composer items.
void removeComposerItem(QgsComposerItem *item, const bool createCommand=true, const bool removeGroupItems=true)
Remove item from the graphics scene. Additionally to QGraphicsScene::removeItem, this function consid...
void beginCommand(const QString &commandText, QgsComposerMergeCommand::Context c=QgsComposerMergeCommand::Unknown)
Starts new composer undo command.
void setPreviewModeEnabled(bool enabled)
Sets whether a preview effect should be used to alter the view&#39;s appearance.
void pasteItems(PasteMode mode)
Pastes items from clipboard.
void setMode(PreviewMode mode)
Sets the mode for the preview effect, which controls how the effect modifies a widgets appearance...
A class to represent a point.
Definition: qgspoint.h:36
Graphics scene for map printing.
Object representing map window.
Frame item for a composer multiframe item.
QgsComposerItem * composerItemAt(QPointF position, const bool ignoreLocked=false) const
Returns the topmost composer item at a specified position.
void setComposerMap(const QgsComposerMap *map)
void ungroupItems()
Ungroups the selected items.
Tool
Current tool.
void setMapCanvas(QgsMapCanvas *canvas)
Sets the map canvas associated with the view.
void copyItems(ClipboardMode mode)
Cuts or copies the selected items.
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
void addComposerPolyline(QgsComposerPolyline *polyline)
Adds a composer polyline and advises composer to create a widget for it (through signal) ...
virtual void addFrame(QgsComposerFrame *frame, bool recalcFrameSizes=true) override
Adds a frame to the multiframe.
void setPreviewMode(QgsPreviewEffect::PreviewMode mode)
Sets the preview mode which should be used to modify the view&#39;s appearance.
QRectF toRectF() const
returns a QRectF with same coordinates.
A composer items that draws common shapes (ellipse, triangle, rectangle)
Composer item for polygons.
PreviewMode previewMode() const
void resizeEvent(QResizeEvent *event) override
QPointF snapPointToGrid(QPointF scenePoint) const
Snaps a scene coordinate point to grid.
void cursorPosChanged(QPointF)
Is emitted when mouse cursor coordinates change.
void setComposition(QgsComposition *c)
Sets the composition for the view.
void addComposerMap(QgsComposerMap *map, const bool setDefaultPreviewStyle=true)
Adds map to the graphics scene and advises composer to create a widget for it (through signal) ...
virtual void setSceneRect(const QRectF &rectangle)
Sets this items bound in scene coordinates such that 1 item size units corresponds to 1 scene size un...
void keyReleaseEvent(QKeyEvent *e) override
int nodesSize()
Returns the number of nodes in the shape.
void setText(const QString &text)
void setSceneRect(const QRectF &rectangle) override
Sets this items bound in scene coordinates such that 1 item size units corresponds to 1 scene size un...
A label that can be placed onto a map composition.
static double snappedAngle(const double angle)
Snaps an angle to its closest 45 degree angle.
void addComposerPicture(QgsComposerPicture *picture)
Adds picture to the graphics scene and advises composer to create a widget for it (through signal) ...
void hideEvent(QHideEvent *e) override
QgsComposerView::Tool currentTool() const
void updateMarker(QPointF pos)
void setSceneRect(const QRectF &rectangle) override
Sets this items bound in scene coordinates such that 1 item size units corresponds to 1 scene size un...
QgsComposition * composition()
Returns the composition or 0 in case of error.
bool positionLock() const
Returns whether position lock for mouse drags is enabled returns true if item is locked for mouse mov...
void addComposerPolygon(QgsComposerPolygon *polygon)
Adds a composer polygon and advises composer to create a widget for it (through signal) ...
const char * cross_hair_cursor[]
Definition: qgscursors.cpp:143
void actionFinished()
Current action (e.g.
void mouseMoveEvent(QMouseEvent *) override
A legend that can be placed onto a map composition.
void addComposerLabel(QgsComposerLabel *label)
Adds label to the graphics scene and advises composer to create a widget for it (through signal) ...
double ANALYSIS_EXPORT angle(Point3D *p1, Point3D *p2, Point3D *p3, Point3D *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cc:857
void addComposerArrow(QgsComposerArrow *arrow)
Adds an arrow item to the graphics scene and advises composer to create a widget for it (through sign...
void addComposerLegend(QgsComposerLegend *legend)
Adds legend to the graphics scene and advises composer to create a widget for it (through signal) ...
void adjustSizeToText()
Resizes the widget such that the text fits to the item. Keeps top left point.
virtual int type() const override
Return correct graphics item type.
void composerViewShow(QgsComposerView *)
Emitted before composerview is shown.
void beginCommand(QgsComposerItem *item, const QString &commandText, const QgsComposerMergeCommand::Context c=QgsComposerMergeCommand::Unknown)
Allocates new item command and saves initial state in it.
void scrollContentsBy(int dx, int dy) override
QList< QgsComposerItem * > selectedComposerItems(const bool includeLockedItems=true)
Returns list of selected composer items.
void mouseDoubleClickEvent(QMouseEvent *e) override
void beginMultiFrameCommand(QgsComposerMultiFrame *multiFrame, const QString &text, const QgsComposerMultiFrameMergeCommand::Context c=QgsComposerMultiFrameMergeCommand::Unknown)