QGIS API Documentation  2.11.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgscomposition.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposition.cpp
3  -------------------
4  begin : January 2005
5  copyright : (C) 2005 by Radim Blazek
6  email : blazek@itc.it
7  ***************************************************************************/
8 /***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
17 #include "qgscomposition.h"
18 #include "qgscomposerutils.h"
19 #include "qgscomposerarrow.h"
20 #include "qgscomposerframe.h"
21 #include "qgscomposerhtml.h"
22 #include "qgscomposerlabel.h"
23 #include "qgscomposerlegend.h"
24 #include "qgscomposermap.h"
25 #include "qgscomposermapoverview.h"
27 #include "qgscomposeritemgroup.h"
28 #include "qgscomposerpicture.h"
29 #include "qgscomposerscalebar.h"
30 #include "qgscomposershape.h"
31 #include "qgscomposermodel.h"
36 #include "qgspaintenginehack.h"
37 #include "qgspaperitem.h"
38 #include "qgsproject.h"
39 #include "qgsgeometry.h"
40 #include "qgsvectorlayer.h"
41 #include "qgsvectordataprovider.h"
42 #include "qgsexpression.h"
43 #include "qgssymbolv2.h"
44 #include "qgssymbollayerv2utils.h"
45 #include "qgsdatadefined.h"
46 #include "qgslogger.h"
47 
48 #include <QDomDocument>
49 #include <QDomElement>
50 #include <QGraphicsRectItem>
51 #include <QGraphicsView>
52 #include <QPainter>
53 #include <QPrinter>
54 #include <QSettings>
55 #include <QDir>
56 
57 #include <limits>
58 
60  : QGraphicsScene( 0 )
61  , mMapRenderer( mapRenderer )
62  , mMapSettings( mapRenderer->mapSettings() )
63  , mAtlasComposition( this )
64 {
65  init();
66 }
67 
69  : QGraphicsScene( 0 )
70  , mMapRenderer( 0 )
71  , mMapSettings( mapSettings )
72  , mAtlasComposition( this )
73 {
74  init();
75 }
76 
78 {
79  // these members should be ideally in constructor's initialization list, but now we have two constructors...
80  mPlotStyle = QgsComposition::Preview;
81  mPageWidth = 297;
82  mPageHeight = 210;
83  mSpaceBetweenPages = 10;
84  mPageStyleSymbol = 0;
85  mPrintAsRaster = false;
86  mGenerateWorldFile = false;
87  mWorldFileMap = 0;
88  mUseAdvancedEffects = true;
89  mSnapToGrid = false;
90  mGridVisible = false;
91  mSnapGridResolution = 0;
92  mSnapGridOffsetX = 0;
93  mSnapGridOffsetY = 0;
94  mAlignmentSnap = true;
95  mGuidesVisible = true;
96  mSmartGuides = true;
97  mSnapTolerance = 0;
98  mBoundingBoxesVisible = true;
99  mSelectionHandles = 0;
100  mActiveItemCommand = 0;
101  mActiveMultiFrameCommand = 0;
102  mAtlasMode = QgsComposition::AtlasOff;
103  mPreventCursorChange = false;
104  mItemsModel = 0;
105  mUndoStack = new QUndoStack();
106 
107  //data defined strings
108  mDataDefinedNames.insert( QgsComposerObject::PresetPaperSize, QString( "dataDefinedPaperSize" ) );
109  mDataDefinedNames.insert( QgsComposerObject::PaperWidth, QString( "dataDefinedPaperWidth" ) );
110  mDataDefinedNames.insert( QgsComposerObject::PaperHeight, QString( "dataDefinedPaperHeight" ) );
111  mDataDefinedNames.insert( QgsComposerObject::NumPages, QString( "dataDefinedNumPages" ) );
112  mDataDefinedNames.insert( QgsComposerObject::PaperOrientation, QString( "dataDefinedPaperOrientation" ) );
113 
114  //connect to atlas toggling on/off and coverage layer and feature changes
115  //to update data defined values
116  connect( &mAtlasComposition, SIGNAL( toggled( bool ) ), this, SLOT( refreshDataDefinedProperty() ) );
117  connect( &mAtlasComposition, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ), this, SLOT( refreshDataDefinedProperty() ) );
118  connect( &mAtlasComposition, SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( refreshDataDefinedProperty() ) );
119  //also, refreshing composition triggers a recalculation of data defined properties
120  connect( this, SIGNAL( refreshItemsTriggered() ), this, SLOT( refreshDataDefinedProperty() ) );
121  //toggling atlas or changing coverage layer requires data defined expressions to be reprepared
122  connect( &mAtlasComposition, SIGNAL( toggled( bool ) ), this, SLOT( prepareAllDataDefinedExpressions() ) );
123  connect( &mAtlasComposition, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ), this, SLOT( prepareAllDataDefinedExpressions() ) );
124 
125  setBackgroundBrush( QColor( 215, 215, 215 ) );
126  createDefaultPageStyleSymbol();
127 
128  addPaperItem();
129 
130  updateBounds();
131 
132  //add mouse selection handles to composition, and initially hide
133  mSelectionHandles = new QgsComposerMouseHandles( this );
134  addItem( mSelectionHandles );
135  mSelectionHandles->hide();
136  mSelectionHandles->setZValue( 500 );
137 
138  mPrintResolution = 300; //hardcoded default
139 
140  //load default composition settings
141  loadDefaults();
142  loadSettings();
143 
144  mItemsModel = new QgsComposerModel( this );
145 }
146 
147 
148 /*
149 QgsComposition::QgsComposition()
150  : QGraphicsScene( 0 )
151  , mMapRenderer( 0 )
152  , mPlotStyle( QgsComposition::Preview )
153  , mPageWidth( 297 )
154  , mPageHeight( 210 )
155  , mSpaceBetweenPages( 10 )
156  , mPageStyleSymbol( 0 )
157  , mPrintAsRaster( false )
158  , mGenerateWorldFile( false )
159  , mWorldFileMap( 0 )
160  , mUseAdvancedEffects( true )
161  , mSnapToGrid( false )
162  , mGridVisible( false )
163  , mSnapGridResolution( 0 )
164  , mSnapGridTolerance( 0 )
165  , mSnapGridOffsetX( 0 )
166  , mSnapGridOffsetY( 0 )
167  , mAlignmentSnap( true )
168  , mGuidesVisible( true )
169  , mSmartGuides( true )
170  , mAlignmentSnapTolerance( 0 )
171  , mSelectionHandles( 0 )
172  , mActiveItemCommand( 0 )
173  , mActiveMultiFrameCommand( 0 )
174  , mAtlasComposition( this )
175  , mAtlasMode( QgsComposition::AtlasOff )
176  , mPreventCursorChange( false )
177  , mItemsModel( 0 )
178 {
179  //load default composition settings
180  loadDefaults();
181  loadSettings();
182  mItemsModel = new QgsComposerModel( this );
183 }*/
184 
186 {
187  removePaperItems();
188  deleteAndRemoveMultiFrames();
189 
190  // make sure that all composer items are removed before
191  // this class is deconstructed - to avoid segfaults
192  // when composer items access in destructor composition that isn't valid anymore
193  QList<QGraphicsItem*> itemList = items();
194  qDeleteAll( itemList );
195 
196  // clear pointers to QgsDataDefined objects
197  qDeleteAll( mDataDefinedProperties );
198  mDataDefinedProperties.clear();
199 
200  //order is important here - we need to delete model last so that all items have already
201  //been deleted. Deleting the undo stack will also delete any items which have been
202  //removed from the scene, so this needs to be done before deleting the model
203  delete mUndoStack;
204 
205  delete mActiveItemCommand;
206  delete mActiveMultiFrameCommand;
207  delete mPageStyleSymbol;
208  delete mItemsModel;
209 }
210 
211 void QgsComposition::loadDefaults()
212 {
213  QSettings settings;
214  mSnapGridResolution = settings.value( "/Composer/defaultSnapGridResolution", 10.0 ).toDouble();
215  mSnapGridOffsetX = settings.value( "/Composer/defaultSnapGridOffsetX", 0 ).toDouble();
216  mSnapGridOffsetY = settings.value( "/Composer/defaultSnapGridOffsetY", 0 ).toDouble();
217  mSnapTolerance = settings.value( "/Composer/defaultSnapTolerancePixels", 5 ).toInt();
218 }
219 
221 {
222  setSceneRect( compositionBounds() );
223 }
224 
226 {
227  emit refreshItemsTriggered();
228  //force a redraw on all maps
230  composerItems( maps );
232  for ( ; mapIt != maps.end(); ++mapIt )
233  {
234  ( *mapIt )->cache();
235  ( *mapIt )->update();
236  }
237 }
238 
240 {
242  if ( item )
243  {
244  item->setSelected( true );
245  emit selectedItemChanged( item );
246  }
247 }
248 
250 {
251  //we can't use QGraphicsScene::clearSelection, as that emits no signals
252  //and we don't know which items are being unselected
253  //accordingly, we can't inform the composition model of selection changes
254  //instead, do the clear selection manually...
255  QList<QGraphicsItem *> selectedItemList = selectedItems();
256  QList<QGraphicsItem *>::iterator itemIter = selectedItemList.begin();
257 
258  for ( ; itemIter != selectedItemList.end(); ++itemIter )
259  {
260  QgsComposerItem* composerItem = dynamic_cast<QgsComposerItem *>( *itemIter );
261  if ( composerItem )
262  {
263  composerItem->setSelected( false );
264  }
265  }
266 }
267 
269 {
270  const QgsExpressionContext* evalContext = context;
272  if ( !evalContext )
273  {
274  scopedContext.reset( createExpressionContext() );
275  evalContext = scopedContext.data();
276  }
277 
278  //updates data defined properties and redraws composition to match
279  if ( property == QgsComposerObject::NumPages || property == QgsComposerObject::AllProperties )
280  {
281  setNumPages( numPages() );
282  }
283  if ( property == QgsComposerObject::PaperWidth || property == QgsComposerObject::PaperHeight ||
286  {
287  refreshPageSize( evalContext );
288  }
289 }
290 
291 QRectF QgsComposition::compositionBounds() const
292 {
293  //start with an empty rectangle
294  QRectF bounds = QRectF( 0, 0, 0, 0 );
295 
296  //add all QgsComposerItems and QgsPaperItems which are in the composition
297  QList<QGraphicsItem *> itemList = items();
298  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
299  for ( ; itemIt != itemList.end(); ++itemIt )
300  {
301  const QgsComposerItem* composerItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
302  const QgsPaperItem* paperItem = dynamic_cast<const QgsPaperItem*>( *itemIt );
303  if (( composerItem || paperItem ) )
304  {
305  //expand bounds with current item's bounds
306  bounds = bounds.united(( *itemIt )->sceneBoundingRect() );
307  }
308  }
309 
310  //finally, expand bounds out by 5% page size to give a bit of a margin
311  bounds.adjust( -mPageWidth * 0.05, -mPageWidth * 0.05, mPageWidth * 0.05, mPageWidth * 0.05 );
312 
313  return bounds;
314 }
315 
316 void QgsComposition::setPaperSize( const double width, const double height )
317 {
318  if ( width == mPageWidth && height == mPageHeight )
319  {
320  return;
321  }
322 
323  //update item positions
324  QList<QGraphicsItem *> itemList = items();
325  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
326  for ( ; itemIt != itemList.end(); ++itemIt )
327  {
328  QgsComposerItem* composerItem = dynamic_cast<QgsComposerItem *>( *itemIt );
329  if ( composerItem )
330  {
331  composerItem->updatePagePos( width, height );
332  }
333  }
334  //update guide positions and size
336  QList< QGraphicsLineItem* >::iterator guideIt = guides->begin();
337  double totalHeight = ( height + spaceBetweenPages() ) * ( numPages() - 1 ) + height;
338  for ( ; guideIt != guides->end(); ++guideIt )
339  {
340  QLineF line = ( *guideIt )->line();
341  if ( line.dx() == 0 )
342  {
343  //vertical line, change height of line
344  ( *guideIt )->setLine( line.x1(), 0, line.x1(), totalHeight );
345  }
346  else
347  {
348  //horizontal line
349  //move to new vertical position and change width of line
350  QPointF curPagePos = positionOnPage( line.p1() );
351  int curPage = pageNumberForPoint( line.p1() ) - 1;
352  double newY = curPage * ( height + spaceBetweenPages() ) + curPagePos.y();
353  ( *guideIt )->setLine( 0, newY, width, newY );
354  }
355  }
356 
357  mPageWidth = width;
358  mPageHeight = height;
359  double currentY = 0;
360  for ( int i = 0; i < mPages.size(); ++i )
361  {
362  mPages.at( i )->setSceneRect( QRectF( 0, currentY, width, height ) );
363  currentY += ( height + mSpaceBetweenPages );
364  }
365  QgsProject::instance()->dirty( true );
366  updateBounds();
367  emit paperSizeChanged();
368 }
369 
371 {
372  return mPageHeight;
373 }
374 
376 {
377  return mPageWidth;
378 }
379 
380 void QgsComposition::setNumPages( const int pages )
381 {
382  int currentPages = numPages();
383  int desiredPages = pages;
384 
385  //data defined num pages set?
386  QVariant exprVal;
388  if ( dataDefinedEvaluate( QgsComposerObject::NumPages, exprVal, *context.data(), &mDataDefinedProperties ) )
389  {
390  bool ok = false;
391  int pagesD = exprVal.toInt( &ok );
392  QgsDebugMsg( QString( "exprVal NumPages:%1" ).arg( pagesD ) );
393  if ( ok )
394  {
395  desiredPages = pagesD;
396  }
397  }
398 
399  int diff = desiredPages - currentPages;
400  if ( diff >= 0 )
401  {
402  for ( int i = 0; i < diff; ++i )
403  {
404  addPaperItem();
405  }
406  }
407  else
408  {
409  diff = -diff;
410  for ( int i = 0; i < diff; ++i )
411  {
412  delete mPages.last();
413  mPages.removeLast();
414  }
415  }
416 
417  //update vertical guide height
419  QList< QGraphicsLineItem* >::iterator guideIt = guides->begin();
420  double totalHeight = ( mPageHeight + spaceBetweenPages() ) * ( pages - 1 ) + mPageHeight;
421  for ( ; guideIt != guides->end(); ++guideIt )
422  {
423  QLineF line = ( *guideIt )->line();
424  if ( line.dx() == 0 )
425  {
426  //vertical line, change height of line
427  ( *guideIt )->setLine( line.x1(), 0, line.x1(), totalHeight );
428  }
429  }
430 
431  //update the corresponding variable
432  QgsExpression::setSpecialColumn( "$numpages", QVariant(( int )numPages() ) );
433 
434  QgsProject::instance()->dirty( true );
435  updateBounds();
436 
437  emit nPagesChanged();
438 }
439 
441 {
442  return mPages.size();
443 }
444 
445 bool QgsComposition::pageIsEmpty( const int page ) const
446 {
447  //get all items on page
449  //composerItemsOnPage uses 0-based page numbering
450  composerItemsOnPage( items, page - 1 );
451 
452  //loop through and check for non-paper items
454  for ( ; itemIt != items.constEnd(); ++itemIt )
455  {
456  //is item a paper item?
457  QgsPaperItem* paper = dynamic_cast<QgsPaperItem*>( *itemIt );
458  if ( !paper )
459  {
460  //item is not a paper item, so we have other items on the page
461  return false;
462  }
463  }
464  //no non-paper items
465  return true;
466 }
467 
468 bool QgsComposition::shouldExportPage( const int page ) const
469 {
470  if ( page > numPages() || page < 1 )
471  {
472  //page number out of range
473  return false;
474  }
475 
476  //check all frame items on page
478  //composerItemsOnPage uses 0 based page numbering
479  composerItemsOnPage( frames, page - 1 );
481  for ( ; frameIt != frames.constEnd(); ++frameIt )
482  {
483  if (( *frameIt )->hidePageIfEmpty() && ( *frameIt )->isEmpty() )
484  {
485  //frame is set to hide page if empty, and frame is empty, so we don't want to export this page
486  return false;
487  }
488  }
489  return true;
490 }
491 
493 {
494  delete mPageStyleSymbol;
495  mPageStyleSymbol = static_cast<QgsFillSymbolV2*>( symbol->clone() );
496  QgsProject::instance()->dirty( true );
497 }
498 
499 void QgsComposition::createDefaultPageStyleSymbol()
500 {
501  delete mPageStyleSymbol;
502  QgsStringMap properties;
503  properties.insert( "color", "white" );
504  properties.insert( "style", "solid" );
505  properties.insert( "style_border", "no" );
506  properties.insert( "joinstyle", "miter" );
507  mPageStyleSymbol = QgsFillSymbolV2::createSimple( properties );
508 }
509 
511 {
512  double y;
513  if ( position.y() > ( mPages.size() - 1 ) * ( paperHeight() + spaceBetweenPages() ) )
514  {
515  //y coordinate is greater then the end of the last page, so return distance between
516  //top of last page and y coordinate
517  y = position.y() - ( mPages.size() - 1 ) * ( paperHeight() + spaceBetweenPages() );
518  }
519  else
520  {
521  //y coordinate is less then the end of the last page
522  y = fmod( position.y(), ( paperHeight() + spaceBetweenPages() ) );
523  }
524  return QPointF( position.x(), y );
525 }
526 
527 int QgsComposition::pageNumberForPoint( const QPointF & position ) const
528 {
529  int pageNumber = qFloor( position.y() / ( paperHeight() + spaceBetweenPages() ) ) + 1;
530  pageNumber = pageNumber < 1 ? 1 : pageNumber;
531  pageNumber = pageNumber > mPages.size() ? mPages.size() : pageNumber;
532  return pageNumber;
533 }
534 
536 {
537  emit statusMsgChanged( message );
538 }
539 
540 QgsComposerItem* QgsComposition::composerItemAt( const QPointF & position, const bool ignoreLocked ) const
541 {
542  return composerItemAt( position, 0, ignoreLocked );
543 }
544 
545 QgsComposerItem* QgsComposition::composerItemAt( const QPointF & position, const QgsComposerItem* belowItem, const bool ignoreLocked ) const
546 {
547  //get a list of items which intersect the specified position, in descending z order
548  QList<QGraphicsItem*> itemList;
549  itemList = items( position, Qt::IntersectsItemShape, Qt::DescendingOrder );
550  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
551 
552  bool foundBelowItem = false;
553  for ( ; itemIt != itemList.end(); ++itemIt )
554  {
555  QgsComposerItem* composerItem = dynamic_cast<QgsComposerItem *>( *itemIt );
556  QgsPaperItem* paperItem = dynamic_cast<QgsPaperItem*>( *itemIt );
557  if ( composerItem && !paperItem )
558  {
559  // If we are not checking for a an item below a specified item, or if we've
560  // already found that item, then we've found our target
561  if (( ! belowItem || foundBelowItem ) && ( !ignoreLocked || !composerItem->positionLock() ) )
562  {
563  return composerItem;
564  }
565  else
566  {
567  if ( composerItem == belowItem )
568  {
569  //Target item is next in list
570  foundBelowItem = true;
571  }
572  }
573  }
574  }
575  return 0;
576 }
577 
578 int QgsComposition::pageNumberAt( const QPointF& position ) const
579 {
580  return position.y() / ( paperHeight() + spaceBetweenPages() );
581 }
582 
584 {
585  return pageNumberAt( QPointF( item->pos().x(), item->pos().y() ) );
586 }
587 
589 {
590  QList<QgsComposerItem*> composerItemList;
591 
592  QList<QGraphicsItem *> graphicsItemList = selectedItems();
593  QList<QGraphicsItem *>::iterator itemIter = graphicsItemList.begin();
594 
595  for ( ; itemIter != graphicsItemList.end(); ++itemIter )
596  {
597  QgsComposerItem* composerItem = dynamic_cast<QgsComposerItem *>( *itemIter );
598  if ( composerItem && ( includeLockedItems || !composerItem->positionLock() ) )
599  {
600  composerItemList.push_back( composerItem );
601  }
602  }
603 
604  return composerItemList;
605 }
606 
608 {
609  QList<const QgsComposerMap*> resultList;
610 
611  QList<QGraphicsItem *> itemList = items();
612  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
613  for ( ; itemIt != itemList.end(); ++itemIt )
614  {
615  const QgsComposerMap* composerMap = dynamic_cast<const QgsComposerMap *>( *itemIt );
616  if ( composerMap )
617  {
618  resultList.push_back( composerMap );
619  }
620  }
621 
622  return resultList;
623 }
624 
626 {
627  QList<QGraphicsItem *> itemList = items();
628  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
629  for ( ; itemIt != itemList.end(); ++itemIt )
630  {
631  const QgsComposerMap* composerMap = dynamic_cast<const QgsComposerMap *>( *itemIt );
632  if ( composerMap )
633  {
634  if ( composerMap->id() == id )
635  {
636  return composerMap;
637  }
638  }
639  }
640  return 0;
641 }
642 
644 {
645  // an html item will be a composer frame and if it is we can try to get
646  // its multiframe parent and then try to cast that to a composer html
647  const QgsComposerFrame* composerFrame =
648  dynamic_cast<const QgsComposerFrame *>( item );
649  if ( composerFrame )
650  {
651  const QgsComposerMultiFrame * mypMultiFrame = composerFrame->multiFrame();
652  const QgsComposerHtml* composerHtml =
653  dynamic_cast<const QgsComposerHtml *>( mypMultiFrame );
654  if ( composerHtml )
655  {
656  return composerHtml;
657  }
658  }
659  return 0;
660 }
661 
663 {
664  QList<QGraphicsItem *> itemList = items();
665  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
666  for ( ; itemIt != itemList.end(); ++itemIt )
667  {
668  const QgsComposerItem* mypItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
669  if ( mypItem )
670  {
671  if ( mypItem->id() == theId )
672  {
673  return mypItem;
674  }
675  }
676  }
677  return 0;
678 }
679 
680 #if 0
681 const QgsComposerItem* QgsComposition::getComposerItemByUuid( QString theUuid, bool inAllComposers ) const
682 {
683  //This does not work since it seems impossible to get the QgisApp::instance() from here... Is there a workaround ?
685 
686  if ( inAllComposers )
687  {
688  composers = QgisApp::instance()->printComposers();
689  }
690  else
691  {
692  composers.insert( this )
693  }
694 
696  for ( ; it != composers.constEnd(); ++it )
697  {
698  QList<QGraphicsItem *> itemList = ( *it )->items();
699  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
700  for ( ; itemIt != itemList.end(); ++itemIt )
701  {
702  const QgsComposerItem* mypItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
703  if ( mypItem )
704  {
705  if ( mypItem->uuid() == theUuid )
706  {
707  return mypItem;
708  }
709  }
710  }
711  }
712 
713  return 0;
714 }
715 #endif
716 
718 {
719  QList<QGraphicsItem *> itemList = items();
720  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
721  for ( ; itemIt != itemList.end(); ++itemIt )
722  {
723  const QgsComposerItem* mypItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
724  if ( mypItem )
725  {
726  if ( mypItem->uuid() == theUuid )
727  {
728  return mypItem;
729  }
730  }
731  }
732 
733  return 0;
734 }
735 
737 {
738  mPrintResolution = dpi;
739  emit printResolutionChanged();
740  QgsProject::instance()->dirty( true );
741 }
742 
743 void QgsComposition::setUseAdvancedEffects( const bool effectsEnabled )
744 {
745  mUseAdvancedEffects = effectsEnabled;
746 
747  //toggle effects for all composer items
748  QList<QGraphicsItem*> itemList = items();
750  for ( ; itemIt != itemList.constEnd(); ++itemIt )
751  {
752  QgsComposerItem* composerItem = dynamic_cast<QgsComposerItem*>( *itemIt );
753  if ( composerItem )
754  {
755  composerItem->setEffectsEnabled( effectsEnabled );
756  }
757  }
758 }
759 
760 int QgsComposition::pixelFontSize( double pointSize ) const
761 {
762  return qRound( QgsComposerUtils::pointsToMM( pointSize ) ); //round to nearest mm
763 }
764 
765 double QgsComposition::pointFontSize( int pixelSize ) const
766 {
767  return QgsComposerUtils::mmToPoints( pixelSize );
768 }
769 
771 {
772  if ( composerElem.isNull() )
773  {
774  return false;
775  }
776 
777  QDomElement compositionElem = doc.createElement( "Composition" );
778  compositionElem.setAttribute( "paperWidth", QString::number( mPageWidth ) );
779  compositionElem.setAttribute( "paperHeight", QString::number( mPageHeight ) );
780  compositionElem.setAttribute( "numPages", mPages.size() );
781 
782  QDomElement pageStyleElem = QgsSymbolLayerV2Utils::saveSymbol( QString(), mPageStyleSymbol, doc );
783  compositionElem.appendChild( pageStyleElem );
784 
785  //snapping
786  if ( mSnapToGrid )
787  {
788  compositionElem.setAttribute( "snapping", "1" );
789  }
790  else
791  {
792  compositionElem.setAttribute( "snapping", "0" );
793  }
794  if ( mGridVisible )
795  {
796  compositionElem.setAttribute( "gridVisible", "1" );
797  }
798  else
799  {
800  compositionElem.setAttribute( "gridVisible", "0" );
801  }
802  compositionElem.setAttribute( "snapGridResolution", QString::number( mSnapGridResolution ) );
803  compositionElem.setAttribute( "snapGridOffsetX", QString::number( mSnapGridOffsetX ) );
804  compositionElem.setAttribute( "snapGridOffsetY", QString::number( mSnapGridOffsetY ) );
805 
806  //custom snap lines
808  for ( ; snapLineIt != mSnapLines.constEnd(); ++snapLineIt )
809  {
810  QDomElement snapLineElem = doc.createElement( "SnapLine" );
811  QLineF line = ( *snapLineIt )->line();
812  snapLineElem.setAttribute( "x1", QString::number( line.x1() ) );
813  snapLineElem.setAttribute( "y1", QString::number( line.y1() ) );
814  snapLineElem.setAttribute( "x2", QString::number( line.x2() ) );
815  snapLineElem.setAttribute( "y2", QString::number( line.y2() ) );
816  compositionElem.appendChild( snapLineElem );
817  }
818 
819  compositionElem.setAttribute( "printResolution", mPrintResolution );
820  compositionElem.setAttribute( "printAsRaster", mPrintAsRaster );
821 
822  compositionElem.setAttribute( "generateWorldFile", mGenerateWorldFile ? 1 : 0 );
823  if ( mGenerateWorldFile && mWorldFileMap )
824  {
825  compositionElem.setAttribute( "worldFileMap", mWorldFileMap->id() );
826  }
827 
828  compositionElem.setAttribute( "alignmentSnap", mAlignmentSnap ? 1 : 0 );
829  compositionElem.setAttribute( "guidesVisible", mGuidesVisible ? 1 : 0 );
830  compositionElem.setAttribute( "smartGuides", mSmartGuides ? 1 : 0 );
831  compositionElem.setAttribute( "snapTolerancePixels", mSnapTolerance );
832 
833  //save items except paper items and frame items (they are saved with the corresponding multiframe)
834  QList<QGraphicsItem*> itemList = items();
836  for ( ; itemIt != itemList.constEnd(); ++itemIt )
837  {
838  const QgsComposerItem* composerItem = dynamic_cast<const QgsComposerItem*>( *itemIt );
839  if ( composerItem )
840  {
841  if ( composerItem->type() != QgsComposerItem::ComposerPaper && composerItem->type() != QgsComposerItem::ComposerFrame )
842  {
843  composerItem->writeXML( compositionElem, doc );
844  }
845  }
846  }
847 
848  //save multiframes
849  QSet<QgsComposerMultiFrame*>::const_iterator multiFrameIt = mMultiFrames.constBegin();
850  for ( ; multiFrameIt != mMultiFrames.constEnd(); ++multiFrameIt )
851  {
852  ( *multiFrameIt )->writeXML( compositionElem, doc );
853  }
854  composerElem.appendChild( compositionElem );
855 
856  //data defined properties
857  QgsComposerUtils::writeDataDefinedPropertyMap( compositionElem, doc, &mDataDefinedNames, &mDataDefinedProperties );
858 
859  //custom properties
860  mCustomProperties.writeXml( compositionElem, doc );
861 
862  return true;
863 }
864 
865 bool QgsComposition::readXML( const QDomElement& compositionElem, const QDomDocument& doc )
866 {
867  Q_UNUSED( doc );
868  if ( compositionElem.isNull() )
869  {
870  return false;
871  }
872 
873  //create pages
874  bool widthConversionOk, heightConversionOk;
875  mPageWidth = compositionElem.attribute( "paperWidth" ).toDouble( &widthConversionOk );
876  mPageHeight = compositionElem.attribute( "paperHeight" ).toDouble( &heightConversionOk );
877  emit paperSizeChanged();
878  int numPages = compositionElem.attribute( "numPages", "1" ).toInt();
879 
880  QDomElement pageStyleSymbolElem = compositionElem.firstChildElement( "symbol" );
881  if ( !pageStyleSymbolElem.isNull() )
882  {
883  delete mPageStyleSymbol;
884  mPageStyleSymbol = QgsSymbolLayerV2Utils::loadSymbol<QgsFillSymbolV2>( pageStyleSymbolElem );
885  }
886 
887  if ( widthConversionOk && heightConversionOk )
888  {
889  removePaperItems();
890  for ( int i = 0; i < numPages; ++i )
891  {
892  addPaperItem();
893  }
894  }
895 
896  //snapping
897  mSnapToGrid = compositionElem.attribute( "snapping", "0" ).toInt() == 0 ? false : true;
898  mGridVisible = compositionElem.attribute( "gridVisible", "0" ).toInt() == 0 ? false : true;
899 
900  mSnapGridResolution = compositionElem.attribute( "snapGridResolution" ).toDouble();
901  mSnapGridOffsetX = compositionElem.attribute( "snapGridOffsetX" ).toDouble();
902  mSnapGridOffsetY = compositionElem.attribute( "snapGridOffsetY" ).toDouble();
903 
904  mAlignmentSnap = compositionElem.attribute( "alignmentSnap", "1" ).toInt() == 0 ? false : true;
905  mGuidesVisible = compositionElem.attribute( "guidesVisible", "1" ).toInt() == 0 ? false : true;
906  mSmartGuides = compositionElem.attribute( "smartGuides", "1" ).toInt() == 0 ? false : true;
907  mSnapTolerance = compositionElem.attribute( "snapTolerancePixels", "10" ).toInt();
908 
909  //custom snap lines
910  QDomNodeList snapLineNodes = compositionElem.elementsByTagName( "SnapLine" );
911  for ( int i = 0; i < snapLineNodes.size(); ++i )
912  {
913  QDomElement snapLineElem = snapLineNodes.at( i ).toElement();
914  QGraphicsLineItem* snapItem = addSnapLine();
915  double x1 = snapLineElem.attribute( "x1" ).toDouble();
916  double y1 = snapLineElem.attribute( "y1" ).toDouble();
917  double x2 = snapLineElem.attribute( "x2" ).toDouble();
918  double y2 = snapLineElem.attribute( "y2" ).toDouble();
919  snapItem->setLine( x1, y1, x2, y2 );
920  }
921 
922  mPrintAsRaster = compositionElem.attribute( "printAsRaster" ).toInt();
923  mPrintResolution = compositionElem.attribute( "printResolution", "300" ).toInt();
924 
925  mGenerateWorldFile = compositionElem.attribute( "generateWorldFile", "0" ).toInt() == 1 ? true : false;
926 
927  //data defined properties
928  QgsComposerUtils::readDataDefinedPropertyMap( compositionElem, &mDataDefinedNames, &mDataDefinedProperties );
929 
930  //custom properties
931  mCustomProperties.readXml( compositionElem );
932 
933  updatePaperItems();
934 
935  updateBounds();
936 
937  return true;
938 }
939 
940 bool QgsComposition::loadFromTemplate( const QDomDocument& doc, QMap<QString, QString>* substitutionMap, bool addUndoCommands, const bool clearComposition )
941 {
942  if ( clearComposition )
943  {
944  deleteAndRemoveMultiFrames();
945 
946  //delete all non paper items and emit itemRemoved signal
947  QList<QGraphicsItem *> itemList = items();
948  QList<QGraphicsItem *>::iterator itemIter = itemList.begin();
949  for ( ; itemIter != itemList.end(); ++itemIter )
950  {
951  QgsComposerItem* cItem = dynamic_cast<QgsComposerItem*>( *itemIter );
952  QgsPaperItem* pItem = dynamic_cast<QgsPaperItem*>( *itemIter );
953  if ( cItem && !pItem )
954  {
955  removeItem( cItem );
956  emit itemRemoved( cItem );
957  delete cItem;
958  }
959  }
960  mItemsModel->clear();
961 
962  removePaperItems();
963  mUndoStack->clear();
964  }
965 
966  QDomDocument importDoc;
967  if ( substitutionMap )
968  {
969  QString xmlString = doc.toString();
970  QMap<QString, QString>::const_iterator sIt = substitutionMap->constBegin();
971  for ( ; sIt != substitutionMap->constEnd(); ++sIt )
972  {
973  xmlString = xmlString.replace( "[" + sIt.key() + "]", encodeStringForXML( sIt.value() ) );
974  }
975 
976  QString errorMsg;
977  int errorLine, errorColumn;
978  if ( !importDoc.setContent( xmlString, &errorMsg, &errorLine, &errorColumn ) )
979  {
980  return false;
981  }
982  }
983  else
984  {
985  importDoc = doc;
986  }
987 
988  //read general settings
989  QDomElement atlasElem;
990  if ( clearComposition )
991  {
992  QDomElement compositionElem = importDoc.documentElement().firstChildElement( "Composition" );
993  if ( compositionElem.isNull() )
994  {
995  return false;
996  }
997 
998  bool ok = readXML( compositionElem, importDoc );
999  if ( !ok )
1000  {
1001  return false;
1002  }
1003 
1004  // read atlas parameters - must be done before adding items
1005  atlasElem = importDoc.documentElement().firstChildElement( "Atlas" );
1006  atlasComposition().readXML( atlasElem, importDoc );
1007  }
1008 
1009  // remove all uuid attributes since we don't want duplicates UUIDS
1010  QDomNodeList composerItemsNodes = importDoc.elementsByTagName( "ComposerItem" );
1011  for ( int i = 0; i < composerItemsNodes.count(); ++i )
1012  {
1013  QDomNode composerItemNode = composerItemsNodes.at( i );
1014  if ( composerItemNode.isElement() )
1015  {
1016  composerItemNode.toElement().setAttribute( "templateUuid", composerItemNode.toElement().attribute( "uuid" ) );
1017  composerItemNode.toElement().removeAttribute( "uuid" );
1018  }
1019  }
1020 
1021  //addItemsFromXML
1022  addItemsFromXML( importDoc.documentElement(), importDoc, 0, addUndoCommands, 0 );
1023 
1024  //read atlas map parameters (for pre 2.2 templates)
1025  //this can only be done after items have been added
1026  if ( clearComposition )
1027  {
1028  atlasComposition().readXMLMapSettings( atlasElem, importDoc );
1029  }
1030  return true;
1031 }
1032 
1033 QPointF QgsComposition::minPointFromXml( const QDomElement& elem ) const
1034 {
1035  double minX = std::numeric_limits<double>::max();
1036  double minY = std::numeric_limits<double>::max();
1037  QDomNodeList composerItemList = elem.elementsByTagName( "ComposerItem" );
1038  for ( int i = 0; i < composerItemList.size(); ++i )
1039  {
1040  QDomElement currentComposerItemElem = composerItemList.at( i ).toElement();
1041  double x, y;
1042  bool xOk, yOk;
1043  x = currentComposerItemElem.attribute( "x" ).toDouble( &xOk );
1044  y = currentComposerItemElem.attribute( "y" ).toDouble( &yOk );
1045  if ( !xOk || !yOk )
1046  {
1047  continue;
1048  }
1049  minX = qMin( minX, x );
1050  minY = qMin( minY, y );
1051  }
1052  if ( minX < std::numeric_limits<double>::max() )
1053  {
1054  return QPointF( minX, minY );
1055  }
1056  else
1057  {
1058  return QPointF( 0, 0 );
1059  }
1060 }
1061 
1063  bool addUndoCommands, QPointF* pos, bool pasteInPlace )
1064 {
1065  QPointF* pasteInPlacePt = 0;
1066 
1067  //if we are adding items to a composition which already contains items, we need to make sure
1068  //these items are placed at the top of the composition and that zValues are not duplicated
1069  //so, calculate an offset which needs to be added to the zValue of created items
1070  int zOrderOffset = mItemsModel->zOrderListSize();
1071 
1072  QPointF pasteShiftPos;
1073  QgsComposerItem* lastPastedItem = 0;
1074  if ( pos )
1075  {
1076  //If we are placing items relative to a certain point, then calculate how much we need
1077  //to shift the items by so that they are placed at this point
1078  //First, calculate the minimum position from the xml
1079  QPointF minItemPos = minPointFromXml( elem );
1080  //next, calculate how much each item needs to be shifted from its original position
1081  //so that it's placed at the correct relative position
1082  pasteShiftPos = *pos - minItemPos;
1083 
1084  //since we are pasting items, clear the existing selection
1085  setAllUnselected();
1086 
1087  if ( pasteInPlace )
1088  {
1089  pasteInPlacePt = new QPointF( 0, pageNumberAt( *pos ) * ( mPageHeight + mSpaceBetweenPages ) );
1090  }
1091  }
1092  QDomNodeList composerLabelList = elem.elementsByTagName( "ComposerLabel" );
1093  for ( int i = 0; i < composerLabelList.size(); ++i )
1094  {
1095  QDomElement currentComposerLabelElem = composerLabelList.at( i ).toElement();
1096  QgsComposerLabel* newLabel = new QgsComposerLabel( this );
1097  newLabel->readXML( currentComposerLabelElem, doc );
1098  if ( pos )
1099  {
1100  if ( pasteInPlacePt )
1101  {
1102  newLabel->setItemPosition( newLabel->pos().x(), fmod( newLabel->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1103  newLabel->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1104  }
1105  else
1106  {
1107  newLabel->move( pasteShiftPos.x(), pasteShiftPos.y() );
1108  }
1109  newLabel->setSelected( true );
1110  lastPastedItem = newLabel;
1111  }
1112  addComposerLabel( newLabel );
1113  newLabel->setZValue( newLabel->zValue() + zOrderOffset );
1114  if ( addUndoCommands )
1115  {
1116  pushAddRemoveCommand( newLabel, tr( "Label added" ) );
1117  }
1118  }
1119  // map
1120  QDomNodeList composerMapList = elem.elementsByTagName( "ComposerMap" );
1121  for ( int i = 0; i < composerMapList.size(); ++i )
1122  {
1123  QDomElement currentComposerMapElem = composerMapList.at( i ).toElement();
1124  QgsComposerMap* newMap = new QgsComposerMap( this );
1125 
1126  if ( mapsToRestore )
1127  {
1128  newMap->setUpdatesEnabled( false );
1129  }
1130 
1131  newMap->readXML( currentComposerMapElem, doc );
1132  newMap->assignFreeId();
1133 
1134  if ( mapsToRestore )
1135  {
1136  mapsToRestore->insert( newMap, ( int )( newMap->previewMode() ) );
1138  newMap->setUpdatesEnabled( true );
1139  }
1140  addComposerMap( newMap, false );
1141  newMap->setZValue( newMap->zValue() + zOrderOffset );
1142  if ( pos )
1143  {
1144  if ( pasteInPlace )
1145  {
1146  newMap->setItemPosition( newMap->pos().x(), fmod( newMap->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1147  newMap->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1148  }
1149  else
1150  {
1151  newMap->move( pasteShiftPos.x(), pasteShiftPos.y() );
1152  }
1153  newMap->setSelected( true );
1154  lastPastedItem = newMap;
1155  }
1156 
1157  if ( addUndoCommands )
1158  {
1159  pushAddRemoveCommand( newMap, tr( "Map added" ) );
1160  }
1161  }
1162  //now that all map items have been created, re-connect overview map signals
1164  composerItems( maps );
1165  for ( QList<QgsComposerMap*>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
1166  {
1167  QgsComposerMap* map = ( *mit );
1168  if ( map )
1169  {
1170  QList<QgsComposerMapOverview* > overviews = map->overviews()->asList();
1171  QList<QgsComposerMapOverview* >::iterator overviewIt = overviews.begin();
1172  for ( ; overviewIt != overviews.end(); ++overviewIt )
1173  {
1174  ( *overviewIt )->connectSignals();
1175  }
1176  }
1177  }
1178 
1179  // arrow
1180  QDomNodeList composerArrowList = elem.elementsByTagName( "ComposerArrow" );
1181  for ( int i = 0; i < composerArrowList.size(); ++i )
1182  {
1183  QDomElement currentComposerArrowElem = composerArrowList.at( i ).toElement();
1184  QgsComposerArrow* newArrow = new QgsComposerArrow( this );
1185  newArrow->readXML( currentComposerArrowElem, doc );
1186  if ( pos )
1187  {
1188  if ( pasteInPlace )
1189  {
1190  newArrow->setItemPosition( newArrow->pos().x(), fmod( newArrow->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1191  newArrow->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1192  }
1193  else
1194  {
1195  newArrow->move( pasteShiftPos.x(), pasteShiftPos.y() );
1196  }
1197  newArrow->setSelected( true );
1198  lastPastedItem = newArrow;
1199  }
1200  addComposerArrow( newArrow );
1201  newArrow->setZValue( newArrow->zValue() + zOrderOffset );
1202  if ( addUndoCommands )
1203  {
1204  pushAddRemoveCommand( newArrow, tr( "Arrow added" ) );
1205  }
1206  }
1207  // scalebar
1208  QDomNodeList composerScaleBarList = elem.elementsByTagName( "ComposerScaleBar" );
1209  for ( int i = 0; i < composerScaleBarList.size(); ++i )
1210  {
1211  QDomElement currentComposerScaleBarElem = composerScaleBarList.at( i ).toElement();
1212  QgsComposerScaleBar* newScaleBar = new QgsComposerScaleBar( this );
1213  newScaleBar->readXML( currentComposerScaleBarElem, doc );
1214  if ( pos )
1215  {
1216  if ( pasteInPlace )
1217  {
1218  newScaleBar->setItemPosition( newScaleBar->pos().x(), fmod( newScaleBar->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1219  newScaleBar->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1220  }
1221  else
1222  {
1223  newScaleBar->move( pasteShiftPos.x(), pasteShiftPos.y() );
1224  }
1225  newScaleBar->setSelected( true );
1226  lastPastedItem = newScaleBar;
1227  }
1228  addComposerScaleBar( newScaleBar );
1229  newScaleBar->setZValue( newScaleBar->zValue() + zOrderOffset );
1230  if ( addUndoCommands )
1231  {
1232  pushAddRemoveCommand( newScaleBar, tr( "Scale bar added" ) );
1233  }
1234  }
1235  // shape
1236  QDomNodeList composerShapeList = elem.elementsByTagName( "ComposerShape" );
1237  for ( int i = 0; i < composerShapeList.size(); ++i )
1238  {
1239  QDomElement currentComposerShapeElem = composerShapeList.at( i ).toElement();
1240  QgsComposerShape* newShape = new QgsComposerShape( this );
1241  newShape->readXML( currentComposerShapeElem, doc );
1242  //new shapes should default to symbol v2
1243  newShape->setUseSymbolV2( true );
1244  if ( pos )
1245  {
1246  if ( pasteInPlace )
1247  {
1248  newShape->setItemPosition( newShape->pos().x(), fmod( newShape->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1249  newShape->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1250  }
1251  else
1252  {
1253  newShape->move( pasteShiftPos.x(), pasteShiftPos.y() );
1254  }
1255  newShape->setSelected( true );
1256  lastPastedItem = newShape;
1257  }
1258  addComposerShape( newShape );
1259  newShape->setZValue( newShape->zValue() + zOrderOffset );
1260  if ( addUndoCommands )
1261  {
1262  pushAddRemoveCommand( newShape, tr( "Shape added" ) );
1263  }
1264  }
1265  // picture
1266  QDomNodeList composerPictureList = elem.elementsByTagName( "ComposerPicture" );
1267  for ( int i = 0; i < composerPictureList.size(); ++i )
1268  {
1269  QDomElement currentComposerPictureElem = composerPictureList.at( i ).toElement();
1270  QgsComposerPicture* newPicture = new QgsComposerPicture( this );
1271  newPicture->readXML( currentComposerPictureElem, doc );
1272  if ( pos )
1273  {
1274  if ( pasteInPlace )
1275  {
1276  newPicture->setItemPosition( newPicture->pos().x(), fmod( newPicture->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1277  newPicture->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1278  }
1279  else
1280  {
1281  newPicture->move( pasteShiftPos.x(), pasteShiftPos.y() );
1282  }
1283  newPicture->setSelected( true );
1284  lastPastedItem = newPicture;
1285  }
1286  addComposerPicture( newPicture );
1287  newPicture->setZValue( newPicture->zValue() + zOrderOffset );
1288  if ( addUndoCommands )
1289  {
1290  pushAddRemoveCommand( newPicture, tr( "Picture added" ) );
1291  }
1292  }
1293  // legend
1294  QDomNodeList composerLegendList = elem.elementsByTagName( "ComposerLegend" );
1295  for ( int i = 0; i < composerLegendList.size(); ++i )
1296  {
1297  QDomElement currentComposerLegendElem = composerLegendList.at( i ).toElement();
1298  QgsComposerLegend* newLegend = new QgsComposerLegend( this );
1299  newLegend->readXML( currentComposerLegendElem, doc );
1300  if ( pos )
1301  {
1302  if ( pasteInPlace )
1303  {
1304  newLegend->setItemPosition( newLegend->pos().x(), fmod( newLegend->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1305  newLegend->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1306  }
1307  else
1308  {
1309  newLegend->move( pasteShiftPos.x(), pasteShiftPos.y() );
1310  }
1311  newLegend->setSelected( true );
1312  lastPastedItem = newLegend;
1313  }
1314  addComposerLegend( newLegend );
1315  newLegend->setZValue( newLegend->zValue() + zOrderOffset );
1316  if ( addUndoCommands )
1317  {
1318  pushAddRemoveCommand( newLegend, tr( "Legend added" ) );
1319  }
1320  }
1321  // table
1322  QDomNodeList composerTableList = elem.elementsByTagName( "ComposerAttributeTable" );
1323  for ( int i = 0; i < composerTableList.size(); ++i )
1324  {
1325  QDomElement currentComposerTableElem = composerTableList.at( i ).toElement();
1326  QgsComposerAttributeTable* newTable = new QgsComposerAttributeTable( this );
1327  newTable->readXML( currentComposerTableElem, doc );
1328  if ( pos )
1329  {
1330  if ( pasteInPlace )
1331  {
1332  newTable->setItemPosition( newTable->pos().x(), fmod( newTable->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1333  newTable->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1334  }
1335  else
1336  {
1337  newTable->move( pasteShiftPos.x(), pasteShiftPos.y() );
1338  }
1339  newTable->setSelected( true );
1340  lastPastedItem = newTable;
1341  }
1342  addComposerTable( newTable );
1343  newTable->setZValue( newTable->zValue() + zOrderOffset );
1344  if ( addUndoCommands )
1345  {
1346  pushAddRemoveCommand( newTable, tr( "Table added" ) );
1347  }
1348  }
1349  // html
1350  //TODO - fix this. pasting multiframe frame items has no effect
1351  QDomNodeList composerHtmlList = elem.elementsByTagName( "ComposerHtml" );
1352  for ( int i = 0; i < composerHtmlList.size(); ++i )
1353  {
1354  QDomElement currentHtmlElem = composerHtmlList.at( i ).toElement();
1355  QgsComposerHtml* newHtml = new QgsComposerHtml( this, false );
1356  newHtml->readXML( currentHtmlElem, doc );
1357  newHtml->setCreateUndoCommands( true );
1358  this->addMultiFrame( newHtml );
1359 
1360  //offset z values for frames
1361  //TODO - fix this after fixing html item paste
1362  /*for ( int frameIdx = 0; frameIdx < newHtml->frameCount(); ++frameIdx )
1363  {
1364  QgsComposerFrame * frame = newHtml->frame( frameIdx );
1365  frame->setZValue( frame->zValue() + zOrderOffset );
1366  }*/
1367  }
1368  QDomNodeList composerAttributeTableV2List = elem.elementsByTagName( "ComposerAttributeTableV2" );
1369  for ( int i = 0; i < composerAttributeTableV2List.size(); ++i )
1370  {
1371  QDomElement currentTableElem = composerAttributeTableV2List.at( i ).toElement();
1372  QgsComposerAttributeTableV2* newTable = new QgsComposerAttributeTableV2( this, false );
1373  newTable->readXML( currentTableElem, doc );
1374  newTable->setCreateUndoCommands( true );
1375  this->addMultiFrame( newTable );
1376 
1377  //offset z values for frames
1378  //TODO - fix this after fixing html item paste
1379  /*for ( int frameIdx = 0; frameIdx < newHtml->frameCount(); ++frameIdx )
1380  {
1381  QgsComposerFrame * frame = newHtml->frame( frameIdx );
1382  frame->setZValue( frame->zValue() + zOrderOffset );
1383  }*/
1384  }
1385 
1386  // groups (must be last as it references uuids of above items)
1387  //TODO - pasted groups lose group properties, since the uuids of group items
1388  //changes
1389  QDomNodeList groupList = elem.elementsByTagName( "ComposerItemGroup" );
1390  for ( int i = 0; i < groupList.size(); ++i )
1391  {
1392  QDomElement groupElem = groupList.at( i ).toElement();
1393  QgsComposerItemGroup *newGroup = new QgsComposerItemGroup( this );
1394  newGroup->readXML( groupElem, doc );
1395  addItem( newGroup );
1396  }
1397 
1398  //Since this function adds items grouped by type, and each item is added to end of
1399  //z order list in turn, it will now be inconsistent with the actual order of items in the scene.
1400  //Make sure z order list matches the actual order of items in the scene.
1401  mItemsModel->rebuildZList();
1402 
1403  if ( lastPastedItem )
1404  {
1405  emit selectedItemChanged( lastPastedItem );
1406  }
1407 
1408  delete pasteInPlacePt;
1409  pasteInPlacePt = 0;
1410 
1411 }
1412 
1414 {
1415  if ( !item )
1416  {
1417  return;
1418  }
1419 
1420  //model handles changes to z list
1421  mItemsModel->addItemAtTop( item );
1422 }
1423 
1425 {
1426  if ( !item )
1427  {
1428  return;
1429  }
1430 
1431  //model handles changes to z list
1432  mItemsModel->removeItem( item );
1433 }
1434 
1436 {
1438  QList<QgsComposerItem*>::iterator it = selectedItems.begin();
1439  bool itemsRaised = false;
1440  for ( ; it != selectedItems.end(); ++it )
1441  {
1442  itemsRaised = itemsRaised | raiseItem( *it );
1443  }
1444 
1445  if ( !itemsRaised )
1446  {
1447  //no change
1448  return;
1449  }
1450 
1451  //update all positions
1452  updateZValues();
1453  update();
1454 }
1455 
1457 {
1458  //model handles reordering items
1459  return mItemsModel->reorderItemUp( item );
1460 }
1461 
1463 {
1464  return mItemsModel->getComposerItemAbove( item );
1465 }
1466 
1468 {
1469  return mItemsModel->getComposerItemBelow( item );
1470 }
1471 
1473 {
1474  QgsComposerItem* previousSelectedItem = 0;
1476  if ( selectedItems.size() > 0 )
1477  {
1478  previousSelectedItem = selectedItems.at( 0 );
1479  }
1480 
1481  if ( !previousSelectedItem )
1482  {
1483  return;
1484  }
1485 
1486  //select item with target z value
1487  QgsComposerItem* selectedItem = 0;
1488  switch ( direction )
1489  {
1491  selectedItem = getComposerItemBelow( previousSelectedItem );
1492  break;
1494  selectedItem = getComposerItemAbove( previousSelectedItem );
1495  break;
1496  }
1497 
1498  if ( !selectedItem )
1499  {
1500  return;
1501  }
1502 
1503  //ok, found a good target item
1504  setAllUnselected();
1505  selectedItem->setSelected( true );
1506  emit selectedItemChanged( selectedItem );
1507 }
1508 
1510 {
1512  QList<QgsComposerItem*>::iterator it = selectedItems.begin();
1513  bool itemsLowered = false;
1514  for ( ; it != selectedItems.end(); ++it )
1515  {
1516  itemsLowered = itemsLowered | lowerItem( *it );
1517  }
1518 
1519  if ( !itemsLowered )
1520  {
1521  //no change
1522  return;
1523  }
1524 
1525  //update all positions
1526  updateZValues();
1527  update();
1528 }
1529 
1531 {
1532  //model handles reordering items
1533  return mItemsModel->reorderItemDown( item );
1534 }
1535 
1537 {
1539  QList<QgsComposerItem*>::iterator it = selectedItems.begin();
1540  bool itemsRaised = false;
1541  for ( ; it != selectedItems.end(); ++it )
1542  {
1543  itemsRaised = itemsRaised | moveItemToTop( *it );
1544  }
1545 
1546  if ( !itemsRaised )
1547  {
1548  //no change
1549  return;
1550  }
1551 
1552  //update all positions
1553  updateZValues();
1554  update();
1555 }
1556 
1558 {
1559  //model handles reordering items
1560  return mItemsModel->reorderItemToTop( item );
1561 }
1562 
1564 {
1566  QList<QgsComposerItem*>::iterator it = selectedItems.begin();
1567  bool itemsLowered = false;
1568  for ( ; it != selectedItems.end(); ++it )
1569  {
1570  itemsLowered = itemsLowered | moveItemToBottom( *it );
1571  }
1572 
1573  if ( !itemsLowered )
1574  {
1575  //no change
1576  return;
1577  }
1578 
1579  //update all positions
1580  updateZValues();
1581  update();
1582 }
1583 
1585 {
1586  //model handles reordering items
1587  return mItemsModel->reorderItemToBottom( item );
1588 }
1589 
1591 {
1593  if ( selectedItems.size() < 2 )
1594  {
1595  return;
1596  }
1597 
1598  QRectF selectedItemBBox;
1599  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1600  {
1601  return;
1602  }
1603 
1604  double minXCoordinate = selectedItemBBox.left();
1605 
1606  //align items left to minimum x coordinate
1607  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items left" ) );
1608  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1609  for ( ; align_it != selectedItems.end(); ++align_it )
1610  {
1611  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1612  subcommand->savePreviousState();
1613  ( *align_it )->setPos( minXCoordinate, ( *align_it )->pos().y() );
1614  subcommand->saveAfterState();
1615  }
1616  mUndoStack->push( parentCommand );
1617  QgsProject::instance()->dirty( true );
1618 }
1619 
1621 {
1623  if ( selectedItems.size() < 2 )
1624  {
1625  return;
1626  }
1627 
1628  QRectF selectedItemBBox;
1629  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1630  {
1631  return;
1632  }
1633 
1634  double averageXCoord = ( selectedItemBBox.left() + selectedItemBBox.right() ) / 2.0;
1635 
1636  //place items
1637  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items horizontal center" ) );
1638  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1639  for ( ; align_it != selectedItems.end(); ++align_it )
1640  {
1641  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1642  subcommand->savePreviousState();
1643  ( *align_it )->setPos( averageXCoord - ( *align_it )->rect().width() / 2.0, ( *align_it )->pos().y() );
1644  subcommand->saveAfterState();
1645  }
1646  mUndoStack->push( parentCommand );
1647  QgsProject::instance()->dirty( true );
1648 }
1649 
1651 {
1653  if ( selectedItems.size() < 2 )
1654  {
1655  return;
1656  }
1657 
1658  QRectF selectedItemBBox;
1659  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1660  {
1661  return;
1662  }
1663 
1664  double maxXCoordinate = selectedItemBBox.right();
1665 
1666  //align items right to maximum x coordinate
1667  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items right" ) );
1668  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1669  for ( ; align_it != selectedItems.end(); ++align_it )
1670  {
1671  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1672  subcommand->savePreviousState();
1673  ( *align_it )->setPos( maxXCoordinate - ( *align_it )->rect().width(), ( *align_it )->pos().y() );
1674  subcommand->saveAfterState();
1675  }
1676  mUndoStack->push( parentCommand );
1677  QgsProject::instance()->dirty( true );
1678 }
1679 
1681 {
1683  if ( selectedItems.size() < 2 )
1684  {
1685  return;
1686  }
1687 
1688  QRectF selectedItemBBox;
1689  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1690  {
1691  return;
1692  }
1693 
1694  double minYCoordinate = selectedItemBBox.top();
1695 
1696  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items top" ) );
1697  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1698  for ( ; align_it != selectedItems.end(); ++align_it )
1699  {
1700  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1701  subcommand->savePreviousState();
1702  ( *align_it )->setPos(( *align_it )->pos().x(), minYCoordinate );
1703  subcommand->saveAfterState();
1704  }
1705  mUndoStack->push( parentCommand );
1706  QgsProject::instance()->dirty( true );
1707 }
1708 
1710 {
1712  if ( selectedItems.size() < 2 )
1713  {
1714  return;
1715  }
1716 
1717  QRectF selectedItemBBox;
1718  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1719  {
1720  return;
1721  }
1722 
1723  double averageYCoord = ( selectedItemBBox.top() + selectedItemBBox.bottom() ) / 2.0;
1724  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items vertical center" ) );
1725  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1726  for ( ; align_it != selectedItems.end(); ++align_it )
1727  {
1728  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1729  subcommand->savePreviousState();
1730  ( *align_it )->setPos(( *align_it )->pos().x(), averageYCoord - ( *align_it )->rect().height() / 2 );
1731  subcommand->saveAfterState();
1732  }
1733  mUndoStack->push( parentCommand );
1734  QgsProject::instance()->dirty( true );
1735 }
1736 
1738 {
1740  if ( selectedItems.size() < 2 )
1741  {
1742  return;
1743  }
1744 
1745  QRectF selectedItemBBox;
1746  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1747  {
1748  return;
1749  }
1750 
1751  double maxYCoord = selectedItemBBox.bottom();
1752  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items bottom" ) );
1753  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1754  for ( ; align_it != selectedItems.end(); ++align_it )
1755  {
1756  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1757  subcommand->savePreviousState();
1758  ( *align_it )->setPos(( *align_it )->pos().x(), maxYCoord - ( *align_it )->rect().height() );
1759  subcommand->saveAfterState();
1760  }
1761  mUndoStack->push( parentCommand );
1762  QgsProject::instance()->dirty( true );
1763 }
1764 
1766 {
1767  QUndoCommand* parentCommand = new QUndoCommand( tr( "Items locked" ) );
1769  QList<QgsComposerItem*>::iterator itemIter = selectionList.begin();
1770  for ( ; itemIter != selectionList.end(); ++itemIter )
1771  {
1772  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *itemIter, "", parentCommand );
1773  subcommand->savePreviousState();
1774  ( *itemIter )->setPositionLock( true );
1775  subcommand->saveAfterState();
1776  }
1777 
1778  setAllUnselected();
1779  mUndoStack->push( parentCommand );
1780  QgsProject::instance()->dirty( true );
1781 }
1782 
1784 {
1785  //unlock all items in composer
1786 
1787  QUndoCommand* parentCommand = new QUndoCommand( tr( "Items unlocked" ) );
1788 
1789  //first, clear the selection
1790  setAllUnselected();
1791 
1792  QList<QGraphicsItem *> itemList = items();
1793  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
1794  for ( ; itemIt != itemList.end(); ++itemIt )
1795  {
1796  QgsComposerItem* mypItem = dynamic_cast<QgsComposerItem *>( *itemIt );
1797  if ( mypItem && mypItem->positionLock() )
1798  {
1799  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( mypItem, "", parentCommand );
1800  subcommand->savePreviousState();
1801  mypItem->setPositionLock( false );
1802  //select unlocked items, same behaviour as illustrator
1803  mypItem->setSelected( true );
1804  emit selectedItemChanged( mypItem );
1805  subcommand->saveAfterState();
1806  }
1807  }
1808  mUndoStack->push( parentCommand );
1809  QgsProject::instance()->dirty( true );
1810 }
1811 
1813 {
1814  if ( items.size() < 2 )
1815  {
1816  //not enough items for a group
1817  return 0;
1818  }
1819 
1820  QgsComposerItemGroup* itemGroup = new QgsComposerItemGroup( this );
1821 
1822  QList<QgsComposerItem*>::iterator itemIter = items.begin();
1823  for ( ; itemIter != items.end(); ++itemIter )
1824  {
1825  itemGroup->addItem( *itemIter );
1826  }
1827 
1828  addItem( itemGroup );
1829  return itemGroup;
1830 }
1831 
1833 {
1834  QList<QgsComposerItem *> ungroupedItems;
1835  if ( !group )
1836  {
1837  return ungroupedItems;
1838  }
1839 
1840  QSet<QgsComposerItem*> groupedItems = group->items();
1841  QSet<QgsComposerItem*>::iterator itemIt = groupedItems.begin();
1842  for ( ; itemIt != groupedItems.end(); ++itemIt )
1843  {
1844  ungroupedItems << ( *itemIt );
1845  }
1846 
1847  group->removeItems();
1848  removeComposerItem( group, false, false );
1849 
1850  emit itemRemoved( group );
1851  delete( group );
1852 
1853  return ungroupedItems;
1854 }
1855 
1856 void QgsComposition::updateZValues( const bool addUndoCommands )
1857 {
1858  int counter = mItemsModel->zOrderListSize();
1860  QgsComposerItem* currentItem = 0;
1861 
1862  QUndoCommand* parentCommand = 0;
1863  if ( addUndoCommands )
1864  {
1865  parentCommand = new QUndoCommand( tr( "Item z-order changed" ) );
1866  }
1867  for ( ; it != mItemsModel->zOrderList()->constEnd(); ++it )
1868  {
1869  currentItem = *it;
1870  if ( currentItem )
1871  {
1872  QgsComposerItemCommand* subcommand = 0;
1873  if ( addUndoCommands )
1874  {
1875  subcommand = new QgsComposerItemCommand( *it, "", parentCommand );
1876  subcommand->savePreviousState();
1877  }
1878  currentItem->setZValue( counter );
1879  if ( addUndoCommands )
1880  {
1881  subcommand->saveAfterState();
1882  }
1883  }
1884  --counter;
1885  }
1886  if ( addUndoCommands )
1887  {
1888  mUndoStack->push( parentCommand );
1889  QgsProject::instance()->dirty( true );
1890  }
1891 }
1892 
1894 {
1895  //model handles changes to item z order list
1896  mItemsModel->rebuildZList();
1897 
1898  //Finally, rebuild the zValue of all items to remove any duplicate zValues and make sure there's
1899  //no missing zValues.
1900  updateZValues( false );
1901 }
1902 
1904 {
1905  if ( !mSnapToGrid || mSnapGridResolution <= 0 || !graphicsView() )
1906  {
1907  return scenePoint;
1908  }
1909 
1910  //y offset to current page
1911  int pageNr = ( int )( scenePoint.y() / ( mPageHeight + mSpaceBetweenPages ) );
1912  double yOffset = pageNr * ( mPageHeight + mSpaceBetweenPages );
1913  double yPage = scenePoint.y() - yOffset; //y-coordinate relative to current page
1914 
1915  //snap x coordinate
1916  int xRatio = ( int )(( scenePoint.x() - mSnapGridOffsetX ) / mSnapGridResolution + 0.5 );
1917  int yRatio = ( int )(( yPage - mSnapGridOffsetY ) / mSnapGridResolution + 0.5 );
1918 
1919  double xSnapped = xRatio * mSnapGridResolution + mSnapGridOffsetX;
1920  double ySnapped = yRatio * mSnapGridResolution + mSnapGridOffsetY + yOffset;
1921 
1922  //convert snap tolerance from pixels to mm
1923  double viewScaleFactor = graphicsView()->transform().m11();
1924  double alignThreshold = mSnapTolerance / viewScaleFactor;
1925 
1926  if ( fabs( xSnapped - scenePoint.x() ) > alignThreshold )
1927  {
1928  //snap distance is outside of tolerance
1929  xSnapped = scenePoint.x();
1930  }
1931  if ( fabs( ySnapped - scenePoint.y() ) > alignThreshold )
1932  {
1933  //snap distance is outside of tolerance
1934  ySnapped = scenePoint.y();
1935  }
1936 
1937  return QPointF( xSnapped, ySnapped );
1938 }
1939 
1941 {
1942  QGraphicsLineItem* item = new QGraphicsLineItem();
1943  QPen linePen( Qt::SolidLine );
1944  linePen.setColor( Qt::red );
1945  // use a pen width of 0, since this activates a cosmetic pen
1946  // which doesn't scale with the composer and keeps a constant size
1947  linePen.setWidthF( 0 );
1948  item->setPen( linePen );
1949  item->setZValue( 100 );
1950  item->setVisible( mGuidesVisible );
1951  addItem( item );
1952  mSnapLines.push_back( item );
1953  return item;
1954 }
1955 
1957 {
1958  removeItem( line );
1959  mSnapLines.removeAll( line );
1960  delete line;
1961 }
1962 
1964 {
1965  QList< QGraphicsLineItem* >::iterator it = mSnapLines.begin();
1966  for ( ; it != mSnapLines.end(); ++it )
1967  {
1968  removeItem(( *it ) );
1969  delete( *it );
1970  }
1971  mSnapLines.clear();
1972 }
1973 
1974 void QgsComposition::setSnapLinesVisible( const bool visible )
1975 {
1976  mGuidesVisible = visible;
1977  QList< QGraphicsLineItem* >::iterator it = mSnapLines.begin();
1978  for ( ; it != mSnapLines.end(); ++it )
1979  {
1980  if ( visible )
1981  {
1982  ( *it )->show();
1983  }
1984  else
1985  {
1986  ( *it )->hide();
1987  }
1988  }
1989 }
1990 
1991 QGraphicsLineItem* QgsComposition::nearestSnapLine( const bool horizontal, const double x, const double y, const double tolerance,
1993 {
1994  double minSqrDist = DBL_MAX;
1995  QGraphicsLineItem* item = 0;
1996  double currentXCoord = 0;
1997  double currentYCoord = 0;
1998  double currentSqrDist = 0;
1999  double sqrTolerance = tolerance * tolerance;
2000 
2001  snappedItems.clear();
2002 
2004  for ( ; it != mSnapLines.constEnd(); ++it )
2005  {
2006  bool itemHorizontal = qgsDoubleNear(( *it )->line().y2() - ( *it )->line().y1(), 0 );
2007  if ( horizontal && itemHorizontal )
2008  {
2009  currentYCoord = ( *it )->line().y1();
2010  currentSqrDist = ( y - currentYCoord ) * ( y - currentYCoord );
2011  }
2012  else if ( !horizontal && !itemHorizontal )
2013  {
2014  currentXCoord = ( *it )->line().x1();
2015  currentSqrDist = ( x - currentXCoord ) * ( x - currentXCoord );
2016  }
2017  else
2018  {
2019  continue;
2020  }
2021 
2022  if ( currentSqrDist < minSqrDist && currentSqrDist < sqrTolerance )
2023  {
2024  item = *it;
2025  minSqrDist = currentSqrDist;
2026  }
2027  }
2028 
2029  double itemTolerance = 0.0000001;
2030  if ( item )
2031  {
2032  //go through all the items to find items snapped to this snap line
2033  QList<QGraphicsItem *> itemList = items();
2034  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
2035  for ( ; itemIt != itemList.end(); ++itemIt )
2036  {
2037  QgsComposerItem* currentItem = dynamic_cast<QgsComposerItem*>( *itemIt );
2038  if ( !currentItem || currentItem->type() == QgsComposerItem::ComposerPaper )
2039  {
2040  continue;
2041  }
2042 
2043  if ( horizontal )
2044  {
2045  if ( qgsDoubleNear( currentYCoord, currentItem->pos().y() + currentItem->rect().top(), itemTolerance ) )
2046  {
2047  snappedItems.append( qMakePair( currentItem, QgsComposerItem::UpperMiddle ) );
2048  }
2049  else if ( qgsDoubleNear( currentYCoord, currentItem->pos().y() + currentItem->rect().center().y(), itemTolerance ) )
2050  {
2051  snappedItems.append( qMakePair( currentItem, QgsComposerItem::Middle ) );
2052  }
2053  else if ( qgsDoubleNear( currentYCoord, currentItem->pos().y() + currentItem->rect().bottom(), itemTolerance ) )
2054  {
2055  snappedItems.append( qMakePair( currentItem, QgsComposerItem::LowerMiddle ) );
2056  }
2057  }
2058  else
2059  {
2060  if ( qgsDoubleNear( currentXCoord, currentItem->pos().x(), itemTolerance ) )
2061  {
2062  snappedItems.append( qMakePair( currentItem, QgsComposerItem::MiddleLeft ) );
2063  }
2064  else if ( qgsDoubleNear( currentXCoord, currentItem->pos().x() + currentItem->rect().center().x(), itemTolerance ) )
2065  {
2066  snappedItems.append( qMakePair( currentItem, QgsComposerItem::Middle ) );
2067  }
2068  else if ( qgsDoubleNear( currentXCoord, currentItem->pos().x() + currentItem->rect().width(), itemTolerance ) )
2069  {
2070  snappedItems.append( qMakePair( currentItem, QgsComposerItem::MiddleRight ) );
2071  }
2072  }
2073  }
2074  }
2075 
2076  return item;
2077 }
2078 
2079 int QgsComposition::boundingRectOfSelectedItems( QRectF& bRect )
2080 {
2082  if ( selectedItems.size() < 1 )
2083  {
2084  return 1;
2085  }
2086 
2087  //set the box to the first item
2088  QgsComposerItem* currentItem = selectedItems.at( 0 );
2089  double minX = currentItem->pos().x();
2090  double minY = currentItem->pos().y();
2091  double maxX = minX + currentItem->rect().width();
2092  double maxY = minY + currentItem->rect().height();
2093 
2094  double currentMinX, currentMinY, currentMaxX, currentMaxY;
2095 
2096  for ( int i = 1; i < selectedItems.size(); ++i )
2097  {
2098  currentItem = selectedItems.at( i );
2099  currentMinX = currentItem->pos().x();
2100  currentMinY = currentItem->pos().y();
2101  currentMaxX = currentMinX + currentItem->rect().width();
2102  currentMaxY = currentMinY + currentItem->rect().height();
2103 
2104  if ( currentMinX < minX )
2105  minX = currentMinX;
2106  if ( currentMaxX > maxX )
2107  maxX = currentMaxX;
2108  if ( currentMinY < minY )
2109  minY = currentMinY;
2110  if ( currentMaxY > maxY )
2111  maxY = currentMaxY;
2112  }
2113 
2114  bRect.setTopLeft( QPointF( minX, minY ) );
2115  bRect.setBottomRight( QPointF( maxX, maxY ) );
2116  return 0;
2117 }
2118 
2120 {
2121  mSnapToGrid = b;
2122  updatePaperItems();
2123 }
2124 
2126 {
2127  mGridVisible = b;
2128  updatePaperItems();
2129 }
2130 
2132 {
2133  mSnapGridResolution = r;
2134  updatePaperItems();
2135 }
2136 
2137 void QgsComposition::setSnapGridOffsetX( const double offset )
2138 {
2139  mSnapGridOffsetX = offset;
2140  updatePaperItems();
2141 }
2142 
2143 void QgsComposition::setSnapGridOffsetY( const double offset )
2144 {
2145  mSnapGridOffsetY = offset;
2146  updatePaperItems();
2147 }
2148 
2150 {
2151  mGridPen = p;
2152  //make sure grid is drawn using a zero-width cosmetic pen
2153  mGridPen.setWidthF( 0 );
2154  updatePaperItems();
2155 }
2156 
2158 {
2159  mGridStyle = s;
2160  updatePaperItems();
2161 }
2162 
2163 void QgsComposition::setBoundingBoxesVisible( const bool boundsVisible )
2164 {
2165  mBoundingBoxesVisible = boundsVisible;
2166 
2167  if ( mSelectionHandles )
2168  {
2169  mSelectionHandles->update();
2170  }
2171 }
2172 
2174 {
2175  //load new composer setting values
2176  loadSettings();
2177  //update any paper items to reflect new settings
2178  updatePaperItems();
2179 }
2180 
2181 void QgsComposition::loadSettings()
2182 {
2183  //read grid style, grid color and pen width from settings
2184  QSettings s;
2185 
2186  QString gridStyleString;
2187  gridStyleString = s.value( "/Composer/gridStyle", "Dots" ).toString();
2188 
2189  int gridRed, gridGreen, gridBlue, gridAlpha;
2190  gridRed = s.value( "/Composer/gridRed", 190 ).toInt();
2191  gridGreen = s.value( "/Composer/gridGreen", 190 ).toInt();
2192  gridBlue = s.value( "/Composer/gridBlue", 190 ).toInt();
2193  gridAlpha = s.value( "/Composer/gridAlpha", 100 ).toInt();
2194  QColor gridColor = QColor( gridRed, gridGreen, gridBlue, gridAlpha );
2195 
2196  mGridPen.setColor( gridColor );
2197  mGridPen.setWidthF( 0 );
2198 
2199  if ( gridStyleString == "Dots" )
2200  {
2201  mGridStyle = Dots;
2202  }
2203  else if ( gridStyleString == "Crosses" )
2204  {
2205  mGridStyle = Crosses;
2206  }
2207  else
2208  {
2209  mGridStyle = Solid;
2210  }
2211 }
2212 
2214 {
2215  delete mActiveItemCommand;
2216  if ( !item )
2217  {
2218  mActiveItemCommand = 0;
2219  return;
2220  }
2221 
2223  {
2224  mActiveItemCommand = new QgsComposerItemCommand( item, commandText );
2225  }
2226  else
2227  {
2228  mActiveItemCommand = new QgsComposerMergeCommand( c, item, commandText );
2229  }
2230  mActiveItemCommand->savePreviousState();
2231 }
2232 
2234 {
2235  if ( mActiveItemCommand )
2236  {
2237  mActiveItemCommand->saveAfterState();
2238  if ( mActiveItemCommand->containsChange() ) //protect against empty commands
2239  {
2240  mUndoStack->push( mActiveItemCommand );
2241  QgsProject::instance()->dirty( true );
2242  }
2243  else
2244  {
2245  delete mActiveItemCommand;
2246  }
2247  mActiveItemCommand = 0;
2248  }
2249 }
2250 
2252 {
2253  delete mActiveItemCommand;
2254  mActiveItemCommand = 0;
2255 }
2256 
2258 {
2259  delete mActiveMultiFrameCommand;
2260 
2261  if ( !multiFrame )
2262  {
2263  mActiveMultiFrameCommand = 0;
2264  return;
2265  }
2266 
2268  {
2269  mActiveMultiFrameCommand = new QgsComposerMultiFrameCommand( multiFrame, text );
2270  }
2271  else
2272  {
2273  mActiveMultiFrameCommand = new QgsComposerMultiFrameMergeCommand( c, multiFrame, text );
2274  }
2275  mActiveMultiFrameCommand->savePreviousState();
2276 }
2277 
2279 {
2280  if ( mActiveMultiFrameCommand )
2281  {
2282  mActiveMultiFrameCommand->saveAfterState();
2283  if ( mActiveMultiFrameCommand->containsChange() )
2284  {
2285  mUndoStack->push( mActiveMultiFrameCommand );
2286  QgsProject::instance()->dirty( true );
2287  }
2288  else
2289  {
2290  delete mActiveMultiFrameCommand;
2291  }
2292  mActiveMultiFrameCommand = 0;
2293  }
2294 }
2295 
2297 {
2298  delete mActiveMultiFrameCommand;
2299  mActiveMultiFrameCommand = 0;
2300 }
2301 
2303 {
2304  mMultiFrames.insert( multiFrame );
2305 
2306  updateBounds();
2307 }
2308 
2310 {
2311  mMultiFrames.remove( multiFrame );
2312 
2313  updateBounds();
2314 }
2315 
2317 {
2318  addItem( arrow );
2319 
2320  updateBounds();
2321  connect( arrow, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2322 
2323  emit composerArrowAdded( arrow );
2324 }
2325 
2327 {
2328  addItem( label );
2329 
2330  updateBounds();
2331  connect( label, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2332 
2333  emit composerLabelAdded( label );
2334 }
2335 
2336 void QgsComposition::addComposerMap( QgsComposerMap* map, const bool setDefaultPreviewStyle )
2337 {
2338  addItem( map );
2339  if ( setDefaultPreviewStyle )
2340  {
2341  //set default preview mode to cache. Must be done here between adding composer map to scene and emiting signal
2343  }
2344 
2345  if ( map->previewMode() != QgsComposerMap::Rectangle )
2346  {
2347  map->cache();
2348  }
2349 
2350  updateBounds();
2351  connect( map, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2352 
2353  emit composerMapAdded( map );
2354 }
2355 
2357 {
2358  addItem( scaleBar );
2359 
2360  updateBounds();
2361  connect( scaleBar, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2362 
2363  emit composerScaleBarAdded( scaleBar );
2364 }
2365 
2367 {
2368  addItem( legend );
2369 
2370  updateBounds();
2371  connect( legend, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2372 
2373  emit composerLegendAdded( legend );
2374 }
2375 
2377 {
2378  addItem( picture );
2379 
2380  updateBounds();
2381  connect( picture, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2382 
2383  emit composerPictureAdded( picture );
2384 }
2385 
2387 {
2388  addItem( shape );
2389 
2390  updateBounds();
2391  connect( shape, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2392 
2393  emit composerShapeAdded( shape );
2394 }
2395 
2397 {
2398  addItem( table );
2399 
2400  updateBounds();
2401  connect( table, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2402 
2403  emit composerTableAdded( table );
2404 }
2405 
2407 {
2408  addItem( frame );
2409 
2410  updateBounds();
2411  connect( frame, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2412 
2413  emit composerHtmlFrameAdded( html, frame );
2414 }
2415 
2417 {
2418  addItem( frame );
2419 
2420  updateBounds();
2421  connect( frame, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2422 
2423  emit composerTableFrameAdded( table, frame );
2424 }
2425 
2426 void QgsComposition::removeComposerItem( QgsComposerItem* item, const bool createCommand, const bool removeGroupItems )
2427 {
2428  QgsComposerMap* map = dynamic_cast<QgsComposerMap *>( item );
2429 
2430  if ( !map || !map->isDrawing() ) //don't delete a composer map while it draws
2431  {
2432  mItemsModel->setItemRemoved( item );
2433  removeItem( item );
2434 
2435  QgsComposerItemGroup* itemGroup = dynamic_cast<QgsComposerItemGroup*>( item );
2436  if ( itemGroup && removeGroupItems )
2437  {
2438  //add add/remove item command for every item in the group
2439  QUndoCommand* parentCommand = new QUndoCommand( tr( "Remove item group" ) );
2440 
2441  QSet<QgsComposerItem*> groupedItems = itemGroup->items();
2442  QSet<QgsComposerItem*>::iterator it = groupedItems.begin();
2443  for ( ; it != groupedItems.end(); ++it )
2444  {
2445  QgsAddRemoveItemCommand* subcommand = new QgsAddRemoveItemCommand( QgsAddRemoveItemCommand::Removed, *it, this, "", parentCommand );
2446  connectAddRemoveCommandSignals( subcommand );
2447  emit itemRemoved( *it );
2448  }
2449 
2450  undoStack()->push( parentCommand );
2451  emit itemRemoved( itemGroup );
2452  delete itemGroup;
2453  }
2454  else
2455  {
2456  bool frameItem = ( item->type() == QgsComposerItem::ComposerFrame );
2457  QgsComposerMultiFrame* multiFrame = 0;
2458  if ( createCommand )
2459  {
2460  if ( frameItem ) //multiframe tracks item changes
2461  {
2462  multiFrame = static_cast<QgsComposerFrame*>( item )->multiFrame();
2463  item->beginItemCommand( tr( "Frame deleted" ) );
2464  emit itemRemoved( item );
2465  item->endItemCommand();
2466  }
2467  else
2468  {
2469  emit itemRemoved( item );
2470  pushAddRemoveCommand( item, tr( "Item deleted" ), QgsAddRemoveItemCommand::Removed );
2471  }
2472  }
2473  else
2474  {
2475  emit itemRemoved( item );
2476  }
2477 
2478  //check if there are frames left. If not, remove the multi frame
2479  if ( frameItem && multiFrame )
2480  {
2481  if ( multiFrame->frameCount() < 1 )
2482  {
2483  removeMultiFrame( multiFrame );
2484  if ( createCommand )
2485  {
2487  multiFrame, this, tr( "Multiframe removed" ) );
2488  undoStack()->push( command );
2489  }
2490  else
2491  {
2492  delete multiFrame;
2493  }
2494  }
2495  }
2496  }
2497  }
2498 
2499  updateBounds();
2500 }
2501 
2503 {
2504  QgsAddRemoveItemCommand* c = new QgsAddRemoveItemCommand( state, item, this, text );
2505  connectAddRemoveCommandSignals( c );
2506  undoStack()->push( c );
2507  QgsProject::instance()->dirty( true );
2508 }
2509 
2510 void QgsComposition::connectAddRemoveCommandSignals( QgsAddRemoveItemCommand* c )
2511 {
2512  if ( !c )
2513  {
2514  return;
2515  }
2516 
2517  QObject::connect( c, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SIGNAL( itemRemoved( QgsComposerItem* ) ) );
2518  QObject::connect( c, SIGNAL( itemAdded( QgsComposerItem* ) ), this, SLOT( sendItemAddedSignal( QgsComposerItem* ) ) );
2519 }
2520 
2522 {
2523  //cast and send proper signal
2524  item->setSelected( true );
2525  QgsComposerArrow* arrow = dynamic_cast<QgsComposerArrow*>( item );
2526  if ( arrow )
2527  {
2528  emit composerArrowAdded( arrow );
2529  emit selectedItemChanged( arrow );
2530  return;
2531  }
2532  QgsComposerLabel* label = dynamic_cast<QgsComposerLabel*>( item );
2533  if ( label )
2534  {
2535  emit composerLabelAdded( label );
2536  emit selectedItemChanged( label );
2537  return;
2538  }
2539  QgsComposerMap* map = dynamic_cast<QgsComposerMap*>( item );
2540  if ( map )
2541  {
2542  emit composerMapAdded( map );
2543  emit selectedItemChanged( map );
2544  return;
2545  }
2546  QgsComposerScaleBar* scalebar = dynamic_cast<QgsComposerScaleBar*>( item );
2547  if ( scalebar )
2548  {
2549  emit composerScaleBarAdded( scalebar );
2550  emit selectedItemChanged( scalebar );
2551  return;
2552  }
2553  QgsComposerLegend* legend = dynamic_cast<QgsComposerLegend*>( item );
2554  if ( legend )
2555  {
2556  emit composerLegendAdded( legend );
2557  emit selectedItemChanged( legend );
2558  return;
2559  }
2560  QgsComposerPicture* picture = dynamic_cast<QgsComposerPicture*>( item );
2561  if ( picture )
2562  {
2563  emit composerPictureAdded( picture );
2564  emit selectedItemChanged( picture );
2565  return;
2566  }
2567  QgsComposerShape* shape = dynamic_cast<QgsComposerShape*>( item );
2568  if ( shape )
2569  {
2570  emit composerShapeAdded( shape );
2571  emit selectedItemChanged( shape );
2572  return;
2573  }
2574  QgsComposerAttributeTable* table = dynamic_cast<QgsComposerAttributeTable*>( item );
2575  if ( table )
2576  {
2577  emit composerTableAdded( table );
2578  emit selectedItemChanged( table );
2579  return;
2580  }
2581  QgsComposerFrame* frame = dynamic_cast<QgsComposerFrame*>( item );
2582  if ( frame )
2583  {
2584  //emit composerFrameAdded( multiframe, frame, );
2585  QgsComposerMultiFrame* mf = frame->multiFrame();
2586  QgsComposerHtml* html = dynamic_cast<QgsComposerHtml*>( mf );
2587  if ( html )
2588  {
2589  emit composerHtmlFrameAdded( html, frame );
2590  }
2591  QgsComposerAttributeTableV2* table = dynamic_cast<QgsComposerAttributeTableV2*>( mf );
2592  if ( table )
2593  {
2594  emit composerTableFrameAdded( table, frame );
2595  }
2596  emit selectedItemChanged( frame );
2597  return;
2598  }
2599 }
2600 
2601 void QgsComposition::updatePaperItems()
2602 {
2603  QList< QgsPaperItem* >::iterator paperIt = mPages.begin();
2604  for ( ; paperIt != mPages.end(); ++paperIt )
2605  {
2606  ( *paperIt )->update();
2607  }
2608 }
2609 
2610 void QgsComposition::addPaperItem()
2611 {
2612  double paperHeight = this->paperHeight();
2613  double paperWidth = this->paperWidth();
2614  double currentY = paperHeight * mPages.size() + mPages.size() * mSpaceBetweenPages; //add 10mm visible space between pages
2615  QgsPaperItem* paperItem = new QgsPaperItem( 0, currentY, paperWidth, paperHeight, this ); //default size A4
2616  paperItem->setBrush( Qt::white );
2617  addItem( paperItem );
2618  paperItem->setZValue( 0 );
2619  mPages.push_back( paperItem );
2620 
2621  QgsExpression::setSpecialColumn( "$numpages", QVariant(( int )mPages.size() ) );
2622 }
2623 
2624 void QgsComposition::removePaperItems()
2625 {
2626  qDeleteAll( mPages );
2627  mPages.clear();
2628  QgsExpression::setSpecialColumn( "$numpages", QVariant(( int )0 ) );
2629 }
2630 
2631 void QgsComposition::deleteAndRemoveMultiFrames()
2632 {
2633  QSet<QgsComposerMultiFrame*>::iterator multiFrameIt = mMultiFrames.begin();
2634  for ( ; multiFrameIt != mMultiFrames.end(); ++multiFrameIt )
2635  {
2636  delete *multiFrameIt;
2637  }
2638  mMultiFrames.clear();
2639 }
2640 
2641 void QgsComposition::beginPrintAsPDF( QPrinter& printer, const QString& file )
2642 {
2643  printer.setOutputFileName( file );
2644  // setOutputFormat should come after setOutputFileName, which auto-sets format to QPrinter::PdfFormat.
2645  // [LS] This should be QPrinter::NativeFormat for Mac, otherwise fonts are not embed-able
2646  // and text is not searchable; however, there are several bugs with <= Qt 4.8.5, 5.1.1, 5.2.0:
2647  // https://bugreports.qt-project.org/browse/QTBUG-10094 - PDF font embedding fails
2648  // https://bugreports.qt-project.org/browse/QTBUG-33583 - PDF output converts text to outline
2649  // Also an issue with PDF paper size using QPrinter::NativeFormat on Mac (always outputs portrait letter-size)
2650  printer.setOutputFormat( QPrinter::PdfFormat );
2651 
2652  refreshPageSize();
2653  //must set orientation to portrait before setting paper size, otherwise size will be flipped
2654  //for landscape sized outputs (#11352)
2655  printer.setOrientation( QPrinter::Portrait );
2656  printer.setPaperSize( QSizeF( paperWidth(), paperHeight() ), QPrinter::Millimeter );
2657 
2658  // TODO: add option for this in Composer
2659  // May not work on Windows or non-X11 Linux. Works fine on Mac using QPrinter::NativeFormat
2660  //printer.setFontEmbeddingEnabled( true );
2661 
2663 }
2664 
2666 {
2667  QPrinter printer;
2668  beginPrintAsPDF( printer, file );
2669  return print( printer );
2670 }
2671 
2672 void QgsComposition::doPrint( QPrinter& printer, QPainter& p, bool startNewPage )
2673 {
2674  if ( ddPageSizeActive() )
2675  {
2676  //set the page size again so that data defined page size takes effect
2677  refreshPageSize();
2678  //must set orientation to portrait before setting paper size, otherwise size will be flipped
2679  //for landscape sized outputs (#11352)
2680  printer.setOrientation( QPrinter::Portrait );
2681  printer.setPaperSize( QSizeF( paperWidth(), paperHeight() ), QPrinter::Millimeter );
2682  }
2683 
2684  //QgsComposition starts page numbering at 0
2685  int fromPage = ( printer.fromPage() < 1 ) ? 0 : printer.fromPage() - 1;
2686  int toPage = ( printer.toPage() < 1 ) ? numPages() - 1 : printer.toPage() - 1;
2687 
2688  bool pageExported = false;
2689  if ( mPrintAsRaster )
2690  {
2691  for ( int i = fromPage; i <= toPage; ++i )
2692  {
2693  if ( !shouldExportPage( i + 1 ) )
2694  {
2695  continue;
2696  }
2697  if (( pageExported && i > fromPage ) || startNewPage )
2698  {
2699  printer.newPage();
2700  }
2701 
2702  QImage image = printPageAsRaster( i );
2703  if ( !image.isNull() )
2704  {
2705  QRectF targetArea( 0, 0, image.width(), image.height() );
2706  p.drawImage( targetArea, image, targetArea );
2707  }
2708  pageExported = true;
2709  }
2710  }
2711 
2712  if ( !mPrintAsRaster )
2713  {
2714  for ( int i = fromPage; i <= toPage; ++i )
2715  {
2716  if ( !shouldExportPage( i + 1 ) )
2717  {
2718  continue;
2719  }
2720  if (( pageExported && i > fromPage ) || startNewPage )
2721  {
2722  printer.newPage();
2723  }
2724  renderPage( &p, i );
2725  pageExported = true;
2726  }
2727  }
2728 }
2729 
2730 void QgsComposition::beginPrint( QPrinter &printer, const bool evaluateDDPageSize )
2731 {
2732  //set resolution based on composer setting
2733  printer.setFullPage( true );
2734  printer.setColorMode( QPrinter::Color );
2735 
2736  //set user-defined resolution
2737  printer.setResolution( printResolution() );
2738 
2739  if ( evaluateDDPageSize && ddPageSizeActive() )
2740  {
2741  //set data defined page size
2742  refreshPageSize();
2743  //must set orientation to portrait before setting paper size, otherwise size will be flipped
2744  //for landscape sized outputs (#11352)
2745  printer.setOrientation( QPrinter::Portrait );
2746  printer.setPaperSize( QSizeF( paperWidth(), paperHeight() ), QPrinter::Millimeter );
2747  }
2748 }
2749 
2750 bool QgsComposition::print( QPrinter &printer, const bool evaluateDDPageSize )
2751 {
2752  beginPrint( printer, evaluateDDPageSize );
2753  QPainter p;
2754  bool ready = p.begin( &printer );
2755  if ( !ready )
2756  {
2757  //error beginning print
2758  return false;
2759  }
2760  doPrint( printer, p );
2761  p.end();
2762  return true;
2763 }
2764 
2766 {
2767  //print out via QImage, code copied from on_mActionExportAsImage_activated
2768  int width = ( int )( printResolution() * paperWidth() / 25.4 );
2769  int height = ( int )( printResolution() * paperHeight() / 25.4 );
2770  QImage image( QSize( width, height ), QImage::Format_ARGB32 );
2771  if ( !image.isNull() )
2772  {
2773  image.setDotsPerMeterX( printResolution() / 25.4 * 1000 );
2774  image.setDotsPerMeterY( printResolution() / 25.4 * 1000 );
2775  image.fill( 0 );
2776  QPainter imagePainter( &image );
2777  renderPage( &imagePainter, page );
2778  if ( !imagePainter.isActive() ) return QImage();
2779  }
2780  return image;
2781 }
2782 
2784 {
2785  if ( mPages.size() <= page )
2786  {
2787  return;
2788  }
2789 
2790  QgsPaperItem* paperItem = mPages[page];
2791  if ( !paperItem )
2792  {
2793  return;
2794  }
2795 
2796  QPaintDevice* paintDevice = p->device();
2797  if ( !paintDevice )
2798  {
2799  return;
2800  }
2801 
2802  QRectF paperRect = QRectF( paperItem->pos().x(), paperItem->pos().y(), paperItem->rect().width(), paperItem->rect().height() );
2803 
2804  QgsComposition::PlotStyle savedPlotStyle = mPlotStyle;
2805  mPlotStyle = QgsComposition::Print;
2806 
2807  setSnapLinesVisible( false );
2808  //hide background before rendering
2809  setBackgroundBrush( Qt::NoBrush );
2810  render( p, QRectF( 0, 0, paintDevice->width(), paintDevice->height() ), paperRect );
2811  //show background after rendering
2812  setBackgroundBrush( QColor( 215, 215, 215 ) );
2813  setSnapLinesVisible( true );
2814 
2815  mPlotStyle = savedPlotStyle;
2816 }
2817 
2818 QString QgsComposition::encodeStringForXML( const QString& str )
2819 {
2820  QString modifiedStr( str );
2821  modifiedStr.replace( "&", "&amp;" );
2822  modifiedStr.replace( "\"", "&quot;" );
2823  modifiedStr.replace( "'", "&apos;" );
2824  modifiedStr.replace( "<", "&lt;" );
2825  modifiedStr.replace( ">", "&gt;" );
2826  return modifiedStr;
2827 }
2828 
2829 QGraphicsView *QgsComposition::graphicsView() const
2830 {
2831  //try to find current view attached to composition
2832  QList<QGraphicsView*> viewList = views();
2833  if ( viewList.size() > 0 )
2834  {
2835  return viewList.at( 0 );
2836  }
2837 
2838  //no view attached to composition
2839  return 0;
2840 }
2841 
2842 void QgsComposition::computeWorldFileParameters( double& a, double& b, double& c, double& d, double& e, double& f ) const
2843 {
2844  //
2845  // Word file parameters : affine transformation parameters from pixel coordinates to map coordinates
2846 
2847  if ( !mWorldFileMap )
2848  {
2849  return;
2850  }
2851 
2852  QRectF brect = mWorldFileMap->mapRectToScene( mWorldFileMap->rect() );
2853  QgsRectangle extent = *mWorldFileMap->currentMapExtent();
2854 
2855  double alpha = mWorldFileMap->mapRotation() / 180 * M_PI;
2856 
2857  double xr = extent.width() / brect.width();
2858  double yr = extent.height() / brect.height();
2859 
2860  double XC = extent.center().x();
2861  double YC = extent.center().y();
2862 
2863  // get the extent for the page
2864  double xmin = extent.xMinimum() - mWorldFileMap->pos().x() * xr;
2865  double ymax = extent.yMaximum() + mWorldFileMap->pos().y() * yr;
2866  QgsRectangle paperExtent( xmin, ymax - paperHeight() * yr, xmin + paperWidth() * xr, ymax );
2867 
2868  double X0 = paperExtent.xMinimum();
2869  double Y0 = paperExtent.yMinimum();
2870 
2871  int widthPx = ( int )( printResolution() * paperWidth() / 25.4 );
2872  int heightPx = ( int )( printResolution() * paperHeight() / 25.4 );
2873 
2874  double Ww = paperExtent.width() / widthPx;
2875  double Hh = paperExtent.height() / heightPx;
2876 
2877  // scaling matrix
2878  double s[6];
2879  s[0] = Ww;
2880  s[1] = 0;
2881  s[2] = X0;
2882  s[3] = 0;
2883  s[4] = -Hh;
2884  s[5] = Y0 + paperExtent.height();
2885 
2886  // rotation matrix
2887  double r[6];
2888  r[0] = cos( alpha );
2889  r[1] = -sin( alpha );
2890  r[2] = XC * ( 1 - cos( alpha ) ) + YC * sin( alpha );
2891  r[3] = sin( alpha );
2892  r[4] = cos( alpha );
2893  r[5] = - XC * sin( alpha ) + YC * ( 1 - cos( alpha ) );
2894 
2895  // result = rotation x scaling = rotation(scaling(X))
2896  a = r[0] * s[0] + r[1] * s[3];
2897  b = r[0] * s[1] + r[1] * s[4];
2898  c = r[0] * s[2] + r[1] * s[5] + r[2];
2899  d = r[3] * s[0] + r[4] * s[3];
2900  e = r[3] * s[1] + r[4] * s[4];
2901  f = r[3] * s[2] + r[4] * s[5] + r[5];
2902 }
2903 
2905 {
2906  mAtlasMode = mode;
2907 
2908  if ( mode == QgsComposition::AtlasOff )
2909  {
2910  mAtlasComposition.endRender();
2911  }
2912  else
2913  {
2914  bool atlasHasFeatures = mAtlasComposition.beginRender();
2915  if ( ! atlasHasFeatures )
2916  {
2917  mAtlasMode = QgsComposition::AtlasOff;
2918  mAtlasComposition.endRender();
2919  return false;
2920  }
2921  }
2922 
2923  update();
2924  return true;
2925 }
2926 
2927 bool QgsComposition::ddPageSizeActive() const
2928 {
2929  //check if any data defined page settings are active
2930  return dataDefinedActive( QgsComposerObject::PresetPaperSize, &mDataDefinedProperties ) ||
2931  dataDefinedActive( QgsComposerObject::PaperWidth, &mDataDefinedProperties ) ||
2932  dataDefinedActive( QgsComposerObject::PaperHeight, &mDataDefinedProperties ) ||
2933  dataDefinedActive( QgsComposerObject::PaperOrientation, &mDataDefinedProperties );
2934 }
2935 
2936 void QgsComposition::refreshPageSize( const QgsExpressionContext* context )
2937 {
2938  const QgsExpressionContext* evalContext = context;
2940  if ( !evalContext )
2941  {
2942  scopedContext.reset( createExpressionContext() );
2943  evalContext = scopedContext.data();
2944  }
2945 
2946  double pageWidth = mPageWidth;
2947  double pageHeight = mPageHeight;
2948 
2949  QVariant exprVal;
2950  //in order of precedence - first consider predefined page size
2951  if ( dataDefinedEvaluate( QgsComposerObject::PresetPaperSize, exprVal, *evalContext, &mDataDefinedProperties ) )
2952  {
2953  QString presetString = exprVal.toString().trimmed();
2954  QgsDebugMsg( QString( "exprVal Paper Preset size :%1" ).arg( presetString ) );
2955  double widthD = 0;
2956  double heightD = 0;
2957  if ( QgsComposerUtils::decodePresetPaperSize( presetString, widthD, heightD ) )
2958  {
2959  pageWidth = widthD;
2960  pageHeight = heightD;
2961  }
2962  }
2963 
2964  //which is overwritten by data defined width/height
2965  if ( dataDefinedEvaluate( QgsComposerObject::PaperWidth, exprVal, *evalContext, &mDataDefinedProperties ) )
2966  {
2967  bool ok;
2968  double widthD = exprVal.toDouble( &ok );
2969  QgsDebugMsg( QString( "exprVal Paper Width:%1" ).arg( widthD ) );
2970  if ( ok )
2971  {
2972  pageWidth = widthD;
2973  }
2974  }
2975  if ( dataDefinedEvaluate( QgsComposerObject::PaperHeight, exprVal, *evalContext, &mDataDefinedProperties ) )
2976  {
2977  bool ok;
2978  double heightD = exprVal.toDouble( &ok );
2979  QgsDebugMsg( QString( "exprVal Paper Height:%1" ).arg( heightD ) );
2980  if ( ok )
2981  {
2982  pageHeight = heightD;
2983  }
2984  }
2985 
2986  //which is finally overwritten by data defined orientation
2987  if ( dataDefinedEvaluate( QgsComposerObject::PaperOrientation, exprVal, *evalContext, &mDataDefinedProperties ) )
2988  {
2989  bool ok;
2990  QString orientationString = exprVal.toString().trimmed();
2991  QgsComposition::PaperOrientation orientation = QgsComposerUtils::decodePaperOrientation( orientationString, ok );
2992  QgsDebugMsg( QString( "exprVal Paper Orientation:%1" ).arg( orientationString ) );
2993  if ( ok )
2994  {
2995  double heightD, widthD;
2996  if ( orientation == QgsComposition::Portrait )
2997  {
2998  heightD = qMax( pageHeight, pageWidth );
2999  widthD = qMin( pageHeight, pageWidth );
3000  }
3001  else
3002  {
3003  heightD = qMin( pageHeight, pageWidth );
3004  widthD = qMax( pageHeight, pageWidth );
3005  }
3006  pageWidth = widthD;
3007  pageHeight = heightD;
3008  }
3009  }
3010 
3011  setPaperSize( pageWidth, pageHeight );
3012 }
3013 
3015 {
3016  if ( property == QgsComposerObject::AllProperties || property == QgsComposerObject::NoProperty )
3017  {
3018  //invalid property
3019  return 0;
3020  }
3021 
3022  //find matching QgsDataDefined for property
3024  if ( it != mDataDefinedProperties.constEnd() )
3025  {
3026  return it.value();
3027  }
3028 
3029  //not found
3030  return 0;
3031 }
3032 
3033 void QgsComposition::setDataDefinedProperty( const QgsComposerObject::DataDefinedProperty property, bool active, bool useExpression, const QString &expression, const QString &field )
3034 {
3035  if ( property == QgsComposerObject::AllProperties || property == QgsComposerObject::NoProperty )
3036  {
3037  //invalid property
3038  return;
3039  }
3040 
3041  bool defaultVals = ( !active && !useExpression && expression.isEmpty() && field.isEmpty() );
3042 
3043  if ( mDataDefinedProperties.contains( property ) )
3044  {
3046  if ( it != mDataDefinedProperties.constEnd() )
3047  {
3048  QgsDataDefined* dd = it.value();
3049  dd->setActive( active );
3050  dd->setExpressionString( expression );
3051  dd->setField( field );
3052  dd->setUseExpression( useExpression );
3053  }
3054  }
3055  else if ( !defaultVals )
3056  {
3057  QgsDataDefined* dd = new QgsDataDefined( active, useExpression, expression, field );
3058  mDataDefinedProperties.insert( property, dd );
3059  }
3060 }
3061 
3062 void QgsComposition::setCustomProperty( const QString& key, const QVariant& value )
3063 {
3064  mCustomProperties.setValue( key, value );
3065 }
3066 
3067 QVariant QgsComposition::customProperty( const QString& key, const QVariant& defaultValue ) const
3068 {
3069  return mCustomProperties.value( key, defaultValue );
3070 }
3071 
3073 {
3074  mCustomProperties.remove( key );
3075 }
3076 
3078 {
3079  return mCustomProperties.keys();
3080 }
3081 
3082 bool QgsComposition::dataDefinedEvaluate( QgsComposerObject::DataDefinedProperty property, QVariant &expressionValue,
3083  const QgsExpressionContext& context,
3085 {
3086  if ( property == QgsComposerObject::NoProperty || property == QgsComposerObject::AllProperties )
3087  {
3088  //invalid property
3089  return false;
3090  }
3091 
3092  //null passed-around QVariant
3093  expressionValue.clear();
3094 
3095  //get fields and feature from atlas
3096  QgsFeature currentFeature;
3097  QgsFields layerFields;
3098  bool useFeature = false;
3099  if ( mAtlasComposition.enabled() )
3100  {
3101  QgsVectorLayer* atlasLayer = mAtlasComposition.coverageLayer();
3102  if ( atlasLayer )
3103  {
3104  layerFields = atlasLayer->fields();
3105  }
3106  if ( mAtlasMode != QgsComposition::AtlasOff )
3107  {
3108  useFeature = true;
3109  currentFeature = mAtlasComposition.feature();
3110  }
3111  }
3112 
3113  //evaluate data defined property using current atlas context
3114  QVariant result = dataDefinedValue( property, useFeature ? &currentFeature : 0, layerFields, context, dataDefinedProperties );
3115 
3116  if ( result.isValid() )
3117  {
3118  expressionValue = result;
3119  return true;
3120  }
3121 
3122  return false;
3123 }
3124 
3125 bool QgsComposition::dataDefinedActive( const QgsComposerObject::DataDefinedProperty property, const QMap<QgsComposerObject::DataDefinedProperty, QgsDataDefined *> *dataDefinedProperties ) const
3126 {
3127  if ( property == QgsComposerObject::AllProperties || property == QgsComposerObject::NoProperty )
3128  {
3129  //invalid property
3130  return false;
3131  }
3132  if ( !dataDefinedProperties->contains( property ) )
3133  {
3134  //missing property
3135  return false;
3136  }
3137 
3138  QgsDataDefined* dd = 0;
3140  if ( it != dataDefinedProperties->constEnd() )
3141  {
3142  dd = it.value();
3143  }
3144 
3145  if ( !dd )
3146  {
3147  return false;
3148  }
3149 
3150  //found the data defined property, return whether it is active
3151  return dd->isActive();
3152 }
3153 
3154 QVariant QgsComposition::dataDefinedValue( QgsComposerObject::DataDefinedProperty property, const QgsFeature *feature, const QgsFields& fields, const QgsExpressionContext& context, QMap<QgsComposerObject::DataDefinedProperty, QgsDataDefined *> *dataDefinedProperties ) const
3155 {
3156  if ( property == QgsComposerObject::AllProperties || property == QgsComposerObject::NoProperty )
3157  {
3158  //invalid property
3159  return QVariant();
3160  }
3161  if ( !dataDefinedProperties->contains( property ) )
3162  {
3163  //missing property
3164  return QVariant();
3165  }
3166 
3167  QgsDataDefined* dd = 0;
3169  if ( it != dataDefinedProperties->constEnd() )
3170  {
3171  dd = it.value();
3172  }
3173 
3174  if ( !dd )
3175  {
3176  return QVariant();
3177  }
3178 
3179  if ( !dd->isActive() )
3180  {
3181  return QVariant();
3182  }
3183 
3184  QVariant result = QVariant();
3185  bool useExpression = dd->useExpression();
3186  QString field = dd->field();
3187 
3188  if ( !dd->expressionIsPrepared() )
3189  {
3190  prepareDataDefinedExpression( dd, dataDefinedProperties, context );
3191  }
3192 
3193  if ( useExpression && dd->expressionIsPrepared() )
3194  {
3195  QgsExpression* expr = dd->expression();
3196 
3197  result = expr->evaluate( &context );
3198  if ( expr->hasEvalError() )
3199  {
3200  QgsDebugMsgLevel( QString( "Evaluate error:" ) + expr->evalErrorString(), 4 );
3201  return QVariant();
3202  }
3203  }
3204  else if ( !useExpression && !field.isEmpty() )
3205  {
3206  if ( !feature )
3207  {
3208  return QVariant();
3209  }
3210  // use direct attribute access instead of evaluating "field" expression (much faster)
3211  int indx = fields.indexFromName( field );
3212  if ( indx != -1 )
3213  {
3214  result = feature->attribute( indx );
3215  }
3216  }
3217  return result;
3218 }
3219 
3220 void QgsComposition::prepareDataDefinedExpression( QgsDataDefined *dd, QMap<QgsComposerObject::DataDefinedProperty, QgsDataDefined *> *dataDefinedProperties,
3221  const QgsExpressionContext& context ) const
3222 {
3223  //if specific QgsDataDefined passed, prepare it
3224  //otherwise prepare all QgsDataDefineds
3225  if ( dd )
3226  {
3227  dd->prepareExpression( context );
3228  }
3229  else
3230  {
3232  for ( ; it != dataDefinedProperties->constEnd(); ++it )
3233  {
3234  it.value()->prepareExpression( context );
3235  }
3236  }
3237 }
3238 
3240 {
3241  QgsExpressionContext* context = new QgsExpressionContext();
3245  if ( mAtlasComposition.enabled() )
3246  {
3247  context->appendScope( QgsExpressionContextUtils::atlasScope( &mAtlasComposition ) );
3248  }
3249  return context;
3250 }
3251 
3252 void QgsComposition::prepareAllDataDefinedExpressions()
3253 {
3255  prepareDataDefinedExpression( 0, &mDataDefinedProperties, *context.data() );
3256 }
3257 
3258 void QgsComposition::relativeResizeRect( QRectF& rectToResize, const QRectF& boundsBefore, const QRectF& boundsAfter )
3259 {
3260  QgsComposerUtils::relativeResizeRect( rectToResize, boundsBefore, boundsAfter );
3261 }
3262 
3263 double QgsComposition::relativePosition( double position, double beforeMin, double beforeMax, double afterMin, double afterMax )
3264 {
3265  return QgsComposerUtils::relativePosition( position, beforeMin, beforeMax, afterMin, afterMax );
3266 }
bool positionLock() const
Returns whether position lock for mouse drags is enabled returns true if item is locked for mouse mov...
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:88
void beginPrint(QPrinter &printer, const bool evaluateDDPageSize=false)
Prepare the printer for printing.
static void relativeResizeRect(QRectF &rectToResize, const QRectF &boundsBefore, const QRectF &boundsAfter)
Resizes a QRectF relative to a resized bounding rectangle.
Item representing the paper.
Definition: qgspaperitem.h:40
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
void clear()
A scale bar item that can be added to a map composition.
QUndoStack * undoStack()
Returns pointer to undo/redo command storage.
QDomNodeList elementsByTagName(const QString &tagname) const
QgsComposerItem * composerItemAt(const QPointF &position, const bool ignoreLocked=false) const
Returns the topmost composer item at a specified position.
void unlockAllItems()
Unlock all items.
void setActive(bool active)
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
double paperWidth() const
Width of paper item.
void setDotsPerMeterX(int x)
void setDotsPerMeterY(int y)
bool writeXML(QDomElement &composerElem, QDomDocument &doc)
Writes settings to xml (paper dimension)
virtual bool writeXML(QDomElement &elem, QDomDocument &doc) const
Stores item state in DOM element.
void setAllUnselected()
Clears any selected items in the composition.
QgsComposerItem * getComposerItemAbove(QgsComposerItem *item) const
Finds the next composer item above an item.
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
A container class for data source field mapping or expression.
bool end()
bool contains(const Key &key) const
Q_DECL_DEPRECATED QVariant evaluate(const QgsFeature *f)
Evaluate the feature and return the result.
An item that draws an arrow between to points.
QgsComposerMapOverviewStack * overviews()
Returns the map item's overview stack, which is used to control how overviews are drawn over the map'...
void render(QPainter *painter, const QRectF &target, const QRectF &source, Qt::AspectRatioMode aspectRatioMode)
void setResolution(int dpi)
QDomNode appendChild(const QDomNode &newChild)
void addItemToZList(QgsComposerItem *item)
Adds item to z list.
void readXml(const QDomNode &parentNode, const QString &keyStartsWith=QString())
Read store contents from XML.
void composerArrowAdded(QgsComposerArrow *arrow)
Is emitted when new composer arrow has been added to the view.
void setBoundingBoxesVisible(const bool boundsVisible)
Sets whether selection bounding boxes should be shown in the composition.
void push_back(const T &value)
QList< QGraphicsItem * > selectedItems() const
void setPageStyleSymbol(QgsFillSymbolV2 *symbol)
Note: added in version 2.1.
static QgsExpressionContextScope * atlasScope(const QgsAtlasComposition *atlas)
Creates a new scope which contains variables and functions relating to a QgsAtlasComposition.
void assignFreeId()
Sets mId to a number not yet used in the composition.
void setOutputFileName(const QString &fileName)
QString field() const
Get the field which this QgsDataDefined represents.
QString attribute(const QString &name, const QString &defValue) const
virtual void beginItemCommand(const QString &text)
void computeWorldFileParameters(double &a, double &b, double &c, double &d, double &e, double &f) const
Compute world file parameters.
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:192
GridStyle
Style to draw the snapping grid.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QgsFields fields() const
Returns the list of fields of this layer.
QList< QGraphicsItem * > items() const
void alignSelectedItemsTop()
void clear()
void rebuildZList()
Rebuilds the z-order list, based on the current stacking of items in the composition.
QString toString(int indent) const
void composerPictureAdded(QgsComposerPicture *picture)
Is emitted when a new composer picture has been added.
static double relativePosition(const double position, const double beforeMin, const double beforeMax, const double afterMin, const double afterMax)
Returns a scaled position given a before and after range.
int pageNumberForPoint(const QPointF &position) const
Returns the page number corresponding to a point in the composition.
QgsDataDefined * dataDefinedProperty(const QgsComposerObject::DataDefinedProperty property)
Returns a reference to the data defined settings for one of the composition's data defined properties...
void removeItems() override
Removes the items but does not delete them.
bool isElement() const
static QgsFillSymbolV2 * createSimple(const QgsStringMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties. ...
void cache()
Create cache image.
int zOrderListSize() const
Returns the size of the z-order list, which includes items which may have been removed from the compo...
const_iterator constBegin() const
const T & at(int i) const
QgsComposerItemGroup * groupItems(QList< QgsComposerItem * > items)
Creates a new group from a list of composer items and adds it to the composition. ...
static void readDataDefinedPropertyMap(const QDomElement &itemElem, QMap< QgsComposerObject::DataDefinedProperty, QString > *dataDefinedNames, QMap< QgsComposerObject::DataDefinedProperty, QgsDataDefined * > *dataDefinedProperties)
Reads all data defined properties from xml.
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 setBackgroundBrush(const QBrush &brush)
void setSelectedItem(QgsComposerItem *item)
Clears any selected items and sets an item as the current selection.
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 removeItemFromZList(QgsComposerItem *item)
Removes item from z list.
bool enabled() const
Returns whether the atlas generation is enabled.
void setSceneRect(const QRectF &rect)
QPointF snapPointToGrid(const QPointF &scenePoint) const
Snaps a scene coordinate point to grid.
QgsExpression * expression()
void updateBounds()
Updates the scene bounds of the composition.
Container of fields for a vector layer.
Definition: qgsfield.h:177
A container for grouping several QgsComposerItems.
void paperSizeChanged()
void sendItemAddedSignal(QgsComposerItem *item)
Casts object to the proper subclass type and calls corresponding itemAdded signal.
qreal top() const
QgsFeature feature() const
Returns the current atlas feature.
virtual void setSelected(bool s)
Set selected, selected item should be highlighted.
void savePreviousState()
Saves current item state as previous state.
QImage printPageAsRaster(int page)
print composer page to image If the image does not fit into memory, a null image is returned ...
A composer command that merges together with other commands having the same context (=id)...
QDomElement documentElement() const
bool moveItemToBottom(QgsComposerItem *item)
void setCreateUndoCommands(bool enabled)
Sets whether undo commands should be created for interactions with the multiframe.
bool isNull() const
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from the composition.
const_iterator insert(const T &value)
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:162
void beginPrintAsPDF(QPrinter &printer, const QString &file)
Prepare the printer for printing in a PDF.
A non GUI class for rendering a map layer set onto a QPainter.
qreal height() const
QRectF mapRectToScene(const QRectF &rect) const
void composerScaleBarAdded(QgsComposerScaleBar *scalebar)
Is emitted when new composer scale bar has been added.
void moveSelectedItemsToBottom()
void clear()
static void writeDataDefinedPropertyMap(QDomElement &itemElem, QDomDocument &doc, const QMap< QgsComposerObject::DataDefinedProperty, QString > *dataDefinedNames, const QMap< QgsComposerObject::DataDefinedProperty, QgsDataDefined * > *dataDefinedProperties)
Writes data defined properties to xml.
bool reorderItemDown(QgsComposerItem *item)
Moves an item down the z-order list.
void alignSelectedItemsHCenter()
double toDouble(bool *ok) const
double spaceBetweenPages() const
Returns the vertical space between pages in a composer view.
void doPrint(QPrinter &printer, QPainter &painter, bool startNewPage=false)
Print on a preconfigured printer.
void setStatusMessage(const QString &message)
Sets the status bar message for the composer window.
QString tr(const char *sourceText, const char *disambiguation, int n)
void remove(const QString &key)
Remove a key (entry) from the store.
void composerMapAdded(QgsComposerMap *map)
Is emitted when new composer map has been added to the view.
int numPages() const
Returns the number of pages in the composition.
qreal left() const
void adjust(qreal dx1, qreal dy1, qreal dx2, qreal dy2)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:350
virtual bool readXML(const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames=false) override
Reads the properties specific to an attribute table from xml.
void update(const QRectF &rect)
static Q_DECL_DEPRECATED double relativePosition(double position, double beforeMin, double beforeMax, double afterMin, double afterMax)
Returns a scaled position given a before and after range.
void setGridVisible(const bool b)
void alignSelectedItemsVCenter()
bool shouldExportPage(const int page) const
Returns whether a specified page number should be included in exports of the composition.
double x() const
Get the x value of the point.
Definition: qgspoint.h:126
qreal dx() const
A table that displays attributes from a vector layer.
DataDefinedProperty
Data defined properties for different item types.
void composerItemsOnPage(QList< T * > &itemList, const int pageNumber) const
Return composer items of a specific type on a specified page.
void endRender()
Ends the rendering.
int size() const
A composer class that displays svg files or raster format (jpg, png, ...)
void readXML(const QDomElement &elem, const QDomDocument &doc)
Reads general atlas settings from xml.
QSet< QgsComposerItem * > items()
void reset(T *other)
bool isDrawing() const
True if a draw is already in progress.
void composerLegendAdded(QgsComposerLegend *legend)
Is emitted when a new composer legend has been added.
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for the composition.
void setItemPosition(double x, double y, ItemPositionMode itemPoint=UpperLeft, int page=-1)
Moves the item to a new position (in canvas coordinates)
The QgsMapSettings class contains configuration for rendering of the map.
QList< QgsComposerItem * > * zOrderList()
Returns the item z-order list.
int width() const
double ANALYSIS_EXPORT max(double x, double y)
Returns the maximum of two doubles or the first argument if both are equal.
QDomElement toElement() const
void addComposerShape(QgsComposerShape *shape)
Adds a composer shape to the graphics scene and advices composer to create a widget for it (through s...
void setGridPen(const QPen &p)
static QDomElement saveSymbol(QString symbolName, QgsSymbolV2 *symbol, QDomDocument &doc)
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Return value for the given key. If the key is not stored, default value will be used.
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Reads the properties specific to an attribute table from xml.
qreal bottom() const
void setUseExpression(bool use)
Controls if the field or the expression part is active.
QgsComposerItem * getComposerItemBelow(QgsComposerItem *item) const
QTransform transform() const
QString uuid() const
Get item identification name.
qreal zValue() const
qreal y1() const
qreal y2() const
QPointF pos() const
void setNumPages(const int pages)
Sets the number of pages for the composition.
int count() const
qreal x1() const
qreal x2() const
QString number(int n, int base)
void refreshItemsTriggered()
Is emitted when item in the composition must be refreshed.
qreal x() const
qreal y() const
QPointF p1() const
bool beginRender()
Begins the rendering.
void setValue(const QString &key, const QVariant &value)
Add an entry to the store. If the entry with the keys exists already, it will be overwritten.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
void setSnapLinesVisible(const bool visible)
Hides / shows custom snap lines.
int itemPageNumber(const QgsComposerItem *) const
Returns on which page number (0-based) is displayed an item.
static QgsComposition::PaperOrientation decodePaperOrientation(const QString orientationString, bool &ok)
Decodes a string representing a paper orientation.
int toInt(bool *ok) const
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:197
void removeItem(QGraphicsItem *item)
void cancelCommand()
Deletes current command.
void fill(uint pixelValue)
void setSnapGridOffsetX(const double offset)
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
bool containsChange() const
Returns true if previous state and after state are valid and different.
int pageNumberAt(const QPointF &position) const
Returns the page number (0-based) given a coordinate.
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 itemRemoved(QgsComposerItem *)
Is emitted when a composer item has been removed from the scene.
int printResolution() const
void updatePagePos(double newPageWidth, double newPageHeight)
Moves the item so that it retains its relative position on the page when the paper size changes...
int width() const
void setAttribute(const QString &name, const QString &value)
void setField(const QString &field)
Set the field name which this QgsDataDefined represents.
void clear()
Clears all items from z-order list and resets the model.
QList< QGraphicsView * > views() const
void removeSnapLine(QGraphicsLineItem *line)
Remove custom snap line (and delete the object)
qreal m11() const
void addItem(QgsComposerItem *item) override
Adds an item to the group.
int toInt(bool *ok, int base) const
void alignSelectedItemsRight()
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QDomNodeList elementsByTagName(const QString &tagname) const
QStringList keys() const
Return list of stored keys.
Abstract base class for composer items with the ability to distribute the content to several frames (...
bool isEmpty() const
int removeAll(const T &value)
QString trimmed() const
const_iterator constEnd() const
void setLine(const QLineF &line)
void addComposerTableFrame(QgsComposerAttributeTableV2 *table, QgsComposerFrame *frame)
Adds composer tablev2 frame and advises composer to create a widget for it (through signal) ...
#define M_PI
QPaintDevice * device() const
const QgsComposerItem * getComposerItemByUuid(const QString theUuid) const
Returns a composer item given its unique identifier.
QList< QgsComposerItem * > ungroupItems(QgsComposerItemGroup *group)
Ungroups items by removing them from an item group and removing the group from the composition...
void cancelMultiFrameCommand()
Deletes current multi frame command.
void setWidthF(qreal width)
void removeComposerItem(QgsComposerItem *item, const bool createCommand=true, const bool removeGroupItems=true)
Remove item from the graphics scene.
static bool decodePresetPaperSize(const QString presetString, double &width, double &height)
Decodes a string representing a preset page size.
QPointF center() const
const_iterator constEnd() const
void removeCustomProperty(const QString &key)
Remove a custom property from the composition.
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
QRectF united(const QRectF &rectangle) const
bool loadFromTemplate(const QDomDocument &doc, QMap< QString, QString > *substitutionMap=0, bool addUndoCommands=false, const bool clearComposition=true)
Load a template document.
void setPaperSize(PaperSize newPaperSize)
void moveSelectedItemsToTop()
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
int frameCount() const
Returns the number of frames associated with this multiframe.
void setColor(const QColor &color)
QStringList customProperties() const
Return list of keys stored in custom properties for composition.
A composer command that merges together with other commands having the same context (=id) for multi f...
double mapRotation(QgsComposerObject::PropertyValueType valueType=QgsComposerObject::EvaluatedValue) const
Returns the rotation used for drawing the map within the composer item.
void setSnapGridResolution(const double r)
bool pageIsEmpty(const int page) const
Returns whether a page is empty, ie, it contains no items except for the background paper item...
virtual QPaintEngine * paintEngine() const
void setPen(const QPen &pen)
void removeMultiFrame(QgsComposerMultiFrame *multiFrame)
Removes multi frame (but does not delete it)
Object representing map window.
Frame item for a composer multiframe item.
bool readXML(const QDomElement &compositionElem, const QDomDocument &doc)
Reads settings from xml file.
bool useExpression() const
Returns if the field or the expression part is active.
int indexFromName(const QString &name) const
Look up field's index from name. Returns -1 on error.
Definition: qgsfield.cpp:338
bool isActive() const
T * data() const
virtual QgsSymbolV2 * clone() const override
iterator end()
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
QgsRectangle * currentMapExtent()
Returns a pointer to the current map extent, which is either the original user specified extent or th...
QgsExpressionContext * createExpressionContext() const
Creates an expression context relating to the compositions's current state.
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 refreshItems()
Forces items in the composition to refresh.
qreal right() const
void setUpdatesEnabled(bool enabled)
Sets whether updates to the composer map are enabled.
iterator begin()
void clear()
QgsComposerItem * getComposerItemAbove(QgsComposerItem *item) const
Q_DECL_DEPRECATED int pixelFontSize(double pointSize) const
Returns the mm font size for a font that has point size set.
void setFullPage(bool fp)
PreviewMode previewMode() const
void setLine(qreal x1, qreal y1, qreal x2, qreal y2)
void nPagesChanged()
void removeItem(QgsComposerItem *item)
Removes an item from the z-order list.
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
void setTopLeft(const QPointF &position)
void updateSettings()
Refreshes the composition when composer related options change.
void saveAfterState()
Saves current item state as after state.
const_iterator constBegin() const
bool setAtlasMode(const QgsComposition::AtlasMode mode)
Sets the current atlas mode of the composition.
bool isNull() const
bool newPage()
void setPositionLock(const bool lock)
Locks / unlocks the item position for mouse drags.
void setOrientation(Orientation orientation)
void setPrintResolution(const int dpi)
bool print(QPrinter &printer, const bool evaluateDDPageSize=false)
Convenience function that prepares the printer and prints.
void composerTableAdded(QgsComposerAttributeTable *table)
Is emitted when a new composer table has been added.
const Key key(const T &value) const
QList< QgsPaperItem * > pages()
Return pages in the correct order.
void refreshZList()
Rebuilds the z order list by adding any item which are present in the composition but missing from th...
int id() const
Get identification number.
void composerShapeAdded(QgsComposerShape *shape)
Is emitted when a new composer shape has been added.
bool exportAsPDF(const QString &file)
Convenience function that prepares the printer for printing in PDF and prints.
void lockSelectedItems()
Lock the selected items.
QString & replace(int position, int n, QChar after)
void setGridStyle(const GridStyle s)
A composer command class for adding / removing composer items.
const QgsComposerItem * getComposerItemById(const QString theId) const
Returns a composer item given its text identifier.
void selectNextByZOrder(const ZValueDirection direction)
void statusMsgChanged(QString message)
Is emitted when the composition has an updated status bar message for the composer window...
QVariant value(const QString &key, const QVariant &defaultValue) const
void clearSnapLines()
Removes all snap lines.
A table class that displays a vector attribute table.
bool reorderItemUp(QgsComposerItem *item)
Moves an item up the z-order list.
void drawImage(const QRectF &target, const QImage &image, const QRectF &source, QFlags< Qt::ImageConversionFlag > flags)
qreal width() const
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:236
QgsComposerItem * getComposerItemBelow(QgsComposerItem *item) const
Finds the next composer item below an item.
bool remove(const T &value)
Undo command to undo/redo all composer item related changes.
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
void setDataDefinedProperty(const QgsComposerObject::DataDefinedProperty property, bool active, bool useExpression, const QString &expression, const QString &field)
Sets parameters for a data defined property for the composition.
A composer items that draws common shapes (ellipse, triangle, rectangle)
void writeXml(QDomNode &parentNode, QDomDocument &doc) const
Write store contents to XML.
virtual void endItemCommand()
void readXMLMapSettings(const QDomElement &elem, const QDomDocument &doc)
Reads old (pre 2.2) map related atlas settings from xml.
Q_DECL_DEPRECATED bool prepareExpression(QgsVectorLayer *layer)
Prepares the expression using a vector layer.
bool expressionIsPrepared() const
Returns whether the data defined object's expression is prepared.
void addComposerHtmlFrame(QgsComposerHtml *html, QgsComposerFrame *frame)
Adds composer html frame and advises composer to create a widget for it (through signal) ...
iterator end()
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QList< QgsComposerMapOverview * > asList() const
Returns a list of QgsComposerMapOverviews contained by the stack.
AtlasMode
Composition atlas modes.
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:352
QDomElement firstChildElement(const QString &tagName) const
QPointF positionOnPage(const QPointF &position) const
Returns the position within a page of a point in the composition.
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) ...
T & last()
void alignSelectedItemsBottom()
static void fixEngineFlags(QPaintEngine *engine)
void update(qreal x, qreal y, qreal w, qreal h)
void alignSelectedItemsLeft()
QGraphicsLineItem * nearestSnapLine(const bool horizontal, const double x, const double y, const double tolerance, QList< QPair< QgsComposerItem *, QgsComposerItem::ItemPositionMode > > &snappedItems) const
Get nearest snap line.
void removeLast()
double paperHeight() const
Height of paper item.
static double pointsToMM(const double pointSize)
Returns the size in mm corresponding to a font point size.
void selectedItemChanged(QgsComposerItem *selected)
Is emitted when selected item changed.
void renderPage(QPainter *p, int page)
Render a page to a paint device.
void setVisible(bool visible)
double y() const
Get the y value of the point.
Definition: qgspoint.h:134
A label that can be placed onto a map composition.
void setUseAdvancedEffects(const bool effectsEnabled)
Used to enable or disable advanced effects such as blend modes in a composition.
bool isValid() const
void setEffectsEnabled(const bool effectsEnabled)
Sets whether effects (eg blend modes) are enabled for the item.
void composerLabelAdded(QgsComposerLabel *label)
Is emitted when new composer label has been added to the view.
qreal height() const
int height() const
void addComposerPicture(QgsComposerPicture *picture)
Adds picture to the graphics scene and advices composer to create a widget for it (through signal) ...
QgsAtlasComposition & atlasComposition()
double toDouble(bool *ok) const
void composerHtmlFrameAdded(QgsComposerHtml *html, QgsComposerFrame *frame)
Is emitted when a new composer html has been added to the view.
iterator insert(const Key &key, const T &value)
static Q_DECL_DEPRECATED void relativeResizeRect(QRectF &rectToResize, const QRectF &boundsBefore, const QRectF &boundsAfter)
Resizes a QRectF relative to the change from boundsBefore to boundsAfter.
void addComposerTable(QgsComposerAttributeTable *table)
Adds a composer table to the graphics scene and advices composer to create a widget for it (through s...
void removeAttribute(const QString &name)
Handles drawing of selection outlines and mouse handles.
void composerTableFrameAdded(QgsComposerAttributeTableV2 *table, QgsComposerFrame *frame)
Is emitted when a new composer table frame has been added to the view.
Q_DECL_DEPRECATED const QgsComposerHtml * getComposerHtmlByItem(QgsComposerItem *item) const
Returns the composer html with specified id (a string as named in the composer user interface item pr...
QgsVectorLayer * coverageLayer() const
Returns the coverage layer used for the atlas features.
void setItemRemoved(QgsComposerItem *item)
Marks an item as removed from the composition.
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from DOM document.
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
void setBrush(const QBrush &brush)
friend class QgsComposerModel
int size() const
static void setSpecialColumn(const QString &name, QVariant value)
Assign a special column.
int height() const
bool readXML(const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames=false) override
Reads multiframe state information from a DOM element.
const_iterator constEnd() const
Q_DECL_DEPRECATED double pointFontSize(int pixelSize) const
Does the inverse calculation and returns points for mm.
int fromPage() const
QGraphicsLineItem * addSnapLine()
Add a custom snap line (can be horizontal or vertical)
QDomElement createElement(const QString &tagName)
void clear()
void composerItems(QList< T * > &itemList)
Return composer items of a specific type.
const_iterator constBegin() const
void printResolutionChanged()
Is emitted when the compositions print resolution changes.
void setColorMode(ColorMode newColorMode)
PlotStyle
Plot type.
void addItem(QGraphicsItem *item)
const QgsComposerMap * getComposerMapById(const int id) const
Returns the composer map with specified id.
void setPreviewMode(PreviewMode m)
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:202
bool raiseItem(QgsComposerItem *item)
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QgsComposerMultiFrame * multiFrame() const
Returns the parent multiframe for the frame.
void setSnapToGridEnabled(const bool b)
bool reorderItemToTop(QgsComposerItem *item)
Moves an item to the top of the z-order list.
void setOutputFormat(OutputFormat format)
Represents a vector layer which manages a vector based data sets.
bool begin(QPaintDevice *device)
void setBottomRight(const QPointF &position)
void move(double dx, double dy)
Moves item in canvas coordinates.
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) ...
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:187
QString toString() const
void setZValue(qreal z)
void addMultiFrame(QgsComposerMultiFrame *multiFrame)
Adds multiframe.
void addItemAtTop(QgsComposerItem *item)
Adds an item to the top of the composition z stack.
QgsPoint center() const
Center point of the rectangle.
Definition: qgsrectangle.h:212
QString evalErrorString() const
Returns evaluation error.
void setExpressionString(const QString &expr)
Sets the expression for this QgsDataDefined.
bool isActive() const
iterator find(const Key &key)
iterator begin()
bool lowerItem(QgsComposerItem *item)
void addComposerArrow(QgsComposerArrow *arrow)
Adds an arrow item to the graphics scene and advices composer to create a widget for it (through sign...
bool reorderItemToBottom(QgsComposerItem *item)
Moves an item to the bottom of the z-order list.
bool moveItemToTop(QgsComposerItem *item)
void setPaperSize(const double width, const double height)
Changes size of paper item.
void addComposerLegend(QgsComposerLegend *legend)
Adds legend to the graphics scene and advices composer to create a widget for it (through signal) ...
bool containsChange() const
Returns true if previous state and after state are valid and different.
Q_DECL_DEPRECATED QgsComposition(QgsMapRenderer *mapRenderer)
void push(QUndoCommand *cmd)
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:207
virtual int type() const override
Return correct graphics item type.
qreal width() const
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
QDomNode at(int index) const
QRectF rect() const
int toPage() const
void setSnapGridOffsetY(const double offset)
void refreshDataDefinedProperty(const QgsComposerObject::DataDefinedProperty property=QgsComposerObject::AllProperties, const QgsExpressionContext *context=0)
Refreshes a data defined property for the composition by reevaluating the property's value and redraw...
const T value(const Key &key) const
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.
void dirty(bool b)
Definition: qgsproject.cpp:381
static QgsExpressionContextScope * compositionScope(const QgsComposition *composition)
Creates a new scope which contains variables and functions relating to a QgsComposition.
QList< QGraphicsLineItem * > * snapLines()
Returns pointer to snap lines collection.
void beginMultiFrameCommand(QgsComposerMultiFrame *multiFrame, const QString &text, const QgsComposerMultiFrameMergeCommand::Context c=QgsComposerMultiFrameMergeCommand::Unknown)
static double mmToPoints(const double mmSize)
Returns the size in mm corresponding to a font point size.
QString id() const
Get item's id (which is not necessarly unique)