QGIS API Documentation  2.0.1-Dufour
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups 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 : [email protected]
7  ***************************************************************************/
8 /***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
17 #include "qgscomposition.h"
18 #include "qgscomposerarrow.h"
19 #include "qgscomposerframe.h"
20 #include "qgscomposerhtml.h"
21 #include "qgscomposerlabel.h"
22 #include "qgscomposerlegend.h"
23 #include "qgscomposermap.h"
24 #include "qgscomposeritemgroup.h"
25 #include "qgscomposerpicture.h"
26 #include "qgscomposerscalebar.h"
27 #include "qgscomposershape.h"
28 #include "qgscomposerlabel.h"
32 #include "qgspaintenginehack.h"
33 #include "qgspaperitem.h"
34 #include "qgsproject.h"
35 #include "qgsgeometry.h"
36 #include "qgsvectorlayer.h"
37 #include "qgsvectordataprovider.h"
38 #include "qgsexpression.h"
39 
40 #include <QDomDocument>
41 #include <QDomElement>
42 #include <QGraphicsRectItem>
43 #include <QPainter>
44 #include <QPrinter>
45 #include <QSettings>
46 #include <QDir>
47 
48 
50  : QGraphicsScene( 0 )
51  , mMapRenderer( mapRenderer )
52  , mPlotStyle( QgsComposition::Preview )
53  , mPageWidth( 297 )
54  , mPageHeight( 210 )
55  , mSpaceBetweenPages( 10 )
56  , mPrintAsRaster( false )
57  , mUseAdvancedEffects( true )
58  , mSelectionTolerance( 0.0 )
59  , mSnapToGrid( false )
60  , mSnapGridResolution( 10.0 )
61  , mSnapGridOffsetX( 0.0 )
62  , mSnapGridOffsetY( 0.0 )
63  , mAlignmentSnap( true )
64  , mAlignmentSnapTolerance( 2 )
65  , mActiveItemCommand( 0 )
66  , mActiveMultiFrameCommand( 0 )
67  , mAtlasComposition( this )
68 {
69  setBackgroundBrush( Qt::gray );
70  addPaperItem();
71 
72  mPrintResolution = 300; //hardcoded default
73  loadSettings();
74 }
75 
77  : QGraphicsScene( 0 ),
78  mMapRenderer( 0 ),
79  mPlotStyle( QgsComposition::Preview ),
80  mPageWidth( 297 ),
81  mPageHeight( 210 ),
82  mSpaceBetweenPages( 10 ),
83  mPrintAsRaster( false ),
84  mUseAdvancedEffects( true ),
85  mSelectionTolerance( 0.0 ),
86  mSnapToGrid( false ),
87  mSnapGridResolution( 10.0 ),
88  mSnapGridOffsetX( 0.0 ),
89  mSnapGridOffsetY( 0.0 ),
90  mAlignmentSnap( true ),
91  mAlignmentSnapTolerance( 2 ),
92  mActiveItemCommand( 0 ),
93  mActiveMultiFrameCommand( 0 ),
94  mAtlasComposition( this )
95 {
96  loadSettings();
97 
98 }
99 
101 {
104 
105  // make sure that all composer items are removed before
106  // this class is deconstructed - to avoid segfaults
107  // when composer items access in destructor composition that isn't valid anymore
108  clear();
109  delete mActiveItemCommand;
111 }
112 
113 void QgsComposition::setPaperSize( double width, double height )
114 {
115  mPageWidth = width;
116  mPageHeight = height;
117  double currentY = 0;
118  for ( int i = 0; i < mPages.size(); ++i )
119  {
120  mPages.at( i )->setSceneRect( QRectF( 0, currentY, width, height ) );
121  currentY += ( height + mSpaceBetweenPages );
122  }
123 }
124 
126 {
127  return mPageHeight;
128 }
129 
131 {
132  return mPageWidth;
133 }
134 
136 {
137  int currentPages = numPages();
138  int diff = pages - currentPages;
139  if ( diff >= 0 )
140  {
141  for ( int i = 0; i < diff; ++i )
142  {
143  addPaperItem();
144  }
145  }
146  else
147  {
148  diff = -diff;
149  for ( int i = 0; i < diff; ++i )
150  {
151  delete mPages.last();
152  mPages.removeLast();
153  }
154  }
155 
156  // update the corresponding variable
157  QgsExpression::setSpecialColumn( "$numpages", QVariant(( int )numPages() ) );
158 
159  emit nPagesChanged();
160 }
161 
163 {
164  return mPages.size();
165 }
166 
168 {
169  QList<QGraphicsItem*> itemList;
170  if ( mSelectionTolerance <= 0.0 )
171  {
172  itemList = items( position );
173  }
174  else
175  {
176  itemList = items( QRectF( position.x() - mSelectionTolerance, position.y() - mSelectionTolerance, 2 * mSelectionTolerance, 2 * mSelectionTolerance ),
177  Qt::IntersectsItemShape, Qt::DescendingOrder );
178  }
179  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
180 
181  for ( ; itemIt != itemList.end(); ++itemIt )
182  {
183  QgsComposerItem* composerItem = dynamic_cast<QgsComposerItem *>( *itemIt );
184  QgsPaperItem* paperItem = dynamic_cast<QgsPaperItem*>( *itemIt );
185  if ( composerItem && !paperItem )
186  {
187  return composerItem;
188  }
189  }
190  return 0;
191 }
192 
193 int QgsComposition::pageNumberAt( const QPointF& position ) const
194 {
195  return position.y() / ( paperHeight() + spaceBetweenPages() );
196 }
197 
199 {
200  return pageNumberAt( QPointF( item->transform().dx(), item->transform().dy() ) );
201 }
202 
203 QList<QgsComposerItem*> QgsComposition::selectedComposerItems()
204 {
205  QList<QgsComposerItem*> composerItemList;
206 
207  QList<QGraphicsItem *> graphicsItemList = selectedItems();
208  QList<QGraphicsItem *>::iterator itemIter = graphicsItemList.begin();
209 
210  for ( ; itemIter != graphicsItemList.end(); ++itemIter )
211  {
212  QgsComposerItem* composerItem = dynamic_cast<QgsComposerItem *>( *itemIter );
213  if ( composerItem )
214  {
215  composerItemList.push_back( composerItem );
216  }
217  }
218 
219  return composerItemList;
220 }
221 
222 QList<const QgsComposerMap*> QgsComposition::composerMapItems() const
223 {
224  QList<const QgsComposerMap*> resultList;
225 
226  QList<QGraphicsItem *> itemList = items();
227  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
228  for ( ; itemIt != itemList.end(); ++itemIt )
229  {
230  const QgsComposerMap* composerMap = dynamic_cast<const QgsComposerMap *>( *itemIt );
231  if ( composerMap )
232  {
233  resultList.push_back( composerMap );
234  }
235  }
236 
237  return resultList;
238 }
239 
241 {
242  QList<QGraphicsItem *> itemList = items();
243  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
244  for ( ; itemIt != itemList.end(); ++itemIt )
245  {
246  const QgsComposerMap* composerMap = dynamic_cast<const QgsComposerMap *>( *itemIt );
247  if ( composerMap )
248  {
249  if ( composerMap->id() == id )
250  {
251  return composerMap;
252  }
253  }
254  }
255  return 0;
256 }
257 
259 {
260  // an html item will be a composer frame and if it is we can try to get
261  // its multiframe parent and then try to cast that to a composer html
262  const QgsComposerFrame* composerFrame =
263  dynamic_cast<const QgsComposerFrame *>( item );
264  if ( composerFrame )
265  {
266  const QgsComposerMultiFrame * mypMultiFrame = composerFrame->multiFrame();
267  const QgsComposerHtml* composerHtml =
268  dynamic_cast<const QgsComposerHtml *>( mypMultiFrame );
269  if ( composerHtml )
270  {
271  return composerHtml;
272  }
273  }
274  return 0;
275 }
276 
278 {
279  QList<QGraphicsItem *> itemList = items();
280  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
281  for ( ; itemIt != itemList.end(); ++itemIt )
282  {
283  const QgsComposerItem* mypItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
284  if ( mypItem )
285  {
286  if ( mypItem->id() == theId )
287  {
288  return mypItem;
289  }
290  }
291  }
292  return 0;
293 }
294 
295 #if 0
296 const QgsComposerItem* QgsComposition::getComposerItemByUuid( QString theUuid, bool inAllComposers ) const
297 {
298  //This does not work since it seems impossible to get the QgisApp::instance() from here... Is there a workaround ?
299  QSet<QgsComposer*> composers = QSet<QgsComposer*>();
300 
301  if ( inAllComposers )
302  {
303  composers = QgisApp::instance()->printComposers();
304  }
305  else
306  {
307  composers.insert( this )
308  }
309 
310  QSet<QgsComposer*>::const_iterator it = composers.constBegin();
311  for ( ; it != composers.constEnd(); ++it )
312  {
313  QList<QGraphicsItem *> itemList = ( *it )->items();
314  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
315  for ( ; itemIt != itemList.end(); ++itemIt )
316  {
317  const QgsComposerItem* mypItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
318  if ( mypItem )
319  {
320  if ( mypItem->uuid() == theUuid )
321  {
322  return mypItem;
323  }
324  }
325  }
326  }
327 
328  return 0;
329 }
330 #endif
331 
333 {
334  QList<QGraphicsItem *> itemList = items();
335  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
336  for ( ; itemIt != itemList.end(); ++itemIt )
337  {
338  const QgsComposerItem* mypItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
339  if ( mypItem )
340  {
341  if ( mypItem->uuid() == theUuid )
342  {
343  return mypItem;
344  }
345  }
346  }
347 
348  return 0;
349 }
350 
351 
352 void QgsComposition::setUseAdvancedEffects( bool effectsEnabled )
353 {
354  mUseAdvancedEffects = effectsEnabled;
355 
356  //toggle effects for all composer items
357  QList<QGraphicsItem*> itemList = items();
358  QList<QGraphicsItem*>::const_iterator itemIt = itemList.constBegin();
359  for ( ; itemIt != itemList.constEnd(); ++itemIt )
360  {
361  QgsComposerItem* composerItem = dynamic_cast<QgsComposerItem*>( *itemIt );
362  if ( composerItem )
363  {
364  composerItem->setEffectsEnabled( effectsEnabled );
365  }
366  }
367 }
368 
369 int QgsComposition::pixelFontSize( double pointSize ) const
370 {
371  //in QgsComposition, one unit = one mm
372  double sizeMillimeters = pointSize * 0.3527;
373  return qRound( sizeMillimeters ); //round to nearest mm
374 }
375 
376 double QgsComposition::pointFontSize( int pixelSize ) const
377 {
378  double sizePoint = pixelSize / 0.3527;
379  return sizePoint;
380 }
381 
382 bool QgsComposition::writeXML( QDomElement& composerElem, QDomDocument& doc )
383 {
384  if ( composerElem.isNull() )
385  {
386  return false;
387  }
388 
389  QDomElement compositionElem = doc.createElement( "Composition" );
390  compositionElem.setAttribute( "paperWidth", QString::number( mPageWidth ) );
391  compositionElem.setAttribute( "paperHeight", QString::number( mPageHeight ) );
392  compositionElem.setAttribute( "numPages", mPages.size() );
393 
394  //snapping
395  if ( mSnapToGrid )
396  {
397  compositionElem.setAttribute( "snapping", "1" );
398  }
399  else
400  {
401  compositionElem.setAttribute( "snapping", "0" );
402  }
403  compositionElem.setAttribute( "snapGridResolution", QString::number( mSnapGridResolution ) );
404  compositionElem.setAttribute( "snapGridOffsetX", QString::number( mSnapGridOffsetX ) );
405  compositionElem.setAttribute( "snapGridOffsetY", QString::number( mSnapGridOffsetY ) );
406 
407  //custom snap lines
408  QList< QGraphicsLineItem* >::const_iterator snapLineIt = mSnapLines.constBegin();
409  for ( ; snapLineIt != mSnapLines.constEnd(); ++snapLineIt )
410  {
411  QDomElement snapLineElem = doc.createElement( "SnapLine" );
412  QLineF line = ( *snapLineIt )->line();
413  snapLineElem.setAttribute( "x1", QString::number( line.x1() ) );
414  snapLineElem.setAttribute( "y1", QString::number( line.y1() ) );
415  snapLineElem.setAttribute( "x2", QString::number( line.x2() ) );
416  snapLineElem.setAttribute( "y2", QString::number( line.y2() ) );
417  compositionElem.appendChild( snapLineElem );
418  }
419 
420  compositionElem.setAttribute( "printResolution", mPrintResolution );
421  compositionElem.setAttribute( "printAsRaster", mPrintAsRaster );
422 
423  compositionElem.setAttribute( "alignmentSnap", mAlignmentSnap ? 1 : 0 );
424  compositionElem.setAttribute( "alignmentSnapTolerance", mAlignmentSnapTolerance );
425 
426  //save items except paper items and frame items (they are saved with the corresponding multiframe)
427  QList<QGraphicsItem*> itemList = items();
428  QList<QGraphicsItem*>::const_iterator itemIt = itemList.constBegin();
429  for ( ; itemIt != itemList.constEnd(); ++itemIt )
430  {
431  const QgsComposerItem* composerItem = dynamic_cast<const QgsComposerItem*>( *itemIt );
432  if ( composerItem )
433  {
434  if ( composerItem->type() != QgsComposerItem::ComposerPaper && composerItem->type() != QgsComposerItem::ComposerFrame )
435  {
436  composerItem->writeXML( compositionElem, doc );
437  }
438  }
439  }
440 
441  //save multiframes
442  QSet<QgsComposerMultiFrame*>::const_iterator multiFrameIt = mMultiFrames.constBegin();
443  for ( ; multiFrameIt != mMultiFrames.constEnd(); ++multiFrameIt )
444  {
445  ( *multiFrameIt )->writeXML( compositionElem, doc );
446  }
447  composerElem.appendChild( compositionElem );
448 
449  return true;
450 }
451 
452 bool QgsComposition::readXML( const QDomElement& compositionElem, const QDomDocument& doc )
453 {
454  Q_UNUSED( doc );
455  if ( compositionElem.isNull() )
456  {
457  return false;
458  }
459 
460  //create pages
461  bool widthConversionOk, heightConversionOk;
462  mPageWidth = compositionElem.attribute( "paperWidth" ).toDouble( &widthConversionOk );
463  mPageHeight = compositionElem.attribute( "paperHeight" ).toDouble( &heightConversionOk );
464  emit paperSizeChanged();
465  int numPages = compositionElem.attribute( "numPages", "1" ).toInt();
466 
467  if ( widthConversionOk && heightConversionOk )
468  {
470  for ( int i = 0; i < numPages; ++i )
471  {
472  addPaperItem();
473  }
474  }
475 
476  //snapping
477  if ( compositionElem.attribute( "snapping" ) == "0" )
478  {
479  mSnapToGrid = false;
480  }
481  else
482  {
483  mSnapToGrid = true;
484  }
485  mSnapGridResolution = compositionElem.attribute( "snapGridResolution" ).toDouble();
486  mSnapGridOffsetX = compositionElem.attribute( "snapGridOffsetX" ).toDouble();
487  mSnapGridOffsetY = compositionElem.attribute( "snapGridOffsetY" ).toDouble();
488 
489  //custom snap lines
490  QDomNodeList snapLineNodes = compositionElem.elementsByTagName( "SnapLine" );
491  for ( int i = 0; i < snapLineNodes.size(); ++i )
492  {
493  QDomElement snapLineElem = snapLineNodes.at( i ).toElement();
494  QGraphicsLineItem* snapItem = addSnapLine();
495  double x1 = snapLineElem.attribute( "x1" ).toDouble();
496  double y1 = snapLineElem.attribute( "y1" ).toDouble();
497  double x2 = snapLineElem.attribute( "x2" ).toDouble();
498  double y2 = snapLineElem.attribute( "y2" ).toDouble();
499  snapItem->setLine( x1, y1, x2, y2 );
500  }
501 
502  mAlignmentSnap = compositionElem.attribute( "alignmentSnap", "1" ).toInt() == 0 ? false : true;
503  mAlignmentSnapTolerance = compositionElem.attribute( "alignmentSnapTolerance", "2.0" ).toDouble();
504 
505  mPrintAsRaster = compositionElem.attribute( "printAsRaster" ).toInt();
506  mPrintResolution = compositionElem.attribute( "printResolution", "300" ).toInt();
507 
509 
510  return true;
511 }
512 
513 bool QgsComposition::loadFromTemplate( const QDomDocument& doc, QMap<QString, QString>* substitutionMap, bool addUndoCommands )
514 {
516 
517  //delete all items and emit itemRemoved signal
518  QList<QGraphicsItem *> itemList = items();
519  QList<QGraphicsItem *>::iterator itemIter = itemList.begin();
520  for ( ; itemIter != itemList.end(); ++itemIter )
521  {
522  QgsComposerItem* cItem = dynamic_cast<QgsComposerItem*>( *itemIter );
523  if ( cItem )
524  {
525  removeItem( cItem );
526  emit itemRemoved( cItem );
527  delete cItem;
528  }
529  }
530  mItemZList.clear();
531 
532  mPages.clear();
533  mUndoStack.clear();
534 
535  QDomDocument importDoc;
536  if ( substitutionMap )
537  {
538  QString xmlString = doc.toString();
539  QMap<QString, QString>::const_iterator sIt = substitutionMap->constBegin();
540  for ( ; sIt != substitutionMap->constEnd(); ++sIt )
541  {
542  xmlString = xmlString.replace( "[" + sIt.key() + "]", encodeStringForXML( sIt.value() ) );
543  }
544 
545  QString errorMsg;
546  int errorLine, errorColumn;
547  if ( !importDoc.setContent( xmlString, &errorMsg, &errorLine, &errorColumn ) )
548  {
549  return false;
550  }
551  }
552  else
553  {
554  importDoc = doc;
555  }
556 
557  //read general settings
558  QDomElement compositionElem = importDoc.documentElement().firstChildElement( "Composition" );
559  if ( compositionElem.isNull() )
560  {
561  return false;
562  }
563 
564  bool ok = readXML( compositionElem, importDoc );
565  if ( !ok )
566  {
567  return false;
568  }
569 
570  // remove all uuid attributes since we don't want duplicates UUIDS
571  QDomNodeList composerItemsNodes = importDoc.elementsByTagName( "ComposerItem" );
572  for ( int i = 0; i < composerItemsNodes.count(); ++i )
573  {
574  QDomNode composerItemNode = composerItemsNodes.at( i );
575  if ( composerItemNode.isElement() )
576  {
577  composerItemNode.toElement().setAttribute( "templateUuid", composerItemNode.toElement().attribute( "uuid" ) );
578  composerItemNode.toElement().removeAttribute( "uuid" );
579  }
580  }
581 
582  //addItemsFromXML
583  addItemsFromXML( importDoc.documentElement(), importDoc, 0, addUndoCommands, 0 );
584 
585  // read atlas parameters
586  QDomElement atlasElem = importDoc.documentElement().firstChildElement( "Atlas" );
587  atlasComposition().readXML( atlasElem, importDoc );
588  return true;
589 }
590 
591 void QgsComposition::addItemsFromXML( const QDomElement& elem, const QDomDocument& doc, QMap< QgsComposerMap*, int >* mapsToRestore,
592  bool addUndoCommands, QPointF* pos, bool pasteInPlace )
593 {
594  QPointF* pasteInPlacePt = 0;
595  if ( pasteInPlace )
596  {
597  pasteInPlacePt = new QPointF( 0, pageNumberAt( *pos ) * ( mPageHeight + mSpaceBetweenPages ) );
598  }
599  QDomNodeList composerLabelList = elem.elementsByTagName( "ComposerLabel" );
600  for ( int i = 0; i < composerLabelList.size(); ++i )
601  {
602  QDomElement currentComposerLabelElem = composerLabelList.at( i ).toElement();
603  QgsComposerLabel* newLabel = new QgsComposerLabel( this );
604  newLabel->readXML( currentComposerLabelElem, doc );
605  if ( pos )
606  {
607  if ( pasteInPlacePt )
608  {
609  newLabel->setItemPosition( newLabel->transform().dx(), fmod( newLabel->transform().dy(), ( paperHeight() + spaceBetweenPages() ) ) );
610  newLabel->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
611  }
612  else
613  {
614  newLabel->setItemPosition( pos->x(), pos->y() );
615  }
616  }
617  addComposerLabel( newLabel );
618  if ( addUndoCommands )
619  {
620  pushAddRemoveCommand( newLabel, tr( "Label added" ) );
621  }
622  }
623  // map
624  QDomNodeList composerMapList = elem.elementsByTagName( "ComposerMap" );
625  for ( int i = 0; i < composerMapList.size(); ++i )
626  {
627  QDomElement currentComposerMapElem = composerMapList.at( i ).toElement();
628  QgsComposerMap* newMap = new QgsComposerMap( this );
629  newMap->readXML( currentComposerMapElem, doc );
630  newMap->assignFreeId();
631 
632  if ( mapsToRestore )
633  {
634  mapsToRestore->insert( newMap, ( int )( newMap->previewMode() ) );
636  }
637  addComposerMap( newMap, false );
638 
639  if ( pos )
640  {
641  if ( pasteInPlace )
642  {
643  newMap->setItemPosition( newMap->transform().dx(), fmod( newMap->transform().dy(), ( paperHeight() + spaceBetweenPages() ) ) );
644  newMap->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
645  }
646  else
647  {
648  newMap->setItemPosition( pos->x(), pos->y() );
649  }
650  }
651 
652  if ( addUndoCommands )
653  {
654  pushAddRemoveCommand( newMap, tr( "Map added" ) );
655  }
656  }
657  // arrow
658  QDomNodeList composerArrowList = elem.elementsByTagName( "ComposerArrow" );
659  for ( int i = 0; i < composerArrowList.size(); ++i )
660  {
661  QDomElement currentComposerArrowElem = composerArrowList.at( i ).toElement();
662  QgsComposerArrow* newArrow = new QgsComposerArrow( this );
663  newArrow->readXML( currentComposerArrowElem, doc );
664  if ( pos )
665  {
666  if ( pasteInPlace )
667  {
668  newArrow->setItemPosition( newArrow->transform().dx(), fmod( newArrow->transform().dy(), ( paperHeight() + spaceBetweenPages() ) ) );
669  newArrow->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
670  }
671  else
672  {
673  newArrow->setItemPosition( pos->x(), pos->y() );
674  }
675  }
676  addComposerArrow( newArrow );
677  if ( addUndoCommands )
678  {
679  pushAddRemoveCommand( newArrow, tr( "Arrow added" ) );
680  }
681  }
682  // scalebar
683  QDomNodeList composerScaleBarList = elem.elementsByTagName( "ComposerScaleBar" );
684  for ( int i = 0; i < composerScaleBarList.size(); ++i )
685  {
686  QDomElement currentComposerScaleBarElem = composerScaleBarList.at( i ).toElement();
687  QgsComposerScaleBar* newScaleBar = new QgsComposerScaleBar( this );
688  newScaleBar->readXML( currentComposerScaleBarElem, doc );
689  if ( pos )
690  {
691  if ( pasteInPlace )
692  {
693  newScaleBar->setItemPosition( newScaleBar->transform().dx(), fmod( newScaleBar->transform().dy(), ( paperHeight() + spaceBetweenPages() ) ) );
694  newScaleBar->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
695  }
696  else
697  {
698  newScaleBar->setItemPosition( pos->x(), pos->y() );
699  }
700  }
701  addComposerScaleBar( newScaleBar );
702  if ( addUndoCommands )
703  {
704  pushAddRemoveCommand( newScaleBar, tr( "Scale bar added" ) );
705  }
706  }
707  // shape
708  QDomNodeList composerShapeList = elem.elementsByTagName( "ComposerShape" );
709  for ( int i = 0; i < composerShapeList.size(); ++i )
710  {
711  QDomElement currentComposerShapeElem = composerShapeList.at( i ).toElement();
712  QgsComposerShape* newShape = new QgsComposerShape( this );
713  newShape->readXML( currentComposerShapeElem, doc );
714  if ( pos )
715  {
716  if ( pasteInPlace )
717  {
718  newShape->setItemPosition( newShape->transform().dx(), fmod( newShape->transform().dy(), ( paperHeight() + spaceBetweenPages() ) ) );
719  newShape->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
720  }
721  else
722  {
723  newShape->setItemPosition( pos->x(), pos->y() );
724  }
725  }
726  addComposerShape( newShape );
727  if ( addUndoCommands )
728  {
729  pushAddRemoveCommand( newShape, tr( "Shape added" ) );
730  }
731  }
732  // picture
733  QDomNodeList composerPictureList = elem.elementsByTagName( "ComposerPicture" );
734  for ( int i = 0; i < composerPictureList.size(); ++i )
735  {
736  QDomElement currentComposerPictureElem = composerPictureList.at( i ).toElement();
737  QgsComposerPicture* newPicture = new QgsComposerPicture( this );
738  newPicture->readXML( currentComposerPictureElem, doc );
739  if ( pos )
740  {
741  if ( pasteInPlace )
742  {
743  newPicture->setItemPosition( newPicture->transform().dx(), fmod( newPicture->transform().dy(), ( paperHeight() + spaceBetweenPages() ) ) );
744  newPicture->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
745  }
746  else
747  {
748  newPicture->setItemPosition( pos->x(), pos->y() );
749  }
750  }
751  addComposerPicture( newPicture );
752  if ( addUndoCommands )
753  {
754  pushAddRemoveCommand( newPicture, tr( "Picture added" ) );
755  }
756  }
757  // legend
758  QDomNodeList composerLegendList = elem.elementsByTagName( "ComposerLegend" );
759  for ( int i = 0; i < composerLegendList.size(); ++i )
760  {
761  QDomElement currentComposerLegendElem = composerLegendList.at( i ).toElement();
762  QgsComposerLegend* newLegend = new QgsComposerLegend( this );
763  newLegend->readXML( currentComposerLegendElem, doc );
764  if ( pos )
765  {
766  if ( pasteInPlace )
767  {
768  newLegend->setItemPosition( newLegend->transform().dx(), fmod( newLegend->transform().dy(), ( paperHeight() + spaceBetweenPages() ) ) );
769  newLegend->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
770  }
771  else
772  {
773  newLegend->setItemPosition( pos->x(), pos->y() );
774  }
775  }
776  addComposerLegend( newLegend );
777  if ( addUndoCommands )
778  {
779  pushAddRemoveCommand( newLegend, tr( "Legend added" ) );
780  }
781  }
782  // table
783  QDomNodeList composerTableList = elem.elementsByTagName( "ComposerAttributeTable" );
784  for ( int i = 0; i < composerTableList.size(); ++i )
785  {
786  QDomElement currentComposerTableElem = composerTableList.at( i ).toElement();
788  newTable->readXML( currentComposerTableElem, doc );
789  if ( pos )
790  {
791  if ( pasteInPlace )
792  {
793  newTable->setItemPosition( newTable->transform().dx(), fmod( newTable->transform().dy(), ( paperHeight() + spaceBetweenPages() ) ) );
794  newTable->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
795  }
796  else
797  {
798  newTable->setItemPosition( pos->x(), pos->y() );
799  }
800  }
801  addComposerTable( newTable );
802  if ( addUndoCommands )
803  {
804  pushAddRemoveCommand( newTable, tr( "Table added" ) );
805  }
806  }
807  // html
808  QDomNodeList composerHtmlList = elem.elementsByTagName( "ComposerHtml" );
809  for ( int i = 0; i < composerHtmlList.size(); ++i )
810  {
811  QDomElement currentHtmlElem = composerHtmlList.at( i ).toElement();
812  QgsComposerHtml* newHtml = new QgsComposerHtml( this, false );
813  newHtml->readXML( currentHtmlElem, doc );
814  newHtml->setCreateUndoCommands( true );
815  this->addMultiFrame( newHtml );
816  }
817 
818 
819  // groups (must be last as it references uuids of above items)
820  QDomNodeList groupList = elem.elementsByTagName( "ComposerItemGroup" );
821  for ( int i = 0; i < groupList.size(); ++i )
822  {
823  QDomElement groupElem = groupList.at( i ).toElement();
824  QgsComposerItemGroup *newGroup = new QgsComposerItemGroup( this );
825  newGroup->readXML( groupElem, doc );
826  addItem( newGroup );
827  }
828 }
829 
831 {
832  if ( !item )
833  {
834  return;
835  }
836  mItemZList.push_back( item );
837  item->setZValue( mItemZList.size() );
838 }
839 
841 {
842  if ( !item )
843  {
844  return;
845  }
846  mItemZList.removeAll( item );
847 }
848 
850 {
851  QList<QgsComposerItem*> selectedItems = selectedComposerItems();
852  QList<QgsComposerItem*>::iterator it = selectedItems.begin();
853  for ( ; it != selectedItems.end(); ++it )
854  {
855  raiseItem( *it );
856  }
857 
858  //update all positions
859  updateZValues();
860  update();
861 }
862 
864 {
865  //search item
866  QMutableLinkedListIterator<QgsComposerItem*> it( mItemZList );
867  if ( it.findNext( item ) )
868  {
869  if ( it.hasNext() )
870  {
871  it.remove();
872  it.next();
873  it.insert( item );
874  }
875  }
876 }
877 
879 {
880  QList<QgsComposerItem*> selectedItems = selectedComposerItems();
881  QList<QgsComposerItem*>::iterator it = selectedItems.begin();
882  for ( ; it != selectedItems.end(); ++it )
883  {
884  lowerItem( *it );
885  }
886 
887  //update all positions
888  updateZValues();
889  update();
890 }
891 
893 {
894  //search item
895  QMutableLinkedListIterator<QgsComposerItem*> it( mItemZList );
896  if ( it.findNext( item ) )
897  {
898  it.previous();
899  if ( it.hasPrevious() )
900  {
901  it.remove();
902  it.previous();
903  it.insert( item );
904  }
905  }
906 }
907 
909 {
910  QList<QgsComposerItem*> selectedItems = selectedComposerItems();
911  QList<QgsComposerItem*>::iterator it = selectedItems.begin();
912 
913  for ( ; it != selectedItems.end(); ++it )
914  {
915  moveItemToTop( *it );
916  }
917 
918  //update all positions
919  updateZValues();
920  update();
921 }
922 
924 {
925  //search item
926  QMutableLinkedListIterator<QgsComposerItem*> it( mItemZList );
927  if ( it.findNext( item ) )
928  {
929  it.remove();
930  }
931  mItemZList.push_back( item );
932 }
933 
935 {
936  QList<QgsComposerItem*> selectedItems = selectedComposerItems();
937  QList<QgsComposerItem*>::iterator it = selectedItems.begin();
938  for ( ; it != selectedItems.end(); ++it )
939  {
940  moveItemToBottom( *it );
941  }
942 
943  //update all positions
944  updateZValues();
945  update();
946 }
947 
949 {
950  //search item
951  QMutableLinkedListIterator<QgsComposerItem*> it( mItemZList );
952  if ( it.findNext( item ) )
953  {
954  it.remove();
955  }
956  mItemZList.push_front( item );
957 }
958 
960 {
961  QList<QgsComposerItem*> selectedItems = selectedComposerItems();
962  if ( selectedItems.size() < 2 )
963  {
964  return;
965  }
966 
967  QRectF selectedItemBBox;
968  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
969  {
970  return;
971  }
972 
973  double minXCoordinate = selectedItemBBox.left();
974 
975  //align items left to minimum x coordinate
976  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items left" ) );
977  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
978  for ( ; align_it != selectedItems.end(); ++align_it )
979  {
980  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
981  subcommand->savePreviousState();
982  QTransform itemTransform = ( *align_it )->transform();
983  itemTransform.translate( minXCoordinate - itemTransform.dx(), 0 );
984  ( *align_it )->setTransform( itemTransform );
985  subcommand->saveAfterState();
986  }
987  mUndoStack.push( parentCommand );
988 }
989 
991 {
992  QList<QgsComposerItem*> selectedItems = selectedComposerItems();
993  if ( selectedItems.size() < 2 )
994  {
995  return;
996  }
997 
998  QRectF selectedItemBBox;
999  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1000  {
1001  return;
1002  }
1003 
1004  double averageXCoord = ( selectedItemBBox.left() + selectedItemBBox.right() ) / 2.0;
1005 
1006  //place items
1007  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items horizontal center" ) );
1008  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1009  for ( ; align_it != selectedItems.end(); ++align_it )
1010  {
1011  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1012  subcommand->savePreviousState();
1013  QTransform itemTransform = ( *align_it )->transform();
1014  itemTransform.translate( averageXCoord - itemTransform.dx() - ( *align_it )->rect().width() / 2.0, 0 );
1015  ( *align_it )->setTransform( itemTransform );
1016  subcommand->saveAfterState();
1017  }
1018  mUndoStack.push( parentCommand );
1019 }
1020 
1022 {
1023  QList<QgsComposerItem*> selectedItems = selectedComposerItems();
1024  if ( selectedItems.size() < 2 )
1025  {
1026  return;
1027  }
1028 
1029  QRectF selectedItemBBox;
1030  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1031  {
1032  return;
1033  }
1034 
1035  double maxXCoordinate = selectedItemBBox.right();
1036 
1037  //align items right to maximum x coordinate
1038  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items right" ) );
1039  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1040  for ( ; align_it != selectedItems.end(); ++align_it )
1041  {
1042  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1043  subcommand->savePreviousState();
1044  QTransform itemTransform = ( *align_it )->transform();
1045  itemTransform.translate( maxXCoordinate - itemTransform.dx() - ( *align_it )->rect().width(), 0 );
1046  ( *align_it )->setTransform( itemTransform );
1047  subcommand->saveAfterState();
1048  }
1049  mUndoStack.push( parentCommand );
1050 }
1051 
1053 {
1054  QList<QgsComposerItem*> selectedItems = selectedComposerItems();
1055  if ( selectedItems.size() < 2 )
1056  {
1057  return;
1058  }
1059 
1060  QRectF selectedItemBBox;
1061  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1062  {
1063  return;
1064  }
1065 
1066  double minYCoordinate = selectedItemBBox.top();
1067 
1068  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items top" ) );
1069  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1070  for ( ; align_it != selectedItems.end(); ++align_it )
1071  {
1072  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1073  subcommand->savePreviousState();
1074  QTransform itemTransform = ( *align_it )->transform();
1075  itemTransform.translate( 0, minYCoordinate - itemTransform.dy() );
1076  ( *align_it )->setTransform( itemTransform );
1077  subcommand->saveAfterState();
1078  }
1079  mUndoStack.push( parentCommand );
1080 }
1081 
1083 {
1084  QList<QgsComposerItem*> selectedItems = selectedComposerItems();
1085  if ( selectedItems.size() < 2 )
1086  {
1087  return;
1088  }
1089 
1090  QRectF selectedItemBBox;
1091  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1092  {
1093  return;
1094  }
1095 
1096  double averageYCoord = ( selectedItemBBox.top() + selectedItemBBox.bottom() ) / 2.0;
1097  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items vertical center" ) );
1098  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1099  for ( ; align_it != selectedItems.end(); ++align_it )
1100  {
1101  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1102  subcommand->savePreviousState();
1103  QTransform itemTransform = ( *align_it )->transform();
1104  itemTransform.translate( 0, averageYCoord - itemTransform.dy() - ( *align_it )->rect().height() / 2 );
1105  ( *align_it )->setTransform( itemTransform );
1106  subcommand->saveAfterState();
1107  }
1108  mUndoStack.push( parentCommand );
1109 }
1110 
1112 {
1113  QList<QgsComposerItem*> selectedItems = selectedComposerItems();
1114  if ( selectedItems.size() < 2 )
1115  {
1116  return;
1117  }
1118 
1119  QRectF selectedItemBBox;
1120  if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
1121  {
1122  return;
1123  }
1124 
1125  double maxYCoord = selectedItemBBox.bottom();
1126  QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items bottom" ) );
1127  QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
1128  for ( ; align_it != selectedItems.end(); ++align_it )
1129  {
1130  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
1131  subcommand->savePreviousState();
1132  QTransform itemTransform = ( *align_it )->transform();
1133  itemTransform.translate( 0, maxYCoord - itemTransform.dy() - ( *align_it )->rect().height() );
1134  ( *align_it )->setTransform( itemTransform );
1135  subcommand->saveAfterState();
1136  }
1137  mUndoStack.push( parentCommand );
1138 }
1139 
1141 {
1142  int counter = 1;
1143  QLinkedList<QgsComposerItem*>::iterator it = mItemZList.begin();
1144  QgsComposerItem* currentItem = 0;
1145 
1146  QUndoCommand* parentCommand = new QUndoCommand( tr( "Item z-order changed" ) );
1147  for ( ; it != mItemZList.end(); ++it )
1148  {
1149  currentItem = *it;
1150  if ( currentItem )
1151  {
1152  QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *it, "", parentCommand );
1153  subcommand->savePreviousState();
1154  currentItem->setZValue( counter );
1155  subcommand->saveAfterState();
1156  }
1157  ++counter;
1158  }
1159  mUndoStack.push( parentCommand );
1160 }
1161 
1163 {
1164  if ( mItemZList.size() < 2 )
1165  {
1166  return;
1167  }
1168 
1169  QLinkedList<QgsComposerItem*>::const_iterator lIt = mItemZList.constBegin();
1170  QLinkedList<QgsComposerItem*> sortedList;
1171 
1172  for ( ; lIt != mItemZList.constEnd(); ++lIt )
1173  {
1174  QLinkedList<QgsComposerItem*>::iterator insertIt = sortedList.begin();
1175  for ( ; insertIt != sortedList.end(); ++insertIt )
1176  {
1177  if (( *lIt )->zValue() < ( *insertIt )->zValue() )
1178  {
1179  break;
1180  }
1181  }
1182  sortedList.insert( insertIt, ( *lIt ) );
1183  }
1184 
1185  mItemZList = sortedList;
1186 }
1187 
1188 QPointF QgsComposition::snapPointToGrid( const QPointF& scenePoint ) const
1189 {
1190  if ( !mSnapToGrid || mSnapGridResolution <= 0 )
1191  {
1192  return scenePoint;
1193  }
1194 
1195  //y offset to current page
1196  int pageNr = ( int )( scenePoint.y() / ( mPageHeight + mSpaceBetweenPages ) );
1197  double yOffset = pageNr * ( mPageHeight + mSpaceBetweenPages );
1198  double yPage = scenePoint.y() - yOffset; //y-coordinate relative to current page
1199 
1200  //snap x coordinate
1201  int xRatio = ( int )(( scenePoint.x() - mSnapGridOffsetX ) / mSnapGridResolution + 0.5 );
1202  int yRatio = ( int )(( yPage - mSnapGridOffsetY ) / mSnapGridResolution + 0.5 );
1203 
1204  return QPointF( xRatio * mSnapGridResolution + mSnapGridOffsetX, yRatio * mSnapGridResolution + mSnapGridOffsetY + yOffset );
1205 }
1206 
1207 QPointF QgsComposition::alignItem( const QgsComposerItem* item, double& alignX, double& alignY, double dx, double dy )
1208 {
1209  if ( !item )
1210  {
1211  return QPointF();
1212  }
1213 
1214  double left = item->transform().dx() + dx;
1215  double right = left + item->rect().width();
1216  double midH = ( left + right ) / 2.0;
1217  double top = item->transform().dy() + dy;
1218  double bottom = top + item->rect().height();
1219  double midV = ( top + bottom ) / 2.0;
1220 
1221  QMap<double, const QgsComposerItem* > xAlignCoordinates;
1222  QMap<double, const QgsComposerItem* > yAlignCoordinates;
1223  collectAlignCoordinates( xAlignCoordinates, yAlignCoordinates, item );
1224 
1225  //find nearest matches x
1226  double xItemLeft = left; //new left coordinate of the item
1227  double xAlignCoord = 0;
1228  double smallestDiffX = DBL_MAX;
1229 
1230  checkNearestItem( left, xAlignCoordinates, smallestDiffX, 0, xItemLeft, xAlignCoord );
1231  checkNearestItem( midH, xAlignCoordinates, smallestDiffX, ( left - right ) / 2.0, xItemLeft, xAlignCoord );
1232  checkNearestItem( right, xAlignCoordinates, smallestDiffX, left - right, xItemLeft, xAlignCoord );
1233 
1234  //find nearest matches y
1235  double yItemTop = top; //new top coordinate of the item
1236  double yAlignCoord = 0;
1237  double smallestDiffY = DBL_MAX;
1238 
1239  checkNearestItem( top, yAlignCoordinates, smallestDiffY, 0, yItemTop, yAlignCoord );
1240  checkNearestItem( midV, yAlignCoordinates, smallestDiffY, ( top - bottom ) / 2.0, yItemTop, yAlignCoord );
1241  checkNearestItem( bottom, yAlignCoordinates, smallestDiffY, top - bottom, yItemTop, yAlignCoord );
1242 
1243  double xCoord = ( smallestDiffX < 5 ) ? xItemLeft : item->transform().dx() + dx;
1244  alignX = ( smallestDiffX < 5 ) ? xAlignCoord : -1;
1245  double yCoord = ( smallestDiffY < 5 ) ? yItemTop : item->transform().dy() + dy;
1246  alignY = ( smallestDiffY < 5 ) ? yAlignCoord : -1;
1247  return QPointF( xCoord, yCoord );
1248 }
1249 
1250 QPointF QgsComposition::alignPos( const QPointF& pos, const QgsComposerItem* excludeItem, double& alignX, double& alignY )
1251 {
1252  QMap<double, const QgsComposerItem* > xAlignCoordinates;
1253  QMap<double, const QgsComposerItem* > yAlignCoordinates;
1254  collectAlignCoordinates( xAlignCoordinates, yAlignCoordinates, excludeItem );
1255 
1256  double nearestX = pos.x();
1257  double nearestY = pos.y();
1258  if ( !nearestItem( xAlignCoordinates, pos.x(), nearestX )
1259  || !nearestItem( yAlignCoordinates, pos.y(), nearestY ) )
1260  {
1261  alignX = -1;
1262  alignY = -1;
1263  return pos;
1264  }
1265 
1266  QPointF result( pos.x(), pos.y() );
1267  if ( abs( nearestX - pos.x() ) < mAlignmentSnapTolerance )
1268  {
1269  result.setX( nearestX );
1270  alignX = nearestX;
1271  }
1272  else
1273  {
1274  alignX = -1;
1275  }
1276 
1277  if ( abs( nearestY - pos.y() ) < mAlignmentSnapTolerance )
1278  {
1279  result.setY( nearestY );
1280  alignY = nearestY;
1281  }
1282  else
1283  {
1284  alignY = -1;
1285  }
1286  return result;
1287 }
1288 
1289 QGraphicsLineItem* QgsComposition::addSnapLine()
1290 {
1291  QGraphicsLineItem* item = new QGraphicsLineItem();
1292  QPen linePen( Qt::SolidLine );
1293  linePen.setColor( Qt::red );
1294  // use a pen width of 0, since this activates a cosmetic pen
1295  // which doesn't scale with the composer and keeps a constant size
1296  linePen.setWidthF( 0 );
1297  item->setPen( linePen );
1298  item->setZValue( 100 );
1299  addItem( item );
1300  mSnapLines.push_back( item );
1301  return item;
1302 }
1303 
1304 void QgsComposition::removeSnapLine( QGraphicsLineItem* line )
1305 {
1306  removeItem( line );
1307  mSnapLines.removeAll( line );
1308  delete line;
1309 }
1310 
1312 {
1313  QList< QGraphicsLineItem* >::iterator it = mSnapLines.begin();
1314  for ( ; it != mSnapLines.end(); ++it )
1315  {
1316  if ( visible )
1317  {
1318  ( *it )->show();
1319  }
1320  else
1321  {
1322  ( *it )->hide();
1323  }
1324  }
1325 }
1326 
1327 QGraphicsLineItem* QgsComposition::nearestSnapLine( bool horizontal, double x, double y, double tolerance,
1328  QList< QPair< QgsComposerItem*, QgsComposerItem::ItemPositionMode> >& snappedItems )
1329 {
1330  double minSqrDist = DBL_MAX;
1331  QGraphicsLineItem* item = 0;
1332  double currentXCoord = 0;
1333  double currentYCoord = 0;
1334  double currentSqrDist = 0;
1335  double sqrTolerance = tolerance * tolerance;
1336 
1337  snappedItems.clear();
1338 
1339  QList< QGraphicsLineItem* >::const_iterator it = mSnapLines.constBegin();
1340  for ( ; it != mSnapLines.constEnd(); ++it )
1341  {
1342  bool itemHorizontal = qgsDoubleNear(( *it )->line().y2() - ( *it )->line().y1(), 0 );
1343  if ( horizontal && itemHorizontal )
1344  {
1345  currentYCoord = ( *it )->line().y1();
1346  currentSqrDist = ( y - currentYCoord ) * ( y - currentYCoord );
1347  }
1348  else if ( !itemHorizontal )
1349  {
1350  currentXCoord = ( *it )->line().x1();
1351  currentSqrDist = ( x - currentXCoord ) * ( x - currentXCoord );
1352  }
1353 
1354  if ( currentSqrDist < minSqrDist && currentSqrDist < sqrTolerance )
1355  {
1356  item = *it;
1357  minSqrDist = currentSqrDist;
1358  }
1359  }
1360 
1361  double itemTolerance = 0.0000001;
1362  if ( item )
1363  {
1364  //go through all the items to find items snapped to this snap line
1365  QList<QGraphicsItem *> itemList = items();
1366  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
1367  for ( ; itemIt != itemList.end(); ++itemIt )
1368  {
1369  QgsComposerItem* currentItem = dynamic_cast<QgsComposerItem*>( *itemIt );
1370  if ( !currentItem || currentItem->type() == QgsComposerItem::ComposerPaper )
1371  {
1372  continue;
1373  }
1374 
1375  if ( horizontal )
1376  {
1377  if ( qgsDoubleNear( currentYCoord, currentItem->transform().dy() + currentItem->rect().top(), itemTolerance ) )
1378  {
1379  snappedItems.append( qMakePair( currentItem, QgsComposerItem::UpperMiddle ) );
1380  }
1381  else if ( qgsDoubleNear( currentYCoord, currentItem->transform().dy() + currentItem->rect().center().y(), itemTolerance ) )
1382  {
1383  snappedItems.append( qMakePair( currentItem, QgsComposerItem::Middle ) );
1384  }
1385  else if ( qgsDoubleNear( currentYCoord, currentItem->transform().dy() + currentItem->rect().bottom(), itemTolerance ) )
1386  {
1387  snappedItems.append( qMakePair( currentItem, QgsComposerItem::LowerMiddle ) );
1388  }
1389  }
1390  else
1391  {
1392  if ( qgsDoubleNear( currentXCoord, currentItem->transform().dx(), itemTolerance ) )
1393  {
1394  snappedItems.append( qMakePair( currentItem, QgsComposerItem::MiddleLeft ) );
1395  }
1396  else if ( qgsDoubleNear( currentXCoord, currentItem->transform().dx() + currentItem->rect().center().x(), itemTolerance ) )
1397  {
1398  snappedItems.append( qMakePair( currentItem, QgsComposerItem::Middle ) );
1399  }
1400  else if ( qgsDoubleNear( currentXCoord, currentItem->transform().dx() + currentItem->rect().width(), itemTolerance ) )
1401  {
1402  snappedItems.append( qMakePair( currentItem, QgsComposerItem::MiddleRight ) );
1403  }
1404  }
1405  }
1406  }
1407 
1408  return item;
1409 }
1410 
1412 {
1413  QList<QgsComposerItem*> selectedItems = selectedComposerItems();
1414  if ( selectedItems.size() < 1 )
1415  {
1416  return 1;
1417  }
1418 
1419  //set the box to the first item
1420  QgsComposerItem* currentItem = selectedItems.at( 0 );
1421  double minX = currentItem->transform().dx();
1422  double minY = currentItem->transform().dy();
1423  double maxX = minX + currentItem->rect().width();
1424  double maxY = minY + currentItem->rect().height();
1425 
1426  double currentMinX, currentMinY, currentMaxX, currentMaxY;
1427 
1428  for ( int i = 1; i < selectedItems.size(); ++i )
1429  {
1430  currentItem = selectedItems.at( i );
1431  currentMinX = currentItem->transform().dx();
1432  currentMinY = currentItem->transform().dy();
1433  currentMaxX = currentMinX + currentItem->rect().width();
1434  currentMaxY = currentMinY + currentItem->rect().height();
1435 
1436  if ( currentMinX < minX )
1437  minX = currentMinX;
1438  if ( currentMaxX > maxX )
1439  maxX = currentMaxX;
1440  if ( currentMinY < minY )
1441  minY = currentMinY;
1442  if ( currentMaxY > maxY )
1443  maxY = currentMaxY;
1444  }
1445 
1446  bRect.setTopLeft( QPointF( minX, minY ) );
1447  bRect.setBottomRight( QPointF( maxX, maxY ) );
1448  return 0;
1449 }
1450 
1452 {
1453  mSnapToGrid = b;
1454  updatePaperItems();
1455  saveSettings();
1456 }
1457 
1459 {
1460  mSnapGridResolution = r;
1461  updatePaperItems();
1462  saveSettings();
1463 }
1464 
1466 {
1467  mSnapGridOffsetX = offset;
1468  updatePaperItems();
1469  saveSettings();
1470 }
1471 
1473 {
1474  mSnapGridOffsetY = offset;
1475  updatePaperItems();
1476  saveSettings();
1477 }
1478 
1479 void QgsComposition::setGridPen( const QPen& p )
1480 {
1481  mGridPen = p;
1482  updatePaperItems();
1483  saveSettings();
1484 }
1485 
1487 {
1488  mGridStyle = s;
1489  updatePaperItems();
1490  saveSettings();
1491 }
1492 
1494 {
1495  mSelectionTolerance = tol;
1496  saveSettings();
1497 }
1498 
1500 {
1501  //read grid style, grid color and pen width from settings
1502  QSettings s;
1503 
1504  QString gridStyleString;
1505  int red, green, blue;
1506  double penWidth;
1507 
1508  gridStyleString = s.value( "/qgis/composerGridStyle", "Dots" ).toString();
1509  penWidth = s.value( "/qgis/composerGridWidth", 0.5 ).toDouble();
1510  red = s.value( "/qgis/composerGridRed", 0 ).toInt();
1511  green = s.value( "/qgis/composerGridGreen", 0 ).toInt();
1512  blue = s.value( "/qgis/composerGridBlue", 0 ).toInt();
1513 
1514  mGridPen.setColor( QColor( red, green, blue ) );
1515  mGridPen.setWidthF( penWidth );
1516 
1517  if ( gridStyleString == "Dots" )
1518  {
1519  mGridStyle = Dots;
1520  }
1521  else if ( gridStyleString == "Crosses" )
1522  {
1523  mGridStyle = Crosses;
1524  }
1525  else
1526  {
1527  mGridStyle = Solid;
1528  }
1529 
1530  mSelectionTolerance = s.value( "/qgis/composerSelectionTolerance", 0.0 ).toDouble();
1531 }
1532 
1534 {
1535  //store grid appearance settings
1536  QSettings s;
1537  s.setValue( "/qgis/composerGridWidth", mGridPen.widthF() );
1538  s.setValue( "/qgis/composerGridRed", mGridPen.color().red() );
1539  s.setValue( "/qgis/composerGridGreen", mGridPen.color().green() );
1540  s.setValue( "/qgis/composerGridBlue", mGridPen.color().blue() );
1541 
1542  if ( mGridStyle == Solid )
1543  {
1544  s.setValue( "/qgis/composerGridStyle", "Solid" );
1545  }
1546  else if ( mGridStyle == Dots )
1547  {
1548  s.setValue( "/qgis/composerGridStyle", "Dots" );
1549  }
1550  else if ( mGridStyle == Crosses )
1551  {
1552  s.setValue( "/qgis/composerGridStyle", "Crosses" );
1553  }
1554 
1555  //store also selection tolerance
1556  s.setValue( "/qgis/composerSelectionTolerance", mSelectionTolerance );
1557 }
1558 
1560 {
1561  delete mActiveItemCommand;
1562  if ( !item )
1563  {
1564  mActiveItemCommand = 0;
1565  return;
1566  }
1567 
1569  {
1570  mActiveItemCommand = new QgsComposerItemCommand( item, commandText );
1571  }
1572  else
1573  {
1574  mActiveItemCommand = new QgsComposerMergeCommand( c, item, commandText );
1575  }
1577 }
1578 
1580 {
1581  if ( mActiveItemCommand )
1582  {
1584  if ( mActiveItemCommand->containsChange() ) //protect against empty commands
1585  {
1587  QgsProject::instance()->dirty( true );
1588  }
1589  else
1590  {
1591  delete mActiveItemCommand;
1592  }
1593  mActiveItemCommand = 0;
1594  }
1595 }
1596 
1598 {
1599  delete mActiveItemCommand;
1600  mActiveItemCommand = 0;
1601 }
1602 
1603 void QgsComposition::beginMultiFrameCommand( QgsComposerMultiFrame* multiFrame, const QString& text )
1604 {
1605  delete mActiveMultiFrameCommand;
1606  mActiveMultiFrameCommand = new QgsComposerMultiFrameCommand( multiFrame, text );
1608 }
1609 
1611 {
1613  {
1616  {
1618  QgsProject::instance()->dirty( true );
1619  }
1620  else
1621  {
1622  delete mActiveMultiFrameCommand;
1623  }
1625  }
1626 }
1627 
1629 {
1630  mMultiFrames.insert( multiFrame );
1631 }
1632 
1634 {
1635  mMultiFrames.remove( multiFrame );
1636 }
1637 
1639 {
1640  addItem( arrow );
1641  emit composerArrowAdded( arrow );
1642  clearSelection();
1643  arrow->setSelected( true );
1644  emit selectedItemChanged( arrow );
1645 }
1646 
1648 {
1649  addItem( label );
1650  emit composerLabelAdded( label );
1651  clearSelection();
1652  label->setSelected( true );
1653  emit selectedItemChanged( label );
1654 }
1655 
1656 void QgsComposition::addComposerMap( QgsComposerMap* map, bool setDefaultPreviewStyle )
1657 {
1658  addItem( map );
1659  if ( setDefaultPreviewStyle )
1660  {
1661  //set default preview mode to cache. Must be done here between adding composer map to scene and emiting signal
1663  }
1664 
1665  if ( map->previewMode() != QgsComposerMap::Rectangle )
1666  {
1667  map->cache();
1668  }
1669 
1670  emit composerMapAdded( map );
1671  clearSelection();
1672  map->setSelected( true );
1673  emit selectedItemChanged( map );
1674 }
1675 
1677 {
1678  addItem( scaleBar );
1679  emit composerScaleBarAdded( scaleBar );
1680  clearSelection();
1681  scaleBar->setSelected( true );
1682  emit selectedItemChanged( scaleBar );
1683 }
1684 
1686 {
1687  //take first available map
1688  QList<const QgsComposerMap*> mapItemList = composerMapItems();
1689  if ( mapItemList.size() > 0 )
1690  {
1691  legend->setComposerMap( mapItemList.at( 0 ) );
1692  }
1693  addItem( legend );
1694  emit composerLegendAdded( legend );
1695  clearSelection();
1696  legend->setSelected( true );
1697  emit selectedItemChanged( legend );
1698 }
1699 
1701 {
1702  addItem( picture );
1703  emit composerPictureAdded( picture );
1704  clearSelection();
1705  picture->setSelected( true );
1706  emit selectedItemChanged( picture );
1707 }
1708 
1710 {
1711  addItem( shape );
1712  emit composerShapeAdded( shape );
1713  clearSelection();
1714  shape->setSelected( true );
1715  emit selectedItemChanged( shape );
1716 }
1717 
1719 {
1720  addItem( table );
1721  emit composerTableAdded( table );
1722  clearSelection();
1723  table->setSelected( true );
1724  emit selectedItemChanged( table );
1725 }
1726 
1728 {
1729  addItem( frame );
1730  emit composerHtmlFrameAdded( html, frame );
1731  clearSelection();
1732  frame->setSelected( true );
1733  emit selectedItemChanged( frame );
1734 }
1735 
1736 void QgsComposition::removeComposerItem( QgsComposerItem* item, bool createCommand )
1737 {
1738  QgsComposerMap* map = dynamic_cast<QgsComposerMap *>( item );
1739 
1740  if ( !map || !map->isDrawing() ) //don't delete a composer map while it draws
1741  {
1742  removeItem( item );
1743  QgsComposerItemGroup* itemGroup = dynamic_cast<QgsComposerItemGroup*>( item );
1744  if ( itemGroup )
1745  {
1746  //add add/remove item command for every item in the group
1747  QUndoCommand* parentCommand = new QUndoCommand( tr( "Remove item group" ) );
1748 
1749  QSet<QgsComposerItem*> groupedItems = itemGroup->items();
1750  QSet<QgsComposerItem*>::iterator it = groupedItems.begin();
1751  for ( ; it != groupedItems.end(); ++it )
1752  {
1753  QgsAddRemoveItemCommand* subcommand = new QgsAddRemoveItemCommand( QgsAddRemoveItemCommand::Removed, *it, this, "", parentCommand );
1754  connectAddRemoveCommandSignals( subcommand );
1755  emit itemRemoved( *it );
1756  }
1757 
1758  undoStack()->push( parentCommand );
1759  delete itemGroup;
1760  emit itemRemoved( itemGroup );
1761  }
1762  else
1763  {
1764  bool frameItem = ( item->type() == QgsComposerItem::ComposerFrame );
1765  QgsComposerMultiFrame* multiFrame = 0;
1766  if ( createCommand )
1767  {
1768  if ( frameItem ) //multiframe tracks item changes
1769  {
1770  multiFrame = static_cast<QgsComposerFrame*>( item )->multiFrame();
1771  item->beginItemCommand( tr( "Frame deleted" ) );
1772  emit itemRemoved( item );
1773  item->endItemCommand();
1774  }
1775  else
1776  {
1777  emit itemRemoved( item );
1778  pushAddRemoveCommand( item, tr( "Item deleted" ), QgsAddRemoveItemCommand::Removed );
1779  }
1780  }
1781  else
1782  {
1783  emit itemRemoved( item );
1784  }
1785 
1786  //check if there are frames left. If not, remove the multi frame
1787  if ( frameItem && multiFrame )
1788  {
1789  if ( multiFrame->frameCount() < 1 )
1790  {
1791  removeMultiFrame( multiFrame );
1792  if ( createCommand )
1793  {
1795  multiFrame, this, tr( "Multiframe removed" ) );
1796  undoStack()->push( command );
1797  }
1798  else
1799  {
1800  delete multiFrame;
1801  }
1802  }
1803  }
1804  }
1805  }
1806 }
1807 
1809 {
1810  QgsAddRemoveItemCommand* c = new QgsAddRemoveItemCommand( state, item, this, text );
1812  undoStack()->push( c );
1813  QgsProject::instance()->dirty( true );
1814 }
1815 
1817 {
1818  if ( !c )
1819  {
1820  return;
1821  }
1822 
1823  QObject::connect( c, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SIGNAL( itemRemoved( QgsComposerItem* ) ) );
1824  QObject::connect( c, SIGNAL( itemAdded( QgsComposerItem* ) ), this, SLOT( sendItemAddedSignal( QgsComposerItem* ) ) );
1825 }
1826 
1828 {
1829  //cast and send proper signal
1830  item->setSelected( true );
1831  QgsComposerArrow* arrow = dynamic_cast<QgsComposerArrow*>( item );
1832  if ( arrow )
1833  {
1834  emit composerArrowAdded( arrow );
1835  emit selectedItemChanged( arrow );
1836  return;
1837  }
1838  QgsComposerLabel* label = dynamic_cast<QgsComposerLabel*>( item );
1839  if ( label )
1840  {
1841  emit composerLabelAdded( label );
1842  emit selectedItemChanged( label );
1843  return;
1844  }
1845  QgsComposerMap* map = dynamic_cast<QgsComposerMap*>( item );
1846  if ( map )
1847  {
1848  emit composerMapAdded( map );
1849  emit selectedItemChanged( map );
1850  return;
1851  }
1852  QgsComposerScaleBar* scalebar = dynamic_cast<QgsComposerScaleBar*>( item );
1853  if ( scalebar )
1854  {
1855  emit composerScaleBarAdded( scalebar );
1856  emit selectedItemChanged( scalebar );
1857  return;
1858  }
1859  QgsComposerLegend* legend = dynamic_cast<QgsComposerLegend*>( item );
1860  if ( legend )
1861  {
1862  emit composerLegendAdded( legend );
1863  emit selectedItemChanged( legend );
1864  return;
1865  }
1866  QgsComposerPicture* picture = dynamic_cast<QgsComposerPicture*>( item );
1867  if ( picture )
1868  {
1869  emit composerPictureAdded( picture );
1870  emit selectedItemChanged( picture );
1871  return;
1872  }
1873  QgsComposerShape* shape = dynamic_cast<QgsComposerShape*>( item );
1874  if ( shape )
1875  {
1876  emit composerShapeAdded( shape );
1877  emit selectedItemChanged( shape );
1878  return;
1879  }
1880  QgsComposerAttributeTable* table = dynamic_cast<QgsComposerAttributeTable*>( item );
1881  if ( table )
1882  {
1883  emit composerTableAdded( table );
1884  emit selectedItemChanged( table );
1885  return;
1886  }
1887  QgsComposerFrame* frame = dynamic_cast<QgsComposerFrame*>( item );
1888  if ( frame )
1889  {
1890  //emit composerFrameAdded( multiframe, frame, );
1891  QgsComposerMultiFrame* mf = frame->multiFrame();
1892  QgsComposerHtml* html = dynamic_cast<QgsComposerHtml*>( mf );
1893  if ( html )
1894  {
1895  emit composerHtmlFrameAdded( html, frame );
1896  }
1897  emit selectedItemChanged( frame );
1898  return;
1899  }
1900 }
1901 
1903 {
1904  QList< QgsPaperItem* >::iterator paperIt = mPages.begin();
1905  for ( ; paperIt != mPages.end(); ++paperIt )
1906  {
1907  ( *paperIt )->update();
1908  }
1909 }
1910 
1912 {
1913  double paperHeight = this->paperHeight();
1914  double paperWidth = this->paperWidth();
1915  double currentY = paperHeight * mPages.size() + mPages.size() * mSpaceBetweenPages; //add 10mm visible space between pages
1916  QgsPaperItem* paperItem = new QgsPaperItem( 0, currentY, paperWidth, paperHeight, this ); //default size A4
1917  paperItem->setBrush( Qt::white );
1918  addItem( paperItem );
1919  paperItem->setZValue( 0 );
1920  mPages.push_back( paperItem );
1921 
1922  QgsExpression::setSpecialColumn( "$numpages", QVariant(( int )mPages.size() ) );
1923 }
1924 
1926 {
1927  for ( int i = 0; i < mPages.size(); ++i )
1928  {
1929  delete mPages.at( i );
1930  }
1931  mPages.clear();
1932  QgsExpression::setSpecialColumn( "$numpages", QVariant(( int )0 ) );
1933 }
1934 
1936 {
1937  QSet<QgsComposerMultiFrame*>::iterator multiFrameIt = mMultiFrames.begin();
1938  for ( ; multiFrameIt != mMultiFrames.end(); ++multiFrameIt )
1939  {
1940  delete *multiFrameIt;
1941  }
1942  mMultiFrames.clear();
1943 }
1944 
1945 void QgsComposition::beginPrintAsPDF( QPrinter& printer, const QString& file )
1946 {
1947  printer.setOutputFormat( QPrinter::PdfFormat );
1948  printer.setOutputFileName( file );
1949  printer.setPaperSize( QSizeF( paperWidth(), paperHeight() ), QPrinter::Millimeter );
1950 
1951  QgsPaintEngineHack::fixEngineFlags( printer.paintEngine() );
1952 }
1953 
1954 void QgsComposition::exportAsPDF( const QString& file )
1955 {
1956  QPrinter printer;
1957  beginPrintAsPDF( printer, file );
1958  print( printer );
1959 }
1960 
1961 void QgsComposition::doPrint( QPrinter& printer, QPainter& p )
1962 {
1963 //QgsComposition starts page numbering at 0
1964  int fromPage = ( printer.fromPage() < 1 ) ? 0 : printer.fromPage() - 1 ;
1965  int toPage = ( printer.toPage() < 1 ) ? numPages() - 1 : printer.toPage() - 1;
1966 
1967  if ( mPrintAsRaster )
1968  {
1969  for ( int i = fromPage; i <= toPage; ++i )
1970  {
1971  if ( i > fromPage )
1972  {
1973  printer.newPage();
1974  }
1975 
1976  QImage image = printPageAsRaster( i );
1977  if ( !image.isNull() )
1978  {
1979  QRectF targetArea( 0, 0, image.width(), image.height() );
1980  p.drawImage( targetArea, image, targetArea );
1981  }
1982  }
1983  }
1984 
1985  if ( !mPrintAsRaster )
1986  {
1987  for ( int i = fromPage; i <= toPage; ++i )
1988  {
1989  if ( i > fromPage )
1990  {
1991  printer.newPage();
1992  }
1993  renderPage( &p, i );
1994  }
1995  }
1996 }
1997 
1998 void QgsComposition::beginPrint( QPrinter &printer )
1999 {
2000  //set resolution based on composer setting
2001  printer.setFullPage( true );
2002  printer.setColorMode( QPrinter::Color );
2003 
2004  //set user-defined resolution
2005  printer.setResolution( printResolution() );
2006 }
2007 
2008 void QgsComposition::print( QPrinter &printer )
2009 {
2010  beginPrint( printer );
2011  QPainter p( &printer );
2012  doPrint( printer, p );
2013 }
2014 
2016 {
2017  //print out via QImage, code copied from on_mActionExportAsImage_activated
2018  int width = ( int )( printResolution() * paperWidth() / 25.4 );
2019  int height = ( int )( printResolution() * paperHeight() / 25.4 );
2020  QImage image( QSize( width, height ), QImage::Format_ARGB32 );
2021  if ( !image.isNull() )
2022  {
2023  image.setDotsPerMeterX( printResolution() / 25.4 * 1000 );
2024  image.setDotsPerMeterY( printResolution() / 25.4 * 1000 );
2025  image.fill( 0 );
2026  QPainter imagePainter( &image );
2027  renderPage( &imagePainter, page );
2028  if ( !imagePainter.isActive() ) return QImage();
2029  }
2030  return image;
2031 }
2032 
2033 void QgsComposition::renderPage( QPainter* p, int page )
2034 {
2035  if ( mPages.size() <= page )
2036  {
2037  return;
2038  }
2039 
2040  QgsPaperItem* paperItem = mPages[page];
2041  if ( !paperItem )
2042  {
2043  return;
2044  }
2045 
2046  QPaintDevice* paintDevice = p->device();
2047  if ( !paintDevice )
2048  {
2049  return;
2050  }
2051 
2052  QRectF paperRect = QRectF( paperItem->transform().dx(), paperItem->transform().dy(), paperItem->rect().width(), paperItem->rect().height() );
2053 
2054  QgsComposition::PlotStyle savedPlotStyle = mPlotStyle;
2056 
2057  setSnapLinesVisible( false );
2058  render( p, QRectF( 0, 0, paintDevice->width(), paintDevice->height() ), paperRect );
2059  setSnapLinesVisible( true );
2060 
2061  mPlotStyle = savedPlotStyle;
2062 }
2063 
2064 QString QgsComposition::encodeStringForXML( const QString& str )
2065 {
2066  QString modifiedStr( str );
2067  modifiedStr.replace( "&", "&amp;" );
2068  modifiedStr.replace( "\"", "&quot;" );
2069  modifiedStr.replace( "'", "&apos;" );
2070  modifiedStr.replace( "<", "&lt;" );
2071  modifiedStr.replace( ">", "&gt;" );
2072  return modifiedStr;
2073 }
2074 
2075 void QgsComposition::collectAlignCoordinates( QMap< double, const QgsComposerItem* >& alignCoordsX, QMap< double, const QgsComposerItem* >& alignCoordsY,
2076  const QgsComposerItem* excludeItem )
2077 {
2078  alignCoordsX.clear();
2079  alignCoordsY.clear();
2080 
2081  QList<QGraphicsItem *> itemList = items();
2082  QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
2083  for ( ; itemIt != itemList.end(); ++itemIt )
2084  {
2085  const QgsComposerItem* currentItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
2086  if ( excludeItem )
2087  {
2088  if ( !currentItem || currentItem == excludeItem )
2089  {
2090  continue;
2091  }
2092  alignCoordsX.insert( currentItem->transform().dx(), currentItem );
2093  alignCoordsX.insert( currentItem->transform().dx() + currentItem->rect().width(), currentItem );
2094  alignCoordsX.insert( currentItem->transform().dx() + currentItem->rect().center().x(), currentItem );
2095  alignCoordsY.insert( currentItem->transform().dy() + currentItem->rect().top(), currentItem );
2096  alignCoordsY.insert( currentItem->transform().dy() + currentItem->rect().center().y(), currentItem );
2097  alignCoordsY.insert( currentItem->transform().dy() + currentItem->rect().bottom(), currentItem );
2098  }
2099  }
2100 
2101  //arbitrary snap lines
2102  QList< QGraphicsLineItem* >::const_iterator sIt = mSnapLines.constBegin();
2103  for ( ; sIt != mSnapLines.constEnd(); ++sIt )
2104  {
2105  double x = ( *sIt )->line().x1();
2106  double y = ( *sIt )->line().y1();
2107  if ( qgsDoubleNear( y, 0.0 ) )
2108  {
2109  alignCoordsX.insert( x, 0 );
2110  }
2111  else
2112  {
2113  alignCoordsY.insert( y, 0 );
2114  }
2115  }
2116 }
2117 
2118 void QgsComposition::checkNearestItem( double checkCoord, const QMap< double, const QgsComposerItem* >& alignCoords, double& smallestDiff,
2119  double itemCoordOffset, double& itemCoord, double& alignCoord ) const
2120 {
2121  double currentCoord = 0;
2122  if ( !nearestItem( alignCoords, checkCoord, currentCoord ) )
2123  {
2124  return;
2125  }
2126 
2127  double currentDiff = abs( checkCoord - currentCoord );
2128  if ( currentDiff < mAlignmentSnapTolerance )
2129  {
2130  itemCoord = currentCoord + itemCoordOffset;
2131  alignCoord = currentCoord;
2132  smallestDiff = currentDiff;
2133  }
2134 }
2135 
2136 bool QgsComposition::nearestItem( const QMap< double, const QgsComposerItem* >& coords, double value, double& nearestValue )
2137 {
2138  if ( coords.size() < 1 )
2139  {
2140  return false;
2141  }
2142 
2143  QMap< double, const QgsComposerItem* >::const_iterator it = coords.lowerBound( value );
2144  if ( it == coords.constBegin() ) //value smaller than first map value
2145  {
2146  nearestValue = it.key();
2147  return true;
2148  }
2149  else if ( it == coords.constEnd() ) //value larger than last map value
2150  {
2151  --it;
2152  nearestValue = it.key();
2153  return true;
2154  }
2155  else
2156  {
2157  //get smaller value and larger value and return the closer one
2158  double upperVal = it.key();
2159  --it;
2160  double lowerVal = it.key();
2161 
2162  double lowerDiff = value - lowerVal;
2163  double upperDiff = upperVal - value;
2164  if ( lowerDiff < upperDiff )
2165  {
2166  nearestValue = lowerVal;
2167  return true;
2168  }
2169  else
2170  {
2171  nearestValue = upperVal;
2172  return true;
2173  }
2174  }
2175 }
2176