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