QGIS API Documentation  2.0.1-Dufour
 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 : [email protected]
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include <QApplication>
19 #include <QMainWindow>
20 #include <QMouseEvent>
21 #include <QKeyEvent>
22 #include <QClipboard>
23 #include <QMimeData>
24 #include <QGridLayout>
25 
26 #include "qgscomposerview.h"
27 #include "qgscomposerarrow.h"
28 #include "qgscomposerframe.h"
29 #include "qgscomposerhtml.h"
30 #include "qgscomposerlabel.h"
31 #include "qgscomposerlegend.h"
32 #include "qgscomposermap.h"
33 #include "qgscomposeritemgroup.h"
34 #include "qgscomposerpicture.h"
35 #include "qgscomposerruler.h"
36 #include "qgscomposerscalebar.h"
37 #include "qgscomposershape.h"
39 #include "qgslogger.h"
41 
42 QgsComposerView::QgsComposerView( QWidget* parent, const char* name, Qt::WFlags f )
43  : QGraphicsView( parent )
44  , mRubberBandItem( 0 )
45  , mRubberBandLineItem( 0 )
46  , mMoveContentItem( 0 )
47  , mPaintingEnabled( true )
48  , mHorizontalRuler( 0 )
49  , mVerticalRuler( 0 )
50 {
51  Q_UNUSED( f );
52  Q_UNUSED( name );
53 
54  setResizeAnchor( QGraphicsView::AnchorViewCenter );
55  setMouseTracking( true );
56  viewport()->setMouseTracking( true );
57  setFrameShape( QFrame::NoFrame );
58 }
59 
60 void QgsComposerView::mousePressEvent( QMouseEvent* e )
61 {
62  if ( !composition() )
63  {
64  return;
65  }
66 
67  QPointF scenePoint = mapToScene( e->pos() );
68  QPointF snappedScenePoint = composition()->snapPointToGrid( scenePoint );
69 
70  //lock/unlock position of item with right click
71  if ( e->button() == Qt::RightButton )
72  {
73  QgsComposerItem* selectedItem = composition()->composerItemAt( scenePoint );
74  if ( selectedItem )
75  {
76  bool lock = selectedItem->positionLock() ? false : true;
77  selectedItem->setPositionLock( lock );
78  selectedItem->update();
79  //make sure the new cursor is correct
80  QPointF itemPoint = selectedItem->mapFromScene( scenePoint );
81  selectedItem->updateCursor( itemPoint );
82  }
83  return;
84  }
85 
86  switch ( mCurrentTool )
87  {
88  //select/deselect items and pass mouse event further
89  case Select:
90  {
91  if ( !( e->modifiers() & Qt::ShiftModifier ) ) //keep selection if shift key pressed
92  {
93  composition()->clearSelection();
94  }
95 
96  //select topmost item at position of event
97  QgsComposerItem* selectedItem = composition()->composerItemAt( scenePoint );
98  if ( !selectedItem )
99  {
100  break;
101  }
102 
103  selectedItem->setSelected( true );
105  emit selectedItemChanged( selectedItem );
106  break;
107  }
108 
109  case MoveItemContent:
110  {
111  //store item as member if it is selected and cursor is over item
112  QgsComposerItem* item = dynamic_cast<QgsComposerItem *>( itemAt( e->pos() ) );
113  if ( item )
114  {
115  mMoveContentStartPos = scenePoint;
116  }
117  mMoveContentItem = item;
118  break;
119  }
120 
121  case AddArrow:
122  {
123  mRubberBandStartPos = QPointF( snappedScenePoint.x(), snappedScenePoint.y() );
124  mRubberBandLineItem = new QGraphicsLineItem( snappedScenePoint.x(), snappedScenePoint.y(), snappedScenePoint.x(), snappedScenePoint.y() );
125  mRubberBandLineItem->setZValue( 100 );
126  scene()->addItem( mRubberBandLineItem );
127  scene()->update();
128  break;
129  }
130 
131  //create rubber band for map and ellipse items
132  case AddMap:
133  case AddRectangle:
134  case AddTriangle:
135  case AddEllipse:
136  case AddHtml:
137  {
138  QTransform t;
139  mRubberBandItem = new QGraphicsRectItem( 0, 0, 0, 0 );
140  mRubberBandStartPos = QPointF( snappedScenePoint.x(), snappedScenePoint.y() );
141  t.translate( snappedScenePoint.x(), snappedScenePoint.y() );
142  mRubberBandItem->setTransform( t );
143  mRubberBandItem->setZValue( 100 );
144  scene()->addItem( mRubberBandItem );
145  scene()->update();
146  }
147  break;
148 
149  case AddLabel:
150  if ( composition() )
151  {
152  QgsComposerLabel* newLabelItem = new QgsComposerLabel( composition() );
153  newLabelItem->setText( tr( "QGIS" ) );
154  newLabelItem->adjustSizeToText();
155  newLabelItem->setSceneRect( QRectF( snappedScenePoint.x(), snappedScenePoint.y(), newLabelItem->rect().width(), newLabelItem->rect().height() ) );
156  composition()->addComposerLabel( newLabelItem );
157  emit actionFinished();
158  composition()->pushAddRemoveCommand( newLabelItem, tr( "Label added" ) );
159  }
160  break;
161 
162  case AddScalebar:
163  if ( composition() )
164  {
165  QgsComposerScaleBar* newScaleBar = new QgsComposerScaleBar( composition() );
166  newScaleBar->setSceneRect( QRectF( snappedScenePoint.x(), snappedScenePoint.y(), 20, 20 ) );
167  composition()->addComposerScaleBar( newScaleBar );
168  QList<const QgsComposerMap*> mapItemList = composition()->composerMapItems();
169  if ( mapItemList.size() > 0 )
170  {
171  newScaleBar->setComposerMap( mapItemList.at( 0 ) );
172  }
173  newScaleBar->applyDefaultSize(); //4 segments, 1/5 of composer map width
174  emit actionFinished();
175  composition()->pushAddRemoveCommand( newScaleBar, tr( "Scale bar added" ) );
176  }
177  break;
178 
179  case AddLegend:
180  {
181  if ( composition() )
182  {
183  QgsComposerLegend* newLegend = new QgsComposerLegend( composition() );
184  newLegend->setSceneRect( QRectF( snappedScenePoint.x(), snappedScenePoint.y(), newLegend->rect().width(), newLegend->rect().height() ) );
185  composition()->addComposerLegend( newLegend );
186  newLegend->updateLegend();
187  emit actionFinished();
188  composition()->pushAddRemoveCommand( newLegend, tr( "Legend added" ) );
189  }
190  break;
191  }
192  case AddPicture:
193  if ( composition() )
194  {
195  QgsComposerPicture* newPicture = new QgsComposerPicture( composition() );
196  newPicture->setSceneRect( QRectF( snappedScenePoint.x(), snappedScenePoint.y(), 30, 30 ) );
197  composition()->addComposerPicture( newPicture );
198  emit actionFinished();
199  composition()->pushAddRemoveCommand( newPicture, tr( "Picture added" ) );
200  }
201  break;
202  case AddTable:
203  if ( composition() )
204  {
206  newTable->setSceneRect( QRectF( snappedScenePoint.x(), snappedScenePoint.y(), 50, 50 ) );
207  composition()->addComposerTable( newTable );
208  emit actionFinished();
209  composition()->pushAddRemoveCommand( newTable, tr( "Table added" ) );
210  }
211  break;
212  default:
213  break;
214  }
215 }
216 
217 void QgsComposerView::addShape( Tool currentTool )
218 {
220 
221  if ( currentTool == AddRectangle )
223  else if ( currentTool == AddTriangle )
225 
226  if ( !mRubberBandItem || mRubberBandItem->rect().width() < 0.1 || mRubberBandItem->rect().width() < 0.1 )
227  {
228  scene()->removeItem( mRubberBandItem );
229  delete mRubberBandItem;
230  mRubberBandItem = 0;
231  return;
232  }
233  if ( composition() )
234  {
235  QgsComposerShape* composerShape = new QgsComposerShape( mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height(), composition() );
236  composerShape->setShapeType( shape );
237  composition()->addComposerShape( composerShape );
238  scene()->removeItem( mRubberBandItem );
239  delete mRubberBandItem;
240  mRubberBandItem = 0;
241  emit actionFinished();
242  composition()->pushAddRemoveCommand( composerShape, tr( "Shape added" ) );
243  }
244 }
245 
247 {
248  if ( mHorizontalRuler )
249  {
250  mHorizontalRuler->setSceneTransform( viewportTransform() );
251  }
252  if ( mVerticalRuler )
253  {
254  mVerticalRuler->setSceneTransform( viewportTransform() );
255  }
256 }
257 
259 {
260  if ( !composition() )
261  {
262  return;
263  }
264 
265  QPointF scenePoint = mapToScene( e->pos() );
266 
267  switch ( mCurrentTool )
268  {
269  case Select:
270  {
272  break;
273  }
274 
275  case MoveItemContent:
276  {
277  if ( mMoveContentItem )
278  {
279  //update map preview if composer map
280  QgsComposerMap* composerMap = dynamic_cast<QgsComposerMap *>( mMoveContentItem );
281  if ( composerMap )
282  {
283  composerMap->setOffset( 0, 0 );
284  }
285 
286  double moveX = scenePoint.x() - mMoveContentStartPos.x();
287  double moveY = scenePoint.y() - mMoveContentStartPos.y();
288 
289  composition()->beginCommand( mMoveContentItem, tr( "Move item content" ) );
290  mMoveContentItem->moveContent( -moveX, -moveY );
291  composition()->endCommand();
292  mMoveContentItem = 0;
293  }
294  break;
295  }
296  case AddArrow:
297  if ( composition() )
298  {
299  QPointF scenePoint = mapToScene( e->pos() );
300  QPointF snappedScenePoint = composition()->snapPointToGrid( scenePoint );
301  QgsComposerArrow* composerArrow = new QgsComposerArrow( mRubberBandStartPos, QPointF( snappedScenePoint.x(), snappedScenePoint.y() ), composition() );
302  composition()->addComposerArrow( composerArrow );
303  scene()->removeItem( mRubberBandLineItem );
304  delete mRubberBandLineItem;
306  emit actionFinished();
307  composition()->pushAddRemoveCommand( composerArrow, tr( "Arrow added" ) );
308  }
309  break;
310 
311  case AddRectangle:
312  case AddTriangle:
313  case AddEllipse:
315  break;
316 
317  case AddMap:
318  if ( !mRubberBandItem || mRubberBandItem->rect().width() < 0.1 || mRubberBandItem->rect().width() < 0.1 )
319  {
320  if ( mRubberBandItem )
321  {
322  scene()->removeItem( mRubberBandItem );
323  delete mRubberBandItem;
324  mRubberBandItem = 0;
325  }
326  return;
327  }
328  if ( composition() )
329  {
330  QgsComposerMap* composerMap = new QgsComposerMap( composition(), mRubberBandItem->transform().dx(), mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(), mRubberBandItem->rect().height() );
331  composition()->addComposerMap( composerMap );
332  scene()->removeItem( mRubberBandItem );
333  delete mRubberBandItem;
334  mRubberBandItem = 0;
335  emit actionFinished();
336  composition()->pushAddRemoveCommand( composerMap, tr( "Map added" ) );
337  }
338  break;
339 
340  case AddHtml:
341  if ( composition() )
342  {
343  QgsComposerHtml* composerHtml = new QgsComposerHtml( composition(), true );
345  composerHtml, composition(), tr( "Html item added" ) );
346  composition()->undoStack()->push( command );
347  QgsComposerFrame* frame = new QgsComposerFrame( composition(), composerHtml, mRubberBandItem->transform().dx(),
348  mRubberBandItem->transform().dy(), mRubberBandItem->rect().width(),
349  mRubberBandItem->rect().height() );
350  composition()->beginMultiFrameCommand( composerHtml, tr( "Html frame added" ) );
351  composerHtml->addFrame( frame );
353  scene()->removeItem( mRubberBandItem );
354  delete mRubberBandItem;
355  mRubberBandItem = 0;
356  emit actionFinished();
357  }
358  default:
359  break;
360  }
361 }
362 
363 void QgsComposerView::mouseMoveEvent( QMouseEvent* e )
364 {
365  if ( !composition() )
366  {
367  return;
368  }
369 
370  updateRulers();
371  if ( mHorizontalRuler )
372  {
373  mHorizontalRuler->updateMarker( e->posF() );
374  }
375  if ( mVerticalRuler )
376  {
377  mVerticalRuler->updateMarker( e->posF() );
378  }
379 
380  if ( e->buttons() == Qt::NoButton )
381  {
382  if ( mCurrentTool == Select )
383  {
385  }
386  }
387  else
388  {
389  QPointF scenePoint = mapToScene( e->pos() );
390 
391  switch ( mCurrentTool )
392  {
393  case Select:
395  break;
396 
397  case AddArrow:
398  {
399  if ( mRubberBandLineItem )
400  {
401  mRubberBandLineItem->setLine( mRubberBandStartPos.x(), mRubberBandStartPos.y(), scenePoint.x(), scenePoint.y() );
402  }
403  break;
404  }
405 
406  case AddMap:
407  case AddRectangle:
408  case AddTriangle:
409  case AddEllipse:
410  case AddHtml:
411  //adjust rubber band item
412  {
413  double x = 0;
414  double y = 0;
415  double width = 0;
416  double height = 0;
417 
418  double dx = scenePoint.x() - mRubberBandStartPos.x();
419  double dy = scenePoint.y() - mRubberBandStartPos.y();
420 
421  if ( dx < 0 )
422  {
423  x = scenePoint.x();
424  width = -dx;
425  }
426  else
427  {
428  x = mRubberBandStartPos.x();
429  width = dx;
430  }
431 
432  if ( dy < 0 )
433  {
434  y = scenePoint.y();
435  height = -dy;
436  }
437  else
438  {
439  y = mRubberBandStartPos.y();
440  height = dy;
441  }
442 
443  if ( mRubberBandItem )
444  {
445  mRubberBandItem->setRect( 0, 0, width, height );
446  QTransform t;
447  t.translate( x, y );
448  mRubberBandItem->setTransform( t );
449  }
450  break;
451  }
452 
453  case MoveItemContent:
454  {
455  //update map preview if composer map
456  QgsComposerMap* composerMap = dynamic_cast<QgsComposerMap *>( mMoveContentItem );
457  if ( composerMap )
458  {
459  composerMap->setOffset( scenePoint.x() - mMoveContentStartPos.x(), scenePoint.y() - mMoveContentStartPos.y() );
460  composerMap->update();
461  }
462  break;
463  }
464  default:
465  break;
466  }
467  }
468 }
469 
471 {
472  e->ignore();
473 }
474 
475 void QgsComposerView::keyPressEvent( QKeyEvent * e )
476 {
477  //TODO : those should be actions (so we could also display menu items and/or toolbar items)
478 
479  if ( !composition() )
480  {
481  return;
482  }
483 
484  QList<QgsComposerItem*> composerItemList = composition()->selectedComposerItems();
485  QList<QgsComposerItem*>::iterator itemIt = composerItemList.begin();
486 
487  if ( e->matches( QKeySequence::Copy ) || e->matches( QKeySequence::Cut ) )
488  {
489  QDomDocument doc;
490  QDomElement documentElement = doc.createElement( "ComposerItemClipboard" );
491  for ( ; itemIt != composerItemList.end(); ++itemIt )
492  {
493  // copy each item in a group
494  QgsComposerItemGroup* itemGroup = dynamic_cast<QgsComposerItemGroup*>( *itemIt );
495  if ( itemGroup && composition() )
496  {
497  QSet<QgsComposerItem*> groupedItems = itemGroup->items();
498  QSet<QgsComposerItem*>::iterator it = groupedItems.begin();
499  for ( ; it != groupedItems.end(); ++it )
500  {
501  ( *it )->writeXML( documentElement, doc );
502  }
503  }
504  ( *itemIt )->writeXML( documentElement, doc );
505  if ( e->matches( QKeySequence::Cut ) )
506  {
507  composition()->removeComposerItem( *itemIt );
508  }
509  }
510  doc.appendChild( documentElement );
511 
512  //if it's a copy, we have to remove the UUIDs since we don't want any duplicate UUID
513  if ( e->matches( QKeySequence::Copy ) )
514  {
515  // remove all uuid attributes
516  QDomNodeList composerItemsNodes = doc.elementsByTagName( "ComposerItem" );
517  for ( int i = 0; i < composerItemsNodes.count(); ++i )
518  {
519  QDomNode composerItemNode = composerItemsNodes.at( i );
520  if ( composerItemNode.isElement() )
521  {
522  composerItemNode.toElement().removeAttribute( "uuid" );
523  }
524  }
525  }
526 
527  QMimeData *mimeData = new QMimeData;
528  mimeData->setData( "text/xml", doc.toByteArray() );
529  QClipboard *clipboard = QApplication::clipboard();
530  clipboard->setMimeData( mimeData );
531  }
532 
533  //TODO : "Ctrl+Shift+V" is one way to paste, but on some platefoms you can use Shift+Ins and F18
534  if ( e->matches( QKeySequence::Paste ) || ( e->key() == Qt::Key_V && e->modifiers() & Qt::ControlModifier && e->modifiers() & Qt::ShiftModifier ) )
535  {
536  QDomDocument doc;
537  QClipboard *clipboard = QApplication::clipboard();
538  if ( doc.setContent( clipboard->mimeData()->data( "text/xml" ) ) )
539  {
540  QDomElement docElem = doc.documentElement();
541  if ( docElem.tagName() == "ComposerItemClipboard" )
542  {
543  if ( composition() )
544  {
545  QPointF pt = mapToScene( mapFromGlobal( QCursor::pos() ) );
546  bool pasteInPlace = ( e->modifiers() & Qt::ShiftModifier );
547  composition()->addItemsFromXML( docElem, doc, 0, true, &pt, pasteInPlace );
548  }
549  }
550  }
551  }
552 
553  //delete selected items
554  if ( e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace )
555  {
556  for ( ; itemIt != composerItemList.end(); ++itemIt )
557  {
558  if ( composition() )
559  {
560  composition()->removeComposerItem( *itemIt );
561  }
562  }
563  }
564 
565  else if ( e->key() == Qt::Key_Left )
566  {
567  for ( ; itemIt != composerItemList.end(); ++itemIt )
568  {
569  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
570  ( *itemIt )->move( -1.0, 0.0 );
571  ( *itemIt )->endCommand();
572  }
573  }
574  else if ( e->key() == Qt::Key_Right )
575  {
576  for ( ; itemIt != composerItemList.end(); ++itemIt )
577  {
578  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
579  ( *itemIt )->move( 1.0, 0.0 );
580  ( *itemIt )->endCommand();
581  }
582  }
583  else if ( e->key() == Qt::Key_Down )
584  {
585  for ( ; itemIt != composerItemList.end(); ++itemIt )
586  {
587  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
588  ( *itemIt )->move( 0.0, 1.0 );
589  ( *itemIt )->endCommand();
590  }
591  }
592  else if ( e->key() == Qt::Key_Up )
593  {
594  for ( ; itemIt != composerItemList.end(); ++itemIt )
595  {
596  ( *itemIt )->beginCommand( tr( "Item moved" ), QgsComposerMergeCommand::ItemMove );
597  ( *itemIt )->move( 0.0, -1.0 );
598  ( *itemIt )->endCommand();
599  }
600  }
601 }
602 
603 void QgsComposerView::wheelEvent( QWheelEvent* event )
604 {
605  QPointF scenePoint = mapToScene( event->pos() );
606 
607  //select topmost item at position of event
608  QgsComposerItem* theItem = composition()->composerItemAt( scenePoint );
609  if ( theItem )
610  {
611  if ( theItem->isSelected() )
612  {
613  QPointF itemPoint = theItem->mapFromScene( scenePoint );
614  theItem->beginCommand( tr( "Zoom item content" ) );
615  theItem->zoomContent( event->delta(), itemPoint.x(), itemPoint.y() );
616  theItem->endCommand();
617  }
618  }
619 }
620 
621 void QgsComposerView::paintEvent( QPaintEvent* event )
622 {
623  if ( mPaintingEnabled )
624  {
625  QGraphicsView::paintEvent( event );
626  event->accept();
627  }
628  else
629  {
630  event->ignore();
631  }
632 }
633 
634 void QgsComposerView::hideEvent( QHideEvent* e )
635 {
636  emit( composerViewHide( this ) );
637  e->ignore();
638 }
639 
640 void QgsComposerView::showEvent( QShowEvent* e )
641 {
642  emit( composerViewShow( this ) );
643  e->ignore();
644 }
645 
646 void QgsComposerView::resizeEvent( QResizeEvent* event )
647 {
649  updateRulers();
650 }
651 
652 void QgsComposerView::scrollContentsBy( int dx, int dy )
653 {
655  updateRulers();
656 }
657 
659 {
660  setScene( c );
661  if ( mHorizontalRuler )
662  {
664  }
665  if ( mVerticalRuler )
666  {
668  }
669 }
670 
672 {
673  if ( scene() )
674  {
675  QgsComposition* c = dynamic_cast<QgsComposition *>( scene() );
676  if ( c )
677  {
678  return c;
679  }
680  }
681  return 0;
682 }
683 
685 {
686  if ( !composition() )
687  {
688  return;
689  }
690 
691  QList<QgsComposerItem*> selectionList = composition()->selectedComposerItems();
692  if ( selectionList.size() < 2 )
693  {
694  return; //not enough items for a group
695  }
697 
698  QList<QgsComposerItem*>::iterator itemIter = selectionList.begin();
699  for ( ; itemIter != selectionList.end(); ++itemIter )
700  {
701  itemGroup->addItem( *itemIter );
702  }
703 
704  composition()->addItem( itemGroup );
705  itemGroup->setSelected( true );
706  emit selectedItemChanged( itemGroup );
707 }
708 
710 {
711  if ( !composition() )
712  {
713  return;
714  }
715 
716  QList<QgsComposerItem*> selectionList = composition()->selectedComposerItems();
717  QList<QgsComposerItem*>::iterator itemIter = selectionList.begin();
718  for ( ; itemIter != selectionList.end(); ++itemIter )
719  {
720  QgsComposerItemGroup* itemGroup = dynamic_cast<QgsComposerItemGroup *>( *itemIter );
721  if ( itemGroup )
722  {
723  itemGroup->removeItems();
724  composition()->removeItem( *itemIter );
725  delete( *itemIter );
726  emit itemRemoved( *itemIter );
727  }
728  }
729 }
730 
732 {
733  QMainWindow* composerObject = 0;
734  QObject* currentObject = parent();
735  if ( !currentObject )
736  {
737  return qobject_cast<QMainWindow *>( currentObject );
738  }
739 
740  while ( true )
741  {
742  composerObject = qobject_cast<QMainWindow*>( currentObject );
743  if ( composerObject || currentObject->parent() == 0 )
744  {
745  return composerObject;
746  }
747  currentObject = currentObject->parent();
748  }
749 
750  return 0;
751 }