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