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