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