LibreOffice Module svx (master) 1
datamodel.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <unordered_set>
21#include <algorithm>
22#include <fstream>
23
26#include <sal/log.hxx>
27#include <utility>
28
29namespace svx::diagram {
30
32: mnXMLType( XML_none )
33, mnSourceOrder( 0 )
34, mnDestOrder( 0 )
35{
36}
37
39: msTextBody(std::make_shared< TextBody >())
40, msPointStylePtr(std::make_shared< PointStyle >())
41, mnXMLType(XML_none)
42, mnMaxChildren(-1)
43, mnPreferredChildren(-1)
44, mnDirection(XML_norm)
45, mnResizeHandles(XML_rel)
46, mnCustomAngle(-1)
47, mnPercentageNeighbourWidth(-1)
48, mnPercentageNeighbourHeight(-1)
49, mnPercentageOwnWidth(-1)
50, mnPercentageOwnHeight(-1)
51, mnIncludeAngleScale(-1)
52, mnRadiusScale(-1)
53, mnWidthScale(-1)
54, mnHeightScale(-1)
55, mnWidthOverride(-1)
56, mnHeightOverride(-1)
57, mnLayoutStyleCount(-1)
58, mnLayoutStyleIndex(-1)
59, mbOrgChartEnabled(false)
60, mbBulletEnabled(false)
61, mbCoherent3DOffset(false)
62, mbCustomHorizontalFlip(false)
63, mbCustomVerticalFlip(false)
64, mbCustomText(false)
65, mbIsPlaceholder(false)
66{
67}
68
70{
71}
72
74{
75}
76
78{
79 for (const auto & aCurrPoint : maPoints)
80 if (aCurrPoint.mnXMLType == TypeConstant::XML_doc)
81 return &aCurrPoint;
82
83 SAL_WARN("svx.diagram", "No root point");
84 return nullptr;
85}
86
87OUString DiagramData::getString() const
88{
89 OUStringBuffer aBuf;
90 const Point* pPoint = getRootPoint();
91 getChildrenString(aBuf, pPoint, 0);
92 return aBuf.makeStringAndClear();
93}
94
95bool DiagramData::removeNode(const OUString& rNodeId)
96{
97 // check if it doesn't have children
98 for (const auto& aCxn : maConnections)
99 if (aCxn.mnXMLType == TypeConstant::XML_parOf && aCxn.msSourceId == rNodeId)
100 {
101 SAL_WARN("svx.diagram", "Node has children - can't be removed");
102 return false;
103 }
104
105 Connection aParCxn;
106 for (const auto& aCxn : maConnections)
107 if (aCxn.mnXMLType == TypeConstant::XML_parOf && aCxn.msDestId == rNodeId)
108 aParCxn = aCxn;
109
110 std::unordered_set<OUString> aIdsToRemove;
111 aIdsToRemove.insert(rNodeId);
112 if (!aParCxn.msParTransId.isEmpty())
113 aIdsToRemove.insert(aParCxn.msParTransId);
114 if (!aParCxn.msSibTransId.isEmpty())
115 aIdsToRemove.insert(aParCxn.msSibTransId);
116
117 for (const Point& rPoint : maPoints)
118 if (aIdsToRemove.count(rPoint.msPresentationAssociationId))
119 aIdsToRemove.insert(rPoint.msModelId);
120
121 // insert also transition nodes
122 for (const auto& aCxn : maConnections)
123 if (aIdsToRemove.count(aCxn.msSourceId) || aIdsToRemove.count(aCxn.msDestId))
124 if (!aCxn.msPresId.isEmpty())
125 aIdsToRemove.insert(aCxn.msPresId);
126
127 // remove connections
128 maConnections.erase(std::remove_if(maConnections.begin(), maConnections.end(),
129 [aIdsToRemove](const Connection& rCxn) {
130 return aIdsToRemove.count(rCxn.msSourceId) || aIdsToRemove.count(rCxn.msDestId);
131 }),
132 maConnections.end());
133
134 // remove data and presentation nodes
135 maPoints.erase(std::remove_if(maPoints.begin(), maPoints.end(),
136 [aIdsToRemove](const Point& rPoint) {
137 return aIdsToRemove.count(rPoint.msModelId);
138 }),
139 maPoints.end());
140
141 // TODO: fix source/dest order
142 return true;
143}
144
146: maConnections(std::move(aConnections))
147, maPoints(std::move(aPoints))
148{
149}
150
152{
153 // Just copy all Connections && Points. The shared_ptr data in
154 // Point-entries is no problem, it just continues exiting shared
155 return std::make_shared< DiagramDataState >(maConnections, maPoints);
156}
157
159{
160 if(rState)
161 {
162 maConnections = rState->getConnections();
163 maPoints = rState->getPoints();
164
165 // Reset temporary buffered ModelData association lists & rebuild them
166 // and the Diagram DataModel. Do that here *immediately* to prevent
167 // re-usage of potentially invalid Connection/Point objects
169 }
170}
171
173 OUStringBuffer& rBuf,
174 const svx::diagram::Point* pPoint,
175 sal_Int32 nLevel) const
176{
177 if (!pPoint)
178 return;
179
180 if (nLevel > 0)
181 {
182 for (sal_Int32 i = 0; i < nLevel-1; i++)
183 rBuf.append('\t');
184 rBuf.append('+');
185 rBuf.append(' ');
186 rBuf.append(pPoint->msTextBody->msText);
187 rBuf.append('\n');
188 }
189
190 std::vector< const svx::diagram::Point* > aChildren;
191 for (const auto& rCxn : maConnections)
192 if (rCxn.mnXMLType == TypeConstant::XML_parOf && rCxn.msSourceId == pPoint->msModelId)
193 {
194 if (rCxn.mnSourceOrder >= static_cast<sal_Int32>(aChildren.size()))
195 aChildren.resize(rCxn.mnSourceOrder + 1);
196 const auto pChild = maPointNameMap.find(rCxn.msDestId);
197 if (pChild != maPointNameMap.end())
198 aChildren[rCxn.mnSourceOrder] = pChild->second;
199 }
200
201 for (auto pChild : aChildren)
202 getChildrenString(rBuf, pChild, nLevel + 1);
203}
204
205std::vector<std::pair<OUString, OUString>> DiagramData::getChildren(const OUString& rParentId) const
206{
207 const OUString sModelId = rParentId.isEmpty() ? getRootPoint()->msModelId : rParentId;
208 std::vector<std::pair<OUString, OUString>> aChildren;
209 for (const auto& rCxn : maConnections)
210 if (rCxn.mnXMLType == TypeConstant::XML_parOf && rCxn.msSourceId == sModelId)
211 {
212 if (rCxn.mnSourceOrder >= static_cast<sal_Int32>(aChildren.size()))
213 aChildren.resize(rCxn.mnSourceOrder + 1);
214 const auto pChild = maPointNameMap.find(rCxn.msDestId);
215 if (pChild != maPointNameMap.end())
216 {
217 aChildren[rCxn.mnSourceOrder] = std::make_pair(
218 pChild->second->msModelId,
219 pChild->second->msTextBody->msText);
220 }
221 }
222
223 // HACK: empty items shouldn't appear there
224 aChildren.erase(std::remove_if(aChildren.begin(), aChildren.end(),
225 [](const std::pair<OUString, OUString>& aItem) { return aItem.first.isEmpty(); }),
226 aChildren.end());
227
228 return aChildren;
229}
230
231OUString DiagramData::addNode(const OUString& rText)
232{
233 const svx::diagram::Point& rDataRoot = *getRootPoint();
234 OUString sPresRoot;
235 for (const auto& aCxn : maConnections)
236 if (aCxn.mnXMLType == TypeConstant::XML_presOf && aCxn.msSourceId == rDataRoot.msModelId)
237 sPresRoot = aCxn.msDestId;
238
239 if (sPresRoot.isEmpty())
240 return OUString();
241
242 OUString sNewNodeId = OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8);
243
244 svx::diagram::Point aDataPoint;
246 aDataPoint.msModelId = sNewNodeId;
247 aDataPoint.msTextBody->msText = rText;
248
249 OUString sDataSibling;
250 for (const auto& aCxn : maConnections)
251 if (aCxn.mnXMLType == TypeConstant::XML_parOf && aCxn.msSourceId == rDataRoot.msModelId)
252 sDataSibling = aCxn.msDestId;
253
254 OUString sPresSibling;
255 for (const auto& aCxn : maConnections)
256 if (aCxn.mnXMLType == TypeConstant::XML_presOf && aCxn.msSourceId == sDataSibling)
257 sPresSibling = aCxn.msDestId;
258
259 svx::diagram::Point aPresPoint;
261 aPresPoint.msModelId = OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8);
262
263 aPresPoint.msPresentationAssociationId = aDataPoint.msModelId;
264 if (!sPresSibling.isEmpty())
265 {
266 // no idea where to get these values from, so copy from previous sibling
267 const svx::diagram::Point* pSiblingPoint = maPointNameMap[sPresSibling];
268 aPresPoint.msPresentationLayoutName = pSiblingPoint->msPresentationLayoutName;
270 aPresPoint.mnLayoutStyleIndex = pSiblingPoint->mnLayoutStyleIndex;
271 aPresPoint.mnLayoutStyleCount = pSiblingPoint->mnLayoutStyleCount;
272 }
273
277
278 // adding at the end, so that references are not invalidated in between
279 maPoints.push_back(aDataPoint);
280 maPoints.push_back(aPresPoint);
281
282 return sNewNodeId;
283}
284
285void DiagramData::addConnection(svx::diagram::TypeConstant nType, const OUString& sSourceId, const OUString& sDestId)
286{
287 sal_Int32 nMaxOrd = -1;
288 for (const auto& aCxn : maConnections)
289 if (aCxn.mnXMLType == nType && aCxn.msSourceId == sSourceId)
290 nMaxOrd = std::max(nMaxOrd, aCxn.mnSourceOrder);
291
292 svx::diagram::Connection& rCxn = maConnections.emplace_back();
293 rCxn.mnXMLType = nType;
294 rCxn.msSourceId = sSourceId;
295 rCxn.msDestId = sDestId;
296 rCxn.mnSourceOrder = nMaxOrd + 1;
297}
298
299// #define DEBUG_OOX_DIAGRAM
300#ifdef DEBUG_OOX_DIAGRAM
301OString normalizeDotName( const OUString& rStr )
302{
303 OUStringBuffer aBuf;
304 aBuf.append('N');
305
306 const sal_Int32 nLen(rStr.getLength());
307 sal_Int32 nCurrIndex(0);
308 while( nCurrIndex < nLen )
309 {
310 const sal_Int32 aChar=rStr.iterateCodePoints(&nCurrIndex);
311 if( aChar != '-' && aChar != '{' && aChar != '}' )
312 aBuf.append((sal_Unicode)aChar);
313 }
314
315 return OUStringToOString(aBuf.makeStringAndClear(),
316 RTL_TEXTENCODING_UTF8);
317}
318#endif
319
320static sal_Int32 calcDepth( std::u16string_view rNodeName,
321 const svx::diagram::Connections& rCnx )
322{
323 // find length of longest path in 'isChild' graph, ending with rNodeName
324 for (auto const& elem : rCnx)
325 {
326 if( !elem.msParTransId.isEmpty() &&
327 !elem.msSibTransId.isEmpty() &&
328 !elem.msSourceId.isEmpty() &&
329 !elem.msDestId.isEmpty() &&
330 elem.mnXMLType == TypeConstant::XML_parOf &&
331 rNodeName == elem.msDestId )
332 {
333 return calcDepth(elem.msSourceId, rCnx) + 1;
334 }
335 }
336
337 return 0;
338}
339
340void DiagramData::buildDiagramDataModel(bool /*bClearOoxShapes*/)
341{
342 // build name-object maps
343 maPointNameMap.clear();
344 maPointsPresNameMap.clear();
345 maConnectionNameMap.clear();
346 maPresOfNameMap.clear();
348
349#ifdef DEBUG_OOX_DIAGRAM
350 std::ofstream output("tree.dot");
351
352 output << "digraph datatree {" << std::endl;
353#endif
354 svx::diagram::Points& rPoints = getPoints();
355 for (auto & point : rPoints)
356 {
357#ifdef DEBUG_OOX_DIAGRAM
358 output << "\t"
359 << normalizeDotName(point.msModelId).getStr()
360 << "[";
361
362 if( !point.msPresentationLayoutName.isEmpty() )
363 output << "label=\""
365 point.msPresentationLayoutName,
366 RTL_TEXTENCODING_UTF8).getStr() << "\", ";
367 else
368 output << "label=\""
370 point.msModelId,
371 RTL_TEXTENCODING_UTF8).getStr() << "\", ";
372
373 switch( point.mnXMLType )
374 {
375 case TypeConstant::XML_doc: output << "style=filled, color=red"; break;
376 case TypeConstant::XML_asst: output << "style=filled, color=green"; break;
377 default:
378 case TypeConstant::XML_node: output << "style=filled, color=blue"; break;
379 case TypeConstant::XML_pres: output << "style=filled, color=yellow"; break;
380 case TypeConstant::XML_parTrans: output << "color=grey"; break;
381 case TypeConstant::XML_sibTrans: output << " "; break;
382 }
383
384 output << "];" << std::endl;
385#endif
386
387 // does currpoint have any text set?
388 if(!point.msTextBody->msText.isEmpty())
389 {
390#ifdef DEBUG_OOX_DIAGRAM
391 static sal_Int32 nCount=0;
392 output << "\t"
393 << "textNode" << nCount
394 << " ["
395 << "label=\""
397 point.msTextBody->msText,
398 RTL_TEXTENCODING_UTF8).getStr()
399 << "\"" << "];" << std::endl;
400 output << "\t"
401 << normalizeDotName(point.msModelId).getStr()
402 << " -> "
403 << "textNode" << nCount++
404 << ";" << std::endl;
405#endif
406 }
407
408 const bool bInserted1 = getPointNameMap().insert(
409 std::make_pair(point.msModelId,&point)).second;
410
411 SAL_WARN_IF(!bInserted1, "oox.drawingml", "DiagramData::build(): non-unique point model id");
412
413 if( !point.msPresentationLayoutName.isEmpty() )
414 {
415 DiagramData::PointsNameMap::value_type::second_type& rVec=
416 getPointsPresNameMap()[point.msPresentationLayoutName];
417 rVec.push_back(&point);
418 }
419 }
420
421 const svx::diagram::Connections& rConnections = getConnections();
422 for (auto const& connection : rConnections)
423 {
424#ifdef DEBUG_OOX_DIAGRAM
425 if( !connection.msParTransId.isEmpty() ||
426 !connection.msSibTransId.isEmpty() )
427 {
428 if( !connection.msSourceId.isEmpty() ||
429 !connection.msDestId.isEmpty() )
430 {
431 output << "\t"
432 << normalizeDotName(connection.msSourceId).getStr()
433 << " -> "
434 << normalizeDotName(connection.msParTransId).getStr()
435 << " -> "
436 << normalizeDotName(connection.msSibTransId).getStr()
437 << " -> "
438 << normalizeDotName(connection.msDestId).getStr()
439 << " [style=dotted,"
440 << ((connection.mnXMLType == TypeConstant::XML_presOf) ? " color=red, " : ((connection.mnXMLType == TypeConstant::XML_presParOf) ? " color=green, " : " "))
441 << "label=\""
442 << OUStringToOString(connection.msModelId,
443 RTL_TEXTENCODING_UTF8 ).getStr()
444 << "\"];" << std::endl;
445 }
446 else
447 {
448 output << "\t"
449 << normalizeDotName(connection.msParTransId).getStr()
450 << " -> "
451 << normalizeDotName(connection.msSibTransId).getStr()
452 << " ["
453 << ((connection.mnXMLType == TypeConstant::XML_presOf) ? " color=red, " : ((connection.mnXMLType == TypeConstant::XML_presParOf) ? " color=green, " : " "))
454 << "label=\""
455 << OUStringToOString(connection.msModelId,
456 RTL_TEXTENCODING_UTF8 ).getStr()
457 << "\"];" << std::endl;
458 }
459 }
460 else if( !connection.msSourceId.isEmpty() ||
461 !connection.msDestId.isEmpty() )
462 output << "\t"
463 << normalizeDotName(connection.msSourceId).getStr()
464 << " -> "
465 << normalizeDotName(connection.msDestId).getStr()
466 << " [label=\""
467 << OUStringToOString(connection.msModelId,
468 RTL_TEXTENCODING_UTF8 ).getStr()
469 << ((connection.mnXMLType == TypeConstant::XML_presOf) ? "\", color=red]" : ((connection.mnXMLType == TypeConstant::XML_presParOf) ? "\", color=green]" : "\"]"))
470 << ";" << std::endl;
471#endif
472
473 const bool bInserted1 = maConnectionNameMap.insert(
474 std::make_pair(connection.msModelId,&connection)).second;
475
476 SAL_WARN_IF(!bInserted1, "oox.drawingml", "DiagramData::build(): non-unique connection model id");
477
478 if( connection.mnXMLType == TypeConstant::XML_presOf )
479 {
480 DiagramData::StringMap::value_type::second_type& rVec = getPresOfNameMap()[connection.msDestId];
481 rVec[connection.mnDestOrder] = { connection.msSourceId, sal_Int32(0) };
482 }
483 }
484
485 // assign outline levels
487 for (auto & elemPresOf : rStringMap)
488 {
489 for (auto & elem : elemPresOf.second)
490 {
491 const sal_Int32 nDepth = calcDepth(elem.second.msSourceId, getConnections());
492 elem.second.mnDepth = nDepth != 0 ? nDepth : -1;
493 }
494 }
495#ifdef DEBUG_OOX_DIAGRAM
496 output << "}" << std::endl;
497#endif
498}
499
500}
501
502/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
temporaryPointVector maPoints
DiagramDataState(Connections aConnections, Points aPoints)
Definition: datamodel.cxx:145
Connections & getConnections()
Definition: datamodel.hxx:206
void addConnection(TypeConstant nType, const OUString &sSourceId, const OUString &sDestId)
Definition: datamodel.cxx:285
std::map< OUString, std::map< sal_Int32, SourceIdAndDepth > > StringMap
Tracks connections: destination id -> {destination order, details} map.
Definition: datamodel.hxx:189
OUString getString() const
Definition: datamodel.cxx:87
virtual void buildDiagramDataModel(bool bClearOoxShapes)
Definition: datamodel.cxx:340
bool removeNode(const OUString &rNodeId)
Definition: datamodel.cxx:95
OUString addNode(const OUString &rText)
Definition: datamodel.cxx:231
StringMap & getPresOfNameMap()
Definition: datamodel.hxx:208
const Point * getRootPoint() const
Definition: datamodel.cxx:77
PointNameMap maPointNameMap
Definition: datamodel.hxx:261
PointsNameMap maPointsPresNameMap
Definition: datamodel.hxx:262
void getChildrenString(OUStringBuffer &rBuf, const Point *pPoint, sal_Int32 nLevel) const
Definition: datamodel.cxx:172
std::vector< std::pair< OUString, OUString > > getChildren(const OUString &rParentId) const
Definition: datamodel.cxx:205
PointNameMap & getPointNameMap()
Definition: datamodel.hxx:209
void applyDiagramDataState(const DiagramDataStatePtr &rState)
Definition: datamodel.cxx:158
ConnectionNameMap maConnectionNameMap
Definition: datamodel.hxx:263
DiagramDataStatePtr extractDiagramDataState() const
Definition: datamodel.cxx:151
PointsNameMap & getPointsPresNameMap()
Definition: datamodel.hxx:210
int nCount
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
aBuf
def point()
OString generateGUIDString()
int i
std::shared_ptr< T > make_shared(Args &&... args)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
static sal_Int32 calcDepth(std::u16string_view rNodeName, const svx::diagram::Connections &rCnx)
Definition: datamodel.cxx:320
std::shared_ptr< DiagramDataState > DiagramDataStatePtr
Definition: datamodel.hxx:171
std::vector< Connection > Connections
Definition: datamodel.hxx:70
std::vector< Point > Points
Definition: datamodel.hxx:155
QPRO_FUNC_TYPE nType
TypeConstant mnXMLType
Definition: datamodel.hxx:58
Styles for a Point (FillStyle/LineStyle/...)
Definition: datamodel.hxx:91
sal_Int32 mnLayoutStyleIndex
Definition: datamodel.hxx:144
sal_Int32 mnLayoutStyleCount
Definition: datamodel.hxx:143
OUString msPresentationLayoutStyleLabel
Definition: datamodel.hxx:122
OUString msPresentationAssociationId
Definition: datamodel.hxx:120
OUString msPresentationLayoutName
Definition: datamodel.hxx:121
TypeConstant mnXMLType
Definition: datamodel.hxx:126
TextBodyPtr msTextBody
Definition: datamodel.hxx:106
Text and properties for a point For proof of concept to make TextData available in svx level this is ...
Definition: datamodel.hxx:78
sal_uInt16 sal_Unicode