QGIS API Documentation  2.9.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 
373 void QgsComposition::setNumPages( const int pages )
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  //
2831  // Word file parameters : affine transformation parameters from pixel coordinates to map coordinates
2832 
2833  if ( !mWorldFileMap )
2834  {
2835  return;
2836  }
2837 
2838  QRectF brect = mWorldFileMap->mapRectToScene( mWorldFileMap->rect() );
2839  QgsRectangle extent = *mWorldFileMap->currentMapExtent();
2840 
2841  double alpha = mWorldFileMap->mapRotation() / 180 * M_PI;
2842 
2843  double xr = extent.width() / brect.width();
2844  double yr = extent.height() / brect.height();
2845 
2846  double XC = extent.center().x();
2847  double YC = extent.center().y();
2848 
2849  // get the extent for the page
2850  double xmin = extent.xMinimum() - mWorldFileMap->pos().x() * xr;
2851  double ymax = extent.yMaximum() + mWorldFileMap->pos().y() * yr;
2852  QgsRectangle paperExtent( xmin, ymax - paperHeight() * yr, xmin + paperWidth() * xr, ymax );
2853 
2854  double X0 = paperExtent.xMinimum();
2855  double Y0 = paperExtent.yMinimum();
2856 
2857  int widthPx = ( int )( printResolution() * paperWidth() / 25.4 );
2858  int heightPx = ( int )( printResolution() * paperHeight() / 25.4 );
2859 
2860  double Ww = paperExtent.width() / widthPx;
2861  double Hh = paperExtent.height() / heightPx;
2862 
2863  // scaling matrix
2864  double s[6];
2865  s[0] = Ww;
2866  s[1] = 0;
2867  s[2] = X0;
2868  s[3] = 0;
2869  s[4] = -Hh;
2870  s[5] = Y0 + paperExtent.height();
2871 
2872  // rotation matrix
2873  double r[6];
2874  r[0] = cos( alpha );
2875  r[1] = -sin( alpha );
2876  r[2] = XC * ( 1 - cos( alpha ) ) + YC * sin( alpha );
2877  r[3] = sin( alpha );
2878  r[4] = cos( alpha );
2879  r[5] = - XC * sin( alpha ) + YC * ( 1 - cos( alpha ) );
2880 
2881  // result = rotation x scaling = rotation(scaling(X))
2882  a = r[0] * s[0] + r[1] * s[3];
2883  b = r[0] * s[1] + r[1] * s[4];
2884  c = r[0] * s[2] + r[1] * s[5] + r[2];
2885  d = r[3] * s[0] + r[4] * s[3];
2886  e = r[3] * s[1] + r[4] * s[4];
2887  f = r[3] * s[2] + r[4] * s[5] + r[5];
2888 }
2889 
2891 {
2892  mAtlasMode = mode;
2893 
2894  if ( mode == QgsComposition::AtlasOff )
2895  {
2896  mAtlasComposition.endRender();
2897  }
2898  else
2899  {
2900  bool atlasHasFeatures = mAtlasComposition.beginRender();
2901  if ( ! atlasHasFeatures )
2902  {
2903  mAtlasMode = QgsComposition::AtlasOff;
2904  mAtlasComposition.endRender();
2905  return false;
2906  }
2907  }
2908 
2909  update();
2910  return true;
2911 }
2912 
2913 bool QgsComposition::ddPageSizeActive() const
2914 {
2915  //check if any data defined page settings are active
2916  return dataDefinedActive( QgsComposerObject::PresetPaperSize, &mDataDefinedProperties ) ||
2917  dataDefinedActive( QgsComposerObject::PaperWidth, &mDataDefinedProperties ) ||
2918  dataDefinedActive( QgsComposerObject::PaperHeight, &mDataDefinedProperties ) ||
2919  dataDefinedActive( QgsComposerObject::PaperOrientation, &mDataDefinedProperties );
2920 }
2921 
2922 void QgsComposition::refreshPageSize()
2923 {
2924  double pageWidth = mPageWidth;
2925  double pageHeight = mPageHeight;
2926 
2927  QVariant exprVal;
2928  //in order of precedence - first consider predefined page size
2929  if ( dataDefinedEvaluate( QgsComposerObject::PresetPaperSize, exprVal, &mDataDefinedProperties ) )
2930  {
2931  QString presetString = exprVal.toString().trimmed();
2932  QgsDebugMsg( QString( "exprVal Paper Preset size :%1" ).arg( presetString ) );
2933  double widthD = 0;
2934  double heightD = 0;
2935  if ( QgsComposerUtils::decodePresetPaperSize( presetString, widthD, heightD ) )
2936  {
2937  pageWidth = widthD;
2938  pageHeight = heightD;
2939  }
2940  }
2941 
2942  //which is overwritten by data defined width/height
2943  if ( dataDefinedEvaluate( QgsComposerObject::PaperWidth, exprVal, &mDataDefinedProperties ) )
2944  {
2945  bool ok;
2946  double widthD = exprVal.toDouble( &ok );
2947  QgsDebugMsg( QString( "exprVal Paper Width:%1" ).arg( widthD ) );
2948  if ( ok )
2949  {
2950  pageWidth = widthD;
2951  }
2952  }
2953  if ( dataDefinedEvaluate( QgsComposerObject::PaperHeight, exprVal, &mDataDefinedProperties ) )
2954  {
2955  bool ok;
2956  double heightD = exprVal.toDouble( &ok );
2957  QgsDebugMsg( QString( "exprVal Paper Height:%1" ).arg( heightD ) );
2958  if ( ok )
2959  {
2960  pageHeight = heightD;
2961  }
2962  }
2963 
2964  //which is finally overwritten by data defined orientation
2965  if ( dataDefinedEvaluate( QgsComposerObject::PaperOrientation, exprVal, &mDataDefinedProperties ) )
2966  {
2967  bool ok;
2968  QString orientationString = exprVal.toString().trimmed();
2969  QgsComposition::PaperOrientation orientation = QgsComposerUtils::decodePaperOrientation( orientationString, ok );
2970  QgsDebugMsg( QString( "exprVal Paper Orientation:%1" ).arg( orientationString ) );
2971  if ( ok )
2972  {
2973  double heightD, widthD;
2974  if ( orientation == QgsComposition::Portrait )
2975  {
2976  heightD = qMax( pageHeight, pageWidth );
2977  widthD = qMin( pageHeight, pageWidth );
2978  }
2979  else
2980  {
2981  heightD = qMin( pageHeight, pageWidth );
2982  widthD = qMax( pageHeight, pageWidth );
2983  }
2984  pageWidth = widthD;
2985  pageHeight = heightD;
2986  }
2987  }
2988 
2989  setPaperSize( pageWidth, pageHeight );
2990 }
2991 
2993 {
2994  if ( property == QgsComposerObject::AllProperties || property == QgsComposerObject::NoProperty )
2995  {
2996  //invalid property
2997  return 0;
2998  }
2999 
3000  //find matching QgsDataDefined for property
3001  QMap< QgsComposerObject::DataDefinedProperty, QgsDataDefined* >::const_iterator it = mDataDefinedProperties.find( property );
3002  if ( it != mDataDefinedProperties.constEnd() )
3003  {
3004  return it.value();
3005  }
3006 
3007  //not found
3008  return 0;
3009 }
3010 
3011 void QgsComposition::setDataDefinedProperty( const QgsComposerObject::DataDefinedProperty property, bool active, bool useExpression, const QString &expression, const QString &field )
3012 {
3013  if ( property == QgsComposerObject::AllProperties || property == QgsComposerObject::NoProperty )
3014  {
3015  //invalid property
3016  return;
3017  }
3018 
3019  bool defaultVals = ( !active && !useExpression && expression.isEmpty() && field.isEmpty() );
3020 
3021  if ( mDataDefinedProperties.contains( property ) )
3022  {
3023  QMap< QgsComposerObject::DataDefinedProperty, QgsDataDefined* >::const_iterator it = mDataDefinedProperties.find( property );
3024  if ( it != mDataDefinedProperties.constEnd() )
3025  {
3026  QgsDataDefined* dd = it.value();
3027  dd->setActive( active );
3028  dd->setUseExpression( useExpression );
3029  dd->setExpressionString( expression );
3030  dd->setField( field );
3031  }
3032  }
3033  else if ( !defaultVals )
3034  {
3035  QgsDataDefined* dd = new QgsDataDefined( active, useExpression, expression, field );
3036  mDataDefinedProperties.insert( property, dd );
3037  }
3038 }
3039 
3040 bool QgsComposition::dataDefinedEvaluate( QgsComposerObject::DataDefinedProperty property, QVariant &expressionValue, QMap<QgsComposerObject::DataDefinedProperty, QgsDataDefined *> *dataDefinedProperties )
3041 {
3042  if ( property == QgsComposerObject::NoProperty || property == QgsComposerObject::AllProperties )
3043  {
3044  //invalid property
3045  return false;
3046  }
3047 
3048  //null passed-around QVariant
3049  expressionValue.clear();
3050 
3051  //get fields and feature from atlas
3052  const QgsFeature* currentFeature = 0;
3053  const QgsFields* layerFields = 0;
3054  if ( mAtlasComposition.enabled() )
3055  {
3056  QgsVectorLayer* atlasLayer = mAtlasComposition.coverageLayer();
3057  if ( atlasLayer )
3058  {
3059  layerFields = &atlasLayer->pendingFields();
3060  }
3061  if ( mAtlasMode != QgsComposition::AtlasOff )
3062  {
3063  currentFeature = mAtlasComposition.currentFeature();
3064  }
3065  }
3066 
3067  //evaluate data defined property using current atlas context
3068  QVariant result = dataDefinedValue( property, currentFeature, layerFields, dataDefinedProperties );
3069 
3070  if ( result.isValid() )
3071  {
3072  expressionValue = result;
3073  return true;
3074  }
3075 
3076  return false;
3077 }
3078 
3079 bool QgsComposition::dataDefinedActive( const QgsComposerObject::DataDefinedProperty property, const QMap<QgsComposerObject::DataDefinedProperty, QgsDataDefined *> *dataDefinedProperties ) const
3080 {
3081  if ( property == QgsComposerObject::AllProperties || property == QgsComposerObject::NoProperty )
3082  {
3083  //invalid property
3084  return false;
3085  }
3086  if ( !dataDefinedProperties->contains( property ) )
3087  {
3088  //missing property
3089  return false;
3090  }
3091 
3092  QgsDataDefined* dd = 0;
3093  QMap< QgsComposerObject::DataDefinedProperty, QgsDataDefined* >::const_iterator it = dataDefinedProperties->find( property );
3094  if ( it != dataDefinedProperties->constEnd() )
3095  {
3096  dd = it.value();
3097  }
3098 
3099  if ( !dd )
3100  {
3101  return false;
3102  }
3103 
3104  //found the data defined property, return whether it is active
3105  return dd->isActive();
3106 }
3107 
3108 QVariant QgsComposition::dataDefinedValue( QgsComposerObject::DataDefinedProperty property, const QgsFeature *feature, const QgsFields *fields, QMap<QgsComposerObject::DataDefinedProperty, QgsDataDefined *> *dataDefinedProperties ) const
3109 {
3110  if ( property == QgsComposerObject::AllProperties || property == QgsComposerObject::NoProperty )
3111  {
3112  //invalid property
3113  return QVariant();
3114  }
3115  if ( !dataDefinedProperties->contains( property ) )
3116  {
3117  //missing property
3118  return QVariant();
3119  }
3120 
3121  QgsDataDefined* dd = 0;
3122  QMap< QgsComposerObject::DataDefinedProperty, QgsDataDefined* >::const_iterator it = dataDefinedProperties->find( property );
3123  if ( it != dataDefinedProperties->constEnd() )
3124  {
3125  dd = it.value();
3126  }
3127 
3128  if ( !dd )
3129  {
3130  return QVariant();
3131  }
3132 
3133  if ( !dd->isActive() )
3134  {
3135  return QVariant();
3136  }
3137 
3138  QVariant result = QVariant();
3139  bool useExpression = dd->useExpression();
3140  QString field = dd->field();
3141 
3142  if ( !dd->expressionIsPrepared() )
3143  {
3144  prepareDataDefinedExpression( dd, dataDefinedProperties );
3145  }
3146 
3147  if ( useExpression && dd->expressionIsPrepared() )
3148  {
3149  QgsExpression* expr = dd->expression();
3150 
3151  result = expr->evaluate( feature );
3152  if ( expr->hasEvalError() )
3153  {
3154  QgsDebugMsgLevel( QString( "Evaluate error:" ) + expr->evalErrorString(), 4 );
3155  return QVariant();
3156  }
3157  }
3158  else if ( !useExpression && !field.isEmpty() && fields )
3159  {
3160  if ( !feature )
3161  {
3162  return QVariant();
3163  }
3164  // use direct attribute access instead of evaluating "field" expression (much faster)
3165  int indx = fields->indexFromName( field );
3166  if ( indx != -1 )
3167  {
3168  result = feature->attribute( indx );
3169  }
3170  }
3171  return result;
3172 }
3173 
3174 void QgsComposition::prepareDataDefinedExpression( QgsDataDefined *dd, QMap<QgsComposerObject::DataDefinedProperty, QgsDataDefined *> *dataDefinedProperties ) const
3175 {
3176  QgsVectorLayer* atlasLayer = 0;
3177 
3178  if ( mAtlasComposition.enabled() )
3179  {
3180  atlasLayer = mAtlasComposition.coverageLayer();
3181  }
3182 
3183  //if specific QgsDataDefined passed, prepare it
3184  //otherwise prepare all QgsDataDefineds
3185  if ( dd )
3186  {
3187  dd->prepareExpression( atlasLayer );
3188  }
3189  else
3190  {
3191  QMap< QgsComposerObject::DataDefinedProperty, QgsDataDefined* >::const_iterator it = dataDefinedProperties->constBegin();
3192  for ( ; it != dataDefinedProperties->constEnd(); ++it )
3193  {
3194  it.value()->prepareExpression( atlasLayer );
3195  }
3196  }
3197 }
3198 
3199 void QgsComposition::prepareAllDataDefinedExpressions()
3200 {
3201  prepareDataDefinedExpression( 0, &mDataDefinedProperties );
3202 }
3203 
3204 void QgsComposition::relativeResizeRect( QRectF& rectToResize, const QRectF& boundsBefore, const QRectF& boundsAfter )
3205 {
3206  QgsComposerUtils::relativeResizeRect( rectToResize, boundsBefore, boundsAfter );
3207 }
3208 
3209 double QgsComposition::relativePosition( double position, double beforeMin, double beforeMax, double afterMin, double afterMax )
3210 {
3211  return QgsComposerUtils::relativePosition( position, beforeMin, beforeMax, afterMin, afterMax );
3212 }
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's overview stack, which is used to control how overviews are drawn over the map'...
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
#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.
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's data defined properties...
void removeItems() override
Removes the items but does not delete them.
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.
DataDefinedProperty
Data defined properties for different item types.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:113
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:418
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:327
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.
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.
PlotStyle
Plot type.
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
GridStyle
Style to draw the snapping grid.
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'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'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.
AtlasMode
Composition atlas modes.
QString file
Definition: qgssvgcache.cpp:76
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.
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.
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's id (which is not necessarly unique)