QGIS API Documentation  2.14.11-Essen
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 : blazek@itc.it
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 "qgscomposerhtml.h"
33 #include "qgscomposerlabel.h"
34 #include "qgscomposerlegend.h"
35 #include "qgscomposermap.h"
37 #include "qgscomposeritemgroup.h"
38 #include "qgscomposerpicture.h"
39 #include "qgscomposerruler.h"
40 #include "qgscomposerscalebar.h"
41 #include "qgscomposershape.h"
43 #include "qgslogger.h"
45 #include "qgspaperitem.h"
46 #include "qgsmapcanvas.h" //for QgsMapCanvas::WheelAction
47 #include "qgscursors.h"
48 #include "qgscomposerutils.h"
49 
50 #define MIN_VIEW_SCALE 0.05
51 #define MAX_VIEW_SCALE 1000.0
52 
53 QgsComposerView::QgsComposerView( QWidget* parent, const char* name, const Qt::WindowFlags& f )
54  : QGraphicsView( parent )
55  , mCurrentTool( Select )
56  , mPreviousTool( Select )
57  , mRubberBandItem( nullptr )
58  , mRubberBandLineItem( nullptr )
59  , mMoveContentItem( nullptr )
60  , mMarqueeSelect( false )
61  , mMarqueeZoom( false )
62  , mTemporaryZoomStatus( QgsComposerView::Inactive )
63  , mPaintingEnabled( true )
64  , mHorizontalRuler( nullptr )
65  , mVerticalRuler( nullptr )
66  , mToolPanning( false )
67  , mMousePanning( false )
68  , mKeyPanning( false )
69  , mMovingItemContent( false )
70  , mPreviewEffect( nullptr )
71 {
72  Q_UNUSED( f );
73  Q_UNUSED( name );
74 
75  setResizeAnchor( QGraphicsView::AnchorViewCenter );
76  setMouseTracking( true );
77  viewport()->setMouseTracking( true );
78  setFrameShape( QFrame::NoFrame );
79 
80  mPreviewEffect = new QgsPreviewEffect( this );
81  viewport()->setGraphicsEffect( mPreviewEffect );
82 }
83 
85 {
86  mCurrentTool = t;
87 
88  //update mouse cursor for current tool
89  if ( !composition() )
90  {
91  return;
92  }
93  switch ( t )
94  {
96  {
97  //lock cursor to prevent composer items changing it
99  viewport()->setCursor( defaultCursorForTool( Pan ) );
100  break;
101  }
103  {
104  //lock cursor to prevent composer items changing it
106  //set the cursor to zoom in
107  viewport()->setCursor( defaultCursorForTool( Zoom ) );
108  break;
109  }
122  {
123  //using a drawing tool
124  //lock cursor to prevent composer items changing it
126  viewport()->setCursor( defaultCursorForTool( mCurrentTool ) );
127  break;
128  }
129  default:
130  {
131  //not using pan tool, composer items can change cursor
133  viewport()->setCursor( Qt::ArrowCursor );
134  }
135  }
136 }
137 
139 {
140  if ( !composition() )
141  {
142  return;
143  }
144 
145  if ( mRubberBandItem || mRubberBandLineItem || mKeyPanning || mMousePanning || mToolPanning || mMovingItemContent )
146  {
147  //ignore clicks during certain operations
148  return;
149  }
150 
151  if ( composition()->selectionHandles()->isDragging() || composition()->selectionHandles()->isResizing() )
152  {
153  //ignore clicks while dragging/resizing items
154  return;
155  }
156 
157  QPointF scenePoint = mapToScene( e->pos() );
158  QPointF snappedScenePoint = composition()->snapPointToGrid( scenePoint );
159  mMousePressStartPos = e->pos();
160 
161  if ( e->button() == Qt::RightButton )
162  {
163  //ignore right clicks for now
164  //TODO - show context menu
165  return;
166  }
167  else if ( e->button() == Qt::MidButton )
168  {
169  //pan composer with middle button
170  mMousePanning = true;
171  mMouseLastXY = e->pos();
172  if ( composition() )
173  {
174  //lock cursor to closed hand cursor
176  }
177  viewport()->setCursor( Qt::ClosedHandCursor );
178  return;
179  }
180 
181  switch ( mCurrentTool )
182  {
183  //select/deselect items and pass mouse event further
184  case Select:
185  {
186  //check if we are clicking on a selection handle
187  if ( composition()->selectionHandles()->isVisible() )
188  {
189  //selection handles are being shown, get mouse action for current cursor position
191 
193  {
194  //mouse is over a resize handle, so propagate event onward
196  return;
197  }
198  }
199 
200  QgsComposerItem* selectedItem = nullptr;
201  QgsComposerItem* previousSelectedItem = nullptr;
202 
203  if ( e->modifiers() & Qt::ControlModifier )
204  {
205  //CTRL modifier, so we are trying to select the next item below the current one
206  //first, find currently selected item
208  if ( !selectedItems.isEmpty() )
209  {
210  previousSelectedItem = selectedItems.at( 0 );
211  }
212  }
213 
214  if ( previousSelectedItem )
215  {
216  //select highest item just below previously selected item at position of event
217  selectedItem = composition()->composerItemAt( scenePoint, previousSelectedItem, true );
218 
219  //if we didn't find a lower item we'll use the top-most as fall-back
220  //this duplicates mapinfo/illustrator/etc behaviour where ctrl-clicks are "cyclic"
221  if ( !selectedItem )
222  {
223  selectedItem = composition()->composerItemAt( scenePoint, true );
224  }
225  }
226  else
227  {
228  //select topmost item at position of event
229  selectedItem = composition()->composerItemAt( scenePoint, true );
230  }
231 
232  if ( !selectedItem )
233  {
234  //not clicking over an item, so start marquee selection
235  startMarqueeSelect( scenePoint );
236  break;
237  }
238 
239  if (( !selectedItem->selected() ) && //keep selection if an already selected item pressed
240  !( e->modifiers() & Qt::ShiftModifier ) ) //keep selection if shift key pressed
241  {
243  }
244 
245  if (( e->modifiers() & Qt::ShiftModifier ) && ( selectedItem->selected() ) )
246  {
247  //SHIFT-clicking a selected item deselects it
248  selectedItem->setSelected( false );
249 
250  //Check if we have any remaining selected items, and if so, update the item panel
252  if ( !selectedItems.isEmpty() )
253  {
254  emit selectedItemChanged( selectedItems.at( 0 ) );
255  }
256  }
257  else
258  {
259  selectedItem->setSelected( true );
261  emit selectedItemChanged( selectedItem );
262  }
263  break;
264  }
265 
266  case Zoom:
267  {
268  if ( !( e->modifiers() & Qt::ShiftModifier ) )
269  {
270  //zoom in action
271  startMarqueeZoom( scenePoint );
272  }
273  else
274  {
275  //zoom out action, so zoom out and recenter on clicked point
276  double scaleFactor = 2;
277  //get current visible part of scene
278  QRect viewportRect( 0, 0, viewport()->width(), viewport()->height() );
279  QgsRectangle visibleRect = QgsRectangle( mapToScene( viewportRect ).boundingRect() );
280 
281  //transform the mouse pos to scene coordinates
282  QPointF scenePoint = mapToScene( e->pos() );
283 
284  visibleRect.scale( scaleFactor, scenePoint.x(), scenePoint.y() );
285  QRectF boundsRect = visibleRect.toRectF();
286 
287  //zoom view to fit desired bounds
288  fitInView( boundsRect, Qt::KeepAspectRatio );
289  }
290  break;
291  }
292 
293  case Pan:
294  {
295  //pan action
296  mToolPanning = true;
297  mMouseLastXY = e->pos();
298  viewport()->setCursor( Qt::ClosedHandCursor );
299  break;
300  }
301 
302  case MoveItemContent:
303  {
304  //get a list of items at clicked position
305  QList<QGraphicsItem *> itemsAtCursorPos = items( e->pos() );
306  if ( itemsAtCursorPos.isEmpty() )
307  {
308  //no items at clicked position
309  return;
310  }
311 
312  //find highest non-locked QgsComposerItem at clicked position
313  //(other graphics items may be higher, eg selection handles)
314  QList<QGraphicsItem*>::iterator itemIter = itemsAtCursorPos.begin();
315  for ( ; itemIter != itemsAtCursorPos.end(); ++itemIter )
316  {
317  QgsComposerItem* item = dynamic_cast<QgsComposerItem *>(( *itemIter ) );
318  if ( item && !item->positionLock() )
319  {
320  //we've found the highest QgsComposerItem
321  mMoveContentStartPos = scenePoint;
322  mMoveContentItem = item;
323  mMovingItemContent = true;
324  break;
325  }
326  }
327 
328  //no QgsComposerItem at clicked position
329  return;
330  }
331 
332  //create rubber band for adding line items
333  case AddArrow:
334  {
335  mRubberBandStartPos = QPointF( snappedScenePoint.x(), snappedScenePoint.y() );
336  mRubberBandLineItem = new QGraphicsLineItem( snappedScenePoint.x(), snappedScenePoint.y(), snappedScenePoint.x(), snappedScenePoint.y() );
337  mRubberBandLineItem->setPen( QPen( QBrush( QColor( 227, 22, 22, 200 ) ), 0 ) );
338  mRubberBandLineItem->setZValue( 1000 );
339  scene()->addItem( mRubberBandLineItem );
340  scene()->update();
341  break;
342  }
343 
344  //create rubber band for adding rectangular items
345  case AddMap:
346  case AddRectangle:
347  case AddTriangle:
348  case AddEllipse:
349  case AddHtml:
350  case AddPicture:
351  case AddLabel:
352  case AddLegend:
353  case AddTable:
354  case AddAttributeTable:
355  {
356  QTransform t;
357  mRubberBandItem = new QGraphicsRectItem( 0, 0, 0, 0 );
358  mRubberBandItem->setBrush( Qt::NoBrush );
359  mRubberBandItem->setPen( QPen( QBrush( QColor( 227, 22, 22, 200 ) ), 0 ) );
360  mRubberBandStartPos = QPointF( snappedScenePoint.x(), snappedScenePoint.y() );
361  t.translate( snappedScenePoint.x(), snappedScenePoint.y() );
362  mRubberBandItem->setTransform( t );
363  mRubberBandItem->setZValue( 1000 );
364  scene()->addItem( mRubberBandItem );
365  scene()->update();
366  }
367  break;
368 
369  case AddScalebar:
370  if ( composition() )
371  {
372  QgsComposerScaleBar* newScaleBar = new QgsComposerScaleBar( composition() );
373  newScaleBar->setSceneRect( QRectF( snappedScenePoint.x(), snappedScenePoint.y(), 20, 20 ) );
374  composition()->addComposerScaleBar( newScaleBar );
376  if ( !mapItemList.isEmpty() )
377  {
378  newScaleBar->setComposerMap( mapItemList.at( 0 ) );
379  }
380  newScaleBar->applyDefaultSize(); //4 segments, 1/5 of composer map width
381 
383  newScaleBar->setSelected( true );
384  emit selectedItemChanged( newScaleBar );
385 
386  emit actionFinished();
387  composition()->pushAddRemoveCommand( newScaleBar, tr( "Scale bar added" ) );
388  }
389  break;
390 
391  default:
392  break;
393  }
394 }
395 
396 QCursor QgsComposerView::defaultCursorForTool( Tool currentTool )
397 {
398  switch ( currentTool )
399  {
400  case Select:
401  return Qt::ArrowCursor;
402 
403  case Zoom:
404  {
405  QPixmap myZoomQPixmap = QPixmap(( const char ** )( zoom_in ) );
406  return QCursor( myZoomQPixmap, 7, 7 );
407  }
408 
409  case Pan:
410  return Qt::OpenHandCursor;
411 
412  case MoveItemContent:
413  return Qt::ArrowCursor;
414 
415  case AddArrow:
416  case AddMap:
417  case AddRectangle:
418  case AddTriangle:
419  case AddEllipse:
420  case AddHtml:
421  case AddLabel:
422  case AddScalebar:
423  case AddLegend:
424  case AddPicture:
425  case AddTable:
426  case AddAttributeTable:
427  {
428  QPixmap myCrosshairQPixmap = QPixmap(( const char ** )( cross_hair_cursor ) );
429  return QCursor( myCrosshairQPixmap, 8, 8 );
430  }
431  }
432  return Qt::ArrowCursor;
433 }
434 
435 void QgsComposerView::addShape( Tool currentTool )
436 {
438 
439  if ( currentTool == AddRectangle )
441  else if ( currentTool == AddTriangle )
443 
444  if ( !mRubberBandItem || ( mRubberBandItem->rect().width() < 0.1 && mRubberBandItem->rect().height() < 0.1 ) )
445  {
446  removeRubberBand();
447  return;
448  }
449  if ( composition() )
450  {
451  QgsComposerShape* composerShape = new QgsComposerShape( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height(), composition() );
452  composerShape->setShapeType( shape );
453  //new shapes use symbol v2 by default
454  composerShape->setUseSymbolV2( true );
455  composition()->addComposerShape( composerShape );
456  removeRubberBand();
457 
459  composerShape->setSelected( true );
460  emit selectedItemChanged( composerShape );
461 
462  emit actionFinished();
463  composition()->pushAddRemoveCommand( composerShape, tr( "Shape added" ) );
464  }
465 }
466 
468 {
469  if ( mHorizontalRuler )
470  {
471  mHorizontalRuler->setSceneTransform( viewportTransform() );
472  }
473  if ( mVerticalRuler )
474  {
475  mVerticalRuler->setSceneTransform( viewportTransform() );
476  }
477 }
478 
479 void QgsComposerView::removeRubberBand()
480 {
481  if ( mRubberBandItem )
482  {
483  scene()->removeItem( mRubberBandItem );
484  delete mRubberBandItem;
485  mRubberBandItem = nullptr;
486  }
487 }
488 
489 void QgsComposerView::startMarqueeSelect( QPointF & scenePoint )
490 {
491  mMarqueeSelect = true;
492 
493  QTransform t;
494  mRubberBandItem = new QGraphicsRectItem( 0, 0, 0, 0 );
495  mRubberBandItem->setBrush( QBrush( QColor( 224, 178, 76, 63 ) ) );
496  mRubberBandItem->setPen( QPen( QBrush( QColor( 254, 58, 29, 100 ) ), 0, Qt::DotLine ) );
497  mRubberBandStartPos = QPointF( scenePoint.x(), scenePoint.y() );
498  t.translate( scenePoint.x(), scenePoint.y() );
499  mRubberBandItem->setTransform( t );
500  mRubberBandItem->setZValue( 1000 );
501  scene()->addItem( mRubberBandItem );
502  scene()->update();
503 }
504 
505 void QgsComposerView::endMarqueeSelect( QMouseEvent* e )
506 {
507  mMarqueeSelect = false;
508 
509  bool subtractingSelection = false;
510  if ( e->modifiers() & Qt::ShiftModifier )
511  {
512  //shift modifer means adding to selection, nothing required here
513  }
514  else if ( e->modifiers() & Qt::ControlModifier )
515  {
516  //control modifier means subtract from current selection
517  subtractingSelection = true;
518  }
519  else
520  {
521  //not adding to or removing from selection, so clear current selection
523  }
524 
525  if ( !mRubberBandItem || ( mRubberBandItem->rect().width() < 0.1 && mRubberBandItem->rect().height() < 0.1 ) )
526  {
527  //just a click, do nothing
528  removeRubberBand();
529  return;
530  }
531 
532  QRectF boundsRect = QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(),
533  mRubberBandItem->rect().width(), mRubberBandItem->rect().height() );
534 
535  //determine item selection mode, default to intersection
536  Qt::ItemSelectionMode selectionMode = Qt::IntersectsItemShape;
537  if ( e->modifiers() & Qt::AltModifier )
538  {
539  //alt modifier switches to contains selection mode
540  selectionMode = Qt::ContainsItemShape;
541  }
542 
543  //find all items in rubber band
544  QList<QGraphicsItem *> itemList = composition()->items( boundsRect, selectionMode );
545  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
546  for ( ; itemIt != itemList.end(); ++itemIt )
547  {
548  QgsComposerItem* mypItem = dynamic_cast<QgsComposerItem *>( *itemIt );
549  QgsPaperItem* paperItem = dynamic_cast<QgsPaperItem*>( *itemIt );
550  if ( mypItem && !paperItem )
551  {
552  if ( !mypItem->positionLock() )
553  {
554  if ( subtractingSelection )
555  {
556  mypItem->setSelected( false );
557  }
558  else
559  {
560  mypItem->setSelected( true );
561  }
562  }
563  }
564  }
565  removeRubberBand();
566 
567  //update item panel
569  if ( !selectedItemList.isEmpty() )
570  {
571  emit selectedItemChanged( selectedItemList[0] );
572  }
573 }
574 
575 void QgsComposerView::startMarqueeZoom( QPointF & scenePoint )
576 {
577  mMarqueeZoom = true;
578 
579  QTransform t;
580  mRubberBandItem = new QGraphicsRectItem( 0, 0, 0, 0 );
581  mRubberBandItem->setBrush( QBrush( QColor( 70, 50, 255, 25 ) ) );
582  mRubberBandItem->setPen( QPen( QColor( 70, 50, 255, 100 ) ) );
583  mRubberBandStartPos = QPointF( scenePoint.x(), scenePoint.y() );
584  t.translate( scenePoint.x(), scenePoint.y() );
585  mRubberBandItem->setTransform( t );
586  mRubberBandItem->setZValue( 1000 );
587  scene()->addItem( mRubberBandItem );
588  scene()->update();
589 }
590 
591 void QgsComposerView::endMarqueeZoom( QMouseEvent* e )
592 {
593  mMarqueeZoom = false;
594 
595  QRectF boundsRect;
596 
597  if ( !mRubberBandItem || ( mRubberBandItem->rect().width() < 0.1 && mRubberBandItem->rect().height() < 0.1 ) )
598  {
599  //just a click, so zoom to clicked point and recenter
600  double scaleFactor = 0.5;
601  //get current visible part of scene
602  QRect viewportRect( 0, 0, viewport()->width(), viewport()->height() );
603  QgsRectangle visibleRect = QgsRectangle( mapToScene( viewportRect ).boundingRect() );
604 
605  //transform the mouse pos to scene coordinates
606  QPointF scenePoint = mapToScene( e->pos() );
607 
608  visibleRect.scale( scaleFactor, scenePoint.x(), scenePoint.y() );
609  boundsRect = visibleRect.toRectF();
610  }
611  else
612  {
613  //marquee zoom
614  //zoom bounds are size marquee object
615  boundsRect = QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(),
616  mRubberBandItem->rect().width(), mRubberBandItem->rect().height() );
617  }
618 
619  removeRubberBand();
620  //zoom view to fit desired bounds
621  fitInView( boundsRect, Qt::KeepAspectRatio );
622 
623  if ( mTemporaryZoomStatus == QgsComposerView::ActiveUntilMouseRelease )
624  {
625  //user was using the temporary keyboard activated zoom tool
626  //and the control or space key was released before mouse button, so end temporary zoom
627  mTemporaryZoomStatus = QgsComposerView::Inactive;
628  setCurrentTool( mPreviousTool );
629  }
630 }
631 
633 {
634  if ( !composition() )
635  {
636  return;
637  }
638 
639  if ( e->button() != Qt::LeftButton &&
641  {
642  //ignore clicks while dragging/resizing items
643  return;
644  }
645 
646  QPoint mousePressStopPoint = e->pos();
647  int diffX = mousePressStopPoint.x() - mMousePressStartPos.x();
648  int diffY = mousePressStopPoint.y() - mMousePressStartPos.y();
649 
650  //was this just a click? or a click and drag?
651  bool clickOnly = false;
652  if ( qAbs( diffX ) < 2 && qAbs( diffY ) < 2 )
653  {
654  clickOnly = true;
655  }
656 
657  QPointF scenePoint = mapToScene( e->pos() );
658 
659  if ( mMousePanning || mToolPanning )
660  {
661  mMousePanning = false;
662  mToolPanning = false;
663 
664  if ( clickOnly && e->button() == Qt::MidButton )
665  {
666  //middle mouse button click = recenter on point
667 
668  //get current visible part of scene
669  QRect viewportRect( 0, 0, viewport()->width(), viewport()->height() );
670  QgsRectangle visibleRect = QgsRectangle( mapToScene( viewportRect ).boundingRect() );
671  visibleRect.scale( 1, scenePoint.x(), scenePoint.y() );
672  QRectF boundsRect = visibleRect.toRectF();
673 
674  //zoom view to fit desired bounds
675  fitInView( boundsRect, Qt::KeepAspectRatio );
676  }
677 
678  //set new cursor
679  if ( mCurrentTool != Pan )
680  {
681  if ( composition() )
682  {
683  //allow composer items to change cursor
685  }
686  }
687  viewport()->setCursor( defaultCursorForTool( mCurrentTool ) );
688  }
689 
690  //for every other tool, ignore clicks of non-left button
691  if ( e->button() != Qt::LeftButton )
692  {
693  return;
694  }
695 
696  if ( mMarqueeSelect )
697  {
698  endMarqueeSelect( e );
699  return;
700  }
701 
702  switch ( mCurrentTool )
703  {
704  case Select:
705  {
707  break;
708  }
709 
710  case Zoom:
711  {
712  if ( mMarqueeZoom )
713  {
714  endMarqueeZoom( e );
715  }
716  break;
717  }
718 
719  case MoveItemContent:
720  {
721  if ( mMoveContentItem )
722  {
723  //update map preview if composer map
724  QgsComposerMap* composerMap = dynamic_cast<QgsComposerMap *>( mMoveContentItem );
725  if ( composerMap )
726  {
727  composerMap->setOffset( 0, 0 );
728  }
729 
730  double moveX = scenePoint.x() - mMoveContentStartPos.x();
731  double moveY = scenePoint.y() - mMoveContentStartPos.y();
732 
733  composition()->beginCommand( mMoveContentItem, tr( "Move item content" ) );
734  mMoveContentItem->moveContent( -moveX, -moveY );
735  composition()->endCommand();
736  mMoveContentItem = nullptr;
737  mMovingItemContent = false;
738  }
739  break;
740  }
741  case AddArrow:
742  if ( !composition() || !mRubberBandLineItem )
743  {
744  scene()->removeItem( mRubberBandLineItem );
745  delete mRubberBandLineItem;
746  mRubberBandLineItem = nullptr;
747  return;
748  }
749  else
750  {
751  QgsComposerArrow* composerArrow = new QgsComposerArrow( mRubberBandLineItem->line().p1(), mRubberBandLineItem->line().p2(), composition() );
752  composition()->addComposerArrow( composerArrow );
753 
755  composerArrow->setSelected( true );
756  emit selectedItemChanged( composerArrow );
757 
758  scene()->removeItem( mRubberBandLineItem );
759  delete mRubberBandLineItem;
760  mRubberBandLineItem = nullptr;
761  emit actionFinished();
762  composition()->pushAddRemoveCommand( composerArrow, tr( "Arrow added" ) );
763  }
764  break;
765 
766  case AddRectangle:
767  case AddTriangle:
768  case AddEllipse:
769  addShape( mCurrentTool );
770  break;
771 
772  case AddMap:
773  if ( !composition() || !mRubberBandItem || ( mRubberBandItem->rect().width() < 0.1 && mRubberBandItem->rect().height() < 0.1 ) )
774  {
775  removeRubberBand();
776  return;
777  }
778  else
779  {
780  QgsComposerMap* composerMap = new QgsComposerMap( composition(), mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height() );
781  composition()->addComposerMap( composerMap );
782 
784  composerMap->setSelected( true );
785  emit selectedItemChanged( composerMap );
786 
787  removeRubberBand();
788  emit actionFinished();
789  composition()->pushAddRemoveCommand( composerMap, tr( "Map added" ) );
790  }
791  break;
792 
793  case AddPicture:
794  if ( !composition() || !mRubberBandItem || ( mRubberBandItem->rect().width() < 0.1 && mRubberBandItem->rect().height() < 0.1 ) )
795  {
796  removeRubberBand();
797  return;
798  }
799  else
800  {
801  QgsComposerPicture* newPicture = new QgsComposerPicture( composition() );
802  newPicture->setSceneRect( QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height() ) );
803  composition()->addComposerPicture( newPicture );
804 
806  newPicture->setSelected( true );
807  emit selectedItemChanged( newPicture );
808 
809  removeRubberBand();
810  emit actionFinished();
811  composition()->pushAddRemoveCommand( newPicture, tr( "Picture added" ) );
812  }
813  break;
814 
815  case AddLabel:
816  if ( !composition() || !mRubberBandItem )
817  {
818  removeRubberBand();
819  return;
820  }
821  else
822  {
823  QgsComposerLabel* newLabelItem = new QgsComposerLabel( composition() );
824  newLabelItem->setText( tr( "QGIS" ) );
825  newLabelItem->adjustSizeToText();
826 
827  //make sure label size is sufficient to fit text
828  double labelWidth = qMax( mRubberBandItem->rect().width(), newLabelItem->rect().width() );
829  double labelHeight = qMax( mRubberBandItem->rect().height(), newLabelItem->rect().height() );
830  newLabelItem->setSceneRect( QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), labelWidth, labelHeight ) );
831 
832  composition()->addComposerLabel( newLabelItem );
833 
835  newLabelItem->setSelected( true );
836  emit selectedItemChanged( newLabelItem );
837 
838  removeRubberBand();
839  emit actionFinished();
840  composition()->pushAddRemoveCommand( newLabelItem, tr( "Label added" ) );
841  }
842  break;
843 
844  case AddLegend:
845  if ( !composition() || !mRubberBandItem )
846  {
847  removeRubberBand();
848  return;
849  }
850  else
851  {
852  QgsComposerLegend* newLegend = new QgsComposerLegend( composition() );
854  if ( !mapItemList.isEmpty() )
855  {
856  newLegend->setComposerMap( mapItemList.at( 0 ) );
857  }
858  newLegend->setSceneRect( QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height() ) );
859  composition()->addComposerLegend( newLegend );
860  newLegend->updateLegend();
861 
863  newLegend->setSelected( true );
864  emit selectedItemChanged( newLegend );
865 
866  removeRubberBand();
867  emit actionFinished();
868  composition()->pushAddRemoveCommand( newLegend, tr( "Legend added" ) );
869  }
870  break;
871 
872  case AddTable:
873  if ( !composition() || !mRubberBandItem )
874  {
875  removeRubberBand();
876  return;
877  }
878  else
879  {
882  if ( !mapItemList.isEmpty() )
883  {
884  newTable->setComposerMap( mapItemList.at( 0 ) );
885  }
886  newTable->setSceneRect( QRectF( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height() ) );
887 
888  composition()->addComposerTable( newTable );
889 
891  newTable->setSelected( true );
892  emit selectedItemChanged( newTable );
893 
894  removeRubberBand();
895  emit actionFinished();
896  composition()->pushAddRemoveCommand( newTable, tr( "Table added" ) );
897  }
898  break;
899 
900  case AddAttributeTable:
901  if ( !composition() || !mRubberBandItem )
902  {
903  removeRubberBand();
904  return;
905  }
906  else
907  {
910  if ( !mapItemList.isEmpty() )
911  {
912  newTable->setComposerMap( mapItemList.at( 0 ) );
913  }
915  newTable, composition(), tr( "Attribute table added" ) );
916  composition()->undoStack()->push( command );
917  QgsComposerFrame* frame = new QgsComposerFrame( composition(), newTable, mRubberBandItem->transform().dx(),
918  mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(),
919  mRubberBandItem->rect().height() );
920  composition()->beginMultiFrameCommand( newTable, tr( "Attribute table frame added" ) );
921  newTable->addFrame( frame );
923 
925  frame->setSelected( true );
926  emit selectedItemChanged( frame );
927 
928  removeRubberBand();
929  emit actionFinished();
930  }
931  break;
932 
933  case AddHtml:
934  if ( !composition() || !mRubberBandItem || ( mRubberBandItem->rect().width() < 0.1 && mRubberBandItem->rect().height() < 0.1 ) )
935  {
936  removeRubberBand();
937  return;
938  }
939  else
940  {
941  QgsComposerHtml* composerHtml = new QgsComposerHtml( composition(), true );
943  composerHtml, composition(), tr( "HTML item added" ) );
944  composition()->undoStack()->push( command );
945  QgsComposerFrame* frame = new QgsComposerFrame( composition(), composerHtml, mRubberBandItem->transform().dx(),
946  mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(),
947  mRubberBandItem->rect().height() );
948  composition()->beginMultiFrameCommand( composerHtml, tr( "HTML frame added" ) );
949  composerHtml->addFrame( frame );
951 
953  frame->setSelected( true );
954  emit selectedItemChanged( frame );
955 
956  removeRubberBand();
957  emit actionFinished();
958  }
959  break;
960  default:
961  break;
962  }
963 }
964 
966 {
967  if ( !composition() )
968  {
969  return;
970  }
971 
972  bool shiftModifier = false;
973  bool altModifier = false;
974  if ( e->modifiers() & Qt::ShiftModifier )
975  {
976  //shift key depressed
977  shiftModifier = true;
978  }
979  if ( e->modifiers() & Qt::AltModifier )
980  {
981  //alt key depressed
982  altModifier = true;
983  }
984 
985  mMouseCurrentXY = e->pos();
986  //update cursor position in composer status bar
987  emit cursorPosChanged( mapToScene( e->pos() ) );
988 
989  updateRulers();
990  if ( mHorizontalRuler )
991  {
992  mHorizontalRuler->updateMarker( e->posF() );
993  }
994  if ( mVerticalRuler )
995  {
996  mVerticalRuler->updateMarker( e->posF() );
997  }
998 
999  if ( mToolPanning || mMousePanning || mKeyPanning )
1000  {
1001  //panning, so scroll view
1002  horizontalScrollBar()->setValue( horizontalScrollBar()->value() - ( e->x() - mMouseLastXY.x() ) );
1003  verticalScrollBar()->setValue( verticalScrollBar()->value() - ( e->y() - mMouseLastXY.y() ) );
1004  mMouseLastXY = e->pos();
1005  return;
1006  }
1007  else if ( e->buttons() == Qt::NoButton )
1008  {
1009  if ( mCurrentTool == Select )
1010  {
1012  }
1013  }
1014  else
1015  {
1016  QPointF scenePoint = mapToScene( e->pos() );
1017 
1018  if ( mMarqueeSelect || mMarqueeZoom )
1019  {
1020  updateRubberBandRect( scenePoint );
1021  return;
1022  }
1023 
1024  switch ( mCurrentTool )
1025  {
1026  case Select:
1028  break;
1029 
1030  case AddArrow:
1031  {
1032  updateRubberBandLine( scenePoint, shiftModifier );
1033  break;
1034  }
1035 
1036  case AddMap:
1037  case AddRectangle:
1038  case AddTriangle:
1039  case AddEllipse:
1040  case AddHtml:
1041  case AddPicture:
1042  case AddLabel:
1043  case AddLegend:
1044  case AddTable:
1045  case AddAttributeTable:
1046  //adjust rubber band item
1047  {
1048  updateRubberBandRect( scenePoint, shiftModifier, altModifier );
1049  break;
1050  }
1051 
1052  case MoveItemContent:
1053  {
1054  //update map preview if composer map
1055  QgsComposerMap* composerMap = dynamic_cast<QgsComposerMap *>( mMoveContentItem );
1056  if ( composerMap )
1057  {
1058  composerMap->setOffset( scenePoint.x() - mMoveContentStartPos.x(), scenePoint.y() - mMoveContentStartPos.y() );
1059  composerMap->update();
1060  }
1061  break;
1062  }
1063  default:
1064  break;
1065  }
1066  }
1067 }
1068 
1069 void QgsComposerView::updateRubberBandRect( QPointF & pos, const bool constrainSquare, const bool fromCenter )
1070 {
1071  if ( !mRubberBandItem )
1072  {
1073  return;
1074  }
1075 
1076  double x = 0;
1077  double y = 0;
1078  double width = 0;
1079  double height = 0;
1080 
1081  double dx = pos.x() - mRubberBandStartPos.x();
1082  double dy = pos.y() - mRubberBandStartPos.y();
1083 
1084  if ( constrainSquare )
1085  {
1086  if ( fabs( dx ) > fabs( dy ) )
1087  {
1088  width = fabs( dx );
1089  height = width;
1090  }
1091  else
1092  {
1093  height = fabs( dy );
1094  width = height;
1095  }
1096 
1097  x = mRubberBandStartPos.x() - (( dx < 0 ) ? width : 0 );
1098  y = mRubberBandStartPos.y() - (( dy < 0 ) ? height : 0 );
1099  }
1100  else
1101  {
1102  //not constraining
1103  if ( dx < 0 )
1104  {
1105  x = pos.x();
1106  width = -dx;
1107  }
1108  else
1109  {
1110  x = mRubberBandStartPos.x();
1111  width = dx;
1112  }
1113 
1114  if ( dy < 0 )
1115  {
1116  y = pos.y();
1117  height = -dy;
1118  }
1119  else
1120  {
1121  y = mRubberBandStartPos.y();
1122  height = dy;
1123  }
1124  }
1125 
1126  if ( fromCenter )
1127  {
1128  x = mRubberBandStartPos.x() - width;
1129  y = mRubberBandStartPos.y() - height;
1130  width *= 2.0;
1131  height *= 2.0;
1132  }
1133 
1134  mRubberBandItem->setRect( 0, 0, width, height );
1135  QTransform t;
1136  t.translate( x, y );
1137  mRubberBandItem->setTransform( t );
1138 }
1139 
1140 void QgsComposerView::updateRubberBandLine( QPointF pos, const bool constrainAngles )
1141 {
1142  if ( !mRubberBandLineItem )
1143  {
1144  return;
1145  }
1146 
1147  //snap to grid
1148  QPointF snappedScenePoint = composition()->snapPointToGrid( pos );
1149 
1150  QLineF newLine = QLineF( mRubberBandStartPos, snappedScenePoint );
1151 
1152  if ( constrainAngles )
1153  {
1154  //movement is contrained to 45 degree angles
1155  double angle = QgsComposerUtils::snappedAngle( newLine.angle() );
1156  newLine.setAngle( angle );
1157  }
1158 
1159  mRubberBandLineItem->setLine( newLine );
1160 }
1161 
1163 {
1164  e->ignore();
1165 }
1166 
1168 {
1169  if ( !composition() )
1170  {
1171  return;
1172  }
1173 
1175  QList<QgsComposerItem*>::iterator itemIt = composerItemList.begin();
1176 
1177  QDomDocument doc;
1178  QDomElement documentElement = doc.createElement( "ComposerItemClipboard" );
1179  for ( ; itemIt != composerItemList.end(); ++itemIt )
1180  {
1181  // copy each item in a group
1182  QgsComposerItemGroup* itemGroup = dynamic_cast<QgsComposerItemGroup*>( *itemIt );
1183  if ( itemGroup && composition() )
1184  {
1185  QSet<QgsComposerItem*> groupedItems = itemGroup->items();
1186  QSet<QgsComposerItem*>::iterator it = groupedItems.begin();
1187  for ( ; it != groupedItems.end(); ++it )
1188  {
1189  ( *it )->writeXML( documentElement, doc );
1190  }
1191  }
1192  ( *itemIt )->writeXML( documentElement, doc );
1193  if ( mode == ClipboardModeCut )
1194  {
1195  composition()->removeComposerItem( *itemIt );
1196  }
1197  }
1198  doc.appendChild( documentElement );
1199 
1200  //if it's a copy, we have to remove the UUIDs since we don't want any duplicate UUID
1201  if ( mode == ClipboardModeCopy )
1202  {
1203  // remove all uuid attributes
1204  QDomNodeList composerItemsNodes = doc.elementsByTagName( "ComposerItem" );
1205  for ( int i = 0; i < composerItemsNodes.count(); ++i )
1206  {
1207  QDomNode composerItemNode = composerItemsNodes.at( i );
1208  if ( composerItemNode.isElement() )
1209  {
1210  composerItemNode.toElement().removeAttribute( "uuid" );
1211  }
1212  }
1213  }
1214 
1215  QMimeData *mimeData = new QMimeData;
1216  mimeData->setData( "text/xml", doc.toByteArray() );
1217  QClipboard *clipboard = QApplication::clipboard();
1218  clipboard->setMimeData( mimeData );
1219 }
1220 
1222 {
1223  if ( !composition() )
1224  {
1225  return;
1226  }
1227 
1228  QDomDocument doc;
1229  QClipboard *clipboard = QApplication::clipboard();
1230  if ( doc.setContent( clipboard->mimeData()->data( "text/xml" ) ) )
1231  {
1232  QDomElement docElem = doc.documentElement();
1233  if ( docElem.tagName() == "ComposerItemClipboard" )
1234  {
1235  if ( composition() )
1236  {
1237  QPointF pt;
1239  {
1240  // place items at cursor position
1241  pt = mapToScene( mapFromGlobal( QCursor::pos() ) );
1242  }
1243  else
1244  {
1245  // place items in center of viewport
1246  pt = mapToScene( viewport()->rect().center() );
1247  }
1248  bool pasteInPlace = ( mode == PasteModeInPlace );
1249  composition()->addItemsFromXML( docElem, doc, nullptr, true, &pt, pasteInPlace );
1250  }
1251  }
1252  }
1253 
1254  //switch back to select tool so that pasted items can be moved/resized (#8958)
1256 }
1257 
1259 {
1260  if ( !composition() )
1261  {
1262  return;
1263  }
1264 
1266  QList<QgsComposerItem*>::iterator itemIt = composerItemList.begin();
1267 
1268  //delete selected items
1269  for ( ; itemIt != composerItemList.end(); ++itemIt )
1270  {
1271  if ( composition() )
1272  {
1273  composition()->removeComposerItem( *itemIt );
1274  }
1275  }
1276 }
1277 
1279 {
1280  if ( !composition() )
1281  {
1282  return;
1283  }
1284 
1285  //select all items in composer
1286  QList<QGraphicsItem *> itemList = composition()->items();
1287  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
1288  for ( ; itemIt != itemList.end(); ++itemIt )
1289  {
1290  QgsComposerItem* mypItem = dynamic_cast<QgsComposerItem *>( *itemIt );
1291  QgsPaperItem* paperItem = dynamic_cast<QgsPaperItem*>( *itemIt );
1292  if ( mypItem && !paperItem )
1293  {
1294  if ( !mypItem->positionLock() )
1295  {
1296  mypItem->setSelected( true );
1297  }
1298  else
1299  {
1300  //deselect all locked items
1301  mypItem->setSelected( false );
1302  }
1303  emit selectedItemChanged( mypItem );
1304  }
1305  }
1306 }
1307 
1309 {
1310  if ( !composition() )
1311  {
1312  return;
1313  }
1314 
1316 }
1317 
1319 {
1320  if ( !composition() )
1321  {
1322  return;
1323  }
1324 
1325  //check all items in composer
1326  QList<QGraphicsItem *> itemList = composition()->items();
1327  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
1328  for ( ; itemIt != itemList.end(); ++itemIt )
1329  {
1330  QgsComposerItem* mypItem = dynamic_cast<QgsComposerItem *>( *itemIt );
1331  QgsPaperItem* paperItem = dynamic_cast<QgsPaperItem*>( *itemIt );
1332  if ( mypItem && !paperItem )
1333  {
1334  //flip selected state for items (and deselect any locked items)
1335  if ( mypItem->selected() || mypItem->positionLock() )
1336  {
1337 
1338  mypItem->setSelected( false );
1339  }
1340  else
1341  {
1342  mypItem->setSelected( true );
1343  emit selectedItemChanged( mypItem );
1344  }
1345  }
1346  }
1347 }
1348 
1350 {
1351  if ( !composition() )
1352  {
1353  return;
1354  }
1355 
1356  if ( mKeyPanning || mMousePanning || mToolPanning || mMovingItemContent ||
1357  composition()->selectionHandles()->isDragging() || composition()->selectionHandles()->isResizing() )
1358  {
1359  return;
1360  }
1361 
1362  if ( mTemporaryZoomStatus != QgsComposerView::Inactive )
1363  {
1364  //temporary keyboard based zoom is active
1365  if ( e->isAutoRepeat() )
1366  {
1367  return;
1368  }
1369 
1370  //respond to changes in ctrl key status
1371  if ( !( e->modifiers() & Qt::ControlModifier ) && !mMarqueeZoom )
1372  {
1373  //space pressed, but control key was released, end of temporary zoom tool
1374  mTemporaryZoomStatus = QgsComposerView::Inactive;
1375  setCurrentTool( mPreviousTool );
1376  }
1377  else if ( !( e->modifiers() & Qt::ControlModifier ) && mMarqueeZoom )
1378  {
1379  //control key released, but user is mid-way through a marquee zoom
1380  //so end temporary zoom when user releases the mouse button
1381  mTemporaryZoomStatus = QgsComposerView::ActiveUntilMouseRelease;
1382  }
1383  else
1384  {
1385  //both control and space pressed
1386  //set cursor to zoom in/out depending on shift key status
1387  QPixmap myZoomQPixmap = QPixmap(( const char ** )(( e->modifiers() & Qt::ShiftModifier ) ? zoom_out : zoom_in ) );
1388  QCursor zoomCursor = QCursor( myZoomQPixmap, 7, 7 );
1389  viewport()->setCursor( zoomCursor );
1390  }
1391  return;
1392  }
1393 
1394  if ( mCurrentTool != QgsComposerView::Zoom && ( mRubberBandItem || mRubberBandLineItem ) )
1395  {
1396  //disable keystrokes while drawing a box
1397  return;
1398  }
1399 
1400  if ( e->key() == Qt::Key_Space && ! e->isAutoRepeat() )
1401  {
1402  if ( !( e->modifiers() & Qt::ControlModifier ) )
1403  {
1404  // Pan composer with space bar
1405  mKeyPanning = true;
1406  mMouseLastXY = mMouseCurrentXY;
1407  if ( composition() )
1408  {
1409  //prevent cursor changes while panning
1411  }
1412  viewport()->setCursor( Qt::ClosedHandCursor );
1413  return;
1414  }
1415  else
1416  {
1417  //ctrl+space pressed, so switch to temporary keyboard based zoom tool
1418  mTemporaryZoomStatus = QgsComposerView::Active;
1419  mPreviousTool = mCurrentTool;
1420  setCurrentTool( Zoom );
1421  //set cursor to zoom in/out depending on shift key status
1422  QPixmap myZoomQPixmap = QPixmap(( const char ** )(( e->modifiers() & Qt::ShiftModifier ) ? zoom_out : zoom_in ) );
1423  QCursor zoomCursor = QCursor( myZoomQPixmap, 7, 7 );
1424  viewport()->setCursor( zoomCursor );
1425  return;
1426  }
1427  }
1428 
1429  if ( mCurrentTool == QgsComposerView::Zoom )
1430  {
1431  //using the zoom tool, respond to changes in shift key status and update mouse cursor accordingly
1432  if ( ! e->isAutoRepeat() )
1433  {
1434  QPixmap myZoomQPixmap = QPixmap(( const char ** )(( e->modifiers() & Qt::ShiftModifier ) ? zoom_out : zoom_in ) );
1435  QCursor zoomCursor = QCursor( myZoomQPixmap, 7, 7 );
1436  viewport()->setCursor( zoomCursor );
1437  }
1438  return;
1439  }
1440 
1442  QList<QgsComposerItem*>::iterator itemIt = composerItemList.begin();
1443 
1444  // increment used for cursor key item movement
1445  double increment = 1.0;
1446  if ( e->modifiers() & Qt::ShiftModifier )
1447  {
1448  //holding shift while pressing cursor keys results in a big step
1449  increment = 10.0;
1450  }
1451  else if ( e->modifiers() & Qt::AltModifier )
1452  {
1453  //holding alt while pressing cursor keys results in a 1 pixel step
1454  double viewScale = transform().m11();
1455  if ( viewScale > 0 )
1456  {
1457  increment = 1 / viewScale;
1458  }
1459  }
1460 
1461  if ( e->key() == Qt::Key_Left )
1462  {
1463  for ( ; itemIt != composerItemList.end(); ++itemIt )
1464  {
1465  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
1466  ( *itemIt )->move( -1 * increment, 0.0 );
1467  ( *itemIt )->endCommand();
1468  }
1469  }
1470  else if ( e->key() == Qt::Key_Right )
1471  {
1472  for ( ; itemIt != composerItemList.end(); ++itemIt )
1473  {
1474  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
1475  ( *itemIt )->move( increment, 0.0 );
1476  ( *itemIt )->endCommand();
1477  }
1478  }
1479  else if ( e->key() == Qt::Key_Down )
1480  {
1481  for ( ; itemIt != composerItemList.end(); ++itemIt )
1482  {
1483  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
1484  ( *itemIt )->move( 0.0, increment );
1485  ( *itemIt )->endCommand();
1486  }
1487  }
1488  else if ( e->key() == Qt::Key_Up )
1489  {
1490  for ( ; itemIt != composerItemList.end(); ++itemIt )
1491  {
1492  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
1493  ( *itemIt )->move( 0.0, -1 * increment );
1494  ( *itemIt )->endCommand();
1495  }
1496  }
1497 }
1498 
1500 {
1501  if ( e->key() == Qt::Key_Space && !e->isAutoRepeat() && mKeyPanning )
1502  {
1503  //end of panning with space key
1504  mKeyPanning = false;
1505 
1506  //reset cursor
1507  if ( mCurrentTool != Pan )
1508  {
1509  if ( composition() )
1510  {
1511  //allow cursor changes again
1512  composition()->setPreventCursorChange( false );
1513  }
1514  }
1515  viewport()->setCursor( defaultCursorForTool( mCurrentTool ) );
1516  return;
1517  }
1518  else if ( e->key() == Qt::Key_Space && !e->isAutoRepeat() && mTemporaryZoomStatus != QgsComposerView::Inactive )
1519  {
1520  //temporary keyboard-based zoom tool is active and space key has been released
1521  if ( mMarqueeZoom )
1522  {
1523  //currently in the middle of a marquee operation, so don't switch tool back immediately
1524  //instead, wait until mouse button has been released before switching tool back
1525  mTemporaryZoomStatus = QgsComposerView::ActiveUntilMouseRelease;
1526  }
1527  else
1528  {
1529  //switch tool back
1530  mTemporaryZoomStatus = QgsComposerView::Inactive;
1531  setCurrentTool( mPreviousTool );
1532  }
1533  }
1534  else if ( mCurrentTool == QgsComposerView::Zoom )
1535  {
1536  //if zoom tool is active, respond to changes in the shift key status and update cursor accordingly
1537  if ( ! e->isAutoRepeat() )
1538  {
1539  QPixmap myZoomQPixmap = QPixmap(( const char ** )(( e->modifiers() & Qt::ShiftModifier ) ? zoom_out : zoom_in ) );
1540  QCursor zoomCursor = QCursor( myZoomQPixmap, 7, 7 );
1541  viewport()->setCursor( zoomCursor );
1542  }
1543  return;
1544  }
1545 }
1546 
1548 {
1549  if ( mRubberBandItem || mRubberBandLineItem )
1550  {
1551  //ignore wheel events while marquee operations are active (eg, creating new item)
1552  return;
1553  }
1554 
1555  if ( composition()->selectionHandles()->isDragging() || composition()->selectionHandles()->isResizing() )
1556  {
1557  //ignore wheel events while dragging/resizing items
1558  return;
1559  }
1560 
1561  if ( currentTool() == MoveItemContent )
1562  {
1563  //move item content tool, so scroll events get handled by the selected composer item
1564 
1565  QPointF scenePoint = mapToScene( event->pos() );
1566  //select topmost item at position of event
1567  QgsComposerItem* theItem = composition()->composerItemAt( scenePoint, true );
1568  if ( theItem )
1569  {
1570  if ( theItem->isSelected() )
1571  {
1572  QSettings settings;
1573  //read zoom mode
1574  QgsComposerItem::ZoomMode zoomMode = ( QgsComposerItem::ZoomMode )settings.value( "/qgis/wheel_action", 2 ).toInt();
1575  if ( zoomMode == QgsComposerItem::NoZoom )
1576  {
1577  //do nothing
1578  return;
1579  }
1580 
1581  double zoomFactor = settings.value( "/qgis/zoom_factor", 2.0 ).toDouble();
1582  if ( event->modifiers() & Qt::ControlModifier )
1583  {
1584  //holding ctrl while wheel zooming results in a finer zoom
1585  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
1586  }
1587  zoomFactor = event->delta() > 0 ? zoomFactor : 1 / zoomFactor;
1588 
1589  QPointF itemPoint = theItem->mapFromScene( scenePoint );
1590  theItem->beginCommand( tr( "Zoom item content" ), QgsComposerMergeCommand::ItemZoomContent );
1591  theItem->zoomContent( zoomFactor, itemPoint, zoomMode );
1592  theItem->endCommand();
1593  }
1594  }
1595  }
1596  else
1597  {
1598  //not using move item content tool, so zoom whole composition
1599  wheelZoom( event );
1600  }
1601 }
1602 
1603 void QgsComposerView::wheelZoom( QWheelEvent * event )
1604 {
1605  //get mouse wheel zoom behaviour settings
1606  QSettings mySettings;
1607  int wheelAction = mySettings.value( "/qgis/wheel_action", 2 ).toInt();
1608  double zoomFactor = mySettings.value( "/qgis/zoom_factor", 2 ).toDouble();
1609 
1611  {
1612  return;
1613  }
1614 
1615  if ( event->modifiers() & Qt::ControlModifier )
1616  {
1617  //holding ctrl while wheel zooming results in a finer zoom
1618  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 10.0;
1619  }
1620 
1621  //caculate zoom scale factor
1622  bool zoomIn = event->delta() > 0;
1623  double scaleFactor = ( zoomIn ? 1 / zoomFactor : zoomFactor );
1624 
1625  //get current visible part of scene
1626  QRect viewportRect( 0, 0, viewport()->width(), viewport()->height() );
1627  QgsRectangle visibleRect = QgsRectangle( mapToScene( viewportRect ).boundingRect() );
1628 
1629  //transform the mouse pos to scene coordinates
1630  QPointF scenePoint = mapToScene( event->pos() );
1631 
1632  //adjust view center according to wheel action setting
1633  switch (( QgsMapCanvas::WheelAction )wheelAction )
1634  {
1636  {
1637  centerOn( scenePoint.x(), scenePoint.y() );
1638  break;
1639  }
1640 
1642  {
1643  QgsPoint oldCenter( visibleRect.center() );
1644  QgsPoint newCenter( scenePoint.x() + (( oldCenter.x() - scenePoint.x() ) * scaleFactor ),
1645  scenePoint.y() + (( oldCenter.y() - scenePoint.y() ) * scaleFactor ) );
1646  centerOn( newCenter.x(), newCenter.y() );
1647  break;
1648  }
1649 
1650  default:
1651  break;
1652  }
1653 
1654  //zoom composition
1655  if ( zoomIn )
1656  {
1657  scaleSafe( zoomFactor );
1658  }
1659  else
1660  {
1661  scaleSafe( 1 / zoomFactor );
1662  }
1663 
1664  //update composition for new zoom
1665  emit zoomLevelChanged();
1666  updateRulers();
1667  update();
1668  //redraw cached map items
1669  QList<QGraphicsItem *> itemList = composition()->items();
1670  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
1671  for ( ; itemIt != itemList.end(); ++itemIt )
1672  {
1673  QgsComposerMap* mypItem = dynamic_cast<QgsComposerMap *>( *itemIt );
1674  if (( mypItem ) && ( mypItem->previewMode() == QgsComposerMap::Render ) )
1675  {
1676  mypItem->updateCachedImage();
1677  }
1678  }
1679 }
1680 
1681 void QgsComposerView::setZoomLevel( double zoomLevel )
1682 {
1683  double dpi = QgsApplication::desktop()->logicalDpiX();
1684  //monitor dpi is not always correct - so make sure the value is sane
1685  if (( dpi < 60 ) || ( dpi > 250 ) )
1686  dpi = 72;
1687 
1688  //desired pixel width for 1mm on screen
1689  double scale = qBound( MIN_VIEW_SCALE, zoomLevel * dpi / 25.4, MAX_VIEW_SCALE );
1690  setTransform( QTransform::fromScale( scale, scale ) );
1691 
1692  updateRulers();
1693  update();
1694  emit zoomLevelChanged();
1695 }
1696 
1698 {
1699  double currentScale = transform().m11();
1700  scale *= currentScale;
1701  scale = qBound( MIN_VIEW_SCALE, scale, MAX_VIEW_SCALE );
1702  setTransform( QTransform::fromScale( scale, scale ) );
1703 }
1704 
1706 {
1707  if ( !mPreviewEffect )
1708  {
1709  return;
1710  }
1711 
1712  mPreviewEffect->setEnabled( enabled );
1713 }
1714 
1716 {
1717  if ( !mPreviewEffect )
1718  {
1719  return;
1720  }
1721 
1722  mPreviewEffect->setMode( mode );
1723 }
1724 
1726 {
1727  if ( mPaintingEnabled )
1728  {
1729  QGraphicsView::paintEvent( event );
1730  event->accept();
1731  }
1732  else
1733  {
1734  event->ignore();
1735  }
1736 }
1737 
1739 {
1740  emit composerViewHide( this );
1741  e->ignore();
1742 }
1743 
1745 {
1746  emit composerViewShow( this );
1747  e->ignore();
1748 }
1749 
1751 {
1752  QGraphicsView::resizeEvent( event );
1753  emit zoomLevelChanged();
1754  updateRulers();
1755 }
1756 
1758 {
1760  updateRulers();
1761 }
1762 
1764 {
1765  setScene( c );
1766  if ( mHorizontalRuler )
1767  {
1768  mHorizontalRuler->setComposition( c );
1769  }
1770  if ( mVerticalRuler )
1771  {
1772  mVerticalRuler->setComposition( c );
1773  }
1774 
1775  //emit compositionSet, so that composer windows can update for the new composition
1776  emit compositionSet( c );
1777 }
1778 
1780 {
1781  if ( scene() )
1782  {
1783  QgsComposition* c = dynamic_cast<QgsComposition *>( scene() );
1784  if ( c )
1785  {
1786  return c;
1787  }
1788  }
1789  return nullptr;
1790 }
1791 
1793 {
1794  if ( !composition() )
1795  {
1796  return;
1797  }
1798 
1799  //group selected items
1801  QgsComposerItemGroup* itemGroup = composition()->groupItems( selectionList );
1802 
1803  if ( !itemGroup )
1804  {
1805  //group could not be created
1806  return;
1807  }
1808 
1809  itemGroup->setSelected( true );
1810  emit selectedItemChanged( itemGroup );
1811 }
1812 
1814 {
1815  if ( !composition() )
1816  {
1817  return;
1818  }
1819 
1820  //hunt through selection for any groups, and ungroup them
1822  QList<QgsComposerItem*>::iterator itemIter = selectionList.begin();
1823  for ( ; itemIter != selectionList.end(); ++itemIter )
1824  {
1825  QgsComposerItemGroup* itemGroup = dynamic_cast<QgsComposerItemGroup *>( *itemIter );
1826  if ( itemGroup )
1827  {
1828  composition()->ungroupItems( itemGroup );
1829  }
1830  }
1831 }
1832 
1834 {
1835  QMainWindow* composerObject = nullptr;
1836  QObject* currentObject = parent();
1837  if ( !currentObject )
1838  {
1839  return qobject_cast<QMainWindow *>( currentObject );
1840  }
1841 
1842  while ( true )
1843  {
1844  composerObject = qobject_cast<QMainWindow*>( currentObject );
1845  if ( composerObject || !currentObject->parent() )
1846  {
1847  return composerObject;
1848  }
1849  currentObject = currentObject->parent();
1850  }
1851 
1852  return nullptr;
1853 }
QgsComposerMouseHandles::MouseAction mouseActionForScenePos(QPointF sceneCoordPos)
Finds out which mouse move action to choose depending on the scene cursor position.
virtual void mouseMoveEvent(QMouseEvent *event)
void setSceneRect(const QRectF &rectangle) override
Adapts mMaximumNumberOfFeatures depending on the rectangle height.
void setShapeType(QgsComposerShape::Shape s)
Item representing the paper.
Definition: qgspaperitem.h:40
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. ...
QTransform fromScale(qreal sx, qreal sy)
A rectangle specified with double values.
Definition: qgsrectangle.h:35
Qt::KeyboardModifiers modifiers() const
QgsPoint center() const
Center point of the rectangle.
Definition: qgsrectangle.h:217
void setAllUnselected()
Clears any selected items in the composition.
QByteArray data(const QString &mimeType) const
QMainWindow * composerWindow()
Returns the composer main window.
void setCursor(const QCursor &)
An item that draws an arrow between to points.
const char * zoom_in[]
Bitmap cursors for map operations.
Definition: qgscursors.cpp:21
QLineF line() const
QList< QGraphicsItem * > items() const
const QMimeData * mimeData(Mode mode) const
QDomNode appendChild(const QDomNode &newChild)
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 centerOn(const QPointF &pos)
void keyPressEvent(QKeyEvent *e) override
qreal dx() const
qreal dy() const
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
QList< QGraphicsItem * > items() const
void setFrameShape(Shape)
void selectInvert()
Inverts current selection.
ZoomMode
Modes for zooming item content.
bool isElement() const
void setComposerMap(const QgsComposerMap *map)
Sets the composer map to use to limit the extent of features shown in the attribute table...
void updateRulers()
Update rulers with current scene rect.
int x() const
int y() const
void applyDefaultSize(ScaleBarUnits u=Meters)
Apply default size (scale bar 1/5 of map item width)
QgsComposerMouseHandles * selectionHandles()
Returns pointer to selection handles.
QPointF mapToScene(const QPoint &point) const
void mousePressEvent(QMouseEvent *) override
const T & at(int i) const
void mouseReleaseEvent(QMouseEvent *) override
void addComposerScaleBar(QgsComposerScaleBar *scaleBar)
Adds scale bar to the graphics scene and advices 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%.
int y() const
virtual void mouseReleaseEvent(QMouseEvent *event)
bool isVisible() const
QRect visibleRect() const
A container for grouping several QgsComposerItems.
QWidget * viewport() const
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...
QDomElement documentElement() const
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)
Qt::MouseButtons buttons() const
void groupItems()
Add an item group containing the selected items.
bool isAutoRepeat() const
const QPoint & pos() const
QGraphicsScene * scene() const
void updateLegend()
Updates the model and all legend entries.
void selectNone()
Deselects all items.
void setEnabled(bool enable)
QString tr(const char *sourceText, const char *disambiguation, int n)
QList< const QgsComposerMap * > composerMapItems() const
Returns pointers to all composer maps in the scene.
void update()
void update(const QRectF &rect)
A table that displays attributes from a vector layer.
void composerViewHide(QgsComposerView *)
Emitted before composerview is hidden.
int x() const
int y() const
A graphics effect which can be applied to a widget to simulate various printing and color blindness m...
void setRect(const QRectF &rectangle)
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()
int width() const
QDomElement toElement() const
void addComposerShape(QgsComposerShape *shape)
Adds a composer shape to the graphics scene and advices composer to create a widget for it (through s...
void scale(qreal sx, qreal sy)
virtual void moveContent(double dx, double dy)
Move Content of item.
QTransform transform() const
QTransform & translate(qreal dx, qreal dy)
int count() const
void showEvent(QShowEvent *e) override
void wheelEvent(QWheelEvent *event) override
qreal x() const
qreal y() const
QPointF p1() const
QPointF p2() const
void ignore()
const char * zoom_out[]
Definition: qgscursors.cpp:45
void paintEvent(QPaintEvent *event) override
int toInt(bool *ok) const
int x() const
void removeItem(QGraphicsItem *item)
void setPreventCursorChange(const bool preventChange)
If true, prevents any mouse cursor changes by the composition or by any composer items Used by QgsCom...
QClipboard * clipboard()
virtual bool event(QEvent *event)
void setUseSymbolV2(bool useSymbolV2)
Controls whether the shape should be drawn using a QgsFillSymbolV2.
void endCommand()
Saves end state of item and pushes command to the undo history.
virtual bool selected() const
Is selected.
void setResizeAnchor(ViewportAnchor anchor)
Qt::MouseButton button() const
qreal m11() const
QPointF posF() const
void setAngle(qreal angle)
bool isEmpty() const
QDomNodeList elementsByTagName(const QString &tagname) const
void addFrame(QgsComposerFrame *frame, bool recalcFrameSizes=true) override
Adds a frame to the multiframe.
void selectedItemChanged(QgsComposerItem *selected)
Is emitted when selected item changed.
void scaleSafe(double scale)
Scales the view in a safe way, by limiting the acceptable range of the scale applied.
void setLine(const QLineF &line)
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
QPoint pos() const
Widget to display the composer items.
void setScene(QGraphicsScene *scene)
void removeComposerItem(QgsComposerItem *item, const bool createCommand=true, const bool removeGroupItems=true)
Remove item from the graphics scene.
void setPreviewModeEnabled(bool enabled)
Sets whether a preview effect should be used to alter the view&#39;s appearance.
void setMimeData(QMimeData *src, Mode mode)
QScrollBar * verticalScrollBar() const
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:65
Graphics scene for map printing.
QRect rect() const
Object representing map window.
Frame item for a composer multiframe item.
int logicalDpiX() const
Qt::KeyboardModifiers modifiers() const
QgsComposerItem * composerItemAt(QPointF position, const bool ignoreLocked=false) const
Returns the topmost composer item at a specified position.
QgsComposerView(QWidget *parent=nullptr, const char *name=nullptr, const Qt::WindowFlags &f=nullptr)
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)
iterator end()
int key() const
void setComposerMap(const QgsComposerMap *map)
iterator begin()
void ungroupItems()
Ungroups the selected items.
virtual void mousePressEvent(QMouseEvent *event)
Tool
Current tool.
void setValue(int)
void copyItems(ClipboardMode mode)
Cuts or copies the selected items.
virtual void paintEvent(QPaintEvent *event)
QVariant value(const QString &key, const QVariant &defaultValue) const
virtual void addFrame(QgsComposerFrame *frame, bool recalcFrameSizes=true) override
Adds a frame to the multiframe.
A table class that displays a vector attribute table.
qreal width() const
void setPreviewMode(QgsPreviewEffect::PreviewMode mode)
Sets the preview mode which should be used to modify the view&#39;s appearance.
QPoint pos()
QRectF toRectF() const
returns a QRectF with same coordinates.
A composer items that draws common shapes (ellipse, triangle, rectangle)
void setPen(const QPen &pen)
QDesktopWidget * desktop()
PreviewMode previewMode() const
QTransform transform() const
iterator end()
void resizeEvent(QResizeEvent *event) override
QPointF snapPointToGrid(QPointF scenePoint) const
Snaps a scene coordinate point to grid.
QPoint mapFromGlobal(const QPoint &pos) const
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 advices composer to create a widget for it (through signal) ...
void setTransform(const QTransform &matrix, bool combine)
void update(qreal x, qreal y, qreal w, qreal h)
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
void setGraphicsEffect(QGraphicsEffect *effect)
void setMouseTracking(bool enable)
qreal angle() const
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...
QScrollBar * horizontalScrollBar() const
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.
qreal height() const
void addComposerPicture(QgsComposerPicture *picture)
Adds picture to the graphics scene and advices composer to create a widget for it (through signal) ...
typedef WindowFlags
double toDouble(bool *ok) const
void hideEvent(QHideEvent *e) override
void addComposerTable(QgsComposerAttributeTable *table)
Adds a composer table to the graphics scene and advices composer to create a widget for it (through s...
void removeAttribute(const QString &name)
QgsComposerView::Tool currentTool() const
QString tagName() 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...
void setData(const QString &mimeType, const QByteArray &data)
QgsComposition * composition()
Returns the composition or 0 in case of error.
const QPoint & pos() const
void setBrush(const QBrush &brush)
QDomElement createElement(const QString &tagName)
bool positionLock() const
Returns whether position lock for mouse drags is enabled returns true if item is locked for mouse mov...
void addItem(QGraphicsItem *item)
const char * cross_hair_cursor[]
Definition: qgscursors.cpp:159
void actionFinished()
Current action (e.g.
void mouseMoveEvent(QMouseEvent *) override
QObject * parent() const
A legend that can be placed onto a map composition.
void addComposerLabel(QgsComposerLabel *label)
Adds label to the graphics scene and advices composer to create a widget for it (through signal) ...
void setZValue(qreal z)
iterator begin()
void addComposerArrow(QgsComposerArrow *arrow)
Adds an arrow item to the graphics scene and advices composer to create a widget for it (through sign...
int height() const
void addComposerLegend(QgsComposerLegend *legend)
Adds legend to the graphics scene and advices composer to create a widget for it (through signal) ...
virtual void scrollContentsBy(int dx, int dy)
void push(QUndoCommand *cmd)
void adjustSizeToText()
Resizes the widget such that the text fits to the item.
QTransform viewportTransform() const
QByteArray toByteArray(int indent) const
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
QDomNode at(int index) const
QRectF rect() const
void fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRatioMode)
void setTransform(const QTransform &matrix, bool combine)
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
virtual void resizeEvent(QResizeEvent *event)
void beginMultiFrameCommand(QgsComposerMultiFrame *multiFrame, const QString &text, const QgsComposerMultiFrameMergeCommand::Context c=QgsComposerMultiFrameMergeCommand::Unknown)