1 /*
2 * This file is part of Office 2007 Filters for Calligra
3 *
4 * Copyright (C) 2010 Sebastian Sauer <sebsauer@kdab.com>
5 * Copyright (c) 2010 Carlos Licea <carlos@kdab.com>
6 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
7 *
8 * Contact: Suresh Chande suresh.chande@nokia.com
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * version 2.1 as published by the Free Software Foundation.
13 *
14 * This library is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 * 02110-1301 USA
23 *
24 */
25 #include "MsooXmlDiagramReader_p.h"
26
27 #include <typeinfo>
28 #include <iterator>
29 #include <QXmlStreamReader>
30 #include <KoXmlWriter.h>
31 #include <KoGenStyles.h>
32 #include <KoEmbeddedDocumentSaver.h>
33 #include <KoShapeSavingContext.h>
34 #include "MsooXmlDebug.h"
35
36 #define MSOOXML_CURRENT_NS "dgm"
37 #define MSOOXML_CURRENT_CLASS MsooXmlDiagramReader
38 #define BIND_READ_CLASS MSOOXML_CURRENT_CLASS
39
40 #include <MsooXmlReader.h>
41 #include <MsooXmlReader_p.h>
42 #include <MsooXmlCommonReader.h>
43 #include <MsooXmlUtils.h>
44 #include <MsooXmlDiagramReader.h>
45
46 #include <QTextStream>
47
48 #include <algorithm>
49
50 namespace MSOOXML { namespace Diagram {
51
52 //#define ASSERT_X(condition, errormessage) Q_ASSERT_X(condition, __FUNCTION__, errormessage)
53 #define ASSERT_X(condition, errormessage)
54 #define DEBUG_DUMP debugMsooXml << QString("%1%2").arg(QString(' ').repeated(level*2)).arg(m_tagName)
55 //#define DEBUG_DUMP debugMsooXml << QString("%1Dgm::%2::%3").arg(QString(' ').repeated(level)).arg(typeid(this).name()).arg(__FUNCTION__) << this << "atom=" << m_tagName
56 #define DEBUG_WRITE debugMsooXml << QString("Dgm::%1::%2").arg(typeid(this).name()).arg(__FUNCTION__) << "atom=" << m_tagName
57
58 }}
59
60 using namespace MSOOXML::Diagram;
61 using namespace MSOOXML;
62
63 /****************************************************************************************************/
64
Context()65 Context::Context()
66 : m_rootPoint(0)
67 , m_connections(new ConnectionListNode)
68 , m_rootLayout(new Diagram::LayoutNodeAtom)
69 , m_parentLayout(m_rootLayout)
70 , m_currentNode(0) {
71 }
72
~Context()73 Context::~Context() {
74 //TODO make sure this is no memoryleak
75 //delete m_rootPoint;
76 //delete m_connections;
77 }
78
currentNode() const79 AbstractNode* Context::currentNode() const {
80 return m_currentNode;
81 }
82
setCurrentNode(AbstractNode * node)83 void Context::setCurrentNode(AbstractNode* node) {
84 m_currentNode = node;
85 }
86
ValueCache()87 ValueCache::ValueCache() : m_rect( QRectF( 0.0f, 0.0f, 100.0f, 100.0f ) ), m_unmodified( true ), m_negativeWidth( false ), m_negativeHeight( false ) {}
88
hasNegativeWidth() const89 bool ValueCache::hasNegativeWidth() const {
90 return m_negativeWidth;
91 }
92
hasNegativeHeight() const93 bool ValueCache::hasNegativeHeight() const {
94 return m_negativeHeight;
95 }
96
value(const QString & name,bool * valid) const97 qreal ValueCache::value( const QString& name, bool *valid ) const {
98 if ( valid )
99 *valid = true;
100 if ( isRectValue( name ) )
101 return rectValue( name );
102 if ( valid && ! m_mapping.contains( name ) )
103 *valid = false;
104 return m_mapping[ name ];
105 }
106
valueExists(const QString & name)107 bool ValueCache::valueExists( const QString& name ) {
108 return isRectValue( name ) || m_mapping.contains( name );
109 }
110
setValue(const QString & name,qreal value)111 void ValueCache::setValue( const QString& name, qreal value ) {
112 if ( isRectValue( name ) )
113 setRectValue( name, value );
114 else
115 m_mapping[ name ] = value;
116 }
117
operator [](const QString & name) const118 qreal ValueCache::operator[]( const QString& name ) const {
119 return value( name );
120 }
121
operator [](const char * name)122 ValueCache::ResultWrapper ValueCache::operator[]( const char* name ) {
123 return ResultWrapper( this, QString::fromLatin1( name ) );
124 }
125
operator [](const QString & name)126 ValueCache::ResultWrapper ValueCache::operator[]( const QString& name ) {
127 return ResultWrapper( this, name );
128 }
129
operator QMap<QString,qreal>() const130 ValueCache::operator QMap< QString, qreal >() const {
131 QMap < QString, qreal > result = m_mapping;
132 result[ "l" ] = m_rect.left();
133 result[ "r" ] = m_rect.right();
134 result[ "t" ] = m_rect.top();
135 result[ "b" ] = m_rect.bottom();
136 result[ "w" ] = m_rect.width();
137 result[ "h" ] = m_rect.height();
138 result[ "ctrX" ] = m_rect.center().rx();
139 result[ "ctrY" ] = m_rect.center().ry();
140 return result;
141 }
142
isRectValue(const QString & name) const143 bool ValueCache::isRectValue( const QString& name ) const {
144 return name == "l" || name == "r" || name == "w" || name == "h" || name == "t" || name == "b" || name == "ctrX" || name == "ctrY";
145 }
146
rectValue(const QString & name) const147 qreal ValueCache::rectValue( const QString& name ) const {
148 if ( name == "l")
149 return m_rect.left();
150 if ( name == "r" )
151 return m_rect.right();
152 if ( name == "w" )
153 return m_rect.width();
154 if ( name == "h" )
155 return m_rect.height();
156 if ( name == "t" )
157 return m_rect.top();
158 if ( name == "b" )
159 return m_rect.bottom();
160 if ( name == "ctrX" )
161 return m_rect.center().rx();
162 if ( name == "ctrY" )
163 return m_rect.center().ry();
164 return 0.0;
165 }
166 #include <limits>
setRectValue(const QString & name,qreal value)167 void ValueCache::setRectValue( const QString& name, qreal value ) {
168 if ( name == "l") {
169 m_rect.moveLeft( value );
170 } else if ( name == "r" ) {
171 m_rect.moveRight( value );
172 } else if ( name == "w" ) {
173 m_rect.setWidth( value );
174 } else if ( name == "h" ) {
175 //TODO this is a hack, as its not really described how to handle infinite values during layouting
176 if ( value != std::numeric_limits<qreal>::infinity() )
177 m_rect.setHeight( value );
178 else
179 m_rect.setHeight( m_rect.width() );
180 } else if ( name == "t" ) {
181 m_rect.moveTop( value );
182 } else if ( name == "b" ) {
183 m_rect.moveBottom( value );
184 } else if ( name == "ctrX" ) {
185 m_rect.moveCenter( QPointF( value, m_rect.center().y() ) );
186 } else if ( name == "ctrY" ) {
187 m_rect.moveCenter( QPointF( m_rect.center().x(), value ) );
188 } else {
189 ASSERT_X( false, QString("TODO unhandled name=%1 value=%2").arg(name).arg(value).toLocal8Bit() );
190 }
191 m_unmodified = false;
192 }
193
194 /****************************************************************************************************/
195
AbstractNode(const QString & tagName)196 AbstractNode::AbstractNode(const QString &tagName) : m_tagName(tagName), m_parent(0) {}
~AbstractNode()197 AbstractNode::~AbstractNode() { qDeleteAll(children()); }
198
dump(Context * context,int level)199 void AbstractNode::dump(Context* context, int level) {
200 foreach(AbstractNode* node, children())
201 node->dump(context, level + 1);
202 }
203
dump(QTextStream & device)204 void AbstractNode::dump( QTextStream& device ) {
205 foreach(AbstractNode* node, children())
206 node->dump( device );
207 }
208
readElement(Context *,MsooXmlDiagramReader *)209 void AbstractNode::readElement(Context*, MsooXmlDiagramReader*) {
210 }
211
readAll(Context * context,MsooXmlDiagramReader * reader)212 void AbstractNode::readAll(Context* context, MsooXmlDiagramReader* reader) {
213 while (!reader->atEnd()) {
214 QXmlStreamReader::TokenType tokenType = reader->readNext();
215 if(tokenType == QXmlStreamReader::Invalid || tokenType == QXmlStreamReader::EndDocument) break;
216 if(!reader->isStartElement() && reader->qualifiedName() == m_tagName) break;
217 readElement(context, reader);
218 }
219 }
220
parent() const221 AbstractNode* AbstractNode::parent() const {
222 return m_parent;
223 }
224
children() const225 QList<AbstractNode*> AbstractNode::children() const {
226 if(m_cachedChildren.isEmpty()) {
227 const int count = m_appendedChildren.count()+m_orderedChildren.count();
228 for(int i = 0, k = -1; i < count; ++i) {
229 if(m_orderedChildren.contains(i)) {
230 foreach(AbstractNode* n, m_orderedChildren[i])
231 m_cachedChildren.append(n);
232 } else {
233 m_cachedChildren.append(m_appendedChildren[++k]);
234 }
235 }
236 }
237 return m_cachedChildren;
238 }
239
insertChild(int index,AbstractNode * node)240 void AbstractNode::insertChild(int index, AbstractNode* node) {
241 //Q_ASSERT(!m_orderedChildren.contains(index));
242 Q_ASSERT(!m_orderedChildrenReverse.contains(node));
243 Q_ASSERT(!m_appendedChildren.contains(node));
244 Q_ASSERT(!node->m_parent);
245 node->m_parent = this;
246 if(m_orderedChildren.contains(index))
247 m_orderedChildren[index].append(node);
248 else
249 m_orderedChildren[index] = QList<AbstractNode*>() << node;
250 m_orderedChildrenReverse[node] = index;
251 m_cachedChildren.clear();
252 //LayoutNodeAtom* layNode = dynamic_cast< LayoutNodeAtom* >( node );
253 }
254
addChild(AbstractNode * node)255 void AbstractNode::addChild(AbstractNode* node) {
256 Q_ASSERT(!node->m_parent);
257 Q_ASSERT(!m_orderedChildrenReverse.contains(node));
258 node->m_parent = this;
259 m_appendedChildren.append(node);
260 m_cachedChildren.clear();
261 //LayoutNodeAtom* layNode = dynamic_cast< LayoutNodeAtom* >( this );
262 }
263
removeChild(AbstractNode * node)264 void AbstractNode::removeChild(AbstractNode* node) {
265 Q_ASSERT(node->m_parent == this);
266 node->m_parent = 0;
267 if(m_orderedChildrenReverse.contains(node)) {
268 int index = m_orderedChildrenReverse.take(node);
269 QList<AbstractNode*> nodes = m_orderedChildren[index];
270 nodes.removeAll(node);
271 m_orderedChildren[index] = nodes;
272 } else {
273 m_appendedChildren.removeAll(node);
274 }
275 m_cachedChildren.clear();
276 }
277
descendant() const278 QList<AbstractNode*> AbstractNode::descendant() const {
279 QList<AbstractNode*> list = children();
280 foreach(AbstractNode* node, children())
281 foreach(AbstractNode* n, node->descendant())
282 list.append(n);
283 return list;
284 }
285
peers() const286 QList<AbstractNode*> AbstractNode::peers() const {
287 QList<AbstractNode*> list;
288 if (m_parent)
289 foreach(AbstractNode* node, m_parent->children())
290 if(node != this)
291 list.append(node);
292 return list;
293 }
294
295 /****************************************************************************************************/
296
dump(Context * context,int level)297 void PointNode::dump(Context* context, int level) {
298 DEBUG_DUMP << "type=" << m_type << "modelId=" << m_modelId << "cxnId=" << m_cxnId;
299 AbstractNode::dump(context, level);
300 }
301
readElement(Context * context,MsooXmlDiagramReader * reader)302 void PointNode::readElement(Context* context, MsooXmlDiagramReader* reader) {
303 if (reader->isStartElement()) {
304 if (reader->qualifiedName() == QLatin1String("dgm:prSet")) {
305 prSet[ QLatin1String( "dgm:prSet" ) ] = reader->attributes().value( "phldrT" ).toString();
306 } else if (reader->qualifiedName() == QLatin1String("dgm:spPr")) {
307 //TODO
308 } else if (reader->qualifiedName() == QLatin1String("dgm:t")) {
309 readTextBody(context, reader);
310 }
311 }
312 }
313
dump(QTextStream & device)314 void MSOOXML::Diagram::ConnectionNode::dump(QTextStream& device) {
315 foreach(AbstractNode* node, peers() ) {
316 ConnectionNode* connNode = dynamic_cast< ConnectionNode* > ( node );
317 PointNode* pointNode = dynamic_cast< PointNode* > ( node );
318 if ( connNode )
319 device << "\"" << m_tagName << m_modelId << "\" -> \"" << connNode->m_tagName << connNode->m_modelId << "\"\n";
320 else if ( pointNode )
321 device << "\"" << m_tagName << m_modelId << "\" -> \"" << pointNode->m_tagName << pointNode->m_modelId << "\"\n";
322 }
323 foreach(AbstractNode* node, children()) {
324 ConnectionNode* connNode = dynamic_cast< ConnectionNode* > ( node );
325 PointNode* pointNode = dynamic_cast< PointNode* > ( node );
326 if ( connNode )
327 device << "\"" << m_tagName << m_modelId << "\" -> \"" << connNode->m_tagName << connNode->m_modelId << "\"\n";
328 else if ( pointNode )
329 device << "\"" << m_tagName << m_modelId << "\" -> \"" << pointNode->m_tagName << pointNode->m_modelId << "\"\n";
330 }
331 MSOOXML::Diagram::AbstractNode::dump(device);
332 }
333
readAll(Context * context,MsooXmlDiagramReader * reader)334 void PointNode::readAll(Context* context, MsooXmlDiagramReader* reader) {
335 const QXmlStreamAttributes attrs(reader->attributes());
336 TRY_READ_ATTR_WITHOUT_NS_INTO(modelId, m_modelId)
337 TRY_READ_ATTR_WITHOUT_NS_INTO(type, m_type)
338 if (m_type.isEmpty()) m_type = "node";
339 if (m_type == QLatin1String("parTrans") || m_type == QLatin1String("sibTrans"))
340 TRY_READ_ATTR_WITHOUT_NS_INTO(cxnId, m_cxnId)
341 else
342 m_cxnId.clear();
343 AbstractNode::readAll(context, reader);
344 }
345
readTextBody(Context *,MsooXmlDiagramReader * reader)346 void PointNode::readTextBody(Context*, MsooXmlDiagramReader* reader) {
347 //m_text.clear();
348 enum { Start, Paragraph, TextRun } s;
349 s = Start;
350 while (!reader->atEnd()) {
351 reader->readNext();
352 if(reader->isEndElement() && reader->qualifiedName() == QLatin1String("dgm:t")) break;
353 switch(s) {
354 case Start:
355 if (reader->isStartElement() && reader->qualifiedName() == QLatin1String("a:p")) s = Paragraph;
356 break;
357 case Paragraph:
358 if (reader->qualifiedName() == QLatin1String("a:r")) // text run
359 s = reader->isStartElement() ? TextRun : Start;
360 break;
361 case TextRun:
362 if (reader->qualifiedName() == QLatin1String("a:t")) {
363 if(reader->isStartElement()) {
364 if(!m_text.isEmpty()) m_text += ' '; // concat multiple strings into one result
365 m_text += reader->readElementText();
366 } else
367 s = Paragraph;
368 }
369 break;
370 }
371 }
372 if ( m_text.isEmpty() )
373 m_text = prSet.value( QLatin1String("dgm:prSet") );
374 }
375
376 /****************************************************************************************************/
377
dump(Context * context,int level)378 void PointListNode::dump(Context* context, int level) {
379 //DEBUG_DUMP;
380 AbstractNode::dump(context, level);
381 }
382
dump(QTextStream & device)383 void PointListNode::dump( QTextStream& device ) {
384 AbstractNode::dump( device );
385 }
386
387 /****************************************************************************************************/
388
readElement(Context * context,MsooXmlDiagramReader * reader)389 void PointListNode::readElement(Context* context, MsooXmlDiagramReader* reader) {
390 if (reader->isStartElement()) {
391 if (reader->qualifiedName() == QLatin1String("dgm:pt")) {
392 PointNode *n = new PointNode;
393 addChild(n);
394 n->readAll(context, reader);
395 }
396 }
397 }
398
dump(Context *,int level)399 void ConnectionNode::dump(Context*, int level) {
400 DEBUG_DUMP << "modelId=" << m_modelId << "type=" << m_type << "srcId=" << m_srcId << "destId=" << m_destId;
401 //AbstractNode::dump(context, level);
402 }
403
dump(QTextStream & device)404 void MSOOXML::Diagram::PointNode::dump(QTextStream& device) {
405 foreach(AbstractNode* node, peers() ) {
406 ConnectionNode* connNode = dynamic_cast< ConnectionNode* > ( node );
407 PointNode* pointNode = dynamic_cast< PointNode* > ( node );
408 if ( connNode )
409 device << "\"" << m_tagName << m_modelId << "\" -> \"" << connNode->m_tagName << connNode->m_modelId << "\"[label=\"" << /*m_tagName << m_modelId << " " <<*/ m_text << "\"]\n";
410 else if ( pointNode )
411 device << "\"" << m_tagName << m_modelId << "\" -> \"" << pointNode->m_tagName << pointNode->m_modelId << "\"[label=\"" << /*m_tagName << m_modelId << " " <<*/ m_text << "\"]\n";
412 }
413 foreach(AbstractNode* node, children()) {
414 ConnectionNode* connNode = dynamic_cast< ConnectionNode* > ( node );
415 PointNode* pointNode = dynamic_cast< PointNode* > ( node );
416 if ( connNode )
417 device << "\"" << m_tagName << m_modelId << "\" -> \"" << connNode->m_tagName << connNode->m_modelId << "\"[label=\"" << /*m_tagName << m_modelId << " " <<*/ m_text << "\"]\n";
418 else if ( pointNode )
419 device << "\"" << m_tagName << m_modelId << "\" -> \"" << pointNode->m_tagName << pointNode->m_modelId << "\"[label=\"" << /*m_tagName << m_modelId << " " <<*/ m_text << "\"]\n";
420 }
421 MSOOXML::Diagram::AbstractNode::dump(device);
422 }
423
readElement(Context * context,MsooXmlDiagramReader * reader)424 void ConnectionNode::readElement(Context* context, MsooXmlDiagramReader* reader) {
425 if (reader->isStartElement()) {
426 if (reader->qualifiedName() == QLatin1String("dgm:cxn")) {
427 ConnectionNode *n = new ConnectionNode;
428 addChild(n);
429 n->readAll(context, reader);
430 }
431 }
432 }
433
readAll(Context * context,MsooXmlDiagramReader * reader)434 void ConnectionNode::readAll(Context* context, MsooXmlDiagramReader* reader) {
435 const QXmlStreamAttributes attrs(reader->attributes());
436 TRY_READ_ATTR_WITHOUT_NS_INTO(modelId, m_modelId)
437 TRY_READ_ATTR_WITHOUT_NS_INTO(type, m_type)
438 if (m_type.isEmpty()) m_type = "parOf";
439 TRY_READ_ATTR_WITHOUT_NS_INTO(srcId, m_srcId)
440 TRY_READ_ATTR_WITHOUT_NS_INTO(destId, m_destId)
441 TRY_READ_ATTR_WITHOUT_NS_INTO(presId, m_presId)
442 TRY_READ_ATTR_WITHOUT_NS_INTO(parTransId, m_parTransId)
443 TRY_READ_ATTR_WITHOUT_NS_INTO(sibTransId, m_sibTransId)
444 TRY_READ_ATTR_WITHOUT_NS(srcOrd)
445 TRY_READ_ATTR_WITHOUT_NS(destOrd)
446 m_srcOrd = srcOrd.toInt();
447 m_destOrd = destOrd.toInt();
448 AbstractNode::readAll(context, reader);
449 }
450
451 /****************************************************************************************************/
452
dump(Context * context,int level)453 void ConnectionListNode::dump(Context* context, int level) {
454 //DEBUG_DUMP;
455 AbstractNode::dump(context, level);
456 }
457
dump(QTextStream & device)458 void ConnectionListNode::dump( QTextStream& device ) {
459 //DEBUG_DUMP;
460 AbstractNode::dump( device );
461 }
462
readElement(Context * context,MsooXmlDiagramReader * reader)463 void ConnectionListNode::readElement(Context* context, MsooXmlDiagramReader* reader) {
464 if (reader->isStartElement()) {
465 if (reader->qualifiedName() == QLatin1String("dgm:cxn")) {
466 ConnectionNode *n = new ConnectionNode;
467 addChild(n);
468 n->readAll(context, reader);
469 }
470 }
471 }
472
473 /****************************************************************************************************/
474
AbstractAtom(const QString & tagName)475 AbstractAtom::AbstractAtom(const QString &tagName) : QSharedData(), m_tagName(tagName) {}
~AbstractAtom()476 AbstractAtom::~AbstractAtom() {}
477
dump(Context * context,int level)478 void AbstractAtom::dump(Context* context, int level) {
479 //DEBUG_DUMP;
480 foreach(QExplicitlySharedDataPointer<AbstractAtom> atom, m_children) {
481 atom->dump(context, level + 1);
482 }
483 }
484
readElement(Context * context,MsooXmlDiagramReader * reader)485 void AbstractAtom::readElement(Context* context, MsooXmlDiagramReader* reader) {
486 if (reader->isStartElement()) {
487 AbstractAtom *node = 0;
488
489 if (reader->qualifiedName() == QLatin1String("dgm:layoutNode")) {
490 node = new LayoutNodeAtom;
491 } else if (reader->qualifiedName() == QLatin1String("dgm:shape")) {
492 node = new ShapeAtom;
493 } else if (reader->qualifiedName() == QLatin1String("dgm:alg")) {
494 node = new AlgorithmAtom;
495 } else if (reader->qualifiedName() == QLatin1String("dgm:presOf")) {
496 node = new PresentationOfAtom;
497 } else if (reader->qualifiedName() == QLatin1String("dgm:choose")) {
498 node = new ChooseAtom;
499 } else if (reader->qualifiedName() == QLatin1String("dgm:forEach")) {
500 node = new ForEachAtom;
501 } else if (reader->qualifiedName() == QLatin1String("dgm:constrLst")) {
502 node = new ListAtom(reader->qualifiedName());
503 } else if (reader->qualifiedName() == QLatin1String("dgm:ruleLst")) {
504 node = new ListAtom(reader->qualifiedName());
505 } else if (reader->qualifiedName() == QLatin1String("dgm:adj")) {
506 node = new AdjustAtom;
507 } else if (reader->qualifiedName() == QLatin1String("dgm:adjLst")) {
508 node = new ListAtom(reader->qualifiedName());
509 } else if (reader->qualifiedName() == QLatin1String("dgm:varLst")) {
510 while (!reader->atEnd()) {
511 QXmlStreamReader::TokenType tokenType = reader->readNext();
512 if(tokenType == QXmlStreamReader::Invalid || tokenType == QXmlStreamReader::EndDocument) break;
513 if(!reader->isStartElement() && reader->qualifiedName() == "dgm:varLst") break;
514 if(reader->isStartElement()) {
515 const QXmlStreamAttributes attrs(reader->attributes());
516 TRY_READ_ATTR_WITHOUT_NS(val)
517 context->m_parentLayout->setVariable(reader->name().toString(), val);
518 }
519 }
520 } else {
521 debugMsooXml<<"TODO atom="<<m_tagName<<"qualifiedName="<<reader->qualifiedName();
522 }
523
524 if (node) {
525 QExplicitlySharedDataPointer<AbstractAtom> ptr(node);
526 addChild(ptr);
527 ptr->readAll(context, reader);
528 }
529 }
530 }
531
readAll(Context * context,MsooXmlDiagramReader * reader)532 void AbstractAtom::readAll(Context* context, MsooXmlDiagramReader* reader) {
533 while (!reader->atEnd()) {
534 QXmlStreamReader::TokenType tokenType = reader->readNext();
535 if(tokenType == QXmlStreamReader::Invalid || tokenType == QXmlStreamReader::EndDocument) break;
536 if(!reader->isStartElement() && reader->qualifiedName() == m_tagName) break;
537 readElement(context, reader);
538 }
539 }
540
build(Context * context)541 void AbstractAtom::build(Context* context) {
542 //typedef QList< QExplicitlySharedDataPointer< AbstractAtom > > SharedAtomList;
543 //for( int i = 0; i < m_children.count(); ++i ) m_children[ i ]->build( context );
544 foreach(QExplicitlySharedDataPointer<AbstractAtom> atom, m_children) {
545 atom->build(context);
546 }
547 }
548
finishBuild(Context * context)549 void AbstractAtom::finishBuild(Context* context) {
550 foreach(QExplicitlySharedDataPointer<AbstractAtom> atom, m_children) {
551 atom->finishBuild(context);
552 }
553 }
554
layoutAtom(Context * context)555 void AbstractAtom::layoutAtom(Context* context) {
556 foreach(QExplicitlySharedDataPointer<AbstractAtom> atom, m_children) {
557 if (LayoutNodeAtom* layAtom = dynamic_cast< LayoutNodeAtom* >( atom.data() ))
558 layAtom->setNeedsRelayout( true );
559 atom->layoutAtom(context);
560 }
561 }
562
writeAtom(Context * context,KoXmlWriter * xmlWriter,KoGenStyles * styles)563 void AbstractAtom::writeAtom(Context* context, KoXmlWriter* xmlWriter, KoGenStyles* styles) {
564 foreach(QExplicitlySharedDataPointer<AbstractAtom> atom, m_children)
565 atom->writeAtom(context, xmlWriter, styles);
566 }
567
parentLayout() const568 QExplicitlySharedDataPointer<LayoutNodeAtom> AbstractAtom::parentLayout() const {
569 LayoutNodeAtom* p = 0;
570 for(QExplicitlySharedDataPointer<AbstractAtom> a = parent(); a && !p; a = a->parent())
571 p = dynamic_cast<LayoutNodeAtom*>(a.data());
572 return QExplicitlySharedDataPointer<LayoutNodeAtom>(p);
573 }
574
parent() const575 QExplicitlySharedDataPointer<AbstractAtom> AbstractAtom::parent() const {
576 return m_parent;
577 }
578
children() const579 QVector< QExplicitlySharedDataPointer<AbstractAtom> > AbstractAtom::children() const {
580 return m_children;
581 }
582
indexOfChild(AbstractAtom * node) const583 int AbstractAtom::indexOfChild(AbstractAtom* node) const {
584 for ( int i = 0; i < m_children.count(); ++i )
585 if ( m_children[ i ].data() == node )
586 return i;
587 return -1;
588 }
589
addChild(AbstractAtom * node)590 void AbstractAtom::addChild(AbstractAtom* node) {
591 addChild(QExplicitlySharedDataPointer<AbstractAtom>(node));
592 }
593
addChild(QExplicitlySharedDataPointer<AbstractAtom> node)594 void AbstractAtom::addChild(QExplicitlySharedDataPointer<AbstractAtom> node) {
595 node->m_parent = this;
596 m_children.append(node);
597 }
598
insertChild(int index,AbstractAtom * node)599 void AbstractAtom::insertChild(int index, AbstractAtom* node) {
600 insertChild(index, QExplicitlySharedDataPointer<AbstractAtom>(node));
601 }
602
insertChild(int index,QExplicitlySharedDataPointer<AbstractAtom> node)603 void AbstractAtom::insertChild(int index, QExplicitlySharedDataPointer<AbstractAtom> node) {
604 node->m_parent = this;
605 if ( index < m_children.count() )
606 m_children.insert(index, node);
607 else
608 m_children.append( node );
609 }
610
removeChild(QExplicitlySharedDataPointer<AbstractAtom> node)611 void AbstractAtom::removeChild(QExplicitlySharedDataPointer<AbstractAtom> node) {
612 const int index = m_children.indexOf(node);
613 Q_ASSERT(index >= 0);
614 m_children.remove(index);
615 node->m_parent = QExplicitlySharedDataPointer<AbstractAtom>();
616 }
617
fetchAxis(Context * context,const QString & _axis,const QString & _ptType,const QString & _start,const QString & _count,const QString & _step) const618 QList<AbstractNode*> AbstractAtom::fetchAxis(Context* context, const QString& _axis, const QString &_ptType, const QString& _start, const QString& _count, const QString& _step) const {
619 const QStringList axisList = _axis.split(' ', QString::SkipEmptyParts);
620 const QStringList typeList = _ptType.split(' ', QString::SkipEmptyParts);
621 const QStringList startList = _start.split(' ', QString::SkipEmptyParts);
622 const QStringList countList = _count.split(' ', QString::SkipEmptyParts);
623 const QStringList stepList = _step.split(' ', QString::SkipEmptyParts);
624 QList<AbstractNode*> result;
625 Q_ASSERT(context->currentNode());
626 result << context->currentNode();
627 for(int i = 0; i < axisList.count(); ++i) {
628 result = fetchAxis(context, result, axisList[i], typeList.value(i), startList.value(i), countList.value(i), stepList.value(i));
629 }
630 return result;
631 }
632
fetchAxis(Context * context,QList<AbstractNode * > list,const QString & axis,const QString & ptType,const QString & start,const QString & count,const QString & step) const633 QList<AbstractNode*> AbstractAtom::fetchAxis(Context* context, QList<AbstractNode*> list, const QString& axis, const QString &ptType, const QString& start, const QString& count, const QString& step) const {
634 QList<AbstractNode*> result;
635
636 // fill the result-list according to the defined axis value
637 foreach(AbstractNode* node, list) {
638 if(axis == QLatin1String("ancst")) { // Ancestor
639 for(AbstractNode* n = node; n; n = n->parent())
640 result.append(n);
641 } else if(axis == QLatin1String("ancstOrSelf")) { // Ancestor Or Self
642 for(AbstractNode* n = node; n; n = n->parent())
643 result.append(n);
644 result.append(node);
645 } else if(axis == QLatin1String("ch")) { // Children
646 foreach(AbstractNode* n, node->children())
647 result.append(n);
648 } else if(axis == QLatin1String("des")) { // Descendant
649 foreach(AbstractNode* n, node->descendant())
650 result.append(n);
651 } else if(axis == QLatin1String("desOrSelf")) { // Descendant Or Self
652 foreach(AbstractNode* n, node->descendant())
653 result.append(n);
654 result.append(node);
655 } else if(axis == QLatin1String("follow")) { // Follow
656 foreach(AbstractNode* peer, node->peers()) {
657 result.append(peer);
658 foreach(AbstractNode* n, peer->descendant())
659 result.append(n);
660 }
661 } else if(axis == QLatin1String("followSib")) { // Follow Sibling
662 foreach(AbstractNode* n, node->peers())
663 result.append(n);
664 } else if(axis == QLatin1String("par")) { // Parent
665 if (AbstractNode* n = node->parent())
666 result.append(n);
667 } else if(axis == QLatin1String("preced")) { // Preceding
668 warnMsooXml<<"TODO preced";
669 //TODO
670 } else if(axis == QLatin1String("precedSib")) { // Preceding Sibling
671 warnMsooXml<<"TODO precedSib";
672 //TODO
673 } else if(axis == QLatin1String("root")) { // Root
674 result.append(context->m_rootPoint);
675 } else if(axis == QLatin1String("self")) { // Self
676 result.append(node);
677 }
678 }
679
680 // optionally filter the list
681 if(!ptType.isEmpty()) {
682 QList<AbstractNode*> list = result;
683 result.clear();
684 foreach(AbstractNode* node, list) {
685 if(PointNode* pt = dynamic_cast<PointNode*>(node)) {
686 if(ptType == pt->m_type || ptType == "all" || (ptType == "nonAsst" && pt->m_type != "asst" ) || (ptType == "nonNorm" && pt->m_type != "norm")) {
687 result.append(pt);
688 }
689 }
690 }
691 }
692
693 // evaluate optional forEach-conditions
694 if(!start.isEmpty() || !count.isEmpty() || !step.isEmpty()) {
695 const int _start = start.isEmpty() ? 1 : start.toInt();
696 const int _count = count.isEmpty() ? 0 : count.toInt();
697 const int _step = step.isEmpty() ? 1 : step.toInt();
698 result = foreachAxis(context, result, _start, _count, _step);
699 }
700
701 return result;
702 }
703
foreachAxis(Context *,const QList<AbstractNode * > & list,int start,int count,int step) const704 QList<AbstractNode*> AbstractAtom::foreachAxis(Context*, const QList<AbstractNode*> &list, int start, int count, int step) const {
705 QList<AbstractNode*> result;
706 const int _start = qMax(0, start - 1);
707 const int _step = qMax(1, step);
708 for(int i = _start; i < list.count(); i += _step) {
709 result.append(list[i]);
710 if(/*count > 0 &&*/ result.count() == count) break;
711 }
712 return result;
713 }
714
715 /****************************************************************************************************/
716
clone(Context * context)717 AlgorithmAtom* AlgorithmAtom::clone(Context* context) {
718 AlgorithmAtom* atom = new AlgorithmAtom;
719 atom->m_type = m_type;
720 atom->m_params = m_params;
721 foreach(QExplicitlySharedDataPointer<AbstractAtom> a, m_children)
722 atom->addChild(a->clone(context));
723 return atom;
724 }
725
dump(Context * context,int level)726 void AlgorithmAtom::dump(Context* context, int level) {
727 DEBUG_DUMP << "type=" << typeAsString() << "params=" << m_params;
728 AbstractAtom::dump(context, level);
729 }
730
readAll(Context * context,MsooXmlDiagramReader * reader)731 void AlgorithmAtom::readAll(Context* context, MsooXmlDiagramReader* reader) {
732 const QXmlStreamAttributes attrs(reader->attributes());
733 TRY_READ_ATTR_WITHOUT_NS(type)
734 if(type == QLatin1String("composite")) m_type = CompositeAlg;
735 else if(type == QLatin1String("conn")) m_type = ConnectorAlg;
736 else if(type == QLatin1String("cycle")) m_type = CycleAlg;
737 else if(type == QLatin1String("hierChild")) m_type = HierChildAlg;
738 else if(type == QLatin1String("hierRoot")) m_type = HierRootAlg;
739 else if(type == QLatin1String("lin")) m_type = LinearAlg;
740 else if(type == QLatin1String("pyra")) m_type = PyramidAlg;
741 else if(type == QLatin1String("snake")) m_type = SnakeAlg;
742 else if(type == QLatin1String("sp")) m_type = SpaceAlg;
743 else if(type == QLatin1String("tx")) m_type = TextAlg;
744 else m_type = UnknownAlg;
745 AbstractAtom::readAll(context, reader);
746 }
747
readElement(Context *,MsooXmlDiagramReader * reader)748 void AlgorithmAtom::readElement(Context*, MsooXmlDiagramReader* reader) {
749 if (reader->isStartElement()) {
750 if (reader->qualifiedName() == QLatin1String("dgm:param")) {
751 const QXmlStreamAttributes attrs(reader->attributes());
752 TRY_READ_ATTR_WITHOUT_NS(type)
753 TRY_READ_ATTR_WITHOUT_NS(val)
754 m_params[type] = val;
755 }
756 }
757 }
758
typeAsString() const759 QString AlgorithmAtom::typeAsString() const {
760 QString s;
761 switch(m_type) {
762 case UnknownAlg: s = "Unknown"; break;
763 case CompositeAlg: s = "Composite"; break;
764 case ConnectorAlg: s = "Connector"; break;
765 case CycleAlg: s = "Cycle"; break;
766 case HierChildAlg: s = "HierChild"; break;
767 case HierRootAlg: s = "HierRoot"; break;
768 case LinearAlg: s = "Linear"; break;
769 case PyramidAlg: s = "Pyramid"; break;
770 case SnakeAlg: s = "Snake"; break;
771 case SpaceAlg: s = "Space"; break;
772 case TextAlg: s = "Text"; break;
773 }
774 return s;
775 }
776
777 /****************************************************************************************************/
778
clone(Context * context)779 LayoutNodeAtom* LayoutNodeAtom::clone(Context* context) {
780 LayoutNodeAtom* atom = new LayoutNodeAtom;
781 atom->m_name = m_name;
782 atom->m_values = m_values;
783 atom->m_factors = m_factors;
784 atom->m_countFactors = m_countFactors;
785 foreach(QExplicitlySharedDataPointer<AbstractAtom> a, m_children)
786 atom->addChild(a->clone(context));
787 atom->m_rotateAngle = m_rotateAngle;
788 atom->m_needsReinit = m_needsReinit;
789 atom->m_needsRelayout = m_needsRelayout;
790 atom->m_childNeedsRelayout = m_childNeedsRelayout;
791 atom->m_variables = m_variables;
792 atom->m_firstLayout = m_firstLayout;
793 atom->setAxis(context, axis( context ));
794 return atom;
795 }
796
dump(Context * context,int level)797 void LayoutNodeAtom::dump(Context* context, int level) {
798 QStringList list;
799 foreach(AbstractNode* n, axis( context ))
800 if(PointNode* p = dynamic_cast<PointNode*>(n))
801 list.append( QString("modelId=%1 type=%2 cxnId=%3").arg(p->m_modelId).arg(p->m_type).arg(p->m_cxnId) );
802 else
803 list.append( QString("tagName=%1").arg(n->m_tagName) );
804 //DEBUG_DUMP << "name=" << m_name << "variables=" << m_variables << "values=" << finalValues();
805 DEBUG_DUMP << "name=" << m_name << list;
806 AbstractAtom::dump(context, level);
807 }
808
readAll(Context * context,MsooXmlDiagramReader * reader)809 void LayoutNodeAtom::readAll(Context* context, MsooXmlDiagramReader* reader) {
810 const QXmlStreamAttributes attrs(reader->attributes());
811 TRY_READ_ATTR_WITHOUT_NS_INTO(name, m_name)
812 //TRY_READ_ATTR_WITHOUT_NS_INTO(styleLbl, m_styleLbl)
813 QExplicitlySharedDataPointer<LayoutNodeAtom> ptr(this);
814 QExplicitlySharedDataPointer<LayoutNodeAtom> oldLayout = context->m_parentLayout;
815 context->m_parentLayout = ptr;
816 AbstractAtom::readAll(context, reader);
817 context->m_parentLayout = oldLayout;
818 }
819
820 /*
821 class ConstraintPredicate
822 {
823 public:
824 bool operator()( const QExplicitlySharedDataPointer<MSOOXML::Diagram::AbstractAtom> &value ) {
825 ListAtom *atom = dynamic_cast< ListAtom* >( value.data() );
826 if ( !atom )
827 return true;
828 foreach( QExplicitlySharedDataPointer<AbstractAtom> val, atom->children() )
829 if ( dynamic_cast< ConstraintAtom* >( val.data() ) )
830 return false;
831 return true;
832 }
833 };
834 */
835
build(Context * context)836 void LayoutNodeAtom::build(Context* context) {
837 #if 0
838 QExplicitlySharedDataPointer<LayoutNodeAtom> oldLayout = context->m_parentLayout;
839 context->m_parentLayout = this;
840 context->m_layoutPointMap[ this ] = context->currentNode();
841 typedef QVector< QExplicitlySharedDataPointer<AbstractAtom> > AtomPList;
842 AtomPList::iterator it = std::stable_partition( m_children.begin(), m_children.end(), ConstraintPredicate() );
843 std::copy( it, m_children.end(), std::back_inserter( m_constraintsToBuild ) );
844 m_children.erase( it, m_children.end() );
845 AbstractAtom::build(context);
846 foreach( QExplicitlySharedDataPointer<AbstractAtom> constr, m_constraintsToBuild )
847 constr->build( context );
848 m_constraintsToBuild.clear();
849 context->m_parentLayout = oldLayout;
850 #else
851 QExplicitlySharedDataPointer<LayoutNodeAtom> oldLayout = context->m_parentLayout;
852 context->m_parentLayout = this;
853 AbstractNode* oldCurrentNode = context->currentNode();
854
855 AbstractAtom::build(context);
856
857 context->setCurrentNode(oldCurrentNode);
858 context->m_parentLayout = oldLayout;
859 #endif
860 }
861
finishBuild(Context * context)862 void LayoutNodeAtom::finishBuild(Context* context) {
863 QExplicitlySharedDataPointer<LayoutNodeAtom> oldLayout = context->m_parentLayout;
864 context->m_parentLayout = this;
865 AbstractAtom::finishBuild(context);
866 context->m_parentLayout = oldLayout;
867
868 delete m_algorithmImpl;
869 m_algorithmImpl = 0;
870 QExplicitlySharedDataPointer<AlgorithmAtom> alg = algorithm();
871 switch(alg ? alg->m_type : AlgorithmAtom::UnknownAlg) {
872 case AlgorithmAtom::UnknownAlg:
873 warnMsooXml << "Layout with name=" << m_name << "defines an unknown algorithm.";
874 // fall through and use the composite-algorithm
875 case AlgorithmAtom::CompositeAlg: m_algorithmImpl = new CompositeAlgorithm; break;
876 case AlgorithmAtom::ConnectorAlg: m_algorithmImpl = new ConnectorAlgorithm; break;
877 case AlgorithmAtom::CycleAlg: m_algorithmImpl = new CycleAlgorithm; break;
878 case AlgorithmAtom::HierChildAlg: m_algorithmImpl = new HierarchyAlgorithm(false); break;
879 case AlgorithmAtom::HierRootAlg: m_algorithmImpl = new HierarchyAlgorithm(true); break;
880 case AlgorithmAtom::LinearAlg: m_algorithmImpl = new LinearAlgorithm; break;
881 case AlgorithmAtom::PyramidAlg: m_algorithmImpl = new LinearAlgorithm; break;
882 case AlgorithmAtom::SnakeAlg: m_algorithmImpl = new SnakeAlgorithm; break;
883 case AlgorithmAtom::SpaceAlg: m_algorithmImpl = new SpaceAlg; break;
884 case AlgorithmAtom::TextAlg: m_algorithmImpl = new TextAlgorithm; break;
885 }
886 }
887
layoutAtom(Context * context)888 void LayoutNodeAtom::layoutAtom(Context* context) {
889 if(m_algorithmImpl) {
890 m_algorithmImpl->doInit(context, QExplicitlySharedDataPointer<LayoutNodeAtom>(this));
891 }
892 if(m_needsRelayout && m_algorithmImpl) {
893 m_needsRelayout = false;
894 m_childNeedsRelayout = true;
895 m_algorithmImpl->doLayout();
896 }
897 if(m_childNeedsRelayout && m_algorithmImpl) {
898 m_childNeedsRelayout = false;
899 m_algorithmImpl->doLayoutChildren();
900 }
901 }
902
writeAtom(Context * context,KoXmlWriter * xmlWriter,KoGenStyles * styles)903 void LayoutNodeAtom::writeAtom(Context* context, KoXmlWriter* xmlWriter, KoGenStyles* styles) {
904 QExplicitlySharedDataPointer<LayoutNodeAtom> oldLayout = context->m_parentLayout;
905 context->m_parentLayout = this;
906 AbstractAtom::writeAtom(context, xmlWriter, styles);
907 context->m_parentLayout = oldLayout;
908 }
909
constraints() const910 QList< QExplicitlySharedDataPointer<ConstraintAtom> > LayoutNodeAtom::constraints() const {
911 QList< QExplicitlySharedDataPointer<ConstraintAtom> > result;
912 foreach( QExplicitlySharedDataPointer<AbstractAtom> atom, m_children )
913 {
914 ConstraintAtom* constraintAtom = dynamic_cast< ConstraintAtom* >( atom.data() );
915 if ( constraintAtom ) {
916 result.append( QExplicitlySharedDataPointer<ConstraintAtom>(constraintAtom));
917 } else if (ListAtom *list = dynamic_cast< ListAtom* >( atom.data() ) ) {
918 foreach( QExplicitlySharedDataPointer<AbstractAtom> val, list->children() ) {
919 constraintAtom = dynamic_cast< ConstraintAtom* >( val.data() );
920 if ( constraintAtom )
921 result.append(QExplicitlySharedDataPointer<ConstraintAtom>(constraintAtom));
922 }
923 }
924 }
925 return result;
926 }
927
shapes() const928 QList< QExplicitlySharedDataPointer<ShapeAtom> > LayoutNodeAtom::shapes() const {
929 QList< QExplicitlySharedDataPointer<ShapeAtom> > result;
930 foreach( QExplicitlySharedDataPointer<AbstractAtom> atom, m_children ) {
931 ShapeAtom* shapeAtom = dynamic_cast< ShapeAtom* >( atom.data() );
932 if ( shapeAtom ) {
933 result.append(QExplicitlySharedDataPointer<ShapeAtom>(dynamic_cast< ShapeAtom* >( atom.data() )));
934 } else if (ListAtom *list = dynamic_cast< ListAtom* >( atom.data() ) ) {
935 foreach( QExplicitlySharedDataPointer<AbstractAtom> val, list->children() ) {
936 shapeAtom = dynamic_cast< ShapeAtom* >( val.data() );
937 if ( shapeAtom )
938 result.append(QExplicitlySharedDataPointer<ShapeAtom>(shapeAtom));
939 }
940 }
941 }
942 return result;
943 }
944
algorithmImpl() const945 AbstractAlgorithm* LayoutNodeAtom::algorithmImpl() const {
946 return m_algorithmImpl;
947 }
948
algorithm() const949 QExplicitlySharedDataPointer<AlgorithmAtom> LayoutNodeAtom::algorithm() const {
950 foreach(QExplicitlySharedDataPointer<AbstractAtom> child, children())
951 if(AlgorithmAtom* alg = dynamic_cast<AlgorithmAtom*>(child.data()))
952 return QExplicitlySharedDataPointer<AlgorithmAtom>(alg);
953 return QExplicitlySharedDataPointer<AlgorithmAtom>();
954 }
955
axis(Context * context) const956 QList<AbstractNode*> LayoutNodeAtom::axis(Context* context) const {
957 return context->m_layoutPointMap.values(this);
958 }
959
setAxis(Context * context,const QList<AbstractNode * > & axis)960 void LayoutNodeAtom::setAxis(Context* context, const QList<AbstractNode*> &axis) {
961 Q_UNUSED(context);
962 // first remove the previous axis
963 foreach(AbstractNode* node, context->m_layoutPointMap.values(this)) {
964 context->m_pointLayoutMap.remove(node, this);
965 }
966 context->m_layoutPointMap.remove(this);
967 Q_ASSERT(!context->m_pointLayoutMap.values().contains(this));
968 Q_ASSERT(!context->m_layoutPointMap.keys().contains(this));
969 // then set the new axis
970 foreach(AbstractNode* node, axis) {
971 context->m_layoutPointMap.insertMulti(this, node);
972 context->m_pointLayoutMap.insertMulti(node, this);
973 }
974 // job done, new layout needed
975 setNeedsRelayout(true);
976 }
977
setAxis(Context * context,PresentationOfAtom * atom)978 void LayoutNodeAtom::setAxis(Context* context, PresentationOfAtom* atom) {
979 setAxis(context, fetchAxis(context, atom->m_axis, atom->m_ptType, atom->m_start, atom->m_count, atom->m_step));
980 }
981
setNeedsReinit(bool needsReinit)982 void LayoutNodeAtom::setNeedsReinit(bool needsReinit) {
983 if(m_needsReinit == needsReinit) return;
984 m_needsReinit = needsReinit;
985 if(m_needsReinit) // if we need to be re-initialized then our children need to be too
986 foreach(QExplicitlySharedDataPointer<AbstractAtom> child, children())
987 if(LayoutNodeAtom* childLayoutAtom = dynamic_cast<LayoutNodeAtom*>(child.data())) {
988 childLayoutAtom->setNeedsReinit(true);
989 }
990 }
991
setNeedsRelayout(bool needsRelayout)992 void LayoutNodeAtom::setNeedsRelayout(bool needsRelayout) {
993 if(needsRelayout == m_needsRelayout) return;
994 m_needsRelayout = needsRelayout;
995 if(m_needsRelayout) // let parent-layouts know that we need a relayout
996 if(QExplicitlySharedDataPointer<LayoutNodeAtom> p = parentLayout())
997 p->m_childNeedsRelayout = true;
998 }
999
algorithmType() const1000 AlgorithmAtom::Algorithm LayoutNodeAtom::algorithmType() const {
1001 if(QExplicitlySharedDataPointer<AlgorithmAtom> alg = algorithm())
1002 return alg->m_type;
1003 return AlgorithmAtom::UnknownAlg;
1004 }
1005
algorithmParams() const1006 QMap<QString,QString> LayoutNodeAtom::algorithmParams() const {
1007 if(QExplicitlySharedDataPointer<AlgorithmAtom> alg = algorithm())
1008 return alg->m_params;
1009 return QMap<QString,QString>();
1010 }
1011
algorithmParam(const QString & name,const QString & defaultValue) const1012 QString LayoutNodeAtom::algorithmParam(const QString &name, const QString &defaultValue) const {
1013 QMap<QString,QString> params = algorithmParams();
1014 return params.contains(name) ? params[name] : defaultValue;
1015 }
1016
variable(const QString & name,bool checkParents) const1017 QString LayoutNodeAtom::variable(const QString &name, bool checkParents) const {
1018 if(m_variables.contains(name))
1019 return m_variables[name];
1020 if(checkParents)
1021 if(QExplicitlySharedDataPointer<LayoutNodeAtom> p = parentLayout())
1022 return p->variable(name, checkParents);
1023 return QString();
1024 }
1025
variables() const1026 QMap<QString, QString> LayoutNodeAtom::variables() const { return m_variables; }
setVariable(const QString & name,const QString & value)1027 void LayoutNodeAtom::setVariable(const QString &name, const QString &value) { m_variables[name] = value; }
1028
finalValues() const1029 QMap<QString, qreal> LayoutNodeAtom::finalValues() const {
1030 //TODO cache
1031 //debugMsooXml << m_name;
1032 ValueCache result = m_values;
1033 //QMap< QString, qreal > print = m_values;
1034 //debugMsooXml << "prefinal: " << print;
1035 //debugMsooXml << "final values";
1036 for( QMap< QString, qreal>::const_iterator it = m_factors.constBegin(); it != m_factors.constEnd(); ++it ) {
1037 result[ it.key() ] = result[ it.key() ] * it.value() / qreal ( m_countFactors[ it.key() ] );
1038 //debugMsooXml << "key " << it.key() << " value: " << it.value() << " count: " << m_countFactors[ it.key() ];
1039 }
1040 //debugMsooXml << "end of final values";
1041 return result;
1042 }
1043
fetchLayouts(Context * context,const QString & forAxis,const QString & forName,const QString & ptType) const1044 QVector< QExplicitlySharedDataPointer<LayoutNodeAtom> > LayoutNodeAtom::fetchLayouts(Context* context, const QString &forAxis, const QString &forName, const QString &ptType) const {
1045 QVector< QExplicitlySharedDataPointer<LayoutNodeAtom> > list;
1046 if ( forAxis == "self" || forAxis.isEmpty() ) {
1047 list.append( QExplicitlySharedDataPointer<LayoutNodeAtom>(const_cast<LayoutNodeAtom*>(this)) );
1048 } else {
1049 if ( forAxis == "ch" ) { // Children
1050 list = childrenLayouts();
1051 } else if ( forAxis == "des" ) { // Descendant
1052 list = descendantLayouts();
1053 } else {
1054 ASSERT_X(false, QString("Unsupported forAxis '%1'").arg( forAxis ).toLocal8Bit());
1055 }
1056 }
1057 QVector< QExplicitlySharedDataPointer<LayoutNodeAtom> > result;
1058 foreach(const QExplicitlySharedDataPointer<LayoutNodeAtom> &l, list) {
1059 if (!forName.isEmpty() && forName != l->m_name) {
1060 continue;
1061 }
1062 if (!ptType.isEmpty()) {
1063 bool ptTypeMatches = false;
1064 foreach(AbstractNode* node, l->axis( context )) {
1065 if ( PointNode *ptNode = dynamic_cast< PointNode* >( node ) ) {
1066 if (ptType != ptNode->m_type)
1067 continue;
1068 } else if ( ConnectionNode *connNode = dynamic_cast< ConnectionNode* >( node ) ) {
1069 if (ptType != connNode->m_type)
1070 continue;
1071 }
1072 ptTypeMatches = true;
1073 break;
1074 }
1075 if (!ptTypeMatches) {
1076 continue;
1077 }
1078 }
1079 result.append(l);
1080 }
1081 return result;
1082 }
1083
childrenLayouts() const1084 QVector< QExplicitlySharedDataPointer<LayoutNodeAtom> > LayoutNodeAtom::childrenLayouts() const {
1085 QVector< QExplicitlySharedDataPointer<LayoutNodeAtom> > result;
1086 foreach(QExplicitlySharedDataPointer<AbstractAtom> atom, children())
1087 if(LayoutNodeAtom* l = dynamic_cast<LayoutNodeAtom*>(atom.data()))
1088 result.append(QExplicitlySharedDataPointer<LayoutNodeAtom>(l));
1089 return result;
1090 }
1091
descendantLayouts() const1092 QVector< QExplicitlySharedDataPointer<LayoutNodeAtom> > LayoutNodeAtom::descendantLayouts() const {
1093 QVector< QExplicitlySharedDataPointer<LayoutNodeAtom> > result = childrenLayouts();
1094 foreach(QExplicitlySharedDataPointer<AbstractAtom> atom, children())
1095 if(LayoutNodeAtom* l = dynamic_cast<LayoutNodeAtom*>(atom.data()))
1096 foreach(QExplicitlySharedDataPointer<LayoutNodeAtom> atom, l->descendantLayouts())
1097 result.append(atom);
1098 return result;
1099 }
1100
neighbors() const1101 QPair<LayoutNodeAtom*,LayoutNodeAtom*> LayoutNodeAtom::neighbors() const {
1102 QExplicitlySharedDataPointer<LayoutNodeAtom> parentlayout = parentLayout();
1103 Q_ASSERT(parentlayout);
1104 QList<LayoutNodeAtom*> siblingLayouts;
1105 int myindex = -1;
1106 foreach(QExplicitlySharedDataPointer<AbstractAtom> atom, parent()->children()) {
1107 if(LayoutNodeAtom* l = dynamic_cast<LayoutNodeAtom*>(atom.data())) {
1108 if(l == this) myindex = siblingLayouts.count();
1109 siblingLayouts.append(l);
1110 }
1111 }
1112 Q_ASSERT(myindex >= 0); // our parent should know about us else something is fundamental broken
1113 if(siblingLayouts.count() < 3) // if we don't have enough neighbors then abort and return NULL for both
1114 return QPair<LayoutNodeAtom*,LayoutNodeAtom*>(0,0);
1115
1116 // Look if our index is the first or last in the list and if that's the case then wrap around the list
1117 // if the used algorithm-type is expected to produced a "circle".
1118 int srcIndex = myindex - 1;
1119 int dstIndex = myindex + 1;
1120 if(srcIndex < 0) {
1121 if(parentlayout->algorithmType() != AlgorithmAtom::CycleAlg)
1122 return QPair<LayoutNodeAtom*,LayoutNodeAtom*>(0,0);
1123 srcIndex = siblingLayouts.count() - 1;
1124 }
1125 if(dstIndex < siblingLayouts.count()) {
1126 --myindex;
1127 } else {
1128 if(parentlayout->algorithmType() != AlgorithmAtom::CycleAlg)
1129 return QPair<LayoutNodeAtom*,LayoutNodeAtom*>(0,0);
1130 dstIndex = 0;
1131 }
1132
1133 LayoutNodeAtom* srcAtom = siblingLayouts[srcIndex];
1134 LayoutNodeAtom* dstAtom = siblingLayouts[dstIndex];
1135 return QPair<LayoutNodeAtom*,LayoutNodeAtom*>(srcAtom,dstAtom);
1136 }
1137
childrenUsedSize() const1138 QSizeF LayoutNodeAtom::childrenUsedSize() const {
1139 qreal w = 0;
1140 qreal h = 0;
1141 foreach( const QExplicitlySharedDataPointer<LayoutNodeAtom> &l, childrenLayouts() ) {
1142 QMap< QString, qreal > vals = l->finalValues();
1143 if ( l->algorithmType() != AlgorithmAtom::SpaceAlg ) {
1144 h += vals[ "h" ];
1145 w += vals[ "w" ];
1146 }
1147 }
1148 return QSizeF(w, h);
1149 }
1150
childrenTotalSize() const1151 QSizeF LayoutNodeAtom::childrenTotalSize() const {
1152 qreal w = 0;
1153 qreal h = 0;
1154 foreach( const QExplicitlySharedDataPointer<LayoutNodeAtom> &l, childrenLayouts() ) {
1155 QMap< QString, qreal > vals = l->finalValues();
1156 h += vals[ "h" ];
1157 w += vals[ "w" ];
1158 }
1159 return QSizeF(w, h);
1160 }
1161
distanceTo(LayoutNodeAtom * otherAtom) const1162 qreal LayoutNodeAtom::distanceTo(LayoutNodeAtom* otherAtom) const {
1163 //TODO specs are missing details from which exact point to calc the distance from...
1164 #if 0
1165 QMap<QString, qreal> srcValues = this->m_values;
1166 QMap<QString, qreal> dstValues = otherAtom->m_values;
1167 //QMap<QString, qreal> srcFactors = this->m_factors;
1168 //QMap<QString, qreal> dstFactors = otherAtom->m_factors;
1169 //QMap<QString, int> srcCountFactors = this->m_countFactors;
1170 //QMap<QString, int> dstCountFactors = otherAtom->m_countFactors;
1171 #else
1172 QMap<QString, qreal> srcValues = this->finalValues();
1173 QMap<QString, qreal> dstValues = otherAtom->finalValues();
1174 #endif
1175 qreal srcX = srcValues["l"];// + srcValues["ctrX"];
1176 qreal srcY = srcValues["t"];// + srcValues["ctrY"];
1177 qreal dstX = dstValues["l"];// + dstValues["ctrX"];
1178 qreal dstY = dstValues["t"];// + dstValues["ctrY"];
1179 // qreal srcX = srcValues["l"] + srcValues["ctrX"] + srcValues["w"] / 2;
1180 // qreal srcY = srcValues["t"] + srcValues["ctrY"] + srcValues["h"] / 2;
1181 // qreal dstX = dstValues["l"] + dstValues["ctrX"] + dstValues["w"] / 2;
1182 // qreal dstY = dstValues["t"] + dstValues["ctrY"] + dstValues["h"] / 2;
1183 qreal diffX = dstX - srcX;
1184 qreal diffY = dstY - srcY;
1185 //qreal diffX = dstX - srcX - srcValues["w"]/2 - dstValues["w"] / 2;
1186 //qreal diffY = dstY - srcY - srcValues["h"]/2 - dstValues["h"] / 2;
1187 return sqrt(diffX*diffX + diffY*diffY);
1188 }
1189
1190 /****************************************************************************************************/
1191
clone(Context * context)1192 ConstraintAtom* ConstraintAtom::clone(Context* context) {
1193 ConstraintAtom* atom = new ConstraintAtom;
1194 atom->m_fact = m_fact;
1195 atom->m_for = m_for;
1196 atom->m_forName = m_forName;
1197 atom->m_op = m_op;
1198 atom->m_ptType = m_ptType;
1199 atom->m_refPtType = m_refPtType;
1200 atom->m_refType = m_refType;
1201 atom->m_refFor = m_refFor;
1202 atom->m_refForName = m_refForName;
1203 atom->m_type = m_type;
1204 atom->m_value = m_value;
1205 foreach(QExplicitlySharedDataPointer<AbstractAtom> a, m_children)
1206 atom->addChild(a->clone(context));
1207 return atom;
1208 }
1209
dump() const1210 QString ConstraintAtom::dump() const {
1211 QString s;
1212 if(!m_fact.isEmpty()) s += QString("fact=%1 ").arg(m_fact);
1213 if(!m_for.isEmpty()) s += QString("for=%1 ").arg(m_for);
1214 if(!m_forName.isEmpty()) s += QString("forName=%1 ").arg(m_forName);
1215 if(!m_op.isEmpty()) s += QString("op=%1 ").arg(m_op);
1216 if(!m_ptType.isEmpty()) s += QString("ptType=%1 ").arg(m_ptType);
1217 if(!m_refPtType.isEmpty()) s += QString("refPtType=%1 ").arg(m_refPtType);
1218 if(!m_refType.isEmpty()) s += QString("refType=%1 ").arg(m_refType);
1219 if(!m_refFor.isEmpty()) s += QString("refFor=%1 ").arg(m_refFor);
1220 if(!m_refForName.isEmpty()) s += QString("refForName=%1 ").arg(m_refForName);
1221 if(!m_type.isEmpty()) s += QString("type=%1 ").arg(m_type);
1222 if(!m_value.isEmpty()) s += QString("val=%1 ").arg(m_value);
1223 return s.trimmed();
1224 }
1225
dump(Context *,int level)1226 void ConstraintAtom::dump(Context*, int level) {
1227 DEBUG_DUMP << dump();
1228 }
1229
readAll(Context *,MsooXmlDiagramReader * reader)1230 void ConstraintAtom::readAll(Context*, MsooXmlDiagramReader* reader) {
1231 const QXmlStreamAttributes attrs(reader->attributes());
1232 TRY_READ_ATTR_WITHOUT_NS_INTO(fact, m_fact)
1233 TRY_READ_ATTR_WITHOUT_NS_INTO(for, m_for)
1234 TRY_READ_ATTR_WITHOUT_NS_INTO(forName, m_forName)
1235 TRY_READ_ATTR_WITHOUT_NS_INTO(op, m_op)
1236 TRY_READ_ATTR_WITHOUT_NS_INTO(ptType, m_ptType)
1237 TRY_READ_ATTR_WITHOUT_NS_INTO(refPtType, m_refPtType)
1238 TRY_READ_ATTR_WITHOUT_NS_INTO(refType, m_refType)
1239 TRY_READ_ATTR_WITHOUT_NS_INTO(refFor, m_refFor)
1240 TRY_READ_ATTR_WITHOUT_NS_INTO(refForName, m_refForName)
1241 TRY_READ_ATTR_WITHOUT_NS_INTO(type, m_type)
1242 TRY_READ_ATTR_WITHOUT_NS_INTO(val, m_value)
1243 //AbstractAtom::readAll(context, reader);
1244 }
1245
build(Context * context)1246 void ConstraintAtom::build(Context* context) {
1247 AbstractAtom::build(context);
1248 }
1249
finishBuild(Context * context)1250 void ConstraintAtom::finishBuild(Context* context) {
1251 #if 0
1252 QExplicitlySharedDataPointer<ConstraintAtom> ptr(this);
1253 QVector< QExplicitlySharedDataPointer<ConstraintAtom> > addedConstraints;
1254
1255 // first evaluate on which layouts this constraint should be applied.
1256 if ( m_for == "self" || m_for.isEmpty() ) {
1257 // nothing to do cause this constraint is already attached to the correct layout.
1258 Q_ASSERT( context->m_parentLayout->constraints().contains(ptr) );
1259 } else {
1260 // We need to select the chosen data-points and determinate the layoutNotes which are connected with
1261 // them to look where we need to move this constraint to.
1262 QList<AbstractNode*> nodes;
1263 if ( m_for == "ch" ) { // Children
1264 nodes = context->currentNode()->children();
1265 } else if ( m_for == "des" ) { // Descendant
1266 nodes = context->currentNode()->descendant();
1267 } else {
1268 Q_ASSERT_X(false, __FUNCTION__, QString("Constraint with unhandled 'for' %1").arg( dump() ).toLocal8Bit());
1269 }
1270
1271 QVector< AbstractNode* > childDataPoints;
1272 foreach( AbstractNode* node, nodes ) {
1273 if ( !m_ptType.isEmpty() ) {
1274 if ( PointNode *ptNode = dynamic_cast< PointNode* >( node ) ) {
1275 if (m_ptType != ptNode->m_type)
1276 continue;
1277 } else if ( ConnectionNode *connNode = dynamic_cast< ConnectionNode* >( node ) ) {
1278 if (m_ptType != connNode->m_type)
1279 continue;
1280 }
1281 }
1282 childDataPoints.append( node );
1283 }
1284
1285 /*TODO why the following? how does that make sense?
1286 if ( m_ptType.isEmpty() )
1287 childDataPoints.append( context->currentNode() );
1288 if ( m_refPtType.isEmpty() )
1289 refChildDataPoints.append( context->currentNode() );
1290 */
1291
1292 Q_ASSERT( !childDataPoints.isEmpty() );
1293
1294 bool constraintedWasApplied = false;
1295 foreach(AbstractNode* node, childDataPoints) {
1296 foreach(LayoutNodeAtom* a, context->m_pointLayoutMap.values(node)) {
1297 if ( !m_forName.isEmpty() && a->m_name != m_forName )
1298 continue;
1299
1300 QExplicitlySharedDataPointer<ConstraintAtom> clonedPtr( ptr->clone(context) );
1301 a->addChild(clonedPtr);
1302 addedConstraints.append(clonedPtr);
1303 constraintedWasApplied = true;
1304 }
1305 }
1306 if (!constraintedWasApplied) dump(0,2);
1307 Q_ASSERT_X(constraintedWasApplied, __FUNCTION__, QString("Constraint could not be applied %1").arg( dump() ).toLocal8Bit());
1308
1309 // this constraint is handled now and we can detach it
1310 Q_ASSERT( context->m_parentLayout->constraints().contains(ptr) );
1311 parent()->removeChild(ptr);
1312 }
1313 // and now evaluated the referenced layout definitions
1314 if ( m_refFor == "self" || m_refFor.isEmpty() ) {
1315 /*
1316 foreach(QExplicitlySharedDataPointer<ConstraintAtom> constraint, addedConstraints) {
1317 constraint->m_referencedLayouts.append( context->m_parentLayout );
1318 }
1319 */
1320 } else {
1321 QList<AbstractNode*> nodes;
1322 if ( m_refFor == "ch" ) { // Children
1323 nodes = context->currentNode()->children();
1324 } else if ( m_refFor == "des" ) { // Descendant
1325 nodes = context->currentNode()->descendant();
1326 } else {
1327 Q_ASSERT_X(false, __FUNCTION__, QString("Constraint with unhandled 'refFor' %1").arg( dump() ).toLocal8Bit());
1328 }
1329
1330 QVector< AbstractNode* > childDataPoints;
1331 foreach( AbstractNode* node, nodes ) {
1332 if ( !m_refPtType.isEmpty() ) {
1333 if ( PointNode *ptNode = dynamic_cast< PointNode* >( node ) ) {
1334 if (m_refPtType != ptNode->m_type)
1335 continue;
1336 } else if ( ConnectionNode *connNode = dynamic_cast< ConnectionNode* >( node ) ) {
1337 if (m_refPtType != connNode->m_type)
1338 continue;
1339 }
1340 }
1341 childDataPoints.append( node );
1342 }
1343
1344 Q_ASSERT( !childDataPoints.isEmpty() );
1345
1346 bool referenceWasApplied = false;
1347 foreach(AbstractNode* node, childDataPoints) {
1348 Q_ASSERT(context->m_pointLayoutMap.contains(node));
1349 foreach(LayoutNodeAtom* a, context->m_pointLayoutMap.values(node)) {
1350 if ( !m_refForName.isEmpty() && a->m_name != m_refForName )
1351 continue;
1352
1353 QExplicitlySharedDataPointer<LayoutNodeAtom> aPtr( a );
1354 foreach(QExplicitlySharedDataPointer<ConstraintAtom> constraint, addedConstraints) {
1355 constraint->m_referencedLayouts.append( aPtr );
1356 }
1357 referenceWasApplied = true;
1358 }
1359 }
1360 Q_ASSERT_X(referenceWasApplied, __FUNCTION__, QString("Reference of constraint could not be applied %1").arg( dump() ).toLocal8Bit());
1361 }
1362 #else
1363 Q_UNUSED(context);
1364 #endif
1365 }
1366
applyConstraint(Context * context,LayoutNodeAtom * atom)1367 void ConstraintAtom::applyConstraint(Context* context, LayoutNodeAtom* atom) {
1368 // Following block shows how we tried to determinate the layouts using there data-points. But that seems to be
1369 // wrong (with me07_basic_radial.xlsx) cause 'for' and 'refFor' are referring to the layout-tree and not the
1370 // data-tree which can be rather different.
1371 #if 0
1372 QExplicitlySharedDataPointer<ConstraintAtom> ptr(this);
1373 QList< LayoutNodeAtom* > applyLayouts;
1374 QList< LayoutNodeAtom* > referencedLayouts;
1375 if ( m_for == "self" || m_for.isEmpty() ) {
1376 applyLayouts.append( atom /* context->m_parentLayout.data() */ );
1377 } else {
1378 QList<AbstractNode*> nodes;
1379 if ( m_for == "ch" ) { // Children
1380 nodes = context->currentNode()->children();
1381 } else if ( m_for == "des" ) { // Descendant
1382 nodes = context->currentNode()->descendant();
1383 } else {
1384 Q_ASSERT_X(false, __FUNCTION__, QString("Constraint with unhandled 'for' %1").arg( dump() ).toLocal8Bit());
1385 }
1386 QVector< AbstractNode* > childDataPoints;
1387 foreach( AbstractNode* node, nodes ) {
1388 if ( !m_ptType.isEmpty() ) {
1389 if ( PointNode *ptNode = dynamic_cast< PointNode* >( node ) ) {
1390 if (m_ptType != ptNode->m_type)
1391 continue;
1392 } else if ( ConnectionNode *connNode = dynamic_cast< ConnectionNode* >( node ) ) {
1393 if (m_ptType != connNode->m_type)
1394 continue;
1395 } else {
1396 Q_ASSERT_X(false, __FUNCTION__, QString("Unhandled ptType=%1 for node=%2").arg(m_ptType).arg(node->m_tagName).toLocal8Bit());
1397 }
1398 }
1399 childDataPoints.append( node );
1400 }
1401 Q_ASSERT_X(!childDataPoints.isEmpty(), __FUNCTION__, QString("No data-points selected for constraint %1").arg(dump()).toLocal8Bit());
1402 foreach(AbstractNode* node, childDataPoints) {
1403 foreach(LayoutNodeAtom* a, context->m_pointLayoutMap.values(node)) {
1404 if ( m_forName.isEmpty() || a->m_name == m_forName )
1405 applyLayouts.append( a );
1406 }
1407 }
1408 Q_ASSERT_X(!applyLayouts.isEmpty(), __FUNCTION__, QString("Failed to determinate the layout on which to apply the constraint %1").arg( dump() ).toLocal8Bit());
1409 }
1410 if ( m_refFor == "self" || m_refFor.isEmpty() ) {
1411 referencedLayouts.append( atom /* context->m_parentLayout.data() */ );
1412 } else {
1413 QList<AbstractNode*> nodes;
1414 if ( m_refFor == "ch" ) { // Children
1415 nodes = context->currentNode()->children();
1416 } else if ( m_refFor == "des" ) { // Descendant
1417 nodes = context->currentNode()->descendant();
1418 } else {
1419 Q_ASSERT_X(false, __FUNCTION__, QString("Constraint with unhandled 'refFor' %1").arg( dump() ).toLocal8Bit());
1420 }
1421 QVector< AbstractNode* > childDataPoints;
1422 foreach( AbstractNode* node, nodes ) {
1423 if ( !m_refPtType.isEmpty() ) {
1424 if ( PointNode *ptNode = dynamic_cast< PointNode* >( node ) ) {
1425 if (m_refPtType != ptNode->m_type)
1426 continue;
1427 } else if ( ConnectionNode *connNode = dynamic_cast< ConnectionNode* >( node ) ) {
1428 if (m_refPtType != connNode->m_type)
1429 continue;
1430 } else {
1431 Q_ASSERT_X(false, __FUNCTION__, QString("Unhandled ptType=%1 for node=%2").arg(m_ptType).arg(node->m_tagName).toLocal8Bit());
1432 }
1433 }
1434 childDataPoints.append( node );
1435 }
1436 Q_ASSERT_X(!childDataPoints.isEmpty(), __FUNCTION__, QString("No data-points selected for constraint %1").arg(dump()).toLocal8Bit());
1437 foreach(AbstractNode* node, childDataPoints)
1438 foreach(LayoutNodeAtom* a, context->m_pointLayoutMap.values(node))
1439 if ( m_refForName.isEmpty() || a->m_name == m_refForName )
1440 referencedLayouts.append(a);
1441 Q_ASSERT_X(!referencedLayouts.isEmpty(), __FUNCTION__, QString("Failed to determinate the referenced layouts for the constraint %1").arg( dump() ).toLocal8Bit());
1442 }
1443 #else
1444 QVector< QExplicitlySharedDataPointer<LayoutNodeAtom> > applyLayouts = atom->fetchLayouts(context, m_for, m_forName, m_ptType);
1445 QVector< QExplicitlySharedDataPointer<LayoutNodeAtom> > referencedLayouts = atom->fetchLayouts(context, m_refFor, m_refForName, m_refPtType);
1446
1447 ASSERT_X(!applyLayouts.isEmpty(), QString("Failed to determinate the layouts for the constraint %1").arg( dump() ).toLocal8Bit());
1448 ASSERT_X(!referencedLayouts.isEmpty(), QString("Failed to determinate the referenced layouts for the constraint %1").arg( dump() ).toLocal8Bit());
1449 debugMsooXml << dump();
1450
1451 foreach(const QExplicitlySharedDataPointer<LayoutNodeAtom> &applyLayout, applyLayouts) {
1452 debugMsooXml << "AppLayout: " << applyLayout->m_name;
1453 if( !m_value.isEmpty() ) {
1454 bool ok;
1455 qreal value = m_value.toDouble( &ok );
1456 ASSERT_X(ok, QString("Layout with name=%1 defines none-double value=%2").arg( atom->m_name ).arg( m_value ).toLocal8Bit());
1457 debugMsooXml << "applyValue: " << value;
1458 if (ok) {
1459 //applyLayout->m_factors.clear();
1460 //applyLayout->m_countFactors.clear();
1461 applyLayout->m_values[ m_type ] = value;
1462 applyLayout->setNeedsRelayout( true );
1463 }
1464 } else {
1465 //TODO proper handle the case where more then one layouts are referenced (means proper eval the constraints operator)
1466 LayoutNodeAtom* referencedLayout = referencedLayouts.isEmpty() ? atom : referencedLayouts.first().data();
1467 Q_ASSERT(referencedLayout);
1468
1469 AbstractAlgorithm* r = referencedLayout->algorithmImpl();
1470 ASSERT_X(r, QString("No algorithm in referenced layout=%1 for constraint='%2'").arg( referencedLayout->m_name ).arg( dump() ).toLocal8Bit());
1471
1472 const QMap<QString, qreal> values = referencedLayout->finalValues();
1473 const QString type = m_refType.isEmpty() ? m_type : m_refType;
1474
1475 qreal value = -1.0;
1476 if( values.contains( type ) ) {
1477 value = values[ type ];
1478 debugMsooXml << "finalValue: " << value;
1479 } else {
1480 value = r ? r->defaultValue( type, values ) : -1.0;
1481 ASSERT_X(value >= 0.0, QString("algorithm=%1 value=%2 constraint='%3'").arg( r ? r->name() : "NULL" ).arg( value ).arg( dump() ).toLocal8Bit());
1482 if (value < 0.0) continue;
1483 debugMsooXml << "default Value: " << value;
1484 }
1485 applyLayout->m_values[ m_type ] = value;
1486 applyLayout->setNeedsRelayout( true );
1487 //applyLayout->m_factors.clear();
1488 //applyLayout->m_countFactors.clear();
1489 }
1490 if ( !m_fact.isEmpty() ) {
1491 bool ok;
1492 qreal v = m_fact.toDouble( &ok );
1493 ASSERT_X(ok, QString("Layout with name=%1 defines none-double factor=%2").arg( atom->m_name ).arg( m_fact ).toLocal8Bit());
1494 debugMsooXml << "fact: " << v;
1495 if (ok) {
1496 applyLayout->m_factors[ m_type ] += v;
1497 applyLayout->m_countFactors[ m_type ] += 1;
1498 //applyLayout->m_values[ m_type ] = applyLayout->m_values[ m_type ] * v;
1499 applyLayout->setNeedsRelayout( true );
1500 }
1501 }
1502 }
1503 #endif
1504 }
1505
1506 /****************************************************************************************************/
1507
clone(Context *)1508 AdjustAtom* AdjustAtom::clone(Context*) {
1509 AdjustAtom* atom = new AdjustAtom;
1510 atom->m_index = m_index;
1511 atom->m_value = m_value;
1512 return atom;
1513 }
1514
dump(Context *,int level)1515 void AdjustAtom::dump(Context*, int level) {
1516 DEBUG_DUMP << "index=" << m_index << "value=" << m_value;
1517 }
1518
readAll(Context *,MsooXmlDiagramReader * reader)1519 void AdjustAtom::readAll(Context*, MsooXmlDiagramReader* reader) {
1520 const QXmlStreamAttributes attrs(reader->attributes());
1521 TRY_READ_ATTR_WITHOUT_NS(idx)
1522 m_index = idx.toInt();
1523 TRY_READ_ATTR_WITHOUT_NS(val)
1524 m_value = val.toDouble();
1525 }
1526
1527 // http://social.msdn.microsoft.com/Forums/en-US/os_binaryfile/thread/74f86b76-37be-4087-b5b0-cf2fc68d5595/
applyAdjustment(Context *,LayoutNodeAtom *)1528 void AdjustAtom::applyAdjustment(Context* /* context */, LayoutNodeAtom* /* atom */) {
1529 ASSERT_X(m_index >= 0 && m_index < context->m_shapeList.count(), QString("Index is out of bounds, index=%1 min=0 max=%2").arg(m_index).arg(context->m_shapeList.count()-1).toLocal8Bit());
1530 //TODO
1531 //ShapeAtom *shape = context->m_shapeList.at(m_index);
1532 //if (m_value > 90) m_value = 360 - (m_value - 90);
1533 //shape->parentLayout()->m_rotateAngle = m_value;
1534 }
1535
1536 /****************************************************************************************************/
1537
clone(Context *)1538 RuleAtom* RuleAtom::clone(Context*) {
1539 RuleAtom* atom = new RuleAtom;
1540 atom->m_fact = m_fact;
1541 atom->m_for = m_for;
1542 atom->m_forName = m_forName;
1543 atom->m_max = m_max;
1544 atom->m_ptType = m_ptType;
1545 atom->m_type = m_type;
1546 atom->m_value = m_value;
1547 return atom;
1548 }
1549
dump(Context *,int level)1550 void RuleAtom::dump(Context*, int level) {
1551 QString s;
1552 if(!m_fact.isEmpty()) s += QString("fact=%1 ").arg(m_fact);
1553 if(!m_for.isEmpty()) s += QString("for=%1 ").arg(m_for);
1554 if(!m_forName.isEmpty()) s += QString("forName=%1 ").arg(m_forName);
1555 if(!m_max.isEmpty()) s += QString("max=%1 ").arg(m_max);
1556 if(!m_ptType.isEmpty()) s += QString("ptType=%1 ").arg(m_ptType);
1557 if(!m_type.isEmpty()) s += QString("type=%1 ").arg(m_type);
1558 if(!m_value.isEmpty()) s += QString("val=%1 ").arg(m_value);
1559 DEBUG_DUMP << s;
1560 }
1561
readAll(Context *,MsooXmlDiagramReader * reader)1562 void RuleAtom::readAll(Context*, MsooXmlDiagramReader* reader) {
1563 const QXmlStreamAttributes attrs(reader->attributes());
1564 TRY_READ_ATTR_WITHOUT_NS_INTO(fact, m_fact)
1565 TRY_READ_ATTR_WITHOUT_NS_INTO(for, m_for)
1566 TRY_READ_ATTR_WITHOUT_NS_INTO(forName, m_forName)
1567 TRY_READ_ATTR_WITHOUT_NS_INTO(max, m_max)
1568 TRY_READ_ATTR_WITHOUT_NS_INTO(ptType, m_ptType)
1569 TRY_READ_ATTR_WITHOUT_NS_INTO(type, m_type)
1570 TRY_READ_ATTR_WITHOUT_NS_INTO(val, m_value)
1571 }
1572
1573 /****************************************************************************************************/
1574
clone(Context * context)1575 ListAtom* ListAtom::clone(Context* context) {
1576 ListAtom* atom = new ListAtom(m_tagName);
1577 foreach(QExplicitlySharedDataPointer<AbstractAtom> a, m_children)
1578 atom->addChild(a->clone(context));
1579 return atom;
1580 }
1581
dump(Context * context,int level)1582 void ListAtom::dump(Context* context, int level) {
1583 DEBUG_DUMP;
1584 AbstractAtom::dump(context, level);
1585 }
1586
readElement(Context * context,MsooXmlDiagramReader * reader)1587 void ListAtom::readElement(Context* context, MsooXmlDiagramReader* reader) {
1588 if (reader->isStartElement()) {
1589 QExplicitlySharedDataPointer<AbstractAtom> node;
1590 if (reader->qualifiedName() == QLatin1String("dgm:constr")) {
1591 node = QExplicitlySharedDataPointer<AbstractAtom>(new ConstraintAtom);
1592 } else if (reader->qualifiedName() == QLatin1String("dgm:adj")) {
1593 node = QExplicitlySharedDataPointer<AbstractAtom>(new AdjustAtom);
1594 } else if (reader->qualifiedName() == QLatin1String("dgm:rule")) {
1595 node = QExplicitlySharedDataPointer<AbstractAtom>(new RuleAtom);
1596 }
1597 if (node) {
1598 addChild(node);
1599 node->readAll(context, reader);
1600 }
1601 }
1602 }
1603
1604 /****************************************************************************************************/
1605
clone(Context * context)1606 ShapeAtom* ShapeAtom::clone(Context* context) {
1607 ShapeAtom* atom = new ShapeAtom;
1608 atom->m_type = m_type;
1609 atom->m_blip = m_blip;
1610 atom->m_hideGeom = m_hideGeom;
1611 foreach(QExplicitlySharedDataPointer<AbstractAtom> a, m_children)
1612 atom->addChild(a->clone(context));
1613 return atom;
1614 }
1615
adjustments() const1616 QList< QExplicitlySharedDataPointer<AdjustAtom> > ShapeAtom::adjustments() const {
1617 QList< QExplicitlySharedDataPointer<AdjustAtom> > result;
1618 foreach( QExplicitlySharedDataPointer<AbstractAtom> atom, m_children ) {
1619 AdjustAtom* adjustedAtom = dynamic_cast< AdjustAtom* >( atom.data() );
1620 if ( adjustedAtom ) {
1621 result.append(QExplicitlySharedDataPointer<AdjustAtom>(adjustedAtom));
1622 } else if (ListAtom *list = dynamic_cast< ListAtom* >( atom.data() ) ) {
1623 foreach( QExplicitlySharedDataPointer<AbstractAtom> val, list->children() ) {
1624 adjustedAtom = dynamic_cast< AdjustAtom* >( val.data() );
1625 if ( adjustedAtom )
1626 result.append(QExplicitlySharedDataPointer<AdjustAtom>(adjustedAtom));
1627 }
1628 }
1629 }
1630 return result;
1631 }
1632
dump(Context * context,int level)1633 void ShapeAtom::dump(Context* context, int level) {
1634 DEBUG_DUMP << "type=" << m_type << "hideGeom=" << m_hideGeom << "blip=" << m_blip;
1635 AbstractAtom::dump(context, level);
1636 }
1637
readAll(Context * context,MsooXmlDiagramReader * reader)1638 void ShapeAtom::readAll(Context* context, MsooXmlDiagramReader* reader) {
1639 const QXmlStreamAttributes attrs(reader->attributes());
1640 TRY_READ_ATTR_WITHOUT_NS_INTO(type, m_type)
1641 //if (m_type.isEmpty()) m_type = "obj";
1642 TRY_READ_ATTR_WITHOUT_NS_INTO(blip, m_blip)
1643 TRY_READ_ATTR_WITHOUT_NS(hideGeom)
1644 m_hideGeom = hideGeom.toInt();
1645 AbstractAtom::readAll(context, reader);
1646 }
1647
build(Context * context)1648 void ShapeAtom::build(Context* context) {
1649 Q_ASSERT(!context->m_shapeList.contains(this));
1650 context->m_shapeList.append(this);
1651 AbstractAtom::build(context);
1652 }
1653
1654 //TODO use filters/libmso/ODrawToOdf.h
writeAtom(Context * context,KoXmlWriter * xmlWriter,KoGenStyles * styles)1655 void ShapeAtom::writeAtom(Context* context, KoXmlWriter* xmlWriter, KoGenStyles* styles) {
1656 Q_ASSERT(context->m_parentLayout);
1657 if(m_type.isEmpty() || m_hideGeom) return;
1658 QMap<QString,QString> params = context->m_parentLayout->algorithmParams();
1659 QMap<QString, qreal> values = context->m_parentLayout->finalValues();
1660 debugMsooXml << values;
1661 //Q_ASSERT(values.contains("l"));
1662 //Q_ASSERT(values.contains("t"));
1663 //if(!values.contains("w")) values["w"]=100;
1664 //if(!values.contains("h")) values["h"]=100;
1665 Q_ASSERT(values.contains("w"));
1666 Q_ASSERT(values.contains("h"));
1667 //Q_ASSERT(values.contains("ctrX"));
1668 //Q_ASSERT(values.contains("ctrY"));
1669 qreal x = values.value("l");
1670 qreal y = values.value("t");
1671 qreal w = values.value("w");
1672 qreal h = values.value("h");
1673 qreal cx = values.value("ctrX");
1674 qreal cy = values.value("ctrY");
1675 /*
1676 QFile shapeListFile( "shapeList.txt" );
1677 QString cxt = QString::number( cx );
1678 QString cyt = QString::number( cy );
1679 QString wt = QString::number( w );
1680 QString ht = QString::number( h );
1681 QString lbrack = "(";
1682 QString rbrack = ")\n";
1683 QString space = " ";
1684 shapeListFile.open( QFile::WriteOnly | QFile::Append );
1685 shapeListFile.write( lbrack.toLatin1() );
1686 shapeListFile.write( cxt.toLatin1() );
1687 shapeListFile.write( space.toLatin1() );
1688 shapeListFile.write( cyt.toLatin1() );
1689 shapeListFile.write( space.toLatin1() );
1690 shapeListFile.write( wt.toLatin1() );
1691 shapeListFile.write( space.toLatin1() );
1692 shapeListFile.write( ht.toLatin1() );
1693 shapeListFile.write( rbrack.toLatin1() );
1694 shapeListFile.close();
1695 */
1696
1697 #if 0
1698 //TODO can spacing between the siblings applied by shriking the shapes or is it needed to apply them along the used algorithm?
1699 if(values.contains("sibSp")) {
1700 qreal sibSp = values["sibSp"];
1701 Q_ASSERT(w >= sibSp);
1702 Q_ASSERT(h >= sibSp);
1703 if(w >= sibSp && h >= sibSp) {
1704 x += sibSp;
1705 y += sibSp;
1706 w -= sibSp;
1707 h -= sibSp;
1708 } else {
1709 warnMsooXml<<"Sibling spacing is bigger then width/height! Skipping sibSp-value! width="<<w<<"height="<<h<<"sibSp="<<sibSp;
1710 context->m_parentLayout->dump(context, 10);
1711 }
1712 }
1713 #endif
1714
1715 DEBUG_WRITE << "type=" << m_type << "blip=" << m_blip << "hideGeom=" << m_hideGeom << "geometry=" << x+cx << y+cy << w << h;
1716 //Q_ASSERT(x >= 0.0); Q_ASSERT(y >= 0.0); Q_ASSERT(cx >= 0.0); Q_ASSERT(cy >= 0.0); // they can be negative
1717 if (w < 0.0) w = -w;
1718 if (h < 0.0) h = -h;
1719
1720 xmlWriter->startElement("draw:custom-shape");
1721 //xmlWriter->addAttribute("draw:layer", "layout");
1722
1723 if (!context->m_parentLayout->m_name.isEmpty())
1724 xmlWriter->addAttribute("draw:name", context->m_parentLayout->m_name);
1725
1726 KoGenStyle style = KoGenStyle(KoGenStyle::GraphicAutoStyle, "graphic");
1727 style.addProperty("draw:fill", "solid" /*none*/, KoGenStyle::GraphicType);
1728 style.addProperty("draw:opacity", "50%");
1729 style.addProperty("draw:textarea-horizontal-align", "center");
1730 style.addProperty("draw:textarea-vertical-align", "middle");
1731 style.addProperty("fo:wrap-option", "wrap");
1732
1733 //const qreal m_svgX = x + qMax(0.0, qMin(w, cx - w/2));
1734 //const qreal m_svgY = y + qMax(0.0, qMin(h, cy - h/2));
1735 const qreal m_svgX = x/* + cx*/;
1736 const qreal m_svgY = y/* + cy*/;
1737
1738 const qreal rotateAngle = context->m_parentLayout->m_rotateAngle; //0=right 45=bottom 90=left 135=top 180=right
1739 if(rotateAngle == 0) {
1740 xmlWriter->addAttribute("svg:x", QString("%1px").arg(m_svgX));
1741 xmlWriter->addAttribute("svg:y", QString("%1px").arg(m_svgY));
1742 }
1743 xmlWriter->addAttribute("svg:width", QString("%1px").arg(w));
1744 xmlWriter->addAttribute("svg:height", QString("%1px").arg(h));
1745 if(rotateAngle != 0) {
1746 QMatrix matrix;
1747 matrix.translate(m_svgX + 0.5 * w, m_svgY + 0.5 * h);
1748 matrix.rotate(rotateAngle);
1749 matrix.translate(-0.5 * w, -0.5 * h);
1750 xmlWriter->addAttribute("draw:transform", QString("matrix(%1 %2 %3 %4 %5pt %6pt)").arg(matrix.m11()).arg(matrix.m12()).arg(matrix.m21()).arg(matrix.m22()).arg(matrix.dx()) .arg(matrix.dy()));
1751 }
1752
1753 if (m_type == QLatin1String("conn")) {
1754 /*
1755 QList<AbstractNode*> axis = context->m_parentLayout->axis();
1756 foreach(AbstractNode* n, axis) n->dump(context,10);
1757 Q_ASSERT(axis.count() == 1);
1758 Q_ASSERT(static_cast<PointNode*>(axis.first())->m_type == "sibTrans");
1759 const QString cxnId = static_cast<PointNode*>(axis.first())->m_cxnId;
1760 Q_ASSERT(!static_cast<PointNode*>(axis.first())->m_cxnId.isEmpty());
1761 ConnectionNode* connection = 0;
1762 foreach(AbstractNode* node, context->m_connections->children()) {
1763 ConnectionNode* n = dynamic_cast<ConnectionNode*>(node);
1764 if(n && n->m_modelId == cxnId) {
1765 connection = n;
1766 break;
1767 }
1768 }
1769 Q_ASSERT(connection);
1770 //connection->m_srcId;
1771 //connection->m_destId;
1772 */
1773 style.addProperty("draw:fill-color", "#9999ff");
1774 } else {
1775 style.addProperty("draw:fill-color", "#3333ff");
1776 }
1777
1778 const QString styleName = styles->insert(style);
1779 xmlWriter->addAttribute("draw:style-name", styleName);
1780 //xmlWriter->addAttribute("draw:text-style-name", "P2");
1781
1782 //xmlWriter->startElement("svg:desc");
1783 //xmlWriter->endElement();
1784
1785 QList<PointNode*> textlist;
1786 foreach(AbstractNode* n, context->m_parentLayout->axis( context )) {
1787 if(PointNode* pn = dynamic_cast<PointNode*>(n))
1788 if(!pn->m_text.isEmpty())
1789 textlist.prepend(pn);
1790 }
1791
1792 if (!textlist.isEmpty()) {
1793 foreach(PointNode* pn, textlist) {
1794 bool bulletEnabled = QVariant(context->m_parentLayout->variable("bulletEnabled", false)).toBool();
1795 if (bulletEnabled) {
1796 int level = 0;
1797 for(AbstractNode* n = pn->parent(); n; n = n->parent(), ++level);
1798 if(level < 2) // seems only level2 has bullets while level1 has not even if bulletEnabled=1 (see me07_horizontal_bullet_list.xlsx).
1799 bulletEnabled = false;
1800 }
1801 if (bulletEnabled) {
1802 xmlWriter->startElement("text:list");
1803 KoListStyle listStyle;
1804 KoListLevelProperties llp;
1805 llp.setLevel(1);
1806 llp.setBulletCharacter(QChar(0x2022));
1807 listStyle.setLevelProperties(llp);
1808 KoGenStyle style(KoGenStyle::ListAutoStyle);
1809 QByteArray array;
1810 QBuffer buffer(&array);
1811 KoXmlWriter tmpXmlWriter(&buffer);
1812 KoEmbeddedDocumentSaver embeddedSaver;
1813 KoShapeSavingContext context(tmpXmlWriter, *styles, embeddedSaver);
1814 listStyle.saveOdf(style, context);
1815 xmlWriter->addAttribute("text:style-name", styles->insert(style));
1816 xmlWriter->startElement("text:list-item");
1817 }
1818 xmlWriter->startElement("text:p");
1819 xmlWriter->addTextNode(pn->m_text);
1820 xmlWriter->endElement();
1821 if (bulletEnabled) {
1822 xmlWriter->endElement();
1823 xmlWriter->endElement();
1824 }
1825 }
1826 }
1827
1828 if (m_type == QLatin1String("ellipse")) {
1829 xmlWriter->startElement("draw:enhanced-geometry");
1830 xmlWriter->addAttribute("draw:enhanced-path", "U 10800 10800 10800 10800 0 360 Z N");
1831 xmlWriter->addAttribute("draw:glue-points", "10800 0 3163 3163 0 10800 3163 18437 10800 21600 18437 18437 21600 10800 18437 3163");
1832 xmlWriter->addAttribute("draw:type", "ellipse");
1833 //xmlWriter->addAttribute("svg:viewBox", "0 0 21600 21600");
1834 xmlWriter->endElement();
1835 } else if (m_type == QLatin1String("cycle")) {
1836 xmlWriter->startElement("draw:enhanced-geometry");
1837 xmlWriter->addAttribute("draw:enhanced-path", "M 0 414114 C 0 304284 43630 198953 121292 121291 198954 43630 304285 0 414115 0 523945 0 629276 43630 706938 121292 784599 198954 828229 304285 828229 414115 828229 523945 784599 629276 706938 706938 629277 784599 523945 828229 414115 828229 304285 828229 198954 784599 121292 706938 43631 629276 1 523945 1 414115 L 0 414114 Z N");
1838 xmlWriter->addAttribute("draw:glue-point-leaving-directions", "-90, -90, -90, -90, -90, -90, -90, -90, -90, -90");
1839 xmlWriter->addAttribute("draw:glue-points", "?f21 ?f22 ?f23 ?f24 ?f25 ?f26 ?f27 ?f28 ?f29 ?f30 ?f27 ?f31 ?f25 ?f32 ?f23 ?f31 ?f33 ?f30 ?f21 ?f22");
1840 xmlWriter->addAttribute("draw:type", "circle");
1841 //xmlWriter->addAttribute("draw:text-areas", "?f34 ?f36 ?f35 ?f37");
1842 //xmlWriter->addAttribute("svg:viewBox", "0 0 828228 828228");
1843 xmlWriter->endElement();
1844 } else if (m_type == QLatin1String("rect")) {
1845 xmlWriter->startElement("draw:enhanced-geometry");
1846 xmlWriter->addAttribute("draw:enhanced-path", "M 0 0 L 1393031 0 1393031 557212 0 557212 0 0 Z N");
1847 xmlWriter->addAttribute("draw:glue-point-leaving-directions", "-90, -90, -90, -90, -90");
1848 xmlWriter->addAttribute("draw:glue-points", "?f12 ?f13 ?f14 ?f13 ?f14 ?f15 ?f12 ?f15 ?f12 ?f13");
1849 xmlWriter->addAttribute("draw:type", "non-primitive");
1850 //xmlWriter->addAttribute("draw:text-areas", "?f16 ?f18 ?f17 ?f19");
1851 //xmlWriter->addAttribute("svg:viewBox", "0 0 1393031 557212");
1852 xmlWriter->endElement();
1853 } else if (m_type == QLatin1String("roundRect")) {
1854 xmlWriter->startElement("draw:enhanced-geometry");
1855 xmlWriter->addAttribute("draw:enhanced-path", "M 0 97707 C 0 71793 10294 46941 28618 28618 46942 10294 71794 0 97707 0 L 804191 0 C 830105 0 854957 10294 873280 28618 891604 46942 901898 71794 901898 97707 L 901898 488526 C 901898 514440 891604 539292 873280 557615 854956 575939 830104 586233 804191 586233 L 97707 586233 C 71793 586233 46941 575939 28618 557615 10294 539291 0 514439 0 488526 L 0 97707 Z N");
1856 xmlWriter->addAttribute("draw:glue-point-leaving-directions", "-90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90");
1857 xmlWriter->addAttribute("draw:glue-points", "?f20 ?f21 ?f22 ?f23 ?f24 ?f25 ?f26 ?f25 ?f27 ?f23 ?f28 ?f21 ?f28 ?f29 ?f27 ?f30 ?f26 ?f31 ?f24 ?f31 ?f22 ?f30 ?f20 ?f29 ?f20 ?f21");
1858 xmlWriter->addAttribute("draw:type", "non-primitive");
1859 //xmlWriter->addAttribute("draw:text-areas", "?f32 ?f34 ?f33 ?f35");
1860 //xmlWriter->addAttribute("svg:viewBox", "0 0 901898 586233");
1861 xmlWriter->endElement();
1862 } else if (m_type == QLatin1String("pie")) {
1863 xmlWriter->startElement("draw:enhanced-geometry");
1864 xmlWriter->addAttribute("draw:enhanced-path", "M 1152145 0 C 1563767 0 1944120 219599 2149931 576074 2355741 932549 2355740 1371744 2149929 1728219 L 1152144 1152144 C 1152144 768096 1152145 384048 1152145 0 Z N");
1865 xmlWriter->addAttribute("draw:glue-point-leaving-directions", "-90, -90, -90, -90, -90");
1866 xmlWriter->addAttribute("draw:glue-points", "?f16 ?f17 ?f18 ?f19 ?f20 ?f21 ?f22 ?f23 ?f16 ?f17");
1867 xmlWriter->addAttribute("draw:type", "non-primitive");
1868 //xmlWriter->addAttribute("draw:text-areas", "?f24 ?f26 ?f25 ?f27");
1869 //xmlWriter->addAttribute("svg:viewBox", "0 0 2304288 2304288");
1870 xmlWriter->endElement();
1871 } else if (m_type == QLatin1String("diamond")) {
1872 xmlWriter->startElement("draw:enhanced-geometry");
1873 xmlWriter->addAttribute("draw:enhanced-path", "M ?f0 ?f7 L ?f11 ?f2 ?f1 ?f7 ?f11 ?f3 Z N");
1874 //xmlWriter->addAttribute("draw:path-stretchpoint-x", "21600");
1875 //xmlWriter->addAttribute("draw:path-stretchpoint-y", "21600");
1876 xmlWriter->addAttribute("draw:type", "non-primitive");
1877 //xmlWriter->addAttribute("draw:text-areas", "?f10 ?f6 ?f12 ?f13");
1878 //xmlWriter->addAttribute("svg:viewBox", "0 0 21600 21600");
1879 xmlWriter->startElement("draw:equation"); xmlWriter->addAttribute("draw:formula", "left"); xmlWriter->addAttribute("draw:name", "f0"); xmlWriter->endElement();
1880 xmlWriter->startElement("draw:equation"); xmlWriter->addAttribute("draw:formula", "right"); xmlWriter->addAttribute("draw:name", "f1");xmlWriter->endElement();
1881 xmlWriter->startElement("draw:equation"); xmlWriter->addAttribute("draw:formula", "top"); xmlWriter->addAttribute("draw:name", "f2");xmlWriter->endElement();
1882 xmlWriter->startElement("draw:equation"); xmlWriter->addAttribute("draw:formula", "bottom"); xmlWriter->addAttribute("draw:name", "f3");xmlWriter->endElement();
1883 xmlWriter->startElement("draw:equation"); xmlWriter->addAttribute("draw:formula", "?f3 - ?f2"); xmlWriter->addAttribute("draw:name", "f4");xmlWriter->endElement();
1884 xmlWriter->startElement("draw:equation"); xmlWriter->addAttribute("draw:formula", "?f4 / 2"); xmlWriter->addAttribute("draw:name", "f5");xmlWriter->endElement();
1885 xmlWriter->startElement("draw:equation"); xmlWriter->addAttribute("draw:formula", "?f4 / 4"); xmlWriter->addAttribute("draw:name", "f6");xmlWriter->endElement();
1886 xmlWriter->startElement("draw:equation"); xmlWriter->addAttribute("draw:formula", "?f2 + ?f5"); xmlWriter->addAttribute("draw:name", "f7");xmlWriter->endElement();
1887 xmlWriter->startElement("draw:equation"); xmlWriter->addAttribute("draw:formula", "?f1 - ?f0"); xmlWriter->addAttribute("draw:name", "f8");xmlWriter->endElement();
1888 xmlWriter->startElement("draw:equation"); xmlWriter->addAttribute("draw:formula", "?f8 / 2"); xmlWriter->addAttribute("draw:name", "f9");xmlWriter->endElement();
1889 xmlWriter->startElement("draw:equation"); xmlWriter->addAttribute("draw:formula", "?f8 / 4"); xmlWriter->addAttribute("draw:name", "f10");xmlWriter->endElement();
1890 xmlWriter->startElement("draw:equation"); xmlWriter->addAttribute("draw:formula", "?f0 + ?f9"); xmlWriter->addAttribute("draw:name", "f11");xmlWriter->endElement();
1891 xmlWriter->startElement("draw:equation"); xmlWriter->addAttribute("draw:formula", "?f8 * 3 / 4"); xmlWriter->addAttribute("draw:name", "f12");xmlWriter->endElement();
1892 xmlWriter->startElement("draw:equation"); xmlWriter->addAttribute("draw:formula", "?f4 * 3 / 4"); xmlWriter->addAttribute("draw:name", "f13");xmlWriter->endElement();
1893 xmlWriter->endElement();
1894 } else if (m_type == QLatin1String("trapezoid")) {
1895 xmlWriter->startElement("draw:enhanced-geometry");
1896 xmlWriter->addAttribute("draw:enhanced-path", "M 0 914400 L 761997 0 762003 0 1524000 914400 0 914400 Z N");
1897 xmlWriter->addAttribute("draw:glue-point-leaving-directions", "-90, -90, -90, -90, -90");
1898 xmlWriter->addAttribute("draw:glue-points", "?f14 ?f15 ?f16 ?f17 ?f18 ?f17 ?f19 ?f15 ?f14 ?f15");
1899 xmlWriter->addAttribute("draw:type", "non-primitive");
1900 //xmlWriter->addAttribute("draw:text-areas", "?f20 ?f22 ?f21 ?f23");
1901 //xmlWriter->addAttribute("svg:viewBox", "0 0 1524000 914400");
1902 xmlWriter->endElement();
1903 } else if (m_type == QLatin1String("conn")) { // Connection shape type
1904 enum EndStyle { Arrow, Auto, NoArrow };
1905 EndStyle endstyle = Arrow;
1906 if(params.contains("endSty")) {
1907 const QString endStyle = params["endSty"];
1908 if(endStyle == "auto") {
1909 //TODO specs say that the algorithm needs to define the style but it misses details how...
1910 } else if(endStyle == "noArr") {
1911 endstyle = NoArrow;
1912 }
1913 }
1914 if(endstyle == NoArrow) { // just a connecting line without arrow
1915 xmlWriter->startElement("draw:enhanced-geometry");
1916 xmlWriter->addAttribute("draw:enhanced-path", "M 1627875 92938 A ?f54 ?f55 ?f56 ?f57 1627875 92938 ?f51 ?f53 W ?f58 ?f59 ?f60 ?f61 1627875 92938 ?f51 ?f53 N");
1917 //xmlWriter->addAttribute("draw:glue-point-leaving-directions", "-90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90");
1918 //xmlWriter->addAttribute("draw:glue-points", "?f20 ?f21 ?f22 ?f23 ?f24 ?f25 ?f26 ?f25 ?f27 ?f23 ?f28 ?f21 ?f28 ?f29 ?f27 ?f30 ?f26 ?f31 ?f24 ?f31 ?f22 ?f30 ?f20 ?f29 ?f20 ?f21");
1919 xmlWriter->addAttribute("draw:type", "non-primitive");
1920 //xmlWriter->addAttribute("draw:text-areas", "?f11 ?f13 ?f12 ?f14");
1921 //xmlWriter->addAttribute("svg:viewBox", "0 0 2341473 2341473");
1922 xmlWriter->endElement();
1923 } else { // arrow-right
1924 xmlWriter->startElement("draw:enhanced-geometry");
1925 xmlWriter->addAttribute("draw:enhanced-path", "M 0 55905 L 110087 55905 110087 0 220174 139764 110087 279527 110087 223622 0 223622 0 55905 Z N");
1926 xmlWriter->addAttribute("draw:glue-point-leaving-directions", "-90, -90, -90, -90, -90, -90, -90, -90");
1927 xmlWriter->addAttribute("draw:glue-points", "?f16 ?f17 ?f18 ?f17 ?f18 ?f19 ?f20 ?f21 ?f18 ?f22 ?f18 ?f23 ?f16 ?f23 ?f16 ?f17");
1928 xmlWriter->addAttribute("draw:type", "non-primitive");
1929 //xmlWriter->addAttribute("draw:text-areas", "?f24 ?f26 ?f25 ?f27");
1930 //xmlWriter->addAttribute("svg:viewBox", "0 0 220174 279527");
1931 xmlWriter->endElement();
1932 }
1933 } else {
1934 ASSERT_X(false, QString("TODO Handle shape of type=%1").arg(m_type).toUtf8());
1935 }
1936
1937 xmlWriter->endElement(); // draw:custom-shape
1938 }
1939
1940 /****************************************************************************************************/
1941
clone(Context * context)1942 PresentationOfAtom* PresentationOfAtom::clone(Context* context) {
1943 PresentationOfAtom* atom = new PresentationOfAtom;
1944 atom->m_axis = m_axis;
1945 atom->m_ptType = m_ptType;
1946 atom->m_start = m_start;
1947 atom->m_step = m_step;
1948 atom->m_count = m_count;
1949 atom->m_hideLastTrans = m_hideLastTrans;
1950 foreach(QExplicitlySharedDataPointer<AbstractAtom> a, m_children)
1951 atom->addChild(a->clone(context));
1952 return atom;
1953 }
1954
isEmpty() const1955 bool PresentationOfAtom::isEmpty() const {
1956 return m_axis.isEmpty() && m_ptType.isEmpty() && m_start.isEmpty() && m_step.isEmpty() && m_count.isEmpty() && m_hideLastTrans.isEmpty();
1957 }
1958
dump() const1959 QString PresentationOfAtom::dump() const {
1960 QString s;
1961 if(!m_axis.isEmpty()) s += QString("axis=%1 ").arg(m_axis);
1962 if(!m_ptType.isEmpty()) s += QString("ptType=%1 ").arg(m_ptType);
1963 if(!m_start.isEmpty()) s += QString("start=%1 ").arg(m_start);
1964 if(!m_step.isEmpty()) s += QString("step=%1 ").arg(m_step);
1965 if(!m_count.isEmpty()) s += QString("count=%1 ").arg(m_count);
1966 if(!m_hideLastTrans.isEmpty()) s += QString("hideLastTrans=%1 ").arg(m_hideLastTrans);
1967 return s.trimmed();
1968 }
1969
dump(Context * context,int level)1970 void PresentationOfAtom::dump(Context* context, int level) {
1971 DEBUG_DUMP << context->m_parentLayout->m_name << dump();
1972 AbstractAtom::dump(context, level);
1973 }
1974
readAll(Context * context,MsooXmlDiagramReader * reader)1975 void PresentationOfAtom::readAll(Context* context, MsooXmlDiagramReader* reader) {
1976 const QXmlStreamAttributes attrs(reader->attributes());
1977 TRY_READ_ATTR_WITHOUT_NS_INTO(axis, m_axis)
1978 TRY_READ_ATTR_WITHOUT_NS_INTO(ptType, m_ptType)
1979 TRY_READ_ATTR_WITHOUT_NS_INTO(cnt, m_count)
1980 TRY_READ_ATTR_WITHOUT_NS_INTO(hideLastTrans, m_hideLastTrans)
1981 TRY_READ_ATTR_WITHOUT_NS_INTO(st, m_start)
1982 TRY_READ_ATTR_WITHOUT_NS_INTO(step, m_step)
1983 AbstractAtom::readAll(context, reader);
1984 }
1985
build(Context * context)1986 void PresentationOfAtom::build(Context* context) {
1987 // first set the axis according to our layout
1988 Q_ASSERT(context->m_parentLayout->axis( context ).isEmpty());
1989 context->m_parentLayout->setAxis( context, this );
1990
1991 // and then adjust the current node if
1992 QList<AbstractNode*> nodes = context->m_parentLayout->axis( context );
1993 if ( nodes.isEmpty() ) {
1994 /*
1995 PointNode* ppp = dynamic_cast<PointNode*>(context->currentNode());
1996 Q_ASSERT(ppp);
1997 debugMsooXml<<QString("modelId=%2 type=%3").arg(ppp->m_modelId).arg(ppp->m_type);
1998 */
1999 ASSERT_X(isEmpty(), QString("Failed to proper apply the non-empty presOf %1").arg(dump()).toLocal8Bit());
2000 } else {
2001 //ASSERT_X(nodes.count() == 1, "Oha. The axis contains more then one note. It's not clear what to do in such cases...");
2002 if (nodes.count() >= 2) warnMsooXml << "TODO The axis contains more then one note. It's not clear what to do in such cases...";
2003 context->setCurrentNode( nodes.first() );
2004 }
2005 }
2006
2007 /****************************************************************************************************/
2008
clone(Context * context)2009 IfAtom* IfAtom::clone(Context* context) {
2010 IfAtom* atom = new IfAtom(m_isTrue);
2011 atom->m_argument = m_argument;
2012 atom->m_axis = m_axis;
2013 atom->m_function = m_function;
2014 atom->m_hideLastTrans = m_hideLastTrans;
2015 atom->m_name = m_name;
2016 atom->m_operator = m_operator;
2017 atom->m_ptType = m_ptType;
2018 atom->m_start = m_start;
2019 atom->m_step = m_step;
2020 atom->m_count = m_count;
2021 atom->m_value = m_value;
2022 foreach(QExplicitlySharedDataPointer<AbstractAtom> a, m_children)
2023 atom->addChild(a->clone(context));
2024 return atom;
2025 }
2026
dump(Context * context,int level)2027 void IfAtom::dump(Context* context, int level) {
2028 DEBUG_DUMP<<"name="<<m_name;
2029 //DEBUG_DUMP << "name=" << m_name << "argument=" << m_argument << "axis=" << m_axis << "count=" << m_count << "function=" << m_function << "hideLastTrans=" << m_hideLastTrans << "operator=" << m_operator << "dataPointType=" << m_ptType << "start=" << m_start << "step=" << m_step << "value=" << m_value;
2030 AbstractAtom::dump(context, level);
2031 }
2032
readAll(Context * context,MsooXmlDiagramReader * reader)2033 void IfAtom::readAll(Context* context, MsooXmlDiagramReader* reader) {
2034 const QXmlStreamAttributes attrs(reader->attributes());
2035 TRY_READ_ATTR_WITHOUT_NS_INTO(arg, m_argument)
2036 TRY_READ_ATTR_WITHOUT_NS_INTO(axis, m_axis)
2037 TRY_READ_ATTR_WITHOUT_NS_INTO(cnt, m_count)
2038 TRY_READ_ATTR_WITHOUT_NS_INTO(func, m_function)
2039 TRY_READ_ATTR_WITHOUT_NS_INTO(hideLastTrans, m_hideLastTrans)
2040 TRY_READ_ATTR_WITHOUT_NS_INTO(name, m_name)
2041 TRY_READ_ATTR_WITHOUT_NS_INTO(op, m_operator)
2042 TRY_READ_ATTR_WITHOUT_NS_INTO(ptType, m_ptType)
2043 TRY_READ_ATTR_WITHOUT_NS_INTO(st, m_start)
2044 TRY_READ_ATTR_WITHOUT_NS_INTO(step, m_step)
2045 TRY_READ_ATTR_WITHOUT_NS_INTO(val, m_value)
2046 AbstractAtom::readAll(context, reader);
2047 }
2048
isTrue() const2049 bool IfAtom::isTrue() const { return m_isTrue; } // is true or false?
2050
testAtom(Context * context)2051 bool IfAtom::testAtom(Context* context) {
2052 QList<AbstractNode*> axis = fetchAxis(context, m_axis, m_ptType, m_start, m_count, m_step);
2053 QString funcValue;
2054 if ( m_name == "Name21" )
2055 {
2056 PointNode* node = dynamic_cast<PointNode* >( context->currentNode() );
2057 Q_ASSERT( node );
2058 debugMsooXml << "RULE21: " << m_axis.count() << " nodeId: " << node->m_modelId;
2059 }
2060 if(m_function == "cnt") { // Specifies a count.
2061 funcValue = QString::number(axis.count());
2062 } else if(m_function == "depth") { // Specifies the depth.
2063 //int depth = 0;
2064 //for(AbstractNode* n = context->currentNode(); n; n = n->parent(), ++depth);
2065 //funcValue = depth;
2066 //TODO
2067 warnMsooXml<<"TODO func=depth";
2068 } else if(m_function == "maxDepth") { // Defines the maximum depth.
2069 //int depth = 0;
2070 //for(AbstractNode* n = context->currentNode(); n; n = n->parent(), ++depth);
2071 //funcValue = depth;
2072 //TODO
2073 warnMsooXml<<"TODO func=maxDepth";
2074 } else if(m_function == "pos") { // Retrieves the position of the node in the specified set of nodes.
2075 const int position = axis.indexOf(context->currentNode()) + 1;
2076 funcValue = QString::number(position);
2077 //TODO 1-based? what index for not-found?
2078 warnMsooXml<<"TODO func=pos funcValue="<<funcValue;
2079 } else if(m_function == "posEven") { // Returns 1 if the specified node is at an even numbered position in the data model.
2080 //const int position = axis.indexOf(context->currentNode())+1;
2081 //funcValue = position>=1 && position % 2 == 0 ? 1 : 0;
2082 //TODO
2083 warnMsooXml<<"TODO func=posEven";
2084 } else if(m_function == "posOdd") { // Returns 1 if the specified node is in an odd position in the data model.
2085 //const int position = axis.indexOf(context->currentNode())+1;
2086 //funcValue = position>=1 && position % 2 != 0 = 1 : 0;
2087 //TODO
2088 warnMsooXml<<"TODO func=posOdd";
2089 } else if(m_function == "revPos") { // Reverse position function.
2090 const int position = axis.indexOf(context->currentNode()) + 1;
2091 funcValue = axis.count()-position;
2092 //TODO lastIndexOf? 1-based? what index for not-found?
2093 warnMsooXml<<"TODO func=revPos";
2094 } else if(m_function == "var") { // Used to reference a variable.
2095 funcValue = context->m_parentLayout->variable(m_argument, true /* check parents */);
2096 if(funcValue.isEmpty()) { // if not defined then use default variable-values
2097 if(m_argument == QLatin1String("dir")) { // Specifies the direction of the diagram.
2098 funcValue = "norm";
2099 } else {
2100 warnMsooXml<<"TODO figure out default for variable="<<m_argument;
2101 }
2102 }
2103 }
2104
2105 bool istrue = false;
2106 if(m_isTrue && !funcValue.isNull()) {
2107 if(m_operator == "equ") {
2108 istrue = funcValue == m_value;
2109 } else {
2110 bool isInt;
2111 const int funcValueInt = funcValue.toInt(&isInt);
2112 const int valueInt = isInt ? m_value.toInt(&isInt) : 0;
2113 if(!isInt) {
2114 // right, that's untested atm since I didn't found a single document that does it and the specs don't cover
2115 // such "details" anyways so it seems. So, if you run into this then it's up to you to fix it :)
2116 warnMsooXml<<"TODO figure out how non-integer comparison is expected to work";
2117 }
2118 if(m_operator == QLatin1String("gt")) {
2119 istrue = isInt ? funcValueInt > valueInt : funcValue > m_value;
2120 } else if(m_operator == QLatin1String("gte")) {
2121 istrue = isInt ? funcValueInt >= valueInt : funcValue >= m_value;
2122 } else if(m_operator == QLatin1String("lt")) {
2123 istrue = isInt ? funcValueInt < valueInt : funcValue < m_value;
2124 } else if(m_operator == QLatin1String("lte")) {
2125 istrue = isInt ? funcValueInt <= valueInt : funcValue <= m_value;
2126 } else if(m_operator == QLatin1String("neq")) {
2127 istrue = isInt ? funcValueInt != valueInt : funcValue != m_value;
2128 } else {
2129 warnMsooXml<<"Unexpected operator="<<m_operator<<"name="<<m_name;
2130 }
2131 }
2132 }
2133 //debugMsooXml<<"name="<<m_name<<"value1="<<funcValue<<"value2="<<m_value<<"operator="<<m_operator<<"istrue="<<istrue;
2134 return istrue;
2135 }
2136
2137 /****************************************************************************************************/
2138
clone(Context * context)2139 ChooseAtom* ChooseAtom::clone(Context* context) {
2140 ChooseAtom* atom = new ChooseAtom;
2141 atom->m_name = m_name;
2142 foreach(QExplicitlySharedDataPointer<AbstractAtom> a, m_children)
2143 atom->addChild(a->clone(context));
2144 return atom;
2145 }
2146
dump(Context * context,int level)2147 void ChooseAtom::dump(Context* context, int level) {
2148 DEBUG_DUMP << "name=" << m_name;
2149 AbstractAtom::dump(context, level);
2150 }
2151
readAll(Context * context,MsooXmlDiagramReader * reader)2152 void ChooseAtom::readAll(Context* context, MsooXmlDiagramReader* reader) {
2153 const QXmlStreamAttributes attrs(reader->attributes());
2154 TRY_READ_ATTR_WITHOUT_NS_INTO(name, m_name)
2155 AbstractAtom::readAll(context, reader);
2156 }
2157
readElement(Context * context,MsooXmlDiagramReader * reader)2158 void ChooseAtom::readElement(Context* context, MsooXmlDiagramReader* reader) {
2159 if(reader->isStartElement()) {
2160 if(reader->qualifiedName() == QLatin1String("dgm:if")) {
2161 QExplicitlySharedDataPointer<AbstractAtom> n(new IfAtom(true));
2162 addChild(n);
2163 n->readAll(context, reader);
2164 } else if(reader->qualifiedName() == QLatin1String("dgm:else")) {
2165 QExplicitlySharedDataPointer<AbstractAtom> n(new IfAtom(false));
2166 addChild(n);
2167 n->readAll(context, reader);
2168 }
2169 }
2170 }
2171
build(Context * context)2172 void ChooseAtom::build(Context* context) {
2173 // build up list of IfAtom's that match the defined condition
2174 QVector< QExplicitlySharedDataPointer<AbstractAtom> > ifResult;
2175 QVector< QExplicitlySharedDataPointer<AbstractAtom> > elseResult;
2176 while(!m_children.isEmpty()) {
2177 QExplicitlySharedDataPointer<AbstractAtom> atom = m_children.first();
2178 m_children.remove(0); // detach child
2179 IfAtom* ifatom = static_cast<IfAtom*>(atom.data());
2180 if(ifatom->isTrue()) {
2181 if(ifatom->testAtom(context)) {
2182 ifResult.append(atom);
2183 }
2184 } else {
2185 elseResult.append(atom);
2186 }
2187 }
2188
2189 #if 0
2190 // move the children of the selected IfAtom's to our parent
2191 int index = m_parent->indexOfChild(this);
2192 Q_ASSERT(index >= 0);
2193 typedef QVector< QExplicitlySharedDataPointer< AbstractAtom > > AtomPList;
2194 foreach( QExplicitlySharedDataPointer<AbstractAtom> atom, ifResult.isEmpty() ? elseResult : ifResult ) {
2195 AtomPList listResult = atom->children();
2196 // move the constraints to the parent's m_constraintsToBuild
2197 AtomPList::iterator it = std::stable_partition( listResult.begin(), listResult.end(), ConstraintPredicate() );
2198 std::copy( it, listResult.end(), std::back_inserter( context->m_parentLayout->m_constraintsToBuild ) );
2199 listResult.erase( it, listResult.end() );
2200 // and move the remaining atom's to the parent
2201 foreach( QExplicitlySharedDataPointer<AbstractAtom> a, listResult ) {
2202 atom->removeChild( a );
2203 m_parent->insertChild( ++index, a );
2204 a->build( context );
2205 }
2206 }
2207 QExplicitlySharedDataPointer<AbstractAtom> ptr(this);
2208 m_parent->removeChild(ptr);
2209 #else
2210 // move the children of the selected IfAtom's to our parent
2211 int index = m_parent->indexOfChild(this);
2212 Q_ASSERT(index >= 0);
2213 QVector< QExplicitlySharedDataPointer<AbstractAtom> > atoms;
2214 foreach( QExplicitlySharedDataPointer<AbstractAtom> atom, ifResult.isEmpty() ? elseResult : ifResult ) {
2215 IfAtom * ifAtom = dynamic_cast< IfAtom* >( atom.data() );
2216 if ( ifAtom )
2217 debugMsooXml << "atomNameChosen" << ifAtom->m_name;
2218 foreach( QExplicitlySharedDataPointer<AbstractAtom> a, atom->children() ) {
2219 atom->removeChild( a );
2220 m_parent->insertChild( ++index, a );
2221 atoms.append( a );
2222 }
2223 }
2224
2225 // and finally detach ourself from our parent since we are done now
2226 QExplicitlySharedDataPointer<AbstractAtom> ptr(this);
2227 m_parent->removeChild(ptr);
2228
2229 // and start building the moved children
2230 foreach( QExplicitlySharedDataPointer<AbstractAtom> atom, atoms ) {
2231 atom->build( context );
2232 }
2233 #endif
2234
2235 }
2236
2237 /****************************************************************************************************/
2238
clone(Context * context)2239 ForEachAtom* ForEachAtom::clone(Context* context) {
2240 ForEachAtom* atom = new ForEachAtom;
2241 atom->m_axis = m_axis;
2242 atom->m_hideLastTrans = m_hideLastTrans;
2243 atom->m_name = m_name;
2244 atom->m_ptType = m_ptType;
2245 atom->m_reference = m_reference;
2246 atom->m_start = m_start;
2247 atom->m_step = m_step;
2248 atom->m_count = m_count;
2249 foreach(QExplicitlySharedDataPointer<AbstractAtom> a, m_children)
2250 atom->addChild(a->clone(context));
2251 return atom;
2252 }
2253
dump() const2254 QString ForEachAtom::dump() const {
2255 QString s;
2256 if(!m_name.isEmpty()) s += QString("name=%1 ").arg(m_name);
2257 if(!m_axis.isEmpty()) s += QString("axis=%1 ").arg(m_axis);
2258 if(!m_ptType.isEmpty()) s += QString("ptType=%1 ").arg(m_ptType);
2259 if(!m_reference.isEmpty()) s += QString("reference=%1 ").arg(m_reference);
2260 if(!m_start.isEmpty()) s += QString("start=%1 ").arg(m_start);
2261 if(!m_step.isEmpty()) s += QString("step=%1 ").arg(m_step);
2262 if(!m_count.isEmpty()) s += QString("count=%1 ").arg(m_count);
2263 if(!m_hideLastTrans.isEmpty()) s += QString("hideLastTrans=%1 ").arg(m_hideLastTrans);
2264 return s.trimmed();
2265 }
2266
dump(Context * context,int level)2267 void ForEachAtom::dump(Context* context, int level) {
2268 DEBUG_DUMP << dump();
2269 foreach(QExplicitlySharedDataPointer<AbstractAtom> atom, m_children)
2270 atom->dump(context, level + 1);
2271 }
2272
readAll(Context * context,MsooXmlDiagramReader * reader)2273 void ForEachAtom::readAll(Context* context, MsooXmlDiagramReader* reader) {
2274 const QXmlStreamAttributes attrs(reader->attributes());
2275 TRY_READ_ATTR_WITHOUT_NS_INTO(axis, m_axis)
2276 TRY_READ_ATTR_WITHOUT_NS_INTO(cnt, m_count)
2277 TRY_READ_ATTR_WITHOUT_NS_INTO(hideLastTrans, m_hideLastTrans)
2278 TRY_READ_ATTR_WITHOUT_NS_INTO(name, m_name)
2279 TRY_READ_ATTR_WITHOUT_NS_INTO(ptType, m_ptType)
2280 TRY_READ_ATTR_WITHOUT_NS_INTO(ref, m_reference)
2281 TRY_READ_ATTR_WITHOUT_NS_INTO(st, m_start)
2282 TRY_READ_ATTR_WITHOUT_NS_INTO(step, m_step)
2283 AbstractAtom::readAll(context, reader);
2284 }
2285
build(Context * context)2286 void ForEachAtom::build(Context* context) {
2287 // determinate which children are selected
2288 QList<AbstractNode*> axis = fetchAxis(context, m_axis, m_ptType, m_start, m_count, m_step);
2289 typedef QPair<AbstractNode*, QList<QExplicitlySharedDataPointer<AbstractAtom> > > NodePair;
2290 QList<NodePair> newChildren;
2291 foreach(AbstractNode* node, axis) {
2292 QList<QExplicitlySharedDataPointer<AbstractAtom> > list;
2293 foreach(QExplicitlySharedDataPointer<AbstractAtom> atom, m_children) {
2294 QExplicitlySharedDataPointer<AbstractAtom> atomCopy(atom->clone(context));
2295 /*
2296 if ( LayoutNodeAtom* layoutAtom = dynamic_cast< LayoutNodeAtom* >( atomCopy.data() ) ) {
2297 Q_ASSERT(layoutAtom->axis(context).isEmpty());
2298 layoutAtom->setAxis( context, QList< AbstractNode* >() << node );
2299 }
2300 */
2301 list.append(atomCopy);
2302 }
2303 newChildren.append(NodePair(node, list));
2304 }
2305
2306 // move the selected children to our parent
2307 int index = m_parent->indexOfChild(this);
2308 Q_ASSERT(index >= 0);
2309 foreach(NodePair p, newChildren) {
2310 foreach(QExplicitlySharedDataPointer<AbstractAtom> atom, p.second) {
2311 m_parent->insertChild(++index, atom);
2312 }
2313 }
2314
2315 // detach ourself from our parent since we will evaluate the forEach once and forever and won't need it afterwards.
2316 QExplicitlySharedDataPointer<AbstractAtom> ptr(this);
2317 m_parent->removeChild(ptr);
2318
2319 // and finally build the selected children which needs to be done here cause our own parent will deal
2320 // with a copy of it's children-list and will not know about it's new children during the build.
2321 AbstractNode* oldCurrentNode = context->currentNode();
2322 foreach(NodePair p, newChildren) {
2323 context->setCurrentNode(p.first); // move on to the next node
2324 foreach(QExplicitlySharedDataPointer<AbstractAtom> atom, p.second) {
2325 atom->build(context);
2326 }
2327 }
2328 context->setCurrentNode(oldCurrentNode);
2329 }
2330
2331 /****************************************************************************************************/
2332
AbstractAlgorithm()2333 AbstractAlgorithm::AbstractAlgorithm() : m_context(0), m_oldCurrentNode(0) {}
2334
~AbstractAlgorithm()2335 AbstractAlgorithm::~AbstractAlgorithm() {
2336 if(m_context) {
2337 m_context->m_parentLayout = m_parentLayout;
2338 m_context->setCurrentNode(m_oldCurrentNode);
2339 }
2340 qDeleteAll( doubleLayoutContext );
2341 }
2342
context() const2343 Context* AbstractAlgorithm::context() const { return m_context; }
layout() const2344 LayoutNodeAtom* AbstractAlgorithm::layout() const { return m_layout.data(); }
parentLayout() const2345 LayoutNodeAtom* AbstractAlgorithm::parentLayout() const { return m_parentLayout.data(); }
2346
childLayouts() const2347 QList<LayoutNodeAtom*> AbstractAlgorithm::childLayouts() const {
2348 QList<LayoutNodeAtom*> result;
2349 foreach(QExplicitlySharedDataPointer<AbstractAtom> atom, m_layout->children())
2350 if(LayoutNodeAtom* l = dynamic_cast<LayoutNodeAtom*>(atom.data()))
2351 result.append(l);
2352 return result;
2353 }
2354
setNodePosition(LayoutNodeAtom * l,qreal x,qreal y,qreal w,qreal h)2355 void AbstractAlgorithm::setNodePosition(LayoutNodeAtom* l, qreal x, qreal y, qreal w, qreal h) {
2356 // QStringList removeList;
2357 l->m_values["l"] = parentLayout()->finalValues()["l"] + x;
2358 l->m_values["t"] = parentLayout()->finalValues()["t"] + y;
2359 // removeList << "l" << "t";
2360 if (w >= 0.0) {
2361 l->m_values["w"] = w;
2362 // removeList << "w";
2363 }
2364 if (h >= 0.0) {
2365 l->m_values["h"] = h;
2366 // removeList << "h";
2367 }
2368 //l->m_values["ctrX"] = 0.0;
2369 //l->m_values["ctrY"] = 0.0;
2370 //l->m_values["r"] = l->m_values["l"] + l->m_values["w"];
2371 //l->m_values.remove("ctrX");
2372 //l->m_values.remove("ctrY");
2373 // removeList << "ctrX" << "ctrY";
2374 // foreach(const QString &s, removeList) {
2375 // //l->m_factors[s] = 1.0;
2376 // //l->m_countFactors[s] = 1;
2377 // l->m_factors.remove(s);
2378 // l->m_countFactors.remove(s);
2379 // }
2380 l->m_needsReinit = false; // we initialized things above already
2381 l->m_needsRelayout = true; // but we clearly need a layout now
2382 l->m_childNeedsRelayout = true; // and our children need to be relayouted too now
2383 }
2384
defaultValue(const QString & type,const QMap<QString,qreal> & values)2385 qreal AbstractAlgorithm::defaultValue(const QString& type, const QMap<QString, qreal>& values) {
2386 qreal value = virtualGetDefaultValue(type, values);
2387 if(value < 0.0) {
2388 // If the layout-algorithm doesn't define a default-value then use one of the common default-values.
2389 // See also http://social.msdn.microsoft.com/Forums/en/os_binaryfile/thread/7c823650-7913-4e63-970f-1c5dab3450c4
2390 if (type == "primFontSz") {
2391 value = 36;
2392 // } else if (type == "tMarg") {
2393 // Q_ASSERT(values.contains("primFontSz"));
2394 // value = values.value("primFontSz") * 0.56;
2395 // } else if (type == "lMarg") {
2396 // Q_ASSERT(values.contains("primFontSz"));
2397 // value = values.value("primFontSz") * 0.40;
2398 // } else if (type == "rMarg") {
2399 // Q_ASSERT(values.contains("primFontSz"));
2400 // value = values.value("primFontSz") * 0.42;
2401 // } else if (type == "bMarg") {
2402 // Q_ASSERT(values.contains("primFontSz"));
2403 // value = values.value("primFontSz") * 0.60;
2404 } else if (type.startsWith(QLatin1String("user"))) { // userA, userB, userC, etc.
2405 bool ok;
2406 const qreal v = layout()->variable(type, true /* checkParents */).toDouble(&ok);
2407 value = ok ? v : 0.0;
2408 }
2409 }
2410 return value;
2411 }
2412
doInit(Context * context,QExplicitlySharedDataPointer<LayoutNodeAtom> layout)2413 void AbstractAlgorithm::doInit(Context* context, QExplicitlySharedDataPointer<LayoutNodeAtom> layout) {
2414 m_context = context;
2415 m_layout = layout;
2416 m_parentLayout = m_context->m_parentLayout;
2417 m_context->m_parentLayout = m_layout;
2418 m_oldCurrentNode = m_context->currentNode();
2419 virtualDoInit();
2420 }
2421
doLayout()2422 void AbstractAlgorithm::doLayout() {
2423 virtualDoLayout();
2424 }
2425
doLayoutChildren()2426 void AbstractAlgorithm::doLayoutChildren() {
2427 virtualDoLayoutChildren();
2428 }
2429
virtualGetDefaultValue(const QString &,const QMap<QString,qreal> &)2430 qreal AbstractAlgorithm::virtualGetDefaultValue(const QString&, const QMap<QString, qreal>&) {
2431 return -1.0;
2432 }
2433
virtualDoInit()2434 void AbstractAlgorithm::virtualDoInit() {
2435 if(layout()->m_needsReinit) {
2436 layout()->m_needsReinit = false; // initialization done
2437 //layout()->m_values = parentLayout()->m_values;
2438 //layout()->m_factors = parentLayout()->m_factors;
2439 //layout()->m_countFactors = parentLayout()->m_countFactors;
2440 }
2441 //QMap<QString, qreal> values = parentLayout()->finalValues();
2442 //Q_ASSERT(values["l"] >= 0.0);
2443 //Q_ASSERT(values["t"] >= 0.0);
2444 //Q_ASSERT(values["w"] > 0.0);
2445 //Q_ASSERT(values["h"] > 0.0);
2446 //Q_ASSERT(values["ctrX"] >= 0.0);
2447 //Q_ASSERT(values["ctrY"] >= 0.0);
2448 }
2449
2450 // http://msdn.microsoft.com/en-us/library/dd439461(v=office.12).aspx
virtualDoLayout()2451 void AbstractAlgorithm::virtualDoLayout() {
2452 Q_ASSERT( layout() );
2453 Q_ASSERT( !name().isEmpty() );
2454 const QString __name = name();
2455 debugMsooXml << "layout=" << layout()->m_name << "algorithm=" << __name;//name();
2456
2457 // Specifies the aspect ratio (width to height) of the composite node to use when determining child constraints. A value of 0 specifies to
2458 // leave the width and height constraints unaltered. The algorithm may temporarily shrink one dimension to achieve the specified ratio.
2459 // For example, if a composite node has a width constraint of 20 and height constraint of 10, and if the value of ar is 1.5, the composite
2460 // algorithm uses a width value of 15 to calculate the composite node’s child constraints. However, the algorithm does not propagate this
2461 // value to other nodes.
2462 qreal aspectRatio = layout()->algorithmParam("ar", "0").toDouble();
2463 if (aspectRatio != 0.0)
2464 layout()->m_values["w"] = layout()->finalValues()["h"] * aspectRatio;
2465
2466 //QVector< QExplicitlySharedDataPointer< LayoutNodeAtom > > allChilds = layout()->childrenLayouts();
2467 //foreach( QExplicitlySharedDataPointer< LayoutNodeAtom > curChild, allChilds )
2468 // setNodePosition( curChild.data(), layout()->finalValues()[ "l" ], layout()->finalValues()[ "t" ], layout()->finalValues()[ "w" ], layout()->finalValues()[ "h" ] );
2469
2470 foreach( QExplicitlySharedDataPointer< ConstraintAtom > constr, layout()->constraints() )
2471 constr->applyConstraint( context(), layout() );
2472
2473 foreach( QExplicitlySharedDataPointer< ShapeAtom > shape, layout()->shapes() )
2474 foreach( QExplicitlySharedDataPointer< AdjustAtom > adj, shape->adjustments() ){
2475 adj->applyAdjustment( context(), layout() );
2476 }
2477
2478 //foreach( QExplicitlySharedDataPointer< LayoutNodeAtom > curChild, allChilds )
2479 // setNodePosition( curChild.data(), layout()->finalValues()[ "l" ], layout()->finalValues()[ "t" ], layout()->finalValues()[ "w" ], layout()->finalValues()[ "h" ] );
2480 }
2481
virtualDoLayoutChildren()2482 void AbstractAlgorithm::virtualDoLayoutChildren() {
2483 foreach(QExplicitlySharedDataPointer<AbstractAtom> atom, layout()->children()) {
2484 if ( LayoutNodeAtom* layAtom = dynamic_cast< LayoutNodeAtom* >( atom.data() ) )
2485 layAtom->setNeedsRelayout( true );
2486 atom->layoutAtom(context());
2487 }
2488 }
2489
2490 /****************************************************************************************************/
2491
2492 // http://msdn.microsoft.com/en-us/library/dd439461(v=office.12).aspx
virtualGetDefaultValue(const QString & type,const QMap<QString,qreal> & values)2493 qreal CompositeAlgorithm::virtualGetDefaultValue(const QString& type, const QMap<QString, qreal>& values) {
2494 Q_UNUSED(values);
2495 qreal value = -1.0;
2496 if (type == "w" || type == "h") {
2497 value = 100;
2498 } else if (type == "l" || type == "t") {
2499 value = 0;
2500 } else if (type == "wOff" || type == "hOff" || type == "lOff" || type == "ctrXOff" || type == "rOff" || type == "tOff" || type == "ctrYOff" || type == "bOff") {
2501 value = 0;
2502 }
2503 return value;
2504 }
2505
2506 /****************************************************************************************************/
2507
connectorDistance() const2508 qreal ConnectorAlgorithm::connectorDistance() const {
2509 QPair<LayoutNodeAtom*,LayoutNodeAtom*> neighbors = layout()->neighbors();
2510 LayoutNodeAtom* srcAtom = neighbors.first;
2511 LayoutNodeAtom* dstAtom = neighbors.second;
2512 return (srcAtom && dstAtom) ? srcAtom->distanceTo(dstAtom) : 0.0;
2513 }
2514
virtualGetDefaultValue(const QString & type,const QMap<QString,qreal> & values)2515 qreal ConnectorAlgorithm::virtualGetDefaultValue(const QString& type, const QMap<QString, qreal>& values) {
2516 qreal value = -1.0;
2517 if (type == "w" || type == "h") {
2518 value = 100;
2519 } else if (type == "connDist") {
2520 value = connectorDistance();
2521 } else if (type == "stemThick") {
2522 value = values.value("h") * 0.60;
2523 } else if (type == "begMarg" || type == "endMarg") {
2524 value = 3.175;
2525 } else if (type == "begPad") {
2526 value = connectorDistance() * 0.22;
2527 } else if (type == "endPad") {
2528 value = connectorDistance() * 0.25;
2529 } else if (type == "bendDist") {
2530 value = connectorDistance() * 0.50;
2531 } else if (type == "hArH") {
2532 value = values.value("h") * 1.00;
2533 } else if (type == "wArH") {
2534 value = values.value("h") * 0.50;
2535 } else if (type == "diam") {
2536 value = connectorDistance() * 1.00;
2537 }
2538 return value;
2539 }
2540
virtualDoLayoutChildren()2541 void ConnectorAlgorithm::virtualDoLayoutChildren() {
2542 // Get a list of all child-layouts of our parent to apply the connector-algorithm on our direct
2543 // neighbors. Also while on it also determinate our own position in that list.
2544 QPair<LayoutNodeAtom*,LayoutNodeAtom*> neighbors = layout()->neighbors();
2545 LayoutNodeAtom* srcAtom = neighbors.first;
2546 LayoutNodeAtom* dstAtom = neighbors.second;
2547 if(!srcAtom || !dstAtom) {
2548 if(layout()->parent()) {
2549 // If there is no source- or destination to connect with then hide our layout by detaching it.
2550 layout()->parent()->removeChild(QExplicitlySharedDataPointer<AbstractAtom>(layout()));
2551 }
2552 return;
2553 }
2554
2555 // Beginning and end points defines different connection sites available on a node. This can be one of the following values;
2556 // * auto Specifies that the algorithm will determine the best connection site to use.
2557 // * bCtr Specifies that the bottom, center connection site is to be used.
2558 // * bL Specifies that the bottom, left connection site is to be used.
2559 // * bR Specifies that the bottom right connection site is to be used.
2560 // * ctr Specifies that the center connection site is to be used.
2561 // * midL Specifies that the middle left connection site is to be used.
2562 // * midR Specifies that the middle right connection site is to be used.
2563 // * radial Specifies connections along a radial path to support the use of connections in cycle diagrams.
2564 // * tCtr Specifies that the top center connection site is to be used.
2565 // * tL Specifies that the top left connection site is to be used.
2566 // * tR Specifies that the top right connection site is to be used.
2567 QString begPts = layout()->algorithmParam("begPts");
2568 QString endPts = layout()->algorithmParam("endPts");
2569 //if (!begPts.isEmpty() && !endPts.isEmpty()) debugMsooXml<<"begPts="<<begPts<<"endPts="<<endPts;
2570
2571 QMap<QString, qreal> srcValues = srcAtom->finalValues();
2572 QMap<QString, qreal> dstValues = dstAtom->finalValues();
2573 qreal srcX = srcValues["l"];//+srcValues["ctrX"];
2574 qreal srcY = srcValues["t"];//+srcValues["ctrY"];
2575 qreal srcW = srcValues["w"];
2576 qreal srcH = srcValues["h"];
2577 qreal dstX = dstValues["l"];//+dstValues["ctrX"];
2578 qreal dstY = dstValues["t"];//+dstValues["ctrY"];
2579 qreal dstW = dstValues["w"];
2580 qreal dstH = dstValues["h"];
2581 qreal srcCX = srcX + srcW/2.0;
2582 qreal srcCY = srcY + srcH/2.0;
2583 qreal dstCX = dstX + dstW/2.0;
2584 qreal dstCY = dstY + dstH/2.0;
2585 layout()->m_rotateAngle = atan2(dstCY - srcCY, dstCX - srcCX) * 180 / M_PI;
2586
2587 AbstractAlgorithm::virtualDoLayoutChildren();
2588 }
2589
2590 /****************************************************************************************************/
2591
virtualGetDefaultValue(const QString & type,const QMap<QString,qreal> &)2592 qreal CycleAlgorithm::virtualGetDefaultValue(const QString& type, const QMap<QString, qreal>&) {
2593 qreal value = -1.0;
2594 if (type == "w" || type == "h") {
2595 value = 100;
2596 } else if (type == "diam") {
2597 value = 0;
2598 } else if (type == "sibSp") {
2599 value = 0;
2600 } else if (type == "sp") {
2601 value = 0;
2602 }
2603 return value;
2604 }
2605
2606 // http://msdn.microsoft.com/en-us/library/dd439451(v=office.12).aspx
virtualDoLayout()2607 void CycleAlgorithm::virtualDoLayout() {
2608 AbstractAlgorithm::virtualDoLayout();
2609
2610 QList<LayoutNodeAtom*> childs = childLayouts();
2611 ASSERT_X(!childs.isEmpty(), QString("Layout %1 does not have child-layouts").arg(layout()->m_name));
2612 if (childs.isEmpty()) return;
2613
2614 // Specifies the angle at which the first shape is placed. Angles are in degrees, measured clockwise from a line pointing straight upward from the center of the cycle.
2615 int startAngel = layout()->algorithmParam("stAng", "0").toInt();
2616 // Specifies the angle the cycle spans. Final shapealign text is placed at stAng+spanAng, unless spanAng=360. In that case, the algorithm places the text so that shapes do not overlap.
2617 int spanAngel = layout()->algorithmParam("spanAng", "360").toInt();
2618
2619 // Specifies where to place nodes in relation to the center circle.
2620 bool firstNodeInCenter = layout()->algorithmParam("ctrShpMap", "none") == "fNode";
2621
2622 LayoutNodeAtom* nodeInCenter = firstNodeInCenter ? childs.takeFirst() : 0;
2623 const qreal childsCount = childs.count();
2624
2625 QMap<QString, qreal> values = layout()->finalValues();
2626 const qreal w = values["w"];
2627 const qreal h = values["h"];
2628 const qreal rx = w / 2.0;
2629 const qreal ry = h / 2.0;
2630 qreal num = 360.0 / childsCount;
2631 const bool inverse = startAngel > spanAngel;
2632 if(inverse) num = -num;
2633
2634 qreal spacing = values.value("sibSp");
2635 qreal dw = ( (2.0 * M_PI * rx - spacing) / childsCount );
2636 qreal dh = ( (2.0 * M_PI * ry - spacing) / childsCount );
2637
2638 if(nodeInCenter) {
2639 //setNodePosition(nodeInCenter, rx, ry, -1, -1); //dw, dh);
2640 setNodePosition(nodeInCenter, rx, ry, dw, dh);
2641 }
2642
2643 //for(qreal degree = startAngel; (!childs.isEmpty()) && (inverse ? degree > spanAngel : degree <= spanAngel); degree -= num) {
2644 for(qreal degree = startAngel; (!childs.isEmpty()) && (inverse ? degree > spanAngel : degree <= spanAngel); degree += num) {
2645 const qreal radian = (degree - 90.0) * (M_PI / 180.0);
2646 const qreal x = rx + cos(radian) * rx;
2647 const qreal y = ry + sin(radian) * ry;
2648 LayoutNodeAtom* l = childs.takeFirst();
2649 //setNodePosition(l, x, y, -1, -1);
2650 setNodePosition(l, x, y, dw, dh);
2651 }
2652 }
2653
2654 /****************************************************************************************************/
2655
virtualGetDefaultValue(const QString & type,const QMap<QString,qreal> & values)2656 qreal LinearAlgorithm::virtualGetDefaultValue(const QString& type, const QMap<QString, qreal>& values) {
2657 Q_UNUSED(type);
2658 Q_UNUSED(values);
2659 qreal value = -1.0;
2660 /*
2661 if (type == "w" || type == "h") {
2662 value = 100;
2663 }
2664 */
2665 return value;
2666 }
2667
2668 // http://msdn.microsoft.com/en-us/library/dd439457%28v=office.12%29.aspx
virtualDoLayout()2669 void LinearAlgorithm::virtualDoLayout() {
2670 AbstractAlgorithm::virtualDoLayout();
2671 // TODO handle infinite values somehow sensible
2672 #if 1
2673 QMap< QString, qreal > values = layout()->finalValues();
2674 QString direction = layout()->algorithmParam( "linDir", "fromL" );
2675 qreal x = 0;
2676 qreal y = 0;
2677 if ( direction == "fromR" )
2678 {
2679 x = values[ "w" ];
2680 }
2681 if ( direction == "fromB" )
2682 {
2683 y = values[ "h" ];
2684 }
2685 QList<LayoutNodeAtom*> childs = childLayouts();
2686 if ( childs.isEmpty() )
2687 return;
2688 LayoutNodeAtom *firstNSpaceNode = nullptr;
2689 LayoutNodeAtom *lastNSpaceNode = nullptr;
2690 debugMsooXml << values;
2691 for ( int i = 0; i < childs.count(); ++ i )
2692 {
2693 if ( direction == "fromL" )
2694 {
2695 childs[ i ]->m_values[ "l" ] = x;
2696 debugMsooXml << "XVAL: " << x;
2697 x = childs[ i ]->finalValues()[ "r" ];
2698 }
2699 else if ( direction == "fromR" )
2700 {
2701 childs[ i ]->m_values[ "r" ] = x;
2702 debugMsooXml << "XVAL: " << x;
2703 x = childs[ i ]->finalValues()[ "l" ];
2704 }
2705 else if ( direction == "fromT" )
2706 {
2707 debugMsooXml << "TVAL: " << childs[ i ]->finalValues()[ "t" ];
2708 debugMsooXml << "BVAL: " << childs[ i ]->finalValues()[ "b" ];
2709 childs[ i ]->m_values[ "t" ] = y;
2710 debugMsooXml << "YVAL: " << y;
2711 y = childs[ i ]->finalValues()[ "b" ];
2712 }
2713 else if ( direction == "fromB" )
2714 {
2715 childs[ i ]->m_values[ "b" ] = y;
2716 debugMsooXml << "YVAL: " << y;
2717 y = childs[ i ]->finalValues()[ "t" ];
2718 }
2719 if (childs[ i ]->algorithm()->m_type != AlgorithmAtom::SpaceAlg) {
2720 if ( !firstNSpaceNode )
2721 firstNSpaceNode = childs[ i ];
2722 lastNSpaceNode = childs[ i ];
2723 }
2724 }
2725 const qreal width = lastNSpaceNode->finalValues()[ "r" ] - firstNSpaceNode->finalValues()[ "l" ];
2726 const qreal height = lastNSpaceNode->finalValues()[ "b" ] - firstNSpaceNode->finalValues()[ "t" ];
2727 const qreal widthStretchFactor = values[ "w" ] / width;
2728 const qreal heightStretchFactor = values[ "h" ] / height;
2729 const qreal xOffset = firstNSpaceNode->finalValues()[ "l" ] < 0 ? -firstNSpaceNode->finalValues()[ "l" ] : 0;
2730 const qreal yOffset = firstNSpaceNode->finalValues()[ "t" ] < 0 ? -firstNSpaceNode->finalValues()[ "t" ] : 0;
2731 const qreal xOffsetRect = values[ "l" ];
2732 const qreal yOffsetRect = values[ "t" ];
2733 debugMsooXml << width;
2734 debugMsooXml << widthStretchFactor;
2735 debugMsooXml << xOffset;
2736
2737 for ( int i = 0; i < childs.count(); ++i )
2738 {
2739 if ( direction == "fromL" || direction == "formR" )
2740 {
2741 const qreal aspectRatio = childs[ i ]->finalValues()[ "h" ] / childs[ i ]->finalValues()[ "w" ];
2742 const qreal heightRatio = widthStretchFactor * aspectRatio;
2743 qreal oldCenterX = childs[ i ]->finalValues()[ "ctrX" ];
2744 childs[ i ]->m_values[ "w" ] = childs[ i ]->finalValues()[ "w" ] * widthStretchFactor;
2745 childs[ i ]->m_values[ "h" ] = childs[ i ]->finalValues()[ "h" ] * heightRatio;
2746 oldCenterX *= widthStretchFactor;
2747 oldCenterX += xOffset * widthStretchFactor + xOffsetRect;
2748 childs[ i ]->m_values[ "ctrX" ] = oldCenterX;
2749 childs[ i ]->m_values[ "ctrY" ] = childs[ i ]->finalValues()[ "ctrY" ] + yOffsetRect;
2750 }
2751 else
2752 {
2753 const qreal aspectRatio = childs[ i ]->finalValues()[ "w" ] / childs[ i ]->finalValues()[ "h" ];
2754 const qreal widthRatio = heightStretchFactor * aspectRatio;
2755 qreal oldCenterY = childs[ i ]->finalValues()[ "ctrY" ];
2756 childs[ i ]->m_values[ "w" ] = childs[ i ]->finalValues()[ "w" ] * widthRatio;
2757 childs[ i ]->m_values[ "h" ] = childs[ i ]->finalValues()[ "h" ] * heightStretchFactor;
2758 oldCenterY *= heightStretchFactor;
2759 oldCenterY += yOffset * heightStretchFactor + yOffsetRect;
2760 childs[ i ]->m_values[ "ctrY" ] = oldCenterY;
2761 childs[ i ]->m_values[ "ctrX" ] = childs[ i ]->finalValues()[ "ctrX" ] + xOffsetRect;
2762 }
2763
2764 }
2765 #endif
2766 #if 0
2767 QString direction = layout()->algorithmParam("linDir", "fromL");
2768 const qreal lMarg = layout()->finalValues()[ "lMarg" ];
2769 const qreal rMarg = layout()->finalValues()[ "rMarg" ];
2770 const qreal tMarg = layout()->finalValues()[ "tMarg" ];
2771 const qreal bMarg = layout()->finalValues()[ "bMarg" ];
2772 const qreal w = layout()->finalValues()["w"] - lMarg - rMarg;
2773 const qreal h = layout()->finalValues()["h"] - bMarg - tMarg;
2774
2775 QList<LayoutNodeAtom*> childs = childLayouts();
2776 ASSERT_X(!childs.isEmpty(), QString("Layout %1 does not have child-layouts").arg(layout()->m_name));
2777 if (childs.isEmpty()) return;
2778
2779 const qreal childsCount = childs.count();
2780 debugMsooXml << "REAL CHILD COUNTERRRRRRRRRRRRRR " << childsCount;
2781 const QSizeF usedSize = layout()->childrenUsedSize();
2782 const QSizeF totalSize = layout()->childrenTotalSize();
2783
2784 int x, y, mx, my;
2785 x = y = mx = my = 0;
2786 if(direction == "fromL") {
2787 mx = w / childsCount;
2788 x = lMarg;
2789 } else if(direction == "fromR") {
2790 x = lMarg + w;
2791 mx = -(w / childsCount);
2792 } else if(direction == "fromT") {
2793 my = h / childsCount;
2794 } else if(direction == "fromB") {
2795 y = h;
2796 my = -(h / childsCount);
2797 }
2798
2799 // calculate weights
2800 qreal currentX = x;
2801 qreal currentY = y;
2802 qreal currentWidth = 0;
2803 qreal currentHeight = 0;
2804 const int xFactor = mx >= 0 ? 1 : -1;
2805 const int yFactor = my >= 0 ? 1 : -1;
2806 foreach(LayoutNodeAtom* l, childs) {
2807 QMap< QString, qreal > values = l->finalValues();
2808 if ( l->algorithmType() != AlgorithmAtom::SpaceAlg ) {
2809 debugMsooXml << "NODETYPE: SPACE";
2810 currentWidth = l->finalValues()[ "w" ] / usedSize.width() * w;
2811 currentHeight = l->finalValues()[ "h" ] / usedSize.height() * h;
2812 setNodePosition(l, currentX, currentY, currentWidth, currentHeight);
2813 if ( direction == "fromR" || direction == "fromL" )
2814 currentX = currentX + xFactor * l->finalValues()[ "w" ];
2815 else
2816 currentY = currentY + yFactor * l->finalValues()[ "h" ];
2817 } else {
2818 debugMsooXml << "NODETYPE: ELSE";
2819 currentWidth = l->finalValues()[ "w" ] / totalSize.width() * w;
2820 currentHeight = l->finalValues()[ "h" ] / totalSize.height() * h;
2821 if ( direction == "fromR" || direction == "fromL" )
2822 currentX += currentWidth;
2823 else
2824 currentY += currentHeight;
2825 }
2826 }
2827 #endif
2828 }
2829
2830 /****************************************************************************************************/
2831
virtualGetDefaultValue(const QString & type,const QMap<QString,qreal> & values)2832 qreal SnakeAlgorithm::virtualGetDefaultValue(const QString& type, const QMap<QString, qreal>& values) {
2833 Q_UNUSED(values);
2834 qreal value = -1.0;
2835 if (type == "w" || type == "h") {
2836 value = 100;
2837 } else if (type == "alignOff" || type == "sp" || type == "begPad" || type == "endPad") {
2838 value = 0;
2839 }
2840 return value;
2841 }
2842
2843 // http://msdn.microsoft.com/en-us/library/dd439436(v=office.12).aspx
virtualDoLayout()2844 void SnakeAlgorithm::virtualDoLayout() {
2845 // Specifies from which corner the snake grows. For example, if the algorithm uses a top left value, the snake grows from the top left.
2846 const QString growDirection = layout()->algorithmParam("grDir", "tL");
2847 // Specifies whether nodes are arranged in rows or columns.
2848 const QString flowDirection = layout()->algorithmParam("flowDir");
2849 // Specifies the direction of the subsequent row or column. For example, if the algorithm initially places the nodes from left to right,
2850 // revDir places the nodes in the next row from right to left. However if the algorithm uses contDir, the nodes on the next row are
2851 // arranged from left to right.
2852 const bool inSameDirection = layout()->algorithmParam("contDir") != "revDir";
2853 // Specifies the offset.
2854 //const QString offset = layout()->algorithmParam("off");
2855
2856 // Specifies the point at which the diagram starts to snake. The value bal specifies that snaking begin at an even number of rows and
2857 // columns. The value fixed specifies that snaking begin at a fixed point, for example, in a row that contains three nodes. The value
2858 // endCnv specifies that snaking begin when there is no more room for a shape in the row.
2859 //const QString breakpoint = layout()->algorithmParam("bkpt", "endCnv");
2860 // Specifies where the snake should break, if bkpt=fixed.
2861 //const int breakpointFixedValue = layout()->algorithmParam("bkPtFixedVal", "2").toInt();
2862
2863 QList<LayoutNodeAtom*> childs = childLayouts();
2864 ASSERT_X(!childs.isEmpty(), QString("Layout %1 does not have child-layouts").arg(layout()->m_name));
2865 if (childs.isEmpty()) return;
2866
2867 bool inRows = flowDirection != "column";
2868 const qreal w = layout()->finalValues()["w"];
2869 const qreal h = layout()->finalValues()["h"];
2870 qreal x = 0;
2871 qreal y = 0;
2872
2873 if (growDirection == "tR") {
2874 x = w - childs.first()->finalValues()["w"];
2875 } else if (growDirection == "bL") {
2876 y = h - childs.first()->finalValues()["h"];
2877 } else if (growDirection == "bR") {
2878 x = w - childs.first()->finalValues()["w"];
2879 y = h - childs.first()->finalValues()["h"];
2880 }
2881
2882 //TODO is hardcoding correct here? The specs say default is 100...
2883 qreal mx = 110;
2884 qreal my = 110;
2885 qreal dw = 100;
2886 qreal dh = 100;
2887 //TODO use direction
2888 foreach(LayoutNodeAtom* l, childs) {
2889 if(l->algorithmType() == AlgorithmAtom::SpaceAlg) continue; // specs says 'or does nothing' but not under which conditions :-/
2890 setNodePosition(l, x, y, dw, dh);
2891 if(!inSameDirection) inRows = !inRows;
2892 if(inRows) {
2893 y += my;
2894 if(y+my > h) {
2895 x += mx;
2896 y = 0;
2897 }
2898 } else {
2899 x += mx;
2900 if(x+mx > w) {
2901 x = 0;
2902 y += my;
2903 }
2904 }
2905 }
2906 }
2907
2908 /****************************************************************************************************/
2909
virtualGetDefaultValue(const QString & type,const QMap<QString,qreal> &)2910 qreal HierarchyAlgorithm::virtualGetDefaultValue(const QString& type, const QMap<QString, qreal>&) {
2911 qreal value = -1.0;
2912 if (type == "w" || type == "h") {
2913 value = 100;
2914 } else if (m_isRoot && (type == "alignOff" || type == "sp")) {
2915 value = 0;
2916 } else if (!m_isRoot && (type == "sibSp" || type == "secSibSp")) {
2917 value = 0;
2918 }
2919 return value;
2920 }
2921
2922 // http://msdn.microsoft.com/en-us/library/dd439442(v=office.12).aspx
2923 // http://msdn.microsoft.com/en-us/library/dd439449(v=office.12).aspx
virtualDoLayout()2924 void HierarchyAlgorithm::virtualDoLayout() {
2925 debugMsooXml<<"TODO Implement algorithm isRoot="<<m_isRoot;
2926 AbstractAlgorithm::virtualDoLayout();
2927 }
2928
2929 /****************************************************************************************************/
2930
virtualGetDefaultValue(const QString & type,const QMap<QString,qreal> &)2931 qreal PyramidAlgorithm::virtualGetDefaultValue(const QString& type, const QMap<QString, qreal>&) {
2932 qreal value = -1.0;
2933 if (type == "w" || type == "h") {
2934 value = 100;
2935 } else if (type == "pyraAcctRatio") {
2936 value = 0.33;
2937 }
2938 return value;
2939 }
2940
virtualDoLayout()2941 void PyramidAlgorithm::virtualDoLayout() {
2942 debugMsooXml<<"TODO Implement algorithm";
2943 AbstractAlgorithm::virtualDoLayout();
2944 }
2945
2946 /****************************************************************************************************/
2947
2948 //NOTE I start to assume that the parent layout-algorithms are also responsible for setting defaults at children
2949 //layout-algorithms. If that's the case then the question is how/where that happens. To bad the specs are
2950 //missing the most basic information :-(
virtualGetDefaultValue(const QString & type,const QMap<QString,qreal> & values)2951 qreal SpaceAlg::virtualGetDefaultValue(const QString& type, const QMap<QString, qreal>& values) {
2952 Q_UNUSED(values);
2953 qreal value = -1.0;
2954 if (type == "w" || type == "h") {
2955 debugMsooXml<<"TODO type="<<type;
2956 value = 100; //TODO what default value is expected here?
2957 } else if (type == "sibSp") {
2958 debugMsooXml<<"TODO type="<<type;
2959 value = 0; //TODO what default value is expected here?
2960 }
2961 return value;
2962 }
2963
virtualDoLayout()2964 void SpaceAlg::virtualDoLayout() {
2965 // just don't do anything cause the space-algorithm is just a placeholder-algorithm
2966 AbstractAlgorithm::virtualDoLayout();
2967 }
2968
2969 /****************************************************************************************************/
2970
virtualGetDefaultValue(const QString & type,const QMap<QString,qreal> & values)2971 qreal TextAlgorithm::virtualGetDefaultValue(const QString& type, const QMap<QString, qreal>& values) {
2972 qreal value = -1.0;
2973 if (type == "w" || type == "h") {
2974 value = 100;
2975 } else if (type == "primFontSz" || type == "secFontSize") {
2976 value = 100;
2977 } else if (type == "tMarg") {
2978 value = values.value("primFontSz") * 0.78;
2979 } else if (type == "bMarg") {
2980 value = values.value("primFontSz") * 0.60;
2981 } else if (type == "lMarg") {
2982 value = values.value("primFontSz") * 0.42;
2983 } else if (type == "rMarg") {
2984 value = values.value("primFontSz") * 0.42;
2985 }/* else if ( type == "r" && values.contains( "w" ) ) {
2986 value = values["l"] + values["w"];
2987 } else if ( type == "l" && values.contains( "r" ) && values.contains( "w" ) ) {
2988 value = values["r"] - values["w"];
2989 } else if ( type == "l" ) {
2990 value = 0;
2991 }*/
2992 return value;
2993 }
2994
virtualDoLayout()2995 void TextAlgorithm::virtualDoLayout() {
2996 //TODO implement the text-layout logic
2997 AbstractAlgorithm::virtualDoLayout();
2998 }
2999