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