QGIS API Documentation  master-59fd5e0
src/core/composer/qgscomposition.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                               qgscomposition.cpp
00003                              -------------------
00004     begin                : January 2005
00005     copyright            : (C) 2005 by Radim Blazek
00006     email                : blazek@itc.it
00007  ***************************************************************************/
00008 /***************************************************************************
00009  *                                                                         *
00010  *   This program is free software; you can redistribute it and/or modify  *
00011  *   it under the terms of the GNU General Public License as published by  *
00012  *   the Free Software Foundation; either version 2 of the License, or     *
00013  *   (at your option) any later version.                                   *
00014  *                                                                         *
00015  ***************************************************************************/
00016 
00017 #include <stdexcept>
00018 
00019 #include "qgscomposition.h"
00020 #include "qgscomposerarrow.h"
00021 #include "qgscomposerframe.h"
00022 #include "qgscomposerhtml.h"
00023 #include "qgscomposerlabel.h"
00024 #include "qgscomposerlegend.h"
00025 #include "qgscomposermap.h"
00026 #include "qgscomposeritemgroup.h"
00027 #include "qgscomposerpicture.h"
00028 #include "qgscomposerscalebar.h"
00029 #include "qgscomposershape.h"
00030 #include "qgscomposerlabel.h"
00031 #include "qgscomposerattributetable.h"
00032 #include "qgsaddremovemultiframecommand.h"
00033 #include "qgscomposermultiframecommand.h"
00034 #include "qgslogger.h"
00035 #include "qgspaintenginehack.h"
00036 #include "qgspaperitem.h"
00037 #include "qgsproject.h"
00038 #include "qgsgeometry.h"
00039 #include "qgsvectorlayer.h"
00040 #include "qgsvectordataprovider.h"
00041 #include "qgsexpression.h"
00042 #include <QDomDocument>
00043 #include <QDomElement>
00044 #include <QGraphicsRectItem>
00045 #include <QPainter>
00046 #include <QPrinter>
00047 #include <QSettings>
00048 #include <QDir>
00049 
00050 
00051 QgsComposition::QgsComposition( QgsMapRenderer* mapRenderer )
00052     : QGraphicsScene( 0 ),
00053     mMapRenderer( mapRenderer ),
00054     mPlotStyle( QgsComposition::Preview ),
00055     mPageWidth( 297 ),
00056     mPageHeight( 210 ),
00057     mSpaceBetweenPages( 10 ),
00058     mPrintAsRaster( false ),
00059     mUseAdvancedEffects( true ),
00060     mSelectionTolerance( 0.0 ),
00061     mSnapToGrid( false ),
00062     mSnapGridResolution( 10.0 ),
00063     mSnapGridOffsetX( 0.0 ),
00064     mSnapGridOffsetY( 0.0 ),
00065     mAlignmentSnap( true ),
00066     mAlignmentSnapTolerance( 2 ),
00067     mActiveItemCommand( 0 ),
00068     mActiveMultiFrameCommand( 0 ),
00069     mAtlasComposition( this )
00070 {
00071   setBackgroundBrush( Qt::gray );
00072   addPaperItem();
00073 
00074   mPrintResolution = 300; //hardcoded default
00075   loadSettings();
00076 }
00077 
00078 QgsComposition::QgsComposition()
00079     : QGraphicsScene( 0 ),
00080     mMapRenderer( 0 ),
00081     mPlotStyle( QgsComposition::Preview ),
00082     mPageWidth( 297 ),
00083     mPageHeight( 210 ),
00084     mSpaceBetweenPages( 10 ),
00085     mPrintAsRaster( false ),
00086     mUseAdvancedEffects( true ),
00087     mSelectionTolerance( 0.0 ),
00088     mSnapToGrid( false ),
00089     mSnapGridResolution( 10.0 ),
00090     mSnapGridOffsetX( 0.0 ),
00091     mSnapGridOffsetY( 0.0 ),
00092     mAlignmentSnap( true ),
00093     mAlignmentSnapTolerance( 2 ),
00094     mActiveItemCommand( 0 ),
00095     mActiveMultiFrameCommand( 0 ),
00096     mAtlasComposition( this )
00097 {
00098   loadSettings();
00099 
00100 }
00101 
00102 QgsComposition::~QgsComposition()
00103 {
00104   removePaperItems();
00105   deleteAndRemoveMultiFrames();
00106 
00107   // make sure that all composer items are removed before
00108   // this class is deconstructed - to avoid segfaults
00109   // when composer items access in destructor composition that isn't valid anymore
00110   clear();
00111   delete mActiveItemCommand;
00112   delete mActiveMultiFrameCommand;
00113 }
00114 
00115 void QgsComposition::setPaperSize( double width, double height )
00116 {
00117   mPageWidth = width;
00118   mPageHeight = height;
00119   double currentY = 0;
00120   for ( int i = 0; i < mPages.size(); ++i )
00121   {
00122     mPages.at( i )->setSceneRect( QRectF( 0, currentY, width, height ) );
00123     currentY += ( height + mSpaceBetweenPages );
00124   }
00125 }
00126 
00127 double QgsComposition::paperHeight() const
00128 {
00129   return mPageHeight;
00130 }
00131 
00132 double QgsComposition::paperWidth() const
00133 {
00134   return mPageWidth;
00135 }
00136 
00137 void QgsComposition::setNumPages( int pages )
00138 {
00139   int currentPages = numPages();
00140   int diff = pages - currentPages;
00141   if ( diff >= 0 )
00142   {
00143     for ( int i = 0; i < diff; ++i )
00144     {
00145       addPaperItem();
00146     }
00147   }
00148   else
00149   {
00150     diff = -diff;
00151     for ( int i = 0; i < diff; ++i )
00152     {
00153       delete mPages.last();
00154       mPages.removeLast();
00155     }
00156   }
00157 
00158   // update the corresponding variable
00159   QgsExpression::setSpecialColumn( "$numpages", QVariant(( int )numPages() ) );
00160 
00161   emit nPagesChanged();
00162 }
00163 
00164 int QgsComposition::numPages() const
00165 {
00166   return mPages.size();
00167 }
00168 
00169 QgsComposerItem* QgsComposition::composerItemAt( const QPointF & position )
00170 {
00171   QList<QGraphicsItem*> itemList;
00172   if ( mSelectionTolerance <= 0.0 )
00173   {
00174     itemList = items( position );
00175   }
00176   else
00177   {
00178     itemList = items( QRectF( position.x() - mSelectionTolerance, position.y() - mSelectionTolerance, 2 * mSelectionTolerance, 2 * mSelectionTolerance ),
00179                       Qt::IntersectsItemShape, Qt::DescendingOrder );
00180   }
00181   QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
00182 
00183   for ( ; itemIt != itemList.end(); ++itemIt )
00184   {
00185     QgsComposerItem* composerItem = dynamic_cast<QgsComposerItem *>( *itemIt );
00186     QgsPaperItem* paperItem = dynamic_cast<QgsPaperItem*>( *itemIt );
00187     if ( composerItem && !paperItem )
00188     {
00189       return composerItem;
00190     }
00191   }
00192   return 0;
00193 }
00194 
00195 int QgsComposition::pageNumberAt( const QPointF& position ) const
00196 {
00197   return position.y() / ( paperHeight() + spaceBetweenPages() );
00198 }
00199 
00200 int QgsComposition::itemPageNumber( const QgsComposerItem* item ) const
00201 {
00202   return pageNumberAt( QPointF( item->transform().dx(), item->transform().dy() ) );
00203 }
00204 
00205 QList<QgsComposerItem*> QgsComposition::selectedComposerItems()
00206 {
00207   QList<QgsComposerItem*> composerItemList;
00208 
00209   QList<QGraphicsItem *> graphicsItemList = selectedItems();
00210   QList<QGraphicsItem *>::iterator itemIter = graphicsItemList.begin();
00211 
00212   for ( ; itemIter != graphicsItemList.end(); ++itemIter )
00213   {
00214     QgsComposerItem* composerItem = dynamic_cast<QgsComposerItem *>( *itemIter );
00215     if ( composerItem )
00216     {
00217       composerItemList.push_back( composerItem );
00218     }
00219   }
00220 
00221   return composerItemList;
00222 }
00223 
00224 QList<const QgsComposerMap*> QgsComposition::composerMapItems() const
00225 {
00226   QList<const QgsComposerMap*> resultList;
00227 
00228   QList<QGraphicsItem *> itemList = items();
00229   QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
00230   for ( ; itemIt != itemList.end(); ++itemIt )
00231   {
00232     const QgsComposerMap* composerMap = dynamic_cast<const QgsComposerMap *>( *itemIt );
00233     if ( composerMap )
00234     {
00235       resultList.push_back( composerMap );
00236     }
00237   }
00238 
00239   return resultList;
00240 }
00241 
00242 const QgsComposerMap* QgsComposition::getComposerMapById( int id ) const
00243 {
00244   QList<QGraphicsItem *> itemList = items();
00245   QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
00246   for ( ; itemIt != itemList.end(); ++itemIt )
00247   {
00248     const QgsComposerMap* composerMap = dynamic_cast<const QgsComposerMap *>( *itemIt );
00249     if ( composerMap )
00250     {
00251       if ( composerMap->id() == id )
00252       {
00253         return composerMap;
00254       }
00255     }
00256   }
00257   return 0;
00258 }
00259 
00260 const QgsComposerHtml* QgsComposition::getComposerHtmlByItem( QgsComposerItem *item ) const
00261 {
00262   // an html item will be a composer frame and if it is we can try to get
00263   // its multiframe parent and then try to cast that to a composer html
00264   const QgsComposerFrame* composerFrame =
00265     dynamic_cast<const QgsComposerFrame *>( item );
00266   if ( composerFrame )
00267   {
00268     const QgsComposerMultiFrame * mypMultiFrame = composerFrame->multiFrame();
00269     const QgsComposerHtml* composerHtml =
00270       dynamic_cast<const QgsComposerHtml *>( mypMultiFrame );
00271     if ( composerHtml )
00272     {
00273       return composerHtml;
00274     }
00275   }
00276   return 0;
00277 }
00278 
00279 const QgsComposerItem* QgsComposition::getComposerItemById( QString theId ) const
00280 {
00281   QList<QGraphicsItem *> itemList = items();
00282   QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
00283   for ( ; itemIt != itemList.end(); ++itemIt )
00284   {
00285     const QgsComposerItem* mypItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
00286     if ( mypItem )
00287     {
00288       if ( mypItem->id() == theId )
00289       {
00290         return mypItem;
00291       }
00292     }
00293   }
00294   return 0;
00295 }
00296 /*
00297 const QgsComposerItem* QgsComposition::getComposerItemByUuid( QString theUuid, bool inAllComposers ) const
00298 {
00299   //This does not work since it seems impossible to get the QgisApp::instance() from here... Is there a workaround ?
00300   QSet<QgsComposer*> composers = QSet<QgsComposer*>();
00301 
00302   if( inAllComposers )
00303   {
00304     composers = QgisApp::instance()->printComposers();
00305   }
00306   else
00307   {
00308     composers.insert( this )
00309   }
00310 
00311   QSet<QgsComposer*>::const_iterator it = composers.constBegin();
00312   for ( ; it != composers.constEnd(); ++it )
00313   {
00314     QList<QGraphicsItem *> itemList = ( *it )->items();
00315     QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
00316     for ( ; itemIt != itemList.end(); ++itemIt )
00317     {
00318       const QgsComposerItem* mypItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
00319       if ( mypItem )
00320       {
00321         if ( mypItem->uuid() == theUuid )
00322         {
00323           return mypItem;
00324         }
00325       }
00326     }
00327   }
00328 
00329   return 0;
00330 }
00331 */
00332 
00333 const QgsComposerItem* QgsComposition::getComposerItemByUuid( QString theUuid ) const
00334 {
00335   QList<QGraphicsItem *> itemList = items();
00336   QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
00337   for ( ; itemIt != itemList.end(); ++itemIt )
00338   {
00339     const QgsComposerItem* mypItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
00340     if ( mypItem )
00341     {
00342       if ( mypItem->uuid() == theUuid )
00343       {
00344         return mypItem;
00345       }
00346     }
00347   }
00348 
00349   return 0;
00350 }
00351 
00352 
00353 void QgsComposition::setUseAdvancedEffects( bool effectsEnabled )
00354 {
00355   mUseAdvancedEffects = effectsEnabled;
00356 
00357   //toggle effects for all composer items
00358   QList<QGraphicsItem*> itemList = items();
00359   QList<QGraphicsItem*>::const_iterator itemIt = itemList.constBegin();
00360   for ( ; itemIt != itemList.constEnd(); ++itemIt )
00361   {
00362     QgsComposerItem* composerItem = dynamic_cast<QgsComposerItem*>( *itemIt );
00363     if ( composerItem )
00364     {
00365       composerItem->setEffectsEnabled( effectsEnabled );
00366     }
00367   }
00368 }
00369 
00370 int QgsComposition::pixelFontSize( double pointSize ) const
00371 {
00372   //in QgsComposition, one unit = one mm
00373   double sizeMillimeters = pointSize * 0.3527;
00374   return qRound( sizeMillimeters ); //round to nearest mm
00375 }
00376 
00377 double QgsComposition::pointFontSize( int pixelSize ) const
00378 {
00379   double sizePoint = pixelSize / 0.3527;
00380   return sizePoint;
00381 }
00382 
00383 bool QgsComposition::writeXML( QDomElement& composerElem, QDomDocument& doc )
00384 {
00385   if ( composerElem.isNull() )
00386   {
00387     return false;
00388   }
00389 
00390   QDomElement compositionElem = doc.createElement( "Composition" );
00391   compositionElem.setAttribute( "paperWidth", QString::number( mPageWidth ) );
00392   compositionElem.setAttribute( "paperHeight", QString::number( mPageHeight ) );
00393   compositionElem.setAttribute( "numPages", mPages.size() );
00394 
00395   //snapping
00396   if ( mSnapToGrid )
00397   {
00398     compositionElem.setAttribute( "snapping", "1" );
00399   }
00400   else
00401   {
00402     compositionElem.setAttribute( "snapping", "0" );
00403   }
00404   compositionElem.setAttribute( "snapGridResolution", QString::number( mSnapGridResolution ) );
00405   compositionElem.setAttribute( "snapGridOffsetX", QString::number( mSnapGridOffsetX ) );
00406   compositionElem.setAttribute( "snapGridOffsetY", QString::number( mSnapGridOffsetY ) );
00407 
00408   //custom snap lines
00409   QList< QGraphicsLineItem* >::const_iterator snapLineIt = mSnapLines.constBegin();
00410   for ( ; snapLineIt != mSnapLines.constEnd(); ++snapLineIt )
00411   {
00412     QDomElement snapLineElem = doc.createElement( "SnapLine" );
00413     QLineF line = ( *snapLineIt )->line();
00414     snapLineElem.setAttribute( "x1", QString::number( line.x1() ) );
00415     snapLineElem.setAttribute( "y1", QString::number( line.y1() ) );
00416     snapLineElem.setAttribute( "x2", QString::number( line.x2() ) );
00417     snapLineElem.setAttribute( "y2", QString::number( line.y2() ) );
00418     compositionElem.appendChild( snapLineElem );
00419   }
00420 
00421   compositionElem.setAttribute( "printResolution", mPrintResolution );
00422   compositionElem.setAttribute( "printAsRaster", mPrintAsRaster );
00423 
00424   compositionElem.setAttribute( "alignmentSnap", mAlignmentSnap ? 1 : 0 );
00425   compositionElem.setAttribute( "alignmentSnapTolerance", mAlignmentSnapTolerance );
00426 
00427   //save items except paper items and frame items (they are saved with the corresponding multiframe)
00428   QList<QGraphicsItem*> itemList = items();
00429   QList<QGraphicsItem*>::const_iterator itemIt = itemList.constBegin();
00430   for ( ; itemIt != itemList.constEnd(); ++itemIt )
00431   {
00432     const QgsComposerItem* composerItem = dynamic_cast<const QgsComposerItem*>( *itemIt );
00433     if ( composerItem )
00434     {
00435       if ( composerItem->type() != QgsComposerItem::ComposerPaper &&  composerItem->type() != QgsComposerItem::ComposerFrame )
00436       {
00437         composerItem->writeXML( compositionElem, doc );
00438       }
00439     }
00440   }
00441 
00442   //save multiframes
00443   QSet<QgsComposerMultiFrame*>::const_iterator multiFrameIt = mMultiFrames.constBegin();
00444   for ( ; multiFrameIt != mMultiFrames.constEnd(); ++multiFrameIt )
00445   {
00446     ( *multiFrameIt )->writeXML( compositionElem, doc );
00447   }
00448   composerElem.appendChild( compositionElem );
00449 
00450   return true;
00451 }
00452 
00453 bool QgsComposition::readXML( const QDomElement& compositionElem, const QDomDocument& doc )
00454 {
00455   Q_UNUSED( doc );
00456   if ( compositionElem.isNull() )
00457   {
00458     return false;
00459   }
00460 
00461   //create pages
00462   bool widthConversionOk, heightConversionOk;
00463   mPageWidth = compositionElem.attribute( "paperWidth" ).toDouble( &widthConversionOk );
00464   mPageHeight = compositionElem.attribute( "paperHeight" ).toDouble( &heightConversionOk );
00465   emit paperSizeChanged();
00466   int numPages = compositionElem.attribute( "numPages", "1" ).toInt();
00467 
00468   if ( widthConversionOk && heightConversionOk )
00469   {
00470     removePaperItems();
00471     for ( int i = 0; i < numPages; ++i )
00472     {
00473       addPaperItem();
00474     }
00475   }
00476 
00477   //snapping
00478   if ( compositionElem.attribute( "snapping" ) == "0" )
00479   {
00480     mSnapToGrid = false;
00481   }
00482   else
00483   {
00484     mSnapToGrid = true;
00485   }
00486   mSnapGridResolution = compositionElem.attribute( "snapGridResolution" ).toDouble();
00487   mSnapGridOffsetX = compositionElem.attribute( "snapGridOffsetX" ).toDouble();
00488   mSnapGridOffsetY = compositionElem.attribute( "snapGridOffsetY" ).toDouble();
00489 
00490   //custom snap lines
00491   QDomNodeList snapLineNodes = compositionElem.elementsByTagName( "SnapLine" );
00492   for ( int i = 0; i < snapLineNodes.size(); ++i )
00493   {
00494     QDomElement snapLineElem = snapLineNodes.at( i ).toElement();
00495     QGraphicsLineItem* snapItem = addSnapLine();
00496     double x1 = snapLineElem.attribute( "x1" ).toDouble();
00497     double y1 = snapLineElem.attribute( "y1" ).toDouble();
00498     double x2 = snapLineElem.attribute( "x2" ).toDouble();
00499     double y2 = snapLineElem.attribute( "y2" ).toDouble();
00500     snapItem->setLine( x1, y1, x2, y2 );
00501   }
00502 
00503   mAlignmentSnap = compositionElem.attribute( "alignmentSnap", "1" ).toInt() == 0 ? false : true;
00504   mAlignmentSnapTolerance = compositionElem.attribute( "alignmentSnapTolerance", "2.0" ).toDouble();
00505 
00506   mPrintAsRaster = compositionElem.attribute( "printAsRaster" ).toInt();
00507   mPrintResolution = compositionElem.attribute( "printResolution", "300" ).toInt();
00508 
00509   updatePaperItems();
00510   return true;
00511 }
00512 
00513 bool QgsComposition::loadFromTemplate( const QDomDocument& doc, QMap<QString, QString>* substitutionMap, bool addUndoCommands )
00514 {
00515   deleteAndRemoveMultiFrames();
00516 
00517   //delete all items and emit itemRemoved signal
00518   QList<QGraphicsItem *> itemList = items();
00519   QList<QGraphicsItem *>::iterator itemIter = itemList.begin();
00520   for ( ; itemIter != itemList.end(); ++itemIter )
00521   {
00522     QgsComposerItem* cItem = dynamic_cast<QgsComposerItem*>( *itemIter );
00523     if ( cItem )
00524     {
00525       removeItem( cItem );
00526       emit itemRemoved( cItem );
00527       delete cItem;
00528     }
00529   }
00530   mItemZList.clear();
00531 
00532   mPages.clear();
00533   mUndoStack.clear();
00534 
00535   QDomDocument importDoc;
00536   if ( substitutionMap )
00537   {
00538     QString xmlString = doc.toString();
00539     QMap<QString, QString>::const_iterator sIt = substitutionMap->constBegin();
00540     for ( ; sIt != substitutionMap->constEnd(); ++sIt )
00541     {
00542       xmlString = xmlString.replace( "[" + sIt.key() + "]", encodeStringForXML( sIt.value() ) );
00543     }
00544 
00545     QString errorMsg;
00546     int errorLine, errorColumn;
00547     if ( !importDoc.setContent( xmlString, &errorMsg, &errorLine, &errorColumn ) )
00548     {
00549       return false;
00550     }
00551   }
00552   else
00553   {
00554     importDoc = doc;
00555   }
00556 
00557   //read general settings
00558   QDomElement compositionElem = importDoc.documentElement().firstChildElement( "Composition" );
00559   if ( compositionElem.isNull() )
00560   {
00561     return false;
00562   }
00563 
00564   bool ok = readXML( compositionElem, importDoc );
00565   if ( !ok )
00566   {
00567     return false;
00568   }
00569 
00570   // remove all uuid attributes since we don't want duplicates UUIDS
00571   QDomNodeList composerItemsNodes = importDoc.elementsByTagName( "ComposerItem" );
00572   for ( int i = 0; i < composerItemsNodes.count(); ++i )
00573   {
00574     QDomNode composerItemNode = composerItemsNodes.at( i );
00575     if ( composerItemNode.isElement() )
00576     {
00577       composerItemNode.toElement().removeAttribute( "uuid" );
00578     }
00579   }
00580 
00581   //addItemsFromXML
00582   addItemsFromXML( importDoc.documentElement(), importDoc, 0, addUndoCommands, 0 );
00583 
00584   // read atlas parameters
00585   QDomElement atlasElem = importDoc.documentElement().firstChildElement( "Atlas" );
00586   atlasComposition().readXML( atlasElem, importDoc );
00587   return true;
00588 }
00589 
00590 void QgsComposition::addItemsFromXML( const QDomElement& elem, const QDomDocument& doc, QMap< QgsComposerMap*, int >* mapsToRestore,
00591                                       bool addUndoCommands, QPointF* pos, bool pasteInPlace )
00592 {
00593   QPointF* pasteInPlacePt = 0;
00594   if ( pasteInPlace )
00595   {
00596     pasteInPlacePt = new QPointF( 0, pageNumberAt( *pos ) * ( mPageHeight + mSpaceBetweenPages ) );
00597   }
00598   QDomNodeList composerLabelList = elem.elementsByTagName( "ComposerLabel" );
00599   for ( int i = 0; i < composerLabelList.size(); ++i )
00600   {
00601     QDomElement currentComposerLabelElem = composerLabelList.at( i ).toElement();
00602     QgsComposerLabel* newLabel = new QgsComposerLabel( this );
00603     newLabel->readXML( currentComposerLabelElem, doc );
00604     if ( pos )
00605     {
00606       if ( pasteInPlacePt )
00607       {
00608         newLabel->setItemPosition( newLabel->transform().dx(), fmod( newLabel->transform().dy(), ( paperHeight() + spaceBetweenPages() ) ) );
00609         newLabel->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
00610       }
00611       else
00612       {
00613         newLabel->setItemPosition( pos->x(), pos->y() );
00614       }
00615     }
00616     addComposerLabel( newLabel );
00617     if ( addUndoCommands )
00618     {
00619       pushAddRemoveCommand( newLabel, tr( "Label added" ) );
00620     }
00621   }
00622   // map
00623   QDomNodeList composerMapList = elem.elementsByTagName( "ComposerMap" );
00624   for ( int i = 0; i < composerMapList.size(); ++i )
00625   {
00626     QDomElement currentComposerMapElem = composerMapList.at( i ).toElement();
00627     QgsComposerMap* newMap = new QgsComposerMap( this );
00628     newMap->readXML( currentComposerMapElem, doc );
00629     newMap->assignFreeId();
00630 
00631     if ( mapsToRestore )
00632     {
00633       mapsToRestore->insert( newMap, ( int )( newMap->previewMode() ) );
00634       newMap->setPreviewMode( QgsComposerMap::Rectangle );
00635     }
00636     addComposerMap( newMap, false );
00637 
00638     if ( pos )
00639     {
00640       if ( pasteInPlace )
00641       {
00642         newMap->setItemPosition( newMap->transform().dx(), fmod( newMap->transform().dy(), ( paperHeight() + spaceBetweenPages() ) ) );
00643         newMap->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
00644       }
00645       else
00646       {
00647         newMap->setItemPosition( pos->x(), pos->y() );
00648       }
00649     }
00650 
00651     if ( addUndoCommands )
00652     {
00653       pushAddRemoveCommand( newMap, tr( "Map added" ) );
00654     }
00655   }
00656   // arrow
00657   QDomNodeList composerArrowList = elem.elementsByTagName( "ComposerArrow" );
00658   for ( int i = 0; i < composerArrowList.size(); ++i )
00659   {
00660     QDomElement currentComposerArrowElem = composerArrowList.at( i ).toElement();
00661     QgsComposerArrow* newArrow = new QgsComposerArrow( this );
00662     newArrow->readXML( currentComposerArrowElem, doc );
00663     if ( pos )
00664     {
00665       if ( pasteInPlace )
00666       {
00667         newArrow->setItemPosition( newArrow->transform().dx(), fmod( newArrow->transform().dy(), ( paperHeight() + spaceBetweenPages() ) ) );
00668         newArrow->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
00669       }
00670       else
00671       {
00672         newArrow->setItemPosition( pos->x(), pos->y() );
00673       }
00674     }
00675     addComposerArrow( newArrow );
00676     if ( addUndoCommands )
00677     {
00678       pushAddRemoveCommand( newArrow, tr( "Arrow added" ) );
00679     }
00680   }
00681   // scalebar
00682   QDomNodeList composerScaleBarList = elem.elementsByTagName( "ComposerScaleBar" );
00683   for ( int i = 0; i < composerScaleBarList.size(); ++i )
00684   {
00685     QDomElement currentComposerScaleBarElem = composerScaleBarList.at( i ).toElement();
00686     QgsComposerScaleBar* newScaleBar = new QgsComposerScaleBar( this );
00687     newScaleBar->readXML( currentComposerScaleBarElem, doc );
00688     if ( pos )
00689     {
00690       if ( pasteInPlace )
00691       {
00692         newScaleBar->setItemPosition( newScaleBar->transform().dx(), fmod( newScaleBar->transform().dy(), ( paperHeight() + spaceBetweenPages() ) ) );
00693         newScaleBar->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
00694       }
00695       else
00696       {
00697         newScaleBar->setItemPosition( pos->x(), pos->y() );
00698       }
00699     }
00700     addComposerScaleBar( newScaleBar );
00701     if ( addUndoCommands )
00702     {
00703       pushAddRemoveCommand( newScaleBar, tr( "Scale bar added" ) );
00704     }
00705   }
00706   // shape
00707   QDomNodeList composerShapeList = elem.elementsByTagName( "ComposerShape" );
00708   for ( int i = 0; i < composerShapeList.size(); ++i )
00709   {
00710     QDomElement currentComposerShapeElem = composerShapeList.at( i ).toElement();
00711     QgsComposerShape* newShape = new QgsComposerShape( this );
00712     newShape->readXML( currentComposerShapeElem, doc );
00713     if ( pos )
00714     {
00715       if ( pasteInPlace )
00716       {
00717         newShape->setItemPosition( newShape->transform().dx(), fmod( newShape->transform().dy(), ( paperHeight() + spaceBetweenPages() ) ) );
00718         newShape->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
00719       }
00720       else
00721       {
00722         newShape->setItemPosition( pos->x(), pos->y() );
00723       }
00724     }
00725     addComposerShape( newShape );
00726     if ( addUndoCommands )
00727     {
00728       pushAddRemoveCommand( newShape, tr( "Shape added" ) );
00729     }
00730   }
00731   // picture
00732   QDomNodeList composerPictureList = elem.elementsByTagName( "ComposerPicture" );
00733   for ( int i = 0; i < composerPictureList.size(); ++i )
00734   {
00735     QDomElement currentComposerPictureElem = composerPictureList.at( i ).toElement();
00736     QgsComposerPicture* newPicture = new QgsComposerPicture( this );
00737     newPicture->readXML( currentComposerPictureElem, doc );
00738     if ( pos )
00739     {
00740       if ( pasteInPlace )
00741       {
00742         newPicture->setItemPosition( newPicture->transform().dx(), fmod( newPicture->transform().dy(), ( paperHeight() + spaceBetweenPages() ) ) );
00743         newPicture->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
00744       }
00745       else
00746       {
00747         newPicture->setItemPosition( pos->x(), pos->y() );
00748       }
00749     }
00750     addComposerPicture( newPicture );
00751     if ( addUndoCommands )
00752     {
00753       pushAddRemoveCommand( newPicture, tr( "Picture added" ) );
00754     }
00755   }
00756   // legend
00757   QDomNodeList composerLegendList = elem.elementsByTagName( "ComposerLegend" );
00758   for ( int i = 0; i < composerLegendList.size(); ++i )
00759   {
00760     QDomElement currentComposerLegendElem = composerLegendList.at( i ).toElement();
00761     QgsComposerLegend* newLegend = new QgsComposerLegend( this );
00762     newLegend->readXML( currentComposerLegendElem, doc );
00763     if ( pos )
00764     {
00765       if ( pasteInPlace )
00766       {
00767         newLegend->setItemPosition( newLegend->transform().dx(), fmod( newLegend->transform().dy(), ( paperHeight() + spaceBetweenPages() ) ) );
00768         newLegend->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
00769       }
00770       else
00771       {
00772         newLegend->setItemPosition( pos->x(), pos->y() );
00773       }
00774     }
00775     addComposerLegend( newLegend );
00776     if ( addUndoCommands )
00777     {
00778       pushAddRemoveCommand( newLegend, tr( "Legend added" ) );
00779     }
00780   }
00781   // table
00782   QDomNodeList composerTableList = elem.elementsByTagName( "ComposerAttributeTable" );
00783   for ( int i = 0; i < composerTableList.size(); ++i )
00784   {
00785     QDomElement currentComposerTableElem = composerTableList.at( i ).toElement();
00786     QgsComposerAttributeTable* newTable = new QgsComposerAttributeTable( this );
00787     newTable->readXML( currentComposerTableElem, doc );
00788     if ( pos )
00789     {
00790       if ( pasteInPlace )
00791       {
00792         newTable->setItemPosition( newTable->transform().dx(), fmod( newTable->transform().dy(), ( paperHeight() + spaceBetweenPages() ) ) );
00793         newTable->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
00794       }
00795       else
00796       {
00797         newTable->setItemPosition( pos->x(), pos->y() );
00798       }
00799     }
00800     addComposerTable( newTable );
00801     if ( addUndoCommands )
00802     {
00803       pushAddRemoveCommand( newTable, tr( "Table added" ) );
00804     }
00805   }
00806   //html
00807   QDomNodeList composerHtmlList = elem.elementsByTagName( "ComposerHtml" );
00808   for ( int i = 0; i < composerHtmlList.size(); ++i )
00809   {
00810     QDomElement currentHtmlElem = composerHtmlList.at( i ).toElement();
00811     QgsComposerHtml* newHtml = new QgsComposerHtml( this, false );
00812     newHtml->readXML( currentHtmlElem, doc );
00813     newHtml->setCreateUndoCommands( true );
00814     this->addMultiFrame( newHtml );
00815   }
00816 }
00817 
00818 void QgsComposition::addItemToZList( QgsComposerItem* item )
00819 {
00820   if ( !item )
00821   {
00822     return;
00823   }
00824   mItemZList.push_back( item );
00825   QgsDebugMsg( QString::number( mItemZList.size() ) );
00826   item->setZValue( mItemZList.size() );
00827 }
00828 
00829 void QgsComposition::removeItemFromZList( QgsComposerItem* item )
00830 {
00831   if ( !item )
00832   {
00833     return;
00834   }
00835   mItemZList.removeAll( item );
00836 }
00837 
00838 void QgsComposition::raiseSelectedItems()
00839 {
00840   QList<QgsComposerItem*> selectedItems = selectedComposerItems();
00841   QList<QgsComposerItem*>::iterator it = selectedItems.begin();
00842   for ( ; it != selectedItems.end(); ++it )
00843   {
00844     raiseItem( *it );
00845   }
00846 
00847   //update all positions
00848   updateZValues();
00849   update();
00850 }
00851 
00852 void QgsComposition::raiseItem( QgsComposerItem* item )
00853 {
00854   //search item
00855   QMutableLinkedListIterator<QgsComposerItem*> it( mItemZList );
00856   if ( it.findNext( item ) )
00857   {
00858     if ( it.hasNext() )
00859     {
00860       it.remove();
00861       it.next();
00862       it.insert( item );
00863     }
00864   }
00865 }
00866 
00867 void QgsComposition::lowerSelectedItems()
00868 {
00869   QList<QgsComposerItem*> selectedItems = selectedComposerItems();
00870   QList<QgsComposerItem*>::iterator it = selectedItems.begin();
00871   for ( ; it != selectedItems.end(); ++it )
00872   {
00873     lowerItem( *it );
00874   }
00875 
00876   //update all positions
00877   updateZValues();
00878   update();
00879 }
00880 
00881 void QgsComposition::lowerItem( QgsComposerItem* item )
00882 {
00883   //search item
00884   QMutableLinkedListIterator<QgsComposerItem*> it( mItemZList );
00885   if ( it.findNext( item ) )
00886   {
00887     it.previous();
00888     if ( it.hasPrevious() )
00889     {
00890       it.remove();
00891       it.previous();
00892       it.insert( item );
00893     }
00894   }
00895 }
00896 
00897 void QgsComposition::moveSelectedItemsToTop()
00898 {
00899   QList<QgsComposerItem*> selectedItems = selectedComposerItems();
00900   QList<QgsComposerItem*>::iterator it = selectedItems.begin();
00901 
00902   for ( ; it != selectedItems.end(); ++it )
00903   {
00904     moveItemToTop( *it );
00905   }
00906 
00907   //update all positions
00908   updateZValues();
00909   update();
00910 }
00911 
00912 void QgsComposition::moveItemToTop( QgsComposerItem* item )
00913 {
00914   //search item
00915   QMutableLinkedListIterator<QgsComposerItem*> it( mItemZList );
00916   if ( it.findNext( item ) )
00917   {
00918     it.remove();
00919   }
00920   mItemZList.push_back( item );
00921 }
00922 
00923 void QgsComposition::moveSelectedItemsToBottom()
00924 {
00925   QList<QgsComposerItem*> selectedItems = selectedComposerItems();
00926   QList<QgsComposerItem*>::iterator it = selectedItems.begin();
00927   for ( ; it != selectedItems.end(); ++it )
00928   {
00929     moveItemToBottom( *it );
00930   }
00931 
00932   //update all positions
00933   updateZValues();
00934   update();
00935 }
00936 
00937 void QgsComposition::moveItemToBottom( QgsComposerItem* item )
00938 {
00939   //search item
00940   QMutableLinkedListIterator<QgsComposerItem*> it( mItemZList );
00941   if ( it.findNext( item ) )
00942   {
00943     it.remove();
00944   }
00945   mItemZList.push_front( item );
00946 }
00947 
00948 void QgsComposition::alignSelectedItemsLeft()
00949 {
00950   QList<QgsComposerItem*> selectedItems = selectedComposerItems();
00951   if ( selectedItems.size() < 2 )
00952   {
00953     return;
00954   }
00955 
00956   QRectF selectedItemBBox;
00957   if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
00958   {
00959     return;
00960   }
00961 
00962   double minXCoordinate = selectedItemBBox.left();
00963 
00964   //align items left to minimum x coordinate
00965   QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items left" ) );
00966   QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
00967   for ( ; align_it != selectedItems.end(); ++align_it )
00968   {
00969     QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
00970     subcommand->savePreviousState();
00971     QTransform itemTransform = ( *align_it )->transform();
00972     itemTransform.translate( minXCoordinate - itemTransform.dx(), 0 );
00973     ( *align_it )->setTransform( itemTransform );
00974     subcommand->saveAfterState();
00975   }
00976   mUndoStack.push( parentCommand );
00977 }
00978 
00979 void QgsComposition::alignSelectedItemsHCenter()
00980 {
00981   QList<QgsComposerItem*> selectedItems = selectedComposerItems();
00982   if ( selectedItems.size() < 2 )
00983   {
00984     return;
00985   }
00986 
00987   QRectF selectedItemBBox;
00988   if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
00989   {
00990     return;
00991   }
00992 
00993   double averageXCoord = ( selectedItemBBox.left() + selectedItemBBox.right() ) / 2.0;
00994 
00995   //place items
00996   QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items horizontal center" ) );
00997   QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
00998   for ( ; align_it != selectedItems.end(); ++align_it )
00999   {
01000     QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
01001     subcommand->savePreviousState();
01002     QTransform itemTransform = ( *align_it )->transform();
01003     itemTransform.translate( averageXCoord - itemTransform.dx() - ( *align_it )->rect().width() / 2.0, 0 );
01004     ( *align_it )->setTransform( itemTransform );
01005     subcommand->saveAfterState();
01006   }
01007   mUndoStack.push( parentCommand );
01008 }
01009 
01010 void QgsComposition::alignSelectedItemsRight()
01011 {
01012   QList<QgsComposerItem*> selectedItems = selectedComposerItems();
01013   if ( selectedItems.size() < 2 )
01014   {
01015     return;
01016   }
01017 
01018   QRectF selectedItemBBox;
01019   if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
01020   {
01021     return;
01022   }
01023 
01024   double maxXCoordinate = selectedItemBBox.right();
01025 
01026   //align items right to maximum x coordinate
01027   QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items right" ) );
01028   QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
01029   for ( ; align_it != selectedItems.end(); ++align_it )
01030   {
01031     QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
01032     subcommand->savePreviousState();
01033     QTransform itemTransform = ( *align_it )->transform();
01034     itemTransform.translate( maxXCoordinate - itemTransform.dx() - ( *align_it )->rect().width(), 0 );
01035     ( *align_it )->setTransform( itemTransform );
01036     subcommand->saveAfterState();
01037   }
01038   mUndoStack.push( parentCommand );
01039 }
01040 
01041 void QgsComposition::alignSelectedItemsTop()
01042 {
01043   QList<QgsComposerItem*> selectedItems = selectedComposerItems();
01044   if ( selectedItems.size() < 2 )
01045   {
01046     return;
01047   }
01048 
01049   QRectF selectedItemBBox;
01050   if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
01051   {
01052     return;
01053   }
01054 
01055   double minYCoordinate = selectedItemBBox.top();
01056 
01057   QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items top" ) );
01058   QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
01059   for ( ; align_it != selectedItems.end(); ++align_it )
01060   {
01061     QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
01062     subcommand->savePreviousState();
01063     QTransform itemTransform = ( *align_it )->transform();
01064     itemTransform.translate( 0, minYCoordinate - itemTransform.dy() );
01065     ( *align_it )->setTransform( itemTransform );
01066     subcommand->saveAfterState();
01067   }
01068   mUndoStack.push( parentCommand );
01069 }
01070 
01071 void QgsComposition::alignSelectedItemsVCenter()
01072 {
01073   QList<QgsComposerItem*> selectedItems = selectedComposerItems();
01074   if ( selectedItems.size() < 2 )
01075   {
01076     return;
01077   }
01078 
01079   QRectF selectedItemBBox;
01080   if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
01081   {
01082     return;
01083   }
01084 
01085   double averageYCoord = ( selectedItemBBox.top() + selectedItemBBox.bottom() ) / 2.0;
01086   QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items vertical center" ) );
01087   QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
01088   for ( ; align_it != selectedItems.end(); ++align_it )
01089   {
01090     QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
01091     subcommand->savePreviousState();
01092     QTransform itemTransform = ( *align_it )->transform();
01093     itemTransform.translate( 0, averageYCoord - itemTransform.dy() - ( *align_it )->rect().height() / 2 );
01094     ( *align_it )->setTransform( itemTransform );
01095     subcommand->saveAfterState();
01096   }
01097   mUndoStack.push( parentCommand );
01098 }
01099 
01100 void QgsComposition::alignSelectedItemsBottom()
01101 {
01102   QList<QgsComposerItem*> selectedItems = selectedComposerItems();
01103   if ( selectedItems.size() < 2 )
01104   {
01105     return;
01106   }
01107 
01108   QRectF selectedItemBBox;
01109   if ( boundingRectOfSelectedItems( selectedItemBBox ) != 0 )
01110   {
01111     return;
01112   }
01113 
01114   double maxYCoord = selectedItemBBox.bottom();
01115   QUndoCommand* parentCommand = new QUndoCommand( tr( "Aligned items bottom" ) );
01116   QList<QgsComposerItem*>::iterator align_it = selectedItems.begin();
01117   for ( ; align_it != selectedItems.end(); ++align_it )
01118   {
01119     QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *align_it, "", parentCommand );
01120     subcommand->savePreviousState();
01121     QTransform itemTransform = ( *align_it )->transform();
01122     itemTransform.translate( 0, maxYCoord - itemTransform.dy() - ( *align_it )->rect().height() );
01123     ( *align_it )->setTransform( itemTransform );
01124     subcommand->saveAfterState();
01125   }
01126   mUndoStack.push( parentCommand );
01127 }
01128 
01129 void QgsComposition::updateZValues()
01130 {
01131   int counter = 1;
01132   QLinkedList<QgsComposerItem*>::iterator it = mItemZList.begin();
01133   QgsComposerItem* currentItem = 0;
01134 
01135   QUndoCommand* parentCommand = new QUndoCommand( tr( "Item z-order changed" ) );
01136   for ( ; it != mItemZList.end(); ++it )
01137   {
01138     currentItem = *it;
01139     if ( currentItem )
01140     {
01141       QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *it, "", parentCommand );
01142       subcommand->savePreviousState();
01143       currentItem->setZValue( counter );
01144       subcommand->saveAfterState();
01145     }
01146     ++counter;
01147   }
01148   mUndoStack.push( parentCommand );
01149 }
01150 
01151 void QgsComposition::sortZList()
01152 {
01153   if ( mItemZList.size() < 2 )
01154   {
01155     return;
01156   }
01157 
01158   QLinkedList<QgsComposerItem*>::const_iterator lIt = mItemZList.constBegin();
01159   QLinkedList<QgsComposerItem*> sortedList;
01160 
01161   for ( ; lIt != mItemZList.constEnd(); ++lIt )
01162   {
01163     QLinkedList<QgsComposerItem*>::iterator insertIt = sortedList.begin();
01164     for ( ; insertIt != sortedList.end(); ++insertIt )
01165     {
01166       if (( *lIt )->zValue() < ( *insertIt )->zValue() )
01167       {
01168         break;
01169       }
01170     }
01171     sortedList.insert( insertIt, ( *lIt ) );
01172   }
01173 
01174   mItemZList = sortedList;
01175 }
01176 
01177 QPointF QgsComposition::snapPointToGrid( const QPointF& scenePoint ) const
01178 {
01179   if ( !mSnapToGrid || mSnapGridResolution <= 0 )
01180   {
01181     return scenePoint;
01182   }
01183 
01184   //y offset to current page
01185   int pageNr = ( int )( scenePoint.y() / ( mPageHeight + mSpaceBetweenPages ) );
01186   double yOffset = pageNr * ( mPageHeight + mSpaceBetweenPages );
01187   double yPage = scenePoint.y() - yOffset; //y-coordinate relative to current page
01188 
01189   //snap x coordinate
01190   int xRatio = ( int )(( scenePoint.x() - mSnapGridOffsetX ) / mSnapGridResolution + 0.5 );
01191   int yRatio = ( int )(( yPage - mSnapGridOffsetY ) / mSnapGridResolution + 0.5 );
01192 
01193   return QPointF( xRatio * mSnapGridResolution + mSnapGridOffsetX, yRatio * mSnapGridResolution + mSnapGridOffsetY + yOffset );
01194 }
01195 
01196 QPointF QgsComposition::alignItem( const QgsComposerItem* item, double& alignX, double& alignY, double dx, double dy )
01197 {
01198   if ( !item )
01199   {
01200     return QPointF();
01201   }
01202 
01203   double left = item->transform().dx() + dx;
01204   double right = left + item->rect().width();
01205   double midH = ( left + right ) / 2.0;
01206   double top = item->transform().dy() + dy;
01207   double bottom = top + item->rect().height();
01208   double midV = ( top + bottom ) / 2.0;
01209 
01210   QMap<double, const QgsComposerItem* > xAlignCoordinates;
01211   QMap<double, const QgsComposerItem* > yAlignCoordinates;
01212   collectAlignCoordinates( xAlignCoordinates, yAlignCoordinates, item );
01213 
01214   //find nearest matches x
01215   double xItemLeft = left; //new left coordinate of the item
01216   double xAlignCoord = 0;
01217   double smallestDiffX = DBL_MAX;
01218 
01219   checkNearestItem( left, xAlignCoordinates, smallestDiffX, 0, xItemLeft, xAlignCoord );
01220   checkNearestItem( midH, xAlignCoordinates, smallestDiffX, ( left - right ) / 2.0, xItemLeft, xAlignCoord );
01221   checkNearestItem( right, xAlignCoordinates, smallestDiffX, left - right, xItemLeft, xAlignCoord );
01222 
01223   //find nearest matches y
01224   double yItemTop = top; //new top coordinate of the item
01225   double yAlignCoord = 0;
01226   double smallestDiffY = DBL_MAX;
01227 
01228   checkNearestItem( top, yAlignCoordinates, smallestDiffY, 0, yItemTop, yAlignCoord );
01229   checkNearestItem( midV, yAlignCoordinates, smallestDiffY, ( top - bottom ) / 2.0, yItemTop, yAlignCoord );
01230   checkNearestItem( bottom, yAlignCoordinates, smallestDiffY, top - bottom, yItemTop, yAlignCoord );
01231 
01232   double xCoord = ( smallestDiffX < 5 ) ? xItemLeft : item->transform().dx() + dx;
01233   alignX = ( smallestDiffX < 5 ) ? xAlignCoord : -1;
01234   double yCoord = ( smallestDiffY < 5 ) ? yItemTop : item->transform().dy() + dy;
01235   alignY = ( smallestDiffY < 5 ) ? yAlignCoord : -1;
01236   return QPointF( xCoord, yCoord );
01237 }
01238 
01239 QPointF QgsComposition::alignPos( const QPointF& pos, const QgsComposerItem* excludeItem, double& alignX, double& alignY )
01240 {
01241   QMap<double, const QgsComposerItem* > xAlignCoordinates;
01242   QMap<double, const QgsComposerItem* > yAlignCoordinates;
01243   collectAlignCoordinates( xAlignCoordinates, yAlignCoordinates, excludeItem );
01244 
01245   double nearestX = pos.x();
01246   double nearestY = pos.y();
01247   if ( !nearestItem( xAlignCoordinates, pos.x(), nearestX )
01248        || !nearestItem( yAlignCoordinates, pos.y(), nearestY ) )
01249   {
01250     alignX = -1;
01251     alignY = -1;
01252     return pos;
01253   }
01254 
01255   QPointF result( pos.x(), pos.y() );
01256   if ( abs( nearestX - pos.x() ) < mAlignmentSnapTolerance )
01257   {
01258     result.setX( nearestX );
01259     alignX = nearestX;
01260   }
01261   else
01262   {
01263     alignX = -1;
01264   }
01265 
01266   if ( abs( nearestY - pos.y() ) < mAlignmentSnapTolerance )
01267   {
01268     result.setY( nearestY );
01269     alignY = nearestY;
01270   }
01271   else
01272   {
01273     alignY = -1;
01274   }
01275   return result;
01276 }
01277 
01278 QGraphicsLineItem* QgsComposition::addSnapLine()
01279 {
01280   QGraphicsLineItem* item = new QGraphicsLineItem();
01281   QPen linePen( Qt::SolidLine );
01282   linePen.setColor( Qt::red );
01283   // use a pen width of 0, since this activates a cosmetic pen
01284   // which doesn't scale with the composer and keeps a constant size
01285   linePen.setWidthF( 0 );
01286   item->setPen( linePen );
01287   item->setZValue( 100 );
01288   addItem( item );
01289   mSnapLines.push_back( item );
01290   return item;
01291 }
01292 
01293 void QgsComposition::removeSnapLine( QGraphicsLineItem* line )
01294 {
01295   removeItem( line );
01296   mSnapLines.removeAll( line );
01297   delete line;
01298 }
01299 
01300 void QgsComposition::setSnapLinesVisible( bool visible )
01301 {
01302   QList< QGraphicsLineItem* >::iterator it = mSnapLines.begin();
01303   for ( ; it != mSnapLines.end(); ++it )
01304   {
01305     if ( visible )
01306     {
01307       ( *it )->show();
01308     }
01309     else
01310     {
01311       ( *it )->hide();
01312     }
01313   }
01314 }
01315 
01316 QGraphicsLineItem* QgsComposition::nearestSnapLine( bool horizontal, double x, double y, double tolerance,
01317     QList< QPair< QgsComposerItem*, QgsComposerItem::ItemPositionMode> >& snappedItems )
01318 {
01319   double minSqrDist = DBL_MAX;
01320   QGraphicsLineItem* item = 0;
01321   double currentXCoord = 0;
01322   double currentYCoord = 0;
01323   double currentSqrDist = 0;
01324   double sqrTolerance = tolerance * tolerance;
01325 
01326   snappedItems.clear();
01327 
01328   QList< QGraphicsLineItem* >::const_iterator it = mSnapLines.constBegin();
01329   for ( ; it != mSnapLines.constEnd(); ++it )
01330   {
01331     bool itemHorizontal = qgsDoubleNear(( *it )->line().y2() - ( *it )->line().y1(), 0 );
01332     if ( horizontal && itemHorizontal )
01333     {
01334       currentYCoord = ( *it )->line().y1();
01335       currentSqrDist = ( y - currentYCoord ) * ( y - currentYCoord );
01336     }
01337     else if ( !itemHorizontal )
01338     {
01339       currentXCoord = ( *it )->line().x1();
01340       currentSqrDist = ( x - currentXCoord ) * ( x - currentXCoord );
01341     }
01342 
01343     if ( currentSqrDist < minSqrDist && currentSqrDist < sqrTolerance )
01344     {
01345       item = *it;
01346       minSqrDist = currentSqrDist;
01347     }
01348   }
01349 
01350   double itemTolerance = 0.0000001;
01351   if ( item )
01352   {
01353     //go through all the items to find items snapped to this snap line
01354     QList<QGraphicsItem *> itemList = items();
01355     QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
01356     for ( ; itemIt != itemList.end(); ++itemIt )
01357     {
01358       QgsComposerItem* currentItem = dynamic_cast<QgsComposerItem*>( *itemIt );
01359       if ( !currentItem || currentItem->type() == QgsComposerItem::ComposerPaper )
01360       {
01361         continue;
01362       }
01363 
01364       if ( horizontal )
01365       {
01366         if ( qgsDoubleNear( currentYCoord, currentItem->transform().dy() + currentItem->rect().top(), itemTolerance ) )
01367         {
01368           snappedItems.append( qMakePair( currentItem, QgsComposerItem::UpperMiddle ) );
01369         }
01370         else if ( qgsDoubleNear( currentYCoord, currentItem->transform().dy() + currentItem->rect().center().y(), itemTolerance ) )
01371         {
01372           snappedItems.append( qMakePair( currentItem, QgsComposerItem::Middle ) );
01373         }
01374         else if ( qgsDoubleNear( currentYCoord, currentItem->transform().dy() + currentItem->rect().bottom(), itemTolerance ) )
01375         {
01376           snappedItems.append( qMakePair( currentItem, QgsComposerItem::LowerMiddle ) );
01377         }
01378       }
01379       else
01380       {
01381         if ( qgsDoubleNear( currentXCoord, currentItem->transform().dx(), itemTolerance ) )
01382         {
01383           snappedItems.append( qMakePair( currentItem, QgsComposerItem::MiddleLeft ) );
01384         }
01385         else if ( qgsDoubleNear( currentXCoord, currentItem->transform().dx() + currentItem->rect().center().x(), itemTolerance ) )
01386         {
01387           snappedItems.append( qMakePair( currentItem, QgsComposerItem::Middle ) );
01388         }
01389         else if ( qgsDoubleNear( currentXCoord,  currentItem->transform().dx() + currentItem->rect().width(), itemTolerance ) )
01390         {
01391           snappedItems.append( qMakePair( currentItem, QgsComposerItem::MiddleRight ) );
01392         }
01393       }
01394     }
01395   }
01396 
01397   return item;
01398 }
01399 
01400 int QgsComposition::boundingRectOfSelectedItems( QRectF& bRect )
01401 {
01402   QList<QgsComposerItem*> selectedItems = selectedComposerItems();
01403   if ( selectedItems.size() < 1 )
01404   {
01405     return 1;
01406   }
01407 
01408   //set the box to the first item
01409   QgsComposerItem* currentItem = selectedItems.at( 0 );
01410   double minX = currentItem->transform().dx();
01411   double minY = currentItem->transform().dy();
01412   double maxX = minX + currentItem->rect().width();
01413   double maxY = minY + currentItem->rect().height();
01414 
01415   double currentMinX, currentMinY, currentMaxX, currentMaxY;
01416 
01417   for ( int i = 1; i < selectedItems.size(); ++i )
01418   {
01419     currentItem = selectedItems.at( i );
01420     currentMinX = currentItem->transform().dx();
01421     currentMinY = currentItem->transform().dy();
01422     currentMaxX = currentMinX + currentItem->rect().width();
01423     currentMaxY = currentMinY + currentItem->rect().height();
01424 
01425     if ( currentMinX < minX )
01426       minX = currentMinX;
01427     if ( currentMaxX > maxX )
01428       maxX = currentMaxX;
01429     if ( currentMinY < minY )
01430       minY = currentMinY;
01431     if ( currentMaxY > maxY )
01432       maxY = currentMaxY;
01433   }
01434 
01435   bRect.setTopLeft( QPointF( minX, minY ) );
01436   bRect.setBottomRight( QPointF( maxX, maxY ) );
01437   return 0;
01438 }
01439 
01440 void QgsComposition::setSnapToGridEnabled( bool b )
01441 {
01442   mSnapToGrid = b;
01443   updatePaperItems();
01444   saveSettings();
01445 }
01446 
01447 void QgsComposition::setSnapGridResolution( double r )
01448 {
01449   mSnapGridResolution = r;
01450   updatePaperItems();
01451   saveSettings();
01452 }
01453 
01454 void QgsComposition::setSnapGridOffsetX( double offset )
01455 {
01456   mSnapGridOffsetX = offset;
01457   updatePaperItems();
01458   saveSettings();
01459 }
01460 
01461 void QgsComposition::setSnapGridOffsetY( double offset )
01462 {
01463   mSnapGridOffsetY = offset;
01464   updatePaperItems();
01465   saveSettings();
01466 }
01467 
01468 void QgsComposition::setGridPen( const QPen& p )
01469 {
01470   mGridPen = p;
01471   updatePaperItems();
01472   saveSettings();
01473 }
01474 
01475 void QgsComposition::setGridStyle( GridStyle s )
01476 {
01477   mGridStyle = s;
01478   updatePaperItems();
01479   saveSettings();
01480 }
01481 
01482 void QgsComposition::setSelectionTolerance( double tol )
01483 {
01484   mSelectionTolerance = tol;
01485   saveSettings();
01486 }
01487 
01488 void QgsComposition::loadSettings()
01489 {
01490   //read grid style, grid color and pen width from settings
01491   QSettings s;
01492 
01493   QString gridStyleString;
01494   int red, green, blue;
01495   double penWidth;
01496 
01497   gridStyleString = s.value( "/qgis/composerGridStyle", "Dots" ).toString();
01498   penWidth = s.value( "/qgis/composerGridWidth", 0.5 ).toDouble();
01499   red = s.value( "/qgis/composerGridRed", 0 ).toInt();
01500   green = s.value( "/qgis/composerGridGreen", 0 ).toInt();
01501   blue = s.value( "/qgis/composerGridBlue", 0 ).toInt();
01502 
01503   mGridPen.setColor( QColor( red, green, blue ) );
01504   mGridPen.setWidthF( penWidth );
01505 
01506   if ( gridStyleString == "Dots" )
01507   {
01508     mGridStyle = Dots;
01509   }
01510   else if ( gridStyleString == "Crosses" )
01511   {
01512     mGridStyle = Crosses;
01513   }
01514   else
01515   {
01516     mGridStyle = Solid;
01517   }
01518 
01519   mSelectionTolerance = s.value( "/qgis/composerSelectionTolerance", 0.0 ).toDouble();
01520 }
01521 
01522 void QgsComposition::saveSettings()
01523 {
01524   //store grid appearance settings
01525   QSettings s;
01526   s.setValue( "/qgis/composerGridWidth", mGridPen.widthF() );
01527   s.setValue( "/qgis/composerGridRed", mGridPen.color().red() );
01528   s.setValue( "/qgis/composerGridGreen", mGridPen.color().green() );
01529   s.setValue( "/qgis/composerGridBlue", mGridPen.color().blue() );
01530 
01531   if ( mGridStyle == Solid )
01532   {
01533     s.setValue( "/qgis/composerGridStyle", "Solid" );
01534   }
01535   else if ( mGridStyle == Dots )
01536   {
01537     s.setValue( "/qgis/composerGridStyle", "Dots" );
01538   }
01539   else if ( mGridStyle == Crosses )
01540   {
01541     s.setValue( "/qgis/composerGridStyle", "Crosses" );
01542   }
01543 
01544   //store also selection tolerance
01545   s.setValue( "/qgis/composerSelectionTolerance", mSelectionTolerance );
01546 }
01547 
01548 void QgsComposition::beginCommand( QgsComposerItem* item, const QString& commandText, QgsComposerMergeCommand::Context c )
01549 {
01550   delete mActiveItemCommand;
01551   if ( !item )
01552   {
01553     mActiveItemCommand = 0;
01554     return;
01555   }
01556 
01557   if ( c == QgsComposerMergeCommand::Unknown )
01558   {
01559     mActiveItemCommand = new QgsComposerItemCommand( item, commandText );
01560   }
01561   else
01562   {
01563     mActiveItemCommand = new QgsComposerMergeCommand( c, item, commandText );
01564   }
01565   mActiveItemCommand->savePreviousState();
01566 }
01567 
01568 void QgsComposition::endCommand()
01569 {
01570   if ( mActiveItemCommand )
01571   {
01572     mActiveItemCommand->saveAfterState();
01573     if ( mActiveItemCommand->containsChange() ) //protect against empty commands
01574     {
01575       mUndoStack.push( mActiveItemCommand );
01576       QgsProject::instance()->dirty( true );
01577     }
01578     else
01579     {
01580       delete mActiveItemCommand;
01581     }
01582     mActiveItemCommand = 0;
01583   }
01584 }
01585 
01586 void QgsComposition::cancelCommand()
01587 {
01588   delete mActiveItemCommand;
01589   mActiveItemCommand = 0;
01590 }
01591 
01592 void QgsComposition::beginMultiFrameCommand( QgsComposerMultiFrame* multiFrame, const QString& text )
01593 {
01594   delete mActiveMultiFrameCommand;
01595   mActiveMultiFrameCommand = new QgsComposerMultiFrameCommand( multiFrame, text );
01596   mActiveMultiFrameCommand->savePreviousState();
01597 }
01598 
01599 void QgsComposition::endMultiFrameCommand()
01600 {
01601   if ( mActiveMultiFrameCommand )
01602   {
01603     mActiveMultiFrameCommand->saveAfterState();
01604     if ( mActiveMultiFrameCommand->containsChange() )
01605     {
01606       mUndoStack.push( mActiveMultiFrameCommand );
01607       QgsProject::instance()->dirty( true );
01608     }
01609     else
01610     {
01611       delete mActiveMultiFrameCommand;
01612     }
01613     mActiveMultiFrameCommand = 0;
01614   }
01615 }
01616 
01617 void QgsComposition::addMultiFrame( QgsComposerMultiFrame* multiFrame )
01618 {
01619   mMultiFrames.insert( multiFrame );
01620 }
01621 
01622 void QgsComposition::removeMultiFrame( QgsComposerMultiFrame* multiFrame )
01623 {
01624   mMultiFrames.remove( multiFrame );
01625 }
01626 
01627 void QgsComposition::addComposerArrow( QgsComposerArrow* arrow )
01628 {
01629   addItem( arrow );
01630   emit composerArrowAdded( arrow );
01631   clearSelection();
01632   arrow->setSelected( true );
01633   emit selectedItemChanged( arrow );
01634 }
01635 
01636 void QgsComposition::addComposerLabel( QgsComposerLabel* label )
01637 {
01638   addItem( label );
01639   emit composerLabelAdded( label );
01640   clearSelection();
01641   label->setSelected( true );
01642   emit selectedItemChanged( label );
01643 }
01644 
01645 void QgsComposition::addComposerMap( QgsComposerMap* map, bool setDefaultPreviewStyle )
01646 {
01647   addItem( map );
01648   if ( setDefaultPreviewStyle )
01649   {
01650     //set default preview mode to cache. Must be done here between adding composer map to scene and emiting signal
01651     map->setPreviewMode( QgsComposerMap::Cache );
01652   }
01653 
01654   if ( map->previewMode() != QgsComposerMap::Rectangle )
01655   {
01656     map->cache();
01657   }
01658 
01659   emit composerMapAdded( map );
01660   clearSelection();
01661   map->setSelected( true );
01662   emit selectedItemChanged( map );
01663 }
01664 
01665 void QgsComposition::addComposerScaleBar( QgsComposerScaleBar* scaleBar )
01666 {
01667   addItem( scaleBar );
01668   emit composerScaleBarAdded( scaleBar );
01669   clearSelection();
01670   scaleBar->setSelected( true );
01671   emit selectedItemChanged( scaleBar );
01672 }
01673 
01674 void QgsComposition::addComposerLegend( QgsComposerLegend* legend )
01675 {
01676   //take first available map
01677   QList<const QgsComposerMap*> mapItemList = composerMapItems();
01678   if ( mapItemList.size() > 0 )
01679   {
01680     legend->setComposerMap( mapItemList.at( 0 ) );
01681   }
01682   addItem( legend );
01683   emit composerLegendAdded( legend );
01684   clearSelection();
01685   legend->setSelected( true );
01686   emit selectedItemChanged( legend );
01687 }
01688 
01689 void QgsComposition::addComposerPicture( QgsComposerPicture* picture )
01690 {
01691   addItem( picture );
01692   emit composerPictureAdded( picture );
01693   clearSelection();
01694   picture->setSelected( true );
01695   emit selectedItemChanged( picture );
01696 }
01697 
01698 void QgsComposition::addComposerShape( QgsComposerShape* shape )
01699 {
01700   addItem( shape );
01701   emit composerShapeAdded( shape );
01702   clearSelection();
01703   shape->setSelected( true );
01704   emit selectedItemChanged( shape );
01705 }
01706 
01707 void QgsComposition::addComposerTable( QgsComposerAttributeTable* table )
01708 {
01709   addItem( table );
01710   emit composerTableAdded( table );
01711   clearSelection();
01712   table->setSelected( true );
01713   emit selectedItemChanged( table );
01714 }
01715 
01716 void QgsComposition::addComposerHtmlFrame( QgsComposerHtml* html, QgsComposerFrame* frame )
01717 {
01718   addItem( frame );
01719   emit composerHtmlFrameAdded( html, frame );
01720   clearSelection();
01721   frame->setSelected( true );
01722   emit selectedItemChanged( frame );
01723 }
01724 
01725 void QgsComposition::removeComposerItem( QgsComposerItem* item, bool createCommand )
01726 {
01727   QgsComposerMap* map = dynamic_cast<QgsComposerMap *>( item );
01728 
01729   if ( !map || !map->isDrawing() ) //don't delete a composer map while it draws
01730   {
01731     removeItem( item );
01732     QgsComposerItemGroup* itemGroup = dynamic_cast<QgsComposerItemGroup*>( item );
01733     if ( itemGroup )
01734     {
01735       //add add/remove item command for every item in the group
01736       QUndoCommand* parentCommand = new QUndoCommand( tr( "Remove item group" ) );
01737 
01738       QSet<QgsComposerItem*> groupedItems = itemGroup->items();
01739       QSet<QgsComposerItem*>::iterator it = groupedItems.begin();
01740       for ( ; it != groupedItems.end(); ++it )
01741       {
01742         QgsAddRemoveItemCommand* subcommand = new QgsAddRemoveItemCommand( QgsAddRemoveItemCommand::Removed, *it, this, "", parentCommand );
01743         connectAddRemoveCommandSignals( subcommand );
01744         emit itemRemoved( *it );
01745       }
01746 
01747       undoStack()->push( parentCommand );
01748       delete itemGroup;
01749       emit itemRemoved( itemGroup );
01750     }
01751     else
01752     {
01753       bool frameItem = ( item->type() == QgsComposerItem::ComposerFrame );
01754       QgsComposerMultiFrame* multiFrame = 0;
01755       if ( createCommand )
01756       {
01757         if ( frameItem ) //multiframe tracks item changes
01758         {
01759           multiFrame = static_cast<QgsComposerFrame*>( item )->multiFrame();
01760           item->beginItemCommand( tr( "Frame deleted" ) );
01761           emit itemRemoved( item );
01762           item->endItemCommand();
01763         }
01764         else
01765         {
01766           emit itemRemoved( item );
01767           pushAddRemoveCommand( item, tr( "Item deleted" ), QgsAddRemoveItemCommand::Removed );
01768         }
01769       }
01770       else
01771       {
01772         emit itemRemoved( item );
01773       }
01774 
01775       //check if there are frames left. If not, remove the multi frame
01776       if ( frameItem && multiFrame )
01777       {
01778         if ( multiFrame->frameCount() < 1 )
01779         {
01780           removeMultiFrame( multiFrame );
01781           if ( createCommand )
01782           {
01783             QgsAddRemoveMultiFrameCommand* command = new QgsAddRemoveMultiFrameCommand( QgsAddRemoveMultiFrameCommand::Removed,
01784                 multiFrame, this, tr( "Multiframe removed" ) );
01785             undoStack()->push( command );
01786           }
01787           else
01788           {
01789             delete multiFrame;
01790           }
01791         }
01792       }
01793     }
01794   }
01795 }
01796 
01797 void QgsComposition::pushAddRemoveCommand( QgsComposerItem* item, const QString& text, QgsAddRemoveItemCommand::State state )
01798 {
01799   QgsAddRemoveItemCommand* c = new QgsAddRemoveItemCommand( state, item, this, text );
01800   connectAddRemoveCommandSignals( c );
01801   undoStack()->push( c );
01802   QgsProject::instance()->dirty( true );
01803 }
01804 
01805 void QgsComposition::connectAddRemoveCommandSignals( QgsAddRemoveItemCommand* c )
01806 {
01807   if ( !c )
01808   {
01809     return;
01810   }
01811 
01812   QObject::connect( c, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SIGNAL( itemRemoved( QgsComposerItem* ) ) );
01813   QObject::connect( c, SIGNAL( itemAdded( QgsComposerItem* ) ), this, SLOT( sendItemAddedSignal( QgsComposerItem* ) ) );
01814 }
01815 
01816 void QgsComposition::sendItemAddedSignal( QgsComposerItem* item )
01817 {
01818   //cast and send proper signal
01819   item->setSelected( true );
01820   QgsComposerArrow* arrow = dynamic_cast<QgsComposerArrow*>( item );
01821   if ( arrow )
01822   {
01823     emit composerArrowAdded( arrow );
01824     emit selectedItemChanged( arrow );
01825     return;
01826   }
01827   QgsComposerLabel* label = dynamic_cast<QgsComposerLabel*>( item );
01828   if ( label )
01829   {
01830     emit composerLabelAdded( label );
01831     emit selectedItemChanged( label );
01832     return;
01833   }
01834   QgsComposerMap* map = dynamic_cast<QgsComposerMap*>( item );
01835   if ( map )
01836   {
01837     emit composerMapAdded( map );
01838     emit selectedItemChanged( map );
01839     return;
01840   }
01841   QgsComposerScaleBar* scalebar = dynamic_cast<QgsComposerScaleBar*>( item );
01842   if ( scalebar )
01843   {
01844     emit composerScaleBarAdded( scalebar );
01845     emit selectedItemChanged( scalebar );
01846     return;
01847   }
01848   QgsComposerLegend* legend = dynamic_cast<QgsComposerLegend*>( item );
01849   if ( legend )
01850   {
01851     emit composerLegendAdded( legend );
01852     emit selectedItemChanged( legend );
01853     return;
01854   }
01855   QgsComposerPicture* picture = dynamic_cast<QgsComposerPicture*>( item );
01856   if ( picture )
01857   {
01858     emit composerPictureAdded( picture );
01859     emit selectedItemChanged( picture );
01860     return;
01861   }
01862   QgsComposerShape* shape = dynamic_cast<QgsComposerShape*>( item );
01863   if ( shape )
01864   {
01865     emit composerShapeAdded( shape );
01866     emit selectedItemChanged( shape );
01867     return;
01868   }
01869   QgsComposerAttributeTable* table = dynamic_cast<QgsComposerAttributeTable*>( item );
01870   if ( table )
01871   {
01872     emit composerTableAdded( table );
01873     emit selectedItemChanged( table );
01874     return;
01875   }
01876   QgsComposerFrame* frame = dynamic_cast<QgsComposerFrame*>( item );
01877   if ( frame )
01878   {
01879     //emit composerFrameAdded( multiframe, frame, );
01880     QgsComposerMultiFrame* mf = frame->multiFrame();
01881     QgsComposerHtml* html = dynamic_cast<QgsComposerHtml*>( mf );
01882     if ( html )
01883     {
01884       emit composerHtmlFrameAdded( html, frame );
01885     }
01886     emit selectedItemChanged( frame );
01887     return;
01888   }
01889 }
01890 
01891 void QgsComposition::updatePaperItems()
01892 {
01893   QList< QgsPaperItem* >::iterator paperIt = mPages.begin();
01894   for ( ; paperIt != mPages.end(); ++paperIt )
01895   {
01896     ( *paperIt )->update();
01897   }
01898 }
01899 
01900 void QgsComposition::addPaperItem()
01901 {
01902   double paperHeight = this->paperHeight();
01903   double paperWidth = this->paperWidth();
01904   double currentY = paperHeight * mPages.size() + mPages.size() * mSpaceBetweenPages; //add 10mm visible space between pages
01905   QgsPaperItem* paperItem = new QgsPaperItem( 0, currentY, paperWidth, paperHeight, this ); //default size A4
01906   paperItem->setBrush( Qt::white );
01907   addItem( paperItem );
01908   paperItem->setZValue( 0 );
01909   mPages.push_back( paperItem );
01910 
01911   QgsExpression::setSpecialColumn( "$numpages", QVariant(( int )mPages.size() ) );
01912 }
01913 
01914 void QgsComposition::removePaperItems()
01915 {
01916   for ( int i = 0; i < mPages.size(); ++i )
01917   {
01918     delete mPages.at( i );
01919   }
01920   mPages.clear();
01921   QgsExpression::setSpecialColumn( "$numpages", QVariant(( int )0 ) );
01922 }
01923 
01924 void QgsComposition::deleteAndRemoveMultiFrames()
01925 {
01926   QSet<QgsComposerMultiFrame*>::iterator multiFrameIt = mMultiFrames.begin();
01927   for ( ; multiFrameIt != mMultiFrames.end(); ++multiFrameIt )
01928   {
01929     delete *multiFrameIt;
01930   }
01931   mMultiFrames.clear();
01932 }
01933 
01934 void QgsComposition::beginPrintAsPDF( QPrinter& printer, const QString& file )
01935 {
01936   printer.setOutputFormat( QPrinter::PdfFormat );
01937   printer.setOutputFileName( file );
01938   printer.setPaperSize( QSizeF( paperWidth(), paperHeight() ), QPrinter::Millimeter );
01939 
01940   QgsPaintEngineHack::fixEngineFlags( printer.paintEngine() );
01941 }
01942 
01943 void QgsComposition::exportAsPDF( const QString& file )
01944 {
01945   QPrinter printer;
01946   beginPrintAsPDF( printer, file );
01947   print( printer );
01948 }
01949 
01950 void QgsComposition::doPrint( QPrinter& printer, QPainter& p )
01951 {
01952 //QgsComposition starts page numbering at 0
01953   int fromPage = ( printer.fromPage() < 1 ) ? 0 : printer.fromPage() - 1 ;
01954   int toPage = ( printer.toPage() < 1 ) ? numPages() - 1 : printer.toPage() - 1;
01955 
01956   if ( mPrintAsRaster )
01957   {
01958     for ( int i = fromPage; i <= toPage; ++i )
01959     {
01960       if ( i > fromPage )
01961       {
01962         printer.newPage();
01963       }
01964 
01965       QImage image = printPageAsRaster( i );
01966       if ( !image.isNull() )
01967       {
01968         QRectF targetArea( 0, 0, image.width(), image.height() );
01969         p.drawImage( targetArea, image, targetArea );
01970       }
01971     }
01972   }
01973 
01974   if ( !mPrintAsRaster )
01975   {
01976     for ( int i = fromPage; i <= toPage; ++i )
01977     {
01978       if ( i > fromPage )
01979       {
01980         printer.newPage();
01981       }
01982       renderPage( &p, i );
01983     }
01984   }
01985 }
01986 
01987 void QgsComposition::beginPrint( QPrinter &printer )
01988 {
01989   //set resolution based on composer setting
01990   printer.setFullPage( true );
01991   printer.setColorMode( QPrinter::Color );
01992 
01993   //set user-defined resolution
01994   printer.setResolution( printResolution() );
01995 }
01996 
01997 void QgsComposition::print( QPrinter &printer )
01998 {
01999   beginPrint( printer );
02000   QPainter p( &printer );
02001   doPrint( printer, p );
02002 }
02003 
02004 QImage QgsComposition::printPageAsRaster( int page )
02005 {
02006   //print out via QImage, code copied from on_mActionExportAsImage_activated
02007   int width = ( int )( printResolution() * paperWidth() / 25.4 );
02008   int height = ( int )( printResolution() * paperHeight() / 25.4 );
02009   QImage image( QSize( width, height ), QImage::Format_ARGB32 );
02010   if ( !image.isNull() )
02011   {
02012     image.setDotsPerMeterX( printResolution() / 25.4 * 1000 );
02013     image.setDotsPerMeterY( printResolution() / 25.4 * 1000 );
02014     image.fill( 0 );
02015     QPainter imagePainter( &image );
02016     renderPage( &imagePainter, page );
02017   }
02018   return image;
02019 }
02020 
02021 void QgsComposition::renderPage( QPainter* p, int page )
02022 {
02023   if ( mPages.size() <= page )
02024   {
02025     return;
02026   }
02027 
02028   QgsPaperItem* paperItem = mPages[page];
02029   if ( !paperItem )
02030   {
02031     return;
02032   }
02033 
02034   QPaintDevice* paintDevice = p->device();
02035   if ( !paintDevice )
02036   {
02037     return;
02038   }
02039 
02040   QRectF paperRect = QRectF( paperItem->transform().dx(), paperItem->transform().dy(), paperItem->rect().width(), paperItem->rect().height() );
02041 
02042   QgsComposition::PlotStyle savedPlotStyle = mPlotStyle;
02043   mPlotStyle = QgsComposition::Print;
02044 
02045   setSnapLinesVisible( false );
02046   render( p, QRectF( 0, 0, paintDevice->width(), paintDevice->height() ), paperRect );
02047   setSnapLinesVisible( true );
02048 
02049   mPlotStyle = savedPlotStyle;
02050 }
02051 
02052 QString QgsComposition::encodeStringForXML( const QString& str )
02053 {
02054   QString modifiedStr( str );
02055   modifiedStr.replace( "&", "&amp;" );
02056   modifiedStr.replace( "\"", "&quot;" );
02057   modifiedStr.replace( "'", "&apos;" );
02058   modifiedStr.replace( "<", "&lt;" );
02059   modifiedStr.replace( ">", "&gt;" );
02060   return modifiedStr;
02061 }
02062 
02063 void QgsComposition::collectAlignCoordinates( QMap< double, const QgsComposerItem* >& alignCoordsX, QMap< double, const QgsComposerItem* >& alignCoordsY,
02064     const QgsComposerItem* excludeItem )
02065 {
02066   alignCoordsX.clear();
02067   alignCoordsY.clear();
02068 
02069   QList<QGraphicsItem *> itemList = items();
02070   QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
02071   for ( ; itemIt != itemList.end(); ++itemIt )
02072   {
02073     const QgsComposerItem* currentItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
02074     if ( excludeItem )
02075     {
02076       if ( !currentItem || currentItem == excludeItem )
02077       {
02078         continue;
02079       }
02080       alignCoordsX.insert( currentItem->transform().dx(), currentItem );
02081       alignCoordsX.insert( currentItem->transform().dx() + currentItem->rect().width(), currentItem );
02082       alignCoordsX.insert( currentItem->transform().dx() + currentItem->rect().center().x(), currentItem );
02083       alignCoordsY.insert( currentItem->transform().dy() + currentItem->rect().top(), currentItem );
02084       alignCoordsY.insert( currentItem->transform().dy() + currentItem->rect().center().y(), currentItem );
02085       alignCoordsY.insert( currentItem->transform().dy() + currentItem->rect().bottom(), currentItem );
02086     }
02087   }
02088 
02089   //arbitrary snap lines
02090   QList< QGraphicsLineItem* >::const_iterator sIt = mSnapLines.constBegin();
02091   for ( ; sIt != mSnapLines.constEnd(); ++sIt )
02092   {
02093     double x = ( *sIt )->line().x1();
02094     double y = ( *sIt )->line().y1();
02095     if ( qgsDoubleNear( y, 0.0 ) )
02096     {
02097       alignCoordsX.insert( x, 0 );
02098     }
02099     else
02100     {
02101       alignCoordsY.insert( y, 0 );
02102     }
02103   }
02104 }
02105 
02106 void QgsComposition::checkNearestItem( double checkCoord, const QMap< double, const QgsComposerItem* >& alignCoords, double& smallestDiff,
02107                                        double itemCoordOffset, double& itemCoord, double& alignCoord ) const
02108 {
02109   double currentCoord = 0;
02110   if ( !nearestItem( alignCoords, checkCoord, currentCoord ) )
02111   {
02112     return;
02113   }
02114 
02115   double currentDiff = abs( checkCoord - currentCoord );
02116   if ( currentDiff < mAlignmentSnapTolerance )
02117   {
02118     itemCoord = currentCoord + itemCoordOffset;
02119     alignCoord = currentCoord;
02120     smallestDiff = currentDiff;
02121   }
02122 }
02123 
02124 bool QgsComposition::nearestItem( const QMap< double, const QgsComposerItem* >& coords, double value, double& nearestValue )
02125 {
02126   if ( coords.size() < 1 )
02127   {
02128     return false;
02129   }
02130 
02131   QMap< double, const QgsComposerItem* >::const_iterator it = coords.lowerBound( value );
02132   if ( it == coords.constBegin() ) //value smaller than first map value
02133   {
02134     nearestValue = it.key();
02135     return true;
02136   }
02137   else if ( it == coords.constEnd() ) //value larger than last map value
02138   {
02139     --it;
02140     nearestValue = it.key();
02141     return true;
02142   }
02143   else
02144   {
02145     //get smaller value and larger value and return the closer one
02146     double upperVal = it.key();
02147     --it;
02148     double lowerVal = it.key();
02149 
02150     double lowerDiff = value - lowerVal;
02151     double upperDiff = upperVal - value;
02152     if ( lowerDiff < upperDiff )
02153     {
02154       nearestValue = lowerVal;
02155       return true;
02156     }
02157     else
02158     {
02159       nearestValue = upperVal;
02160       return true;
02161     }
02162   }
02163 }
02164 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines