Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgslayoutpagecollection.cpp
3 : : ----------------------------
4 : : begin : July 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 "qgslayoutpagecollection.h"
18 : : #include "qgslayout.h"
19 : : #include "qgsreadwritecontext.h"
20 : : #include "qgsproject.h"
21 : : #include "qgslayoutitemundocommand.h"
22 : : #include "qgssymbollayerutils.h"
23 : : #include "qgslayoutframe.h"
24 : : #include "qgslayoutundostack.h"
25 : :
26 : 0 : QgsLayoutPageCollection::QgsLayoutPageCollection( QgsLayout *layout )
27 : 0 : : QObject( layout )
28 : 0 : , mLayout( layout )
29 : 0 : , mGuideCollection( new QgsLayoutGuideCollection( layout, this ) )
30 : 0 : {
31 : 0 : createDefaultPageStyleSymbol();
32 : 0 : }
33 : :
34 : 0 : QgsLayoutPageCollection::~QgsLayoutPageCollection()
35 : 0 : {
36 : 0 : const auto constMPages = mPages;
37 : 0 : for ( QgsLayoutItemPage *page : constMPages )
38 : : {
39 : 0 : mLayout->removeItem( page );
40 : 0 : page->deleteLater();
41 : : }
42 : 0 : }
43 : :
44 : 0 : void QgsLayoutPageCollection::setPageStyleSymbol( QgsFillSymbol *symbol )
45 : : {
46 : 0 : if ( !symbol )
47 : 0 : return;
48 : :
49 : 0 : mPageStyleSymbol.reset( static_cast<QgsFillSymbol *>( symbol->clone() ) );
50 : :
51 : 0 : for ( QgsLayoutItemPage *page : std::as_const( mPages ) )
52 : : {
53 : 0 : page->setPageStyleSymbol( symbol->clone() );
54 : 0 : page->update();
55 : : }
56 : 0 : }
57 : :
58 : 0 : const QgsFillSymbol *QgsLayoutPageCollection::pageStyleSymbol() const
59 : : {
60 : 0 : return mPageStyleSymbol.get();
61 : : }
62 : :
63 : 0 : void QgsLayoutPageCollection::beginPageSizeChange()
64 : : {
65 : 0 : mPreviousItemPositions.clear();
66 : 0 : QList< QgsLayoutItem * > items;
67 : 0 : mLayout->layoutItems( items );
68 : :
69 : 0 : for ( QgsLayoutItem *item : std::as_const( items ) )
70 : : {
71 : 0 : if ( item->type() == QgsLayoutItemRegistry::LayoutPage )
72 : 0 : continue;
73 : :
74 : 0 : mPreviousItemPositions.insert( item->uuid(), qMakePair( item->page(), item->pagePositionWithUnits() ) );
75 : : }
76 : 0 : }
77 : :
78 : 0 : void QgsLayoutPageCollection::endPageSizeChange()
79 : : {
80 : 0 : for ( auto it = mPreviousItemPositions.constBegin(); it != mPreviousItemPositions.constEnd(); ++it )
81 : : {
82 : 0 : if ( QgsLayoutItem *item = mLayout->itemByUuid( it.key() ) )
83 : : {
84 : 0 : if ( !mBlockUndoCommands )
85 : 0 : item->beginCommand( QString() );
86 : 0 : item->attemptMove( it.value().second, true, false, it.value().first );
87 : 0 : if ( !mBlockUndoCommands )
88 : 0 : item->endCommand();
89 : 0 : }
90 : 0 : }
91 : 0 : mPreviousItemPositions.clear();
92 : 0 : }
93 : :
94 : 0 : void QgsLayoutPageCollection::reflow()
95 : : {
96 : 0 : double currentY = 0;
97 : 0 : QgsLayoutPoint p( 0, 0, mLayout->units() );
98 : 0 : const auto constMPages = mPages;
99 : 0 : for ( QgsLayoutItemPage *page : constMPages )
100 : : {
101 : 0 : page->attemptMove( p );
102 : 0 : currentY += mLayout->convertToLayoutUnits( page->pageSize() ).height() + spaceBetweenPages();
103 : 0 : p.setY( currentY );
104 : : }
105 : 0 : mLayout->guides().update();
106 : 0 : mLayout->updateBounds();
107 : 0 : emit changed();
108 : 0 : }
109 : :
110 : 0 : double QgsLayoutPageCollection::maximumPageWidth() const
111 : : {
112 : 0 : double maxWidth = 0;
113 : 0 : for ( QgsLayoutItemPage *page : mPages )
114 : : {
115 : 0 : maxWidth = std::max( maxWidth, mLayout->convertToLayoutUnits( page->pageSize() ).width() );
116 : : }
117 : 0 : return maxWidth;
118 : : }
119 : :
120 : 0 : QSizeF QgsLayoutPageCollection::maximumPageSize() const
121 : : {
122 : 0 : double maxArea = 0;
123 : 0 : QSizeF maxSize;
124 : 0 : for ( QgsLayoutItemPage *page : mPages )
125 : : {
126 : 0 : QSizeF pageSize = mLayout->convertToLayoutUnits( page->pageSize() );
127 : 0 : double area = pageSize.width() * pageSize.height();
128 : 0 : if ( area > maxArea )
129 : : {
130 : 0 : maxArea = area;
131 : 0 : maxSize = pageSize;
132 : 0 : }
133 : : }
134 : 0 : return maxSize;
135 : : }
136 : :
137 : 0 : bool QgsLayoutPageCollection::hasUniformPageSizes() const
138 : : {
139 : 0 : QSizeF size;
140 : 0 : for ( QgsLayoutItemPage *page : mPages )
141 : : {
142 : 0 : QSizeF pageSize = mLayout->convertToLayoutUnits( page->pageSize() );
143 : 0 : if ( !size.isValid() )
144 : 0 : size = pageSize;
145 : : else
146 : : {
147 : 0 : if ( !qgsDoubleNear( pageSize.width(), size.width(), 0.01 )
148 : 0 : || !qgsDoubleNear( pageSize.height(), size.height(), 0.01 ) )
149 : 0 : return false;
150 : : }
151 : : }
152 : 0 : return true;
153 : 0 : }
154 : :
155 : 0 : int QgsLayoutPageCollection::pageNumberForPoint( QPointF point ) const
156 : : {
157 : 0 : int pageNumber = 0;
158 : 0 : double startNextPageY = 0;
159 : 0 : const auto constMPages = mPages;
160 : 0 : for ( QgsLayoutItemPage *page : constMPages )
161 : : {
162 : 0 : startNextPageY += page->rect().height() + spaceBetweenPages();
163 : 0 : if ( startNextPageY > point.y() )
164 : 0 : break;
165 : 0 : pageNumber++;
166 : : }
167 : :
168 : 0 : if ( pageNumber > mPages.count() - 1 )
169 : 0 : pageNumber = mPages.count() - 1;
170 : 0 : return pageNumber;
171 : 0 : }
172 : :
173 : 0 : int QgsLayoutPageCollection::predictPageNumberForPoint( QPointF point ) const
174 : : {
175 : 0 : if ( mPages.empty() )
176 : 0 : return 0;
177 : :
178 : 0 : int pageNumber = 0;
179 : 0 : double startNextPageY = 0;
180 : 0 : const auto constMPages = mPages;
181 : 0 : for ( QgsLayoutItemPage *page : constMPages )
182 : : {
183 : 0 : startNextPageY += page->rect().height() + spaceBetweenPages();
184 : 0 : if ( startNextPageY >= point.y() )
185 : 0 : break;
186 : 0 : pageNumber++;
187 : : }
188 : :
189 : 0 : if ( startNextPageY >= point.y() )
190 : : {
191 : : // found an existing page
192 : 0 : return pageNumber;
193 : : }
194 : :
195 : 0 : double lastPageHeight = mPages.last()->rect().height();
196 : 0 : while ( startNextPageY < point.y() )
197 : : {
198 : 0 : startNextPageY += lastPageHeight + spaceBetweenPages();
199 : 0 : if ( startNextPageY >= point.y() )
200 : 0 : break;
201 : 0 : pageNumber++;
202 : : }
203 : :
204 : 0 : return pageNumber;
205 : 0 : }
206 : :
207 : 0 : QgsLayoutItemPage *QgsLayoutPageCollection::pageAtPoint( QPointF point ) const
208 : : {
209 : 0 : const QList< QGraphicsItem * > items = mLayout->items( point );
210 : 0 : for ( QGraphicsItem *item : items )
211 : : {
212 : 0 : if ( item->type() == QgsLayoutItemRegistry::LayoutPage )
213 : : {
214 : 0 : QgsLayoutItemPage *page = static_cast< QgsLayoutItemPage * >( item );
215 : 0 : if ( page->mapToScene( page->rect() ).boundingRect().contains( point ) )
216 : 0 : return page;
217 : 0 : }
218 : : }
219 : 0 : return nullptr;
220 : 0 : }
221 : :
222 : 0 : QPointF QgsLayoutPageCollection::pagePositionToLayoutPosition( int page, const QgsLayoutPoint &position ) const
223 : : {
224 : 0 : QPointF layoutUnitsPos = mLayout->convertToLayoutUnits( position );
225 : 0 : if ( page > 0 && page < mPages.count() )
226 : : {
227 : 0 : layoutUnitsPos.ry() += mPages.at( page )->pos().y();
228 : 0 : }
229 : 0 : return layoutUnitsPos;
230 : : }
231 : :
232 : 0 : QgsLayoutPoint QgsLayoutPageCollection::pagePositionToAbsolute( int page, const QgsLayoutPoint &position ) const
233 : : {
234 : 0 : double vDelta = 0.0;
235 : 0 : if ( page > 0 && page < mPages.count() )
236 : : {
237 : 0 : vDelta = mLayout->convertFromLayoutUnits( mPages.at( page )->pos().y(), position.units() ).length();
238 : 0 : }
239 : :
240 : 0 : return QgsLayoutPoint( position.x(), position.y() + vDelta, position.units() );
241 : : }
242 : :
243 : 0 : QPointF QgsLayoutPageCollection::positionOnPage( QPointF position ) const
244 : : {
245 : 0 : double startCurrentPageY = 0;
246 : 0 : double startNextPageY = 0;
247 : 0 : int pageNumber = 0;
248 : 0 : const auto constMPages = mPages;
249 : 0 : for ( QgsLayoutItemPage *page : constMPages )
250 : : {
251 : 0 : startCurrentPageY = startNextPageY;
252 : 0 : startNextPageY += page->rect().height() + spaceBetweenPages();
253 : 0 : if ( startNextPageY > position.y() )
254 : 0 : break;
255 : 0 : pageNumber++;
256 : : }
257 : :
258 : : double y;
259 : 0 : if ( pageNumber == mPages.size() )
260 : : {
261 : : //y coordinate is greater then the end of the last page, so return distance between
262 : : //top of last page and y coordinate
263 : 0 : y = position.y() - ( startNextPageY - spaceBetweenPages() );
264 : 0 : }
265 : : else
266 : : {
267 : : //y coordinate is less then the end of the last page
268 : 0 : y = position.y() - startCurrentPageY;
269 : : }
270 : 0 : return QPointF( position.x(), y );
271 : 0 : }
272 : :
273 : 0 : double QgsLayoutPageCollection::spaceBetweenPages() const
274 : : {
275 : 0 : return mLayout->convertToLayoutUnits( QgsLayoutMeasurement( 10 ) );
276 : : }
277 : :
278 : 0 : double QgsLayoutPageCollection::pageShadowWidth() const
279 : : {
280 : 0 : return spaceBetweenPages() / 2;
281 : : }
282 : :
283 : 0 : void QgsLayoutPageCollection::resizeToContents( const QgsMargins &margins, QgsUnitTypes::LayoutUnit marginUnits )
284 : : {
285 : : //calculate current bounds
286 : 0 : QRectF bounds = mLayout->layoutBounds( true, 0.0 );
287 : 0 : if ( bounds.isEmpty() )
288 : 0 : return;
289 : :
290 : 0 : if ( !mBlockUndoCommands )
291 : 0 : mLayout->undoStack()->beginCommand( this, tr( "Resize to Contents" ) );
292 : :
293 : 0 : for ( int page = mPages.count() - 1; page > 0; page-- )
294 : : {
295 : 0 : deletePage( page );
296 : 0 : }
297 : :
298 : 0 : if ( mPages.empty() )
299 : : {
300 : 0 : std::unique_ptr< QgsLayoutItemPage > page = std::make_unique< QgsLayoutItemPage >( mLayout );
301 : 0 : addPage( page.release() );
302 : 0 : }
303 : :
304 : 0 : QgsLayoutItemPage *page = mPages.at( 0 );
305 : :
306 : 0 : double marginLeft = mLayout->convertToLayoutUnits( QgsLayoutMeasurement( margins.left(), marginUnits ) );
307 : 0 : double marginTop = mLayout->convertToLayoutUnits( QgsLayoutMeasurement( margins.top(), marginUnits ) );
308 : 0 : double marginBottom = mLayout->convertToLayoutUnits( QgsLayoutMeasurement( margins.bottom(), marginUnits ) );
309 : 0 : double marginRight = mLayout->convertToLayoutUnits( QgsLayoutMeasurement( margins.right(), marginUnits ) );
310 : :
311 : 0 : bounds.setWidth( bounds.width() + marginLeft + marginRight );
312 : 0 : bounds.setHeight( bounds.height() + marginTop + marginBottom );
313 : :
314 : 0 : QgsLayoutSize newPageSize = mLayout->convertFromLayoutUnits( bounds.size(), mLayout->units() );
315 : 0 : page->setPageSize( newPageSize );
316 : :
317 : 0 : reflow();
318 : :
319 : : //also move all items so that top-left of bounds is at marginLeft, marginTop
320 : 0 : double diffX = marginLeft - bounds.left();
321 : 0 : double diffY = marginTop - bounds.top();
322 : :
323 : 0 : const QList<QGraphicsItem *> itemList = mLayout->items();
324 : 0 : for ( QGraphicsItem *item : itemList )
325 : : {
326 : 0 : if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
327 : : {
328 : 0 : QgsLayoutItemPage *pageItem = dynamic_cast<QgsLayoutItemPage *>( layoutItem );
329 : 0 : if ( !pageItem )
330 : : {
331 : 0 : layoutItem->beginCommand( tr( "Move Item" ) );
332 : 0 : layoutItem->attemptMoveBy( diffX, diffY );
333 : 0 : layoutItem->endCommand();
334 : 0 : }
335 : 0 : }
336 : : }
337 : :
338 : : //also move guides
339 : 0 : mLayout->undoStack()->beginCommand( &mLayout->guides(), tr( "Move Guides" ) );
340 : 0 : const QList< QgsLayoutGuide * > verticalGuides = mLayout->guides().guides( Qt::Vertical );
341 : 0 : for ( QgsLayoutGuide *guide : verticalGuides )
342 : : {
343 : 0 : guide->setLayoutPosition( guide->layoutPosition() + diffX );
344 : : }
345 : 0 : const QList< QgsLayoutGuide * > horizontalGuides = mLayout->guides().guides( Qt::Horizontal );
346 : 0 : for ( QgsLayoutGuide *guide : horizontalGuides )
347 : : {
348 : 0 : guide->setLayoutPosition( guide->layoutPosition() + diffY );
349 : : }
350 : 0 : mLayout->undoStack()->endCommand();
351 : :
352 : 0 : if ( !mBlockUndoCommands )
353 : 0 : mLayout->undoStack()->endCommand();
354 : 0 : }
355 : :
356 : 0 : bool QgsLayoutPageCollection::writeXml( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context ) const
357 : : {
358 : 0 : QDomElement element = document.createElement( QStringLiteral( "PageCollection" ) );
359 : :
360 : 0 : QDomElement pageStyleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mPageStyleSymbol.get(), document, context );
361 : 0 : element.appendChild( pageStyleElem );
362 : :
363 : 0 : for ( const QgsLayoutItemPage *page : mPages )
364 : : {
365 : 0 : page->writeXml( element, document, context );
366 : : }
367 : :
368 : 0 : mGuideCollection->writeXml( element, document, context );
369 : :
370 : 0 : parentElement.appendChild( element );
371 : : return true;
372 : 0 : }
373 : :
374 : 0 : bool QgsLayoutPageCollection::readXml( const QDomElement &e, const QDomDocument &document, const QgsReadWriteContext &context )
375 : : {
376 : 0 : QDomElement element = e;
377 : 0 : if ( element.nodeName() != QLatin1String( "PageCollection" ) )
378 : : {
379 : 0 : element = element.firstChildElement( QStringLiteral( "PageCollection" ) );
380 : 0 : }
381 : :
382 : 0 : if ( element.nodeName() != QLatin1String( "PageCollection" ) )
383 : : {
384 : 0 : return false;
385 : : }
386 : :
387 : 0 : mBlockUndoCommands = true;
388 : :
389 : 0 : int i = 0;
390 : 0 : for ( QgsLayoutItemPage *page : std::as_const( mPages ) )
391 : : {
392 : 0 : emit pageAboutToBeRemoved( i );
393 : 0 : mLayout->removeItem( page );
394 : 0 : page->deleteLater();
395 : 0 : ++i;
396 : : }
397 : 0 : mPages.clear();
398 : :
399 : 0 : QDomElement pageStyleSymbolElem = element.firstChildElement( QStringLiteral( "symbol" ) );
400 : 0 : if ( !pageStyleSymbolElem.isNull() )
401 : : {
402 : 0 : mPageStyleSymbol.reset( QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( pageStyleSymbolElem, context ) );
403 : 0 : }
404 : :
405 : 0 : QDomNodeList pageList = element.elementsByTagName( QStringLiteral( "LayoutItem" ) );
406 : 0 : for ( int i = 0; i < pageList.size(); ++i )
407 : : {
408 : 0 : QDomElement pageElement = pageList.at( i ).toElement();
409 : 0 : std::unique_ptr< QgsLayoutItemPage > page( new QgsLayoutItemPage( mLayout ) );
410 : 0 : if ( mPageStyleSymbol )
411 : 0 : page->setPageStyleSymbol( mPageStyleSymbol->clone() );
412 : 0 : page->readXml( pageElement, document, context );
413 : 0 : page->finalizeRestoreFromXml();
414 : 0 : mPages.append( page.get() );
415 : 0 : mLayout->addItem( page.release() );
416 : 0 : }
417 : :
418 : 0 : reflow();
419 : :
420 : 0 : mGuideCollection->readXml( element, document, context );
421 : :
422 : 0 : mBlockUndoCommands = false;
423 : 0 : return true;
424 : 0 : }
425 : 0 :
426 : 0 : QgsLayoutGuideCollection &QgsLayoutPageCollection::guides()
427 : : {
428 : 0 : return *mGuideCollection;
429 : : }
430 : :
431 : 0 : const QgsLayoutGuideCollection &QgsLayoutPageCollection::guides() const
432 : : {
433 : 0 : return *mGuideCollection;
434 : : }
435 : :
436 : 0 : void QgsLayoutPageCollection::redraw()
437 : : {
438 : 0 : const auto constMPages = mPages;
439 : 0 : for ( QgsLayoutItemPage *page : constMPages )
440 : : {
441 : 0 : page->redraw();
442 : : }
443 : 0 : }
444 : :
445 : 0 : QgsLayout *QgsLayoutPageCollection::layout()
446 : : {
447 : 0 : return mLayout;
448 : : }
449 : :
450 : 0 : QList<QgsLayoutItemPage *> QgsLayoutPageCollection::pages()
451 : : {
452 : 0 : return mPages;
453 : : }
454 : :
455 : 0 : int QgsLayoutPageCollection::pageCount() const
456 : : {
457 : 0 : return mPages.count();
458 : : }
459 : :
460 : 0 : QgsLayoutItemPage *QgsLayoutPageCollection::page( int pageNumber )
461 : : {
462 : 0 : return mPages.value( pageNumber );
463 : : }
464 : :
465 : 0 : const QgsLayoutItemPage *QgsLayoutPageCollection::page( int pageNumber ) const
466 : : {
467 : 0 : return mPages.value( pageNumber );
468 : : }
469 : :
470 : 0 : int QgsLayoutPageCollection::pageNumber( QgsLayoutItemPage *page ) const
471 : : {
472 : 0 : return mPages.indexOf( page );
473 : : }
474 : :
475 : 0 : QList<QgsLayoutItemPage *> QgsLayoutPageCollection::visiblePages( const QRectF ®ion ) const
476 : : {
477 : 0 : QList<QgsLayoutItemPage *> pages;
478 : 0 : const auto constMPages = mPages;
479 : 0 : for ( QgsLayoutItemPage *page : constMPages )
480 : : {
481 : 0 : if ( page->mapToScene( page->rect() ).boundingRect().intersects( region ) )
482 : 0 : pages << page;
483 : : }
484 : 0 : return pages;
485 : 0 : }
486 : :
487 : 0 : QList<int> QgsLayoutPageCollection::visiblePageNumbers( const QRectF ®ion ) const
488 : : {
489 : 0 : QList< int > pages;
490 : 0 : int p = 0;
491 : 0 : const auto constMPages = mPages;
492 : 0 : for ( QgsLayoutItemPage *page : constMPages )
493 : : {
494 : 0 : if ( page->mapToScene( page->rect() ).boundingRect().intersects( region ) )
495 : 0 : pages << p;
496 : 0 : p++;
497 : : }
498 : 0 : return pages;
499 : 0 : }
500 : :
501 : 0 : bool QgsLayoutPageCollection::pageIsEmpty( int page ) const
502 : : {
503 : : //get all items on page
504 : 0 : const QList<QgsLayoutItem *> items = mLayout->pageCollection()->itemsOnPage( page );
505 : :
506 : : //loop through and check for non-paper items
507 : 0 : for ( QgsLayoutItem *item : items )
508 : : {
509 : : //is item a paper item?
510 : 0 : if ( item->type() != QgsLayoutItemRegistry::LayoutPage )
511 : : {
512 : : //item is not a paper item, so we have other items on the page
513 : 0 : return false;
514 : : }
515 : : }
516 : : //no non-paper items
517 : 0 : return true;
518 : 0 : }
519 : :
520 : 0 : QList<QgsLayoutItem *> QgsLayoutPageCollection::itemsOnPage( int page ) const
521 : : {
522 : 0 : QList<QgsLayoutItem *> itemList;
523 : 0 : const QList<QGraphicsItem *> graphicsItemList = mLayout->items();
524 : 0 : itemList.reserve( graphicsItemList.size() );
525 : 0 : for ( QGraphicsItem *graphicsItem : graphicsItemList )
526 : : {
527 : 0 : QgsLayoutItem *item = dynamic_cast<QgsLayoutItem *>( graphicsItem );
528 : 0 : if ( item && item->page() == page )
529 : : {
530 : 0 : itemList.push_back( item );
531 : 0 : }
532 : : }
533 : 0 : return itemList;
534 : 0 : }
535 : :
536 : 0 : bool QgsLayoutPageCollection::shouldExportPage( int page ) const
537 : : {
538 : 0 : if ( page >= mPages.count() || page < 0 )
539 : : {
540 : : //page number out of range, of course we shouldn't export it - stop smoking crack!
541 : 0 : return false;
542 : : }
543 : :
544 : 0 : QgsLayoutItemPage *pageItem = mPages.at( page );
545 : 0 : if ( !pageItem->shouldDrawItem() )
546 : 0 : return false;
547 : :
548 : : //check all frame items on page
549 : 0 : QList<QgsLayoutFrame *> frames;
550 : 0 : itemsOnPage( frames, page );
551 : 0 : for ( QgsLayoutFrame *frame : std::as_const( frames ) )
552 : : {
553 : 0 : if ( frame->hidePageIfEmpty() && frame->isEmpty() )
554 : : {
555 : : //frame is set to hide page if empty, and frame is empty, so we don't want to export this page
556 : 0 : return false;
557 : : }
558 : : }
559 : 0 : return true;
560 : 0 : }
561 : :
562 : 0 : void QgsLayoutPageCollection::addPage( QgsLayoutItemPage *page )
563 : : {
564 : 0 : if ( !mBlockUndoCommands )
565 : 0 : mLayout->undoStack()->beginCommand( this, tr( "Add Page" ) );
566 : 0 : mPages.append( page );
567 : 0 : mLayout->addItem( page );
568 : 0 : reflow();
569 : 0 : if ( !mBlockUndoCommands )
570 : 0 : mLayout->undoStack()->endCommand();
571 : 0 : }
572 : :
573 : 0 : QgsLayoutItemPage *QgsLayoutPageCollection::extendByNewPage()
574 : : {
575 : 0 : if ( mPages.empty() )
576 : 0 : return nullptr;
577 : :
578 : 0 : QgsLayoutItemPage *lastPage = mPages.at( mPages.count() - 1 );
579 : 0 : std::unique_ptr< QgsLayoutItemPage > newPage = std::make_unique< QgsLayoutItemPage >( mLayout );
580 : 0 : newPage->attemptResize( lastPage->sizeWithUnits() );
581 : 0 : addPage( newPage.release() );
582 : 0 : return mPages.at( mPages.count() - 1 );
583 : 0 : }
584 : :
585 : 0 : void QgsLayoutPageCollection::insertPage( QgsLayoutItemPage *page, int beforePage )
586 : : {
587 : 0 : if ( !mBlockUndoCommands )
588 : : {
589 : 0 : mLayout->undoStack()->beginMacro( tr( "Add Page" ) );
590 : 0 : mLayout->undoStack()->beginCommand( this, tr( "Add Page" ) );
591 : 0 : }
592 : :
593 : 0 : if ( beforePage < 0 )
594 : 0 : beforePage = 0;
595 : :
596 : 0 : beginPageSizeChange();
597 : 0 : if ( beforePage >= mPages.count() )
598 : : {
599 : 0 : mPages.append( page );
600 : 0 : }
601 : : else
602 : : {
603 : 0 : mPages.insert( beforePage, page );
604 : : }
605 : 0 : mLayout->addItem( page );
606 : 0 : reflow();
607 : :
608 : : // bump up stored page numbers to account
609 : 0 : for ( auto it = mPreviousItemPositions.begin(); it != mPreviousItemPositions.end(); ++it ) // clazy:exclude=detaching-member
610 : : {
611 : 0 : if ( it.value().first < beforePage )
612 : 0 : continue;
613 : :
614 : 0 : it.value().first = it.value().first + 1;
615 : 0 : }
616 : :
617 : 0 : endPageSizeChange();
618 : 0 : if ( ! mBlockUndoCommands )
619 : : {
620 : 0 : mLayout->undoStack()->endCommand();
621 : 0 : mLayout->undoStack()->endMacro();
622 : 0 : }
623 : 0 : }
624 : :
625 : 0 : void QgsLayoutPageCollection::deletePage( int pageNumber )
626 : : {
627 : 0 : if ( pageNumber < 0 || pageNumber >= mPages.count() )
628 : 0 : return;
629 : :
630 : 0 : if ( !mBlockUndoCommands )
631 : : {
632 : 0 : mLayout->undoStack()->beginMacro( tr( "Remove Page" ) );
633 : 0 : mLayout->undoStack()->beginCommand( this, tr( "Remove Page" ) );
634 : 0 : }
635 : 0 : emit pageAboutToBeRemoved( pageNumber );
636 : 0 : beginPageSizeChange();
637 : 0 : QgsLayoutItemPage *page = mPages.takeAt( pageNumber );
638 : 0 : mLayout->removeItem( page );
639 : 0 : page->deleteLater();
640 : 0 : reflow();
641 : :
642 : : // bump stored page numbers to account
643 : 0 : for ( auto it = mPreviousItemPositions.begin(); it != mPreviousItemPositions.end(); ++it ) // clazy:exclude=detaching-member
644 : : {
645 : 0 : if ( it.value().first <= pageNumber )
646 : 0 : continue;
647 : :
648 : 0 : it.value().first = it.value().first - 1;
649 : 0 : }
650 : :
651 : 0 : endPageSizeChange();
652 : 0 : if ( ! mBlockUndoCommands )
653 : : {
654 : 0 : mLayout->undoStack()->endCommand();
655 : 0 : mLayout->undoStack()->endMacro();
656 : 0 : }
657 : 0 : }
658 : :
659 : 0 : void QgsLayoutPageCollection::deletePage( QgsLayoutItemPage *page )
660 : : {
661 : 0 : if ( !mPages.contains( page ) )
662 : 0 : return;
663 : :
664 : 0 : if ( !mBlockUndoCommands )
665 : : {
666 : 0 : mLayout->undoStack()->beginMacro( tr( "Remove Page" ) );
667 : 0 : mLayout->undoStack()->beginCommand( this, tr( "Remove Page" ) );
668 : 0 : }
669 : 0 : int pageIndex = mPages.indexOf( page );
670 : 0 : emit pageAboutToBeRemoved( pageIndex );
671 : 0 : beginPageSizeChange();
672 : 0 : mPages.removeAll( page );
673 : 0 : page->deleteLater();
674 : : // remove immediately from scene -- otherwise immediately calculation of layout bounds (such as is done
675 : : // in reflow) will still consider the page, at least until it's actually deleted at the next event loop
676 : 0 : mLayout->removeItem( page );
677 : 0 : reflow();
678 : :
679 : : // bump stored page numbers to account
680 : 0 : for ( auto it = mPreviousItemPositions.begin(); it != mPreviousItemPositions.end(); ++it ) // clazy:exclude=detaching-member
681 : : {
682 : 0 : if ( it.value().first <= pageIndex )
683 : 0 : continue;
684 : :
685 : 0 : it.value().first = it.value().first - 1;
686 : 0 : }
687 : :
688 : 0 : endPageSizeChange();
689 : 0 : if ( !mBlockUndoCommands )
690 : : {
691 : 0 : mLayout->undoStack()->endCommand();
692 : 0 : mLayout->undoStack()->endMacro();
693 : 0 : }
694 : 0 : }
695 : :
696 : 0 : void QgsLayoutPageCollection::clear()
697 : : {
698 : 0 : if ( !mBlockUndoCommands )
699 : : {
700 : 0 : mLayout->undoStack()->beginMacro( tr( "Remove Pages" ) );
701 : 0 : mLayout->undoStack()->beginCommand( this, tr( "Remove Pages" ) );
702 : 0 : }
703 : 0 : for ( int i = mPages.count() - 1; i >= 0; --i )
704 : : {
705 : 0 : emit pageAboutToBeRemoved( i );
706 : 0 : mPages.takeAt( i )->deleteLater();
707 : 0 : }
708 : 0 : reflow();
709 : 0 : if ( !mBlockUndoCommands )
710 : : {
711 : 0 : mLayout->undoStack()->endCommand();
712 : 0 : mLayout->undoStack()->endMacro();
713 : 0 : }
714 : 0 : }
715 : :
716 : 0 : QgsLayoutItemPage *QgsLayoutPageCollection::takePage( QgsLayoutItemPage *page )
717 : : {
718 : 0 : mPages.removeAll( page );
719 : 0 : return page;
720 : : }
721 : :
722 : 0 : void QgsLayoutPageCollection::createDefaultPageStyleSymbol()
723 : : {
724 : 0 : QVariantMap properties;
725 : 0 : properties.insert( QStringLiteral( "color" ), QStringLiteral( "white" ) );
726 : 0 : properties.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
727 : 0 : properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "no" ) );
728 : 0 : properties.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
729 : 0 : mPageStyleSymbol.reset( QgsFillSymbol::createSimple( properties ) );
730 : 0 : }
731 : :
|