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