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