QGIS API Documentation  2.15.0-Master (972fc9f)
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  return true;
1072 }
1073 
1074 bool QgsComposition::loadFromTemplate( const QDomDocument& doc, QMap<QString, QString>* substitutionMap, bool addUndoCommands, const bool clearComposition )
1075 {
1076  if ( clearComposition )
1077  {
1078  deleteAndRemoveMultiFrames();
1079 
1080  //delete all non paper items and emit itemRemoved signal
1081  QList<QGraphicsItem *> itemList = items();
1082  QList<QGraphicsItem *>::iterator itemIter = itemList.begin();
1083  for ( ; itemIter != itemList.end(); ++itemIter )
1084  {
1085  QgsComposerItem* cItem = dynamic_cast<QgsComposerItem*>( *itemIter );
1086  QgsPaperItem* pItem = dynamic_cast<QgsPaperItem*>( *itemIter );
1087  if ( cItem && !pItem )
1088  {
1089  removeItem( cItem );
1090  emit itemRemoved( cItem );
1091  delete cItem;
1092  }
1093  }
1094  mItemsModel->clear();
1095 
1096  removePaperItems();
1097  mUndoStack->clear();
1098  }
1099 
1100  QDomDocument importDoc;
1101  if ( substitutionMap )
1102  {
1103  QString xmlString = doc.toString();
1104  QMap<QString, QString>::const_iterator sIt = substitutionMap->constBegin();
1105  for ( ; sIt != substitutionMap->constEnd(); ++sIt )
1106  {
1107  xmlString = xmlString.replace( '[' + sIt.key() + ']', encodeStringForXML( sIt.value() ) );
1108  }
1109 
1110  QString errorMsg;
1111  int errorLine, errorColumn;
1112  if ( !importDoc.setContent( xmlString, &errorMsg, &errorLine, &errorColumn ) )
1113  {
1114  return false;
1115  }
1116  }
1117  else
1118  {
1119  importDoc = doc;
1120  }
1121 
1122  //read general settings
1123  QDomElement atlasElem;
1124  if ( clearComposition )
1125  {
1126  QDomElement compositionElem = importDoc.documentElement().firstChildElement( "Composition" );
1127  if ( compositionElem.isNull() )
1128  {
1129  return false;
1130  }
1131 
1132  bool ok = readXML( compositionElem, importDoc );
1133  if ( !ok )
1134  {
1135  return false;
1136  }
1137 
1138  // read atlas parameters - must be done before adding items
1139  atlasElem = importDoc.documentElement().firstChildElement( "Atlas" );
1140  atlasComposition().readXML( atlasElem, importDoc );
1141  }
1142 
1143  // remove all uuid attributes since we don't want duplicates UUIDS
1144  QDomNodeList composerItemsNodes = importDoc.elementsByTagName( "ComposerItem" );
1145  for ( int i = 0; i < composerItemsNodes.count(); ++i )
1146  {
1147  QDomNode composerItemNode = composerItemsNodes.at( i );
1148  if ( composerItemNode.isElement() )
1149  {
1150  composerItemNode.toElement().setAttribute( "templateUuid", composerItemNode.toElement().attribute( "uuid" ) );
1151  composerItemNode.toElement().removeAttribute( "uuid" );
1152  }
1153  }
1154 
1155  //addItemsFromXML
1156  addItemsFromXML( importDoc.documentElement(), importDoc, nullptr, addUndoCommands, nullptr );
1157 
1158  //read atlas map parameters (for pre 2.2 templates)
1159  //this can only be done after items have been added
1160  if ( clearComposition )
1161  {
1162  atlasComposition().readXMLMapSettings( atlasElem, importDoc );
1163  }
1164  return true;
1165 }
1166 
1167 QPointF QgsComposition::minPointFromXml( const QDomElement& elem ) const
1168 {
1169  double minX = std::numeric_limits<double>::max();
1170  double minY = std::numeric_limits<double>::max();
1171  QDomNodeList composerItemList = elem.elementsByTagName( "ComposerItem" );
1172  for ( int i = 0; i < composerItemList.size(); ++i )
1173  {
1174  QDomElement currentComposerItemElem = composerItemList.at( i ).toElement();
1175  double x, y;
1176  bool xOk, yOk;
1177  x = currentComposerItemElem.attribute( "x" ).toDouble( &xOk );
1178  y = currentComposerItemElem.attribute( "y" ).toDouble( &yOk );
1179  if ( !xOk || !yOk )
1180  {
1181  continue;
1182  }
1183  minX = qMin( minX, x );
1184  minY = qMin( minY, y );
1185  }
1186  if ( minX < std::numeric_limits<double>::max() )
1187  {
1188  return QPointF( minX, minY );
1189  }
1190  else
1191  {
1192  return QPointF( 0, 0 );
1193  }
1194 }
1195 
1197  bool addUndoCommands, QPointF* pos, bool pasteInPlace )
1198 {
1199  QPointF* pasteInPlacePt = nullptr;
1200 
1201  //if we are adding items to a composition which already contains items, we need to make sure
1202  //these items are placed at the top of the composition and that zValues are not duplicated
1203  //so, calculate an offset which needs to be added to the zValue of created items
1204  int zOrderOffset = mItemsModel->zOrderListSize();
1205 
1206  QPointF pasteShiftPos;
1207  QgsComposerItem* lastPastedItem = nullptr;
1208  if ( pos )
1209  {
1210  //If we are placing items relative to a certain point, then calculate how much we need
1211  //to shift the items by so that they are placed at this point
1212  //First, calculate the minimum position from the xml
1213  QPointF minItemPos = minPointFromXml( elem );
1214  //next, calculate how much each item needs to be shifted from its original position
1215  //so that it's placed at the correct relative position
1216  pasteShiftPos = *pos - minItemPos;
1217 
1218  //since we are pasting items, clear the existing selection
1219  setAllUnselected();
1220 
1221  if ( pasteInPlace )
1222  {
1223  pasteInPlacePt = new QPointF( 0, pageNumberAt( *pos ) * ( mPageHeight + mSpaceBetweenPages ) );
1224  }
1225  }
1226  QDomNodeList composerLabelList = elem.elementsByTagName( "ComposerLabel" );
1227  for ( int i = 0; i < composerLabelList.size(); ++i )
1228  {
1229  QDomElement currentComposerLabelElem = composerLabelList.at( i ).toElement();
1230  QgsComposerLabel* newLabel = new QgsComposerLabel( this );
1231  newLabel->readXML( currentComposerLabelElem, doc );
1232  if ( pos )
1233  {
1234  if ( pasteInPlacePt )
1235  {
1236  newLabel->setItemPosition( newLabel->pos().x(), fmod( newLabel->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1237  newLabel->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1238  }
1239  else
1240  {
1241  newLabel->move( pasteShiftPos.x(), pasteShiftPos.y() );
1242  }
1243  newLabel->setSelected( true );
1244  lastPastedItem = newLabel;
1245  }
1246  addComposerLabel( newLabel );
1247  newLabel->setZValue( newLabel->zValue() + zOrderOffset );
1248  if ( addUndoCommands )
1249  {
1250  pushAddRemoveCommand( newLabel, tr( "Label added" ) );
1251  }
1252  }
1253  // map
1254  QDomNodeList composerMapList = elem.elementsByTagName( "ComposerMap" );
1255  for ( int i = 0; i < composerMapList.size(); ++i )
1256  {
1257  QDomElement currentComposerMapElem = composerMapList.at( i ).toElement();
1258  QgsComposerMap* newMap = new QgsComposerMap( this );
1259 
1260  if ( mapsToRestore )
1261  {
1262  newMap->setUpdatesEnabled( false );
1263  }
1264 
1265  newMap->readXML( currentComposerMapElem, doc );
1266  newMap->assignFreeId();
1267 
1268  if ( mapsToRestore )
1269  {
1270  mapsToRestore->insert( newMap, static_cast< int >( newMap->previewMode() ) );
1272  newMap->setUpdatesEnabled( true );
1273  }
1274  addComposerMap( newMap, false );
1275  newMap->setZValue( newMap->zValue() + zOrderOffset );
1276  if ( pos )
1277  {
1278  if ( pasteInPlace )
1279  {
1280  newMap->setItemPosition( newMap->pos().x(), fmod( newMap->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1281  newMap->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1282  }
1283  else
1284  {
1285  newMap->move( pasteShiftPos.x(), pasteShiftPos.y() );
1286  }
1287  newMap->setSelected( true );
1288  lastPastedItem = newMap;
1289  }
1290 
1291  if ( addUndoCommands )
1292  {
1293  pushAddRemoveCommand( newMap, tr( "Map added" ) );
1294  }
1295  }
1296  //now that all map items have been created, re-connect overview map signals
1298  composerItems( maps );
1299  for ( QList<QgsComposerMap*>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
1300  {
1301  QgsComposerMap* map = ( *mit );
1302  if ( map )
1303  {
1304  QList<QgsComposerMapOverview* > overviews = map->overviews()->asList();
1305  QList<QgsComposerMapOverview* >::iterator overviewIt = overviews.begin();
1306  for ( ; overviewIt != overviews.end(); ++overviewIt )
1307  {
1308  ( *overviewIt )->connectSignals();
1309  }
1310  }
1311  }
1312 
1313  // arrow
1314  QDomNodeList composerArrowList = elem.elementsByTagName( "ComposerArrow" );
1315  for ( int i = 0; i < composerArrowList.size(); ++i )
1316  {
1317  QDomElement currentComposerArrowElem = composerArrowList.at( i ).toElement();
1318  QgsComposerArrow* newArrow = new QgsComposerArrow( this );
1319  newArrow->readXML( currentComposerArrowElem, doc );
1320  if ( pos )
1321  {
1322  if ( pasteInPlace )
1323  {
1324  newArrow->setItemPosition( newArrow->pos().x(), fmod( newArrow->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1325  newArrow->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1326  }
1327  else
1328  {
1329  newArrow->move( pasteShiftPos.x(), pasteShiftPos.y() );
1330  }
1331  newArrow->setSelected( true );
1332  lastPastedItem = newArrow;
1333  }
1334  addComposerArrow( newArrow );
1335  newArrow->setZValue( newArrow->zValue() + zOrderOffset );
1336  if ( addUndoCommands )
1337  {
1338  pushAddRemoveCommand( newArrow, tr( "Arrow added" ) );
1339  }
1340  }
1341  // scalebar
1342  QDomNodeList composerScaleBarList = elem.elementsByTagName( "ComposerScaleBar" );
1343  for ( int i = 0; i < composerScaleBarList.size(); ++i )
1344  {
1345  QDomElement currentComposerScaleBarElem = composerScaleBarList.at( i ).toElement();
1346  QgsComposerScaleBar* newScaleBar = new QgsComposerScaleBar( this );
1347  newScaleBar->readXML( currentComposerScaleBarElem, doc );
1348  if ( pos )
1349  {
1350  if ( pasteInPlace )
1351  {
1352  newScaleBar->setItemPosition( newScaleBar->pos().x(), fmod( newScaleBar->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1353  newScaleBar->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1354  }
1355  else
1356  {
1357  newScaleBar->move( pasteShiftPos.x(), pasteShiftPos.y() );
1358  }
1359  newScaleBar->setSelected( true );
1360  lastPastedItem = newScaleBar;
1361  }
1362  addComposerScaleBar( newScaleBar );
1363  newScaleBar->setZValue( newScaleBar->zValue() + zOrderOffset );
1364  if ( addUndoCommands )
1365  {
1366  pushAddRemoveCommand( newScaleBar, tr( "Scale bar added" ) );
1367  }
1368  }
1369  // shape
1370  QDomNodeList composerShapeList = elem.elementsByTagName( "ComposerShape" );
1371  for ( int i = 0; i < composerShapeList.size(); ++i )
1372  {
1373  QDomElement currentComposerShapeElem = composerShapeList.at( i ).toElement();
1374  QgsComposerShape* newShape = new QgsComposerShape( this );
1375  newShape->readXML( currentComposerShapeElem, doc );
1376  //new shapes should default to symbol v2
1377  newShape->setUseSymbolV2( true );
1378  if ( pos )
1379  {
1380  if ( pasteInPlace )
1381  {
1382  newShape->setItemPosition( newShape->pos().x(), fmod( newShape->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1383  newShape->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1384  }
1385  else
1386  {
1387  newShape->move( pasteShiftPos.x(), pasteShiftPos.y() );
1388  }
1389  newShape->setSelected( true );
1390  lastPastedItem = newShape;
1391  }
1392  addComposerShape( newShape );
1393  newShape->setZValue( newShape->zValue() + zOrderOffset );
1394  if ( addUndoCommands )
1395  {
1396  pushAddRemoveCommand( newShape, tr( "Shape added" ) );
1397  }
1398  }
1399 
1400  // polygon
1401  QDomNodeList composerPolygonList = elem.elementsByTagName( "ComposerPolygon" );
1402  for ( int i = 0; i < composerPolygonList.size(); ++i )
1403  {
1404  QDomElement currentComposerPolygonElem = composerPolygonList.at( i ).toElement();
1405  QgsComposerPolygon* newPolygon = new QgsComposerPolygon( this );
1406  newPolygon->readXML( currentComposerPolygonElem, doc );
1407 
1408  if ( pos )
1409  {
1410  if ( pasteInPlace )
1411  {
1412  newPolygon->setItemPosition( newPolygon->pos().x(), fmod( newPolygon->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1413  newPolygon->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1414  }
1415  else
1416  {
1417  newPolygon->move( pasteShiftPos.x(), pasteShiftPos.y() );
1418  }
1419  newPolygon->setSelected( true );
1420  lastPastedItem = newPolygon;
1421  }
1422 
1423  addComposerPolygon( newPolygon );
1424  newPolygon->setZValue( newPolygon->zValue() + zOrderOffset );
1425  if ( addUndoCommands )
1426  {
1427  pushAddRemoveCommand( newPolygon, tr( "Polygon added" ) );
1428  }
1429  }
1430 
1431  // polyline
1432  QDomNodeList addComposerPolylineList = elem.elementsByTagName( "ComposerPolyline" );
1433  for ( int i = 0; i < addComposerPolylineList.size(); ++i )
1434  {
1435  QDomElement currentComposerPolylineElem = addComposerPolylineList.at( i ).toElement();
1436  QgsComposerPolyline* newPolyline = new QgsComposerPolyline( this );
1437  newPolyline->readXML( currentComposerPolylineElem, doc );
1438 
1439  if ( pos )
1440  {
1441  if ( pasteInPlace )
1442  {
1443  newPolyline->setItemPosition( newPolyline->pos().x(), fmod( newPolyline->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1444  newPolyline->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1445  }
1446  else
1447  {
1448  newPolyline->move( pasteShiftPos.x(), pasteShiftPos.y() );
1449  }
1450  newPolyline->setSelected( true );
1451  lastPastedItem = newPolyline;
1452  }
1453 
1454  addComposerPolyline( newPolyline );
1455  newPolyline->setZValue( newPolyline->zValue() + zOrderOffset );
1456  if ( addUndoCommands )
1457  {
1458  pushAddRemoveCommand( newPolyline, tr( "Polyline added" ) );
1459  }
1460  }
1461 
1462  // picture
1463  QDomNodeList composerPictureList = elem.elementsByTagName( "ComposerPicture" );
1464  for ( int i = 0; i < composerPictureList.size(); ++i )
1465  {
1466  QDomElement currentComposerPictureElem = composerPictureList.at( i ).toElement();
1467  QgsComposerPicture* newPicture = new QgsComposerPicture( this );
1468  newPicture->readXML( currentComposerPictureElem, doc );
1469  if ( pos )
1470  {
1471  if ( pasteInPlace )
1472  {
1473  newPicture->setItemPosition( newPicture->pos().x(), fmod( newPicture->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1474  newPicture->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1475  }
1476  else
1477  {
1478  newPicture->move( pasteShiftPos.x(), pasteShiftPos.y() );
1479  }
1480  newPicture->setSelected( true );
1481  lastPastedItem = newPicture;
1482  }
1483  addComposerPicture( newPicture );
1484  newPicture->setZValue( newPicture->zValue() + zOrderOffset );
1485  if ( addUndoCommands )
1486  {
1487  pushAddRemoveCommand( newPicture, tr( "Picture added" ) );
1488  }
1489  }
1490  // legend
1491  QDomNodeList composerLegendList = elem.elementsByTagName( "ComposerLegend" );
1492  for ( int i = 0; i < composerLegendList.size(); ++i )
1493  {
1494  QDomElement currentComposerLegendElem = composerLegendList.at( i ).toElement();
1495  QgsComposerLegend* newLegend = new QgsComposerLegend( this );
1496  newLegend->readXML( currentComposerLegendElem, doc );
1497  if ( pos )
1498  {
1499  if ( pasteInPlace )
1500  {
1501  newLegend->setItemPosition( newLegend->pos().x(), fmod( newLegend->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1502  newLegend->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1503  }
1504  else
1505  {
1506  newLegend->move( pasteShiftPos.x(), pasteShiftPos.y() );
1507  }
1508  newLegend->setSelected( true );
1509  lastPastedItem = newLegend;
1510  }
1511  addComposerLegend( newLegend );
1512  newLegend->setZValue( newLegend->zValue() + zOrderOffset );
1513  if ( addUndoCommands )
1514  {
1515  pushAddRemoveCommand( newLegend, tr( "Legend added" ) );
1516  }
1517  }
1518  // table
1519  QDomNodeList composerTableList = elem.elementsByTagName( "ComposerAttributeTable" );
1520  for ( int i = 0; i < composerTableList.size(); ++i )
1521  {
1522  QDomElement currentComposerTableElem = composerTableList.at( i ).toElement();
1523  QgsComposerAttributeTable* newTable = new QgsComposerAttributeTable( this );
1524  newTable->readXML( currentComposerTableElem, doc );
1525  if ( pos )
1526  {
1527  if ( pasteInPlace )
1528  {
1529  newTable->setItemPosition( newTable->pos().x(), fmod( newTable->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
1530  newTable->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
1531  }
1532  else
1533  {
1534  newTable->move( pasteShiftPos.x(), pasteShiftPos.y() );
1535  }
1536  newTable->setSelected( true );
1537  lastPastedItem = newTable;
1538  }
1539  addComposerTable( newTable );
1540  newTable->setZValue( newTable->zValue() + zOrderOffset );
1541  if ( addUndoCommands )
1542  {
1543  pushAddRemoveCommand( newTable, tr( "Table added" ) );
1544  }
1545  }
1546  // html
1547  //TODO - fix this. pasting multiframe frame items has no effect
1548  QDomNodeList composerHtmlList = elem.elementsByTagName( "ComposerHtml" );
1549  for ( int i = 0; i < composerHtmlList.size(); ++i )
1550  {
1551  QDomElement currentHtmlElem = composerHtmlList.at( i ).toElement();
1552  QgsComposerHtml* newHtml = new QgsComposerHtml( this, false );
1553  newHtml->readXML( currentHtmlElem, doc );
1554  newHtml->setCreateUndoCommands( true );
1555  this->addMultiFrame( newHtml );
1556 
1557  //offset z values for frames
1558  //TODO - fix this after fixing html item paste
1559  /*for ( int frameIdx = 0; frameIdx < newHtml->frameCount(); ++frameIdx )
1560  {
1561  QgsComposerFrame * frame = newHtml->frame( frameIdx );
1562  frame->setZValue( frame->zValue() + zOrderOffset );
1563  }*/
1564  }
1565  QDomNodeList composerAttributeTableV2List = elem.elementsByTagName( "ComposerAttributeTableV2" );
1566  for ( int i = 0; i < composerAttributeTableV2List.size(); ++i )
1567  {
1568  QDomElement currentTableElem = composerAttributeTableV2List.at( i ).toElement();
1569  QgsComposerAttributeTableV2* newTable = new QgsComposerAttributeTableV2( this, false );
1570  newTable->readXML( currentTableElem, doc );
1571  newTable->setCreateUndoCommands( true );
1572  this->addMultiFrame( newTable );
1573 
1574  //offset z values for frames
1575  //TODO - fix this after fixing html item paste
1576  /*for ( int frameIdx = 0; frameIdx < newHtml->frameCount(); ++frameIdx )
1577  {
1578  QgsComposerFrame * frame = newHtml->frame( frameIdx );
1579  frame->setZValue( frame->zValue() + zOrderOffset );
1580  }*/
1581  }
1582 
1583  // groups (must be last as it references uuids of above items)
1584  //TODO - pasted groups lose group properties, since the uuids of group items
1585  //changes
1586  QDomNodeList groupList = elem.elementsByTagName( "ComposerItemGroup" );
1587  for ( int i = 0; i < groupList.size(); ++i )
1588  {
1589  QDomElement groupElem = groupList.at( i ).toElement();
1590  QgsComposerItemGroup *newGroup = new QgsComposerItemGroup( this );
1591  newGroup->readXML( groupElem, doc );
1592  addItem( newGroup );
1593  if ( addUndoCommands )
1594  {
1595  pushAddRemoveCommand( newGroup, tr( "Group added" ) );
1596  }
1597  }
1598 
1599  //Since this function adds items grouped by type, and each item is added to end of
1600  //z order list in turn, it will now be inconsistent with the actual order of items in the scene.
1601  //Make sure z order list matches the actual order of items in the scene.
1602  mItemsModel->rebuildZList();
1603 
1604  if ( lastPastedItem )
1605  {
1606  emit selectedItemChanged( lastPastedItem );
1607  }
1608 
1609  delete pasteInPlacePt;
1610  pasteInPlacePt = nullptr;
1611 
1612 }
1613 
1615 {
1616  if ( !item )
1617  {
1618  return;
1619  }
1620 
1621  //model handles changes to z list
1622  mItemsModel->addItemAtTop( item );
1623 }
1624 
1626 {
1627  if ( !item )
1628  {
1629  return;
1630  }
1631 
1632  //model handles changes to z list
1633  mItemsModel->removeItem( item );
1634 }
1635 
1637 {
1639  QList<QgsComposerItem*>::iterator it = selectedItems.begin();
1640  bool itemsRaised = false;
1641  for ( ; it != selectedItems.end(); ++it )
1642  {
1643  itemsRaised = itemsRaised | raiseItem( *it );
1644  }
1645 
1646  if ( !itemsRaised )
1647  {
1648  //no change
1649  return;
1650  }
1651 
1652  //update all positions
1653  updateZValues();
1654  update();
1655 }
1656 
1658 {
1659  //model handles reordering items
1660  return mItemsModel->reorderItemUp( item );
1661 }
1662 
1664 {
1665  return mItemsModel->getComposerItemAbove( item );
1666 }
1667 
1669 {
1670  return mItemsModel->getComposerItemBelow( item );
1671 }
1672 
1674 {
1675  QgsComposerItem* previousSelectedItem = nullptr;
1677  if ( !selectedItems.isEmpty() )
1678  {
1679  previousSelectedItem = selectedItems.at( 0 );
1680  }
1681 
1682  if ( !previousSelectedItem )
1683  {
1684  return;
1685  }
1686 
1687  //select item with target z value
1688  QgsComposerItem* selectedItem = nullptr;
1689  switch ( direction )
1690  {
1692  selectedItem = getComposerItemBelow( previousSelectedItem );
1693  break;
1695  selectedItem = getComposerItemAbove( previousSelectedItem );
1696  break;
1697  }
1698 
1699  if ( !selectedItem )
1700  {
1701  return;
1702  }
1703 
1704  //ok, found a good target item
1705  setAllUnselected();
1706  selectedItem->setSelected( true );
1707  emit selectedItemChanged( selectedItem );
1708 }
1709 
1711 {
1713  QList<QgsComposerItem*>::iterator it = selectedItems.begin();
1714  bool itemsLowered = false;
1715  for ( ; it != selectedItems.end(); ++it )
1716  {
1717  itemsLowered = itemsLowered | lowerItem( *it );
1718  }
1719 
1720  if ( !itemsLowered )
1721  {
1722  //no change
1723  return;
1724  }
1725 
1726  //update all positions
1727  updateZValues();
1728  update();
1729 }
1730 
1732 {
1733  //model handles reordering items
1734  return mItemsModel->reorderItemDown( item );
1735 }
1736 
1738 {
1740  QList<QgsComposerItem*>::iterator it = selectedItems.begin();
1741  bool itemsRaised = false;
1742  for ( ; it != selectedItems.end(); ++it )
1743  {
1744  itemsRaised = itemsRaised | moveItemToTop( *it );
1745  }
1746 
1747  if ( !itemsRaised )
1748  {
1749  //no change
1750  return;
1751  }
1752 
1753  //update all positions
1754  updateZValues();
1755  update();
1756 }
1757 
1759 {
1760  //model handles reordering items
1761  return mItemsModel->reorderItemToTop( item );
1762 }
1763 
1765 {
1767  QList<QgsComposerItem*>::iterator it = selectedItems.begin();
1768  bool itemsLowered = false;
1769  for ( ; it != selectedItems.end(); ++it )
1770  {
1771  itemsLowered = itemsLowered | moveItemToBottom( *it );
1772  }
1773 
1774  if ( !itemsLowered )
1775  {
1776  //no change
1777  return;
1778  }
1779 
1780  //update all positions
1781  updateZValues();
1782  update();
1783 }
1784 
1786 {
1787  //model handles reordering items
1788  return mItemsModel->reorderItemToBottom( item );
1789 }
1790 
1792 {
1794  if ( selectedItems.size() < 2 )
1795  {
1796  return;
1797  }
1798 
1799  QRectF selectedItemBBox;
1800  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1801  {
1802  return;
1803  }
1804 
1805  double minXCoordinate = selectedItemBBox.left();
1806 
1807  //align items left to minimum x coordinate
1808  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items left" ) );
1809  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1810  for ( ; align_it != selectedItems.end(); ++align_it )
1811  {
1812  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1813  subcommand->savePreviousState();
1814  ( *align_it )->setPos( minXCoordinate, ( *align_it )->pos().y() );
1815  subcommand->saveAfterState();
1816  }
1817  mUndoStack->push( parentCommand );
1818  QgsProject::instance()->setDirty( true );
1819 }
1820 
1822 {
1824  if ( selectedItems.size() < 2 )
1825  {
1826  return;
1827  }
1828 
1829  QRectF selectedItemBBox;
1830  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1831  {
1832  return;
1833  }
1834 
1835  double averageXCoord = ( selectedItemBBox.left() + selectedItemBBox.right() ) / 2.0;
1836 
1837  //place items
1838  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items horizontal center" ) );
1839  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1840  for ( ; align_it != selectedItems.end(); ++align_it )
1841  {
1842  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1843  subcommand->savePreviousState();
1844  ( *align_it )->setPos( averageXCoord - ( *align_it )->rect().width() / 2.0, ( *align_it )->pos().y() );
1845  subcommand->saveAfterState();
1846  }
1847  mUndoStack->push( parentCommand );
1848  QgsProject::instance()->setDirty( true );
1849 }
1850 
1852 {
1854  if ( selectedItems.size() < 2 )
1855  {
1856  return;
1857  }
1858 
1859  QRectF selectedItemBBox;
1860  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1861  {
1862  return;
1863  }
1864 
1865  double maxXCoordinate = selectedItemBBox.right();
1866 
1867  //align items right to maximum x coordinate
1868  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items right" ) );
1869  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1870  for ( ; align_it != selectedItems.end(); ++align_it )
1871  {
1872  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1873  subcommand->savePreviousState();
1874  ( *align_it )->setPos( maxXCoordinate - ( *align_it )->rect().width(), ( *align_it )->pos().y() );
1875  subcommand->saveAfterState();
1876  }
1877  mUndoStack->push( parentCommand );
1878  QgsProject::instance()->setDirty( true );
1879 }
1880 
1882 {
1884  if ( selectedItems.size() < 2 )
1885  {
1886  return;
1887  }
1888 
1889  QRectF selectedItemBBox;
1890  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1891  {
1892  return;
1893  }
1894 
1895  double minYCoordinate = selectedItemBBox.top();
1896 
1897  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items top" ) );
1898  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1899  for ( ; align_it != selectedItems.end(); ++align_it )
1900  {
1901  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1902  subcommand->savePreviousState();
1903  ( *align_it )->setPos(( *align_it )->pos().x(), minYCoordinate );
1904  subcommand->saveAfterState();
1905  }
1906  mUndoStack->push( parentCommand );
1907  QgsProject::instance()->setDirty( true );
1908 }
1909 
1911 {
1913  if ( selectedItems.size() < 2 )
1914  {
1915  return;
1916  }
1917 
1918  QRectF selectedItemBBox;
1919  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1920  {
1921  return;
1922  }
1923 
1924  double averageYCoord = ( selectedItemBBox.top() + selectedItemBBox.bottom() ) / 2.0;
1925  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items vertical center" ) );
1926  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1927  for ( ; align_it != selectedItems.end(); ++align_it )
1928  {
1929  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1930  subcommand->savePreviousState();
1931  ( *align_it )->setPos(( *align_it )->pos().x(), averageYCoord - ( *align_it )->rect().height() / 2 );
1932  subcommand->saveAfterState();
1933  }
1934  mUndoStack->push( parentCommand );
1935  QgsProject::instance()->setDirty( true );
1936 }
1937 
1939 {
1941  if ( selectedItems.size() < 2 )
1942  {
1943  return;
1944  }
1945 
1946  QRectF selectedItemBBox;
1947  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1948  {
1949  return;
1950  }
1951 
1952  double maxYCoord = selectedItemBBox.bottom();
1953  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items bottom" ) );
1954  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1955  for ( ; align_it != selectedItems.end(); ++align_it )
1956  {
1957  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1958  subcommand->savePreviousState();
1959  ( *align_it )->setPos(( *align_it )->pos().x(), maxYCoord - ( *align_it )->rect().height() );
1960  subcommand->saveAfterState();
1961  }
1962  mUndoStack->push( parentCommand );
1963  QgsProject::instance()->setDirty( true );
1964 }
1965 
1967 {
1968  QUndoCommand* parentCommand = new QUndoCommand( tr( "Items locked" ) );
1970  QList<QgsComposerItem*>::iterator itemIter = selectionList.begin();
1971  for ( ; itemIter != selectionList.end(); ++itemIter )
1972  {
1973  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *itemIter, "", parentCommand );
1974  subcommand->savePreviousState();
1975  ( *itemIter )->setPositionLock( true );
1976  subcommand->saveAfterState();
1977  }
1978 
1979  setAllUnselected();
1980  mUndoStack->push( parentCommand );
1981  QgsProject::instance()->setDirty( true );
1982 }
1983 
1985 {
1986  //unlock all items in composer
1987 
1988  QUndoCommand* parentCommand = new QUndoCommand( tr( "Items unlocked" ) );
1989 
1990  //first, clear the selection
1991  setAllUnselected();
1992 
1993  QList<QGraphicsItem *> itemList = items();
1994  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
1995  for ( ; itemIt != itemList.end(); ++itemIt )
1996  {
1997  QgsComposerItem* mypItem = dynamic_cast<QgsComposerItem *>( *itemIt );
1998  if ( mypItem && mypItem->positionLock() )
1999  {
2000  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( mypItem, "", parentCommand );
2001  subcommand->savePreviousState();
2002  mypItem->setPositionLock( false );
2003  //select unlocked items, same behaviour as illustrator
2004  mypItem->setSelected( true );
2005  emit selectedItemChanged( mypItem );
2006  subcommand->saveAfterState();
2007  }
2008  }
2009  mUndoStack->push( parentCommand );
2010  QgsProject::instance()->setDirty( true );
2011 }
2012 
2014 {
2015  if ( items.size() < 2 )
2016  {
2017  //not enough items for a group
2018  return nullptr;
2019  }
2020 
2021  QgsComposerItemGroup* itemGroup = new QgsComposerItemGroup( this );
2022  QgsDebugMsg( QString( "itemgroup created with %1 items (%2 to be added)" ) .arg( itemGroup->items().size() ).arg( items.size() ) );
2023 
2024  QList<QgsComposerItem*>::iterator itemIter = items.begin();
2025  for ( ; itemIter != items.end(); ++itemIter )
2026  {
2027  itemGroup->addItem( *itemIter );
2028  QgsDebugMsg( QString( "itemgroup now has %1" )
2029  .arg( itemGroup->items().size() ) );
2030  }
2031 
2032  addItem( itemGroup );
2033 
2035  QObject::connect( c, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SIGNAL( itemRemoved( QgsComposerItem* ) ) );
2036  QObject::connect( c, SIGNAL( itemAdded( QgsComposerItem* ) ), this, SLOT( sendItemAddedSignal( QgsComposerItem* ) ) );
2037 
2038  undoStack()->push( c );
2039  QgsProject::instance()->setDirty( true );
2040  //QgsDebugMsg( QString( "itemgroup after pushAddRemove has %1" ) .arg( itemGroup->items().size() ) );
2041 
2042  emit composerItemGroupAdded( itemGroup );
2043 
2044  return itemGroup;
2045 }
2046 
2048 {
2049  QList<QgsComposerItem *> ungroupedItems;
2050  if ( !group )
2051  {
2052  return ungroupedItems;
2053  }
2054 
2055  // group ownership transferred to QgsGroupUngroupItemsCommand
2056  // Call this before removing group items so it can keep note
2057  // of contents
2059  QObject::connect( c, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SIGNAL( itemRemoved( QgsComposerItem* ) ) );
2060  QObject::connect( c, SIGNAL( itemAdded( QgsComposerItem* ) ), this, SLOT( sendItemAddedSignal( QgsComposerItem* ) ) );
2061 
2062  undoStack()->push( c );
2063  QgsProject::instance()->setDirty( true );
2064 
2065 
2066  QSet<QgsComposerItem*> groupedItems = group->items();
2067  QSet<QgsComposerItem*>::iterator itemIt = groupedItems.begin();
2068  for ( ; itemIt != groupedItems.end(); ++itemIt )
2069  {
2070  ungroupedItems << ( *itemIt );
2071  }
2072 
2073  group->removeItems();
2074 
2075  // note: emits itemRemoved
2076  removeComposerItem( group, false, false );
2077 
2078  return ungroupedItems;
2079 }
2080 
2081 void QgsComposition::updateZValues( const bool addUndoCommands )
2082 {
2083  int counter = mItemsModel->zOrderListSize();
2085  QgsComposerItem* currentItem = nullptr;
2086 
2087  QUndoCommand* parentCommand = nullptr;
2088  if ( addUndoCommands )
2089  {
2090  parentCommand = new QUndoCommand( tr( "Item z-order changed" ) );
2091  }
2092  for ( ; it != mItemsModel->zOrderList()->constEnd(); ++it )
2093  {
2094  currentItem = *it;
2095  if ( currentItem )
2096  {
2097  QgsComposerItemCommand* subcommand = nullptr;
2098  if ( addUndoCommands )
2099  {
2100  subcommand = new QgsComposerItemCommand( *it, "", parentCommand );
2101  subcommand->savePreviousState();
2102  }
2103  currentItem->setZValue( counter );
2104  if ( addUndoCommands )
2105  {
2106  subcommand->saveAfterState();
2107  }
2108  }
2109  --counter;
2110  }
2111  if ( addUndoCommands )
2112  {
2113  mUndoStack->push( parentCommand );
2114  QgsProject::instance()->setDirty( true );
2115  }
2116 }
2117 
2119 {
2120  //model handles changes to item z order list
2121  mItemsModel->rebuildZList();
2122 
2123  //Finally, rebuild the zValue of all items to remove any duplicate zValues and make sure there's
2124  //no missing zValues.
2125  updateZValues( false );
2126 }
2127 
2129 {
2130  if ( !mSnapToGrid || mSnapGridResolution <= 0 || !graphicsView() )
2131  {
2132  return scenePoint;
2133  }
2134 
2135  //y offset to current page
2136  int pageNr = static_cast< int >( scenePoint.y() / ( mPageHeight + mSpaceBetweenPages ) );
2137  double yOffset = pageNr * ( mPageHeight + mSpaceBetweenPages );
2138  double yPage = scenePoint.y() - yOffset; //y-coordinate relative to current page
2139 
2140  //snap x coordinate
2141  int xRatio = static_cast< int >(( scenePoint.x() - mSnapGridOffsetX ) / mSnapGridResolution + 0.5 );
2142  int yRatio = static_cast< int >(( yPage - mSnapGridOffsetY ) / mSnapGridResolution + 0.5 );
2143 
2144  double xSnapped = xRatio * mSnapGridResolution + mSnapGridOffsetX;
2145  double ySnapped = yRatio * mSnapGridResolution + mSnapGridOffsetY + yOffset;
2146 
2147  //convert snap tolerance from pixels to mm
2148  double viewScaleFactor = graphicsView()->transform().m11();
2149  double alignThreshold = mSnapTolerance / viewScaleFactor;
2150 
2151  if ( fabs( xSnapped - scenePoint.x() ) > alignThreshold )
2152  {
2153  //snap distance is outside of tolerance
2154  xSnapped = scenePoint.x();
2155  }
2156  if ( fabs( ySnapped - scenePoint.y() ) > alignThreshold )
2157  {
2158  //snap distance is outside of tolerance
2159  ySnapped = scenePoint.y();
2160  }
2161 
2162  return QPointF( xSnapped, ySnapped );
2163 }
2164 
2166 {
2167  QGraphicsLineItem* item = new QGraphicsLineItem();
2168  QPen linePen( Qt::SolidLine );
2169  linePen.setColor( Qt::red );
2170  // use a pen width of 0, since this activates a cosmetic pen
2171  // which doesn't scale with the composer and keeps a constant size
2172  linePen.setWidthF( 0 );
2173  item->setPen( linePen );
2174  item->setZValue( 100 );
2175  item->setVisible( mGuidesVisible );
2176  addItem( item );
2177  mSnapLines.push_back( item );
2178  return item;
2179 }
2180 
2182 {
2183  removeItem( line );
2184  mSnapLines.removeAll( line );
2185  delete line;
2186 }
2187 
2189 {
2190  Q_FOREACH ( QGraphicsLineItem* line, mSnapLines )
2191  {
2192  removeItem( line );
2193  delete( line );
2194  }
2195  mSnapLines.clear();
2196 }
2197 
2198 void QgsComposition::setSnapLinesVisible( const bool visible )
2199 {
2200  mGuidesVisible = visible;
2201  Q_FOREACH ( QGraphicsLineItem* line, mSnapLines )
2202  {
2203  line->setVisible( visible );
2204  }
2205 }
2206 
2208 {
2209  mPagesVisible = visible;
2210  update();
2211 }
2212 
2213 QGraphicsLineItem* QgsComposition::nearestSnapLine( const bool horizontal, const double x, const double y, const double tolerance,
2215 {
2216  double minSqrDist = DBL_MAX;
2217  QGraphicsLineItem* item = nullptr;
2218  double currentXCoord = 0;
2219  double currentYCoord = 0;
2220  double currentSqrDist = 0;
2221  double sqrTolerance = tolerance * tolerance;
2222 
2223  snappedItems.clear();
2224 
2226  for ( ; it != mSnapLines.constEnd(); ++it )
2227  {
2228  bool itemHorizontal = qgsDoubleNear(( *it )->line().y2() - ( *it )->line().y1(), 0 );
2229  if ( horizontal && itemHorizontal )
2230  {
2231  currentYCoord = ( *it )->line().y1();
2232  currentSqrDist = ( y - currentYCoord ) * ( y - currentYCoord );
2233  }
2234  else if ( !horizontal && !itemHorizontal )
2235  {
2236  currentXCoord = ( *it )->line().x1();
2237  currentSqrDist = ( x - currentXCoord ) * ( x - currentXCoord );
2238  }
2239  else
2240  {
2241  continue;
2242  }
2243 
2244  if ( currentSqrDist < minSqrDist && currentSqrDist < sqrTolerance )
2245  {
2246  item = *it;
2247  minSqrDist = currentSqrDist;
2248  }
2249  }
2250 
2251  double itemTolerance = 0.0000001;
2252  if ( item )
2253  {
2254  //go through all the items to find items snapped to this snap line
2255  QList<QGraphicsItem *> itemList = items();
2256  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
2257  for ( ; itemIt != itemList.end(); ++itemIt )
2258  {
2259  QgsComposerItem* currentItem = dynamic_cast<QgsComposerItem*>( *itemIt );
2260  if ( !currentItem || currentItem->type() == QgsComposerItem::ComposerPaper )
2261  {
2262  continue;
2263  }
2264 
2265  if ( horizontal )
2266  {
2267  if ( qgsDoubleNear( currentYCoord, currentItem->pos().y() + currentItem->rect().top(), itemTolerance ) )
2268  {
2269  snappedItems.append( qMakePair( currentItem, QgsComposerItem::UpperMiddle ) );
2270  }
2271  else if ( qgsDoubleNear( currentYCoord, currentItem->pos().y() + currentItem->rect().center().y(), itemTolerance ) )
2272  {
2273  snappedItems.append( qMakePair( currentItem, QgsComposerItem::Middle ) );
2274  }
2275  else if ( qgsDoubleNear( currentYCoord, currentItem->pos().y() + currentItem->rect().bottom(), itemTolerance ) )
2276  {
2277  snappedItems.append( qMakePair( currentItem, QgsComposerItem::LowerMiddle ) );
2278  }
2279  }
2280  else
2281  {
2282  if ( qgsDoubleNear( currentXCoord, currentItem->pos().x(), itemTolerance ) )
2283  {
2284  snappedItems.append( qMakePair( currentItem, QgsComposerItem::MiddleLeft ) );
2285  }
2286  else if ( qgsDoubleNear( currentXCoord, currentItem->pos().x() + currentItem->rect().center().x(), itemTolerance ) )
2287  {
2288  snappedItems.append( qMakePair( currentItem, QgsComposerItem::Middle ) );
2289  }
2290  else if ( qgsDoubleNear( currentXCoord, currentItem->pos().x() + currentItem->rect().width(), itemTolerance ) )
2291  {
2292  snappedItems.append( qMakePair( currentItem, QgsComposerItem::MiddleRight ) );
2293  }
2294  }
2295  }
2296  }
2297 
2298  return item;
2299 }
2300 
2301 int QgsComposition::boundingRectOfSelectedItems( QRectF& bRect )
2302 {
2304  if ( selectedItems.size() < 1 )
2305  {
2306  return 1;
2307  }
2308 
2309  //set the box to the first item
2310  QgsComposerItem* currentItem = selectedItems.at( 0 );
2311  double minX = currentItem->pos().x();
2312  double minY = currentItem->pos().y();
2313  double maxX = minX + currentItem->rect().width();
2314  double maxY = minY + currentItem->rect().height();
2315 
2316  double currentMinX, currentMinY, currentMaxX, currentMaxY;
2317 
2318  for ( int i = 1; i < selectedItems.size(); ++i )
2319  {
2320  currentItem = selectedItems.at( i );
2321  currentMinX = currentItem->pos().x();
2322  currentMinY = currentItem->pos().y();
2323  currentMaxX = currentMinX + currentItem->rect().width();
2324  currentMaxY = currentMinY + currentItem->rect().height();
2325 
2326  if ( currentMinX < minX )
2327  minX = currentMinX;
2328  if ( currentMaxX > maxX )
2329  maxX = currentMaxX;
2330  if ( currentMinY < minY )
2331  minY = currentMinY;
2332  if ( currentMaxY > maxY )
2333  maxY = currentMaxY;
2334  }
2335 
2336  bRect.setTopLeft( QPointF( minX, minY ) );
2337  bRect.setBottomRight( QPointF( maxX, maxY ) );
2338  return 0;
2339 }
2340 
2342 {
2343  mSnapToGrid = b;
2344  updatePaperItems();
2345 }
2346 
2348 {
2349  mGridVisible = b;
2350  updatePaperItems();
2351 }
2352 
2354 {
2355  mSnapGridResolution = r;
2356  updatePaperItems();
2357 }
2358 
2359 void QgsComposition::setSnapGridOffsetX( const double offset )
2360 {
2361  mSnapGridOffsetX = offset;
2362  updatePaperItems();
2363 }
2364 
2365 void QgsComposition::setSnapGridOffsetY( const double offset )
2366 {
2367  mSnapGridOffsetY = offset;
2368  updatePaperItems();
2369 }
2370 
2372 {
2373  mGridPen = p;
2374  //make sure grid is drawn using a zero-width cosmetic pen
2375  mGridPen.setWidthF( 0 );
2376  updatePaperItems();
2377 }
2378 
2380 {
2381  mGridStyle = s;
2382  updatePaperItems();
2383 }
2384 
2385 void QgsComposition::setBoundingBoxesVisible( const bool boundsVisible )
2386 {
2387  mBoundingBoxesVisible = boundsVisible;
2388 
2389  if ( mSelectionHandles )
2390  {
2391  mSelectionHandles->update();
2392  }
2393 }
2394 
2396 {
2397  //load new composer setting values
2398  loadSettings();
2399  //update any paper items to reflect new settings
2400  updatePaperItems();
2401 }
2402 
2403 void QgsComposition::loadSettings()
2404 {
2405  //read grid style, grid color and pen width from settings
2406  QSettings s;
2407 
2408  QString gridStyleString;
2409  gridStyleString = s.value( "/Composer/gridStyle", "Dots" ).toString();
2410 
2411  int gridRed, gridGreen, gridBlue, gridAlpha;
2412  gridRed = s.value( "/Composer/gridRed", 190 ).toInt();
2413  gridGreen = s.value( "/Composer/gridGreen", 190 ).toInt();
2414  gridBlue = s.value( "/Composer/gridBlue", 190 ).toInt();
2415  gridAlpha = s.value( "/Composer/gridAlpha", 100 ).toInt();
2416  QColor gridColor = QColor( gridRed, gridGreen, gridBlue, gridAlpha );
2417 
2418  mGridPen.setColor( gridColor );
2419  mGridPen.setWidthF( 0 );
2420 
2421  if ( gridStyleString == "Dots" )
2422  {
2423  mGridStyle = Dots;
2424  }
2425  else if ( gridStyleString == "Crosses" )
2426  {
2427  mGridStyle = Crosses;
2428  }
2429  else
2430  {
2431  mGridStyle = Solid;
2432  }
2433 }
2434 
2436 {
2437  delete mActiveItemCommand;
2438  if ( !item )
2439  {
2440  mActiveItemCommand = nullptr;
2441  return;
2442  }
2443 
2445  {
2446  mActiveItemCommand = new QgsComposerItemCommand( item, commandText );
2447  }
2448  else
2449  {
2450  mActiveItemCommand = new QgsComposerMergeCommand( c, item, commandText );
2451  }
2452  mActiveItemCommand->savePreviousState();
2453 }
2454 
2456 {
2457  if ( mActiveItemCommand )
2458  {
2459  mActiveItemCommand->saveAfterState();
2460  if ( mActiveItemCommand->containsChange() ) //protect against empty commands
2461  {
2462  mUndoStack->push( mActiveItemCommand );
2463  QgsProject::instance()->setDirty( true );
2464  }
2465  else
2466  {
2467  delete mActiveItemCommand;
2468  }
2469  mActiveItemCommand = nullptr;
2470  }
2471 }
2472 
2474 {
2475  delete mActiveItemCommand;
2476  mActiveItemCommand = nullptr;
2477 }
2478 
2480 {
2481  delete mActiveMultiFrameCommand;
2482 
2483  if ( !multiFrame )
2484  {
2485  mActiveMultiFrameCommand = nullptr;
2486  return;
2487  }
2488 
2490  {
2491  mActiveMultiFrameCommand = new QgsComposerMultiFrameCommand( multiFrame, text );
2492  }
2493  else
2494  {
2495  mActiveMultiFrameCommand = new QgsComposerMultiFrameMergeCommand( c, multiFrame, text );
2496  }
2497  mActiveMultiFrameCommand->savePreviousState();
2498 }
2499 
2501 {
2502  if ( mActiveMultiFrameCommand )
2503  {
2504  mActiveMultiFrameCommand->saveAfterState();
2505  if ( mActiveMultiFrameCommand->containsChange() )
2506  {
2507  mUndoStack->push( mActiveMultiFrameCommand );
2508  QgsProject::instance()->setDirty( true );
2509  }
2510  else
2511  {
2512  delete mActiveMultiFrameCommand;
2513  }
2514  mActiveMultiFrameCommand = nullptr;
2515  }
2516 }
2517 
2519 {
2520  delete mActiveMultiFrameCommand;
2521  mActiveMultiFrameCommand = nullptr;
2522 }
2523 
2525 {
2526  mMultiFrames.insert( multiFrame );
2527 
2528  updateBounds();
2529 }
2530 
2532 {
2533  mMultiFrames.remove( multiFrame );
2534 
2535  updateBounds();
2536 }
2537 
2539 {
2540  addItem( arrow );
2541 
2542  updateBounds();
2543  connect( arrow, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2544 
2545  emit composerArrowAdded( arrow );
2546 }
2547 
2549 {
2550  addItem( polygon );
2551 
2552  updateBounds();
2553  connect( polygon, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2554 
2555  emit composerPolygonAdded( polygon );
2556 }
2557 
2559 {
2560  addItem( polyline );
2561 
2562  updateBounds();
2563  connect( polyline, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2564 
2565  emit composerPolylineAdded( polyline );
2566 }
2567 
2569 {
2570  addItem( label );
2571 
2572  updateBounds();
2573  connect( label, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2574 
2575  emit composerLabelAdded( label );
2576 }
2577 
2578 void QgsComposition::addComposerMap( QgsComposerMap* map, const bool setDefaultPreviewStyle )
2579 {
2580  addItem( map );
2581  if ( setDefaultPreviewStyle )
2582  {
2583  //set default preview mode to cache. Must be done here between adding composer map to scene and emiting signal
2585  }
2586 
2587  if ( map->previewMode() != QgsComposerMap::Rectangle )
2588  {
2589  map->cache();
2590  }
2591 
2592  updateBounds();
2593  connect( map, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2594 
2595  emit composerMapAdded( map );
2596 }
2597 
2599 {
2600  addItem( scaleBar );
2601 
2602  updateBounds();
2603  connect( scaleBar, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2604 
2605  emit composerScaleBarAdded( scaleBar );
2606 }
2607 
2609 {
2610  addItem( legend );
2611 
2612  updateBounds();
2613  connect( legend, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2614 
2615  emit composerLegendAdded( legend );
2616 }
2617 
2619 {
2620  addItem( picture );
2621 
2622  updateBounds();
2623  connect( picture, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2624 
2625  emit composerPictureAdded( picture );
2626 }
2627 
2629 {
2630  addItem( shape );
2631 
2632  updateBounds();
2633  connect( shape, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2634 
2635  emit composerShapeAdded( shape );
2636 }
2637 
2639 {
2640  addItem( table );
2641 
2642  updateBounds();
2643  connect( table, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2644 
2645  emit composerTableAdded( table );
2646 }
2647 
2649 {
2650  addItem( frame );
2651 
2652  updateBounds();
2653  connect( frame, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2654 
2655  emit composerHtmlFrameAdded( html, frame );
2656 }
2657 
2659 {
2660  addItem( frame );
2661 
2662  updateBounds();
2663  connect( frame, SIGNAL( sizeChanged() ), this, SLOT( updateBounds() ) );
2664 
2665  emit composerTableFrameAdded( table, frame );
2666 }
2667 
2668 /* public */
2669 void QgsComposition::removeComposerItem( QgsComposerItem* item, const bool createCommand, const bool removeGroupItems )
2670 {
2671  QgsComposerMap* map = dynamic_cast<QgsComposerMap *>( item );
2672 
2673  if ( !map || !map->isDrawing() ) //don't delete a composer map while it draws
2674  {
2675  mItemsModel->setItemRemoved( item );
2676  removeItem( item );
2677  emit itemRemoved( item );
2678 
2679  QgsDebugMsg( QString( "removeComposerItem called, createCommand:%1 removeGroupItems:%2" )
2680  .arg( createCommand ).arg( removeGroupItems ) );
2681 
2682  QgsComposerItemGroup* itemGroup = dynamic_cast<QgsComposerItemGroup*>( item );
2683  if ( itemGroup && removeGroupItems )
2684  {
2685  QgsDebugMsg( QString( "itemGroup && removeGroupItems" ) );
2686 
2687  // Takes ownership of itemGroup
2688  QgsAddRemoveItemCommand* parentCommand = new QgsAddRemoveItemCommand(
2689  QgsAddRemoveItemCommand::Removed, itemGroup, this,
2690  tr( "Remove item group" ) );
2691  connectAddRemoveCommandSignals( parentCommand );
2692 
2693  //add add/remove item command for every item in the group
2694  QSet<QgsComposerItem*> groupedItems = itemGroup->items();
2695  QgsDebugMsg( QString( "itemGroup contains %1 items" ) .arg( groupedItems.size() ) );
2696  QSet<QgsComposerItem*>::iterator it = groupedItems.begin();
2697  for ( ; it != groupedItems.end(); ++it )
2698  {
2699  mItemsModel->setItemRemoved( *it );
2700  removeItem( *it );
2701  QgsAddRemoveItemCommand* subcommand = new QgsAddRemoveItemCommand( QgsAddRemoveItemCommand::Removed, *it, this, "", parentCommand );
2702  connectAddRemoveCommandSignals( subcommand );
2703  emit itemRemoved( *it );
2704  }
2705 
2706  undoStack()->push( parentCommand );
2707  }
2708  else
2709  {
2710  bool frameItem = ( item->type() == QgsComposerItem::ComposerFrame );
2711  QgsComposerMultiFrame* multiFrame = nullptr;
2712  if ( createCommand )
2713  {
2714  if ( frameItem ) //multiframe tracks item changes
2715  {
2716  multiFrame = static_cast<QgsComposerFrame*>( item )->multiFrame();
2717  item->beginItemCommand( tr( "Frame deleted" ) );
2718  item->endItemCommand();
2719  }
2720  else
2721  {
2722  pushAddRemoveCommand( item, tr( "Item deleted" ), QgsAddRemoveItemCommand::Removed );
2723  }
2724  }
2725 
2726  //check if there are frames left. If not, remove the multi frame
2727  if ( frameItem && multiFrame )
2728  {
2729  if ( multiFrame->frameCount() < 1 )
2730  {
2731  removeMultiFrame( multiFrame );
2732  if ( createCommand )
2733  {
2735  multiFrame, this, tr( "Multiframe removed" ) );
2736  undoStack()->push( command );
2737  }
2738  else
2739  {
2740  delete multiFrame;
2741  }
2742  }
2743  }
2744  }
2745  }
2746 
2747  updateBounds();
2748 }
2749 
2751 {
2752  QgsAddRemoveItemCommand* c = new QgsAddRemoveItemCommand( state, item, this, text );
2753  connectAddRemoveCommandSignals( c );
2754  undoStack()->push( c );
2755  QgsProject::instance()->setDirty( true );
2756 }
2757 
2758 void QgsComposition::connectAddRemoveCommandSignals( QgsAddRemoveItemCommand* c )
2759 {
2760  if ( !c )
2761  {
2762  return;
2763  }
2764 
2765  QObject::connect( c, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SIGNAL( itemRemoved( QgsComposerItem* ) ) );
2766  QObject::connect( c, SIGNAL( itemAdded( QgsComposerItem* ) ), this, SLOT( sendItemAddedSignal( QgsComposerItem* ) ) );
2767 }
2768 
2770 {
2771  //cast and send proper signal
2772  item->setSelected( true );
2773  QgsComposerArrow* arrow = dynamic_cast<QgsComposerArrow*>( item );
2774  if ( arrow )
2775  {
2776  emit composerArrowAdded( arrow );
2777  emit selectedItemChanged( arrow );
2778  return;
2779  }
2780  QgsComposerLabel* label = dynamic_cast<QgsComposerLabel*>( item );
2781  if ( label )
2782  {
2783  emit composerLabelAdded( label );
2784  emit selectedItemChanged( label );
2785  return;
2786  }
2787  QgsComposerMap* map = dynamic_cast<QgsComposerMap*>( item );
2788  if ( map )
2789  {
2790  emit composerMapAdded( map );
2791  emit selectedItemChanged( map );
2792  return;
2793  }
2794  QgsComposerScaleBar* scalebar = dynamic_cast<QgsComposerScaleBar*>( item );
2795  if ( scalebar )
2796  {
2797  emit composerScaleBarAdded( scalebar );
2798  emit selectedItemChanged( scalebar );
2799  return;
2800  }
2801  QgsComposerLegend* legend = dynamic_cast<QgsComposerLegend*>( item );
2802  if ( legend )
2803  {
2804  emit composerLegendAdded( legend );
2805  emit selectedItemChanged( legend );
2806  return;
2807  }
2808  QgsComposerPicture* picture = dynamic_cast<QgsComposerPicture*>( item );
2809  if ( picture )
2810  {
2811  emit composerPictureAdded( picture );
2812  emit selectedItemChanged( picture );
2813  return;
2814  }
2815  QgsComposerShape* shape = dynamic_cast<QgsComposerShape*>( item );
2816  if ( shape )
2817  {
2818  emit composerShapeAdded( shape );
2819  emit selectedItemChanged( shape );
2820  return;
2821  }
2822  QgsComposerPolygon* polygon = dynamic_cast<QgsComposerPolygon*>( item );
2823  if ( polygon )
2824  {
2825  emit composerPolygonAdded( polygon );
2826  emit selectedItemChanged( polygon );
2827  return;
2828  }
2829  QgsComposerPolyline* polyline = dynamic_cast<QgsComposerPolyline*>( item );
2830  if ( polyline )
2831  {
2832  emit composerPolylineAdded( polyline );
2833  emit selectedItemChanged( polyline );
2834  return;
2835  }
2836  QgsComposerAttributeTable* table = dynamic_cast<QgsComposerAttributeTable*>( item );
2837  if ( table )
2838  {
2839  emit composerTableAdded( table );
2840  emit selectedItemChanged( table );
2841  return;
2842  }
2843  QgsComposerFrame* frame = dynamic_cast<QgsComposerFrame*>( item );
2844  if ( frame )
2845  {
2846  //emit composerFrameAdded( multiframe, frame, );
2847  QgsComposerMultiFrame* mf = frame->multiFrame();
2848  QgsComposerHtml* html = dynamic_cast<QgsComposerHtml*>( mf );
2849  if ( html )
2850  {
2851  emit composerHtmlFrameAdded( html, frame );
2852  }
2853  QgsComposerAttributeTableV2* table = dynamic_cast<QgsComposerAttributeTableV2*>( mf );
2854  if ( table )
2855  {
2856  emit composerTableFrameAdded( table, frame );
2857  }
2858  emit selectedItemChanged( frame );
2859  return;
2860  }
2861  QgsComposerItemGroup* group = dynamic_cast<QgsComposerItemGroup*>( item );
2862  if ( group )
2863  {
2864  emit composerItemGroupAdded( group );
2865  }
2866 }
2867 
2868 void QgsComposition::updatePaperItems()
2869 {
2870  Q_FOREACH ( QgsPaperItem* page, mPages )
2871  {
2872  page->update();
2873  }
2874 }
2875 
2876 void QgsComposition::addPaperItem()
2877 {
2878  double paperHeight = this->paperHeight();
2879  double paperWidth = this->paperWidth();
2880  double currentY = paperHeight * mPages.size() + mPages.size() * mSpaceBetweenPages; //add 10mm visible space between pages
2881  QgsPaperItem* paperItem = new QgsPaperItem( 0, currentY, paperWidth, paperHeight, this ); //default size A4
2882  paperItem->setBrush( Qt::white );
2883  addItem( paperItem );
2884  paperItem->setZValue( 0 );
2885  mPages.push_back( paperItem );
2886 }
2887 
2888 void QgsComposition::removePaperItems()
2889 {
2890  qDeleteAll( mPages );
2891  mPages.clear();
2892 }
2893 
2894 void QgsComposition::deleteAndRemoveMultiFrames()
2895 {
2896  qDeleteAll( mMultiFrames );
2897  mMultiFrames.clear();
2898 }
2899 
2900 void QgsComposition::beginPrintAsPDF( QPrinter& printer, const QString& file )
2901 {
2902  printer.setOutputFileName( file );
2903  // setOutputFormat should come after setOutputFileName, which auto-sets format to QPrinter::PdfFormat.
2904  // [LS] This should be QPrinter::NativeFormat for Mac, otherwise fonts are not embed-able
2905  // and text is not searchable; however, there are several bugs with <= Qt 4.8.5, 5.1.1, 5.2.0:
2906  // https://bugreports.qt-project.org/browse/QTBUG-10094 - PDF font embedding fails
2907  // https://bugreports.qt-project.org/browse/QTBUG-33583 - PDF output converts text to outline
2908  // Also an issue with PDF paper size using QPrinter::NativeFormat on Mac (always outputs portrait letter-size)
2909  printer.setOutputFormat( QPrinter::PdfFormat );
2910 
2911  refreshPageSize();
2912  //must set orientation to portrait before setting paper size, otherwise size will be flipped
2913  //for landscape sized outputs (#11352)
2914  printer.setOrientation( QPrinter::Portrait );
2915  printer.setPaperSize( QSizeF( paperWidth(), paperHeight() ), QPrinter::Millimeter );
2916 
2917  // TODO: add option for this in Composer
2918  // May not work on Windows or non-X11 Linux. Works fine on Mac using QPrinter::NativeFormat
2919  //printer.setFontEmbeddingEnabled( true );
2920 
2922 }
2923 
2925 {
2926  QPrinter printer;
2927  beginPrintAsPDF( printer, file );
2928  return print( printer );
2929 }
2930 
2932  const QRectF& exportRegion, double dpi ) const
2933 {
2934  if ( dpi < 0 )
2935  dpi = printResolution();
2936 
2937  double* t = computeGeoTransform( map, exportRegion, dpi );
2938  if ( !t )
2939  return;
2940 
2941  // important - we need to manually specify the DPI in advance, as GDAL will otherwise
2942  // assume a DPI of 150
2943  CPLSetConfigOption( "GDAL_PDF_DPI", QString::number( dpi ).toLocal8Bit().constData() );
2944  GDALDatasetH outputDS = GDALOpen( file.toLocal8Bit().constData(), GA_Update );
2945  if ( outputDS )
2946  {
2947  GDALSetGeoTransform( outputDS, t );
2948 #if 0
2949  //TODO - metadata can be set here, eg:
2950  GDALSetMetadataItem( outputDS, "AUTHOR", "me", nullptr );
2951 #endif
2952  GDALSetProjection( outputDS, mMapSettings.destinationCrs().toWkt().toLocal8Bit().constData() );
2953  GDALClose( outputDS );
2954  }
2955  CPLSetConfigOption( "GDAL_PDF_DPI", nullptr );
2956  delete[] t;
2957 }
2958 
2959 void QgsComposition::doPrint( QPrinter& printer, QPainter& p, bool startNewPage )
2960 {
2961  if ( ddPageSizeActive() )
2962  {
2963  //set the page size again so that data defined page size takes effect
2964  refreshPageSize();
2965  //must set orientation to portrait before setting paper size, otherwise size will be flipped
2966  //for landscape sized outputs (#11352)
2967  printer.setOrientation( QPrinter::Portrait );
2968  printer.setPaperSize( QSizeF( paperWidth(), paperHeight() ), QPrinter::Millimeter );
2969  }
2970 
2971  //QgsComposition starts page numbering at 0
2972  int fromPage = ( printer.fromPage() < 1 ) ? 0 : printer.fromPage() - 1;
2973  int toPage = ( printer.toPage() < 1 ) ? numPages() - 1 : printer.toPage() - 1;
2974 
2975  bool pageExported = false;
2976  if ( mPrintAsRaster )
2977  {
2978  for ( int i = fromPage; i <= toPage; ++i )
2979  {
2980  if ( !shouldExportPage( i + 1 ) )
2981  {
2982  continue;
2983  }
2984  if (( pageExported && i > fromPage ) || startNewPage )
2985  {
2986  printer.newPage();
2987  }
2988 
2989  QImage image = printPageAsRaster( i );
2990  if ( !image.isNull() )
2991  {
2992  QRectF targetArea( 0, 0, image.width(), image.height() );
2993  p.drawImage( targetArea, image, targetArea );
2994  }
2995  pageExported = true;
2996  }
2997  }
2998 
2999  if ( !mPrintAsRaster )
3000  {
3001  for ( int i = fromPage; i <= toPage; ++i )
3002  {
3003  if ( !shouldExportPage( i + 1 ) )
3004  {
3005  continue;
3006  }
3007  if (( pageExported && i > fromPage ) || startNewPage )
3008  {
3009  printer.newPage();
3010  }
3011  renderPage( &p, i );
3012  pageExported = true;
3013  }
3014  }
3015 }
3016 
3017 void QgsComposition::beginPrint( QPrinter &printer, const bool evaluateDDPageSize )
3018 {
3019  //set resolution based on composer setting
3020  printer.setFullPage( true );
3021  printer.setColorMode( QPrinter::Color );
3022 
3023  //set user-defined resolution
3024  printer.setResolution( printResolution() );
3025 
3026  if ( evaluateDDPageSize && ddPageSizeActive() )
3027  {
3028  //set data defined page size
3029  refreshPageSize();
3030  //must set orientation to portrait before setting paper size, otherwise size will be flipped
3031  //for landscape sized outputs (#11352)
3032  printer.setOrientation( QPrinter::Portrait );
3033  printer.setPaperSize( QSizeF( paperWidth(), paperHeight() ), QPrinter::Millimeter );
3034  }
3035 }
3036 
3037 bool QgsComposition::print( QPrinter &printer, const bool evaluateDDPageSize )
3038 {
3039  beginPrint( printer, evaluateDDPageSize );
3040  QPainter p;
3041  bool ready = p.begin( &printer );
3042  if ( !ready )
3043  {
3044  //error beginning print
3045  return false;
3046  }
3047  doPrint( printer, p );
3048  p.end();
3049  return true;
3050 }
3051 
3052 QImage QgsComposition::printPageAsRaster( int page, QSize imageSize, int dpi )
3053 {
3054  int resolution = mPrintResolution;
3055  if ( imageSize.isValid() )
3056  {
3057  //output size in pixels specified, calculate resolution using average of
3058  //derived x/y dpi
3059  resolution = ( imageSize.width() / mPageWidth
3060  + imageSize.height() / mPageHeight ) / 2.0 * 25.4;
3061  }
3062  else if ( dpi > 0 )
3063  {
3064  //dpi overridden by function parameters
3065  resolution = dpi;
3066  }
3067 
3068  int width = imageSize.isValid() ? imageSize.width()
3069  : static_cast< int >( resolution * mPageWidth / 25.4 );
3070  int height = imageSize.isValid() ? imageSize.height()
3071  : static_cast< int >( resolution * mPageHeight / 25.4 );
3072 
3073  QImage image( QSize( width, height ), QImage::Format_ARGB32 );
3074  if ( !image.isNull() )
3075  {
3076  image.setDotsPerMeterX( resolution / 25.4 * 1000 );
3077  image.setDotsPerMeterY( resolution / 25.4 * 1000 );
3078  image.fill( 0 );
3079  QPainter imagePainter( &image );
3080  renderPage( &imagePainter, page );
3081  if ( !imagePainter.isActive() ) return QImage();
3082  }
3083  return image;
3084 }
3085 
3086 QImage QgsComposition::renderRectAsRaster( const QRectF& rect, QSize imageSize, int dpi )
3087 {
3088  int resolution = mPrintResolution;
3089  if ( imageSize.isValid() )
3090  {
3091  //output size in pixels specified, calculate resolution using average of
3092  //derived x/y dpi
3093  resolution = ( imageSize.width() / rect.width()
3094  + imageSize.height() / rect.height() ) / 2.0 * 25.4;
3095  }
3096  else if ( dpi > 0 )
3097  {
3098  //dpi overridden by function parameters
3099  resolution = dpi;
3100  }
3101 
3102  int width = imageSize.isValid() ? imageSize.width()
3103  : static_cast< int >( resolution * rect.width() / 25.4 );
3104  int height = imageSize.isValid() ? imageSize.height()
3105  : static_cast< int >( resolution * rect.height() / 25.4 );
3106 
3107  QImage image( QSize( width, height ), QImage::Format_ARGB32 );
3108  if ( !image.isNull() )
3109  {
3110  image.setDotsPerMeterX( resolution / 25.4 * 1000 );
3111  image.setDotsPerMeterY( resolution / 25.4 * 1000 );
3112  image.fill( Qt::transparent );
3113  QPainter imagePainter( &image );
3114  renderRect( &imagePainter, rect );
3115  if ( !imagePainter.isActive() ) return QImage();
3116  }
3117  return image;
3118 }
3119 
3121 {
3122  if ( mPages.size() <= page )
3123  {
3124  return;
3125  }
3126 
3127  QgsPaperItem* paperItem = mPages.at( page );
3128  if ( !paperItem )
3129  {
3130  return;
3131  }
3132 
3133  QRectF paperRect = QRectF( paperItem->pos().x(), paperItem->pos().y(), paperItem->rect().width(), paperItem->rect().height() );
3134  renderRect( p, paperRect );
3135 }
3136 
3138 {
3139  QPaintDevice* paintDevice = p->device();
3140  if ( !paintDevice )
3141  {
3142  return;
3143  }
3144 
3145  QgsComposition::PlotStyle savedPlotStyle = mPlotStyle;
3146  mPlotStyle = QgsComposition::Print;
3147 
3148  setSnapLinesVisible( false );
3149  //hide background before rendering
3150  setBackgroundBrush( Qt::NoBrush );
3151  render( p, QRectF( 0, 0, paintDevice->width(), paintDevice->height() ), rect );
3152  //show background after rendering
3153  setBackgroundBrush( QColor( 215, 215, 215 ) );
3154  setSnapLinesVisible( true );
3155 
3156  mPlotStyle = savedPlotStyle;
3157 }
3158 
3159 double* QgsComposition::computeGeoTransform( const QgsComposerMap* map, const QRectF& region , double dpi ) const
3160 {
3161  if ( !map )
3162  map = worldFileMap();
3163 
3164  if ( !map )
3165  return nullptr;
3166 
3167  if ( dpi < 0 )
3168  dpi = printResolution();
3169 
3170  // calculate region of composition to export (in mm)
3171  QRectF exportRegion = region;
3172  if ( !exportRegion.isValid() )
3173  {
3174  int pageNumber = map->page() - 1;
3175  double pageY = pageNumber * ( mPageHeight + mSpaceBetweenPages );
3176  exportRegion = QRectF( 0, pageY, mPageWidth, mPageHeight );
3177  }
3178 
3179  // map rectangle (in mm)
3180  QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
3181 
3182  // destination width/height in mm
3183  double outputHeightMM = exportRegion.height();
3184  double outputWidthMM = exportRegion.width();
3185 
3186  // map properties
3187  QgsRectangle mapExtent = *map->currentMapExtent();
3188  double mapXCenter = mapExtent.center().x();
3189  double mapYCenter = mapExtent.center().y();
3190  double alpha = - map->mapRotation() / 180 * M_PI;
3191  double sinAlpha = sin( alpha );
3192  double cosAlpha = cos( alpha );
3193 
3194  // get the extent (in map units) for the exported region
3195  QPointF mapItemPos = map->pos();
3196  //adjust item position so it is relative to export region
3197  mapItemPos.rx() -= exportRegion.left();
3198  mapItemPos.ry() -= exportRegion.top();
3199 
3200  // calculate extent of entire page in map units
3201  double xRatio = mapExtent.width() / mapItemSceneRect.width();
3202  double yRatio = mapExtent.height() / mapItemSceneRect.height();
3203  double xmin = mapExtent.xMinimum() - mapItemPos.x() * xRatio;
3204  double ymax = mapExtent.yMaximum() + mapItemPos.y() * yRatio;
3205  QgsRectangle paperExtent( xmin, ymax - outputHeightMM * yRatio, xmin + outputWidthMM * xRatio, ymax );
3206 
3207  // calculate origin of page
3208  double X0 = paperExtent.xMinimum();
3209  double Y0 = paperExtent.yMaximum();
3210 
3211  if ( !qgsDoubleNear( alpha, 0.0 ) )
3212  {
3213  // translate origin to account for map rotation
3214  double X1 = X0 - mapXCenter;
3215  double Y1 = Y0 - mapYCenter;
3216  double X2 = X1 * cosAlpha + Y1 * sinAlpha;
3217  double Y2 = -X1 * sinAlpha + Y1 * cosAlpha;
3218  X0 = X2 + mapXCenter;
3219  Y0 = Y2 + mapYCenter;
3220  }
3221 
3222  // calculate scaling of pixels
3223  int pageWidthPixels = static_cast< int >( dpi * outputWidthMM / 25.4 );
3224  int pageHeightPixels = static_cast< int >( dpi * outputHeightMM / 25.4 );
3225  double pixelWidthScale = paperExtent.width() / pageWidthPixels;
3226  double pixelHeightScale = paperExtent.height() / pageHeightPixels;
3227 
3228  // transform matrix
3229  double* t = new double[6];
3230  t[0] = X0;
3231  t[1] = cosAlpha * pixelWidthScale;
3232  t[2] = -sinAlpha * pixelWidthScale;
3233  t[3] = Y0;
3234  t[4] = -sinAlpha * pixelHeightScale;
3235  t[5] = -cosAlpha * pixelHeightScale;
3236 
3237  return t;
3238 }
3239 
3240 QString QgsComposition::encodeStringForXML( const QString& str )
3241 {
3242  QString modifiedStr( str );
3243  modifiedStr.replace( '&', "&amp;" );
3244  modifiedStr.replace( '\"', "&quot;" );
3245  modifiedStr.replace( '\'', "&apos;" );
3246  modifiedStr.replace( '<', "&lt;" );
3247  modifiedStr.replace( '>', "&gt;" );
3248  return modifiedStr;
3249 }
3250 
3251 QGraphicsView *QgsComposition::graphicsView() const
3252 {
3253  //try to find current view attached to composition
3254  QList<QGraphicsView*> viewList = views();
3255  if ( !viewList.isEmpty() )
3256  {
3257  return viewList.at( 0 );
3258  }
3259 
3260  //no view attached to composition
3261  return nullptr;
3262 }
3263 
3264 void QgsComposition::computeWorldFileParameters( double& a, double& b, double& c, double& d, double& e, double& f ) const
3265 {
3266  const QgsComposerMap* map = worldFileMap();
3267  if ( !map )
3268  {
3269  return;
3270  }
3271 
3272  int pageNumber = map->page() - 1;
3273  double pageY = pageNumber * ( mPageHeight + mSpaceBetweenPages );
3274  QRectF pageRect( 0, pageY, mPageWidth, mPageHeight );
3275  computeWorldFileParameters( pageRect, a, b, c, d, e, f );
3276 }
3277 
3278 void QgsComposition::computeWorldFileParameters( const QRectF& exportRegion, double& a, double& b, double& c, double& d, double& e, double& f ) const
3279 {
3280  // World file parameters : affine transformation parameters from pixel coordinates to map coordinates
3281  QgsComposerMap* map = worldFileMap();
3282  if ( !map )
3283  {
3284  return;
3285  }
3286 
3287  double destinationHeight = exportRegion.height();
3288  double destinationWidth = exportRegion.width();
3289 
3290  QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
3291  QgsRectangle mapExtent = *map->currentMapExtent();
3292 
3293  double alpha = map->mapRotation() / 180 * M_PI;
3294 
3295  double xRatio = mapExtent.width() / mapItemSceneRect.width();
3296  double yRatio = mapExtent.height() / mapItemSceneRect.height();
3297 
3298  double xCenter = mapExtent.center().x();
3299  double yCenter = mapExtent.center().y();
3300 
3301  // get the extent (in map units) for the region
3302  QPointF mapItemPos = map->pos();
3303  //adjust item position so it is relative to export region
3304  mapItemPos.rx() -= exportRegion.left();
3305  mapItemPos.ry() -= exportRegion.top();
3306 
3307  double xmin = mapExtent.xMinimum() - mapItemPos.x() * xRatio;
3308  double ymax = mapExtent.yMaximum() + mapItemPos.y() * yRatio;
3309  QgsRectangle paperExtent( xmin, ymax - destinationHeight * yRatio, xmin + destinationWidth * xRatio, ymax );
3310 
3311  double X0 = paperExtent.xMinimum();
3312  double Y0 = paperExtent.yMinimum();
3313 
3314  int widthPx = static_cast< int >( printResolution() * destinationWidth / 25.4 );
3315  int heightPx = static_cast< int >( printResolution() * destinationHeight / 25.4 );
3316 
3317  double Ww = paperExtent.width() / widthPx;
3318  double Hh = paperExtent.height() / heightPx;
3319 
3320  // scaling matrix
3321  double s[6];
3322  s[0] = Ww;
3323  s[1] = 0;
3324  s[2] = X0;
3325  s[3] = 0;
3326  s[4] = -Hh;
3327  s[5] = Y0 + paperExtent.height();
3328 
3329  // rotation matrix
3330  double r[6];
3331  r[0] = cos( alpha );
3332  r[1] = -sin( alpha );
3333  r[2] = xCenter * ( 1 - cos( alpha ) ) + yCenter * sin( alpha );
3334  r[3] = sin( alpha );
3335  r[4] = cos( alpha );
3336  r[5] = - xCenter * sin( alpha ) + yCenter * ( 1 - cos( alpha ) );
3337 
3338  // result = rotation x scaling = rotation(scaling(X))
3339  a = r[0] * s[0] + r[1] * s[3];
3340  b = r[0] * s[1] + r[1] * s[4];
3341  c = r[0] * s[2] + r[1] * s[5] + r[2];
3342  d = r[3] * s[0] + r[4] * s[3];
3343  e = r[3] * s[1] + r[4] * s[4];
3344  f = r[3] * s[2] + r[4] * s[5] + r[5];
3345 }
3346 
3348 {
3349  mAtlasMode = mode;
3350 
3351  if ( mode == QgsComposition::AtlasOff )
3352  {
3353  mAtlasComposition.endRender();
3354  }
3355  else
3356  {
3357  bool atlasHasFeatures = mAtlasComposition.beginRender();
3358  if ( ! atlasHasFeatures )
3359  {
3360  mAtlasMode = QgsComposition::AtlasOff;
3361  mAtlasComposition.endRender();
3362  return false;
3363  }
3364  }
3365 
3366  update();
3367  return true;
3368 }
3369 
3370 bool QgsComposition::ddPageSizeActive() const
3371 {
3372  //check if any data defined page settings are active
3373  return dataDefinedActive( QgsComposerObject::PresetPaperSize, &mDataDefinedProperties ) ||
3374  dataDefinedActive( QgsComposerObject::PaperWidth, &mDataDefinedProperties ) ||
3375  dataDefinedActive( QgsComposerObject::PaperHeight, &mDataDefinedProperties ) ||
3376  dataDefinedActive( QgsComposerObject::PaperOrientation, &mDataDefinedProperties );
3377 }
3378 
3379 void QgsComposition::refreshPageSize( const QgsExpressionContext* context )
3380 {
3381  const QgsExpressionContext* evalContext = context;
3383  if ( !evalContext )
3384  {
3385  scopedContext.reset( createExpressionContext() );
3386  evalContext = scopedContext.data();
3387  }
3388 
3389  double pageWidth = mPageWidth;
3390  double pageHeight = mPageHeight;
3391 
3392  QVariant exprVal;
3393  //in order of precedence - first consider predefined page size
3394  if ( dataDefinedEvaluate( QgsComposerObject::PresetPaperSize, exprVal, *evalContext, &mDataDefinedProperties ) )
3395  {
3396  QString presetString = exprVal.toString().trimmed();
3397  QgsDebugMsg( QString( "exprVal Paper Preset size :%1" ).arg( presetString ) );
3398  double widthD = 0;
3399  double heightD = 0;
3400  if ( QgsComposerUtils::decodePresetPaperSize( presetString, widthD, heightD ) )
3401  {
3402  pageWidth = widthD;
3403  pageHeight = heightD;
3404  }
3405  }
3406 
3407  //which is overwritten by data defined width/height
3408  if ( dataDefinedEvaluate( QgsComposerObject::PaperWidth, exprVal, *evalContext, &mDataDefinedProperties ) )
3409  {
3410  bool ok;
3411  double widthD = exprVal.toDouble( &ok );
3412  QgsDebugMsg( QString( "exprVal Paper Width:%1" ).arg( widthD ) );
3413  if ( ok )
3414  {
3415  pageWidth = widthD;
3416  }
3417  }
3418  if ( dataDefinedEvaluate( QgsComposerObject::PaperHeight, exprVal, *evalContext, &mDataDefinedProperties ) )
3419  {
3420  bool ok;
3421  double heightD = exprVal.toDouble( &ok );
3422  QgsDebugMsg( QString( "exprVal Paper Height:%1" ).arg( heightD ) );
3423  if ( ok )
3424  {
3425  pageHeight = heightD;
3426  }
3427  }
3428 
3429  //which is finally overwritten by data defined orientation
3430  if ( dataDefinedEvaluate( QgsComposerObject::PaperOrientation, exprVal, *evalContext, &mDataDefinedProperties ) )
3431  {
3432  bool ok;
3433  QString orientationString = exprVal.toString().trimmed();
3434  QgsComposition::PaperOrientation orientation = QgsComposerUtils::decodePaperOrientation( orientationString, ok );
3435  QgsDebugMsg( QString( "exprVal Paper Orientation:%1" ).arg( orientationString ) );
3436  if ( ok )
3437  {
3438  double heightD, widthD;
3439  if ( orientation == QgsComposition::Portrait )
3440  {
3441  heightD = qMax( pageHeight, pageWidth );
3442  widthD = qMin( pageHeight, pageWidth );
3443  }
3444  else
3445  {
3446  heightD = qMin( pageHeight, pageWidth );
3447  widthD = qMax( pageHeight, pageWidth );
3448  }
3449  pageWidth = widthD;
3450  pageHeight = heightD;
3451  }
3452  }
3453 
3454  setPaperSize( pageWidth, pageHeight );
3455 }
3456 
3458 {
3459  if ( property == QgsComposerObject::AllProperties || property == QgsComposerObject::NoProperty )
3460  {
3461  //invalid property
3462  return nullptr;
3463  }
3464 
3465  //find matching QgsDataDefined for property
3467  if ( it != mDataDefinedProperties.constEnd() )
3468  {
3469  return it.value();
3470  }
3471 
3472  //not found
3473  return nullptr;
3474 }
3475 
3476 void QgsComposition::setDataDefinedProperty( const QgsComposerObject::DataDefinedProperty property, bool active, bool useExpression, const QString &expression, const QString &field )
3477 {
3478  if ( property == QgsComposerObject::AllProperties || property == QgsComposerObject::NoProperty )
3479  {
3480  //invalid property
3481  return;
3482  }
3483 
3484  bool defaultVals = ( !active && !useExpression && expression.isEmpty() && field.isEmpty() );
3485 
3486  if ( mDataDefinedProperties.contains( property ) )
3487  {
3489  if ( it != mDataDefinedProperties.constEnd() )
3490  {
3491  QgsDataDefined* dd = it.value();
3492  dd->setActive( active );
3493  dd->setExpressionString( expression );
3494  dd->setField( field );
3495  dd->setUseExpression( useExpression );
3496  }
3497  }
3498  else if ( !defaultVals )
3499  {
3500  QgsDataDefined* dd = new QgsDataDefined( active, useExpression, expression, field );
3501  mDataDefinedProperties.insert( property, dd );
3502  }
3503 }
3504 
3505 void QgsComposition::setCustomProperty( const QString& key, const QVariant& value )
3506 {
3507  mCustomProperties.setValue( key, value );
3508 }
3509 
3510 QVariant QgsComposition::customProperty( const QString& key, const QVariant& defaultValue ) const
3511 {
3512  return mCustomProperties.value( key, defaultValue );
3513 }
3514 
3516 {
3517  mCustomProperties.remove( key );
3518 }
3519 
3521 {
3522  return mCustomProperties.keys();
3523 }
3524 
3525 bool QgsComposition::dataDefinedEvaluate( QgsComposerObject::DataDefinedProperty property, QVariant &expressionValue,
3526  const QgsExpressionContext& context,
3528 {
3530  {
3531  //invalid property
3532  return false;
3533  }
3534 
3535  //null passed-around QVariant
3536  expressionValue.clear();
3537 
3538  //get fields and feature from atlas
3539  QgsFeature currentFeature;
3540  QgsFields layerFields;
3541  bool useFeature = false;
3542  if ( mAtlasComposition.enabled() )
3543  {
3544  QgsVectorLayer* atlasLayer = mAtlasComposition.coverageLayer();
3545  if ( atlasLayer )
3546  {
3547  layerFields = atlasLayer->fields();
3548  }
3549  if ( mAtlasMode != QgsComposition::AtlasOff )
3550  {
3551  useFeature = true;
3552  currentFeature = mAtlasComposition.feature();
3553  }
3554  }
3555 
3556  //evaluate data defined property using current atlas context
3557  QVariant result = dataDefinedValue( property, useFeature ? &currentFeature : nullptr, layerFields, context, dataDefinedProperties );
3558 
3559  if ( result.isValid() )
3560  {
3561  expressionValue = result;
3562  return true;
3563  }
3564 
3565  return false;
3566 }
3567 
3568 bool QgsComposition::dataDefinedActive( const QgsComposerObject::DataDefinedProperty property, const QMap<QgsComposerObject::DataDefinedProperty, QgsDataDefined *> *dataDefinedProperties ) const
3569 {
3571  {
3572  //invalid property
3573  return false;
3574  }
3575  if ( !dataDefinedProperties->contains( property ) )
3576  {
3577  //missing property
3578  return false;
3579  }
3580 
3581  QgsDataDefined* dd = nullptr;
3583  if ( it != dataDefinedProperties->constEnd() )
3584  {
3585  dd = it.value();
3586  }
3587 
3588  if ( !dd )
3589  {
3590  return false;
3591  }
3592 
3593  //found the data defined property, return whether it is active
3594  return dd->isActive();
3595 }
3596 
3597 QVariant QgsComposition::dataDefinedValue( QgsComposerObject::DataDefinedProperty property, const QgsFeature *feature, const QgsFields& fields, const QgsExpressionContext& context, QMap<QgsComposerObject::DataDefinedProperty, QgsDataDefined *> *dataDefinedProperties ) const
3598 {
3600  {
3601  //invalid property
3602  return QVariant();
3603  }
3604  if ( !dataDefinedProperties->contains( property ) )
3605  {
3606  //missing property
3607  return QVariant();
3608  }
3609 
3610  QgsDataDefined* dd = nullptr;
3612  if ( it != dataDefinedProperties->constEnd() )
3613  {
3614  dd = it.value();
3615  }
3616 
3617  if ( !dd )
3618  {
3619  return QVariant();
3620  }
3621 
3622  if ( !dd->isActive() )
3623  {
3624  return QVariant();
3625  }
3626 
3627  QVariant result = QVariant();
3628  bool useExpression = dd->useExpression();
3629  QString field = dd->field();
3630 
3631  if ( !dd->expressionIsPrepared() )
3632  {
3633  prepareDataDefinedExpression( dd, dataDefinedProperties, context );
3634  }
3635 
3636  if ( useExpression && dd->expressionIsPrepared() )
3637  {
3638  QgsExpression* expr = dd->expression();
3639 
3640  result = expr->evaluate( &context );
3641  if ( expr->hasEvalError() )
3642  {
3643  QgsDebugMsgLevel( QString( "Evaluate error:" ) + expr->evalErrorString(), 4 );
3644  return QVariant();
3645  }
3646  }
3647  else if ( !useExpression && !field.isEmpty() )
3648  {
3649  if ( !feature )
3650  {
3651  return QVariant();
3652  }
3653  // use direct attribute access instead of evaluating "field" expression (much faster)
3654  int indx = fields.indexFromName( field );
3655  if ( indx != -1 )
3656  {
3657  result = feature->attribute( indx );
3658  }
3659  }
3660  return result;
3661 }
3662 
3663 void QgsComposition::prepareDataDefinedExpression( QgsDataDefined *dd, QMap<QgsComposerObject::DataDefinedProperty, QgsDataDefined *> *dataDefinedProperties,
3664  const QgsExpressionContext& context ) const
3665 {
3666  //if specific QgsDataDefined passed, prepare it
3667  //otherwise prepare all QgsDataDefineds
3668  if ( dd )
3669  {
3670  dd->prepareExpression( context );
3671  }
3672  else
3673  {
3675  for ( ; it != dataDefinedProperties->constEnd(); ++it )
3676  {
3677  it.value()->prepareExpression( context );
3678  }
3679  }
3680 }
3681 
3683 {
3684  QgsExpressionContext* context = new QgsExpressionContext();
3688  if ( mAtlasComposition.enabled() )
3689  {
3690  context->appendScope( QgsExpressionContextUtils::atlasScope( &mAtlasComposition ) );
3691  }
3692  return context;
3693 }
3694 
3695 void QgsComposition::prepareAllDataDefinedExpressions()
3696 {
3698  prepareDataDefinedExpression( nullptr, &mDataDefinedProperties, *context.data() );
3699 }
3700 
3701 void QgsComposition::relativeResizeRect( QRectF& rectToResize, const QRectF& boundsBefore, const QRectF& boundsAfter )
3702 {
3703  QgsComposerUtils::relativeResizeRect( rectToResize, boundsBefore, boundsAfter );
3704 }
3705 
3706 double QgsComposition::relativePosition( double position, double beforeMin, double beforeMax, double afterMin, double afterMax )
3707 {
3708  return QgsComposerUtils::relativePosition( position, beforeMin, beforeMax, afterMin, afterMax );
3709 }
bool positionLock() const
Returns whether position lock for mouse drags is enabled returns true if item is locked for mouse mov...
Class for parsing and evaluation of expressions (formerly called "search strings").
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:40
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
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)
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
double paperWidth() const
Width of paper item.
bool isValid() const
void setDotsPerMeterX(int x)
void setDotsPerMeterY(int y)
bool writeXML(QDomElement &composerElem, QDomDocument &doc)
Writes settings to xml (paper dimension)
virtual bool writeXML(QDomElement &elem, QDomDocument &doc) const
Stores item state in DOM element.
void setAllUnselected()
Clears any selected items in the composition.
Composer item for polylines.
int pageNumberAt(QPointF position) const
Returns the page number (0-based) given a coordinate.
QgsComposerItem * getComposerItemAbove(QgsComposerItem *item) const
Finds the next composer item above an item.
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
int width() const
A container class for data source field mapping or expression.
bool end()
bool contains(const Key &key) const
Q_DECL_DEPRECATED QVariant evaluate(const QgsFeature *f)
Evaluate the feature and return the result.
An item that draws an arrow between to points.
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;...
void render(QPainter *painter, const QRectF &target, const QRectF &source, Qt::AspectRatioMode aspectRatioMode)
void setResolution(int dpi)
QDomNode appendChild(const QDomNode &newChild)
void addItemToZList(QgsComposerItem *item)
Adds item to z list.
void 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.
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 field() const
Get the field which this QgsDataDefined represents.
QString attribute(const QString &name, const QString &defValue) const
virtual void beginItemCommand(const QString &text)
QPointF snapPointToGrid(QPointF scenePoint) const
Snaps a scene coordinate point to grid.
void computeWorldFileParameters(double &a, double &b, double &c, double &d, double &e, double &f) const
Compute world file parameters.
void setResizeToContentsMargins(double marginTop, double marginRight, double marginBottom, double marginLeft)
Sets the resize to contents margins.
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:197
GridStyle
Style to draw the snapping grid.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QgsFields fields() const
Returns the list of fields of this layer.
QList< QGraphicsItem * > items() const
void alignSelectedItemsTop()
void clear()
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.
const QgsMapSettings & mapSettings() const
Return setting of QGIS map canvas.
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
static QgsFillSymbolV2 * createSimple(const QgsStringMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties. ...
void cache()
Create cache image.
int zOrderListSize() const
Returns the size of the z-order list, which includes items which may have been removed from the compo...
const_iterator constBegin() const
const T & at(int i) const
QgsComposerItemGroup * groupItems(QList< QgsComposerItem * > items)
Creates a new group from a list of composer items and adds it to the composition. ...
static void readDataDefinedPropertyMap(const QDomElement &itemElem, QMap< QgsComposerObject::DataDefinedProperty, QString > *dataDefinedNames, QMap< QgsComposerObject::DataDefinedProperty, QgsDataDefined * > *dataDefinedProperties)
Reads all data defined properties from xml.
QList< const QgsComposerMap * > composerMapItems() const
Returns pointers to all composer maps in the scene.
void addComposerScaleBar(QgsComposerScaleBar *scaleBar)
Adds scale bar to the graphics scene and advises composer to create a widget for it (through signal) ...
QString toWkt() const
Returns a WKT representation of this CRS.
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...
void removeItemFromZList(QgsComposerItem *item)
Removes item from z list.
static QDomElement saveSymbol(const QString &symbolName, QgsSymbolV2 *symbol, QDomDocument &doc)
bool enabled() const
Returns whether the atlas generation is enabled.
void setSceneRect(const QRectF &rect)
QgsExpression * expression()
void updateBounds()
Updates the scene bounds of the composition.
Container of fields for a vector layer.
Definition: qgsfield.h:193
A container for grouping several QgsComposerItems.
void resizeToContentsMargins(double &marginTop, double &marginRight, double &marginBottom, double &marginLeft) const
Returns the resize to contents margins.
void paperSizeChanged()
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.
void sendItemAddedSignal(QgsComposerItem *item)
Casts object to the proper subclass type and calls corresponding itemAdded signal.
qreal top() const
QgsFeature feature() const
Returns the current atlas feature.
virtual void setSelected(bool s)
Set selected, selected item should be highlighted.
void savePreviousState()
Saves current item state as previous state.
const QgsComposerItem * getComposerItemByUuid(const QString &theUuid) const
Returns a composer item given its unique identifier.
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.
bool isNull() const
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from the composition.
const_iterator insert(const T &value)
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h: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
double spaceBetweenPages() const
Returns the vertical space between pages in a composer view.
void doPrint(QPrinter &printer, QPainter &painter, bool startNewPage=false)
Print on a preconfigured printer.
void 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)
void remove(const QString &key)
Remove a key (entry) from the store.
void composerMapAdded(QgsComposerMap *map)
Is emitted when new composer map has been added to the view.
int numPages() const
Returns the number of pages in the composition.
qreal left() const
void adjust(qreal dx1, qreal dy1, qreal dx2, qreal dy2)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:352
virtual bool readXML(const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames=false) override
Reads the properties specific to an attribute table from xml.
void update(const QRectF &rect)
static Q_DECL_DEPRECATED double relativePosition(double position, double beforeMin, double beforeMax, double afterMin, double afterMax)
Returns a scaled position given a before and after range.
void setGridVisible(const bool b)
void alignSelectedItemsVCenter()
bool shouldExportPage(const int page) const
Returns whether a specified page number should be included in exports of the composition.
double x() const
Get the x value of the point.
Definition: qgspoint.h:185
qreal dx() const
A table that displays attributes from a vector layer.
DataDefinedProperty
Data defined properties for different item types.
void composerItemsOnPage(QList< T * > &itemList, const int pageNumber) const
Return composer items of a specific type on a specified page.
void endRender()
Ends the rendering.
int size() const
QRectF pageItemBounds(int pageNumber, bool visibleOnly=false) const
Returns the bounding box of the items contained on a specified page.
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 isDrawing() const
True if a draw is already in progress.
void composerLegendAdded(QgsComposerLegend *legend)
Is emitted when a new composer legend has been added.
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for the composition.
void setItemPosition(double x, double y, ItemPositionMode itemPoint=UpperLeft, int page=-1)
Moves the item to a new position (in canvas coordinates)
The QgsMapSettings class contains configuration for rendering of the map.
QList< QgsComposerItem * > * zOrderList()
Returns the item z-order list.
int width() const
double ANALYSIS_EXPORT max(double x, double y)
Returns the maximum of two doubles or the first argument if both are equal.
QDomElement toElement() const
void addComposerShape(QgsComposerShape *shape)
Adds a composer shape to the graphics scene and advises composer to create a widget for it (through s...
void setGridPen(const QPen &p)
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Return value for the given key. If the key is not stored, default value will be used.
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Reads the properties specific to an attribute table from xml.
qreal bottom() const
void setUseExpression(bool use)
Controls if the field or the expression part is active.
QgsComposerItem * getComposerItemBelow(QgsComposerItem *item) const
QTransform transform() const
QString uuid() const
Get item identification name.
qreal zValue() const
qreal y1() const
qreal y2() const
QPointF pos() const
void setNumPages(const int pages)
Sets the number of pages for the composition.
int count() const
qreal x1() const
qreal x2() const
QString number(int n, int base)
void refreshItemsTriggered()
Is emitted when item in the composition must be refreshed.
qreal x() const
qreal y() const
QPointF p1() const
bool beginRender()
Begins the rendering.
void setValue(const QString &key, const QVariant &value)
Add an entry to the store. If the entry with the keys exists already, it will be overwritten.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
void 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.
int itemPageNumber(const QgsComposerItem *) const
Returns on which page number (0-based) is displayed an item.
QVariant property(const char *name) const
int toInt(bool *ok) const
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:202
void removeItem(QGraphicsItem *item)
void cancelCommand()
Deletes current command.
void fill(uint pixelValue)
void setSnapGridOffsetX(const double offset)
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
bool containsChange() const
Returns true if previous state and after state are valid and different.
void setUseSymbolV2(bool useSymbolV2)
Controls whether the shape should be drawn using a QgsFillSymbolV2.
void endCommand()
Saves end state of item and pushes command to the undo history.
void itemRemoved(QgsComposerItem *)
Is emitted when a composer item has been removed from the scene.
int printResolution() const
void updatePagePos(double newPageWidth, double newPageHeight)
Moves the item so that it retains its relative position on the page when the paper size changes...
int width() const
void setAttribute(const QString &name, const QString &value)
void setField(const QString &field)
Set the field name which this QgsDataDefined represents.
void clear()
Clears all items from z-order list and resets the model.
QList< QGraphicsView * > views() const
void removeSnapLine(QGraphicsLineItem *line)
Remove custom snap line (and delete the object)
qreal m11() const
const QgsCoordinateReferenceSystem & destinationCrs() const
returns CRS of destination coordinate reference system
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
QStringList keys() const
Return list of stored keys.
Abstract base class for composer items with the ability to distribute the content to several frames (...
bool isEmpty() const
QgsComposerMap * worldFileMap() const
Returns the map item which will be used to generate corresponding world files when the composition is...
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)
void addComposerTableFrame(QgsComposerAttributeTableV2 *table, QgsComposerFrame *frame)
Adds composer tablev2 frame and advises composer to create a widget for it (through signal) ...
#define M_PI
QPaintDevice * device() const
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.
QPointF center() const
QRectF compositionBounds(bool ignorePages=false, double margin=0.0) const
Calculates the bounds of all non-gui items in the composition.
const_iterator constEnd() const
void removeCustomProperty(const QString &key)
Remove a custom property from the composition.
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.
int frameCount() const
Returns the number of frames associated with this multiframe.
void setColor(const QColor &color)
QImage printPageAsRaster(int page, QSize imageSize=QSize(), int dpi=0)
Renders a composer page to an image.
void * GDALDatasetH
QStringList customProperties() const
Return list of keys stored in custom properties for composition.
A composer command that merges together with other commands having the same context (=id) for multi f...
double mapRotation(QgsComposerObject::PropertyValueType valueType=QgsComposerObject::EvaluatedValue) const
Returns the rotation used for drawing the map within the composer item.
void setSnapGridResolution(const double r)
bool pageIsEmpty(const int page) const
Returns whether a page is empty, ie, it contains no items except for the background paper item...
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 QgsComposerItem * getComposerItemById(const QString &theId) const
Returns a composer item given its text identifier.
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 useExpression() const
Returns if the field or the expression part is active.
int indexFromName(const QString &name) const
Look up field&#39;s index from name. Returns -1 on error.
Definition: qgsfield.cpp:424
bool isActive() const
T * data() const
iterator end()
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
QByteArray toLocal8Bit() const
QgsExpressionContext * createExpressionContext() const
Creates an expression context relating to the compositions&#39;s current state.
void refreshItems()
Forces items in the composition to refresh.
qreal right() const
void setUpdatesEnabled(bool enabled)
Sets whether updates to the composer map are enabled.
iterator begin()
void clear()
QgsComposerItem * getComposerItemAbove(QgsComposerItem *item) const
Q_DECL_DEPRECATED int pixelFontSize(double pointSize) const
Returns the mm font size for a font that has point size set.
void setFullPage(bool fp)
PreviewMode previewMode() const
virtual QgsFillSymbolV2 * clone() const override
void setLine(qreal x1, qreal y1, qreal x2, qreal y2)
void nPagesChanged()
void removeItem(QgsComposerItem *item)
Removes an item from the z-order list.
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
void setTopLeft(const QPointF &position)
void updateSettings()
Refreshes the composition when composer related options change.
void 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.
bool setAtlasMode(const QgsComposition::AtlasMode mode)
Sets the current atlas mode of the composition.
bool isNull() const
int pageNumberForPoint(QPointF position) const
Returns the page number corresponding to a point in the composition.
bool newPage()
void setPositionLock(const bool lock)
Locks / unlocks the item position for mouse drags.
void setOrientation(Orientation orientation)
void setPrintResolution(const int dpi)
bool print(QPrinter &printer, const bool evaluateDDPageSize=false)
Convenience function that prepares the printer and prints.
void composerTableAdded(QgsComposerAttributeTable *table)
Is emitted when a new composer table has been added.
const Key key(const T &value) const
QList< QgsPaperItem * > pages()
Return pages in the correct order.
void refreshZList()
Rebuilds the z order list by adding any item which are present in the composition but missing from th...
int id() const
Get identification number.
void 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
void clearSnapLines()
Removes all snap lines.
A table class that displays a vector attribute table.
bool reorderItemUp(QgsComposerItem *item)
Moves an item up the z-order list.
void drawImage(const QRectF &target, const QImage &image, const QRectF &source, QFlags< Qt::ImageConversionFlag > flags)
qreal width() const
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:271
QgsComposerItem * getComposerItemBelow(QgsComposerItem *item) const
Finds the next composer item below an item.
bool remove(const T &value)
Undo command to undo/redo all composer item related changes.
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
void setDataDefinedProperty(const QgsComposerObject::DataDefinedProperty property, bool active, bool useExpression, const QString &expression, const QString &field)
Sets parameters for a data defined property for the composition.
A composer items that draws common shapes (ellipse, triangle, rectangle)
void writeXml(QDomNode &parentNode, QDomDocument &doc) const
Write store contents to XML.
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.
QPointF positionOnPage(QPointF position) const
Returns the position within a page of a point in the composition.
Q_DECL_DEPRECATED bool prepareExpression(QgsVectorLayer *layer)
Prepares the expression using a vector layer.
bool expressionIsPrepared() const
Returns whether the data defined object&#39;s expression is prepared.
void addComposerHtmlFrame(QgsComposerHtml *html, QgsComposerFrame *frame)
Adds composer html frame and advises composer to create a widget for it (through signal) ...
iterator end()
QgsComposerItem * composerItemAt(QPointF position, const bool ignoreLocked=false) const
Returns the topmost composer item at a specified position.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QList< QgsComposerMapOverview * > asList() const
Returns a list of QgsComposerMapOverviews contained by the stack.
AtlasMode
Composition atlas modes.
static QgsProject * instance()
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)
void renderRect(QPainter *p, const QRectF &rect)
Renders a portion of the composition to a paint device.
void alignSelectedItemsLeft()
int height() const
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.
qreal & rx()
qreal & ry()
void removeLast()
double paperHeight() const
Height of paper item.
static double pointsToMM(const double pointSize)
Returns the size in mm corresponding to a font point size.
int page() const
Gets the page the item is currently on.
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)
double y() const
Get the y value of the point.
Definition: qgspoint.h:193
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.
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 toDouble(bool *ok) const
void composerHtmlFrameAdded(QgsComposerHtml *html, QgsComposerFrame *frame)
Is emitted when a new composer html has been added to the view.
iterator insert(const Key &key, const T &value)
static Q_DECL_DEPRECATED void relativeResizeRect(QRectF &rectToResize, const QRectF &boundsBefore, const QRectF &boundsAfter)
Resizes a QRectF relative to the change from boundsBefore to boundsAfter.
void addComposerTable(QgsComposerAttributeTable *table)
Adds a composer table to the graphics scene and advises composer to create a widget for it (through s...
void removeAttribute(const QString &name)
Handles drawing of selection outlines and mouse handles.
void composerTableFrameAdded(QgsComposerAttributeTableV2 *table, QgsComposerFrame *frame)
Is emitted when a new composer table frame has been added to the view.
Q_DECL_DEPRECATED const QgsComposerHtml * getComposerHtmlByItem(QgsComposerItem *item) const
Returns the composer html with specified id (a string as named in the composer user interface item pr...
QgsVectorLayer * coverageLayer() const
Returns the coverage layer used for the atlas features.
void setItemRemoved(QgsComposerItem *item)
Marks an item as removed from the composition.
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from DOM document.
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
void setBrush(const QBrush &brush)
friend class QgsComposerModel
int size() const
int height() const
bool readXML(const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames=false) override
Reads multiframe state information from a DOM element.
const_iterator constEnd() const
Q_DECL_DEPRECATED double pointFontSize(int pixelSize) const
Does the inverse calculation and returns points for mm.
int fromPage() const
QGraphicsLineItem * addSnapLine()
Add a custom snap line (can be horizontal or vertical)
QDomElement createElement(const QString &tagName)
void clear()
void composerItems(QList< T * > &itemList)
Return composer items of a specific type.
const_iterator constBegin() const
void 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)
PlotStyle
Plot type.
void addItem(QGraphicsItem *item)
const QgsComposerMap * getComposerMapById(const int id) const
Returns the composer map with specified id.
void setPreviewMode(PreviewMode m)
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:207
bool raiseItem(QgsComposerItem *item)
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QgsComposerMultiFrame * multiFrame() const
Returns the parent multiframe for the frame.
void setSnapToGridEnabled(const bool b)
bool reorderItemToTop(QgsComposerItem *item)
Moves an item to the top of the z-order list.
void setOutputFormat(OutputFormat format)
Represents a vector layer which manages a vector based data sets.
bool begin(QPaintDevice *device)
void setBottomRight(const QPointF &position)
void move(double dx, double dy)
Moves item in canvas coordinates.
A legend that can be placed onto a map composition.
void addComposerLabel(QgsComposerLabel *label)
Adds label to the graphics scene and advises composer to create a widget for it (through signal) ...
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:192
QString toString() const
void setZValue(qreal z)
void addMultiFrame(QgsComposerMultiFrame *multiFrame)
Adds multiframe.
void addItemAtTop(QgsComposerItem *item)
Adds an item to the top of the composition z stack.
QgsPoint center() const
Center point of the rectangle.
Definition: qgsrectangle.h:217
const QgsRectangle * currentMapExtent() const
Returns a pointer to the current map extent, which is either the original user specified extent or th...
QString evalErrorString() const
Returns evaluation error.
void setExpressionString(const QString &expr)
Sets the expression for this QgsDataDefined.
bool isActive() const
iterator find(const Key &key)
iterator begin()
bool lowerItem(QgsComposerItem *item)
void addComposerArrow(QgsComposerArrow *arrow)
Adds an arrow item to the graphics scene and 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)
void addComposerLegend(QgsComposerLegend *legend)
Adds legend to the graphics scene and advises composer to create a widget for it (through signal) ...
bool containsChange() const
Returns true if previous state and after state are valid and different.
Q_DECL_DEPRECATED QgsComposition(QgsMapRenderer *mapRenderer)
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.
QString id() const
Get item&#39;s id (which is not necessarly unique)