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 : }
|