QGIS API Documentation  2.99.0-Master (37c43df)
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" //for QgsMapCanvas::WheelAction
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  unselectNode();
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 behaviour 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::ShiftModifier ) )
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, eg 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.isNull() )
462  {
463  mPolygonItem.reset( new QGraphicsPolygonItem() );
464  mPolygonItem.data()->setBrush( Qt::NoBrush );
465  mPolygonItem.data()->setPen( QPen( QBrush( QColor( 227, 22, 22, 200 ) ), 0 ) );
466  mPolygonItem.data()->setZValue( 1000 );
467 
468  scene()->addItem( mPolygonItem.data() );
469  scene()->update();
470  }
471 
472  break;
473  }
474 
475  case AddPolyline:
476  {
477  if ( mPolylineItem.isNull() && mPolygonItem.isNull() )
478  {
479  mPolygonItem.reset( new QGraphicsPolygonItem() );
480 
481  mPolylineItem.reset( new QGraphicsPathItem() );
482  mPolylineItem.data()->setPen( QPen( QBrush( QColor( 227, 22, 22, 200 ) ), 0 ) );
483  mPolylineItem.data()->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.isNull() )
800  {
801  QPolygonF poly = mPolygonItem.data()->polygon();
802 
803  // last (temporary) point is removed
804  poly.remove( poly.count() - 1 );
805  if ( poly.size() >= 3 )
806  {
807  mPolygonItem.data()->setPolygon( poly );
808 
809  // add polygon in composition
810  QgsComposerPolygon *composerPolygon = new QgsComposerPolygon( mPolygonItem.data()->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.data() );
823  mPolygonItem.reset();
824  emit actionFinished();
825  }
826  break;
827  }
828 
829  case AddPolyline:
830  {
831  if ( ! mPolygonItem.isNull() && ! mPolylineItem.isNull() )
832  {
833  // ignore the last point due to release event before doubleClick event
834  QPolygonF poly = mPolygonItem.data()->polygon();
835 
836  // last (temporary) point is removed
837  poly.remove( poly.count() - 1 );
838  if ( poly.size() >= 2 )
839  {
840  mPolygonItem.data()->setPolygon( poly );
841 
842  // add polygon in composition
843  QgsComposerPolyline *composerPolyline = new QgsComposerPolyline( mPolygonItem.data()->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.data() );
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.isNull() )
968  addPolygonNode( scenePoint );
969 
970  break;
971  }
972 
973  case AddPolyline:
974  {
975  if ( ! mPolygonItem.isNull() && ! mPolylineItem.isNull() )
976  {
977  addPolygonNode( scenePoint );
978 
979  // rebuild a new qpainter path
980  QPainterPath path;
981  path.addPolygon( mPolygonItem.data()->polygon() );
982  mPolylineItem.data()->setPath( path );
983 
984  // add it to the scene
985  scene()->addItem( mPolylineItem.data() );
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  composition()->addComposerMap( composerMap );
1001 
1003  composerMap->setSelected( true );
1004  emit selectedItemChanged( composerMap );
1005 
1006  removeRubberBand();
1007  emit actionFinished();
1008  composition()->pushAddRemoveCommand( composerMap, tr( "Map added" ) );
1009  }
1010  break;
1011 
1012  case AddPicture:
1013  if ( !composition() || !mRubberBandItem || ( mRubberBandItem->rect().width() < 0.1 && mRubberBandItem->rect().height() < 0.1 ) )
1014  {
1015  removeRubberBand();
1016  return;
1017  }
1018  else
1019  {
1020  QgsComposerPicture* newPicture = new QgsComposerPicture( composition() );
1021  newPicture->setSceneRect( QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height() ) );
1022  composition()->addComposerPicture( newPicture );
1023 
1025  newPicture->setSelected( true );
1026  emit selectedItemChanged( newPicture );
1027 
1028  removeRubberBand();
1029  emit actionFinished();
1030  composition()->pushAddRemoveCommand( newPicture, tr( "Picture added" ) );
1031  }
1032  break;
1033 
1034  case AddLabel:
1035  if ( !composition() || !mRubberBandItem )
1036  {
1037  removeRubberBand();
1038  return;
1039  }
1040  else
1041  {
1042  QgsComposerLabel* newLabelItem = new QgsComposerLabel( composition() );
1043  newLabelItem->setText( tr( "QGIS" ) );
1044  newLabelItem->adjustSizeToText();
1045 
1046  //make sure label size is sufficient to fit text
1047  double labelWidth = qMax( mRubberBandItem->rect().width(), newLabelItem->rect().width() );
1048  double labelHeight = qMax( mRubberBandItem->rect().height(), newLabelItem->rect().height() );
1049  newLabelItem->setSceneRect( QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), labelWidth, labelHeight ) );
1050 
1051  composition()->addComposerLabel( newLabelItem );
1052 
1054  newLabelItem->setSelected( true );
1055  emit selectedItemChanged( newLabelItem );
1056 
1057  removeRubberBand();
1058  emit actionFinished();
1059  composition()->pushAddRemoveCommand( newLabelItem, tr( "Label added" ) );
1060  }
1061  break;
1062 
1063  case AddLegend:
1064  if ( !composition() || !mRubberBandItem )
1065  {
1066  removeRubberBand();
1067  return;
1068  }
1069  else
1070  {
1071  QgsComposerLegend* newLegend = new QgsComposerLegend( composition() );
1072  QList<const QgsComposerMap*> mapItemList = composition()->composerMapItems();
1073  if ( !mapItemList.isEmpty() )
1074  {
1075  newLegend->setComposerMap( mapItemList.at( 0 ) );
1076  }
1077  newLegend->setSceneRect( QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height() ) );
1078  composition()->addComposerLegend( newLegend );
1079  newLegend->updateLegend();
1080 
1082  newLegend->setSelected( true );
1083  emit selectedItemChanged( newLegend );
1084 
1085  removeRubberBand();
1086  emit actionFinished();
1087  composition()->pushAddRemoveCommand( newLegend, tr( "Legend added" ) );
1088  }
1089  break;
1090 
1091  case AddAttributeTable:
1092  if ( !composition() || !mRubberBandItem )
1093  {
1094  removeRubberBand();
1095  return;
1096  }
1097  else
1098  {
1100  QList<const QgsComposerMap*> mapItemList = composition()->composerMapItems();
1101  if ( !mapItemList.isEmpty() )
1102  {
1103  newTable->setComposerMap( mapItemList.at( 0 ) );
1104  }
1106  newTable, composition(), tr( "Attribute table added" ) );
1107  composition()->undoStack()->push( command );
1108  QgsComposerFrame* frame = new QgsComposerFrame( composition(), newTable, mRubberBandItem->transform().dx(),
1109  mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(),
1110  mRubberBandItem->rect().height() );
1111  composition()->beginMultiFrameCommand( newTable, tr( "Attribute table frame added" ) );
1112  newTable->addFrame( frame );
1114 
1116  frame->setSelected( true );
1117  emit selectedItemChanged( frame );
1118 
1119  removeRubberBand();
1120  emit actionFinished();
1121  }
1122  break;
1123 
1124  case AddHtml:
1125  if ( !composition() || !mRubberBandItem || ( mRubberBandItem->rect().width() < 0.1 && mRubberBandItem->rect().height() < 0.1 ) )
1126  {
1127  removeRubberBand();
1128  return;
1129  }
1130  else
1131  {
1132  QgsComposerHtml* composerHtml = new QgsComposerHtml( composition(), true );
1134  composerHtml, composition(), tr( "HTML item added" ) );
1135  composition()->undoStack()->push( command );
1136  QgsComposerFrame* frame = new QgsComposerFrame( composition(), composerHtml, mRubberBandItem->transform().dx(),
1137  mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(),
1138  mRubberBandItem->rect().height() );
1139  composition()->beginMultiFrameCommand( composerHtml, tr( "HTML frame added" ) );
1140  composerHtml->addFrame( frame );
1142 
1144  frame->setSelected( true );
1145  emit selectedItemChanged( frame );
1146 
1147  removeRubberBand();
1148  emit actionFinished();
1149  }
1150  break;
1151  default:
1152  break;
1153  }
1154 }
1155 
1156 void QgsComposerView::mouseMoveEvent( QMouseEvent* e )
1157 {
1158  if ( !composition() )
1159  {
1160  return;
1161  }
1162 
1163  bool shiftModifier = false;
1164  bool altModifier = false;
1165  if ( e->modifiers() & Qt::ShiftModifier )
1166  {
1167  //shift key depressed
1168  shiftModifier = true;
1169  }
1170  if ( e->modifiers() & Qt::AltModifier )
1171  {
1172  //alt key depressed
1173  altModifier = true;
1174  }
1175 
1176  mMouseCurrentXY = e->pos();
1177  //update cursor position in composer status bar
1178  emit cursorPosChanged( mapToScene( e->pos() ) );
1179 
1180  updateRulers();
1181  if ( mHorizontalRuler )
1182  {
1183  mHorizontalRuler->updateMarker( e->posF() );
1184  }
1185  if ( mVerticalRuler )
1186  {
1187  mVerticalRuler->updateMarker( e->posF() );
1188  }
1189 
1190  if ( mToolPanning || mMousePanning || mKeyPanning )
1191  {
1192  //panning, so scroll view
1193  horizontalScrollBar()->setValue( horizontalScrollBar()->value() - ( e->x() - mMouseLastXY.x() ) );
1194  verticalScrollBar()->setValue( verticalScrollBar()->value() - ( e->y() - mMouseLastXY.y() ) );
1195  mMouseLastXY = e->pos();
1196  return;
1197  }
1198  else if (( e->buttons() == Qt::NoButton ) && ( mPolygonItem.isNull() ) )
1199  {
1200  if ( mCurrentTool == Select )
1201  {
1202  QGraphicsView::mouseMoveEvent( e );
1203  }
1204  }
1205  else
1206  {
1207  QPointF scenePoint = mapToScene( e->pos() );
1208 
1209  if ( mMarqueeSelect || mMarqueeZoom )
1210  {
1211  updateRubberBandRect( scenePoint );
1212  return;
1213  }
1214 
1215  switch ( mCurrentTool )
1216  {
1217  case Select:
1218  QGraphicsView::mouseMoveEvent( e );
1219  break;
1220 
1221  case AddArrow:
1222  {
1223  updateRubberBandLine( scenePoint, shiftModifier );
1224  break;
1225  }
1226 
1227  case AddMap:
1228  case AddRectangle:
1229  case AddTriangle:
1230  case AddEllipse:
1231  case AddHtml:
1232  case AddPicture:
1233  case AddLabel:
1234  case AddLegend:
1235  case AddTable:
1236  case AddAttributeTable:
1237  //adjust rubber band item
1238  {
1239  updateRubberBandRect( scenePoint, shiftModifier, altModifier );
1240  break;
1241  }
1242 
1243  case AddPolygon:
1244  {
1245  if ( ! mPolygonItem.isNull() )
1246  movePolygonNode( scenePoint );
1247 
1248  break;
1249  }
1250 
1251  case AddPolyline:
1252  {
1253  if ( ! mPolygonItem.isNull() && ! mPolylineItem.isNull() )
1254  {
1255  movePolygonNode( scenePoint );
1256 
1257  // rebuild a new qpainter path
1258  QPainterPath path;
1259  path.addPolygon( mPolygonItem.data()->polygon() );
1260  mPolylineItem.data()->setPath( path );
1261  }
1262 
1263  break;
1264  }
1265 
1266  case MoveItemContent:
1267  {
1268  //update map preview if composer map
1269  QgsComposerMap* composerMap = dynamic_cast<QgsComposerMap *>( mMoveContentItem );
1270  if ( composerMap )
1271  {
1272  composerMap->setOffset( scenePoint.x() - mMoveContentStartPos.x(), scenePoint.y() - mMoveContentStartPos.y() );
1273  composerMap->update();
1274  }
1275  break;
1276  }
1277 
1278  case EditNodesItem:
1279  {
1280  if ( mNodesItemIndex != -1 )
1281  {
1282  QPointF scenePoint = mapToScene( e->pos() );
1283  mNodesItem->moveNode( mNodesItemIndex, scenePoint );
1284  scene()->update();
1285  }
1286 
1287  break;
1288  }
1289 
1290  default:
1291  break;
1292  }
1293  }
1294 }
1295 
1296 void QgsComposerView::updateRubberBandRect( QPointF & pos, const bool constrainSquare, const bool fromCenter )
1297 {
1298  if ( !mRubberBandItem )
1299  {
1300  return;
1301  }
1302 
1303  double x = 0;
1304  double y = 0;
1305  double width = 0;
1306  double height = 0;
1307 
1308  double dx = pos.x() - mRubberBandStartPos.x();
1309  double dy = pos.y() - mRubberBandStartPos.y();
1310 
1311  if ( constrainSquare )
1312  {
1313  if ( fabs( dx ) > fabs( dy ) )
1314  {
1315  width = fabs( dx );
1316  height = width;
1317  }
1318  else
1319  {
1320  height = fabs( dy );
1321  width = height;
1322  }
1323 
1324  x = mRubberBandStartPos.x() - (( dx < 0 ) ? width : 0 );
1325  y = mRubberBandStartPos.y() - (( dy < 0 ) ? height : 0 );
1326  }
1327  else
1328  {
1329  //not constraining
1330  if ( dx < 0 )
1331  {
1332  x = pos.x();
1333  width = -dx;
1334  }
1335  else
1336  {
1337  x = mRubberBandStartPos.x();
1338  width = dx;
1339  }
1340 
1341  if ( dy < 0 )
1342  {
1343  y = pos.y();
1344  height = -dy;
1345  }
1346  else
1347  {
1348  y = mRubberBandStartPos.y();
1349  height = dy;
1350  }
1351  }
1352 
1353  if ( fromCenter )
1354  {
1355  x = mRubberBandStartPos.x() - width;
1356  y = mRubberBandStartPos.y() - height;
1357  width *= 2.0;
1358  height *= 2.0;
1359  }
1360 
1361  mRubberBandItem->setRect( 0, 0, width, height );
1362  QTransform t;
1363  t.translate( x, y );
1364  mRubberBandItem->setTransform( t );
1365 }
1366 
1367 void QgsComposerView::updateRubberBandLine( QPointF pos, const bool constrainAngles )
1368 {
1369  if ( !mRubberBandLineItem )
1370  {
1371  return;
1372  }
1373 
1374  //snap to grid
1375  QPointF snappedScenePoint = composition()->snapPointToGrid( pos );
1376 
1377  QLineF newLine = QLineF( mRubberBandStartPos, snappedScenePoint );
1378 
1379  if ( constrainAngles )
1380  {
1381  //movement is contrained to 45 degree angles
1382  double angle = QgsComposerUtils::snappedAngle( newLine.angle() );
1383  newLine.setAngle( angle );
1384  }
1385 
1386  mRubberBandLineItem->setLine( newLine );
1387 }
1388 
1390 {
1391  QPointF scenePoint = mapToScene( e->pos() );
1392 
1393  switch ( mCurrentTool )
1394  {
1395  case EditNodesItem:
1396  {
1397  // erase status previously set by the mousePressEvent method
1398  if ( mNodesItemIndex != -1 )
1399  {
1400  mNodesItem = nullptr;
1401  mNodesItemIndex = -1;
1402  unselectNode();
1403  }
1404 
1405  // search items in composer
1406  QList<QGraphicsItem *> itemsAtCursorPos = items( e->pos().x(), e->pos().y(),
1407  mMoveContentSearchRadius,
1408  mMoveContentSearchRadius );
1409  if ( itemsAtCursorPos.isEmpty() )
1410  return;
1411 
1412  bool rc = false;
1413  QList<QGraphicsItem*>::iterator itemIter = itemsAtCursorPos.begin();
1414  for ( ; itemIter != itemsAtCursorPos.end(); ++itemIter )
1415  {
1416  QgsComposerItem* item = dynamic_cast<QgsComposerItem *>(( *itemIter ) );
1417 
1418  if ( item && !item->positionLock() )
1419  {
1420  if (( item->type() == QgsComposerItem::ComposerPolygon
1421  || item->type() == QgsComposerItem::ComposerPolyline ) )
1422  {
1423  QgsComposerNodesItem* itemP = dynamic_cast<QgsComposerNodesItem *>( item );
1424 
1425  composition()->beginCommand( itemP, tr( "Add item node" ) );
1426  rc = itemP->addNode( scenePoint );
1427 
1428  if ( rc )
1429  {
1430  composition()->endCommand();
1431  mNodesItem = itemP;
1432  mNodesItemIndex = mNodesItem->nodeAtPosition( scenePoint );
1433  }
1434  else
1436  }
1437  }
1438 
1439  if ( rc )
1440  break;
1441  }
1442 
1443  if ( rc )
1444  {
1445  setSelectedNode( mNodesItem, mNodesItemIndex );
1446  scene()->update();
1447  }
1448 
1449  break;
1450  }
1451 
1452  default:
1453  break;
1454  }
1455 }
1456 
1458 {
1459  if ( !composition() )
1460  {
1461  return;
1462  }
1463 
1464  QList<QgsComposerItem*> composerItemList = composition()->selectedComposerItems();
1465  QList<QgsComposerItem*>::iterator itemIt = composerItemList.begin();
1466 
1467  QDomDocument doc;
1468  QDomElement documentElement = doc.createElement( QStringLiteral( "ComposerItemClipboard" ) );
1469  for ( ; itemIt != composerItemList.end(); ++itemIt )
1470  {
1471  // copy each item in a group
1472  QgsComposerItemGroup* itemGroup = dynamic_cast<QgsComposerItemGroup*>( *itemIt );
1473  if ( itemGroup && composition() )
1474  {
1475  QSet<QgsComposerItem*> groupedItems = itemGroup->items();
1476  QSet<QgsComposerItem*>::iterator it = groupedItems.begin();
1477  for ( ; it != groupedItems.end(); ++it )
1478  {
1479  ( *it )->writeXml( documentElement, doc );
1480  }
1481  }
1482  ( *itemIt )->writeXml( documentElement, doc );
1483  if ( mode == ClipboardModeCut )
1484  {
1485  composition()->removeComposerItem( *itemIt );
1486  }
1487  }
1488  doc.appendChild( documentElement );
1489 
1490  //if it's a copy, we have to remove the UUIDs since we don't want any duplicate UUID
1491  if ( mode == ClipboardModeCopy )
1492  {
1493  // remove all uuid attributes
1494  QDomNodeList composerItemsNodes = doc.elementsByTagName( QStringLiteral( "ComposerItem" ) );
1495  for ( int i = 0; i < composerItemsNodes.count(); ++i )
1496  {
1497  QDomNode composerItemNode = composerItemsNodes.at( i );
1498  if ( composerItemNode.isElement() )
1499  {
1500  composerItemNode.toElement().removeAttribute( QStringLiteral( "uuid" ) );
1501  }
1502  }
1503  }
1504 
1505  QMimeData *mimeData = new QMimeData;
1506  mimeData->setData( QStringLiteral( "text/xml" ), doc.toByteArray() );
1507  QClipboard *clipboard = QApplication::clipboard();
1508  clipboard->setMimeData( mimeData );
1509 }
1510 
1512 {
1513  if ( !composition() )
1514  {
1515  return;
1516  }
1517 
1518  QDomDocument doc;
1519  QClipboard *clipboard = QApplication::clipboard();
1520  if ( doc.setContent( clipboard->mimeData()->data( QStringLiteral( "text/xml" ) ) ) )
1521  {
1522  QDomElement docElem = doc.documentElement();
1523  if ( docElem.tagName() == QLatin1String( "ComposerItemClipboard" ) )
1524  {
1525  if ( composition() )
1526  {
1527  QPointF pt;
1529  {
1530  // place items at cursor position
1531  pt = mapToScene( mapFromGlobal( QCursor::pos() ) );
1532  }
1533  else
1534  {
1535  // place items in center of viewport
1536  pt = mapToScene( viewport()->rect().center() );
1537  }
1538  bool pasteInPlace = ( mode == PasteModeInPlace );
1539  composition()->addItemsFromXml( docElem, doc, nullptr, true, &pt, pasteInPlace );
1540  }
1541  }
1542  }
1543 
1544  //switch back to select tool so that pasted items can be moved/resized (#8958)
1546 }
1547 
1549 {
1550  if ( !composition() )
1551  {
1552  return;
1553  }
1554 
1555  if ( mCurrentTool == QgsComposerView::EditNodesItem )
1556  {
1557  if ( mNodesItemIndex != -1 )
1558  {
1559  composition()->beginCommand( mNodesItem, tr( "Remove item node" ) );
1560  if ( mNodesItem->removeNode( mNodesItemIndex ) )
1561  {
1562  composition()->endCommand();
1563  if ( mNodesItem->nodesSize() > 0 )
1564  {
1565  mNodesItemIndex = mNodesItem->selectedNode();
1566  // setSelectedNode( mNodesItem, mNodesItemIndex );
1567  }
1568  else
1569  {
1570  mNodesItemIndex = -1;
1571  mNodesItem = nullptr;
1572  }
1573  scene()->update();
1574  }
1575  else
1576  {
1578  }
1579  }
1580  }
1581  else
1582  {
1583  QList<QgsComposerItem*> composerItemList = composition()->selectedComposerItems();
1584  QList<QgsComposerItem*>::iterator itemIt = composerItemList.begin();
1585 
1586  //delete selected items
1587  for ( ; itemIt != composerItemList.end(); ++itemIt )
1588  {
1589  if ( composition() )
1590  {
1591  composition()->removeComposerItem( *itemIt );
1592  }
1593  }
1594  }
1595 }
1596 
1598 {
1599  if ( !composition() )
1600  {
1601  return;
1602  }
1603 
1604  //select all items in composer
1605  QList<QGraphicsItem *> itemList = composition()->items();
1606  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
1607  for ( ; itemIt != itemList.end(); ++itemIt )
1608  {
1609  QgsComposerItem* mypItem = dynamic_cast<QgsComposerItem *>( *itemIt );
1610  QgsPaperItem* paperItem = dynamic_cast<QgsPaperItem*>( *itemIt );
1611  if ( mypItem && !paperItem )
1612  {
1613  if ( !mypItem->positionLock() )
1614  {
1615  mypItem->setSelected( true );
1616  }
1617  else
1618  {
1619  //deselect all locked items
1620  mypItem->setSelected( false );
1621  }
1622  emit selectedItemChanged( mypItem );
1623  }
1624  }
1625 }
1626 
1628 {
1629  if ( !composition() )
1630  {
1631  return;
1632  }
1633 
1635 }
1636 
1638 {
1639  if ( !composition() )
1640  {
1641  return;
1642  }
1643 
1644  //check all items in composer
1645  QList<QGraphicsItem *> itemList = composition()->items();
1646  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
1647  for ( ; itemIt != itemList.end(); ++itemIt )
1648  {
1649  QgsComposerItem* mypItem = dynamic_cast<QgsComposerItem *>( *itemIt );
1650  QgsPaperItem* paperItem = dynamic_cast<QgsPaperItem*>( *itemIt );
1651  if ( mypItem && !paperItem )
1652  {
1653  //flip selected state for items (and deselect any locked items)
1654  if ( mypItem->selected() || mypItem->positionLock() )
1655  {
1656 
1657  mypItem->setSelected( false );
1658  }
1659  else
1660  {
1661  mypItem->setSelected( true );
1662  emit selectedItemChanged( mypItem );
1663  }
1664  }
1665  }
1666 }
1667 
1668 void QgsComposerView::keyPressEvent( QKeyEvent * e )
1669 {
1670  if ( !composition() )
1671  {
1672  return;
1673  }
1674 
1675  if ( mKeyPanning || mMousePanning || mToolPanning || mMovingItemContent ||
1676  composition()->selectionHandles()->isDragging() || composition()->selectionHandles()->isResizing() )
1677  {
1678  return;
1679  }
1680 
1681  if ( mTemporaryZoomStatus != QgsComposerView::Inactive )
1682  {
1683  //temporary keyboard based zoom is active
1684  if ( e->isAutoRepeat() )
1685  {
1686  return;
1687  }
1688 
1689  //respond to changes in ctrl key status
1690  if ( !( e->modifiers() & Qt::ControlModifier ) && !mMarqueeZoom )
1691  {
1692  //space pressed, but control key was released, end of temporary zoom tool
1693  mTemporaryZoomStatus = QgsComposerView::Inactive;
1694  setCurrentTool( mPreviousTool );
1695  }
1696  else if ( !( e->modifiers() & Qt::ControlModifier ) && mMarqueeZoom )
1697  {
1698  //control key released, but user is mid-way through a marquee zoom
1699  //so end temporary zoom when user releases the mouse button
1700  mTemporaryZoomStatus = QgsComposerView::ActiveUntilMouseRelease;
1701  }
1702  else
1703  {
1704  //both control and space pressed
1705  //set cursor to zoom in/out depending on shift key status
1706  QPixmap myZoomQPixmap = QPixmap(( const char ** )(( e->modifiers() & Qt::ShiftModifier ) ? zoom_out : zoom_in ) );
1707  QCursor zoomCursor = QCursor( myZoomQPixmap, 7, 7 );
1708  viewport()->setCursor( zoomCursor );
1709  }
1710  return;
1711  }
1712 
1713  if ( mCurrentTool != QgsComposerView::Zoom && ( mRubberBandItem || mRubberBandLineItem ) )
1714  {
1715  //disable keystrokes while drawing a box
1716  return;
1717  }
1718 
1719  if ( e->key() == Qt::Key_Space && ! e->isAutoRepeat() )
1720  {
1721  if ( !( e->modifiers() & Qt::ControlModifier ) )
1722  {
1723  // Pan composer with space bar
1724  mKeyPanning = true;
1725  mMouseLastXY = mMouseCurrentXY;
1726  if ( composition() )
1727  {
1728  //prevent cursor changes while panning
1730  }
1731  viewport()->setCursor( Qt::ClosedHandCursor );
1732  return;
1733  }
1734  else
1735  {
1736  //ctrl+space pressed, so switch to temporary keyboard based zoom tool
1737  mTemporaryZoomStatus = QgsComposerView::Active;
1738  mPreviousTool = mCurrentTool;
1739  setCurrentTool( Zoom );
1740  //set cursor to zoom in/out depending on shift key status
1741  QPixmap myZoomQPixmap = QPixmap(( const char ** )(( e->modifiers() & Qt::ShiftModifier ) ? zoom_out : zoom_in ) );
1742  QCursor zoomCursor = QCursor( myZoomQPixmap, 7, 7 );
1743  viewport()->setCursor( zoomCursor );
1744  return;
1745  }
1746  }
1747 
1748  if ( mCurrentTool == QgsComposerView::Zoom )
1749  {
1750  //using the zoom tool, respond to changes in shift key status and update mouse cursor accordingly
1751  if ( ! e->isAutoRepeat() )
1752  {
1753  QPixmap myZoomQPixmap = QPixmap(( const char ** )(( e->modifiers() & Qt::ShiftModifier ) ? zoom_out : zoom_in ) );
1754  QCursor zoomCursor = QCursor( myZoomQPixmap, 7, 7 );
1755  viewport()->setCursor( zoomCursor );
1756  }
1757  return;
1758  }
1759 
1760  QList<QgsComposerItem*> composerItemList = composition()->selectedComposerItems();
1761  QList<QgsComposerItem*>::iterator itemIt = composerItemList.begin();
1762 
1763  // increment used for cursor key item movement
1764  double increment = 1.0;
1765  if ( e->modifiers() & Qt::ShiftModifier )
1766  {
1767  //holding shift while pressing cursor keys results in a big step
1768  increment = 10.0;
1769  }
1770  else if ( e->modifiers() & Qt::AltModifier )
1771  {
1772  //holding alt while pressing cursor keys results in a 1 pixel step
1773  double viewScale = transform().m11();
1774  if ( viewScale > 0 )
1775  {
1776  increment = 1 / viewScale;
1777  }
1778  }
1779 
1780  if ( e->key() == Qt::Key_Left )
1781  {
1782  if ( mCurrentTool == EditNodesItem )
1783  {
1784  if ( mNodesItemIndex != -1 )
1785  {
1786  QPointF currentPos;
1787 
1788  if ( mNodesItem->nodePosition( mNodesItemIndex, currentPos ) )
1789  {
1790  currentPos.setX( currentPos.x() - increment );
1791 
1792  composition()->beginCommand( mNodesItem, tr( "Move item node" ) );
1793  mNodesItem->moveNode( mNodesItemIndex, currentPos );
1794  composition()->endCommand();
1795 
1796  scene()->update();
1797  }
1798  }
1799  }
1800  else
1801  {
1802  for ( ; itemIt != composerItemList.end(); ++itemIt )
1803  {
1804  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
1805  ( *itemIt )->move( -1 * increment, 0.0 );
1806  ( *itemIt )->endCommand();
1807  }
1808  }
1809  }
1810  else if ( e->key() == Qt::Key_Right )
1811  {
1812  if ( mCurrentTool == EditNodesItem )
1813  {
1814  if ( mNodesItemIndex != -1 )
1815  {
1816  QPointF currentPos;
1817 
1818  if ( mNodesItem->nodePosition( mNodesItemIndex, currentPos ) )
1819  {
1820  currentPos.setX( currentPos.x() + increment );
1821 
1822  composition()->beginCommand( mNodesItem, tr( "Move item node" ) );
1823  mNodesItem->moveNode( mNodesItemIndex, currentPos );
1824  composition()->endCommand();
1825 
1826  scene()->update();
1827  }
1828  }
1829  }
1830  else
1831  {
1832  for ( ; itemIt != composerItemList.end(); ++itemIt )
1833  {
1834  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
1835  ( *itemIt )->move( increment, 0.0 );
1836  ( *itemIt )->endCommand();
1837  }
1838  }
1839  }
1840  else if ( e->key() == Qt::Key_Down )
1841  {
1842  if ( mCurrentTool == EditNodesItem )
1843  {
1844  if ( mNodesItemIndex != -1 )
1845  {
1846  QPointF currentPos;
1847 
1848  if ( mNodesItem->nodePosition( mNodesItemIndex, currentPos ) )
1849  {
1850  currentPos.setY( currentPos.y() + increment );
1851 
1852  composition()->beginCommand( mNodesItem, tr( "Move item node" ) );
1853  mNodesItem->moveNode( mNodesItemIndex, currentPos );
1854  composition()->endCommand();
1855 
1856  scene()->update();
1857  }
1858  }
1859  }
1860  else
1861  {
1862  for ( ; itemIt != composerItemList.end(); ++itemIt )
1863  {
1864  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
1865  ( *itemIt )->move( 0.0, increment );
1866  ( *itemIt )->endCommand();
1867  }
1868  }
1869  }
1870  else if ( e->key() == Qt::Key_Up )
1871  {
1872  if ( mCurrentTool == EditNodesItem )
1873  {
1874  if ( mNodesItemIndex != -1 )
1875  {
1876  QPointF currentPos;
1877 
1878  if ( mNodesItem->nodePosition( mNodesItemIndex, currentPos ) )
1879  {
1880  currentPos.setY( currentPos.y() - increment );
1881 
1882  composition()->beginCommand( mNodesItem, tr( "Move item node" ) );
1883  mNodesItem->moveNode( mNodesItemIndex, currentPos );
1884  composition()->endCommand();
1885 
1886  scene()->update();
1887  }
1888  }
1889  }
1890  else
1891  {
1892  for ( ; itemIt != composerItemList.end(); ++itemIt )
1893  {
1894  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
1895  ( *itemIt )->move( 0.0, -1 * increment );
1896  ( *itemIt )->endCommand();
1897  }
1898  }
1899  }
1900 }
1901 
1903 {
1904  if ( e->key() == Qt::Key_Space && !e->isAutoRepeat() && mKeyPanning )
1905  {
1906  //end of panning with space key
1907  mKeyPanning = false;
1908 
1909  //reset cursor
1910  if ( mCurrentTool != Pan )
1911  {
1912  if ( composition() )
1913  {
1914  //allow cursor changes again
1915  composition()->setPreventCursorChange( false );
1916  }
1917  }
1918  viewport()->setCursor( defaultCursorForTool( mCurrentTool ) );
1919  return;
1920  }
1921  else if ( e->key() == Qt::Key_Space && !e->isAutoRepeat() && mTemporaryZoomStatus != QgsComposerView::Inactive )
1922  {
1923  //temporary keyboard-based zoom tool is active and space key has been released
1924  if ( mMarqueeZoom )
1925  {
1926  //currently in the middle of a marquee operation, so don't switch tool back immediately
1927  //instead, wait until mouse button has been released before switching tool back
1928  mTemporaryZoomStatus = QgsComposerView::ActiveUntilMouseRelease;
1929  }
1930  else
1931  {
1932  //switch tool back
1933  mTemporaryZoomStatus = QgsComposerView::Inactive;
1934  setCurrentTool( mPreviousTool );
1935  }
1936  }
1937  else if ( mCurrentTool == QgsComposerView::Zoom )
1938  {
1939  //if zoom tool is active, respond to changes in the shift key status and update cursor accordingly
1940  if ( ! e->isAutoRepeat() )
1941  {
1942  QPixmap myZoomQPixmap = QPixmap(( const char ** )(( e->modifiers() & Qt::ShiftModifier ) ? zoom_out : zoom_in ) );
1943  QCursor zoomCursor = QCursor( myZoomQPixmap, 7, 7 );
1944  viewport()->setCursor( zoomCursor );
1945  }
1946  return;
1947  }
1948 }
1949 
1950 void QgsComposerView::wheelEvent( QWheelEvent* event )
1951 {
1952  if ( mRubberBandItem || mRubberBandLineItem )
1953  {
1954  //ignore wheel events while marquee operations are active (eg, creating new item)
1955  return;
1956  }
1957 
1958  if ( composition()->selectionHandles()->isDragging() || composition()->selectionHandles()->isResizing() )
1959  {
1960  //ignore wheel events while dragging/resizing items
1961  return;
1962  }
1963 
1964  if ( currentTool() == MoveItemContent )
1965  {
1966  //move item content tool, so scroll events get handled by the selected composer item
1967 
1968  QPointF scenePoint = mapToScene( event->pos() );
1969  //select topmost item at position of event
1970  QgsComposerItem* theItem = composition()->composerItemAt( scenePoint, true );
1971  if ( theItem )
1972  {
1973  if ( theItem->isSelected() )
1974  {
1975  QSettings settings;
1976  //read zoom mode
1977  QgsComposerItem::ZoomMode zoomMode = ( QgsComposerItem::ZoomMode )settings.value( QStringLiteral( "/qgis/wheel_action" ), 2 ).toInt();
1978  if ( zoomMode == QgsComposerItem::NoZoom )
1979  {
1980  //do nothing
1981  return;
1982  }
1983 
1984  double zoomFactor = settings.value( QStringLiteral( "/qgis/zoom_factor" ), 2.0 ).toDouble();
1985  if ( event->modifiers() & Qt::ControlModifier )
1986  {
1987  //holding ctrl while wheel zooming results in a finer zoom
1988  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
1989  }
1990  zoomFactor = event->delta() > 0 ? zoomFactor : 1 / zoomFactor;
1991 
1992  QPointF itemPoint = theItem->mapFromScene( scenePoint );
1993  theItem->beginCommand( tr( "Zoom item content" ), QgsComposerMergeCommand::ItemZoomContent );
1994  theItem->zoomContent( zoomFactor, itemPoint, zoomMode );
1995  theItem->endCommand();
1996  }
1997  }
1998  }
1999  else
2000  {
2001  //not using move item content tool, so zoom whole composition
2002  wheelZoom( event );
2003  }
2004 }
2005 
2006 void QgsComposerView::wheelZoom( QWheelEvent * event )
2007 {
2008  //get mouse wheel zoom behaviour settings
2009  QSettings mySettings;
2010  double zoomFactor = mySettings.value( QStringLiteral( "/qgis/zoom_factor" ), 2 ).toDouble();
2011 
2012  if ( event->modifiers() & Qt::ControlModifier )
2013  {
2014  //holding ctrl while wheel zooming results in a finer zoom
2015  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 10.0;
2016  }
2017 
2018  //caculate zoom scale factor
2019  bool zoomIn = event->delta() > 0;
2020  double scaleFactor = ( zoomIn ? 1 / zoomFactor : zoomFactor );
2021 
2022  //get current visible part of scene
2023  QRect viewportRect( 0, 0, viewport()->width(), viewport()->height() );
2024  QgsRectangle visibleRect = QgsRectangle( mapToScene( viewportRect ).boundingRect() );
2025 
2026  //transform the mouse pos to scene coordinates
2027  QPointF scenePoint = mapToScene( event->pos() );
2028 
2029  //adjust view center
2030  QgsPoint oldCenter( visibleRect.center() );
2031  QgsPoint newCenter( scenePoint.x() + (( oldCenter.x() - scenePoint.x() ) * scaleFactor ),
2032  scenePoint.y() + (( oldCenter.y() - scenePoint.y() ) * scaleFactor ) );
2033  centerOn( newCenter.x(), newCenter.y() );
2034 
2035  //zoom composition
2036  if ( zoomIn )
2037  {
2038  scaleSafe( zoomFactor );
2039  }
2040  else
2041  {
2042  scaleSafe( 1 / zoomFactor );
2043  }
2044 
2045  //update composition for new zoom
2046  emit zoomLevelChanged();
2047  updateRulers();
2048  update();
2049  //redraw cached map items
2050  QList<QGraphicsItem *> itemList = composition()->items();
2051  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
2052  for ( ; itemIt != itemList.end(); ++itemIt )
2053  {
2054  QgsComposerMap* mypItem = dynamic_cast<QgsComposerMap *>( *itemIt );
2055  if (( mypItem ) && ( mypItem->previewMode() == QgsComposerMap::Render ) )
2056  {
2057  mypItem->updateCachedImage();
2058  }
2059  }
2060 }
2061 
2062 void QgsComposerView::setZoomLevel( double zoomLevel )
2063 {
2064  double dpi = QgsApplication::desktop()->logicalDpiX();
2065  //monitor dpi is not always correct - so make sure the value is sane
2066  if (( dpi < 60 ) || ( dpi > 250 ) )
2067  dpi = 72;
2068 
2069  //desired pixel width for 1mm on screen
2070  double scale = qBound( MIN_VIEW_SCALE, zoomLevel * dpi / 25.4, MAX_VIEW_SCALE );
2071  setTransform( QTransform::fromScale( scale, scale ) );
2072 
2073  updateRulers();
2074  update();
2075  emit zoomLevelChanged();
2076 }
2077 
2078 void QgsComposerView::scaleSafe( double scale )
2079 {
2080  double currentScale = transform().m11();
2081  scale *= currentScale;
2082  scale = qBound( MIN_VIEW_SCALE, scale, MAX_VIEW_SCALE );
2083  setTransform( QTransform::fromScale( scale, scale ) );
2084 }
2085 
2087 {
2088  if ( !mPreviewEffect )
2089  {
2090  return;
2091  }
2092 
2093  mPreviewEffect->setEnabled( enabled );
2094 }
2095 
2097 {
2098  if ( !mPreviewEffect )
2099  {
2100  return;
2101  }
2102 
2103  mPreviewEffect->setMode( mode );
2104 }
2105 
2106 void QgsComposerView::paintEvent( QPaintEvent* event )
2107 {
2108  if ( mPaintingEnabled )
2109  {
2110  QGraphicsView::paintEvent( event );
2111  event->accept();
2112  }
2113  else
2114  {
2115  event->ignore();
2116  }
2117 }
2118 
2119 void QgsComposerView::hideEvent( QHideEvent* e )
2120 {
2121  emit composerViewHide( this );
2122  e->ignore();
2123 }
2124 
2125 void QgsComposerView::showEvent( QShowEvent* e )
2126 {
2127  emit composerViewShow( this );
2128  e->ignore();
2129 }
2130 
2131 void QgsComposerView::resizeEvent( QResizeEvent* event )
2132 {
2133  QGraphicsView::resizeEvent( event );
2134  emit zoomLevelChanged();
2135  updateRulers();
2136 }
2137 
2139 {
2140  QGraphicsView::scrollContentsBy( dx, dy );
2141  updateRulers();
2142 }
2143 
2145 {
2146  setScene( c );
2147  if ( mHorizontalRuler )
2148  {
2149  mHorizontalRuler->setComposition( c );
2150  }
2151  if ( mVerticalRuler )
2152  {
2153  mVerticalRuler->setComposition( c );
2154  }
2155 
2156  //emit compositionSet, so that composer windows can update for the new composition
2157  emit compositionSet( c );
2158 }
2159 
2161 {
2162  if ( scene() )
2163  {
2164  QgsComposition* c = dynamic_cast<QgsComposition *>( scene() );
2165  if ( c )
2166  {
2167  return c;
2168  }
2169  }
2170  return nullptr;
2171 }
2172 
2174 {
2175  if ( !composition() )
2176  {
2177  return;
2178  }
2179 
2180  //group selected items
2181  QList<QgsComposerItem*> selectionList = composition()->selectedComposerItems();
2182  QgsComposerItemGroup* itemGroup = composition()->groupItems( selectionList );
2183 
2184  if ( !itemGroup )
2185  {
2186  //group could not be created
2187  return;
2188  }
2189 
2190  itemGroup->setSelected( true );
2191  emit selectedItemChanged( itemGroup );
2192 }
2193 
2195 {
2196  if ( !composition() )
2197  {
2198  return;
2199  }
2200 
2201  //hunt through selection for any groups, and ungroup them
2202  QList<QgsComposerItem*> selectionList = composition()->selectedComposerItems();
2203  QList<QgsComposerItem*>::iterator itemIter = selectionList.begin();
2204  for ( ; itemIter != selectionList.end(); ++itemIter )
2205  {
2206  QgsComposerItemGroup* itemGroup = dynamic_cast<QgsComposerItemGroup *>( *itemIter );
2207  if ( itemGroup )
2208  {
2209  composition()->ungroupItems( itemGroup );
2210  }
2211  }
2212 }
2213 
2215 {
2216  QMainWindow* composerObject = nullptr;
2217  QObject* currentObject = parent();
2218  if ( !currentObject )
2219  {
2220  return qobject_cast<QMainWindow *>( currentObject );
2221  }
2222 
2223  while ( true )
2224  {
2225  composerObject = qobject_cast<QMainWindow*>( currentObject );
2226  if ( composerObject || !currentObject->parent() )
2227  {
2228  return composerObject;
2229  }
2230  currentObject = currentObject->parent();
2231  }
2232 
2233  return nullptr;
2234 }
2235 
2236 void QgsComposerView::addPolygonNode( QPointF scenePoint )
2237 {
2238  QPolygonF polygon = mPolygonItem.data()->polygon();
2239  polygon.append( QPointF( scenePoint.x(), scenePoint.y() ) );
2240 
2241  if ( polygon.size() == 1 )
2242  polygon.append( QPointF( scenePoint.x(), scenePoint.y() ) );
2243 
2244  mPolygonItem.data()->setPolygon( polygon );
2245 }
2246 
2247 void QgsComposerView::movePolygonNode( QPointF scenePoint )
2248 {
2249  QPolygonF polygon = mPolygonItem.data()->polygon();
2250 
2251  if ( polygon.size() > 0 )
2252  {
2253  polygon.replace( polygon.size() - 1, scenePoint );
2254  mPolygonItem.data()->setPolygon( polygon );
2255  }
2256 }
2257 
2258 void QgsComposerView::displayNodes( const bool display )
2259 {
2260  QList<QgsComposerNodesItem*> nodesShapes;
2261  composition()->composerItems( nodesShapes );
2262 
2263  QList<QgsComposerNodesItem*>::iterator it = nodesShapes.begin();
2264  for ( ; it != nodesShapes.end(); ++it )
2265  ( *it )->setDisplayNodes( display );
2266 
2267  scene()->update();
2268 }
2269 
2270 void QgsComposerView::setSelectedNode( QgsComposerNodesItem *shape,
2271  const int index )
2272 {
2273  QList<QgsComposerNodesItem*> nodesShapes;
2274  composition()->composerItems( nodesShapes );
2275 
2276  QList<QgsComposerNodesItem*>::iterator it = nodesShapes.begin();
2277  for ( ; it != nodesShapes.end(); ++it )
2278  {
2279  if (( *it ) == shape )
2280  {
2281  ( *it )->setSelectedNode( index );
2282  selectNone();
2283  ( *it )->setSelected( true );
2284  emit selectedItemChanged(( *it ) );
2285  }
2286  else
2287  ( *it )->unselectNode();
2288  }
2289 
2290  scene()->update();
2291 }
2292 
2293 void QgsComposerView::unselectNode()
2294 {
2295  QList<QgsComposerNodesItem*> nodesShapes;
2296  composition()->composerItems( nodesShapes );
2297 
2298  QList<QgsComposerNodesItem*>::iterator it = nodesShapes.begin();
2299  for ( ; it != nodesShapes.end(); ++it )
2300  ( *it )->unselectNode();
2301 
2302  scene()->update();
2303 }
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:35
QgsPoint center() const
Center point of the rectangle.
Definition: qgsrectangle.h:221
void setAllUnselected()
Clears any selected items in the composition.
Composer item for polylines.
QMainWindow * composerWindow()
Returns the composer main window.
void composerItems(QList< T *> &itemList)
Return composer items of a specific type.
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 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.
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.
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...
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.
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:111
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 copyItems(ClipboardMode mode)
Cuts or copies the selected items.
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)