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