LCOV - code coverage report
Current view: top level - core/layout - qgslayout.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 650 0.0 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :                               qgslayout.cpp
       3                 :            :                              -------------------
       4                 :            :     begin                : June 2017
       5                 :            :     copyright            : (C) 2017 by Nyall Dawson
       6                 :            :     email                : nyall dot dawson at gmail dot com
       7                 :            :  ***************************************************************************/
       8                 :            : /***************************************************************************
       9                 :            :  *                                                                         *
      10                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      11                 :            :  *   it under the terms of the GNU General Public License as published by  *
      12                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      13                 :            :  *   (at your option) any later version.                                   *
      14                 :            :  *                                                                         *
      15                 :            :  ***************************************************************************/
      16                 :            : 
      17                 :            : #include "qgslayout.h"
      18                 :            : #include "qgslayoutitem.h"
      19                 :            : #include "qgslayoutmodel.h"
      20                 :            : #include "qgslayoutpagecollection.h"
      21                 :            : #include "qgslayoutguidecollection.h"
      22                 :            : #include "qgsreadwritecontext.h"
      23                 :            : #include "qgsproject.h"
      24                 :            : #include "qgslayoutitemundocommand.h"
      25                 :            : #include "qgslayoutitemgroup.h"
      26                 :            : #include "qgslayoutitemgroupundocommand.h"
      27                 :            : #include "qgslayoutmultiframe.h"
      28                 :            : #include "qgslayoutitemmap.h"
      29                 :            : #include "qgslayoutundostack.h"
      30                 :            : #include "qgscompositionconverter.h"
      31                 :            : #include "qgsvectorlayer.h"
      32                 :            : #include "qgsexpressioncontextutils.h"
      33                 :            : #include "qgsstyleentityvisitor.h"
      34                 :            : #include "qgsruntimeprofiler.h"
      35                 :            : 
      36                 :          0 : QgsLayout::QgsLayout( QgsProject *project )
      37                 :          0 :   : mProject( project )
      38                 :          0 :   , mRenderContext( new QgsLayoutRenderContext( this ) )
      39                 :          0 :   , mReportContext( new QgsLayoutReportContext( this ) )
      40                 :          0 :   , mSnapper( QgsLayoutSnapper( this ) )
      41                 :          0 :   , mGridSettings( this )
      42                 :          0 :   , mPageCollection( new QgsLayoutPageCollection( this ) )
      43                 :          0 :   , mUndoStack( new QgsLayoutUndoStack( this ) )
      44                 :          0 : {
      45                 :            :   // just to make sure - this should be the default, but maybe it'll change in some future Qt version...
      46                 :          0 :   setBackgroundBrush( Qt::NoBrush );
      47                 :          0 :   mItemsModel.reset( new QgsLayoutModel( this ) );
      48                 :          0 : }
      49                 :            : 
      50                 :          0 : QgsLayout::~QgsLayout()
      51                 :          0 : {
      52                 :            :   // no need for undo commands when we're destroying the layout
      53                 :          0 :   mUndoStack->blockCommands( true );
      54                 :            : 
      55                 :          0 :   deleteAndRemoveMultiFrames();
      56                 :            : 
      57                 :            :   // make sure that all layout items are removed before
      58                 :            :   // this class is deconstructed - to avoid segfaults
      59                 :            :   // when layout items access in destructor layout that isn't valid anymore
      60                 :            : 
      61                 :            :   // since deletion of some item types (e.g. groups) trigger deletion
      62                 :            :   // of other items, we have to do this careful, one at a time...
      63                 :          0 :   QList<QGraphicsItem *> itemList = items();
      64                 :          0 :   bool deleted = true;
      65                 :          0 :   while ( deleted )
      66                 :            :   {
      67                 :          0 :     deleted = false;
      68                 :          0 :     for ( QGraphicsItem *item : std::as_const( itemList ) )
      69                 :            :     {
      70                 :          0 :       if ( dynamic_cast< QgsLayoutItem * >( item ) && !dynamic_cast< QgsLayoutItemPage *>( item ) )
      71                 :            :       {
      72                 :          0 :         delete item;
      73                 :          0 :         deleted = true;
      74                 :          0 :         break;
      75                 :            :       }
      76                 :            :     }
      77                 :          0 :     itemList = items();
      78                 :            :   }
      79                 :            : 
      80                 :          0 :   mItemsModel.reset(); // manually delete, so we can control order of destruction
      81                 :          0 : }
      82                 :            : 
      83                 :          0 : QgsLayout *QgsLayout::clone() const
      84                 :            : {
      85                 :          0 :   QDomDocument currentDoc;
      86                 :            : 
      87                 :          0 :   QgsReadWriteContext context;
      88                 :          0 :   QDomElement elem = writeXml( currentDoc, context );
      89                 :          0 :   currentDoc.appendChild( elem );
      90                 :            : 
      91                 :          0 :   std::unique_ptr< QgsLayout > newLayout = std::make_unique< QgsLayout >( mProject );
      92                 :          0 :   bool ok = false;
      93                 :          0 :   newLayout->loadFromTemplate( currentDoc, context, true, &ok );
      94                 :          0 :   if ( !ok )
      95                 :            :   {
      96                 :          0 :     return nullptr;
      97                 :            :   }
      98                 :            : 
      99                 :          0 :   return newLayout.release();
     100                 :          0 : }
     101                 :            : 
     102                 :          0 : void QgsLayout::initializeDefaults()
     103                 :            : {
     104                 :            :   // default to a A4 landscape page
     105                 :          0 :   QgsLayoutItemPage *page = new QgsLayoutItemPage( this );
     106                 :          0 :   page->setPageSize( QgsLayoutSize( 297, 210, QgsUnitTypes::LayoutMillimeters ) );
     107                 :          0 :   mPageCollection->addPage( page );
     108                 :          0 :   mUndoStack->stack()->clear();
     109                 :          0 : }
     110                 :            : 
     111                 :          0 : void QgsLayout::clear()
     112                 :            : {
     113                 :          0 :   deleteAndRemoveMultiFrames();
     114                 :            : 
     115                 :            :   //delete all non paper items
     116                 :          0 :   const QList<QGraphicsItem *> itemList = items();
     117                 :          0 :   for ( QGraphicsItem *item : itemList )
     118                 :            :   {
     119                 :          0 :     QgsLayoutItem *cItem = dynamic_cast<QgsLayoutItem *>( item );
     120                 :          0 :     QgsLayoutItemPage *pItem = dynamic_cast<QgsLayoutItemPage *>( item );
     121                 :          0 :     if ( cItem && !pItem )
     122                 :            :     {
     123                 :          0 :       removeLayoutItemPrivate( cItem );
     124                 :          0 :     }
     125                 :            :   }
     126                 :          0 :   mItemsModel->clear();
     127                 :            : 
     128                 :          0 :   mPageCollection->clear();
     129                 :          0 :   mUndoStack->stack()->clear();
     130                 :          0 : }
     131                 :            : 
     132                 :          0 : QgsProject *QgsLayout::project() const
     133                 :            : {
     134                 :          0 :   return mProject;
     135                 :            : }
     136                 :            : 
     137                 :          0 : QgsLayoutModel *QgsLayout::itemsModel()
     138                 :            : {
     139                 :          0 :   return mItemsModel.get();
     140                 :            : }
     141                 :            : 
     142                 :          0 : QList<QgsLayoutItem *> QgsLayout::selectedLayoutItems( const bool includeLockedItems )
     143                 :            : {
     144                 :          0 :   QList<QgsLayoutItem *> layoutItemList;
     145                 :            : 
     146                 :          0 :   const QList<QGraphicsItem *> graphicsItemList = selectedItems();
     147                 :          0 :   for ( QGraphicsItem *item : graphicsItemList )
     148                 :            :   {
     149                 :          0 :     QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item );
     150                 :          0 :     if ( layoutItem && ( includeLockedItems || !layoutItem->isLocked() ) )
     151                 :            :     {
     152                 :          0 :       layoutItemList.push_back( layoutItem );
     153                 :          0 :     }
     154                 :            :   }
     155                 :            : 
     156                 :          0 :   return layoutItemList;
     157                 :          0 : }
     158                 :            : 
     159                 :          0 : void QgsLayout::setSelectedItem( QgsLayoutItem *item )
     160                 :            : {
     161                 :          0 :   whileBlocking( this )->deselectAll();
     162                 :          0 :   if ( item )
     163                 :            :   {
     164                 :          0 :     item->setSelected( true );
     165                 :          0 :   }
     166                 :          0 :   emit selectedItemChanged( item );
     167                 :          0 : }
     168                 :            : 
     169                 :          0 : void QgsLayout::deselectAll()
     170                 :            : {
     171                 :            :   //we can't use QGraphicsScene::clearSelection, as that emits no signals
     172                 :            :   //and we don't know which items are being deselected
     173                 :            :   //accordingly, we can't inform the layout model of selection changes
     174                 :            :   //instead, do the clear selection manually...
     175                 :          0 :   const QList<QGraphicsItem *> selectedItemList = selectedItems();
     176                 :          0 :   for ( QGraphicsItem *item : selectedItemList )
     177                 :            :   {
     178                 :          0 :     if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
     179                 :            :     {
     180                 :          0 :       layoutItem->setSelected( false );
     181                 :          0 :     }
     182                 :            :   }
     183                 :          0 :   emit selectedItemChanged( nullptr );
     184                 :          0 : }
     185                 :            : 
     186                 :          0 : bool QgsLayout::raiseItem( QgsLayoutItem *item, bool deferUpdate )
     187                 :            : {
     188                 :            :   //model handles reordering items
     189                 :          0 :   bool result = mItemsModel->reorderItemUp( item );
     190                 :          0 :   if ( result && !deferUpdate )
     191                 :            :   {
     192                 :            :     //update all positions
     193                 :          0 :     updateZValues();
     194                 :          0 :     update();
     195                 :          0 :   }
     196                 :          0 :   return result;
     197                 :            : }
     198                 :            : 
     199                 :          0 : bool QgsLayout::lowerItem( QgsLayoutItem *item, bool deferUpdate )
     200                 :            : {
     201                 :            :   //model handles reordering items
     202                 :          0 :   bool result = mItemsModel->reorderItemDown( item );
     203                 :          0 :   if ( result && !deferUpdate )
     204                 :            :   {
     205                 :            :     //update all positions
     206                 :          0 :     updateZValues();
     207                 :          0 :     update();
     208                 :          0 :   }
     209                 :          0 :   return result;
     210                 :            : }
     211                 :            : 
     212                 :          0 : bool QgsLayout::moveItemToTop( QgsLayoutItem *item, bool deferUpdate )
     213                 :            : {
     214                 :            :   //model handles reordering items
     215                 :          0 :   bool result = mItemsModel->reorderItemToTop( item );
     216                 :          0 :   if ( result && !deferUpdate )
     217                 :            :   {
     218                 :            :     //update all positions
     219                 :          0 :     updateZValues();
     220                 :          0 :     update();
     221                 :          0 :   }
     222                 :          0 :   return result;
     223                 :            : }
     224                 :            : 
     225                 :          0 : bool QgsLayout::moveItemToBottom( QgsLayoutItem *item, bool deferUpdate )
     226                 :            : {
     227                 :            :   //model handles reordering items
     228                 :          0 :   bool result = mItemsModel->reorderItemToBottom( item );
     229                 :          0 :   if ( result && !deferUpdate )
     230                 :            :   {
     231                 :            :     //update all positions
     232                 :          0 :     updateZValues();
     233                 :          0 :     update();
     234                 :          0 :   }
     235                 :          0 :   return result;
     236                 :            : }
     237                 :            : 
     238                 :          0 : QgsLayoutItem *QgsLayout::itemByUuid( const QString &uuid, bool includeTemplateUuids ) const
     239                 :            : {
     240                 :          0 :   QList<QgsLayoutItem *> itemList;
     241                 :          0 :   layoutItems( itemList );
     242                 :          0 :   for ( QgsLayoutItem *item : std::as_const( itemList ) )
     243                 :            :   {
     244                 :          0 :     if ( item->uuid() == uuid )
     245                 :          0 :       return item;
     246                 :          0 :     else if ( includeTemplateUuids && item->mTemplateUuid == uuid )
     247                 :          0 :       return item;
     248                 :            :   }
     249                 :            : 
     250                 :          0 :   return nullptr;
     251                 :          0 : }
     252                 :            : 
     253                 :          0 : QgsLayoutItem *QgsLayout::itemByTemplateUuid( const QString &uuid ) const
     254                 :            : {
     255                 :          0 :   QList<QgsLayoutItem *> itemList;
     256                 :          0 :   layoutItems( itemList );
     257                 :          0 :   for ( QgsLayoutItem *item : std::as_const( itemList ) )
     258                 :            :   {
     259                 :          0 :     if ( item->mTemplateUuid == uuid )
     260                 :          0 :       return item;
     261                 :            :   }
     262                 :            : 
     263                 :          0 :   return nullptr;
     264                 :          0 : }
     265                 :            : 
     266                 :          0 : QgsLayoutItem *QgsLayout::itemById( const QString &id ) const
     267                 :            : {
     268                 :          0 :   const QList<QGraphicsItem *> itemList = items();
     269                 :          0 :   for ( QGraphicsItem *item : itemList )
     270                 :            :   {
     271                 :          0 :     QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item );
     272                 :          0 :     if ( layoutItem && layoutItem->id() == id )
     273                 :            :     {
     274                 :          0 :       return layoutItem;
     275                 :            :     }
     276                 :            :   }
     277                 :          0 :   return nullptr;
     278                 :          0 : }
     279                 :            : 
     280                 :          0 : QgsLayoutMultiFrame *QgsLayout::multiFrameByUuid( const QString &uuid, bool includeTemplateUuids ) const
     281                 :            : {
     282                 :          0 :   for ( QgsLayoutMultiFrame *mf : mMultiFrames )
     283                 :            :   {
     284                 :          0 :     if ( mf->uuid() == uuid )
     285                 :          0 :       return mf;
     286                 :          0 :     else if ( includeTemplateUuids && mf->mTemplateUuid == uuid )
     287                 :          0 :       return mf;
     288                 :            :   }
     289                 :            : 
     290                 :          0 :   return nullptr;
     291                 :          0 : }
     292                 :            : 
     293                 :          0 : QgsLayoutItem *QgsLayout::layoutItemAt( QPointF position, const bool ignoreLocked ) const
     294                 :            : {
     295                 :          0 :   return layoutItemAt( position, nullptr, ignoreLocked );
     296                 :            : }
     297                 :            : 
     298                 :          0 : QgsLayoutItem *QgsLayout::layoutItemAt( QPointF position, const QgsLayoutItem *belowItem, const bool ignoreLocked ) const
     299                 :            : {
     300                 :            :   //get a list of items which intersect the specified position, in descending z order
     301                 :          0 :   const QList<QGraphicsItem *> itemList = items( position, Qt::IntersectsItemShape, Qt::DescendingOrder );
     302                 :            : 
     303                 :          0 :   bool foundBelowItem = false;
     304                 :          0 :   for ( QGraphicsItem *graphicsItem : itemList )
     305                 :            :   {
     306                 :          0 :     QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( graphicsItem );
     307                 :          0 :     QgsLayoutItemPage *paperItem = dynamic_cast<QgsLayoutItemPage *>( layoutItem );
     308                 :          0 :     if ( layoutItem && !paperItem )
     309                 :            :     {
     310                 :            :       // If we are not checking for a an item below a specified item, or if we've
     311                 :            :       // already found that item, then we've found our target
     312                 :          0 :       if ( ( ! belowItem || foundBelowItem ) && ( !ignoreLocked || !layoutItem->isLocked() ) )
     313                 :            :       {
     314                 :          0 :         return layoutItem;
     315                 :            :       }
     316                 :            :       else
     317                 :            :       {
     318                 :          0 :         if ( layoutItem == belowItem )
     319                 :            :         {
     320                 :            :           //Target item is next in list
     321                 :          0 :           foundBelowItem = true;
     322                 :          0 :         }
     323                 :            :       }
     324                 :          0 :     }
     325                 :            :   }
     326                 :          0 :   return nullptr;
     327                 :          0 : }
     328                 :            : 
     329                 :          0 : double QgsLayout::convertToLayoutUnits( QgsLayoutMeasurement measurement ) const
     330                 :            : {
     331                 :          0 :   return mRenderContext->measurementConverter().convert( measurement, mUnits ).length();
     332                 :            : }
     333                 :            : 
     334                 :          0 : QSizeF QgsLayout::convertToLayoutUnits( const QgsLayoutSize &size ) const
     335                 :            : {
     336                 :          0 :   return mRenderContext->measurementConverter().convert( size, mUnits ).toQSizeF();
     337                 :            : }
     338                 :            : 
     339                 :          0 : QPointF QgsLayout::convertToLayoutUnits( const QgsLayoutPoint &point ) const
     340                 :            : {
     341                 :          0 :   return mRenderContext->measurementConverter().convert( point, mUnits ).toQPointF();
     342                 :            : }
     343                 :            : 
     344                 :          0 : QgsLayoutMeasurement QgsLayout::convertFromLayoutUnits( const double length, const QgsUnitTypes::LayoutUnit unit ) const
     345                 :            : {
     346                 :          0 :   return mRenderContext->measurementConverter().convert( QgsLayoutMeasurement( length, mUnits ), unit );
     347                 :            : }
     348                 :            : 
     349                 :          0 : QgsLayoutSize QgsLayout::convertFromLayoutUnits( QSizeF size, const QgsUnitTypes::LayoutUnit unit ) const
     350                 :            : {
     351                 :          0 :   return mRenderContext->measurementConverter().convert( QgsLayoutSize( size.width(), size.height(), mUnits ), unit );
     352                 :            : }
     353                 :            : 
     354                 :          0 : QgsLayoutPoint QgsLayout::convertFromLayoutUnits( QPointF point, const QgsUnitTypes::LayoutUnit unit ) const
     355                 :            : {
     356                 :          0 :   return mRenderContext->measurementConverter().convert( QgsLayoutPoint( point.x(), point.y(), mUnits ), unit );
     357                 :            : }
     358                 :            : 
     359                 :          0 : QgsLayoutRenderContext &QgsLayout::renderContext()
     360                 :            : {
     361                 :          0 :   return *mRenderContext;
     362                 :            : }
     363                 :            : 
     364                 :          0 : const QgsLayoutRenderContext &QgsLayout::renderContext() const
     365                 :            : {
     366                 :          0 :   return *mRenderContext;
     367                 :            : }
     368                 :            : 
     369                 :          0 : QgsLayoutReportContext &QgsLayout::reportContext()
     370                 :            : {
     371                 :          0 :   return *mReportContext;
     372                 :            : }
     373                 :            : 
     374                 :          0 : const QgsLayoutReportContext &QgsLayout::reportContext() const
     375                 :            : {
     376                 :          0 :   return *mReportContext;
     377                 :            : }
     378                 :            : 
     379                 :          0 : void QgsLayout::reloadSettings()
     380                 :            : {
     381                 :          0 :   mGridSettings.loadFromSettings();
     382                 :          0 :   mPageCollection->redraw();
     383                 :          0 : }
     384                 :            : 
     385                 :          0 : QgsLayoutGuideCollection &QgsLayout::guides()
     386                 :            : {
     387                 :          0 :   return mPageCollection->guides();
     388                 :            : }
     389                 :            : 
     390                 :          0 : const QgsLayoutGuideCollection &QgsLayout::guides() const
     391                 :            : {
     392                 :          0 :   return mPageCollection->guides();
     393                 :            : }
     394                 :            : 
     395                 :          0 : QgsExpressionContext QgsLayout::createExpressionContext() const
     396                 :            : {
     397                 :          0 :   QgsExpressionContext context = QgsExpressionContext();
     398                 :          0 :   context.appendScope( QgsExpressionContextUtils::globalScope() );
     399                 :          0 :   context.appendScope( QgsExpressionContextUtils::projectScope( mProject ) );
     400                 :          0 :   if ( mReportContext->layer() )
     401                 :          0 :     context.appendScope( QgsExpressionContextUtils::layerScope( mReportContext->layer() ) );
     402                 :            : 
     403                 :          0 :   context.appendScope( QgsExpressionContextUtils::layoutScope( this ) );
     404                 :          0 :   return context;
     405                 :          0 : }
     406                 :            : 
     407                 :          0 : void QgsLayout::setCustomProperty( const QString &key, const QVariant &value )
     408                 :            : {
     409                 :          0 :   mCustomProperties.setValue( key, value );
     410                 :            : 
     411                 :          0 :   if ( key.startsWith( QLatin1String( "variable" ) ) )
     412                 :          0 :     emit variablesChanged();
     413                 :          0 : }
     414                 :            : 
     415                 :          0 : QVariant QgsLayout::customProperty( const QString &key, const QVariant &defaultValue ) const
     416                 :            : {
     417                 :          0 :   return mCustomProperties.value( key, defaultValue );
     418                 :            : }
     419                 :            : 
     420                 :          0 : void QgsLayout::removeCustomProperty( const QString &key )
     421                 :            : {
     422                 :          0 :   mCustomProperties.remove( key );
     423                 :          0 : }
     424                 :            : 
     425                 :          0 : QStringList QgsLayout::customProperties() const
     426                 :            : {
     427                 :          0 :   return mCustomProperties.keys();
     428                 :            : }
     429                 :            : 
     430                 :          0 : QgsLayoutItemMap *QgsLayout::referenceMap() const
     431                 :            : {
     432                 :            :   // prefer explicitly set reference map
     433                 :          0 :   if ( QgsLayoutItemMap *map = qobject_cast< QgsLayoutItemMap * >( itemByUuid( mWorldFileMapId ) ) )
     434                 :          0 :     return map;
     435                 :            : 
     436                 :            :   // else try to find largest map
     437                 :          0 :   QList< QgsLayoutItemMap * > maps;
     438                 :          0 :   layoutItems( maps );
     439                 :          0 :   QgsLayoutItemMap *largestMap = nullptr;
     440                 :          0 :   double largestMapArea = 0;
     441                 :          0 :   for ( QgsLayoutItemMap *map : std::as_const( maps ) )
     442                 :            :   {
     443                 :          0 :     double area = map->rect().width() * map->rect().height();
     444                 :          0 :     if ( area > largestMapArea )
     445                 :            :     {
     446                 :          0 :       largestMapArea = area;
     447                 :          0 :       largestMap = map;
     448                 :          0 :     }
     449                 :            :   }
     450                 :          0 :   return largestMap;
     451                 :          0 : }
     452                 :            : 
     453                 :          0 : void QgsLayout::setReferenceMap( QgsLayoutItemMap *map )
     454                 :            : {
     455                 :          0 :   mWorldFileMapId = map ? map->uuid() : QString();
     456                 :          0 :   mProject->setDirty( true );
     457                 :          0 : }
     458                 :            : 
     459                 :          0 : QgsLayoutPageCollection *QgsLayout::pageCollection()
     460                 :            : {
     461                 :          0 :   return mPageCollection.get();
     462                 :            : }
     463                 :            : 
     464                 :          0 : const QgsLayoutPageCollection *QgsLayout::pageCollection() const
     465                 :            : {
     466                 :          0 :   return mPageCollection.get();
     467                 :            : }
     468                 :            : 
     469                 :          0 : QRectF QgsLayout::layoutBounds( bool ignorePages, double margin ) const
     470                 :            : {
     471                 :            :   //start with an empty rectangle
     472                 :          0 :   QRectF bounds;
     473                 :            : 
     474                 :            :   //add all layout items and pages which are in the layout
     475                 :          0 :   const auto constItems = items();
     476                 :          0 :   for ( const QGraphicsItem *item : constItems )
     477                 :            :   {
     478                 :          0 :     const QgsLayoutItem *layoutItem = dynamic_cast<const QgsLayoutItem *>( item );
     479                 :          0 :     if ( !layoutItem )
     480                 :          0 :       continue;
     481                 :            : 
     482                 :          0 :     bool isPage = layoutItem->type() == QgsLayoutItemRegistry::LayoutPage;
     483                 :          0 :     if ( !isPage || !ignorePages )
     484                 :            :     {
     485                 :            :       //expand bounds with current item's bounds
     486                 :          0 :       QRectF itemBounds;
     487                 :          0 :       if ( isPage )
     488                 :            :       {
     489                 :            :         // for pages we only consider the item's rect - not the bounding rect
     490                 :            :         // as the bounding rect contains extra padding
     491                 :          0 :         itemBounds = layoutItem->mapToScene( layoutItem->rect() ).boundingRect();
     492                 :          0 :       }
     493                 :            :       else
     494                 :          0 :         itemBounds = item->sceneBoundingRect();
     495                 :            : 
     496                 :          0 :       if ( bounds.isValid() )
     497                 :          0 :         bounds = bounds.united( itemBounds );
     498                 :            :       else
     499                 :          0 :         bounds = itemBounds;
     500                 :          0 :     }
     501                 :            :   }
     502                 :            : 
     503                 :          0 :   if ( bounds.isValid() && margin > 0.0 )
     504                 :            :   {
     505                 :            :     //finally, expand bounds out by specified margin of page size
     506                 :          0 :     double maxWidth = mPageCollection->maximumPageWidth();
     507                 :          0 :     bounds.adjust( -maxWidth * margin, -maxWidth * margin, maxWidth * margin, maxWidth * margin );
     508                 :          0 :   }
     509                 :            : 
     510                 :            :   return bounds;
     511                 :            : 
     512                 :          0 : }
     513                 :            : 
     514                 :          0 : QRectF QgsLayout::pageItemBounds( int page, bool visibleOnly ) const
     515                 :            : {
     516                 :            :   //start with an empty rectangle
     517                 :          0 :   QRectF bounds;
     518                 :            : 
     519                 :            :   //add all QgsLayoutItems on page
     520                 :          0 :   const QList<QGraphicsItem *> itemList = items();
     521                 :          0 :   for ( QGraphicsItem *item : itemList )
     522                 :            :   {
     523                 :          0 :     const QgsLayoutItem *layoutItem = dynamic_cast<const QgsLayoutItem *>( item );
     524                 :          0 :     if ( layoutItem && layoutItem->type() != QgsLayoutItemRegistry::LayoutPage && layoutItem->page() == page )
     525                 :            :     {
     526                 :          0 :       if ( visibleOnly && !layoutItem->isVisible() )
     527                 :          0 :         continue;
     528                 :            : 
     529                 :            :       //expand bounds with current item's bounds
     530                 :          0 :       if ( bounds.isValid() )
     531                 :          0 :         bounds = bounds.united( item->sceneBoundingRect() );
     532                 :            :       else
     533                 :          0 :         bounds = item->sceneBoundingRect();
     534                 :          0 :     }
     535                 :            :   }
     536                 :            : 
     537                 :            :   return bounds;
     538                 :          0 : }
     539                 :            : 
     540                 :          0 : void QgsLayout::addLayoutItem( QgsLayoutItem *item )
     541                 :            : {
     542                 :          0 :   addLayoutItemPrivate( item );
     543                 :          0 :   QString undoText;
     544                 :          0 :   if ( QgsLayoutItemAbstractMetadata *metadata = QgsApplication::layoutItemRegistry()->itemMetadata( item->type() ) )
     545                 :            :   {
     546                 :          0 :     undoText = tr( "Create %1" ).arg( metadata->visibleName() );
     547                 :          0 :   }
     548                 :            :   else
     549                 :            :   {
     550                 :          0 :     undoText = tr( "Create Item" );
     551                 :            :   }
     552                 :          0 :   if ( !mUndoStack->isBlocked() )
     553                 :          0 :     mUndoStack->push( new QgsLayoutItemAddItemCommand( item, undoText ) );
     554                 :          0 : }
     555                 :            : 
     556                 :          0 : void QgsLayout::removeLayoutItem( QgsLayoutItem *item )
     557                 :            : {
     558                 :          0 :   std::unique_ptr< QgsLayoutItemDeleteUndoCommand > deleteCommand;
     559                 :          0 :   if ( !mUndoStack->isBlocked() )
     560                 :            :   {
     561                 :          0 :     mUndoStack->beginMacro( tr( "Delete Items" ) );
     562                 :          0 :     deleteCommand.reset( new QgsLayoutItemDeleteUndoCommand( item, tr( "Delete Item" ) ) );
     563                 :          0 :   }
     564                 :          0 :   removeLayoutItemPrivate( item );
     565                 :          0 :   if ( deleteCommand )
     566                 :            :   {
     567                 :          0 :     mUndoStack->push( deleteCommand.release() );
     568                 :          0 :     mUndoStack->endMacro();
     569                 :          0 :   }
     570                 :          0 : }
     571                 :            : 
     572                 :          0 : void QgsLayout::addMultiFrame( QgsLayoutMultiFrame *multiFrame )
     573                 :            : {
     574                 :          0 :   if ( !multiFrame )
     575                 :          0 :     return;
     576                 :            : 
     577                 :          0 :   if ( !mMultiFrames.contains( multiFrame ) )
     578                 :          0 :     mMultiFrames << multiFrame;
     579                 :          0 : }
     580                 :            : 
     581                 :          0 : void QgsLayout::removeMultiFrame( QgsLayoutMultiFrame *multiFrame )
     582                 :            : {
     583                 :          0 :   mMultiFrames.removeAll( multiFrame );
     584                 :          0 : }
     585                 :            : 
     586                 :          0 : QList<QgsLayoutMultiFrame *> QgsLayout::multiFrames() const
     587                 :            : {
     588                 :          0 :   return mMultiFrames;
     589                 :            : }
     590                 :            : 
     591                 :          0 : bool QgsLayout::saveAsTemplate( const QString &path, const QgsReadWriteContext &context ) const
     592                 :            : {
     593                 :          0 :   QFile templateFile( path );
     594                 :          0 :   if ( !templateFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
     595                 :            :   {
     596                 :          0 :     return false;
     597                 :            :   }
     598                 :            : 
     599                 :          0 :   QDomDocument saveDocument;
     600                 :          0 :   QDomElement elem = writeXml( saveDocument, context );
     601                 :          0 :   saveDocument.appendChild( elem );
     602                 :            : 
     603                 :          0 :   if ( templateFile.write( saveDocument.toByteArray() ) == -1 )
     604                 :          0 :     return false;
     605                 :            : 
     606                 :          0 :   return true;
     607                 :          0 : }
     608                 :            : 
     609                 :          0 : QList< QgsLayoutItem * > QgsLayout::loadFromTemplate( const QDomDocument &document, const QgsReadWriteContext &context, bool clearExisting, bool *ok )
     610                 :            : {
     611                 :          0 :   if ( ok )
     612                 :          0 :     *ok = false;
     613                 :            : 
     614                 :          0 :   QList< QgsLayoutItem * > result;
     615                 :            : 
     616                 :          0 :   if ( clearExisting )
     617                 :            :   {
     618                 :          0 :     clear();
     619                 :          0 :   }
     620                 :            : 
     621                 :          0 :   QDomDocument doc;
     622                 :            : 
     623                 :            :   // If this is a 2.x composition template, convert it to a layout template
     624                 :          0 :   if ( QgsCompositionConverter::isCompositionTemplate( document ) )
     625                 :            :   {
     626                 :          0 :     doc = QgsCompositionConverter::convertCompositionTemplate( document, mProject );
     627                 :          0 :   }
     628                 :            :   else
     629                 :            :   {
     630                 :          0 :     doc = document;
     631                 :            :   }
     632                 :            : 
     633                 :            :   // remove all uuid attributes since we don't want duplicates UUIDS
     634                 :          0 :   QDomNodeList itemsNodes = doc.elementsByTagName( QStringLiteral( "LayoutItem" ) );
     635                 :          0 :   for ( int i = 0; i < itemsNodes.count(); ++i )
     636                 :            :   {
     637                 :          0 :     QDomNode itemNode = itemsNodes.at( i );
     638                 :          0 :     if ( itemNode.isElement() )
     639                 :            :     {
     640                 :          0 :       itemNode.toElement().removeAttribute( QStringLiteral( "uuid" ) );
     641                 :          0 :     }
     642                 :          0 :   }
     643                 :          0 :   QDomNodeList multiFrameNodes = doc.elementsByTagName( QStringLiteral( "LayoutMultiFrame" ) );
     644                 :          0 :   for ( int i = 0; i < multiFrameNodes.count(); ++i )
     645                 :            :   {
     646                 :          0 :     QDomNode multiFrameNode = multiFrameNodes.at( i );
     647                 :          0 :     if ( multiFrameNode.isElement() )
     648                 :            :     {
     649                 :          0 :       multiFrameNode.toElement().removeAttribute( QStringLiteral( "uuid" ) );
     650                 :          0 :       QDomNodeList frameNodes = multiFrameNode.toElement().elementsByTagName( QStringLiteral( "childFrame" ) );
     651                 :          0 :       QDomNode itemNode = frameNodes.at( i );
     652                 :          0 :       if ( itemNode.isElement() )
     653                 :            :       {
     654                 :          0 :         itemNode.toElement().removeAttribute( QStringLiteral( "uuid" ) );
     655                 :          0 :       }
     656                 :          0 :     }
     657                 :          0 :   }
     658                 :            : 
     659                 :            :   //read general settings
     660                 :          0 :   if ( clearExisting )
     661                 :            :   {
     662                 :          0 :     QDomElement layoutElem = doc.documentElement();
     663                 :          0 :     if ( layoutElem.isNull() )
     664                 :            :     {
     665                 :          0 :       return result;
     666                 :            :     }
     667                 :            : 
     668                 :          0 :     bool loadOk = readXml( layoutElem, doc, context );
     669                 :          0 :     if ( !loadOk )
     670                 :            :     {
     671                 :          0 :       return result;
     672                 :            :     }
     673                 :          0 :     layoutItems( result );
     674                 :          0 :   }
     675                 :            :   else
     676                 :            :   {
     677                 :          0 :     result = addItemsFromXml( doc.documentElement(), doc, context );
     678                 :            :   }
     679                 :            : 
     680                 :          0 :   if ( ok )
     681                 :          0 :     *ok = true;
     682                 :            : 
     683                 :          0 :   return result;
     684                 :          0 : }
     685                 :            : 
     686                 :          0 : QgsLayoutUndoStack *QgsLayout::undoStack()
     687                 :            : {
     688                 :          0 :   return mUndoStack.get();
     689                 :            : }
     690                 :            : 
     691                 :          0 : const QgsLayoutUndoStack *QgsLayout::undoStack() const
     692                 :            : {
     693                 :          0 :   return mUndoStack.get();
     694                 :            : }
     695                 :            : 
     696                 :            : ///@cond PRIVATE
     697                 :          0 : class QgsLayoutUndoCommand: public QgsAbstractLayoutUndoCommand
     698                 :            : {
     699                 :            :   public:
     700                 :            : 
     701                 :          0 :     QgsLayoutUndoCommand( QgsLayout *layout, const QString &text, int id, QUndoCommand *parent SIP_TRANSFERTHIS = nullptr )
     702                 :          0 :       : QgsAbstractLayoutUndoCommand( text, id, parent )
     703                 :          0 :       , mLayout( layout )
     704                 :          0 :     {}
     705                 :            : 
     706                 :            :   protected:
     707                 :            : 
     708                 :          0 :     void saveState( QDomDocument &stateDoc ) const override
     709                 :            :     {
     710                 :          0 :       stateDoc.clear();
     711                 :          0 :       QDomElement documentElement = stateDoc.createElement( QStringLiteral( "UndoState" ) );
     712                 :          0 :       mLayout->writeXmlLayoutSettings( documentElement, stateDoc, QgsReadWriteContext() );
     713                 :          0 :       stateDoc.appendChild( documentElement );
     714                 :          0 :     }
     715                 :            : 
     716                 :          0 :     void restoreState( QDomDocument &stateDoc ) override
     717                 :            :     {
     718                 :          0 :       if ( !mLayout )
     719                 :            :       {
     720                 :          0 :         return;
     721                 :            :       }
     722                 :            : 
     723                 :          0 :       mLayout->readXmlLayoutSettings( stateDoc.documentElement(), stateDoc, QgsReadWriteContext() );
     724                 :          0 :       mLayout->project()->setDirty( true );
     725                 :          0 :     }
     726                 :            : 
     727                 :            :   private:
     728                 :            : 
     729                 :            :     QgsLayout *mLayout = nullptr;
     730                 :            : };
     731                 :            : ///@endcond
     732                 :            : 
     733                 :          0 : QgsAbstractLayoutUndoCommand *QgsLayout::createCommand( const QString &text, int id, QUndoCommand *parent )
     734                 :            : {
     735                 :          0 :   return new QgsLayoutUndoCommand( this, text, id, parent );
     736                 :          0 : }
     737                 :            : 
     738                 :          0 : QgsLayoutItemGroup *QgsLayout::groupItems( const QList<QgsLayoutItem *> &items )
     739                 :            : {
     740                 :          0 :   if ( items.size() < 2 )
     741                 :            :   {
     742                 :            :     //not enough items for a group
     743                 :          0 :     return nullptr;
     744                 :            :   }
     745                 :            : 
     746                 :          0 :   mUndoStack->beginMacro( tr( "Group Items" ) );
     747                 :          0 :   std::unique_ptr< QgsLayoutItemGroup > itemGroup( new QgsLayoutItemGroup( this ) );
     748                 :          0 :   for ( QgsLayoutItem *item : items )
     749                 :            :   {
     750                 :          0 :     itemGroup->addItem( item );
     751                 :            :   }
     752                 :          0 :   QgsLayoutItemGroup *returnGroup = itemGroup.get();
     753                 :          0 :   addLayoutItem( itemGroup.release() );
     754                 :            : 
     755                 :          0 :   std::unique_ptr< QgsLayoutItemGroupUndoCommand > c( new QgsLayoutItemGroupUndoCommand( QgsLayoutItemGroupUndoCommand::Grouped, returnGroup, this, tr( "Group Items" ) ) );
     756                 :          0 :   mUndoStack->push( c.release() );
     757                 :          0 :   mProject->setDirty( true );
     758                 :            : 
     759                 :          0 :   mUndoStack->endMacro();
     760                 :            : 
     761                 :          0 :   return returnGroup;
     762                 :          0 : }
     763                 :            : 
     764                 :          0 : QList<QgsLayoutItem *> QgsLayout::ungroupItems( QgsLayoutItemGroup *group )
     765                 :            : {
     766                 :          0 :   QList<QgsLayoutItem *> ungroupedItems;
     767                 :          0 :   if ( !group )
     768                 :            :   {
     769                 :          0 :     return ungroupedItems;
     770                 :            :   }
     771                 :            : 
     772                 :          0 :   mUndoStack->beginMacro( tr( "Ungroup Items" ) );
     773                 :            :   // Call this before removing group items so it can keep note
     774                 :            :   // of contents
     775                 :          0 :   std::unique_ptr< QgsLayoutItemGroupUndoCommand > c( new QgsLayoutItemGroupUndoCommand( QgsLayoutItemGroupUndoCommand::Ungrouped, group, this, tr( "Ungroup Items" ) ) );
     776                 :          0 :   mUndoStack->push( c.release() );
     777                 :            : 
     778                 :          0 :   mProject->setDirty( true );
     779                 :            : 
     780                 :          0 :   ungroupedItems = group->items();
     781                 :          0 :   group->removeItems();
     782                 :            : 
     783                 :          0 :   removeLayoutItem( group );
     784                 :          0 :   mUndoStack->endMacro();
     785                 :            : 
     786                 :          0 :   return ungroupedItems;
     787                 :          0 : }
     788                 :            : 
     789                 :          0 : bool QgsLayout::accept( QgsStyleEntityVisitorInterface *visitor ) const
     790                 :            : {
     791                 :          0 :   const QList< QGraphicsItem * > constItems = items();
     792                 :          0 :   for ( const QGraphicsItem *item : constItems )
     793                 :            :   {
     794                 :          0 :     const QgsLayoutItem *layoutItem = dynamic_cast<const QgsLayoutItem *>( item );
     795                 :          0 :     if ( !layoutItem )
     796                 :          0 :       continue;
     797                 :            : 
     798                 :          0 :     if ( !layoutItem->accept( visitor ) )
     799                 :          0 :       return false;
     800                 :            :   }
     801                 :          0 :   return true;
     802                 :          0 : }
     803                 :            : 
     804                 :          0 : void QgsLayout::refresh()
     805                 :            : {
     806                 :          0 :   mUndoStack->blockCommands( true );
     807                 :          0 :   mPageCollection->beginPageSizeChange();
     808                 :          0 :   emit refreshed();
     809                 :          0 :   mPageCollection->reflow();
     810                 :          0 :   mPageCollection->endPageSizeChange();
     811                 :          0 :   mUndoStack->blockCommands( false );
     812                 :          0 :   update();
     813                 :          0 : }
     814                 :            : 
     815                 :          0 : void QgsLayout::writeXmlLayoutSettings( QDomElement &element, QDomDocument &document, const QgsReadWriteContext & ) const
     816                 :            : {
     817                 :          0 :   mCustomProperties.writeXml( element, document );
     818                 :          0 :   element.setAttribute( QStringLiteral( "units" ), QgsUnitTypes::encodeUnit( mUnits ) );
     819                 :          0 :   element.setAttribute( QStringLiteral( "worldFileMap" ), mWorldFileMapId );
     820                 :          0 :   element.setAttribute( QStringLiteral( "printResolution" ), mRenderContext->dpi() );
     821                 :          0 : }
     822                 :            : 
     823                 :          0 : QDomElement QgsLayout::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
     824                 :            : {
     825                 :          0 :   QDomElement element = document.createElement( QStringLiteral( "Layout" ) );
     826                 :          0 :   auto save = [&]( const QgsLayoutSerializableObject * object )->bool
     827                 :            :   {
     828                 :          0 :     return object->writeXml( element, document, context );
     829                 :            :   };
     830                 :          0 :   save( &mSnapper );
     831                 :          0 :   save( &mGridSettings );
     832                 :          0 :   save( mPageCollection.get() );
     833                 :            : 
     834                 :            :   //save items except paper items and frame items (they are saved with the corresponding multiframe)
     835                 :          0 :   const QList<QGraphicsItem *> itemList = items();
     836                 :          0 :   for ( const QGraphicsItem *graphicsItem : itemList )
     837                 :            :   {
     838                 :          0 :     if ( const QgsLayoutItem *item = dynamic_cast< const QgsLayoutItem *>( graphicsItem ) )
     839                 :            :     {
     840                 :          0 :       if ( item->type() == QgsLayoutItemRegistry::LayoutPage )
     841                 :          0 :         continue;
     842                 :            : 
     843                 :          0 :       item->writeXml( element, document, context );
     844                 :          0 :     }
     845                 :            :   }
     846                 :            : 
     847                 :            :   //save multiframes
     848                 :          0 :   for ( QgsLayoutMultiFrame *mf : mMultiFrames )
     849                 :            :   {
     850                 :          0 :     if ( mf->frameCount() > 0 )
     851                 :          0 :       mf->writeXml( element, document, context );
     852                 :            :   }
     853                 :            : 
     854                 :          0 :   writeXmlLayoutSettings( element, document, context );
     855                 :          0 :   return element;
     856                 :          0 : }
     857                 :            : 
     858                 :          0 : bool QgsLayout::readXmlLayoutSettings( const QDomElement &layoutElement, const QDomDocument &, const QgsReadWriteContext & )
     859                 :            : {
     860                 :          0 :   mCustomProperties.readXml( layoutElement );
     861                 :          0 :   setUnits( QgsUnitTypes::decodeLayoutUnit( layoutElement.attribute( QStringLiteral( "units" ) ) ) );
     862                 :          0 :   mWorldFileMapId = layoutElement.attribute( QStringLiteral( "worldFileMap" ) );
     863                 :          0 :   mRenderContext->setDpi( layoutElement.attribute( QStringLiteral( "printResolution" ), QStringLiteral( "300" ) ).toDouble() );
     864                 :          0 :   emit changed();
     865                 :            : 
     866                 :          0 :   return true;
     867                 :          0 : }
     868                 :            : 
     869                 :          0 : void QgsLayout::addLayoutItemPrivate( QgsLayoutItem *item )
     870                 :            : {
     871                 :          0 :   addItem( item );
     872                 :          0 :   updateBounds();
     873                 :          0 :   mItemsModel->rebuildZList();
     874                 :          0 :   connect( item, &QgsLayoutItem::backgroundTaskCountChanged, this, &QgsLayout::itemBackgroundTaskCountChanged );
     875                 :          0 : }
     876                 :            : 
     877                 :          0 : void QgsLayout::removeLayoutItemPrivate( QgsLayoutItem *item )
     878                 :            : {
     879                 :          0 :   mItemsModel->setItemRemoved( item );
     880                 :            :   // small chance that item is still in a scene - the model may have
     881                 :            :   // rejected the removal for some reason. This is probably not necessary,
     882                 :            :   // but can't hurt...
     883                 :          0 :   if ( item->scene() )
     884                 :          0 :     removeItem( item );
     885                 :            : #if 0 //TODO
     886                 :            :   emit itemRemoved( item );
     887                 :            : #endif
     888                 :          0 :   item->cleanup();
     889                 :          0 :   item->deleteLater();
     890                 :          0 : }
     891                 :            : 
     892                 :          0 : void QgsLayout::deleteAndRemoveMultiFrames()
     893                 :            : {
     894                 :          0 :   qDeleteAll( mMultiFrames );
     895                 :          0 :   mMultiFrames.clear();
     896                 :          0 : }
     897                 :            : 
     898                 :          0 : QPointF QgsLayout::minPointFromXml( const QDomElement &elem ) const
     899                 :            : {
     900                 :          0 :   double minX = std::numeric_limits<double>::max();
     901                 :          0 :   double minY = std::numeric_limits<double>::max();
     902                 :          0 :   const QDomNodeList itemList = elem.elementsByTagName( QStringLiteral( "LayoutItem" ) );
     903                 :          0 :   bool found = false;
     904                 :          0 :   for ( int i = 0; i < itemList.size(); ++i )
     905                 :            :   {
     906                 :          0 :     const QDomElement currentItemElem = itemList.at( i ).toElement();
     907                 :            : 
     908                 :          0 :     QgsLayoutPoint pos = QgsLayoutPoint::decodePoint( currentItemElem.attribute( QStringLiteral( "position" ) ) );
     909                 :          0 :     QPointF layoutPoint = convertToLayoutUnits( pos );
     910                 :            : 
     911                 :          0 :     minX = std::min( minX, layoutPoint.x() );
     912                 :          0 :     minY = std::min( minY, layoutPoint.y() );
     913                 :          0 :     found = true;
     914                 :          0 :   }
     915                 :          0 :   return found ? QPointF( minX, minY ) : QPointF( 0, 0 );
     916                 :          0 : }
     917                 :            : 
     918                 :          0 : void QgsLayout::updateZValues( const bool addUndoCommands )
     919                 :            : {
     920                 :          0 :   int counter = mItemsModel->zOrderListSize();
     921                 :          0 :   const QList<QgsLayoutItem *> zOrderList = mItemsModel->zOrderList();
     922                 :            : 
     923                 :          0 :   if ( addUndoCommands )
     924                 :            :   {
     925                 :          0 :     mUndoStack->beginMacro( tr( "Change Item Stacking" ) );
     926                 :          0 :   }
     927                 :          0 :   for ( QgsLayoutItem *currentItem : zOrderList )
     928                 :            :   {
     929                 :          0 :     if ( currentItem )
     930                 :            :     {
     931                 :          0 :       if ( addUndoCommands )
     932                 :            :       {
     933                 :          0 :         mUndoStack->beginCommand( currentItem, QString() );
     934                 :          0 :       }
     935                 :          0 :       currentItem->setZValue( counter );
     936                 :          0 :       if ( addUndoCommands )
     937                 :            :       {
     938                 :          0 :         mUndoStack->endCommand();
     939                 :          0 :       }
     940                 :          0 :     }
     941                 :          0 :     --counter;
     942                 :            :   }
     943                 :          0 :   if ( addUndoCommands )
     944                 :            :   {
     945                 :          0 :     mUndoStack->endMacro();
     946                 :          0 :   }
     947                 :          0 : }
     948                 :            : 
     949                 :          0 : bool QgsLayout::readXml( const QDomElement &layoutElement, const QDomDocument &document, const QgsReadWriteContext &context )
     950                 :            : {
     951                 :          0 :   if ( layoutElement.nodeName() != QLatin1String( "Layout" ) )
     952                 :            :   {
     953                 :          0 :     return false;
     954                 :            :   }
     955                 :            : 
     956                 :          0 :   auto restore = [&]( QgsLayoutSerializableObject * object )->bool
     957                 :            :   {
     958                 :          0 :     return object->readXml( layoutElement, document, context );
     959                 :            :   };
     960                 :            : 
     961                 :          0 :   std::unique_ptr< QgsScopedRuntimeProfile > profile;
     962                 :          0 :   if ( QgsApplication::profiler()->groupIsActive( QStringLiteral( "projectload" ) ) )
     963                 :          0 :     profile = std::make_unique< QgsScopedRuntimeProfile >( tr( "Read layout settings" ), QStringLiteral( "projectload" ) );
     964                 :            : 
     965                 :          0 :   blockSignals( true ); // defer changed signal to end
     966                 :          0 :   readXmlLayoutSettings( layoutElement, document, context );
     967                 :          0 :   blockSignals( false );
     968                 :            : 
     969                 :          0 :   if ( profile )
     970                 :          0 :     profile->switchTask( tr( "Load pages" ) );
     971                 :          0 :   restore( mPageCollection.get() );
     972                 :          0 :   if ( profile )
     973                 :          0 :     profile->switchTask( tr( "Load snapping settings" ) );
     974                 :          0 :   restore( &mSnapper );
     975                 :          0 :   if ( profile )
     976                 :          0 :     profile->switchTask( tr( "Load grid settings" ) );
     977                 :          0 :   restore( &mGridSettings );
     978                 :            : 
     979                 :          0 :   if ( profile )
     980                 :          0 :     profile->switchTask( tr( "Restore items" ) );
     981                 :          0 :   addItemsFromXml( layoutElement, document, context );
     982                 :            : 
     983                 :          0 :   emit changed();
     984                 :            : 
     985                 :          0 :   return true;
     986                 :          0 : }
     987                 :            : 
     988                 :          0 : QList< QgsLayoutItem * > QgsLayout::addItemsFromXml( const QDomElement &parentElement, const QDomDocument &document, const QgsReadWriteContext &context, QPointF *position, bool pasteInPlace )
     989                 :            : {
     990                 :          0 :   QList< QgsLayoutItem * > newItems;
     991                 :          0 :   QList< QgsLayoutMultiFrame * > newMultiFrames;
     992                 :            : 
     993                 :            :   //if we are adding items to a layout which already contains items, we need to make sure
     994                 :            :   //these items are placed at the top of the layout and that zValues are not duplicated
     995                 :            :   //so, calculate an offset which needs to be added to the zValue of created items
     996                 :          0 :   int zOrderOffset = mItemsModel->zOrderListSize();
     997                 :            : 
     998                 :          0 :   QPointF pasteShiftPos;
     999                 :          0 :   int pageNumber = -1;
    1000                 :          0 :   if ( position )
    1001                 :            :   {
    1002                 :            :     //If we are placing items relative to a certain point, then calculate how much we need
    1003                 :            :     //to shift the items by so that they are placed at this point
    1004                 :            :     //First, calculate the minimum position from the xml
    1005                 :          0 :     QPointF minItemPos = minPointFromXml( parentElement );
    1006                 :            :     //next, calculate how much each item needs to be shifted from its original position
    1007                 :            :     //so that it's placed at the correct relative position
    1008                 :          0 :     pasteShiftPos = *position - minItemPos;
    1009                 :          0 :     if ( pasteInPlace )
    1010                 :            :     {
    1011                 :          0 :       pageNumber = mPageCollection->pageNumberForPoint( *position );
    1012                 :          0 :     }
    1013                 :          0 :   }
    1014                 :            : 
    1015                 :          0 :   std::unique_ptr< QgsScopedRuntimeProfile > profile;
    1016                 :          0 :   if ( QgsApplication::profiler()->groupIsActive( QStringLiteral( "projectload" ) ) )
    1017                 :          0 :     profile = std::make_unique< QgsScopedRuntimeProfile >( tr( "Read items" ), QStringLiteral( "projectload" ) );
    1018                 :            : 
    1019                 :            :   // multiframes
    1020                 :            : 
    1021                 :            :   //TODO - fix this. pasting multiframe frame items has no effect
    1022                 :          0 :   const QDomNodeList multiFrameList = parentElement.elementsByTagName( QStringLiteral( "LayoutMultiFrame" ) );
    1023                 :          0 :   for ( int i = 0; i < multiFrameList.size(); ++i )
    1024                 :            :   {
    1025                 :          0 :     const QDomElement multiFrameElem = multiFrameList.at( i ).toElement();
    1026                 :          0 :     const int itemType = multiFrameElem.attribute( QStringLiteral( "type" ) ).toInt();
    1027                 :            : 
    1028                 :          0 :     if ( profile )
    1029                 :            :     {
    1030                 :          0 :       if ( QgsLayoutMultiFrameAbstractMetadata *metadata = QgsApplication::layoutItemRegistry()->multiFrameMetadata( itemType ) )
    1031                 :            :       {
    1032                 :          0 :         profile->switchTask( tr( "Load %1" ).arg( metadata->visibleName() ) );
    1033                 :          0 :       }
    1034                 :          0 :     }
    1035                 :            : 
    1036                 :          0 :     std::unique_ptr< QgsLayoutMultiFrame > mf( QgsApplication::layoutItemRegistry()->createMultiFrame( itemType, this ) );
    1037                 :          0 :     if ( !mf )
    1038                 :            :     {
    1039                 :            :       // e.g. plugin based item which is no longer available
    1040                 :          0 :       continue;
    1041                 :            :     }
    1042                 :          0 :     mf->readXml( multiFrameElem, document, context );
    1043                 :            : 
    1044                 :            : #if 0 //TODO?
    1045                 :            :     mf->setCreateUndoCommands( true );
    1046                 :            : #endif
    1047                 :            : 
    1048                 :          0 :     QgsLayoutMultiFrame *m = mf.get();
    1049                 :          0 :     this->addMultiFrame( mf.release() );
    1050                 :            : 
    1051                 :            :     //offset z values for frames
    1052                 :            :     //TODO - fix this after fixing multiframe item paste
    1053                 :            :     /*for ( int frameIdx = 0; frameIdx < mf->frameCount(); ++frameIdx )
    1054                 :            :     {
    1055                 :            :       QgsLayoutItemFrame * frame = mf->frame( frameIdx );
    1056                 :            :       frame->setZValue( frame->zValue() + zOrderOffset );
    1057                 :            : 
    1058                 :            :       // also need to shift frames according to position/pasteInPlacePt
    1059                 :            :     }*/
    1060                 :          0 :     newMultiFrames << m;
    1061                 :          0 :   }
    1062                 :            : 
    1063                 :          0 :   const QDomNodeList layoutItemList = parentElement.childNodes();
    1064                 :          0 :   for ( int i = 0; i < layoutItemList.size(); ++i )
    1065                 :            :   {
    1066                 :          0 :     const QDomElement currentItemElem = layoutItemList.at( i ).toElement();
    1067                 :          0 :     if ( currentItemElem.nodeName() != QLatin1String( "LayoutItem" ) )
    1068                 :          0 :       continue;
    1069                 :            : 
    1070                 :          0 :     const int itemType = currentItemElem.attribute( QStringLiteral( "type" ) ).toInt();
    1071                 :            : 
    1072                 :          0 :     if ( profile )
    1073                 :            :     {
    1074                 :          0 :       if ( QgsLayoutItemAbstractMetadata *metadata = QgsApplication::layoutItemRegistry()->itemMetadata( itemType ) )
    1075                 :            :       {
    1076                 :          0 :         profile->switchTask( tr( "Load %1" ).arg( metadata->visibleName() ) );
    1077                 :          0 :       }
    1078                 :          0 :     }
    1079                 :            : 
    1080                 :          0 :     std::unique_ptr< QgsLayoutItem > item( QgsApplication::layoutItemRegistry()->createItem( itemType, this ) );
    1081                 :          0 :     if ( !item )
    1082                 :            :     {
    1083                 :            :       // e.g. plugin based item which is no longer available
    1084                 :          0 :       continue;
    1085                 :            :     }
    1086                 :            : 
    1087                 :          0 :     item->readXml( currentItemElem, document, context );
    1088                 :          0 :     if ( position )
    1089                 :            :     {
    1090                 :          0 :       if ( pasteInPlace )
    1091                 :            :       {
    1092                 :          0 :         QgsLayoutPoint posOnPage = QgsLayoutPoint::decodePoint( currentItemElem.attribute( QStringLiteral( "positionOnPage" ) ) );
    1093                 :          0 :         item->attemptMove( posOnPage, true, false, pageNumber );
    1094                 :          0 :       }
    1095                 :            :       else
    1096                 :            :       {
    1097                 :          0 :         item->attemptMoveBy( pasteShiftPos.x(), pasteShiftPos.y() );
    1098                 :            :       }
    1099                 :          0 :     }
    1100                 :            : 
    1101                 :          0 :     QgsLayoutItem *layoutItem = item.get();
    1102                 :          0 :     addLayoutItem( item.release() );
    1103                 :          0 :     layoutItem->setZValue( layoutItem->zValue() + zOrderOffset );
    1104                 :          0 :     newItems << layoutItem;
    1105                 :          0 :   }
    1106                 :            : 
    1107                 :            :   // we now allow items to "post-process", e.g. if they need to setup connections
    1108                 :            :   // to other items in the layout, which may not have existed at the time the
    1109                 :            :   // item's state was restored. E.g. a scalebar may have been restored before the map
    1110                 :            :   // it is linked to
    1111                 :          0 :   std::unique_ptr< QgsScopedRuntimeProfile > itemProfile;
    1112                 :          0 :   if ( profile )
    1113                 :            :   {
    1114                 :          0 :     profile->switchTask( tr( "Finalize restore" ) );
    1115                 :          0 :   }
    1116                 :          0 :   for ( QgsLayoutItem *item : std::as_const( newItems ) )
    1117                 :            :   {
    1118                 :          0 :     if ( profile )
    1119                 :          0 :       itemProfile = std::make_unique< QgsScopedRuntimeProfile >( item->displayName(), QStringLiteral( "projectload" ) );
    1120                 :          0 :     item->finalizeRestoreFromXml();
    1121                 :          0 :     if ( itemProfile )
    1122                 :          0 :       itemProfile.reset();
    1123                 :            :   }
    1124                 :          0 :   for ( QgsLayoutMultiFrame *mf : std::as_const( newMultiFrames ) )
    1125                 :            :   {
    1126                 :          0 :     if ( profile )
    1127                 :          0 :       itemProfile = std::make_unique< QgsScopedRuntimeProfile >( mf->displayName(), QStringLiteral( "projectload" ) );
    1128                 :          0 :     mf->finalizeRestoreFromXml();
    1129                 :          0 :     if ( itemProfile )
    1130                 :          0 :       itemProfile.reset();
    1131                 :            :   }
    1132                 :            : 
    1133                 :          0 :   for ( QgsLayoutItem *item : std::as_const( newItems ) )
    1134                 :            :   {
    1135                 :          0 :     item->mTemplateUuid.clear();
    1136                 :            :   }
    1137                 :          0 :   for ( QgsLayoutMultiFrame *mf : std::as_const( newMultiFrames ) )
    1138                 :            :   {
    1139                 :          0 :     mf->mTemplateUuid.clear();
    1140                 :            :   }
    1141                 :            : 
    1142                 :            :   //Since this function adds items in an order which isn't the z-order, and each item is added to end of
    1143                 :            :   //z order list in turn, it will now be inconsistent with the actual order of items in the scene.
    1144                 :            :   //Make sure z order list matches the actual order of items in the scene.
    1145                 :            : 
    1146                 :          0 :   if ( profile )
    1147                 :          0 :     profile->switchTask( tr( "Update model" ) );
    1148                 :          0 :   mItemsModel->rebuildZList();
    1149                 :            : 
    1150                 :          0 :   return newItems;
    1151                 :          0 : }
    1152                 :            : 
    1153                 :          0 : void QgsLayout::updateBounds()
    1154                 :            : {
    1155                 :          0 :   setSceneRect( layoutBounds( false, 0.05 ) );
    1156                 :          0 : }
    1157                 :            : 
    1158                 :          0 : void QgsLayout::itemBackgroundTaskCountChanged( int count )
    1159                 :            : {
    1160                 :          0 :   QgsLayoutItem *item = qobject_cast<QgsLayoutItem *>( sender() );
    1161                 :          0 :   if ( !item )
    1162                 :          0 :     return;
    1163                 :            : 
    1164                 :          0 :   if ( count > 0 )
    1165                 :          0 :     mBackgroundTaskCount.insert( item, count );
    1166                 :            :   else
    1167                 :          0 :     mBackgroundTaskCount.remove( item );
    1168                 :            : 
    1169                 :            :   // sum up new count of background tasks
    1170                 :          0 :   int total = 0;
    1171                 :          0 :   for ( auto it = mBackgroundTaskCount.constBegin(); it != mBackgroundTaskCount.constEnd(); ++it )
    1172                 :            :   {
    1173                 :          0 :     total += it.value();
    1174                 :          0 :   }
    1175                 :            : 
    1176                 :          0 :   emit backgroundTaskCountChanged( total );
    1177                 :          0 : }

Generated by: LCOV version 1.14