Bump version to 24.04.3.4
[LibreOffice.git] / xmloff / source / draw / shapeimport.cxx
blob5186c8be2b47eb78b86b303b8bdbe20c153fb0ff
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <tools/debug.hxx>
21 #include <comphelper/diagnose_ex.hxx>
22 #include <sal/log.hxx>
23 #include <comphelper/attributelist.hxx>
25 #include <com/sun/star/beans/XPropertySet.hpp>
26 #include <com/sun/star/text/PositionLayoutDir.hpp>
27 #include <com/sun/star/drawing/XShapes3.hpp>
29 #include <utility>
30 #include <xmloff/unointerfacetouniqueidentifiermapper.hxx>
32 #include <xmloff/shapeimport.hxx>
33 #include <xmloff/xmlstyle.hxx>
34 #include <xmloff/xmltkmap.hxx>
35 #include <xmloff/xmlnamespace.hxx>
36 #include <xmloff/xmltoken.hxx>
37 #include <xmloff/table/XMLTableImport.hxx>
38 #include "eventimp.hxx"
39 #include "ximpshap.hxx"
40 #include "sdpropls.hxx"
41 #include <xmloff/xmlprmap.hxx>
42 #include "ximp3dscene.hxx"
43 #include "ximp3dobject.hxx"
44 #include "ximpgrp.hxx"
45 #include "ximplink.hxx"
47 #include <unordered_map>
48 #include <string_view>
49 #include <vector>
51 namespace {
53 class ShapeGroupContext;
57 using namespace ::com::sun::star;
58 using namespace ::xmloff::token;
60 namespace {
62 struct ConnectionHint
64 css::uno::Reference< css::drawing::XShape > mxConnector;
65 OUString aDestShapeId;
66 sal_Int32 nDestGlueId;
67 bool bStart;
72 /** this map store all gluepoint id mappings for shapes that had user defined gluepoints. This
73 is needed because on insertion the gluepoints will get a new and unique id */
74 typedef std::map<sal_Int32,sal_Int32> GluePointIdMap;
75 typedef std::unordered_map< css::uno::Reference < css::drawing::XShape >, GluePointIdMap > ShapeGluePointsMap;
77 /** this struct is created for each startPage() call and stores information that is needed during
78 import of shapes for one page. Since pages could be nested ( notes pages inside impress ) there
79 is a pointer so one can build up a stack of this structs */
80 struct XMLShapeImportPageContextImpl
82 ShapeGluePointsMap maShapeGluePointsMap;
84 uno::Reference < drawing::XShapes > mxShapes;
86 std::shared_ptr<XMLShapeImportPageContextImpl> mpNext;
89 /** this class is to enable adding members to the XMLShapeImportHelper without getting incompatible */
90 struct XMLShapeImportHelperImpl
92 // context for sorting shapes
93 std::shared_ptr<ShapeGroupContext> mpGroupContext;
95 std::vector<ConnectionHint> maConnections;
97 // #88546# possibility to switch progress bar handling on/off
98 bool mbHandleProgressBar;
100 // stores the capability of the current model to create presentation shapes
101 bool mbIsPresentationShapesSupported;
104 constexpr OUStringLiteral gsStartShape(u"StartShape");
105 constexpr OUStringLiteral gsEndShape(u"EndShape");
106 constexpr OUStringLiteral gsStartGluePointIndex(u"StartGluePointIndex");
107 constexpr OUStringLiteral gsEndGluePointIndex(u"EndGluePointIndex");
109 XMLShapeImportHelper::XMLShapeImportHelper(
110 SvXMLImport& rImporter,
111 const uno::Reference< frame::XModel>& rModel,
112 SvXMLImportPropertyMapper *pExtMapper )
113 : mpImpl( new XMLShapeImportHelperImpl ),
114 mrImporter( rImporter )
116 mpImpl->mpGroupContext = nullptr;
118 // #88546# init to sal_False
119 mpImpl->mbHandleProgressBar = false;
121 mpSdPropHdlFactory = new XMLSdPropHdlFactory( rModel, rImporter );
123 // construct PropertySetMapper
124 rtl::Reference < XMLPropertySetMapper > xMapper = new XMLShapePropertySetMapper(mpSdPropHdlFactory, false);
125 mpPropertySetMapper = new SvXMLImportPropertyMapper( xMapper, rImporter );
127 if( pExtMapper )
129 rtl::Reference < SvXMLImportPropertyMapper > xExtMapper( pExtMapper );
130 mpPropertySetMapper->ChainImportMapper( xExtMapper );
133 // chain text attributes
134 mpPropertySetMapper->ChainImportMapper(XMLTextImportHelper::CreateParaExtPropMapper(rImporter));
135 mpPropertySetMapper->ChainImportMapper(XMLTextImportHelper::CreateParaDefaultExtPropMapper(rImporter));
137 // construct PresPagePropsMapper
138 xMapper = new XMLPropertySetMapper(aXMLSDPresPageProps, mpSdPropHdlFactory, false);
139 mpPresPagePropsMapper = new SvXMLImportPropertyMapper( xMapper, rImporter );
141 uno::Reference< lang::XServiceInfo > xInfo( rImporter.GetModel(), uno::UNO_QUERY );
142 mpImpl->mbIsPresentationShapesSupported = xInfo.is() && xInfo->supportsService( "com.sun.star.presentation.PresentationDocument" );
145 XMLShapeImportHelper::~XMLShapeImportHelper()
147 SAL_WARN_IF( !mpImpl->maConnections.empty(), "xmloff", "XMLShapeImportHelper::restoreConnections() was not called!" );
149 // cleanup factory, decrease refcount. Should lead to destruction.
150 mpSdPropHdlFactory.clear();
152 // cleanup mapper, decrease refcount. Should lead to destruction.
153 mpPropertySetMapper.clear();
155 // cleanup presPage mapper, decrease refcount. Should lead to destruction.
156 mpPresPagePropsMapper.clear();
158 // Styles or AutoStyles context?
159 if(mxStylesContext.is())
160 mxStylesContext->dispose();
162 if(mxAutoStylesContext.is())
163 mxAutoStylesContext->dispose();
167 SvXMLShapeContext* XMLShapeImportHelper::Create3DSceneChildContext(
168 SvXMLImport& rImport,
169 sal_Int32 nElement,
170 const uno::Reference< xml::sax::XFastAttributeList>& xAttrList,
171 uno::Reference< drawing::XShapes > const & rShapes)
173 SdXMLShapeContext *pContext = nullptr;
175 if(rShapes.is())
177 switch(nElement)
179 case XML_ELEMENT(DR3D, XML_SCENE):
181 // dr3d:3dscene inside dr3d:3dscene context
182 pContext = new SdXML3DSceneShapeContext( rImport, xAttrList, rShapes, false);
183 break;
185 case XML_ELEMENT(DR3D, XML_CUBE):
187 // dr3d:3dcube inside dr3d:3dscene context
188 pContext = new SdXML3DCubeObjectShapeContext( rImport, xAttrList, rShapes);
189 break;
191 case XML_ELEMENT(DR3D, XML_SPHERE):
193 // dr3d:3dsphere inside dr3d:3dscene context
194 pContext = new SdXML3DSphereObjectShapeContext( rImport, xAttrList, rShapes);
195 break;
197 case XML_ELEMENT(DR3D, XML_ROTATE):
199 // dr3d:3dlathe inside dr3d:3dscene context
200 pContext = new SdXML3DLatheObjectShapeContext( rImport, xAttrList, rShapes);
201 break;
203 case XML_ELEMENT(DR3D, XML_EXTRUDE):
205 // dr3d:3dextrude inside dr3d:3dscene context
206 pContext = new SdXML3DExtrudeObjectShapeContext( rImport, xAttrList, rShapes);
207 break;
209 default:
210 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
214 if (!pContext)
215 return nullptr;
217 // now parse the attribute list and call the child context for each unknown attribute
218 for(auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
220 if (!pContext->processAttribute( aIter ))
221 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
225 return pContext;
228 void XMLShapeImportHelper::SetStylesContext(SvXMLStylesContext* pNew)
230 mxStylesContext.set(pNew);
233 void XMLShapeImportHelper::SetAutoStylesContext(SvXMLStylesContext* pNew)
235 mxAutoStylesContext.set(pNew);
238 SvXMLShapeContext* XMLShapeImportHelper::CreateGroupChildContext(
239 SvXMLImport& rImport,
240 sal_Int32 nElement,
241 const uno::Reference< xml::sax::XFastAttributeList>& xAttrList,
242 uno::Reference< drawing::XShapes > const & rShapes,
243 bool bTemporaryShape)
245 SdXMLShapeContext *pContext = nullptr;
246 switch (nElement)
248 case XML_ELEMENT(DRAW, XML_G):
249 // draw:g inside group context (RECURSIVE)
250 pContext = new SdXMLGroupShapeContext( rImport, xAttrList, rShapes, bTemporaryShape);
251 break;
252 case XML_ELEMENT(DR3D, XML_SCENE):
254 // dr3d:3dscene inside group context
255 pContext = new SdXML3DSceneShapeContext( rImport, xAttrList, rShapes, bTemporaryShape);
256 break;
258 case XML_ELEMENT(DRAW, XML_RECT):
260 // draw:rect inside group context
261 pContext = new SdXMLRectShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
262 break;
264 case XML_ELEMENT(DRAW, XML_LINE):
266 // draw:line inside group context
267 pContext = new SdXMLLineShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
268 break;
270 case XML_ELEMENT(DRAW, XML_CIRCLE):
271 case XML_ELEMENT(DRAW, XML_ELLIPSE):
273 // draw:circle or draw:ellipse inside group context
274 pContext = new SdXMLEllipseShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
275 break;
277 case XML_ELEMENT(DRAW, XML_POLYGON):
278 case XML_ELEMENT(DRAW, XML_POLYLINE):
280 // draw:polygon or draw:polyline inside group context
281 pContext = new SdXMLPolygonShapeContext( rImport, xAttrList, rShapes,
282 nElement == XML_ELEMENT(DRAW, XML_POLYGON), bTemporaryShape );
283 break;
285 case XML_ELEMENT(DRAW, XML_PATH):
287 // draw:path inside group context
288 pContext = new SdXMLPathShapeContext( rImport, xAttrList, rShapes, bTemporaryShape);
289 break;
291 case XML_ELEMENT(DRAW, XML_FRAME):
293 // text:text-box inside group context
294 pContext = new SdXMLFrameShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
295 break;
297 case XML_ELEMENT(DRAW, XML_CONTROL):
299 // draw:control inside group context
300 pContext = new SdXMLControlShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
301 break;
303 case XML_ELEMENT(DRAW, XML_CONNECTOR):
305 // draw:connector inside group context
306 pContext = new SdXMLConnectorShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
307 break;
309 case XML_ELEMENT(DRAW, XML_MEASURE):
311 // draw:measure inside group context
312 pContext = new SdXMLMeasureShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
313 break;
315 case XML_ELEMENT(DRAW, XML_PAGE_THUMBNAIL):
317 // draw:page inside group context
318 pContext = new SdXMLPageShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
319 break;
321 case XML_ELEMENT(DRAW, XML_CAPTION):
322 case XML_ELEMENT(OFFICE, XML_ANNOTATION):
324 // draw:caption inside group context
325 pContext = new SdXMLCaptionShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
326 break;
328 case XML_ELEMENT(CHART, XML_CHART):
330 // chart:chart inside group context
331 pContext = new SdXMLChartShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
332 break;
334 case XML_ELEMENT(DRAW, XML_CUSTOM_SHAPE):
336 // draw:customshape
337 pContext = new SdXMLCustomShapeContext( rImport, xAttrList, rShapes );
338 break;
340 case XML_ELEMENT(DRAW, XML_A):
341 return new SdXMLShapeLinkContext( rImport, xAttrList, rShapes );
342 // add other shapes here...
343 default:
344 XMLOFF_INFO_UNKNOWN_ELEMENT("xmloff", nElement);
345 return new SvXMLShapeContext( rImport, bTemporaryShape );
348 // now parse the attribute list and call the child context for each unknown attribute
349 for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
351 if (!pContext->processAttribute( aIter ))
352 XMLOFF_INFO_UNKNOWN("xmloff", aIter);
354 return pContext;
357 // This method is called from SdXMLFrameShapeContext to create children of draw:frame
358 SvXMLShapeContext* XMLShapeImportHelper::CreateFrameChildContext(
359 SvXMLImport& rImport,
360 sal_Int32 nElement,
361 const uno::Reference< xml::sax::XFastAttributeList>& rAttrList,
362 uno::Reference< drawing::XShapes > const & rShapes,
363 const uno::Reference< xml::sax::XFastAttributeList>& rFrameAttrList)
365 SdXMLShapeContext *pContext = nullptr;
367 rtl::Reference<sax_fastparser::FastAttributeList> xCombinedAttrList = new sax_fastparser::FastAttributeList(rAttrList);
368 if( rFrameAttrList.is() )
369 xCombinedAttrList->add(rFrameAttrList);
371 switch(nElement)
373 case XML_ELEMENT(DRAW, XML_TEXT_BOX):
375 // text:text-box inside group context
376 pContext = new SdXMLTextBoxShapeContext( rImport, xCombinedAttrList, rShapes );
377 break;
379 case XML_ELEMENT(DRAW, XML_IMAGE):
381 // office:image inside group context
382 pContext = new SdXMLGraphicObjectShapeContext( rImport, xCombinedAttrList, rShapes );
383 break;
385 case XML_ELEMENT(DRAW, XML_OBJECT):
386 case XML_ELEMENT(DRAW, XML_OBJECT_OLE):
388 // draw:object or draw:object_ole
389 pContext = new SdXMLObjectShapeContext( rImport, xCombinedAttrList, rShapes );
390 break;
392 case XML_ELEMENT(TABLE, XML_TABLE):
394 // draw:object or draw:object_ole
395 if( rImport.IsTableShapeSupported() )
396 pContext = new SdXMLTableShapeContext( rImport, xCombinedAttrList, rShapes );
397 break;
400 case XML_ELEMENT(DRAW, XML_PLUGIN):
402 // draw:plugin
403 pContext = new SdXMLPluginShapeContext( rImport, xCombinedAttrList, rShapes );
404 break;
406 case XML_ELEMENT(DRAW, XML_FLOATING_FRAME):
408 // draw:floating-frame
409 pContext = new SdXMLFloatingFrameShapeContext( rImport, xCombinedAttrList, rShapes );
410 break;
412 case XML_ELEMENT(DRAW, XML_APPLET):
414 // draw:applet
415 pContext = new SdXMLAppletShapeContext( rImport, xCombinedAttrList, rShapes );
416 break;
418 // add other shapes here...
419 default:
420 SAL_INFO("xmloff", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement));
421 break;
424 if( pContext )
426 // now parse the attribute list and call the child context for each unknown attribute
427 for(auto& aIter : *xCombinedAttrList)
429 if (!pContext->processAttribute( aIter ))
430 SAL_INFO("xmloff", "unknown attribute " << SvXMLImport::getPrefixAndNameFromToken(aIter.getToken()) << " value=" << aIter.toString());
434 return pContext;
437 css::uno::Reference< css::xml::sax::XFastContextHandler > XMLShapeImportHelper::CreateFrameChildContext(
438 SvXMLImportContext *pThisContext,
439 sal_Int32 nElement,
440 const uno::Reference< xml::sax::XFastAttributeList>& xAttrList )
442 css::uno::Reference< css::xml::sax::XFastContextHandler > xContext;
443 SdXMLFrameShapeContext *pFrameContext = dynamic_cast<SdXMLFrameShapeContext*>( pThisContext );
444 if (pFrameContext)
445 xContext = pFrameContext->createFastChildContext( nElement, xAttrList );
447 if (!xContext)
448 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
449 return xContext;
452 /** this function is called whenever the implementation classes like to add this new
453 shape to the given XShapes.
455 void XMLShapeImportHelper::addShape( uno::Reference< drawing::XShape >& rShape,
456 const uno::Reference< xml::sax::XFastAttributeList >&,
457 uno::Reference< drawing::XShapes >& rShapes)
459 if( rShape.is() && rShapes.is() )
461 // add new shape to parent
462 rShapes->add( rShape );
464 uno::Reference<beans::XPropertySet> xPropertySet(rShape, uno::UNO_QUERY);
465 if (xPropertySet.is())
467 static constexpr OUStringLiteral sHandlePathObjScale = u"HandlePathObjScale";
468 xPropertySet->setPropertyValue(sHandlePathObjScale, uno::Any(true));
473 /** this function is called whenever the implementation classes have finished importing
474 a shape to the given XShapes. The shape is already inserted into its XShapes and
475 all properties and styles are set.
477 void XMLShapeImportHelper::finishShape(
478 css::uno::Reference< css::drawing::XShape >& rShape,
479 const css::uno::Reference< css::xml::sax::XFastAttributeList >&,
480 css::uno::Reference< css::drawing::XShapes >&)
482 /* Set property <PositionLayoutDir>
483 to <PositionInHoriL2R>, if it exists and the import states that
484 the shape positioning attributes are in horizontal left-to-right
485 layout. This is the case for the OpenOffice.org file format.
486 This setting is done for Writer documents, because the property
487 only exists at service css::text::Shape - the Writer
488 UNO service for shapes.
489 The value indicates that the positioning attributes are given
490 in horizontal left-to-right layout. The property is evaluated
491 during the first positioning of the shape in order to convert
492 the shape position given in the OpenOffice.org file format to
493 the one for the OASIS Open Office file format. (#i28749#, #i36248#)
495 uno::Reference< beans::XPropertySet > xPropSet(rShape, uno::UNO_QUERY);
496 if ( xPropSet.is() )
498 if ( mrImporter.IsShapePositionInHoriL2R() &&
499 xPropSet->getPropertySetInfo()->hasPropertyByName(
500 "PositionLayoutDir") )
502 uno::Any aPosLayoutDir;
503 aPosLayoutDir <<= text::PositionLayoutDir::PositionInHoriL2R;
504 xPropSet->setPropertyValue( "PositionLayoutDir", aPosLayoutDir );
509 namespace {
511 // helper functions for z-order sorting
512 struct ZOrderHint
514 sal_Int32 nIs;
515 sal_Int32 nShould;
516 /// The hint is for this shape. We don't use uno::Reference here to speed up
517 /// some operations, and because this shape is always held by mxShapes
518 drawing::XShape* pShape;
520 bool operator<(const ZOrderHint& rComp) const { return nShould < rComp.nShould; }
523 // a) handle z-order of group contents after it has been imported
524 // b) apply group events over group contents after it has been imported
525 class ShapeGroupContext
527 public:
528 uno::Reference< drawing::XShapes > mxShapes;
529 std::vector<SdXMLEventContextData> maEventData;
530 std::vector<ZOrderHint> maZOrderList;
531 std::vector<ZOrderHint> maUnsortedList;
533 sal_Int32 mnCurrentZ;
534 std::shared_ptr<ShapeGroupContext> mpParentContext;
536 ShapeGroupContext( uno::Reference< drawing::XShapes > xShapes, std::shared_ptr<ShapeGroupContext> pParentContext );
538 void popGroupAndPostProcess();
539 private:
540 void moveShape( sal_Int32 nSourcePos, sal_Int32 nDestPos );
545 ShapeGroupContext::ShapeGroupContext( uno::Reference< drawing::XShapes > xShapes, std::shared_ptr<ShapeGroupContext> pParentContext )
546 : mxShapes(std::move( xShapes )), mnCurrentZ( 0 ), mpParentContext( std::move(pParentContext) )
550 void ShapeGroupContext::moveShape( sal_Int32 nSourcePos, sal_Int32 nDestPos )
552 uno::Any aAny( mxShapes->getByIndex( nSourcePos ) );
553 uno::Reference< beans::XPropertySet > xPropSet;
554 aAny >>= xPropSet;
556 if( !(xPropSet.is() && xPropSet->getPropertySetInfo()->hasPropertyByName( "ZOrder" )) )
557 return;
559 xPropSet->setPropertyValue( "ZOrder", uno::Any(nDestPos) );
561 for( ZOrderHint& rHint : maZOrderList )
563 if( rHint.nIs < nSourcePos )
565 DBG_ASSERT(rHint.nIs >= nDestPos, "Shape sorting failed" );
566 rHint.nIs++;
570 for( ZOrderHint& rHint : maUnsortedList )
572 if( rHint.nIs < nSourcePos )
574 SAL_WARN_IF( rHint.nIs < nDestPos, "xmloff", "shape sorting failed" );
575 rHint.nIs++;
580 // sort shapes
581 void ShapeGroupContext::popGroupAndPostProcess()
583 if (!maEventData.empty())
585 // tdf#127791 wait until a group is popped to set its event data
586 for (auto& event : maEventData)
587 event.ApplyProperties();
588 maEventData.clear();
591 // only do something if we have shapes to sort
592 if( maZOrderList.empty() )
593 return;
595 // check if there are more shapes than inserted with ::shapeWithZIndexAdded()
596 // This can happen if there where already shapes on the page before import
597 // Since the writer may delete some of this shapes during import, we need
598 // to do this here and not in our c'tor anymore
600 // check if we have more shapes than we know of
601 sal_Int32 nCount = mxShapes->getCount();
603 nCount -= maZOrderList.size();
604 nCount -= maUnsortedList.size();
606 if( nCount > 0 )
608 // first update offsets of added shapes
609 for (ZOrderHint& rHint : maZOrderList)
610 rHint.nIs += nCount;
611 for (ZOrderHint& rHint : maUnsortedList)
612 rHint.nIs += nCount;
614 // second add the already existing shapes in the unsorted list
615 ZOrderHint aNewHint;
616 aNewHint.pShape = nullptr;
619 nCount--;
621 aNewHint.nIs = nCount;
622 aNewHint.nShould = -1;
624 maUnsortedList.insert(maUnsortedList.begin(), aNewHint);
626 while( nCount );
629 bool bSorted = std::is_sorted(maZOrderList.begin(), maZOrderList.end(),
630 [](const ZOrderHint& rLeft, const ZOrderHint& rRight)
631 { return rLeft.nShould < rRight.nShould; } );
633 if (bSorted)
634 return; // nothin' to do
636 // sort z-ordered shapes by nShould field
637 std::sort(maZOrderList.begin(), maZOrderList.end());
639 uno::Reference<drawing::XShapes3> xShapes3(mxShapes, uno::UNO_QUERY);
640 if( xShapes3.is())
642 uno::Sequence<sal_Int32> aNewOrder(maZOrderList.size() + maUnsortedList.size());
643 auto pNewOrder = aNewOrder.getArray();
644 sal_Int32 nIndex = 0;
646 for (const ZOrderHint& rHint : maZOrderList)
648 // fill in the gaps from unordered list
649 for (std::vector<ZOrderHint>::iterator aIt = maUnsortedList.begin(); aIt != maUnsortedList.end() && nIndex < rHint.nShould; )
651 pNewOrder[nIndex++] = (*aIt).nIs;
652 aIt = maUnsortedList.erase(aIt);
655 pNewOrder[nIndex] = rHint.nIs;
656 nIndex++;
661 xShapes3->sort(aNewOrder);
662 maZOrderList.clear();
663 return;
665 catch (const css::lang::IllegalArgumentException& /*e*/)
669 // this is the current index, all shapes before that
670 // index are finished
671 sal_Int32 nIndex = 0;
672 for (const ZOrderHint& rHint : maZOrderList)
674 for (std::vector<ZOrderHint>::iterator aIt = maUnsortedList.begin(); aIt != maUnsortedList.end() && nIndex < rHint.nShould; )
676 moveShape( (*aIt).nIs, nIndex++ );
677 aIt = maUnsortedList.erase(aIt);
681 if(rHint.nIs != nIndex )
682 moveShape( rHint.nIs, nIndex );
684 nIndex++;
686 maZOrderList.clear();
689 void XMLShapeImportHelper::pushGroupForPostProcessing( uno::Reference< drawing::XShapes >& rShapes )
691 mpImpl->mpGroupContext = std::make_shared<ShapeGroupContext>( rShapes, mpImpl->mpGroupContext );
694 void XMLShapeImportHelper::addShapeEvents(SdXMLEventContextData& rData)
696 if (mpImpl->mpGroupContext && mpImpl->mpGroupContext->mxShapes == rData.mxShape)
698 // tdf#127791 wait until a group is popped to set its event data so
699 // that the events are applied to all its children, which are not available
700 // at the start of the group tag
701 mpImpl->mpGroupContext->maEventData.push_back(rData);
703 else
704 rData.ApplyProperties();
707 void XMLShapeImportHelper::popGroupAndPostProcess()
709 SAL_WARN_IF( !mpImpl->mpGroupContext, "xmloff", "No context to sort!" );
710 if( !mpImpl->mpGroupContext )
711 return;
715 mpImpl->mpGroupContext->popGroupAndPostProcess();
717 catch( const uno::Exception& )
719 DBG_UNHANDLED_EXCEPTION("xmloff", "exception while sorting shapes, sorting failed");
722 // put parent on top and drop current context, we are done
723 mpImpl->mpGroupContext = mpImpl->mpGroupContext->mpParentContext;
726 void XMLShapeImportHelper::shapeWithZIndexAdded( css::uno::Reference< css::drawing::XShape > const & xShape, sal_Int32 nZIndex )
728 if( !mpImpl->mpGroupContext)
729 return;
731 ZOrderHint aNewHint;
732 aNewHint.nIs = mpImpl->mpGroupContext->mnCurrentZ++;
733 aNewHint.nShould = nZIndex;
734 aNewHint.pShape = xShape.get();
736 if( nZIndex == -1 )
738 // don't care, so add to unsorted list
739 mpImpl->mpGroupContext->maUnsortedList.push_back(aNewHint);
741 else
743 // insert into sort list
744 mpImpl->mpGroupContext->maZOrderList.push_back(aNewHint);
748 void XMLShapeImportHelper::shapeRemoved(const uno::Reference<drawing::XShape>& xShape)
750 auto it = std::find_if(mpImpl->mpGroupContext->maZOrderList.begin(), mpImpl->mpGroupContext->maZOrderList.end(), [&xShape](const ZOrderHint& rHint)
752 return rHint.pShape == xShape.get();
754 if (it == mpImpl->mpGroupContext->maZOrderList.end())
755 // Part of the unsorted list, nothing to do.
756 return;
758 sal_Int32 nZIndex = it->nIs;
760 for (it = mpImpl->mpGroupContext->maZOrderList.begin(); it != mpImpl->mpGroupContext->maZOrderList.end();)
762 if (it->nIs == nZIndex)
764 // This is xShape: remove it and adjust the max of indexes
765 // accordingly.
766 it = mpImpl->mpGroupContext->maZOrderList.erase(it);
767 mpImpl->mpGroupContext->mnCurrentZ--;
768 continue;
770 else if (it->nIs > nZIndex)
771 // On top of xShape: adjust actual index to reflect removal.
772 it->nIs--;
774 // On top of or below xShape.
775 ++it;
779 void XMLShapeImportHelper::addShapeConnection( css::uno::Reference< css::drawing::XShape > const & rConnectorShape,
780 bool bStart,
781 const OUString& rDestShapeId,
782 sal_Int32 nDestGlueId )
784 ConnectionHint aHint;
785 aHint.mxConnector = rConnectorShape;
786 aHint.bStart = bStart;
787 aHint.aDestShapeId = rDestShapeId;
788 aHint.nDestGlueId = nDestGlueId;
790 mpImpl->maConnections.push_back( aHint );
793 void XMLShapeImportHelper::restoreConnections()
795 const std::vector<ConnectionHint>::size_type nCount = mpImpl->maConnections.size();
796 for( std::vector<ConnectionHint>::size_type i = 0; i < nCount; i++ )
798 ConnectionHint& rHint = mpImpl->maConnections[i];
799 uno::Reference< beans::XPropertySet > xConnector( rHint.mxConnector, uno::UNO_QUERY );
800 if( xConnector.is() )
802 // #86637# remember line deltas
803 uno::Any aLine1Delta;
804 uno::Any aLine2Delta;
805 uno::Any aLine3Delta;
806 OUString aStr1("EdgeLine1Delta");
807 OUString aStr2("EdgeLine2Delta");
808 OUString aStr3("EdgeLine3Delta");
809 aLine1Delta = xConnector->getPropertyValue(aStr1);
810 aLine2Delta = xConnector->getPropertyValue(aStr2);
811 aLine3Delta = xConnector->getPropertyValue(aStr3);
813 // #86637# simply setting these values WILL force the connector to do
814 // a new layout promptly. So the line delta values have to be rescued
815 // and restored around connector changes.
816 uno::Reference< drawing::XShape > xShape(
817 mrImporter.getInterfaceToIdentifierMapper().getReference( rHint.aDestShapeId ), uno::UNO_QUERY );
818 if( xShape.is() )
820 if (rHint.bStart)
821 xConnector->setPropertyValue( gsStartShape, uno::Any(xShape) );
822 else
823 xConnector->setPropertyValue( gsEndShape, uno::Any(xShape) );
825 sal_Int32 nGlueId = rHint.nDestGlueId < 4 ? rHint.nDestGlueId : getGluePointId( xShape, rHint.nDestGlueId );
826 if(rHint.bStart)
827 xConnector->setPropertyValue( gsStartGluePointIndex, uno::Any(nGlueId) );
828 else
829 xConnector->setPropertyValue( gsEndGluePointIndex, uno::Any(nGlueId) );
832 // #86637# restore line deltas
833 xConnector->setPropertyValue(aStr1, aLine1Delta );
834 xConnector->setPropertyValue(aStr2, aLine2Delta );
835 xConnector->setPropertyValue(aStr3, aLine3Delta );
838 mpImpl->maConnections.clear();
841 SvXMLImportPropertyMapper* XMLShapeImportHelper::CreateShapePropMapper( const uno::Reference< frame::XModel>& rModel, SvXMLImport& rImport )
843 rtl::Reference< XMLPropertyHandlerFactory > xFactory = new XMLSdPropHdlFactory( rModel, rImport );
844 rtl::Reference < XMLPropertySetMapper > xMapper = new XMLShapePropertySetMapper( xFactory, false );
845 SvXMLImportPropertyMapper* pResult = new SvXMLImportPropertyMapper( xMapper, rImport );
847 // chain text attributes
848 pResult->ChainImportMapper( XMLTextImportHelper::CreateParaExtPropMapper( rImport ) );
849 return pResult;
852 /** adds a mapping for a gluepoint identifier from an xml file to the identifier created after inserting
853 the new gluepoint into the core. The saved mappings can be retrieved by getGluePointId() */
854 void XMLShapeImportHelper::addGluePointMapping( css::uno::Reference< css::drawing::XShape > const & xShape,
855 sal_Int32 nSourceId, sal_Int32 nDestinnationId )
857 if( mpPageContext )
858 mpPageContext->maShapeGluePointsMap[xShape][nSourceId] = nDestinnationId;
861 /** moves all current DestinationId's by n */
862 void XMLShapeImportHelper::moveGluePointMapping( const css::uno::Reference< css::drawing::XShape >& xShape, const sal_Int32 n )
864 if( mpPageContext )
866 ShapeGluePointsMap::iterator aShapeIter( mpPageContext->maShapeGluePointsMap.find( xShape ) );
867 if( aShapeIter != mpPageContext->maShapeGluePointsMap.end() )
869 for ( auto& rShapeId : (*aShapeIter).second )
871 if ( rShapeId.second != -1 )
872 rShapeId.second += n;
878 /** retrieves a mapping for a gluepoint identifier from the current xml file to the identifier created after
879 inserting the new gluepoint into the core. The mapping must be initialized first with addGluePointMapping() */
880 sal_Int32 XMLShapeImportHelper::getGluePointId( const css::uno::Reference< css::drawing::XShape >& xShape, sal_Int32 nSourceId )
882 if( mpPageContext )
884 ShapeGluePointsMap::iterator aShapeIter( mpPageContext->maShapeGluePointsMap.find( xShape ) );
885 if( aShapeIter != mpPageContext->maShapeGluePointsMap.end() )
887 GluePointIdMap::iterator aIdIter = (*aShapeIter).second.find(nSourceId);
888 if( aIdIter != (*aShapeIter).second.end() )
889 return (*aIdIter).second;
893 return -1;
896 /** this method must be calling before the first shape is imported for the given page */
897 void XMLShapeImportHelper::startPage( css::uno::Reference< css::drawing::XShapes > const & rShapes )
899 const std::shared_ptr<XMLShapeImportPageContextImpl> pOldContext = mpPageContext;
900 mpPageContext = std::make_shared<XMLShapeImportPageContextImpl>();
901 mpPageContext->mpNext = pOldContext;
902 mpPageContext->mxShapes = rShapes;
905 /** this method must be calling after the last shape is imported for the given page */
906 void XMLShapeImportHelper::endPage( css::uno::Reference< css::drawing::XShapes > const & rShapes )
908 SAL_WARN_IF( !mpPageContext || (mpPageContext->mxShapes != rShapes), "xmloff", "wrong call to endPage(), no startPage called or wrong page" );
909 if( nullptr == mpPageContext )
910 return;
912 restoreConnections();
914 mpPageContext = mpPageContext->mpNext;
917 /** defines if the import should increment the progress bar or not */
918 void XMLShapeImportHelper::enableHandleProgressBar()
920 mpImpl->mbHandleProgressBar = true;
923 bool XMLShapeImportHelper::IsHandleProgressBarEnabled() const
925 return mpImpl->mbHandleProgressBar;
928 /** queries the capability of the current model to create presentation shapes */
929 bool XMLShapeImportHelper::IsPresentationShapesSupported() const
931 return mpImpl->mbIsPresentationShapesSupported;
934 const rtl::Reference< XMLTableImport >& XMLShapeImportHelper::GetShapeTableImport()
936 if( !mxShapeTableImport.is() )
938 rtl::Reference< XMLPropertyHandlerFactory > xFactory( new XMLSdPropHdlFactory( mrImporter.GetModel(), mrImporter ) );
939 rtl::Reference< XMLPropertySetMapper > xPropertySetMapper( new XMLShapePropertySetMapper( xFactory, false ) );
940 mxShapeTableImport = new XMLTableImport( mrImporter, xPropertySetMapper, xFactory );
943 return mxShapeTableImport;
946 void SvXMLShapeContext::setHyperlink( const OUString& rHyperlink )
948 msHyperlink = rHyperlink;
951 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */