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