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