QGIS API Documentation  2.12.0-Lyon
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( 0 )
55  , mRubberBandLineItem( 0 )
56  , mMoveContentItem( 0 )
57  , mMarqueeSelect( false )
58  , mMarqueeZoom( false )
59  , mTemporaryZoomStatus( QgsComposerView::Inactive )
60  , mPaintingEnabled( true )
61  , mHorizontalRuler( 0 )
62  , mVerticalRuler( 0 )
63  , mToolPanning( false )
64  , mMousePanning( false )
65  , mKeyPanning( false )
66  , mMovingItemContent( false )
67  , mPreviewEffect( 0 )
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 = 0;
198  QgsComposerItem* previousSelectedItem = 0;
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.size() > 0 )
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.size() > 0 )
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.size() == 0 )
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.size() > 0 )
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 = 0;
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.size() > 0 )
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 = 0;
734  mMovingItemContent = false;
735  }
736  break;
737  }
738  case AddArrow:
739  if ( !composition() || !mRubberBandLineItem )
740  {
741  scene()->removeItem( mRubberBandLineItem );
742  delete mRubberBandLineItem;
743  mRubberBandLineItem = 0;
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 = 0;
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.size() > 0 )
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.size() > 0 )
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.size() > 0 )
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  default:
957  break;
958  }
959 }
960 
962 {
963  if ( !composition() )
964  {
965  return;
966  }
967 
968  bool shiftModifier = false;
969  bool altModifier = false;
970  if ( e->modifiers() & Qt::ShiftModifier )
971  {
972  //shift key depressed
973  shiftModifier = true;
974  }
975  if ( e->modifiers() & Qt::AltModifier )
976  {
977  //alt key depressed
978  altModifier = true;
979  }
980 
981  mMouseCurrentXY = e->pos();
982  //update cursor position in composer status bar
983  emit cursorPosChanged( mapToScene( e->pos() ) );
984 
985  updateRulers();
986  if ( mHorizontalRuler )
987  {
988  mHorizontalRuler->updateMarker( e->posF() );
989  }
990  if ( mVerticalRuler )
991  {
992  mVerticalRuler->updateMarker( e->posF() );
993  }
994 
995  if ( mToolPanning || mMousePanning || mKeyPanning )
996  {
997  //panning, so scroll view
998  horizontalScrollBar()->setValue( horizontalScrollBar()->value() - ( e->x() - mMouseLastXY.x() ) );
999  verticalScrollBar()->setValue( verticalScrollBar()->value() - ( e->y() - mMouseLastXY.y() ) );
1000  mMouseLastXY = e->pos();
1001  return;
1002  }
1003  else if ( e->buttons() == Qt::NoButton )
1004  {
1005  if ( mCurrentTool == Select )
1006  {
1008  }
1009  }
1010  else
1011  {
1012  QPointF scenePoint = mapToScene( e->pos() );
1013 
1014  if ( mMarqueeSelect || mMarqueeZoom )
1015  {
1016  updateRubberBandRect( scenePoint );
1017  return;
1018  }
1019 
1020  switch ( mCurrentTool )
1021  {
1022  case Select:
1024  break;
1025 
1026  case AddArrow:
1027  {
1028  updateRubberBandLine( scenePoint, shiftModifier );
1029  break;
1030  }
1031 
1032  case AddMap:
1033  case AddRectangle:
1034  case AddTriangle:
1035  case AddEllipse:
1036  case AddHtml:
1037  case AddPicture:
1038  case AddLabel:
1039  case AddLegend:
1040  case AddTable:
1041  case AddAttributeTable:
1042  //adjust rubber band item
1043  {
1044  updateRubberBandRect( scenePoint, shiftModifier, altModifier );
1045  break;
1046  }
1047 
1048  case MoveItemContent:
1049  {
1050  //update map preview if composer map
1051  QgsComposerMap* composerMap = dynamic_cast<QgsComposerMap *>( mMoveContentItem );
1052  if ( composerMap )
1053  {
1054  composerMap->setOffset( scenePoint.x() - mMoveContentStartPos.x(), scenePoint.y() - mMoveContentStartPos.y() );
1055  composerMap->update();
1056  }
1057  break;
1058  }
1059  default:
1060  break;
1061  }
1062  }
1063 }
1064 
1065 void QgsComposerView::updateRubberBandRect( QPointF & pos, const bool constrainSquare, const bool fromCenter )
1066 {
1067  if ( !mRubberBandItem )
1068  {
1069  return;
1070  }
1071 
1072  double x = 0;
1073  double y = 0;
1074  double width = 0;
1075  double height = 0;
1076 
1077  double dx = pos.x() - mRubberBandStartPos.x();
1078  double dy = pos.y() - mRubberBandStartPos.y();
1079 
1080  if ( constrainSquare )
1081  {
1082  if ( fabs( dx ) > fabs( dy ) )
1083  {
1084  width = fabs( dx );
1085  height = width;
1086  }
1087  else
1088  {
1089  height = fabs( dy );
1090  width = height;
1091  }
1092 
1093  x = mRubberBandStartPos.x() - (( dx < 0 ) ? width : 0 );
1094  y = mRubberBandStartPos.y() - (( dy < 0 ) ? height : 0 );
1095  }
1096  else
1097  {
1098  //not constraining
1099  if ( dx < 0 )
1100  {
1101  x = pos.x();
1102  width = -dx;
1103  }
1104  else
1105  {
1106  x = mRubberBandStartPos.x();
1107  width = dx;
1108  }
1109 
1110  if ( dy < 0 )
1111  {
1112  y = pos.y();
1113  height = -dy;
1114  }
1115  else
1116  {
1117  y = mRubberBandStartPos.y();
1118  height = dy;
1119  }
1120  }
1121 
1122  if ( fromCenter )
1123  {
1124  x = mRubberBandStartPos.x() - width;
1125  y = mRubberBandStartPos.y() - height;
1126  width *= 2.0;
1127  height *= 2.0;
1128  }
1129 
1130  mRubberBandItem->setRect( 0, 0, width, height );
1131  QTransform t;
1132  t.translate( x, y );
1133  mRubberBandItem->setTransform( t );
1134 }
1135 
1136 void QgsComposerView::updateRubberBandLine( const QPointF &pos, const bool constrainAngles )
1137 {
1138  if ( !mRubberBandLineItem )
1139  {
1140  return;
1141  }
1142 
1143  //snap to grid
1144  QPointF snappedScenePoint = composition()->snapPointToGrid( pos );
1145 
1146  QLineF newLine = QLineF( mRubberBandStartPos, snappedScenePoint );
1147 
1148  if ( constrainAngles )
1149  {
1150  //movement is contrained to 45 degree angles
1151  double angle = QgsComposerUtils::snappedAngle( newLine.angle() );
1152  newLine.setAngle( angle );
1153  }
1154 
1155  mRubberBandLineItem->setLine( newLine );
1156 }
1157 
1159 {
1160  e->ignore();
1161 }
1162 
1164 {
1165  if ( !composition() )
1166  {
1167  return;
1168  }
1169 
1171  QList<QgsComposerItem*>::iterator itemIt = composerItemList.begin();
1172 
1173  QDomDocument doc;
1174  QDomElement documentElement = doc.createElement( "ComposerItemClipboard" );
1175  for ( ; itemIt != composerItemList.end(); ++itemIt )
1176  {
1177  // copy each item in a group
1178  QgsComposerItemGroup* itemGroup = dynamic_cast<QgsComposerItemGroup*>( *itemIt );
1179  if ( itemGroup && composition() )
1180  {
1181  QSet<QgsComposerItem*> groupedItems = itemGroup->items();
1182  QSet<QgsComposerItem*>::iterator it = groupedItems.begin();
1183  for ( ; it != groupedItems.end(); ++it )
1184  {
1185  ( *it )->writeXML( documentElement, doc );
1186  }
1187  }
1188  ( *itemIt )->writeXML( documentElement, doc );
1189  if ( mode == ClipboardModeCut )
1190  {
1191  composition()->removeComposerItem( *itemIt );
1192  }
1193  }
1194  doc.appendChild( documentElement );
1195 
1196  //if it's a copy, we have to remove the UUIDs since we don't want any duplicate UUID
1197  if ( mode == ClipboardModeCopy )
1198  {
1199  // remove all uuid attributes
1200  QDomNodeList composerItemsNodes = doc.elementsByTagName( "ComposerItem" );
1201  for ( int i = 0; i < composerItemsNodes.count(); ++i )
1202  {
1203  QDomNode composerItemNode = composerItemsNodes.at( i );
1204  if ( composerItemNode.isElement() )
1205  {
1206  composerItemNode.toElement().removeAttribute( "uuid" );
1207  }
1208  }
1209  }
1210 
1211  QMimeData *mimeData = new QMimeData;
1212  mimeData->setData( "text/xml", doc.toByteArray() );
1213  QClipboard *clipboard = QApplication::clipboard();
1214  clipboard->setMimeData( mimeData );
1215 }
1216 
1218 {
1219  if ( !composition() )
1220  {
1221  return;
1222  }
1223 
1224  QDomDocument doc;
1225  QClipboard *clipboard = QApplication::clipboard();
1226  if ( doc.setContent( clipboard->mimeData()->data( "text/xml" ) ) )
1227  {
1228  QDomElement docElem = doc.documentElement();
1229  if ( docElem.tagName() == "ComposerItemClipboard" )
1230  {
1231  if ( composition() )
1232  {
1233  QPointF pt;
1235  {
1236  // place items at cursor position
1237  pt = mapToScene( mapFromGlobal( QCursor::pos() ) );
1238  }
1239  else
1240  {
1241  // place items in center of viewport
1242  pt = mapToScene( viewport()->rect().center() );
1243  }
1244  bool pasteInPlace = ( mode == PasteModeInPlace );
1245  composition()->addItemsFromXML( docElem, doc, 0, true, &pt, pasteInPlace );
1246  }
1247  }
1248  }
1249 
1250  //switch back to select tool so that pasted items can be moved/resized (#8958)
1252 }
1253 
1255 {
1256  if ( !composition() )
1257  {
1258  return;
1259  }
1260 
1262  QList<QgsComposerItem*>::iterator itemIt = composerItemList.begin();
1263 
1264  //delete selected items
1265  for ( ; itemIt != composerItemList.end(); ++itemIt )
1266  {
1267  if ( composition() )
1268  {
1269  composition()->removeComposerItem( *itemIt );
1270  }
1271  }
1272 }
1273 
1275 {
1276  if ( !composition() )
1277  {
1278  return;
1279  }
1280 
1281  //select all items in composer
1282  QList<QGraphicsItem *> itemList = composition()->items();
1283  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
1284  for ( ; itemIt != itemList.end(); ++itemIt )
1285  {
1286  QgsComposerItem* mypItem = dynamic_cast<QgsComposerItem *>( *itemIt );
1287  QgsPaperItem* paperItem = dynamic_cast<QgsPaperItem*>( *itemIt );
1288  if ( mypItem && !paperItem )
1289  {
1290  if ( !mypItem->positionLock() )
1291  {
1292  mypItem->setSelected( true );
1293  }
1294  else
1295  {
1296  //deselect all locked items
1297  mypItem->setSelected( false );
1298  }
1299  emit selectedItemChanged( mypItem );
1300  }
1301  }
1302 }
1303 
1305 {
1306  if ( !composition() )
1307  {
1308  return;
1309  }
1310 
1312 }
1313 
1315 {
1316  if ( !composition() )
1317  {
1318  return;
1319  }
1320 
1321  //check all items in composer
1322  QList<QGraphicsItem *> itemList = composition()->items();
1323  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
1324  for ( ; itemIt != itemList.end(); ++itemIt )
1325  {
1326  QgsComposerItem* mypItem = dynamic_cast<QgsComposerItem *>( *itemIt );
1327  QgsPaperItem* paperItem = dynamic_cast<QgsPaperItem*>( *itemIt );
1328  if ( mypItem && !paperItem )
1329  {
1330  //flip selected state for items (and deselect any locked items)
1331  if ( mypItem->selected() || mypItem->positionLock() )
1332  {
1333 
1334  mypItem->setSelected( false );
1335  }
1336  else
1337  {
1338  mypItem->setSelected( true );
1339  emit selectedItemChanged( mypItem );
1340  }
1341  }
1342  }
1343 }
1344 
1346 {
1347  if ( !composition() )
1348  {
1349  return;
1350  }
1351 
1352  if ( mKeyPanning || mMousePanning || mToolPanning || mMovingItemContent ||
1353  composition()->selectionHandles()->isDragging() || composition()->selectionHandles()->isResizing() )
1354  {
1355  return;
1356  }
1357 
1358  if ( mTemporaryZoomStatus != QgsComposerView::Inactive )
1359  {
1360  //temporary keyboard based zoom is active
1361  if ( e->isAutoRepeat() )
1362  {
1363  return;
1364  }
1365 
1366  //respond to changes in ctrl key status
1367  if ( !( e->modifiers() & Qt::ControlModifier ) && !mMarqueeZoom )
1368  {
1369  //space pressed, but control key was released, end of temporary zoom tool
1370  mTemporaryZoomStatus = QgsComposerView::Inactive;
1371  setCurrentTool( mPreviousTool );
1372  }
1373  else if ( !( e->modifiers() & Qt::ControlModifier ) && mMarqueeZoom )
1374  {
1375  //control key released, but user is mid-way through a marquee zoom
1376  //so end temporary zoom when user releases the mouse button
1377  mTemporaryZoomStatus = QgsComposerView::ActiveUntilMouseRelease;
1378  }
1379  else
1380  {
1381  //both control and space pressed
1382  //set cursor to zoom in/out depending on shift key status
1383  QPixmap myZoomQPixmap = QPixmap(( const char ** )(( e->modifiers() & Qt::ShiftModifier ) ? zoom_out : zoom_in ) );
1384  QCursor zoomCursor = QCursor( myZoomQPixmap, 7, 7 );
1385  viewport()->setCursor( zoomCursor );
1386  }
1387  return;
1388  }
1389 
1390  if ( mCurrentTool != QgsComposerView::Zoom && ( mRubberBandItem || mRubberBandLineItem ) )
1391  {
1392  //disable keystrokes while drawing a box
1393  return;
1394  }
1395 
1396  if ( e->key() == Qt::Key_Space && ! e->isAutoRepeat() )
1397  {
1398  if ( !( e->modifiers() & Qt::ControlModifier ) )
1399  {
1400  // Pan composer with space bar
1401  mKeyPanning = true;
1402  mMouseLastXY = mMouseCurrentXY;
1403  if ( composition() )
1404  {
1405  //prevent cursor changes while panning
1407  }
1408  viewport()->setCursor( Qt::ClosedHandCursor );
1409  return;
1410  }
1411  else
1412  {
1413  //ctrl+space pressed, so switch to temporary keyboard based zoom tool
1414  mTemporaryZoomStatus = QgsComposerView::Active;
1415  mPreviousTool = mCurrentTool;
1416  setCurrentTool( Zoom );
1417  //set cursor to zoom in/out depending on shift key status
1418  QPixmap myZoomQPixmap = QPixmap(( const char ** )(( e->modifiers() & Qt::ShiftModifier ) ? zoom_out : zoom_in ) );
1419  QCursor zoomCursor = QCursor( myZoomQPixmap, 7, 7 );
1420  viewport()->setCursor( zoomCursor );
1421  return;
1422  }
1423  }
1424 
1425  if ( mCurrentTool == QgsComposerView::Zoom )
1426  {
1427  //using the zoom tool, respond to changes in shift key status and update mouse cursor accordingly
1428  if ( ! e->isAutoRepeat() )
1429  {
1430  QPixmap myZoomQPixmap = QPixmap(( const char ** )(( e->modifiers() & Qt::ShiftModifier ) ? zoom_out : zoom_in ) );
1431  QCursor zoomCursor = QCursor( myZoomQPixmap, 7, 7 );
1432  viewport()->setCursor( zoomCursor );
1433  }
1434  return;
1435  }
1436 
1438  QList<QgsComposerItem*>::iterator itemIt = composerItemList.begin();
1439 
1440  // increment used for cursor key item movement
1441  double increment = 1.0;
1442  if ( e->modifiers() & Qt::ShiftModifier )
1443  {
1444  //holding shift while pressing cursor keys results in a big step
1445  increment = 10.0;
1446  }
1447  else if ( e->modifiers() & Qt::AltModifier )
1448  {
1449  //holding alt while pressing cursor keys results in a 1 pixel step
1450  double viewScale = transform().m11();
1451  if ( viewScale > 0 )
1452  {
1453  increment = 1 / viewScale;
1454  }
1455  }
1456 
1457  if ( e->key() == Qt::Key_Left )
1458  {
1459  for ( ; itemIt != composerItemList.end(); ++itemIt )
1460  {
1461  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
1462  ( *itemIt )->move( -1 * increment, 0.0 );
1463  ( *itemIt )->endCommand();
1464  }
1465  }
1466  else if ( e->key() == Qt::Key_Right )
1467  {
1468  for ( ; itemIt != composerItemList.end(); ++itemIt )
1469  {
1470  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
1471  ( *itemIt )->move( increment, 0.0 );
1472  ( *itemIt )->endCommand();
1473  }
1474  }
1475  else if ( e->key() == Qt::Key_Down )
1476  {
1477  for ( ; itemIt != composerItemList.end(); ++itemIt )
1478  {
1479  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
1480  ( *itemIt )->move( 0.0, increment );
1481  ( *itemIt )->endCommand();
1482  }
1483  }
1484  else if ( e->key() == Qt::Key_Up )
1485  {
1486  for ( ; itemIt != composerItemList.end(); ++itemIt )
1487  {
1488  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
1489  ( *itemIt )->move( 0.0, -1 * increment );
1490  ( *itemIt )->endCommand();
1491  }
1492  }
1493 }
1494 
1496 {
1497  if ( e->key() == Qt::Key_Space && !e->isAutoRepeat() && mKeyPanning )
1498  {
1499  //end of panning with space key
1500  mKeyPanning = false;
1501 
1502  //reset cursor
1503  if ( mCurrentTool != Pan )
1504  {
1505  if ( composition() )
1506  {
1507  //allow cursor changes again
1508  composition()->setPreventCursorChange( false );
1509  }
1510  }
1511  viewport()->setCursor( defaultCursorForTool( mCurrentTool ) );
1512  return;
1513  }
1514  else if ( e->key() == Qt::Key_Space && !e->isAutoRepeat() && mTemporaryZoomStatus != QgsComposerView::Inactive )
1515  {
1516  //temporary keyboard-based zoom tool is active and space key has been released
1517  if ( mMarqueeZoom )
1518  {
1519  //currently in the middle of a marquee operation, so don't switch tool back immediately
1520  //instead, wait until mouse button has been released before switching tool back
1521  mTemporaryZoomStatus = QgsComposerView::ActiveUntilMouseRelease;
1522  }
1523  else
1524  {
1525  //switch tool back
1526  mTemporaryZoomStatus = QgsComposerView::Inactive;
1527  setCurrentTool( mPreviousTool );
1528  }
1529  }
1530  else if ( mCurrentTool == QgsComposerView::Zoom )
1531  {
1532  //if zoom tool is active, respond to changes in the shift key status and update cursor accordingly
1533  if ( ! e->isAutoRepeat() )
1534  {
1535  QPixmap myZoomQPixmap = QPixmap(( const char ** )(( e->modifiers() & Qt::ShiftModifier ) ? zoom_out : zoom_in ) );
1536  QCursor zoomCursor = QCursor( myZoomQPixmap, 7, 7 );
1537  viewport()->setCursor( zoomCursor );
1538  }
1539  return;
1540  }
1541 }
1542 
1544 {
1545  if ( mRubberBandItem || mRubberBandLineItem )
1546  {
1547  //ignore wheel events while marquee operations are active (eg, creating new item)
1548  return;
1549  }
1550 
1551  if ( composition()->selectionHandles()->isDragging() || composition()->selectionHandles()->isResizing() )
1552  {
1553  //ignore wheel events while dragging/resizing items
1554  return;
1555  }
1556 
1557  if ( currentTool() == MoveItemContent )
1558  {
1559  //move item content tool, so scroll events get handled by the selected composer item
1560 
1561  QPointF scenePoint = mapToScene( event->pos() );
1562  //select topmost item at position of event
1563  QgsComposerItem* theItem = composition()->composerItemAt( scenePoint, true );
1564  if ( theItem )
1565  {
1566  if ( theItem->isSelected() )
1567  {
1568  QSettings settings;
1569  //read zoom mode
1570  QgsComposerItem::ZoomMode zoomMode = ( QgsComposerItem::ZoomMode )settings.value( "/qgis/wheel_action", 2 ).toInt();
1571  if ( zoomMode == QgsComposerItem::NoZoom )
1572  {
1573  //do nothing
1574  return;
1575  }
1576 
1577  double zoomFactor = settings.value( "/qgis/zoom_factor", 2.0 ).toDouble();
1578  if ( event->modifiers() & Qt::ControlModifier )
1579  {
1580  //holding ctrl while wheel zooming results in a finer zoom
1581  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 20.0;
1582  }
1583  zoomFactor = event->delta() > 0 ? zoomFactor : 1 / zoomFactor;
1584 
1585  QPointF itemPoint = theItem->mapFromScene( scenePoint );
1586  theItem->beginCommand( tr( "Zoom item content" ), QgsComposerMergeCommand::ItemZoomContent );
1587  theItem->zoomContent( zoomFactor, itemPoint, zoomMode );
1588  theItem->endCommand();
1589  }
1590  }
1591  }
1592  else
1593  {
1594  //not using move item content tool, so zoom whole composition
1595  wheelZoom( event );
1596  }
1597 }
1598 
1599 void QgsComposerView::wheelZoom( QWheelEvent * event )
1600 {
1601  //get mouse wheel zoom behaviour settings
1602  QSettings mySettings;
1603  int wheelAction = mySettings.value( "/qgis/wheel_action", 2 ).toInt();
1604  double zoomFactor = mySettings.value( "/qgis/zoom_factor", 2 ).toDouble();
1605 
1607  {
1608  return;
1609  }
1610 
1611  if ( event->modifiers() & Qt::ControlModifier )
1612  {
1613  //holding ctrl while wheel zooming results in a finer zoom
1614  zoomFactor = 1.0 + ( zoomFactor - 1.0 ) / 10.0;
1615  }
1616 
1617  //caculate zoom scale factor
1618  bool zoomIn = event->delta() > 0;
1619  double scaleFactor = ( zoomIn ? 1 / zoomFactor : zoomFactor );
1620 
1621  //get current visible part of scene
1622  QRect viewportRect( 0, 0, viewport()->width(), viewport()->height() );
1623  QgsRectangle visibleRect = QgsRectangle( mapToScene( viewportRect ).boundingRect() );
1624 
1625  //transform the mouse pos to scene coordinates
1626  QPointF scenePoint = mapToScene( event->pos() );
1627 
1628  //adjust view center according to wheel action setting
1629  switch (( QgsMapCanvas::WheelAction )wheelAction )
1630  {
1632  {
1633  centerOn( scenePoint.x(), scenePoint.y() );
1634  break;
1635  }
1636 
1638  {
1639  QgsPoint oldCenter( visibleRect.center() );
1640  QgsPoint newCenter( scenePoint.x() + (( oldCenter.x() - scenePoint.x() ) * scaleFactor ),
1641  scenePoint.y() + (( oldCenter.y() - scenePoint.y() ) * scaleFactor ) );
1642  centerOn( newCenter.x(), newCenter.y() );
1643  break;
1644  }
1645 
1646  default:
1647  break;
1648  }
1649 
1650  //zoom composition
1651  if ( zoomIn )
1652  {
1653  scale( zoomFactor, zoomFactor );
1654  }
1655  else
1656  {
1657  scale( 1 / zoomFactor, 1 / zoomFactor );
1658  }
1659 
1660  //update composition for new zoom
1661  emit zoomLevelChanged();
1662  updateRulers();
1663  update();
1664  //redraw cached map items
1665  QList<QGraphicsItem *> itemList = composition()->items();
1666  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
1667  for ( ; itemIt != itemList.end(); ++itemIt )
1668  {
1669  QgsComposerMap* mypItem = dynamic_cast<QgsComposerMap *>( *itemIt );
1670  if (( mypItem ) && ( mypItem->previewMode() == QgsComposerMap::Render ) )
1671  {
1672  mypItem->updateCachedImage();
1673  }
1674  }
1675 }
1676 
1677 void QgsComposerView::setZoomLevel( double zoomLevel )
1678 {
1679  double dpi = QgsApplication::desktop()->logicalDpiX();
1680  //monitor dpi is not always correct - so make sure the value is sane
1681  if (( dpi < 60 ) || ( dpi > 250 ) )
1682  dpi = 72;
1683 
1684  //desired pixel width for 1mm on screen
1685  double scale = zoomLevel * dpi / 25.4;
1686  setTransform( QTransform::fromScale( scale, scale ) );
1687 
1688  updateRulers();
1689  update();
1690  emit zoomLevelChanged();
1691 }
1692 
1694 {
1695  if ( !mPreviewEffect )
1696  {
1697  return;
1698  }
1699 
1700  mPreviewEffect->setEnabled( enabled );
1701 }
1702 
1704 {
1705  if ( !mPreviewEffect )
1706  {
1707  return;
1708  }
1709 
1710  mPreviewEffect->setMode( mode );
1711 }
1712 
1714 {
1715  if ( mPaintingEnabled )
1716  {
1717  QGraphicsView::paintEvent( event );
1718  event->accept();
1719  }
1720  else
1721  {
1722  event->ignore();
1723  }
1724 }
1725 
1727 {
1728  emit composerViewHide( this );
1729  e->ignore();
1730 }
1731 
1733 {
1734  emit composerViewShow( this );
1735  e->ignore();
1736 }
1737 
1739 {
1740  QGraphicsView::resizeEvent( event );
1741  emit zoomLevelChanged();
1742  updateRulers();
1743 }
1744 
1746 {
1748  updateRulers();
1749 }
1750 
1752 {
1753  setScene( c );
1754  if ( mHorizontalRuler )
1755  {
1756  mHorizontalRuler->setComposition( c );
1757  }
1758  if ( mVerticalRuler )
1759  {
1760  mVerticalRuler->setComposition( c );
1761  }
1762 
1763  //emit compositionSet, so that composer windows can update for the new composition
1764  emit compositionSet( c );
1765 }
1766 
1768 {
1769  if ( scene() )
1770  {
1771  QgsComposition* c = dynamic_cast<QgsComposition *>( scene() );
1772  if ( c )
1773  {
1774  return c;
1775  }
1776  }
1777  return 0;
1778 }
1779 
1781 {
1782  if ( !composition() )
1783  {
1784  return;
1785  }
1786 
1787  //group selected items
1789  QgsComposerItemGroup* itemGroup = composition()->groupItems( selectionList );
1790 
1791  if ( !itemGroup )
1792  {
1793  //group could not be created
1794  return;
1795  }
1796 
1797  itemGroup->setSelected( true );
1798  emit selectedItemChanged( itemGroup );
1799 }
1800 
1802 {
1803  if ( !composition() )
1804  {
1805  return;
1806  }
1807 
1808  //hunt through selection for any groups, and ungroup them
1810  QList<QgsComposerItem*>::iterator itemIter = selectionList.begin();
1811  for ( ; itemIter != selectionList.end(); ++itemIter )
1812  {
1813  QgsComposerItemGroup* itemGroup = dynamic_cast<QgsComposerItemGroup *>( *itemIter );
1814  if ( itemGroup )
1815  {
1816  composition()->ungroupItems( itemGroup );
1817  }
1818  }
1819 }
1820 
1822 {
1823  QMainWindow* composerObject = 0;
1824  QObject* currentObject = parent();
1825  if ( !currentObject )
1826  {
1827  return qobject_cast<QMainWindow *>( currentObject );
1828  }
1829 
1830  while ( true )
1831  {
1832  composerObject = qobject_cast<QMainWindow*>( currentObject );
1833  if ( composerObject || currentObject->parent() == 0 )
1834  {
1835  return composerObject;
1836  }
1837  currentObject = currentObject->parent();
1838  }
1839 
1840  return 0;
1841 }
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.
QgsComposerItem * composerItemAt(const QPointF &position, const bool ignoreLocked=false) const
Returns the topmost composer item at a specified position.
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 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) ...
virtual bool selected() const
Is selected.
QList< QGraphicsItem * > items() const
void setFrameShape(Shape)
void selectInvert()
Inverts current selection.
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...
MouseAction
Describes the action (move or resize in different directon) to be done during mouse move...
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 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%.
void updateMarker(const QPointF &pos)
int y() const
virtual void mouseReleaseEvent(QMouseEvent *event)
bool isVisible() const
QPointF snapPointToGrid(const QPointF &scenePoint) const
Snaps a scene coordinate point to grid.
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)
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...
int size() const
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()
Tool
Current tool.
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()
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)
QgsComposerView(QWidget *parent=0, const char *name=0, const Qt::WindowFlags &f=0)
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...
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'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:63
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
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 addItemsFromXML(const QDomElement &elem, const QDomDocument &doc, QMap< QgsComposerMap *, int > *mapsToRestore=0, bool addUndoCommands=false, QPointF *pos=0, bool pasteInPlace=false)
Add items from XML representation to the graphics scene (for project file reading, pasting items from clipboard)
void setComposerMap(const QgsComposerMap *map)
iterator begin()
void ungroupItems()
Ungroups the selected items.
virtual void mousePressEvent(QMouseEvent *event)
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'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
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 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)
ZoomMode
Modes for zooming item content.
QgsPoint center() const
Center point of the rectangle.
Definition: qgsrectangle.h:216
QgsComposerMouseHandles::MouseAction mouseActionForScenePos(const QPointF &sceneCoordPos)
Finds out which mouse move action to choose depending on the scene cursor position.
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
void scale(double scaleFactor, const QgsPoint *c=0)
Scale the rectangle around its center point.
virtual void resizeEvent(QResizeEvent *event)
void beginMultiFrameCommand(QgsComposerMultiFrame *multiFrame, const QString &text, const QgsComposerMultiFrameMergeCommand::Context c=QgsComposerMultiFrameMergeCommand::Unknown)