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