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