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