diff options
Diffstat (limited to 'xmloff/source/draw/shapeimport.cxx')
-rw-r--r-- | xmloff/source/draw/shapeimport.cxx | 950 |
1 files changed, 950 insertions, 0 deletions
diff --git a/xmloff/source/draw/shapeimport.cxx b/xmloff/source/draw/shapeimport.cxx new file mode 100644 index 000000000..930051b2a --- /dev/null +++ b/xmloff/source/draw/shapeimport.cxx @@ -0,0 +1,950 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> +#include <comphelper/attributelist.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/text/PositionLayoutDir.hpp> +#include <com/sun/star/drawing/XShapes3.hpp> + +#include <utility> +#include <xmloff/unointerfacetouniqueidentifiermapper.hxx> + +#include <xmloff/shapeimport.hxx> +#include <xmloff/xmlstyle.hxx> +#include <xmloff/xmltkmap.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/table/XMLTableImport.hxx> +#include <xmloff/attrlist.hxx> +#include "eventimp.hxx" +#include "ximpshap.hxx" +#include "sdpropls.hxx" +#include <xmloff/xmlprmap.hxx> +#include "ximp3dscene.hxx" +#include "ximp3dobject.hxx" +#include "ximpgrp.hxx" +#include "ximplink.hxx" + +#include <unordered_map> +#include <string_view> +#include <vector> + +namespace { + +class ShapeGroupContext; + +} + +using namespace ::std; +using namespace ::com::sun::star; +using namespace ::xmloff::token; + +namespace { + +struct ConnectionHint +{ + css::uno::Reference< css::drawing::XShape > mxConnector; + OUString aDestShapeId; + sal_Int32 nDestGlueId; + bool bStart; +}; + +} + +/** this map store all gluepoint id mappings for shapes that had user defined gluepoints. This + is needed because on insertion the gluepoints will get a new and unique id */ +typedef std::map<sal_Int32,sal_Int32> GluePointIdMap; +typedef std::unordered_map< css::uno::Reference < css::drawing::XShape >, GluePointIdMap > ShapeGluePointsMap; + +/** this struct is created for each startPage() call and stores information that is needed during + import of shapes for one page. Since pages could be nested ( notes pages inside impress ) there + is a pointer so one can build up a stack of this structs */ +struct XMLShapeImportPageContextImpl +{ + ShapeGluePointsMap maShapeGluePointsMap; + + uno::Reference < drawing::XShapes > mxShapes; + + std::shared_ptr<XMLShapeImportPageContextImpl> mpNext; +}; + +/** this class is to enable adding members to the XMLShapeImportHelper without getting incompatible */ +struct XMLShapeImportHelperImpl +{ + // context for sorting shapes + std::shared_ptr<ShapeGroupContext> mpGroupContext; + + std::vector<ConnectionHint> maConnections; + + // #88546# possibility to switch progress bar handling on/off + bool mbHandleProgressBar; + + // stores the capability of the current model to create presentation shapes + bool mbIsPresentationShapesSupported; +}; + +constexpr OUStringLiteral gsStartShape(u"StartShape"); +constexpr OUStringLiteral gsEndShape(u"EndShape"); +constexpr OUStringLiteral gsStartGluePointIndex(u"StartGluePointIndex"); +constexpr OUStringLiteral gsEndGluePointIndex(u"EndGluePointIndex"); + +XMLShapeImportHelper::XMLShapeImportHelper( + SvXMLImport& rImporter, + const uno::Reference< frame::XModel>& rModel, + SvXMLImportPropertyMapper *pExtMapper ) +: mpImpl( new XMLShapeImportHelperImpl ), + mrImporter( rImporter ) +{ + mpImpl->mpGroupContext = nullptr; + + // #88546# init to sal_False + mpImpl->mbHandleProgressBar = false; + + mpSdPropHdlFactory = new XMLSdPropHdlFactory( rModel, rImporter ); + + // construct PropertySetMapper + rtl::Reference < XMLPropertySetMapper > xMapper = new XMLShapePropertySetMapper(mpSdPropHdlFactory, false); + mpPropertySetMapper = new SvXMLImportPropertyMapper( xMapper, rImporter ); + + if( pExtMapper ) + { + rtl::Reference < SvXMLImportPropertyMapper > xExtMapper( pExtMapper ); + mpPropertySetMapper->ChainImportMapper( xExtMapper ); + } + + // chain text attributes + mpPropertySetMapper->ChainImportMapper(XMLTextImportHelper::CreateParaExtPropMapper(rImporter)); + mpPropertySetMapper->ChainImportMapper(XMLTextImportHelper::CreateParaDefaultExtPropMapper(rImporter)); + + // construct PresPagePropsMapper + xMapper = new XMLPropertySetMapper(aXMLSDPresPageProps, mpSdPropHdlFactory, false); + mpPresPagePropsMapper = new SvXMLImportPropertyMapper( xMapper, rImporter ); + + uno::Reference< lang::XServiceInfo > xInfo( rImporter.GetModel(), uno::UNO_QUERY ); + mpImpl->mbIsPresentationShapesSupported = xInfo.is() && xInfo->supportsService( "com.sun.star.presentation.PresentationDocument" ); +} + +XMLShapeImportHelper::~XMLShapeImportHelper() +{ + SAL_WARN_IF( !mpImpl->maConnections.empty(), "xmloff", "XMLShapeImportHelper::restoreConnections() was not called!" ); + + // cleanup factory, decrease refcount. Should lead to destruction. + mpSdPropHdlFactory.clear(); + + // cleanup mapper, decrease refcount. Should lead to destruction. + mpPropertySetMapper.clear(); + + // cleanup presPage mapper, decrease refcount. Should lead to destruction. + mpPresPagePropsMapper.clear(); + + // Styles or AutoStyles context? + if(mxStylesContext.is()) + mxStylesContext->dispose(); + + if(mxAutoStylesContext.is()) + mxAutoStylesContext->dispose(); +} + + +SvXMLShapeContext* XMLShapeImportHelper::Create3DSceneChildContext( + SvXMLImport& rImport, + sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList>& xAttrList, + uno::Reference< drawing::XShapes > const & rShapes) +{ + SdXMLShapeContext *pContext = nullptr; + + if(rShapes.is()) + { + switch(nElement) + { + case XML_ELEMENT(DR3D, XML_SCENE): + { + // dr3d:3dscene inside dr3d:3dscene context + pContext = new SdXML3DSceneShapeContext( rImport, xAttrList, rShapes, false); + break; + } + case XML_ELEMENT(DR3D, XML_CUBE): + { + // dr3d:3dcube inside dr3d:3dscene context + pContext = new SdXML3DCubeObjectShapeContext( rImport, xAttrList, rShapes); + break; + } + case XML_ELEMENT(DR3D, XML_SPHERE): + { + // dr3d:3dsphere inside dr3d:3dscene context + pContext = new SdXML3DSphereObjectShapeContext( rImport, xAttrList, rShapes); + break; + } + case XML_ELEMENT(DR3D, XML_ROTATE): + { + // dr3d:3dlathe inside dr3d:3dscene context + pContext = new SdXML3DLatheObjectShapeContext( rImport, xAttrList, rShapes); + break; + } + case XML_ELEMENT(DR3D, XML_EXTRUDE): + { + // dr3d:3dextrude inside dr3d:3dscene context + pContext = new SdXML3DExtrudeObjectShapeContext( rImport, xAttrList, rShapes); + break; + } + default: + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + } + } + + if (!pContext) + return nullptr; + + // now parse the attribute list and call the child context for each unknown attribute + for(auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList)) + { + if (!pContext->processAttribute( aIter )) + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + + } + + return pContext; +} + +void XMLShapeImportHelper::SetStylesContext(SvXMLStylesContext* pNew) +{ + mxStylesContext.set(pNew); +} + +void XMLShapeImportHelper::SetAutoStylesContext(SvXMLStylesContext* pNew) +{ + mxAutoStylesContext.set(pNew); +} + +SvXMLShapeContext* XMLShapeImportHelper::CreateGroupChildContext( + SvXMLImport& rImport, + sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList>& xAttrList, + uno::Reference< drawing::XShapes > const & rShapes, + bool bTemporaryShape) +{ + SdXMLShapeContext *pContext = nullptr; + switch (nElement) + { + case XML_ELEMENT(DRAW, XML_G): + // draw:g inside group context (RECURSIVE) + pContext = new SdXMLGroupShapeContext( rImport, xAttrList, rShapes, bTemporaryShape); + break; + case XML_ELEMENT(DR3D, XML_SCENE): + { + // dr3d:3dscene inside group context + pContext = new SdXML3DSceneShapeContext( rImport, xAttrList, rShapes, bTemporaryShape); + break; + } + case XML_ELEMENT(DRAW, XML_RECT): + { + // draw:rect inside group context + pContext = new SdXMLRectShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ); + break; + } + case XML_ELEMENT(DRAW, XML_LINE): + { + // draw:line inside group context + pContext = new SdXMLLineShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ); + break; + } + case XML_ELEMENT(DRAW, XML_CIRCLE): + case XML_ELEMENT(DRAW, XML_ELLIPSE): + { + // draw:circle or draw:ellipse inside group context + pContext = new SdXMLEllipseShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ); + break; + } + case XML_ELEMENT(DRAW, XML_POLYGON): + case XML_ELEMENT(DRAW, XML_POLYLINE): + { + // draw:polygon or draw:polyline inside group context + pContext = new SdXMLPolygonShapeContext( rImport, xAttrList, rShapes, + nElement == XML_ELEMENT(DRAW, XML_POLYGON), bTemporaryShape ); + break; + } + case XML_ELEMENT(DRAW, XML_PATH): + { + // draw:path inside group context + pContext = new SdXMLPathShapeContext( rImport, xAttrList, rShapes, bTemporaryShape); + break; + } + case XML_ELEMENT(DRAW, XML_FRAME): + { + // text:text-box inside group context + pContext = new SdXMLFrameShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ); + break; + } + case XML_ELEMENT(DRAW, XML_CONTROL): + { + // draw:control inside group context + pContext = new SdXMLControlShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ); + break; + } + case XML_ELEMENT(DRAW, XML_CONNECTOR): + { + // draw:connector inside group context + pContext = new SdXMLConnectorShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ); + break; + } + case XML_ELEMENT(DRAW, XML_MEASURE): + { + // draw:measure inside group context + pContext = new SdXMLMeasureShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ); + break; + } + case XML_ELEMENT(DRAW, XML_PAGE_THUMBNAIL): + { + // draw:page inside group context + pContext = new SdXMLPageShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ); + break; + } + case XML_ELEMENT(DRAW, XML_CAPTION): + case XML_ELEMENT(OFFICE, XML_ANNOTATION): + { + // draw:caption inside group context + pContext = new SdXMLCaptionShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ); + break; + } + case XML_ELEMENT(CHART, XML_CHART): + { + // chart:chart inside group context + pContext = new SdXMLChartShapeContext( rImport, xAttrList, rShapes, bTemporaryShape ); + break; + } + case XML_ELEMENT(DRAW, XML_CUSTOM_SHAPE): + { + // draw:customshape + pContext = new SdXMLCustomShapeContext( rImport, xAttrList, rShapes ); + break; + } + case XML_ELEMENT(DRAW, XML_A): + return new SdXMLShapeLinkContext( rImport, xAttrList, rShapes ); + // add other shapes here... + default: + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + return new SvXMLShapeContext( rImport, bTemporaryShape ); + } + + // now parse the attribute list and call the child context for each unknown attribute + for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList )) + { + if (!pContext->processAttribute( aIter )) + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + return pContext; +} + +// This method is called from SdXMLFrameShapeContext to create children of draw:frame +SvXMLShapeContext* XMLShapeImportHelper::CreateFrameChildContext( + SvXMLImport& rImport, + sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList>& rAttrList, + uno::Reference< drawing::XShapes > const & rShapes, + const uno::Reference< xml::sax::XFastAttributeList>& rFrameAttrList) +{ + SdXMLShapeContext *pContext = nullptr; + + rtl::Reference<sax_fastparser::FastAttributeList> xCombinedAttrList = new sax_fastparser::FastAttributeList(rAttrList); + if( rFrameAttrList.is() ) + xCombinedAttrList->add(rFrameAttrList); + + switch(nElement) + { + case XML_ELEMENT(DRAW, XML_TEXT_BOX): + { + // text:text-box inside group context + pContext = new SdXMLTextBoxShapeContext( rImport, xCombinedAttrList, rShapes ); + break; + } + case XML_ELEMENT(DRAW, XML_IMAGE): + { + // office:image inside group context + pContext = new SdXMLGraphicObjectShapeContext( rImport, xCombinedAttrList, rShapes ); + break; + } + case XML_ELEMENT(DRAW, XML_OBJECT): + case XML_ELEMENT(DRAW, XML_OBJECT_OLE): + { + // draw:object or draw:object_ole + pContext = new SdXMLObjectShapeContext( rImport, xCombinedAttrList, rShapes ); + break; + } + case XML_ELEMENT(TABLE, XML_TABLE): + { + // draw:object or draw:object_ole + if( rImport.IsTableShapeSupported() ) + pContext = new SdXMLTableShapeContext( rImport, xCombinedAttrList, rShapes ); + break; + + } + case XML_ELEMENT(DRAW, XML_PLUGIN): + { + // draw:plugin + pContext = new SdXMLPluginShapeContext( rImport, xCombinedAttrList, rShapes ); + break; + } + case XML_ELEMENT(DRAW, XML_FLOATING_FRAME): + { + // draw:floating-frame + pContext = new SdXMLFloatingFrameShapeContext( rImport, xCombinedAttrList, rShapes ); + break; + } + case XML_ELEMENT(DRAW, XML_APPLET): + { + // draw:applet + pContext = new SdXMLAppletShapeContext( rImport, xCombinedAttrList, rShapes ); + break; + } + // add other shapes here... + default: + SAL_INFO("xmloff", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement)); + break; + } + + if( pContext ) + { + // now parse the attribute list and call the child context for each unknown attribute + for(auto& aIter : *xCombinedAttrList) + { + if (!pContext->processAttribute( aIter )) + SAL_INFO("xmloff", "unknown attribute " << SvXMLImport::getPrefixAndNameFromToken(aIter.getToken()) << " value=" << aIter.toString()); + } + } + + return pContext; +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > XMLShapeImportHelper::CreateFrameChildContext( + SvXMLImportContext *pThisContext, + sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList>& xAttrList ) +{ + css::uno::Reference< css::xml::sax::XFastContextHandler > xContext; + SdXMLFrameShapeContext *pFrameContext = dynamic_cast<SdXMLFrameShapeContext*>( pThisContext ); + if (pFrameContext) + xContext = pFrameContext->createFastChildContext( nElement, xAttrList ); + + if (!xContext) + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + return xContext; +} + +/** this function is called whenever the implementation classes like to add this new + shape to the given XShapes. +*/ +void XMLShapeImportHelper::addShape( uno::Reference< drawing::XShape >& rShape, + const uno::Reference< xml::sax::XFastAttributeList >&, + uno::Reference< drawing::XShapes >& rShapes) +{ + if( rShape.is() && rShapes.is() ) + { + // add new shape to parent + rShapes->add( rShape ); + + uno::Reference<beans::XPropertySet> xPropertySet(rShape, uno::UNO_QUERY); + if (xPropertySet.is()) + { + xPropertySet->setPropertyValue("HandlePathObjScale", uno::Any(true)); + } + } +} + +/** this function is called whenever the implementation classes have finished importing + a shape to the given XShapes. The shape is already inserted into its XShapes and + all properties and styles are set. +*/ +void XMLShapeImportHelper::finishShape( + css::uno::Reference< css::drawing::XShape >& rShape, + const css::uno::Reference< css::xml::sax::XFastAttributeList >&, + css::uno::Reference< css::drawing::XShapes >&) +{ + /* Set property <PositionLayoutDir> + to <PositionInHoriL2R>, if it exists and the import states that + the shape positioning attributes are in horizontal left-to-right + layout. This is the case for the OpenOffice.org file format. + This setting is done for Writer documents, because the property + only exists at service css::text::Shape - the Writer + UNO service for shapes. + The value indicates that the positioning attributes are given + in horizontal left-to-right layout. The property is evaluated + during the first positioning of the shape in order to convert + the shape position given in the OpenOffice.org file format to + the one for the OASIS Open Office file format. (#i28749#, #i36248#) + */ + uno::Reference< beans::XPropertySet > xPropSet(rShape, uno::UNO_QUERY); + if ( xPropSet.is() ) + { + if ( mrImporter.IsShapePositionInHoriL2R() && + xPropSet->getPropertySetInfo()->hasPropertyByName( + "PositionLayoutDir") ) + { + uno::Any aPosLayoutDir; + aPosLayoutDir <<= text::PositionLayoutDir::PositionInHoriL2R; + xPropSet->setPropertyValue( "PositionLayoutDir", aPosLayoutDir ); + } + } +} + +namespace { + +// helper functions for z-order sorting +struct ZOrderHint +{ + sal_Int32 nIs; + sal_Int32 nShould; + /// The hint is for this shape. + uno::Reference<drawing::XShape> xShape; + + bool operator<(const ZOrderHint& rComp) const { return nShould < rComp.nShould; } +}; + +// a) handle z-order of group contents after it has been imported +// b) apply group events over group contents after it has been imported +class ShapeGroupContext +{ +public: + uno::Reference< drawing::XShapes > mxShapes; + std::vector<SdXMLEventContextData> maEventData; + vector<ZOrderHint> maZOrderList; + vector<ZOrderHint> maUnsortedList; + + sal_Int32 mnCurrentZ; + std::shared_ptr<ShapeGroupContext> mpParentContext; + + ShapeGroupContext( uno::Reference< drawing::XShapes > const & rShapes, std::shared_ptr<ShapeGroupContext> pParentContext ); + + void popGroupAndPostProcess(); +private: + void moveShape( sal_Int32 nSourcePos, sal_Int32 nDestPos ); +}; + +} + +ShapeGroupContext::ShapeGroupContext( uno::Reference< drawing::XShapes > const & rShapes, std::shared_ptr<ShapeGroupContext> pParentContext ) +: mxShapes( rShapes ), mnCurrentZ( 0 ), mpParentContext( std::move(pParentContext) ) +{ +} + +void ShapeGroupContext::moveShape( sal_Int32 nSourcePos, sal_Int32 nDestPos ) +{ + uno::Any aAny( mxShapes->getByIndex( nSourcePos ) ); + uno::Reference< beans::XPropertySet > xPropSet; + aAny >>= xPropSet; + + if( !(xPropSet.is() && xPropSet->getPropertySetInfo()->hasPropertyByName( "ZOrder" )) ) + return; + + xPropSet->setPropertyValue( "ZOrder", uno::Any(nDestPos) ); + + for( ZOrderHint& rHint : maZOrderList ) + { + if( rHint.nIs < nSourcePos ) + { + DBG_ASSERT(rHint.nIs >= nDestPos, "Shape sorting failed" ); + rHint.nIs++; + } + } + + for( ZOrderHint& rHint : maUnsortedList ) + { + if( rHint.nIs < nSourcePos ) + { + SAL_WARN_IF( rHint.nIs < nDestPos, "xmloff", "shape sorting failed" ); + rHint.nIs++; + } + } +} + +// sort shapes +void ShapeGroupContext::popGroupAndPostProcess() +{ + if (!maEventData.empty()) + { + // tdf#127791 wait until a group is popped to set its event data + for (auto& event : maEventData) + event.ApplyProperties(); + maEventData.clear(); + } + + // only do something if we have shapes to sort + if( maZOrderList.empty() ) + return; + + // check if there are more shapes than inserted with ::shapeWithZIndexAdded() + // This can happen if there where already shapes on the page before import + // Since the writer may delete some of this shapes during import, we need + // to do this here and not in our c'tor anymore + + // check if we have more shapes than we know of + sal_Int32 nCount = mxShapes->getCount(); + + nCount -= maZOrderList.size(); + nCount -= maUnsortedList.size(); + + if( nCount > 0 ) + { + // first update offsets of added shapes + for (ZOrderHint& rHint : maZOrderList) + rHint.nIs += nCount; + for (ZOrderHint& rHint : maUnsortedList) + rHint.nIs += nCount; + + // second add the already existing shapes in the unsorted list + ZOrderHint aNewHint; + do + { + nCount--; + + aNewHint.nIs = nCount; + aNewHint.nShould = -1; + + maUnsortedList.insert(maUnsortedList.begin(), aNewHint); + } + while( nCount ); + } + + bool bSorted = std::is_sorted(maZOrderList.begin(), maZOrderList.end(), + [](const ZOrderHint& rLeft, const ZOrderHint& rRight) + { return rLeft.nShould < rRight.nShould; } ); + + if (bSorted) + return; // nothin' to do + + // sort z-ordered shapes by nShould field + std::sort(maZOrderList.begin(), maZOrderList.end()); + + uno::Reference<drawing::XShapes3> xShapes3(mxShapes, uno::UNO_QUERY); + if( xShapes3.is()) + { + uno::Sequence<sal_Int32> aNewOrder(maZOrderList.size() + maUnsortedList.size()); + auto pNewOrder = aNewOrder.getArray(); + sal_Int32 nIndex = 0; + + for (const ZOrderHint& rHint : maZOrderList) + { + // fill in the gaps from unordered list + for (vector<ZOrderHint>::iterator aIt = maUnsortedList.begin(); aIt != maUnsortedList.end() && nIndex < rHint.nShould; ) + { + pNewOrder[nIndex++] = (*aIt).nIs; + aIt = maUnsortedList.erase(aIt); + } + + pNewOrder[nIndex] = rHint.nIs; + nIndex++; + } + + try + { + xShapes3->sort(aNewOrder); + maZOrderList.clear(); + return; + } + catch (const css::lang::IllegalArgumentException& /*e*/) + {} + } + + // this is the current index, all shapes before that + // index are finished + sal_Int32 nIndex = 0; + for (const ZOrderHint& rHint : maZOrderList) + { + for (vector<ZOrderHint>::iterator aIt = maUnsortedList.begin(); aIt != maUnsortedList.end() && nIndex < rHint.nShould; ) + { + moveShape( (*aIt).nIs, nIndex++ ); + aIt = maUnsortedList.erase(aIt); + + } + + if(rHint.nIs != nIndex ) + moveShape( rHint.nIs, nIndex ); + + nIndex++; + } + maZOrderList.clear(); +} + +void XMLShapeImportHelper::pushGroupForPostProcessing( uno::Reference< drawing::XShapes >& rShapes ) +{ + mpImpl->mpGroupContext = std::make_shared<ShapeGroupContext>( rShapes, mpImpl->mpGroupContext ); +} + +void XMLShapeImportHelper::addShapeEvents(SdXMLEventContextData& rData) +{ + if (mpImpl->mpGroupContext && mpImpl->mpGroupContext->mxShapes == rData.mxShape) + { + // tdf#127791 wait until a group is popped to set its event data so + // that the events are applied to all its children, which are not available + // at the start of the group tag + mpImpl->mpGroupContext->maEventData.push_back(rData); + } + else + rData.ApplyProperties(); +} + +void XMLShapeImportHelper::popGroupAndPostProcess() +{ + SAL_WARN_IF( !mpImpl->mpGroupContext, "xmloff", "No context to sort!" ); + if( !mpImpl->mpGroupContext ) + return; + + try + { + mpImpl->mpGroupContext->popGroupAndPostProcess(); + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("xmloff", "exception while sorting shapes, sorting failed"); + } + + // put parent on top and drop current context, we are done + mpImpl->mpGroupContext = mpImpl->mpGroupContext->mpParentContext; +} + +void XMLShapeImportHelper::shapeWithZIndexAdded( css::uno::Reference< css::drawing::XShape > const & xShape, sal_Int32 nZIndex ) +{ + if( !mpImpl->mpGroupContext) + return; + + ZOrderHint aNewHint; + aNewHint.nIs = mpImpl->mpGroupContext->mnCurrentZ++; + aNewHint.nShould = nZIndex; + aNewHint.xShape = xShape; + + if( nZIndex == -1 ) + { + // don't care, so add to unsorted list + mpImpl->mpGroupContext->maUnsortedList.push_back(aNewHint); + } + else + { + // insert into sort list + mpImpl->mpGroupContext->maZOrderList.push_back(aNewHint); + } +} + +void XMLShapeImportHelper::shapeRemoved(const uno::Reference<drawing::XShape>& xShape) +{ + auto it = std::find_if(mpImpl->mpGroupContext->maZOrderList.begin(), mpImpl->mpGroupContext->maZOrderList.end(), [&xShape](const ZOrderHint& rHint) + { + return rHint.xShape == xShape; + }); + if (it == mpImpl->mpGroupContext->maZOrderList.end()) + // Part of the unsorted list, nothing to do. + return; + + sal_Int32 nZIndex = it->nIs; + + for (it = mpImpl->mpGroupContext->maZOrderList.begin(); it != mpImpl->mpGroupContext->maZOrderList.end();) + { + if (it->nIs == nZIndex) + { + // This is xShape: remove it and adjust the max of indexes + // accordingly. + it = mpImpl->mpGroupContext->maZOrderList.erase(it); + mpImpl->mpGroupContext->mnCurrentZ--; + continue; + } + else if (it->nIs > nZIndex) + // On top of xShape: adjust actual index to reflect removal. + it->nIs--; + + // On top of or below xShape. + ++it; + } +} + +void XMLShapeImportHelper::addShapeConnection( css::uno::Reference< css::drawing::XShape > const & rConnectorShape, + bool bStart, + const OUString& rDestShapeId, + sal_Int32 nDestGlueId ) +{ + ConnectionHint aHint; + aHint.mxConnector = rConnectorShape; + aHint.bStart = bStart; + aHint.aDestShapeId = rDestShapeId; + aHint.nDestGlueId = nDestGlueId; + + mpImpl->maConnections.push_back( aHint ); +} + +void XMLShapeImportHelper::restoreConnections() +{ + const vector<ConnectionHint>::size_type nCount = mpImpl->maConnections.size(); + for( vector<ConnectionHint>::size_type i = 0; i < nCount; i++ ) + { + ConnectionHint& rHint = mpImpl->maConnections[i]; + uno::Reference< beans::XPropertySet > xConnector( rHint.mxConnector, uno::UNO_QUERY ); + if( xConnector.is() ) + { + // #86637# remember line deltas + uno::Any aLine1Delta; + uno::Any aLine2Delta; + uno::Any aLine3Delta; + OUString aStr1("EdgeLine1Delta"); + OUString aStr2("EdgeLine2Delta"); + OUString aStr3("EdgeLine3Delta"); + aLine1Delta = xConnector->getPropertyValue(aStr1); + aLine2Delta = xConnector->getPropertyValue(aStr2); + aLine3Delta = xConnector->getPropertyValue(aStr3); + + // #86637# simply setting these values WILL force the connector to do + // a new layout promptly. So the line delta values have to be rescued + // and restored around connector changes. + uno::Reference< drawing::XShape > xShape( + mrImporter.getInterfaceToIdentifierMapper().getReference( rHint.aDestShapeId ), uno::UNO_QUERY ); + if( xShape.is() ) + { + if (rHint.bStart) + xConnector->setPropertyValue( gsStartShape, uno::Any(xShape) ); + else + xConnector->setPropertyValue( gsEndShape, uno::Any(xShape) ); + + sal_Int32 nGlueId = rHint.nDestGlueId < 4 ? rHint.nDestGlueId : getGluePointId( xShape, rHint.nDestGlueId ); + if(rHint.bStart) + xConnector->setPropertyValue( gsStartGluePointIndex, uno::Any(nGlueId) ); + else + xConnector->setPropertyValue( gsEndGluePointIndex, uno::Any(nGlueId) ); + } + + // #86637# restore line deltas + xConnector->setPropertyValue(aStr1, aLine1Delta ); + xConnector->setPropertyValue(aStr2, aLine2Delta ); + xConnector->setPropertyValue(aStr3, aLine3Delta ); + } + } + mpImpl->maConnections.clear(); +} + +SvXMLImportPropertyMapper* XMLShapeImportHelper::CreateShapePropMapper( const uno::Reference< frame::XModel>& rModel, SvXMLImport& rImport ) +{ + rtl::Reference< XMLPropertyHandlerFactory > xFactory = new XMLSdPropHdlFactory( rModel, rImport ); + rtl::Reference < XMLPropertySetMapper > xMapper = new XMLShapePropertySetMapper( xFactory, false ); + SvXMLImportPropertyMapper* pResult = new SvXMLImportPropertyMapper( xMapper, rImport ); + + // chain text attributes + pResult->ChainImportMapper( XMLTextImportHelper::CreateParaExtPropMapper( rImport ) ); + return pResult; +} + +/** adds a mapping for a gluepoint identifier from an xml file to the identifier created after inserting + the new gluepoint into the core. The saved mappings can be retrieved by getGluePointId() */ +void XMLShapeImportHelper::addGluePointMapping( css::uno::Reference< css::drawing::XShape > const & xShape, + sal_Int32 nSourceId, sal_Int32 nDestinnationId ) +{ + if( mpPageContext ) + mpPageContext->maShapeGluePointsMap[xShape][nSourceId] = nDestinnationId; +} + +/** moves all current DestinationId's by n */ +void XMLShapeImportHelper::moveGluePointMapping( const css::uno::Reference< css::drawing::XShape >& xShape, const sal_Int32 n ) +{ + if( mpPageContext ) + { + ShapeGluePointsMap::iterator aShapeIter( mpPageContext->maShapeGluePointsMap.find( xShape ) ); + if( aShapeIter != mpPageContext->maShapeGluePointsMap.end() ) + { + for ( auto& rShapeId : (*aShapeIter).second ) + { + if ( rShapeId.second != -1 ) + rShapeId.second += n; + } + } + } +} + +/** retrieves a mapping for a gluepoint identifier from the current xml file to the identifier created after + inserting the new gluepoint into the core. The mapping must be initialized first with addGluePointMapping() */ +sal_Int32 XMLShapeImportHelper::getGluePointId( const css::uno::Reference< css::drawing::XShape >& xShape, sal_Int32 nSourceId ) +{ + if( mpPageContext ) + { + ShapeGluePointsMap::iterator aShapeIter( mpPageContext->maShapeGluePointsMap.find( xShape ) ); + if( aShapeIter != mpPageContext->maShapeGluePointsMap.end() ) + { + GluePointIdMap::iterator aIdIter = (*aShapeIter).second.find(nSourceId); + if( aIdIter != (*aShapeIter).second.end() ) + return (*aIdIter).second; + } + } + + return -1; +} + +/** this method must be calling before the first shape is imported for the given page */ +void XMLShapeImportHelper::startPage( css::uno::Reference< css::drawing::XShapes > const & rShapes ) +{ + const std::shared_ptr<XMLShapeImportPageContextImpl> pOldContext = mpPageContext; + mpPageContext = std::make_shared<XMLShapeImportPageContextImpl>(); + mpPageContext->mpNext = pOldContext; + mpPageContext->mxShapes = rShapes; +} + +/** this method must be calling after the last shape is imported for the given page */ +void XMLShapeImportHelper::endPage( css::uno::Reference< css::drawing::XShapes > const & rShapes ) +{ + SAL_WARN_IF( !mpPageContext || (mpPageContext->mxShapes != rShapes), "xmloff", "wrong call to endPage(), no startPage called or wrong page" ); + if( nullptr == mpPageContext ) + return; + + restoreConnections(); + + mpPageContext = mpPageContext->mpNext; +} + +/** defines if the import should increment the progress bar or not */ +void XMLShapeImportHelper::enableHandleProgressBar() +{ + mpImpl->mbHandleProgressBar = true; +} + +bool XMLShapeImportHelper::IsHandleProgressBarEnabled() const +{ + return mpImpl->mbHandleProgressBar; +} + +/** queries the capability of the current model to create presentation shapes */ +bool XMLShapeImportHelper::IsPresentationShapesSupported() const +{ + return mpImpl->mbIsPresentationShapesSupported; +} + +const rtl::Reference< XMLTableImport >& XMLShapeImportHelper::GetShapeTableImport() +{ + if( !mxShapeTableImport.is() ) + { + rtl::Reference< XMLPropertyHandlerFactory > xFactory( new XMLSdPropHdlFactory( mrImporter.GetModel(), mrImporter ) ); + rtl::Reference< XMLPropertySetMapper > xPropertySetMapper( new XMLShapePropertySetMapper( xFactory, false ) ); + mxShapeTableImport = new XMLTableImport( mrImporter, xPropertySetMapper, xFactory ); + } + + return mxShapeTableImport; +} + +void SvXMLShapeContext::setHyperlink( const OUString& rHyperlink ) +{ + msHyperlink = rHyperlink; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |