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