@@ -584,6 +584,26 @@ QDomElement QgsLayout::writeXml( QDomDocument &document, const QgsReadWriteConte
584
584
save ( &mGridSettings );
585
585
save ( mPageCollection .get () );
586
586
587
+ // save items except paper items and frame items (they are saved with the corresponding multiframe)
588
+ const QList<QGraphicsItem *> itemList = items ();
589
+ for ( const QGraphicsItem *graphicsItem : itemList )
590
+ {
591
+ if ( const QgsLayoutItem *item = dynamic_cast < const QgsLayoutItem *>( graphicsItem ) )
592
+ {
593
+ if ( item->type () == QgsLayoutItemRegistry::LayoutPage
594
+ || item->type () == QgsLayoutItemRegistry::LayoutFrame )
595
+ continue ;
596
+
597
+ item->writeXml ( element, document, context );
598
+ }
599
+ }
600
+
601
+ // save multiframes
602
+ for ( QgsLayoutMultiFrame *mf : mMultiFrames )
603
+ {
604
+ mf->writeXml ( element, document, context );
605
+ }
606
+
587
607
writeXmlLayoutSettings ( element, document, context );
588
608
return element;
589
609
}
@@ -623,6 +643,26 @@ void QgsLayout::deleteAndRemoveMultiFrames()
623
643
mMultiFrames .clear ();
624
644
}
625
645
646
+ QPointF QgsLayout::minPointFromXml ( const QDomElement &elem ) const
647
+ {
648
+ double minX = std::numeric_limits<double >::max ();
649
+ double minY = std::numeric_limits<double >::max ();
650
+ const QDomNodeList itemList = elem.elementsByTagName ( QStringLiteral ( " LayoutItem" ) );
651
+ bool found = false ;
652
+ for ( int i = 0 ; i < itemList.size (); ++i )
653
+ {
654
+ const QDomElement currentItemElem = itemList.at ( i ).toElement ();
655
+
656
+ QgsLayoutPoint pos = QgsLayoutPoint::decodePoint ( currentItemElem.attribute ( QStringLiteral ( " position" ) ) );
657
+ QPointF layoutPoint = convertToLayoutUnits ( pos );
658
+
659
+ minX = std::min ( minX, layoutPoint.x () );
660
+ minY = std::min ( minY, layoutPoint.y () );
661
+ found = true ;
662
+ }
663
+ return found ? QPointF ( minX, minY ) : QPointF ( 0 , 0 );
664
+ }
665
+
626
666
void QgsLayout::updateZValues ( const bool addUndoCommands )
627
667
{
628
668
int counter = mItemsModel ->zOrderListSize ();
@@ -671,10 +711,137 @@ bool QgsLayout::readXml( const QDomElement &layoutElement, const QDomDocument &d
671
711
restore ( mPageCollection .get () );
672
712
restore ( &mSnapper );
673
713
restore ( &mGridSettings );
714
+ addItemsFromXml ( layoutElement, document, context );
674
715
675
716
return true ;
676
717
}
677
718
719
+ QVector< QgsLayoutItem * > QgsLayout::addItemsFromXml ( const QDomElement &parentElement, const QDomDocument &document, const QgsReadWriteContext &context, QPointF *position, bool pasteInPlace )
720
+ {
721
+ std::unique_ptr< QPointF > pasteInPlacePt;
722
+ QVector< QgsLayoutItem * > newItems;
723
+ QVector< QgsLayoutMultiFrame * > newMultiFrames;
724
+
725
+ // if we are adding items to a layout which already contains items, we need to make sure
726
+ // these items are placed at the top of the layout and that zValues are not duplicated
727
+ // so, calculate an offset which needs to be added to the zValue of created items
728
+ int zOrderOffset = mItemsModel ->zOrderListSize ();
729
+
730
+ QPointF pasteShiftPos;
731
+ if ( position )
732
+ {
733
+ // If we are placing items relative to a certain point, then calculate how much we need
734
+ // to shift the items by so that they are placed at this point
735
+ // First, calculate the minimum position from the xml
736
+ QPointF minItemPos = minPointFromXml ( parentElement );
737
+ // next, calculate how much each item needs to be shifted from its original position
738
+ // so that it's placed at the correct relative position
739
+ pasteShiftPos = *position - minItemPos;
740
+
741
+ #if 0 // TODO - move to gui
742
+ //since we are pasting items, clear the existing selection
743
+ setAllDeselected();
744
+ #endif
745
+ if ( pasteInPlace )
746
+ {
747
+ int pageNumber = mPageCollection ->pageNumberForPoint ( *position );
748
+ pasteInPlacePt = qgis::make_unique< QPointF >( 0 , mPageCollection ->page ( pageNumber )->pos ().y () );
749
+ }
750
+ }
751
+
752
+ const QDomNodeList layoutItemList = parentElement.elementsByTagName ( QStringLiteral ( " LayoutItem" ) );
753
+ for ( int i = 0 ; i < layoutItemList.size (); ++i )
754
+ {
755
+ const QDomElement currentItemElem = layoutItemList.at ( i ).toElement ();
756
+ const int itemType = currentItemElem.attribute ( QStringLiteral ( " type" ) ).toInt ();
757
+ std::unique_ptr< QgsLayoutItem > item ( QgsApplication::layoutItemRegistry ()->createItem ( itemType, this ) );
758
+ if ( !item )
759
+ {
760
+ // e.g. plugin based item which is no longer available
761
+ continue ;
762
+ }
763
+
764
+ item->readXml ( currentItemElem, document, context );
765
+ if ( position )
766
+ {
767
+ #if 0 //TODO
768
+ if ( pasteInPlacePt )
769
+ {
770
+ item->setItemPosition( newLabel->pos().x(), std::fmod( newLabel->pos().y(), ( paperHeight() + spaceBetweenPages() ) ) );
771
+ item->move( pasteInPlacePt->x(), pasteInPlacePt->y() );
772
+ }
773
+ else
774
+ {
775
+ item->move( pasteShiftPos.x(), pasteShiftPos.y() );
776
+ }
777
+ #endif
778
+ }
779
+
780
+ #if 0 //TODO - move to gui
781
+ newLabel->setSelected( true );
782
+ #endif
783
+
784
+ QgsLayoutItem *layoutItem = item.get ();
785
+ addLayoutItem ( item.release () );
786
+ layoutItem->setZValue ( layoutItem->zValue () + zOrderOffset );
787
+ newItems << layoutItem;
788
+ }
789
+
790
+ // multiframes
791
+
792
+ // TODO - fix this. pasting multiframe frame items has no effect
793
+ const QDomNodeList multiFrameList = parentElement.elementsByTagName ( QStringLiteral ( " LayoutMultiFrame" ) );
794
+ for ( int i = 0 ; i < multiFrameList.size (); ++i )
795
+ {
796
+ const QDomElement multiFrameElem = multiFrameList.at ( i ).toElement ();
797
+ const int itemType = multiFrameElem.attribute ( QStringLiteral ( " type" ) ).toInt ();
798
+ std::unique_ptr< QgsLayoutMultiFrame > mf ( QgsApplication::layoutItemRegistry ()->createMultiFrame ( itemType, this ) );
799
+ if ( !mf )
800
+ {
801
+ // e.g. plugin based item which is no longer available
802
+ continue ;
803
+ }
804
+ mf->readXml ( multiFrameElem, document, context );
805
+
806
+ #if 0 //TODO?
807
+ mf->setCreateUndoCommands( true );
808
+ #endif
809
+
810
+ QgsLayoutMultiFrame *m = mf.get ();
811
+ this ->addMultiFrame ( mf.release () );
812
+
813
+ // offset z values for frames
814
+ // TODO - fix this after fixing multiframe item paste
815
+ /* for ( int frameIdx = 0; frameIdx < mf->frameCount(); ++frameIdx )
816
+ {
817
+ QgsLayoutItemFrame * frame = mf->frame( frameIdx );
818
+ frame->setZValue( frame->zValue() + zOrderOffset );
819
+ }*/
820
+ newMultiFrames << m;
821
+ }
822
+
823
+
824
+ // we now allow items to "post-process", e.g. if they need to setup connections
825
+ // to other items in the layout, which may not have existed at the time the
826
+ // item's state was restored. E.g. a scalebar may have been restored before the map
827
+ // it is linked to
828
+ for ( QgsLayoutItem *item : qgis::as_const ( newItems ) )
829
+ {
830
+ item->finalizeRestoreFromXml ();
831
+ }
832
+ for ( QgsLayoutMultiFrame *mf : qgis::as_const ( newMultiFrames ) )
833
+ {
834
+ mf->finalizeRestoreFromXml ();
835
+ }
836
+
837
+ // Since this function adds items in an order which isn't the z-order, and each item is added to end of
838
+ // z order list in turn, it will now be inconsistent with the actual order of items in the scene.
839
+ // Make sure z order list matches the actual order of items in the scene.
840
+ mItemsModel ->rebuildZList ();
841
+
842
+ return newItems;
843
+ }
844
+
678
845
void QgsLayout::updateBounds ()
679
846
{
680
847
setSceneRect ( layoutBounds ( false , 0.05 ) );
0 commit comments