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