Advanced Diagram support: Move Diagram Text information to svx
In a next step to get the Diagram mechanism/ModelData independent from oox, I moved the Text ModelData to svx, using a TextBody struct. This is a 1st move that covers most of what the algorithms to handle Diagram re-layout and other functionality use. This will potentially have to be extended accordingly when missing data is detected. It is potentially much more simple as the oox TextBody, by purpose. Due to functionality using that data I could now massively move more of it to svx. Change-Id: I6d6e6c572f119aeefa4e91eff56f58f3ceb6a31e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132523 Tested-by: Jenkins Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
This commit is contained in:
committed by
Armin Le Grand
parent
a2bcac670e
commit
c79fa460fe
@@ -56,6 +56,9 @@ public:
|
||||
/** Returns the name of the passed property identifier. */
|
||||
static const OUString& getPropertyName( sal_Int32 nPropId );
|
||||
|
||||
/** Returns the property identifier of the passed name. */
|
||||
static sal_Int32 getPropertyId( std::u16string_view sPropName );
|
||||
|
||||
/** Returns true, if the map contains a property with the passed identifier. */
|
||||
bool hasProperty( sal_Int32 nPropId ) const;
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
#include <rtl/ustring.hxx>
|
||||
#include <rtl/ustrbuf.hxx>
|
||||
|
||||
#include <com/sun/star/uno/Sequence.hxx>
|
||||
#include <com/sun/star/beans/PropertyValue.hpp>
|
||||
|
||||
namespace svx::diagram {
|
||||
|
||||
enum SVXCORE_DLLPUBLIC TypeConstant {
|
||||
@@ -66,12 +69,32 @@ struct SVXCORE_DLLPUBLIC Connection
|
||||
|
||||
typedef std::vector< Connection > Connections;
|
||||
|
||||
/** Text and properies for a point
|
||||
* For proof of concept to make TextData available in svx level this
|
||||
* is in a first run pretty simple, but may need to be extended accodingly
|
||||
* up to similar data as in oox::drawingml::TextBody. Pls have a look at
|
||||
* secureDataFromShapeToModelAfterDiagramImport() resp.
|
||||
* restoreDataFromModelToShapeAfterReCreation() on it's usage/purpose
|
||||
*/
|
||||
struct SVXCORE_DLLPUBLIC TextBody
|
||||
{
|
||||
// text from 1st paragraph (1st run)
|
||||
OUString msText;
|
||||
|
||||
// attributes from TextBody::getTextProperties()
|
||||
css::uno::Sequence< css::beans::PropertyValue > maTextProps;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr< TextBody > TextBodyPtr;
|
||||
|
||||
/** A point
|
||||
*/
|
||||
struct SVXCORE_DLLPUBLIC Point
|
||||
{
|
||||
Point();
|
||||
|
||||
TextBodyPtr msTextBody;
|
||||
|
||||
OUString msCnxId;
|
||||
OUString msModelId;
|
||||
OUString msColorTransformCategoryId;
|
||||
@@ -143,7 +166,7 @@ public:
|
||||
virtual ~DiagramData();
|
||||
|
||||
// creates temporary processing data from model data
|
||||
virtual void build(bool bClearOoxShapes) = 0;
|
||||
virtual void buildDiagramDataModel(bool bClearOoxShapes);
|
||||
|
||||
Connections& getConnections() { return maConnections; }
|
||||
Points& getPoints() { return maPoints; }
|
||||
@@ -156,12 +179,12 @@ public:
|
||||
virtual void dump() const = 0;
|
||||
|
||||
OUString getString() const;
|
||||
virtual std::vector<std::pair<OUString, OUString>> getChildren(const OUString& rParentId) const = 0;
|
||||
virtual OUString addNode(const OUString& rText) = 0;
|
||||
std::vector<std::pair<OUString, OUString>> getChildren(const OUString& rParentId) const;
|
||||
OUString addNode(const OUString& rText);
|
||||
bool removeNode(const OUString& rNodeId);
|
||||
|
||||
protected:
|
||||
virtual void getChildrenString(OUStringBuffer& rBuf, const Point* pPoint, sal_Int32 nLevel) const = 0;
|
||||
void getChildrenString(OUStringBuffer& rBuf, const Point* pPoint, sal_Int32 nLevel) const;
|
||||
void addConnection(TypeConstant nType, const OUString& sSourceId, const OUString& sDestId);
|
||||
|
||||
// evtl. existing alternative imported visualization identifier
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "datamodel.hxx"
|
||||
|
||||
#include <rtl/ustrbuf.hxx>
|
||||
#include <sal/log.hxx>
|
||||
#include <drawingml/fillproperties.hxx>
|
||||
@@ -25,10 +26,8 @@
|
||||
#include <drawingml/textparagraph.hxx>
|
||||
#include <drawingml/textrun.hxx>
|
||||
#include <oox/drawingml/shape.hxx>
|
||||
#include <comphelper/xmltools.hxx>
|
||||
|
||||
#include <unordered_set>
|
||||
#include <fstream>
|
||||
|
||||
using namespace ::com::sun::star;
|
||||
|
||||
@@ -55,16 +54,31 @@ Shape* DiagramData::getOrCreateAssociatedShape(const svx::diagram::Point& rPoint
|
||||
return rShapePtr.get();
|
||||
}
|
||||
|
||||
void DiagramData::restoreDataFromModelToShapeAfterReCreation(const svx::diagram::Point& rPoint, Shape& rNewShape) const
|
||||
void DiagramData::restoreDataFromModelToShapeAfterReCreation(const svx::diagram::Point& rPoint, Shape& rNewShape)
|
||||
{
|
||||
// If we did create a new oox::drawingml::Shape, directly apply
|
||||
// available data from the Diagram ModelData to it as preparation
|
||||
|
||||
// This is e.g. the Text, but may get more (styles?)
|
||||
const auto pTextForShape = maPointTextMap.find(rPoint.msModelId);
|
||||
if (pTextForShape != maPointTextMap.end())
|
||||
if(rPoint.msTextBody && !rPoint.msTextBody->msText.isEmpty())
|
||||
{
|
||||
rNewShape.setTextBody(pTextForShape->second);
|
||||
TextBodyPtr aNewTextBody(std::make_shared<TextBody>());
|
||||
rNewShape.setTextBody(aNewTextBody);
|
||||
TextRunPtr pTextRun = std::make_shared<TextRun>();
|
||||
pTextRun->getText() = rPoint.msTextBody->msText;
|
||||
aNewTextBody->addParagraph().addRun(pTextRun);
|
||||
|
||||
if(rPoint.msTextBody->maTextProps.hasElements())
|
||||
{
|
||||
oox::PropertyMap& rTargetMap(aNewTextBody->getTextProperties().maPropertyMap);
|
||||
|
||||
for (auto const& prop : rPoint.msTextBody->maTextProps)
|
||||
{
|
||||
sal_Int32 nPropId(oox::PropertyMap::getPropertyId(prop.Name));
|
||||
if(nPropId > 0)
|
||||
rTargetMap.setAnyProperty(nPropId, prop.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,18 +93,35 @@ void DiagramData::secureDataFromShapeToModelAfterDiagramImport()
|
||||
{
|
||||
Shape* pShapeCandidate(getOrCreateAssociatedShape(point));
|
||||
|
||||
if(nullptr != pShapeCandidate
|
||||
&& pShapeCandidate->getTextBody()
|
||||
&& !pShapeCandidate->getTextBody()->isEmpty())
|
||||
if(nullptr != pShapeCandidate)
|
||||
{
|
||||
maPointTextMap[point.msModelId] = pShapeCandidate->getTextBody();
|
||||
if(pShapeCandidate->getTextBody() && !pShapeCandidate->getTextBody()->isEmpty())
|
||||
{
|
||||
point.msTextBody = std::make_shared<svx::diagram::TextBody>();
|
||||
point.msTextBody->msText = pShapeCandidate->getTextBody()->toString();
|
||||
point.msTextBody->maTextProps = pShapeCandidate->getTextBody()->getTextProperties().maPropertyMap.makePropertyValueSequence();
|
||||
}
|
||||
|
||||
// At this place a mechanism to find missing data should be added:
|
||||
// Create a Shape from so-far secured data & compare it with the
|
||||
// imported one. Report differences to allow extending the mechanism
|
||||
// more easily.
|
||||
#ifdef DBG_UTIL
|
||||
// The oiginal is pShapeCandidate, re-create potential new oox::drawingml::Shape
|
||||
// as aNew to be able to compare these
|
||||
ShapePtr aNew(std::make_shared<Shape>());
|
||||
restoreDataFromModelToShapeAfterReCreation(point, *aNew);
|
||||
|
||||
// Unfortunately oox::drawingml::Shape has no operator==. I tried to add
|
||||
// one, but that is too expensive. I stopped at oox::drawingml::Color.
|
||||
// To compare it is necessary to use the debugger, or for single aspects
|
||||
// of the oox data it might be possible to call local dump() methods at
|
||||
// both instances to compare them/their output
|
||||
|
||||
// bool bSame(aNew.get() == pShapeCandidate);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// At this place a mechanism to find missing data should be added:
|
||||
// Create a Shape from so-far secured data & compare it with the
|
||||
// imported one. Report differences to allow extending the mechanism
|
||||
// more easily.
|
||||
}
|
||||
|
||||
DiagramData::DiagramData()
|
||||
@@ -132,341 +163,29 @@ void DiagramData::dump() const
|
||||
Point_dump(rPoint, getOrCreateAssociatedShape(rPoint));
|
||||
}
|
||||
|
||||
void DiagramData::getChildrenString(
|
||||
OUStringBuffer& rBuf,
|
||||
const svx::diagram::Point* pPoint,
|
||||
sal_Int32 nLevel) const
|
||||
void DiagramData::buildDiagramDataModel(bool bClearOoxShapes)
|
||||
{
|
||||
if (!pPoint)
|
||||
return;
|
||||
|
||||
Shape* pShape(getOrCreateAssociatedShape(*pPoint));
|
||||
if(!pShape)
|
||||
return;
|
||||
|
||||
if (nLevel > 0)
|
||||
{
|
||||
for (sal_Int32 i = 0; i < nLevel-1; i++)
|
||||
rBuf.append('\t');
|
||||
rBuf.append('+');
|
||||
rBuf.append(' ');
|
||||
if(pShape->getTextBody())
|
||||
rBuf.append(pShape->getTextBody()->toString());
|
||||
rBuf.append('\n');
|
||||
}
|
||||
|
||||
std::vector< const svx::diagram::Point* > aChildren;
|
||||
for (const auto& rCxn : maConnections)
|
||||
if (rCxn.mnXMLType == svx::diagram::TypeConstant::XML_parOf && rCxn.msSourceId == pPoint->msModelId)
|
||||
{
|
||||
if (rCxn.mnSourceOrder >= static_cast<sal_Int32>(aChildren.size()))
|
||||
aChildren.resize(rCxn.mnSourceOrder + 1);
|
||||
const auto pChild = maPointNameMap.find(rCxn.msDestId);
|
||||
if (pChild != maPointNameMap.end())
|
||||
aChildren[rCxn.mnSourceOrder] = pChild->second;
|
||||
}
|
||||
|
||||
for (auto pChild : aChildren)
|
||||
getChildrenString(rBuf, pChild, nLevel + 1);
|
||||
}
|
||||
|
||||
std::vector<std::pair<OUString, OUString>> DiagramData::getChildren(const OUString& rParentId) const
|
||||
{
|
||||
const OUString sModelId = rParentId.isEmpty() ? getRootPoint()->msModelId : rParentId;
|
||||
std::vector<std::pair<OUString, OUString>> aChildren;
|
||||
for (const auto& rCxn : maConnections)
|
||||
if (rCxn.mnXMLType == svx::diagram::TypeConstant::XML_parOf && rCxn.msSourceId == sModelId)
|
||||
{
|
||||
if (rCxn.mnSourceOrder >= static_cast<sal_Int32>(aChildren.size()))
|
||||
aChildren.resize(rCxn.mnSourceOrder + 1);
|
||||
const auto pChild = maPointNameMap.find(rCxn.msDestId);
|
||||
if (pChild != maPointNameMap.end())
|
||||
{
|
||||
Shape* pShape(getOrCreateAssociatedShape(*(pChild->second)));
|
||||
aChildren[rCxn.mnSourceOrder] = std::make_pair(
|
||||
pChild->second->msModelId,
|
||||
nullptr != pShape && pShape->getTextBody() ? pShape->getTextBody()->toString() : OUString());
|
||||
}
|
||||
}
|
||||
|
||||
// HACK: empty items shouldn't appear there
|
||||
aChildren.erase(std::remove_if(aChildren.begin(), aChildren.end(),
|
||||
[](const std::pair<OUString, OUString>& aItem) { return aItem.first.isEmpty(); }),
|
||||
aChildren.end());
|
||||
|
||||
return aChildren;
|
||||
}
|
||||
|
||||
OUString DiagramData::addNode(const OUString& rText)
|
||||
{
|
||||
const svx::diagram::Point& rDataRoot = *getRootPoint();
|
||||
OUString sPresRoot;
|
||||
for (const auto& aCxn : maConnections)
|
||||
if (aCxn.mnXMLType == svx::diagram::TypeConstant::XML_presOf && aCxn.msSourceId == rDataRoot.msModelId)
|
||||
sPresRoot = aCxn.msDestId;
|
||||
|
||||
if (sPresRoot.isEmpty())
|
||||
return OUString();
|
||||
|
||||
OUString sNewNodeId = OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8);
|
||||
|
||||
svx::diagram::Point aDataPoint;
|
||||
aDataPoint.mnXMLType = svx::diagram::TypeConstant::XML_node;
|
||||
aDataPoint.msModelId = sNewNodeId;
|
||||
|
||||
Shape* pShape(getOrCreateAssociatedShape(aDataPoint, true));
|
||||
pShape->setTextBody(std::make_shared<TextBody>());
|
||||
TextRunPtr pTextRun = std::make_shared<TextRun>();
|
||||
pTextRun->getText() = rText;
|
||||
pShape->getTextBody()->addParagraph().addRun(pTextRun);
|
||||
|
||||
OUString sDataSibling;
|
||||
for (const auto& aCxn : maConnections)
|
||||
if (aCxn.mnXMLType == svx::diagram::TypeConstant::XML_parOf && aCxn.msSourceId == rDataRoot.msModelId)
|
||||
sDataSibling = aCxn.msDestId;
|
||||
|
||||
OUString sPresSibling;
|
||||
for (const auto& aCxn : maConnections)
|
||||
if (aCxn.mnXMLType == svx::diagram::TypeConstant::XML_presOf && aCxn.msSourceId == sDataSibling)
|
||||
sPresSibling = aCxn.msDestId;
|
||||
|
||||
svx::diagram::Point aPresPoint;
|
||||
aPresPoint.mnXMLType = svx::diagram::TypeConstant::XML_pres;
|
||||
aPresPoint.msModelId = OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8);
|
||||
|
||||
// create pesPoint shape
|
||||
getOrCreateAssociatedShape(aPresPoint, true);
|
||||
|
||||
aPresPoint.msPresentationAssociationId = aDataPoint.msModelId;
|
||||
if (!sPresSibling.isEmpty())
|
||||
{
|
||||
// no idea where to get these values from, so copy from previous sibling
|
||||
const svx::diagram::Point* pSiblingPoint = maPointNameMap[sPresSibling];
|
||||
aPresPoint.msPresentationLayoutName = pSiblingPoint->msPresentationLayoutName;
|
||||
aPresPoint.msPresentationLayoutStyleLabel = pSiblingPoint->msPresentationLayoutStyleLabel;
|
||||
aPresPoint.mnLayoutStyleIndex = pSiblingPoint->mnLayoutStyleIndex;
|
||||
aPresPoint.mnLayoutStyleCount = pSiblingPoint->mnLayoutStyleCount;
|
||||
}
|
||||
|
||||
addConnection(svx::diagram::TypeConstant::XML_parOf, rDataRoot.msModelId, aDataPoint.msModelId);
|
||||
addConnection(svx::diagram::TypeConstant::XML_presParOf, sPresRoot, aPresPoint.msModelId);
|
||||
addConnection(svx::diagram::TypeConstant::XML_presOf, aDataPoint.msModelId, aPresPoint.msModelId);
|
||||
|
||||
// adding at the end, so that references are not invalidated in between
|
||||
maPoints.push_back(aDataPoint);
|
||||
maPoints.push_back(aPresPoint);
|
||||
|
||||
build(true);
|
||||
return sNewNodeId;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_OOX_DIAGRAM
|
||||
OString normalizeDotName( const OUString& rStr )
|
||||
{
|
||||
OUStringBuffer aBuf;
|
||||
aBuf.append('N');
|
||||
|
||||
const sal_Int32 nLen(rStr.getLength());
|
||||
sal_Int32 nCurrIndex(0);
|
||||
while( nCurrIndex < nLen )
|
||||
{
|
||||
const sal_Int32 aChar=rStr.iterateCodePoints(&nCurrIndex);
|
||||
if( aChar != '-' && aChar != '{' && aChar != '}' )
|
||||
aBuf.append((sal_Unicode)aChar);
|
||||
}
|
||||
|
||||
return OUStringToOString(aBuf.makeStringAndClear(),
|
||||
RTL_TEXTENCODING_UTF8);
|
||||
}
|
||||
#endif
|
||||
|
||||
static sal_Int32 calcDepth( std::u16string_view rNodeName,
|
||||
const svx::diagram::Connections& rCnx )
|
||||
{
|
||||
// find length of longest path in 'isChild' graph, ending with rNodeName
|
||||
for (auto const& elem : rCnx)
|
||||
{
|
||||
if( !elem.msParTransId.isEmpty() &&
|
||||
!elem.msSibTransId.isEmpty() &&
|
||||
!elem.msSourceId.isEmpty() &&
|
||||
!elem.msDestId.isEmpty() &&
|
||||
elem.mnXMLType == svx::diagram::TypeConstant::XML_parOf &&
|
||||
rNodeName == elem.msDestId )
|
||||
{
|
||||
return calcDepth(elem.msSourceId, rCnx) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DiagramData::build(bool bClearOoxShapes)
|
||||
{
|
||||
// Delete/remove all existing oox::drawingml::Shape
|
||||
if(bClearOoxShapes)
|
||||
{
|
||||
// Delete/remove all existing oox::drawingml::Shape
|
||||
maPointShapeMap.clear();
|
||||
}
|
||||
|
||||
// build name-object maps
|
||||
maPointNameMap.clear();
|
||||
maPointsPresNameMap.clear();
|
||||
maConnectionNameMap.clear();
|
||||
maPresOfNameMap.clear();
|
||||
// call parent
|
||||
svx::diagram::DiagramData::buildDiagramDataModel(bClearOoxShapes);
|
||||
|
||||
#ifdef DEBUG_OOX_DIAGRAM
|
||||
std::ofstream output("tree.dot");
|
||||
|
||||
output << "digraph datatree {" << std::endl;
|
||||
#endif
|
||||
svx::diagram::Points& rPoints = getPoints();
|
||||
for (auto & point : rPoints)
|
||||
if(bClearOoxShapes)
|
||||
{
|
||||
#ifdef DEBUG_OOX_DIAGRAM
|
||||
output << "\t"
|
||||
<< normalizeDotName(point.msModelId).getStr()
|
||||
<< "[";
|
||||
// re-create all existing oox::drawingml::Shape
|
||||
svx::diagram::Points& rPoints = getPoints();
|
||||
|
||||
if( !point.msPresentationLayoutName.isEmpty() )
|
||||
output << "label=\""
|
||||
<< OUStringToOString(
|
||||
point.msPresentationLayoutName,
|
||||
RTL_TEXTENCODING_UTF8).getStr() << "\", ";
|
||||
else
|
||||
output << "label=\""
|
||||
<< OUStringToOString(
|
||||
point.msModelId,
|
||||
RTL_TEXTENCODING_UTF8).getStr() << "\", ";
|
||||
|
||||
switch( point.mnType )
|
||||
for (auto & point : rPoints)
|
||||
{
|
||||
case XML_doc: output << "style=filled, color=red"; break;
|
||||
case XML_asst: output << "style=filled, color=green"; break;
|
||||
default:
|
||||
case XML_node: output << "style=filled, color=blue"; break;
|
||||
case XML_pres: output << "style=filled, color=yellow"; break;
|
||||
case XML_parTrans: output << "color=grey"; break;
|
||||
case XML_sibTrans: output << " "; break;
|
||||
}
|
||||
|
||||
output << "];" << std::endl;
|
||||
#endif
|
||||
|
||||
// Create/get shape. Re-create here, that may also set needed
|
||||
// and available data from the Diagram ModelData at the Shape
|
||||
Shape* pShape(getOrCreateAssociatedShape(point, true));
|
||||
|
||||
// does currpoint have any text set?
|
||||
if( nullptr != pShape && pShape->getTextBody() && !pShape->getTextBody()->isEmpty() )
|
||||
{
|
||||
#ifdef DEBUG_OOX_DIAGRAM
|
||||
static sal_Int32 nCount=0;
|
||||
output << "\t"
|
||||
<< "textNode" << nCount
|
||||
<< " ["
|
||||
<< "label=\""
|
||||
<< OUStringToOString(
|
||||
pShape->getTextBody()->toString(),
|
||||
RTL_TEXTENCODING_UTF8).getStr()
|
||||
<< "\"" << "];" << std::endl;
|
||||
output << "\t"
|
||||
<< normalizeDotName(point.msModelId).getStr()
|
||||
<< " -> "
|
||||
<< "textNode" << nCount++
|
||||
<< ";" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
const bool bInserted1 = getPointNameMap().insert(
|
||||
std::make_pair(point.msModelId,&point)).second;
|
||||
|
||||
SAL_WARN_IF(!bInserted1, "oox.drawingml", "DiagramData::build(): non-unique point model id");
|
||||
|
||||
if( !point.msPresentationLayoutName.isEmpty() )
|
||||
{
|
||||
DiagramData::PointsNameMap::value_type::second_type& rVec=
|
||||
getPointsPresNameMap()[point.msPresentationLayoutName];
|
||||
rVec.push_back(&point);
|
||||
// Create/get shape. Re-create here, that may also set needed
|
||||
// and available data from the Diagram ModelData at the Shape
|
||||
getOrCreateAssociatedShape(point, true);
|
||||
}
|
||||
}
|
||||
|
||||
const svx::diagram::Connections& rConnections = getConnections();
|
||||
for (auto const& connection : rConnections)
|
||||
{
|
||||
#ifdef DEBUG_OOX_DIAGRAM
|
||||
if( !connection.msParTransId.isEmpty() ||
|
||||
!connection.msSibTransId.isEmpty() )
|
||||
{
|
||||
if( !connection.msSourceId.isEmpty() ||
|
||||
!connection.msDestId.isEmpty() )
|
||||
{
|
||||
output << "\t"
|
||||
<< normalizeDotName(connection.msSourceId).getStr()
|
||||
<< " -> "
|
||||
<< normalizeDotName(connection.msParTransId).getStr()
|
||||
<< " -> "
|
||||
<< normalizeDotName(connection.msSibTransId).getStr()
|
||||
<< " -> "
|
||||
<< normalizeDotName(connection.msDestId).getStr()
|
||||
<< " [style=dotted,"
|
||||
<< ((connection.mnType == XML_presOf) ? " color=red, " : ((connection.mnType == XML_presParOf) ? " color=green, " : " "))
|
||||
<< "label=\""
|
||||
<< OUStringToOString(connection.msModelId,
|
||||
RTL_TEXTENCODING_UTF8 ).getStr()
|
||||
<< "\"];" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
output << "\t"
|
||||
<< normalizeDotName(connection.msParTransId).getStr()
|
||||
<< " -> "
|
||||
<< normalizeDotName(connection.msSibTransId).getStr()
|
||||
<< " ["
|
||||
<< ((connection.mnType == XML_presOf) ? " color=red, " : ((connection.mnType == XML_presParOf) ? " color=green, " : " "))
|
||||
<< "label=\""
|
||||
<< OUStringToOString(connection.msModelId,
|
||||
RTL_TEXTENCODING_UTF8 ).getStr()
|
||||
<< "\"];" << std::endl;
|
||||
}
|
||||
}
|
||||
else if( !connection.msSourceId.isEmpty() ||
|
||||
!connection.msDestId.isEmpty() )
|
||||
output << "\t"
|
||||
<< normalizeDotName(connection.msSourceId).getStr()
|
||||
<< " -> "
|
||||
<< normalizeDotName(connection.msDestId).getStr()
|
||||
<< " [label=\""
|
||||
<< OUStringToOString(connection.msModelId,
|
||||
RTL_TEXTENCODING_UTF8 ).getStr()
|
||||
<< ((connection.mnType == XML_presOf) ? "\", color=red]" : ((connection.mnType == XML_presParOf) ? "\", color=green]" : "\"]"))
|
||||
<< ";" << std::endl;
|
||||
#endif
|
||||
|
||||
const bool bInserted1 = maConnectionNameMap.insert(
|
||||
std::make_pair(connection.msModelId,&connection)).second;
|
||||
|
||||
SAL_WARN_IF(!bInserted1, "oox.drawingml", "DiagramData::build(): non-unique connection model id");
|
||||
|
||||
if( connection.mnXMLType == svx::diagram::TypeConstant::XML_presOf )
|
||||
{
|
||||
DiagramData::StringMap::value_type::second_type& rVec = getPresOfNameMap()[connection.msDestId];
|
||||
rVec[connection.mnDestOrder] = { connection.msSourceId, sal_Int32(0) };
|
||||
}
|
||||
}
|
||||
|
||||
// assign outline levels
|
||||
DiagramData::StringMap& rStringMap = getPresOfNameMap();
|
||||
for (auto & elemPresOf : rStringMap)
|
||||
{
|
||||
for (auto & elem : elemPresOf.second)
|
||||
{
|
||||
const sal_Int32 nDepth = calcDepth(elem.second.msSourceId, getConnections());
|
||||
elem.second.mnDepth = nDepth != 0 ? nDepth : -1;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG_OOX_DIAGRAM
|
||||
output << "}" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,35 +37,27 @@ class DiagramData : public svx::diagram::DiagramData
|
||||
{
|
||||
public:
|
||||
typedef std::map< OUString, ShapePtr > PointShapeMap;
|
||||
typedef std::map< OUString, TextBodyPtr > PointTextMap;
|
||||
|
||||
DiagramData();
|
||||
virtual ~DiagramData();
|
||||
|
||||
// creates temporary processing data from model data
|
||||
virtual void build(bool bClearOoxShapes);
|
||||
virtual void buildDiagramDataModel(bool bClearOoxShapes);
|
||||
|
||||
FillPropertiesPtr& getFillProperties() { return mpFillProperties; }
|
||||
virtual void dump() const;
|
||||
|
||||
virtual std::vector<std::pair<OUString, OUString>> getChildren(const OUString& rParentId) const;
|
||||
virtual OUString addNode(const OUString& rText);
|
||||
|
||||
Shape* getOrCreateAssociatedShape(const svx::diagram::Point& rPoint, bool bCreateOnDemand = false) const;
|
||||
|
||||
// get/set data between Diagram DataModel and oox::drawingml::Shape
|
||||
void secureDataFromShapeToModelAfterDiagramImport();
|
||||
void restoreDataFromModelToShapeAfterReCreation(const svx::diagram::Point& rPoint, Shape& rNewShape) const;
|
||||
static void restoreDataFromModelToShapeAfterReCreation(const svx::diagram::Point& rPoint, Shape& rNewShape);
|
||||
|
||||
protected:
|
||||
virtual void getChildrenString(OUStringBuffer& rBuf, const svx::diagram::Point* pPoint, sal_Int32 nLevel) const;
|
||||
|
||||
// The model definition, the parts *only* available in oox. Also look for already
|
||||
// defined ModelData in svx::diagram::DiagramData
|
||||
// - FillStyle
|
||||
// - Texts for oox::drawingml::Points/svx::diagram::Points, associated by ModelId
|
||||
FillPropertiesPtr mpFillProperties;
|
||||
PointTextMap maPointTextMap;
|
||||
|
||||
// temporary processing data, deleted when using build()
|
||||
PointShapeMap maPointShapeMap;
|
||||
|
||||
@@ -410,7 +410,7 @@ void loadDiagram( ShapePtr const & pShape,
|
||||
|
||||
// collect data, init maps
|
||||
// for Diagram import, do - for now - NOT clear all oox::drawingml::Shape
|
||||
pData->build(false);
|
||||
pData->buildDiagramDataModel(false);
|
||||
|
||||
// diagram loaded. now lump together & attach to shape
|
||||
pDiagram->addTo(pShape);
|
||||
|
||||
@@ -224,6 +224,22 @@ const OUString& PropertyMap::getPropertyName( sal_Int32 nPropId )
|
||||
return GetPropertyNameVector()[ nPropId ];
|
||||
}
|
||||
|
||||
sal_Int32 PropertyMap::getPropertyId( std::u16string_view sPropName )
|
||||
{
|
||||
// This may use a std::map to get faster from String to ID in the
|
||||
// future, inside the [0..PROP_COUNT[ entries. Since it is currently
|
||||
// only used for Diagram re-creation I opted fo less mem usage here
|
||||
if(sPropName.empty())
|
||||
return -1;
|
||||
|
||||
const std::vector<OUString>& rVec(GetPropertyNameVector());
|
||||
for(size_t a(0); a < rVec.size(); a++)
|
||||
if(rVec[a] == sPropName)
|
||||
return a;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void PropertyMap::assignAll( const PropertyMap& rPropMap )
|
||||
{
|
||||
for (auto const& prop : rPropMap.maProperties)
|
||||
|
||||
@@ -19,8 +19,10 @@
|
||||
|
||||
#include <unordered_set>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
|
||||
#include <svx/diagram/datamodel.hxx>
|
||||
#include <comphelper/xmltools.hxx>
|
||||
#include <sal/log.hxx>
|
||||
|
||||
namespace svx::diagram {
|
||||
@@ -135,10 +137,128 @@ bool DiagramData::removeNode(const OUString& rNodeId)
|
||||
|
||||
// TODO: fix source/dest order
|
||||
|
||||
build(true);
|
||||
buildDiagramDataModel(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DiagramData::getChildrenString(
|
||||
OUStringBuffer& rBuf,
|
||||
const svx::diagram::Point* pPoint,
|
||||
sal_Int32 nLevel) const
|
||||
{
|
||||
if (!pPoint)
|
||||
return;
|
||||
|
||||
if (nLevel > 0)
|
||||
{
|
||||
for (sal_Int32 i = 0; i < nLevel-1; i++)
|
||||
rBuf.append('\t');
|
||||
rBuf.append('+');
|
||||
rBuf.append(' ');
|
||||
if(pPoint->msTextBody)
|
||||
rBuf.append(pPoint->msTextBody->msText);
|
||||
rBuf.append('\n');
|
||||
}
|
||||
|
||||
std::vector< const svx::diagram::Point* > aChildren;
|
||||
for (const auto& rCxn : maConnections)
|
||||
if (rCxn.mnXMLType == TypeConstant::XML_parOf && rCxn.msSourceId == pPoint->msModelId)
|
||||
{
|
||||
if (rCxn.mnSourceOrder >= static_cast<sal_Int32>(aChildren.size()))
|
||||
aChildren.resize(rCxn.mnSourceOrder + 1);
|
||||
const auto pChild = maPointNameMap.find(rCxn.msDestId);
|
||||
if (pChild != maPointNameMap.end())
|
||||
aChildren[rCxn.mnSourceOrder] = pChild->second;
|
||||
}
|
||||
|
||||
for (auto pChild : aChildren)
|
||||
getChildrenString(rBuf, pChild, nLevel + 1);
|
||||
}
|
||||
|
||||
std::vector<std::pair<OUString, OUString>> DiagramData::getChildren(const OUString& rParentId) const
|
||||
{
|
||||
const OUString sModelId = rParentId.isEmpty() ? getRootPoint()->msModelId : rParentId;
|
||||
std::vector<std::pair<OUString, OUString>> aChildren;
|
||||
for (const auto& rCxn : maConnections)
|
||||
if (rCxn.mnXMLType == TypeConstant::XML_parOf && rCxn.msSourceId == sModelId)
|
||||
{
|
||||
if (rCxn.mnSourceOrder >= static_cast<sal_Int32>(aChildren.size()))
|
||||
aChildren.resize(rCxn.mnSourceOrder + 1);
|
||||
const auto pChild = maPointNameMap.find(rCxn.msDestId);
|
||||
if (pChild != maPointNameMap.end())
|
||||
{
|
||||
aChildren[rCxn.mnSourceOrder] = std::make_pair(
|
||||
pChild->second->msModelId,
|
||||
pChild->second->msTextBody ? pChild->second->msTextBody->msText : OUString());
|
||||
}
|
||||
}
|
||||
|
||||
// HACK: empty items shouldn't appear there
|
||||
aChildren.erase(std::remove_if(aChildren.begin(), aChildren.end(),
|
||||
[](const std::pair<OUString, OUString>& aItem) { return aItem.first.isEmpty(); }),
|
||||
aChildren.end());
|
||||
|
||||
return aChildren;
|
||||
}
|
||||
|
||||
OUString DiagramData::addNode(const OUString& rText)
|
||||
{
|
||||
const svx::diagram::Point& rDataRoot = *getRootPoint();
|
||||
OUString sPresRoot;
|
||||
for (const auto& aCxn : maConnections)
|
||||
if (aCxn.mnXMLType == TypeConstant::XML_presOf && aCxn.msSourceId == rDataRoot.msModelId)
|
||||
sPresRoot = aCxn.msDestId;
|
||||
|
||||
if (sPresRoot.isEmpty())
|
||||
return OUString();
|
||||
|
||||
OUString sNewNodeId = OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8);
|
||||
|
||||
svx::diagram::Point aDataPoint;
|
||||
aDataPoint.mnXMLType = TypeConstant::XML_node;
|
||||
aDataPoint.msModelId = sNewNodeId;
|
||||
aDataPoint.msTextBody = std::make_shared<TextBody>();
|
||||
aDataPoint.msTextBody->msText = rText;
|
||||
|
||||
OUString sDataSibling;
|
||||
for (const auto& aCxn : maConnections)
|
||||
if (aCxn.mnXMLType == TypeConstant::XML_parOf && aCxn.msSourceId == rDataRoot.msModelId)
|
||||
sDataSibling = aCxn.msDestId;
|
||||
|
||||
OUString sPresSibling;
|
||||
for (const auto& aCxn : maConnections)
|
||||
if (aCxn.mnXMLType == TypeConstant::XML_presOf && aCxn.msSourceId == sDataSibling)
|
||||
sPresSibling = aCxn.msDestId;
|
||||
|
||||
svx::diagram::Point aPresPoint;
|
||||
aPresPoint.mnXMLType = TypeConstant::XML_pres;
|
||||
aPresPoint.msModelId = OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8);
|
||||
|
||||
aPresPoint.msPresentationAssociationId = aDataPoint.msModelId;
|
||||
if (!sPresSibling.isEmpty())
|
||||
{
|
||||
// no idea where to get these values from, so copy from previous sibling
|
||||
const svx::diagram::Point* pSiblingPoint = maPointNameMap[sPresSibling];
|
||||
aPresPoint.msPresentationLayoutName = pSiblingPoint->msPresentationLayoutName;
|
||||
aPresPoint.msPresentationLayoutStyleLabel = pSiblingPoint->msPresentationLayoutStyleLabel;
|
||||
aPresPoint.mnLayoutStyleIndex = pSiblingPoint->mnLayoutStyleIndex;
|
||||
aPresPoint.mnLayoutStyleCount = pSiblingPoint->mnLayoutStyleCount;
|
||||
}
|
||||
|
||||
addConnection(svx::diagram::TypeConstant::XML_parOf, rDataRoot.msModelId, aDataPoint.msModelId);
|
||||
addConnection(svx::diagram::TypeConstant::XML_presParOf, sPresRoot, aPresPoint.msModelId);
|
||||
addConnection(svx::diagram::TypeConstant::XML_presOf, aDataPoint.msModelId, aPresPoint.msModelId);
|
||||
|
||||
// adding at the end, so that references are not invalidated in between
|
||||
maPoints.push_back(aDataPoint);
|
||||
maPoints.push_back(aPresPoint);
|
||||
|
||||
buildDiagramDataModel(true);
|
||||
|
||||
return sNewNodeId;
|
||||
}
|
||||
|
||||
void DiagramData::addConnection(svx::diagram::TypeConstant nType, const OUString& sSourceId, const OUString& sDestId)
|
||||
{
|
||||
sal_Int32 nMaxOrd = -1;
|
||||
@@ -153,6 +273,206 @@ void DiagramData::addConnection(svx::diagram::TypeConstant nType, const OUString
|
||||
rCxn.mnSourceOrder = nMaxOrd + 1;
|
||||
}
|
||||
|
||||
// #define DEBUG_OOX_DIAGRAM
|
||||
#ifdef DEBUG_OOX_DIAGRAM
|
||||
OString normalizeDotName( const OUString& rStr )
|
||||
{
|
||||
OUStringBuffer aBuf;
|
||||
aBuf.append('N');
|
||||
|
||||
const sal_Int32 nLen(rStr.getLength());
|
||||
sal_Int32 nCurrIndex(0);
|
||||
while( nCurrIndex < nLen )
|
||||
{
|
||||
const sal_Int32 aChar=rStr.iterateCodePoints(&nCurrIndex);
|
||||
if( aChar != '-' && aChar != '{' && aChar != '}' )
|
||||
aBuf.append((sal_Unicode)aChar);
|
||||
}
|
||||
|
||||
return OUStringToOString(aBuf.makeStringAndClear(),
|
||||
RTL_TEXTENCODING_UTF8);
|
||||
}
|
||||
#endif
|
||||
|
||||
static sal_Int32 calcDepth( std::u16string_view rNodeName,
|
||||
const svx::diagram::Connections& rCnx )
|
||||
{
|
||||
// find length of longest path in 'isChild' graph, ending with rNodeName
|
||||
for (auto const& elem : rCnx)
|
||||
{
|
||||
if( !elem.msParTransId.isEmpty() &&
|
||||
!elem.msSibTransId.isEmpty() &&
|
||||
!elem.msSourceId.isEmpty() &&
|
||||
!elem.msDestId.isEmpty() &&
|
||||
elem.mnXMLType == TypeConstant::XML_parOf &&
|
||||
rNodeName == elem.msDestId )
|
||||
{
|
||||
return calcDepth(elem.msSourceId, rCnx) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DiagramData::buildDiagramDataModel(bool /*bClearOoxShapes*/)
|
||||
{
|
||||
// build name-object maps
|
||||
maPointNameMap.clear();
|
||||
maPointsPresNameMap.clear();
|
||||
maConnectionNameMap.clear();
|
||||
maPresOfNameMap.clear();
|
||||
|
||||
#ifdef DEBUG_OOX_DIAGRAM
|
||||
std::ofstream output("tree.dot");
|
||||
|
||||
output << "digraph datatree {" << std::endl;
|
||||
#endif
|
||||
svx::diagram::Points& rPoints = getPoints();
|
||||
for (auto & point : rPoints)
|
||||
{
|
||||
#ifdef DEBUG_OOX_DIAGRAM
|
||||
output << "\t"
|
||||
<< normalizeDotName(point.msModelId).getStr()
|
||||
<< "[";
|
||||
|
||||
if( !point.msPresentationLayoutName.isEmpty() )
|
||||
output << "label=\""
|
||||
<< OUStringToOString(
|
||||
point.msPresentationLayoutName,
|
||||
RTL_TEXTENCODING_UTF8).getStr() << "\", ";
|
||||
else
|
||||
output << "label=\""
|
||||
<< OUStringToOString(
|
||||
point.msModelId,
|
||||
RTL_TEXTENCODING_UTF8).getStr() << "\", ";
|
||||
|
||||
switch( point.mnXMLType )
|
||||
{
|
||||
case TypeConstant::XML_doc: output << "style=filled, color=red"; break;
|
||||
case TypeConstant::XML_asst: output << "style=filled, color=green"; break;
|
||||
default:
|
||||
case TypeConstant::XML_node: output << "style=filled, color=blue"; break;
|
||||
case TypeConstant::XML_pres: output << "style=filled, color=yellow"; break;
|
||||
case TypeConstant::XML_parTrans: output << "color=grey"; break;
|
||||
case TypeConstant::XML_sibTrans: output << " "; break;
|
||||
}
|
||||
|
||||
output << "];" << std::endl;
|
||||
#endif
|
||||
|
||||
// does currpoint have any text set?
|
||||
if(point.msTextBody && !point.msTextBody->msText.isEmpty())
|
||||
{
|
||||
#ifdef DEBUG_OOX_DIAGRAM
|
||||
static sal_Int32 nCount=0;
|
||||
output << "\t"
|
||||
<< "textNode" << nCount
|
||||
<< " ["
|
||||
<< "label=\""
|
||||
<< OUStringToOString(
|
||||
point.msTextBody->msText,
|
||||
RTL_TEXTENCODING_UTF8).getStr()
|
||||
<< "\"" << "];" << std::endl;
|
||||
output << "\t"
|
||||
<< normalizeDotName(point.msModelId).getStr()
|
||||
<< " -> "
|
||||
<< "textNode" << nCount++
|
||||
<< ";" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
const bool bInserted1 = getPointNameMap().insert(
|
||||
std::make_pair(point.msModelId,&point)).second;
|
||||
|
||||
SAL_WARN_IF(!bInserted1, "oox.drawingml", "DiagramData::build(): non-unique point model id");
|
||||
|
||||
if( !point.msPresentationLayoutName.isEmpty() )
|
||||
{
|
||||
DiagramData::PointsNameMap::value_type::second_type& rVec=
|
||||
getPointsPresNameMap()[point.msPresentationLayoutName];
|
||||
rVec.push_back(&point);
|
||||
}
|
||||
}
|
||||
|
||||
const svx::diagram::Connections& rConnections = getConnections();
|
||||
for (auto const& connection : rConnections)
|
||||
{
|
||||
#ifdef DEBUG_OOX_DIAGRAM
|
||||
if( !connection.msParTransId.isEmpty() ||
|
||||
!connection.msSibTransId.isEmpty() )
|
||||
{
|
||||
if( !connection.msSourceId.isEmpty() ||
|
||||
!connection.msDestId.isEmpty() )
|
||||
{
|
||||
output << "\t"
|
||||
<< normalizeDotName(connection.msSourceId).getStr()
|
||||
<< " -> "
|
||||
<< normalizeDotName(connection.msParTransId).getStr()
|
||||
<< " -> "
|
||||
<< normalizeDotName(connection.msSibTransId).getStr()
|
||||
<< " -> "
|
||||
<< normalizeDotName(connection.msDestId).getStr()
|
||||
<< " [style=dotted,"
|
||||
<< ((connection.mnXMLType == TypeConstant::XML_presOf) ? " color=red, " : ((connection.mnXMLType == TypeConstant::XML_presParOf) ? " color=green, " : " "))
|
||||
<< "label=\""
|
||||
<< OUStringToOString(connection.msModelId,
|
||||
RTL_TEXTENCODING_UTF8 ).getStr()
|
||||
<< "\"];" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
output << "\t"
|
||||
<< normalizeDotName(connection.msParTransId).getStr()
|
||||
<< " -> "
|
||||
<< normalizeDotName(connection.msSibTransId).getStr()
|
||||
<< " ["
|
||||
<< ((connection.mnXMLType == TypeConstant::XML_presOf) ? " color=red, " : ((connection.mnXMLType == TypeConstant::XML_presParOf) ? " color=green, " : " "))
|
||||
<< "label=\""
|
||||
<< OUStringToOString(connection.msModelId,
|
||||
RTL_TEXTENCODING_UTF8 ).getStr()
|
||||
<< "\"];" << std::endl;
|
||||
}
|
||||
}
|
||||
else if( !connection.msSourceId.isEmpty() ||
|
||||
!connection.msDestId.isEmpty() )
|
||||
output << "\t"
|
||||
<< normalizeDotName(connection.msSourceId).getStr()
|
||||
<< " -> "
|
||||
<< normalizeDotName(connection.msDestId).getStr()
|
||||
<< " [label=\""
|
||||
<< OUStringToOString(connection.msModelId,
|
||||
RTL_TEXTENCODING_UTF8 ).getStr()
|
||||
<< ((connection.mnXMLType == TypeConstant::XML_presOf) ? "\", color=red]" : ((connection.mnXMLType == TypeConstant::XML_presParOf) ? "\", color=green]" : "\"]"))
|
||||
<< ";" << std::endl;
|
||||
#endif
|
||||
|
||||
const bool bInserted1 = maConnectionNameMap.insert(
|
||||
std::make_pair(connection.msModelId,&connection)).second;
|
||||
|
||||
SAL_WARN_IF(!bInserted1, "oox.drawingml", "DiagramData::build(): non-unique connection model id");
|
||||
|
||||
if( connection.mnXMLType == TypeConstant::XML_presOf )
|
||||
{
|
||||
DiagramData::StringMap::value_type::second_type& rVec = getPresOfNameMap()[connection.msDestId];
|
||||
rVec[connection.mnDestOrder] = { connection.msSourceId, sal_Int32(0) };
|
||||
}
|
||||
}
|
||||
|
||||
// assign outline levels
|
||||
DiagramData::StringMap& rStringMap = getPresOfNameMap();
|
||||
for (auto & elemPresOf : rStringMap)
|
||||
{
|
||||
for (auto & elem : elemPresOf.second)
|
||||
{
|
||||
const sal_Int32 nDepth = calcDepth(elem.second.msSourceId, getConnections());
|
||||
elem.second.mnDepth = nDepth != 0 ? nDepth : -1;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG_OOX_DIAGRAM
|
||||
output << "}" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||
|
||||
Reference in New Issue
Block a user