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