initial import
[vym] / branchobj.cpp
1 #include "branchobj.h"
2
3 // #include "texteditor.h"
4 #include "geometry.h"
5 #include "mapeditor.h"
6 #include "mainwindow.h"
7 #include "misc.h"
8
9 class TextEditor;
10
11 extern TextEditor *textEditor;
12 extern Main *mainWindow;
13 extern FlagRowObj *standardFlagsDefault;
14
15
16 /////////////////////////////////////////////////////////////////
17 // BranchObj
18 /////////////////////////////////////////////////////////////////
19
20 BranchObj* BranchObj::itLast=NULL;
21 BranchObj* BranchObj::itFirst=NULL;
22
23
24 BranchObj::BranchObj () :OrnamentedObj()
25 {
26 //    cout << "Const BranchObj ()\n";
27     setParObj (this);   
28     init();
29     depth=-1;
30 }
31
32 BranchObj::BranchObj (QGraphicsScene* s):OrnamentedObj (s)
33 {
34 //    cout << "Const BranchObj (s)  called from MapCenterObj (s)\n";
35         parObj=NULL;
36     scene=s;
37 }
38
39 BranchObj::BranchObj (QGraphicsScene* s, LinkableMapObj* p):OrnamentedObj (s)
40 {
41 //    cout << "Const BranchObj (s,p)\n";
42     scene=s;
43     setParObj (p);      
44     depth=p->getDepth()+1;
45         if (depth==1)
46                 // Calc angle to mapCenter if I am a mainbranch
47                 // needed for reordering the mainbranches clockwise 
48                 // around mapcenter 
49                 angle=getAngle (QPointF (x() - parObj->getChildPos().x() , 
50                                                                 (y() - parObj->getChildPos().y() ) ) );
51     init();
52 }
53
54 BranchObj::~BranchObj ()
55 {
56         // If I'm animated, I need to un-animate myself first
57         if (anim.isAnimated() )
58         {
59                 anim.setAnimated (false);
60                 mapEditor->getModel()->stopAnimation (this);
61         }
62
63
64         //cout << "Destr BranchObj of "<<this<<endl;
65         // Check, if this branch was the last child to be deleted
66         // If so, unset the scrolled flags
67
68         BranchObj *po=(BranchObj*)parObj;
69         BranchObj *bo;
70         if (po)
71         {
72                 bo=((BranchObj*)parObj)->getLastBranch();
73                 if (bo) po->unScroll();
74         }
75         clear();
76 }
77
78 bool BranchObj::operator< ( const BranchObj & other )
79 {
80     return  angle < other.angle;
81 }
82
83 bool BranchObj::operator== ( const BranchObj & other )
84 {
85     return angle == other.angle;
86 }
87
88 void BranchObj::init () 
89 {
90         if (parObj)
91         {
92                 absPos=getRandPos();
93                 absPos+=parObj->getChildPos();
94         }
95
96     lastSelectedBranch=0;
97
98     setChildObj(this);
99
100         scrolled=false;
101         tmpUnscrolled=false;
102
103         includeImagesVer=false;
104         includeImagesHor=false;
105 }
106
107 void BranchObj::copy (BranchObj* other)
108 {
109     OrnamentedObj::copy(other);
110
111         branch.clear();
112         for (int i=0; i<other->branch.size(); ++i)
113                 // Make deep copy of b
114                 // Because addBranch again calls copy for the childs,
115                 // Those will get a deep copy, too
116                 addBranch(other->branch.at(i) );        
117
118         for (int i=0; i<other->floatimage.size(); ++i)
119                 addFloatImage  (other->floatimage.at(i));
120         scrolled=other->scrolled;
121         tmpUnscrolled=other->tmpUnscrolled;
122         setVisibility (other->visible);
123
124         angle=other->angle;
125
126     positionBBox();
127 }
128
129 void BranchObj::clear() 
130 {
131         setVisibility (true);
132
133         while (!floatimage.isEmpty())
134                 delete floatimage.takeFirst();
135
136         while (!xlink.isEmpty())
137                 delete xlink.takeFirst();
138
139         while (!branch.isEmpty())
140                 delete branch.takeFirst();
141 }
142
143 bool isAbove (BranchObj* a, BranchObj *b)
144 {
145         if (a->angle < b->angle)
146                 return true;
147         else    
148                 return false;
149 }
150
151 int BranchObj::getNum()
152 {
153         if (parObj)
154                 return ((BranchObj*)parObj)->getNum (this);
155         else
156                 return 0;
157 }
158
159 int BranchObj::getNum(BranchObj *bo)
160 {
161         return branch.indexOf (bo);
162 }
163
164 int BranchObj::getFloatImageNum(FloatImageObj *fio)
165 {
166         return floatimage.indexOf(fio);
167 }
168
169 int BranchObj::countBranches()
170 {
171         return branch.count();
172 }
173
174 int BranchObj::countFloatImages()
175 {
176         return floatimage.count();
177 }
178
179 int BranchObj::countXLinks()
180 {
181         return xlink.count();
182 }
183
184 void BranchObj::setParObjTmp(LinkableMapObj* lmo, QPointF m, int off)
185 {
186         // Temporary link to lmo
187         // m is position of mouse pointer 
188         // offset 0: default 1: below lmo   -1 above lmo  (if possible)
189
190
191         BranchObj* o=(BranchObj*)(lmo);
192         if (!parObjTmpBuf) 
193                 parObjTmpBuf=parObj;
194
195         // ignore mapcenter and mainbranch
196         if (lmo->getDepth()<2) off=0;
197         if (off==0)
198                 link2ParPos=false;
199         else
200                 link2ParPos=true;
201         parObj=o;
202
203         depth=parObj->getDepth()+1;
204
205         // setLinkStyle calls updateLink, only set it once
206         if (style!=getDefLinkStyle() ) setLinkStyle (getDefLinkStyle());
207
208         // Move temporary to new position at destination
209         // Usually the positioning would be done by reposition(),
210         // but then also the destination branch would "Jump" around...
211         // Better just do it approximately
212         if (depth==1)
213         {       // new parent is the mapcenter itself
214
215                 QPointF p= normalise ( QPointF (m.x() - o->getChildPos().x(),
216                                                                           m.y() - o->getChildPos().y() ));
217                 if (p.x()<0) p.setX( p.x()-bbox.width() );
218                 move2RelPos (p);
219         } else
220         {       
221                 qreal y;
222                 if (off==0)
223                 {
224                         // new parent is just a branch, link to it
225                         QRectF t=o->getBBoxSizeWithChilds();
226                         if (o->getLastBranch())
227                                 y=t.y() + t.height() ;
228                         else
229                                 y=t.y();
230
231                 } else
232                 {
233                         if (off<0)
234                                 // we want to link above lmo
235                                 y=o->y() - height() + 5;
236                         else    
237                                 // we want to link below lmo
238                                 // Bottom of sel should be 5 pixels above
239                                 // the bottom of the branch _below_ the target:
240                                 // Don't try to find that branch, guess 12 pixels
241                                 y=o->getChildPos().y()  -height() + 12; 
242                 }       
243                 if (o->getOrientation()==LinkableMapObj::LeftOfCenter)
244                         move ( o->getChildPos().x() - linkwidth, y );
245                 else    
246                         move (o->getChildPos().x() + linkwidth, y );
247         }       
248
249         // updateLink is called implicitly in move
250         requestReposition();    
251 }
252
253 void BranchObj::unsetParObjTmp()
254 {
255         if (parObjTmpBuf) 
256         {
257                 link2ParPos=false;
258                 parObj=parObjTmpBuf;
259                 parObjTmpBuf=NULL;
260                 depth=parObj->getDepth()+1;
261                 setLinkStyle (getDefLinkStyle() );
262                 updateLink();
263         }               
264 }
265
266 void BranchObj::unScroll()
267 {
268         if (tmpUnscrolled) resetTmpUnscroll();
269         if (scrolled) toggleScroll();
270 }
271
272 void BranchObj::toggleScroll()
273 {
274         if (scrolled)
275         {
276                 scrolled=false;
277                 systemFlags->deactivate("scrolledright");
278                 for (int i=0; i<branch.size(); ++i)
279                         branch.at(i)->setVisibility(true);
280         } else
281         {
282                 scrolled=true;
283                 systemFlags->activate("scrolledright");
284                 for (int i=0; i<branch.size(); ++i)
285                         branch.at(i)->setVisibility(false);
286         }
287         calcBBoxSize();
288         positionBBox(); 
289         move (absPos.x(), absPos.y() );
290         forceReposition();
291 }
292
293 bool BranchObj::isScrolled()
294 {
295         return scrolled;
296 }
297
298 bool BranchObj::hasScrolledParent(BranchObj *start)
299 {
300         // Calls parents recursivly to
301         // find out, if we are scrolled at all.
302         // But ignore myself, just look at parents.
303
304         if (this !=start && scrolled) return true;
305
306         BranchObj* bo=(BranchObj*)(parObj);
307         if (bo) 
308                 return bo->hasScrolledParent(start);
309         else
310                 return false;
311 }
312
313 void BranchObj::tmpUnscroll()
314 {
315         // Unscroll parent (recursivly)
316         BranchObj* bo=(BranchObj*)(parObj);
317         if (bo) bo->tmpUnscroll();
318                 
319         // Unscroll myself
320         if (scrolled)
321         {
322                 tmpUnscrolled=true;
323                 systemFlags->activate("tmpUnscrolledright");
324                 toggleScroll();
325         }       
326 }
327
328 void BranchObj::resetTmpUnscroll()
329 {
330         // Unscroll parent (recursivly)
331         BranchObj* bo=(BranchObj*)(parObj);
332         if (bo)
333                 bo->resetTmpUnscroll();
334                 
335         // Unscroll myself
336         if (tmpUnscrolled)
337         {
338                 tmpUnscrolled=false;
339                 systemFlags->deactivate("tmpUnscrolledright");
340                 toggleScroll();
341         }       
342 }
343
344 void BranchObj::setVisibility(bool v, int toDepth)
345 {
346     if (depth <= toDepth)
347     {
348                 frame->setVisibility(v);
349                 heading->setVisibility(v);
350                 systemFlags->setVisibility(v);
351                 standardFlags->setVisibility(v);
352                 LinkableMapObj::setVisibility (v);
353                 
354                 // Only change childs, if I am not scrolled
355                 if (!scrolled && (depth < toDepth))
356                 {
357                         // Now go recursivly through all childs
358                         int i;
359                         for (i=0; i<branch.size(); ++i)
360                                 branch.at(i)->setVisibility (v,toDepth);        
361                         for (i=0; i<floatimage.size(); ++i)
362                                 floatimage.at(i)->setVisibility (v);
363                         for (i=0; i<xlink.size(); ++i)  
364                                 xlink.at(i)->setVisibility ();  
365                 }
366     } // depth <= toDepth       
367         requestReposition();
368 }       
369
370 void BranchObj::setVisibility(bool v)
371 {
372     setVisibility (v,MAX_DEPTH);
373 }
374
375
376 void BranchObj::setLinkColor ()
377 {
378         // Overloaded from LinkableMapObj
379         // BranchObj can use color of heading
380
381         if (mapEditor)
382         {
383                 if (mapEditor->getMapLinkColorHint()==HeadingColor)
384                         LinkableMapObj::setLinkColor (heading->getColor() );
385                 else    
386                         LinkableMapObj::setLinkColor ();
387         }               
388 }
389
390 void BranchObj::setColorSubtree(QColor col)
391 {
392         setColor (col);
393         for (int i=0; i<branch.size(); ++i)
394                 branch.at(i)->setColorSubtree(col);
395 }
396
397 BranchObj* BranchObj::first()
398 {
399         itLast=NULL;    
400         itFirst=this;
401         return this; 
402 }
403         
404 BranchObj* BranchObj::next()
405 {
406         BranchObj *bo;
407         BranchObj *lmo;
408         BranchObj *po=(BranchObj*)parObj;
409
410         if (branch.isEmpty())
411                 bo=NULL;
412         else
413                 bo=branch.first();
414
415         if (!itLast)
416         {
417                 // no itLast, we are just beginning
418                 if (bo) 
419                 {
420                         // we have childs, return first one
421                         itLast=this;
422                         return bo;
423                 }       
424                 else
425                 {
426                         // No childs, so there is no next
427                         itLast=this;
428                         return NULL;
429                 }       
430         }
431
432         // We have an itLast
433         if (itLast==po)
434         {       // We come from parent
435                 if (bo)
436                 {
437                         // there are childs, go there
438                         itLast=this;
439                         return bo;
440                 }       
441                 else
442                 {       // no childs, try to go up again
443                         if (po)
444                         {
445                                 // go back to parent and try to find next there
446                                 itLast=this;
447                                 lmo=po->next();
448                                 itLast=this;
449                                 return lmo;
450
451                         }       
452                         else
453                         {
454                                 // can't go up, I am mapCenter, no next
455                                 itLast=NULL;
456                                 return NULL;
457                         }       
458                 }
459         }
460
461         // We don't come from parent, but from brother or childs
462
463         // Try to find last child, where we came from, in my own childs
464         bool searching=true;
465         int i=0;
466         while (i<branch.size())
467         {
468                 // Try to find itLast in my own childs
469                 if (itLast==branch.at(i))
470                 {
471                         // ok, we come from my own childs
472                         if (i<branch.size()-1)
473                                 bo=branch.at(i+1);
474                          else
475                                 bo=NULL;
476                         searching=false;
477                         i=branch.size();
478                 }       
479                 ++i;    
480         }
481         if (!searching)
482         {       // found itLast in my childs
483                 if (bo)
484                 {
485                         // found a brother of lastLMO 
486                         itLast=this;
487                         return bo;
488                 }       
489                 else
490                 {
491                         if (po)
492                         {
493                                 if (this==itFirst) return NULL; // Stop at starting point
494                                 // go up
495                                 itLast=this;
496                                 lmo=po->next();
497                                 itLast=this;
498                                 return lmo;
499                         }
500                         else
501                         {
502                                 // can't go up, I am mapCenter
503                                 itLast=NULL;
504                                 return NULL;
505                         }       
506                 }
507         }
508
509         // couldn't find last child, it must be a nephew of mine
510         if (branch.size()>0)
511         {
512                 // proceed with my first child
513                 itLast=this;    
514                 return branch.first();
515         }       
516         else
517         {
518                 // or go back to my parents
519                 if (po)
520                 {
521                         // go up
522                         itLast=this;
523                         lmo=po->next();
524                         itLast=this;
525                         return lmo;
526                 }       
527                 else
528                 {
529                         // can't go up, I am mapCenter
530                         itLast=NULL;
531                         return NULL;
532                 }       
533         }       
534 }
535
536 BranchObj* BranchObj::getLastIterator()
537 {
538         return itLast;
539 }
540
541 void BranchObj::setLastIterator(BranchObj* it)
542 {
543         itLast=it;
544 }
545
546 void BranchObj::positionContents()
547 {
548     for (int i=0; i<floatimage.size(); ++i )
549                 floatimage.at(i)->reposition();
550         OrnamentedObj::positionContents();
551 }
552
553 void BranchObj::move (double x, double y)
554 {
555         OrnamentedObj::move (x,y);
556     for (int i=0; i<floatimage.size(); ++i )
557                 floatimage.at(i)->reposition();
558     positionBBox();
559 }
560
561 void BranchObj::move (QPointF p)
562 {
563         move (p.x(), p.y());
564 }
565
566 void BranchObj::moveBy (double x, double y)
567 {
568         OrnamentedObj::moveBy (x,y);
569         for (int i=0; i<branch.size(); ++i)
570                 branch.at(i)->moveBy (x,y);
571     positionBBox();
572 }
573         
574 void BranchObj::moveBy (QPointF p)
575 {
576         moveBy (p.x(), p.y());
577 }
578
579
580 void BranchObj::positionBBox()
581 {
582         QPointF ap=getAbsPos();
583         bbox.moveTopLeft (ap);
584         positionContents();
585
586         // set the frame
587         frame->setRect(QRectF(bbox.x(),bbox.y(),bbox.width(),bbox.height() ) );
588
589         // Update links to other branches
590         for (int i=0; i<xlink.size(); ++i)
591                 xlink.at(i)->updateXLink();
592 }
593
594 void BranchObj::calcBBoxSize()
595 {
596     QSizeF heading_r=heading->getSize();
597     qreal heading_w=(qreal) heading_r.width() ;
598     qreal heading_h=(qreal) heading_r.height() ;
599     QSizeF sysflags_r=systemFlags->getSize();
600         qreal sysflags_h=sysflags_r.height();
601         qreal sysflags_w=sysflags_r.width();
602     QSizeF stanflags_r=standardFlags->getSize();
603         qreal stanflags_h=stanflags_r.height();
604         qreal stanflags_w=stanflags_r.width();
605     qreal w;
606     qreal h;
607
608         // set width to sum of all widths
609         w=heading_w + sysflags_w + stanflags_w;
610         // set height to maximum needed height
611         h=max (sysflags_h,stanflags_h);
612         h=max (h,heading_h);
613
614         // Save the dimension of flags and heading
615         ornamentsBBox.setSize ( QSizeF(w,h));
616
617         // clickBox includes Flags and Heading
618     clickBox.setSize (ornamentsBBox.size() );
619
620         // Floatimages 
621         QPointF rp;
622
623         topPad=botPad=leftPad=rightPad=0;
624         if (includeImagesVer || includeImagesHor)
625         {
626                 if (countFloatImages()>0)
627                 {
628                         for (int i=0; i<floatimage.size(); ++i )
629                         {
630                                 rp=floatimage.at(i)->getRelPos();
631                                 if (includeImagesVer)
632                                 {
633                                         if (rp.y() < 0) 
634                                                 topPad=max (topPad,-rp.y()-h);
635                                         if (rp.y()+floatimage.at(i)->height() > 0)
636                                                 botPad=max (botPad,rp.y()+floatimage.at(i)->height());
637                                 }               
638                                 if (includeImagesHor)
639                                 {
640                                         if (orientation==LinkableMapObj::RightOfCenter)
641                                         {
642                                                 if (-rp.x()-w > 0) 
643                                                         leftPad=max (leftPad,-rp.x()-w);
644                                                 if (rp.x()+floatimage.at(i)->width() > 0)
645                                                         rightPad=max (rightPad,rp.x()+floatimage.at(i)->width());
646                                         } else
647                                         {
648                                                 if (rp.x()< 0) 
649                                                         leftPad=max (leftPad,-rp.x());
650                                                 if (rp.x()+floatimage.at(i)->width() > w)
651                                                         rightPad=max (rightPad,rp.x()+floatimage.at(i)->width()-w);
652                                         }
653                                 }               
654                         }       
655                 }       
656                 h+=topPad+botPad;
657                 w+=leftPad+rightPad;
658         }
659
660         // Frame thickness
661     w+=frame->getPadding();
662     h+=frame->getPadding();
663         
664         // Finally set size
665     bbox.setSize (QSizeF (w,h));
666 }
667
668 void BranchObj::setDockPos()
669 {
670         // Sets childpos and parpos depending on orientation
671         if (getOrientation()==LinkableMapObj::LeftOfCenter )
672     {
673                 childPos=QPointF (
674                         ornamentsBBox.bottomLeft().x(), 
675                         bottomlineY);
676                 parPos=QPointF (
677                         ornamentsBBox.bottomRight().x(),
678                         bottomlineY);
679     } else
680     {
681                 childPos=QPointF (
682                         ornamentsBBox.bottomRight().x(), 
683                         bottomlineY);
684                 parPos=QPointF (
685                         ornamentsBBox.bottomLeft().x(),
686                         bottomlineY);
687     }
688 }
689
690 LinkableMapObj* BranchObj::findMapObj(QPointF p, LinkableMapObj* excludeLMO)
691 {
692         // Search branches
693     LinkableMapObj *lmo;
694         for (int i=0; i<branch.size(); ++i)
695     {   
696                 lmo=branch.at(i)->findMapObj(p, excludeLMO);
697                 if (lmo != NULL) return lmo;
698     }
699         
700
701         // Search myself
702     if (inBox (p,clickBox) && (this != excludeLMO) && isVisibleObj() ) 
703                 return this;
704
705         // Search float images
706     for (int i=0; i<floatimage.size(); ++i )
707                 if (inBox(p,floatimage.at(i)->getClickBox()) && 
708                         (floatimage.at(i) != excludeLMO) && 
709                         floatimage.at(i)->getParObj()!= excludeLMO &&
710                         floatimage.at(i)->isVisibleObj() 
711                 ) return floatimage.at(i);
712
713     return NULL;
714 }
715
716 LinkableMapObj* BranchObj::findID (QString sid)
717 {
718         // Search branches
719     LinkableMapObj *lmo;
720         for (int i=0; i<branch.size(); ++i)
721     {   
722                 lmo=branch.at(i)->findID (sid);
723                 if (lmo != NULL) return lmo;
724     }
725         
726         // Search myself
727         if (sid==objID) return this;
728
729
730 /*
731         // Search float images
732     for (int i=0; i<floatimage.size(); ++i )
733                 if (floatimage.at(i)->inBox(p) && 
734                         (floatimage.at(i) != excludeLMO) && 
735                         floatimage.at(i)->getParObj()!= excludeLMO &&
736                         floatimage.at(i)->isVisibleObj() 
737                 ) return floatimage.at(i);
738 */
739     return NULL;
740 }
741
742 void BranchObj::setHeading(QString s)
743 {
744     heading->setText(s);        // set new heading
745         calcBBoxSize();                 // recalculate bbox
746     positionBBox();                     // rearrange contents
747         requestReposition();
748 }
749
750 void BranchObj::setHideTmp (HideTmpMode mode)
751 {
752         if (mode==HideExport && (hideExport|| hasHiddenExportParent() ) )
753         {
754                 // Hide stuff according to hideExport flag and parents
755                 setVisibility (false);
756                 hidden=true;
757         }else
758         {
759                 // Do not hide, but still take care of scrolled status
760                 if (hasScrolledParent(this))
761                         setVisibility (false);
762                 else
763                         setVisibility (true);
764                 hidden=false;
765         }       
766
767         // And take care of my childs
768         for (int i=0; i<branch.size(); ++i)
769                 branch.at(i)->setHideTmp (mode);
770 }
771
772 bool BranchObj::hasHiddenExportParent()
773 {
774         // Calls parents recursivly to
775         // find out, if we or parents are temp. hidden
776
777         if (hidden || hideExport) return true;
778
779         BranchObj* bo=(BranchObj*)parObj;
780         if (bo) 
781                 return bo->hasHiddenExportParent();
782         else
783                 return false;
784 }
785
786 QString BranchObj::saveToDir (const QString &tmpdir,const QString &prefix, const QPointF& offset)
787 {
788         // Cloudy stuff can be hidden during exports
789         if (hidden) return "";
790
791         // Update of note is usually done while unselecting a branch
792         if (isNoteInEditor) getNoteFromTextEditor();
793         
794     QString s,a;
795         QString scrolledAttr;
796         if (scrolled) 
797                 scrolledAttr=attribut ("scrolled","yes");
798         else
799                 scrolledAttr="";
800
801         // save area, if not scrolled
802         QString areaAttr;
803         if (!((BranchObj*)(parObj))->isScrolled() )
804         {
805                 areaAttr=
806                         attribut("x1",QString().setNum(absPos.x()-offset.x())) +
807                         attribut("y1",QString().setNum(absPos.y()-offset.y())) +
808                         attribut("x2",QString().setNum(absPos.x()+width()-offset.x())) +
809                         attribut("y2",QString().setNum(absPos.y()+height()-offset.y()));
810
811         } else
812                 areaAttr="";
813         
814         // Providing an ID for a branch makes export to XHTML easier
815         QString idAttr;
816         if (countXLinks()>0)
817                 idAttr=attribut ("id",mapEditor->getModel()->getSelectString(this)); //TODO directly access model
818         else
819                 idAttr="";
820
821     s=beginElement ("branch" 
822                 +getOrnXMLAttr() 
823                 +scrolledAttr 
824                 +areaAttr 
825                 +idAttr 
826                 +getIncludeImageAttr() );
827     incIndent();
828
829         // save heading
830     s+=valueElement("heading", getHeading(),
831                 attribut ("textColor",QColor(heading->getColor()).name()));
832
833         // Save frame
834         if (frame->getFrameType()!=FrameObj::NoFrame) 
835                 s+=frame->saveToDir ();
836
837         // save names of flags set
838         s+=standardFlags->saveToDir(tmpdir,prefix,0);
839         
840         // Save FloatImages
841         for (int i=0; i<floatimage.size(); ++i)
842                 s+=floatimage.at(i)->saveToDir (tmpdir,prefix);
843
844         // save note
845         if (!note.isEmpty() )
846                 s+=note.saveToDir();
847         
848         // Save branches
849         for (int i=0; i<branch.size(); ++i)
850                 s+=branch.at(i)->saveToDir(tmpdir,prefix,offset);
851
852         // Save XLinks
853         QString ol;     // old link
854         QString cl;     // current link
855         for (int i=0; i<xlink.size(); ++i)
856         {
857                 cl=xlink.at(i)->saveToDir();
858                 if (cl!=ol)
859                 {
860                         s+=cl;
861                         ol=cl;
862                 } else
863                 {
864                         qWarning (QString("Ignoring of duplicate xLink in %1").arg(getHeading()));
865                 }
866         }       
867
868     decIndent();
869     s+=endElement   ("branch");
870     return s;
871 }
872
873 void BranchObj::addXLink (XLinkObj *xlo)
874 {
875         xlink.append (xlo);
876         
877 }
878
879 void BranchObj::removeXLinkRef (XLinkObj *xlo)
880 {
881         xlink.removeAt (xlink.indexOf(xlo));
882 }
883
884 void BranchObj::deleteXLink(XLinkObj *xlo)
885 {
886         xlo->deactivate();
887         if (!xlo->isUsed()) delete (xlo);
888 }
889
890 void BranchObj::deleteXLinkAt (int i)
891 {
892         XLinkObj *xlo=xlink.at(i);
893         xlo->deactivate();
894         if (!xlo->isUsed()) delete(xlo);
895 }
896
897 XLinkObj* BranchObj::XLinkAt (int i)
898 {
899         return xlink.at(i);
900 }
901
902 int BranchObj::countXLink()
903 {
904         return xlink.count();
905 }
906
907
908 BranchObj* BranchObj::XLinkTargetAt (int i)
909 {
910         if (i>=0 && i<xlink.size())
911         {
912                 if (xlink.at(i))
913                         return xlink.at(i)->otherBranch (this);
914         }
915         return NULL;
916 }
917
918 void BranchObj::setIncludeImagesVer(bool b)
919 {
920         includeImagesVer=b;
921         calcBBoxSize();
922         positionBBox();
923         requestReposition();
924 }
925
926 bool BranchObj::getIncludeImagesVer()
927 {
928         return includeImagesVer;
929 }
930
931 void BranchObj::setIncludeImagesHor(bool b)
932 {
933         includeImagesHor=b;
934         calcBBoxSize();
935         positionBBox();
936         requestReposition();
937 }
938
939 bool BranchObj::getIncludeImagesHor()
940 {
941         return includeImagesHor;
942 }
943
944 QString BranchObj::getIncludeImageAttr()
945 {
946         QString a;
947         if (includeImagesVer)
948                 a=attribut ("incImgV","true");
949         else
950                 a=attribut ("incImgV","false");
951         if (includeImagesHor)
952                 a+=attribut ("incImgH","true");
953         else
954                 a+=attribut ("incImgH","false");
955         return a;       
956 }
957
958 FloatImageObj* BranchObj::addFloatImage ()
959 {
960         FloatImageObj *newfi=new FloatImageObj (scene,this);
961         floatimage.append (newfi);
962         if (hasScrolledParent(this) )
963                 newfi->setVisibility (false);
964         else    
965                 newfi->setVisibility(visible);
966                 /*
967         calcBBoxSize();
968         positionBBox();
969         */
970         requestReposition();
971         return newfi;
972 }
973
974 FloatImageObj* BranchObj::addFloatImage (FloatImageObj *fio)
975 {
976         FloatImageObj *newfi=new FloatImageObj (scene,this);
977         floatimage.append (newfi);
978         newfi->copy (fio);
979         if (hasScrolledParent(this) )
980                 newfi->setVisibility (false);
981         else    
982                 newfi->setVisibility(visible);
983                 /*
984         calcBBoxSize();
985         positionBBox();
986         */
987         requestReposition();
988         return newfi;
989 }
990
991 FloatImageObj* BranchObj::getFirstFloatImage ()
992 {
993     return floatimage.first();
994 }
995
996 FloatImageObj* BranchObj::getLastFloatImage ()
997 {
998     return floatimage.last();
999 }
1000
1001 FloatImageObj* BranchObj::getFloatImageNum (const uint &i)
1002 {
1003     return floatimage.at(i);
1004 }
1005
1006 void BranchObj::removeFloatImage (FloatImageObj *fio)
1007 {
1008         int i=floatimage.indexOf (fio);
1009         if (i>-1) delete (floatimage.takeAt (i));
1010         calcBBoxSize();
1011         positionBBox();
1012         requestReposition();
1013 }
1014
1015 void BranchObj::savePosInAngle ()
1016 {
1017         // Save position in angle
1018         for (int i=0; i<branch.size(); ++i)
1019                 branch.at(i)->angle=i;
1020 }
1021
1022 void BranchObj::setDefAttr (BranchModification mod)
1023 {
1024         int fontsize;
1025         switch (depth)
1026         {
1027                 case 0: fontsize=16; break;
1028                 case 1: fontsize=12; break;
1029                 default: fontsize=10; break;
1030         }       
1031
1032         setLinkColor ();
1033         setLinkStyle(getDefLinkStyle());
1034         QFont font("Sans Serif,8,-1,5,50,0,0,0,0,0");
1035         font.setPointSize(fontsize);
1036         heading->setFont(font );
1037
1038         if (mod==NewBranch)
1039                 setColor (((BranchObj*)(parObj))->getColor());
1040         
1041         calcBBoxSize();
1042 }
1043
1044 BranchObj* BranchObj::addBranch()
1045 {
1046     BranchObj* newbo=new BranchObj(scene,this);
1047     branch.append (newbo);
1048     newbo->setParObj(this);
1049         newbo->setDefAttr(NewBranch);
1050     newbo->setHeading ("new");
1051         if (scrolled)
1052                 newbo->setVisibility (false);
1053         else    
1054                 newbo->setVisibility(visible);
1055         newbo->updateLink();    
1056         requestReposition();
1057         return newbo;
1058 }
1059
1060 BranchObj* BranchObj::addBranch(BranchObj* bo)
1061 {
1062     BranchObj* newbo=new BranchObj(scene,this);
1063     branch.append (newbo);
1064     newbo->copy(bo);
1065     newbo->setParObj(this);
1066         newbo->setDefAttr(MovedBranch);
1067         if (scrolled)
1068                 newbo->setVisibility (false);
1069         else    
1070                 newbo->setVisibility(bo->visible);
1071         newbo->updateLink();    
1072         requestReposition();
1073         return newbo;
1074 }
1075
1076 BranchObj* BranchObj::addBranchPtr(BranchObj* bo)
1077 {
1078         branch.append (bo);
1079         bo->setParObj (this);
1080         bo->depth=depth+1;
1081         bo->setDefAttr(MovedBranch);
1082         if (scrolled) tmpUnscroll();
1083         setLastSelectedBranch (bo);
1084         return bo;
1085 }
1086
1087 BranchObj* BranchObj::insertBranch(int pos)
1088 {
1089         savePosInAngle();
1090         // Add new bo and resort branches
1091         BranchObj *newbo=addBranch ();
1092         newbo->angle=pos-0.5;
1093         qSort (branch.begin(),branch.end(), isAbove);
1094         return newbo;
1095 }
1096
1097 BranchObj* BranchObj::insertBranch(BranchObj* bo, int pos)
1098 {
1099         savePosInAngle();
1100         // Add new bo and resort branches
1101         bo->angle=pos-0.5;
1102         BranchObj *newbo=addBranch (bo);
1103         qSort (branch.begin(),branch.end(), isAbove);
1104         return newbo;
1105 }
1106
1107 BranchObj* BranchObj::insertBranchPtr (BranchObj* bo, int pos)
1108 {
1109         savePosInAngle();
1110         // Add new bo and resort branches
1111         bo->angle=pos-0.5;
1112         branch.append (bo);
1113         bo->setParObj (this);
1114         bo->depth=depth+1;
1115         bo->setDefAttr (MovedBranch);
1116         if (scrolled) tmpUnscroll();
1117         setLastSelectedBranch (bo);
1118         qSort (branch.begin(),branch.end(), isAbove);
1119         return bo;
1120 }
1121
1122 void BranchObj::removeBranchHere(BranchObj* borem)
1123 {
1124         // This removes the branch bo from list, but 
1125         // inserts its childs at the place of bo
1126         BranchObj *bo;
1127         bo=borem->getLastBranch();
1128         int pos=borem->getNum();
1129         while (bo)
1130         {
1131                 bo->linkTo (this,pos+1);
1132                 bo=borem->getLastBranch();
1133         }       
1134         removeBranch (borem);
1135 }
1136
1137 void BranchObj::removeChilds()
1138 {
1139         clear();
1140 }
1141
1142 void BranchObj::removeBranch(BranchObj* bo)
1143 {
1144     // if bo is not in branch remove returns false, we
1145     // don't care...
1146         
1147         int i=branch.indexOf(bo);
1148     if (i>=0)
1149         {
1150                 delete (bo);
1151                 branch.removeAt (i);
1152         } else
1153                 qWarning ("BranchObj::removeBranch tried to remove non existing branch?!\n");
1154         requestReposition();
1155 }
1156
1157 void BranchObj::removeBranchPtr(BranchObj* bo)
1158 {
1159         int i=branch.indexOf(bo);
1160         
1161         if (i>=0)
1162                 branch.removeAt (i);
1163         else    
1164                 qWarning ("BranchObj::removeBranchPtr tried to remove non existing branch?!\n");
1165         requestReposition();
1166 }
1167
1168 void BranchObj::setLastSelectedBranch (BranchObj* bo)
1169 {
1170     lastSelectedBranch=branch.indexOf(bo);
1171 }
1172
1173 BranchObj* BranchObj::getLastSelectedBranch ()
1174 {
1175     if (lastSelectedBranch>=0)
1176         {
1177                 if ( branch.size()>lastSelectedBranch) 
1178                         return branch.at(lastSelectedBranch);
1179                 if (branch.size()>0)
1180                         return branch.last();
1181         }       
1182     return NULL;
1183 }
1184
1185 BranchObj* BranchObj::getFirstBranch ()
1186 {
1187         if (branch.size()>0)
1188                 return branch.first();
1189         else
1190                 return NULL;
1191 }
1192
1193 BranchObj* BranchObj::getLastBranch ()
1194 {
1195         if (branch.size()>0)
1196                 return branch.last();
1197         else
1198                 return NULL;
1199 }
1200
1201 BranchObj* BranchObj::getBranchNum (int i)
1202 {
1203         if (i>=0 && i<branch.size())
1204                 return branch.at(i);
1205         else    
1206                 return  NULL;
1207 }
1208
1209 bool BranchObj::canMoveBranchUp() 
1210 {
1211         if (!parObj || depth==1) return false;
1212         BranchObj* par=(BranchObj*)parObj;
1213         if (this==par->getFirstBranch())
1214                 return false;
1215         else
1216                 return true;
1217 }
1218
1219 BranchObj* BranchObj::moveBranchUp(BranchObj* bo1) // modify my childlist
1220 {
1221         savePosInAngle();
1222     int i=branch.indexOf(bo1);
1223     if (i>0) 
1224         {       // -1 if bo1 not found 
1225                 branch.at(i)->angle--;
1226                 branch.at(i-1)->angle++;
1227                 qSort (branch.begin(),branch.end(), isAbove);
1228                 return branch.at(i);
1229         } else
1230                 return NULL;
1231 }
1232
1233 bool BranchObj::canMoveBranchDown() 
1234 {
1235         if (!parObj|| depth==1) return false;
1236         BranchObj* par=(BranchObj*)parObj;
1237         if (this==par->getLastBranch())
1238                 return false;
1239         else
1240                 return true;
1241 }
1242
1243 BranchObj* BranchObj::moveBranchDown(BranchObj* bo1)// modify my childlist
1244 {
1245         savePosInAngle();
1246     int i=branch.indexOf(bo1);
1247         int j;
1248         if (i <branch.size())
1249         {
1250                 j = i+1;
1251                 branch.at(i)->angle++;
1252                 branch.at(j)->angle--;
1253                 qSort (branch.begin(),branch.end(), isAbove);
1254                 return branch.at(i);
1255         } else
1256                 return NULL;
1257 }
1258
1259 void BranchObj::sortChildren()
1260 {
1261         int childCount=branch.count();
1262         int curChildIndex;
1263         bool madeChanges=false;
1264         do
1265         {
1266                 madeChanges=false;
1267                 for(curChildIndex=1;curChildIndex<childCount;curChildIndex++){
1268                         BranchObj* curChild=(BranchObj*)branch.at(curChildIndex);
1269                         BranchObj* prevChild=(BranchObj*)branch.at(curChildIndex-1);
1270                         if(prevChild->heading->text().compare(curChild->heading->text())>0)
1271                         {
1272                                 this->moveBranchUp(curChild);
1273                                 madeChanges=true;
1274                         }
1275                 }
1276         }while(madeChanges);
1277 }
1278
1279
1280 BranchObj* BranchObj::linkTo (BranchObj* dst, int pos)
1281 {
1282         // Find current parent and 
1283         // remove pointer to myself there
1284         if (!dst) return NULL;
1285         BranchObj *par=(BranchObj*)parObj;
1286         if (par)
1287                 par->removeBranchPtr (this);
1288         else
1289                 return NULL;
1290
1291         // Create new pointer to myself at dst
1292         if (pos<0||dst->getDepth()==0)
1293         {       
1294                 // links myself as last branch at dst
1295                 dst->addBranchPtr (this);
1296                 updateLink();
1297                 return this;
1298         } else
1299         {
1300                 // inserts me at pos in parent of dst
1301                 if (par)
1302                 {
1303                         BranchObj *bo=dst->insertBranchPtr (this,pos);
1304                         bo->setDefAttr(MovedBranch);
1305                         updateLink();
1306                         return bo;
1307
1308                 } else
1309                         return NULL;
1310         }       
1311 }
1312
1313 void BranchObj::alignRelativeTo (QPointF ref,bool alignSelf)
1314 {
1315         qreal th = bboxTotal.height();  
1316 // TODO testing
1317 /*
1318         QPointF pp; if (parObj) pp=parObj->getChildPos();
1319         cout << "BO::alignRelTo "<<qPrintable (getHeading());
1320         cout << "    d="<<depth<<
1321                 "  ref="<<ref<<
1322 //              "  bbox.topLeft="<<bboxTotal.topLeft()<<
1323                 "  absPos="<<absPos<<
1324                 "  relPos="<<relPos<<
1325                 "  parPos="<<pp<<
1326                 "  orient="<<orientation<<
1327 //              "  pad="<<topPad<<","<<botPad<<","<<leftPad<<","<<rightPad<<
1328 //              "  hidden="<<hidden<<
1329 //              "  th="<<th<<
1330                 endl;
1331 */
1332
1333         setOrientation();
1334         //updateLink();
1335
1336         if (depth<2)
1337         {
1338                 if (depth==1)
1339                 {
1340                         // Position relatively, if needed
1341                         //if (useRelPos) move2RelPos (relPos.x(), relPos.y());
1342
1343                         // Calc angle to mapCenter if I am a mainbranch
1344                         // needed for reordering the mainbranches clockwise 
1345                         // around mapcenter 
1346                         angle=getAngle (QPointF ((int)(x() - parObj->getChildPos().x() ), 
1347                                                                         (int)(y() - parObj->getChildPos().y() ) ) );
1348                 }                                                       
1349         } 
1350         else
1351     {
1352                 // Align myself depending on orientation and parent, but
1353                 // only if I am not a mainbranch or mapcenter itself
1354
1355                 if (anim.isAnimated())
1356                 {
1357                         move2RelPos(anim);
1358                 } else
1359                 {
1360                         LinkableMapObj::Orientation o;
1361                         o=parObj->getOrientation();
1362                         if (alignSelf)
1363                                 switch (orientation) 
1364                                 {
1365                                         case LinkableMapObj::LeftOfCenter:
1366                                                 move (ref.x() - bbox.width(), ref.y() + (th-bbox.height())/2 );
1367                                         break;
1368                                         case LinkableMapObj::RightOfCenter:     
1369                                                 move (ref.x() , ref.y() + (th-bbox.height())/2  );
1370                                         break;
1371                                         default:
1372                                                 qWarning ("LMO::alignRelativeTo: oops, no orientation given...");
1373                                         break;
1374                                 }       
1375                 }
1376     }           
1377
1378         if (scrolled) return;
1379
1380     // Set reference point for alignment of childs
1381     QPointF ref2;
1382     if (orientation==LinkableMapObj::LeftOfCenter)
1383                 ref2.setX(bbox.topLeft().x() - linkwidth);
1384     else        
1385                 ref2.setX(bbox.topRight().x() + linkwidth);
1386
1387         if (depth==1)
1388                 ref2.setY(absPos.y()-(bboxTotal.height()-bbox.height())/2);
1389         else    
1390                 ref2.setY(ref.y() );    
1391
1392     // Align the childs depending on reference point 
1393         for (int i=0; i<branch.size(); ++i)
1394     {   
1395                 if (!branch.at(i)->isHidden())
1396                 {
1397                         branch.at(i)->alignRelativeTo (ref2,true);
1398                         ref2.setY(ref2.y() + branch.at(i)->getBBoxSizeWithChilds().height() );
1399                 }
1400     }
1401 }
1402
1403
1404 void BranchObj::reposition()
1405 {       
1406 /* TODO testing only
1407         if (!getHeading().isEmpty())
1408                 cout << "BO::reposition  "<<qPrintable(getHeading())<<endl;
1409         else    
1410                 cout << "BO::reposition  ???"<<endl;
1411
1412         cout << "  orient="<<orientation<<endl;
1413 */              
1414
1415         if (depth==0)
1416         {
1417                 // only calculate the sizes once. If the deepest LMO 
1418                 // changes its height,
1419                 // all upper LMOs have to change, too.
1420                 calcBBoxSizeWithChilds();
1421                 updateLink();   // This update is needed if the scene is resized 
1422                                                 // due to excessive moving of a FIO
1423
1424             alignRelativeTo ( QPointF (absPos.x(),
1425                         absPos.y()-(bboxTotal.height()-bbox.height())/2) );
1426                 qSort (branch.begin(),branch.end(), isAbove);
1427                 positionBBox(); // Reposition bbox and contents
1428         } else
1429         {
1430                 // This is only important for moving branches:
1431                 // For editing a branch it isn't called...
1432             alignRelativeTo ( QPointF (absPos.x(),
1433                                                         absPos.y()-(bboxTotal.height()-bbox.height())/2) );
1434         }
1435 }
1436
1437 void BranchObj::unsetAllRepositionRequests()
1438 {
1439         repositionRequest=false;
1440         for (int i=0; i<branch.size(); ++i)
1441                 branch.at(i)->unsetAllRepositionRequests();
1442 }
1443
1444
1445 QPolygonF BranchObj::shape()
1446 {
1447         QPolygonF p;
1448
1449         QRectF r=getTotalBBox();
1450         if (orientation==LinkableMapObj::LeftOfCenter)
1451                 p   <<r.bottomLeft()
1452                         <<r.topLeft()
1453                         <<QPointF (bbox.topLeft().x(), r.topLeft().y() )
1454                         <<bbox.topRight()
1455                         <<bbox.bottomRight()
1456                         <<QPointF (bbox.bottomLeft().x(), r.bottomLeft().y() ) ;
1457         else            
1458                 p   <<r.bottomRight()
1459                         <<r.topRight()
1460                         <<QPointF (bbox.topRight().x(), r.topRight().y() )
1461                         <<bbox.topLeft()
1462                         <<bbox.bottomLeft()
1463                         <<QPointF (bbox.bottomRight().x(), r.bottomRight().y() ) ;
1464         return p;
1465 }
1466
1467 QRectF BranchObj::getTotalBBox()
1468 {
1469         QRectF r=bbox;
1470
1471         if (scrolled) return r;
1472
1473         for (int i=0; i<branch.size(); ++i)
1474                 if (!branch.at(i)->isHidden())
1475                         r=addBBox(branch.at(i)->getTotalBBox(),r);
1476
1477         for (int i=0; i<floatimage.size(); ++i)
1478                 if (!floatimage.at(i)->isHidden())
1479                         r=addBBox(floatimage.at(i)->getTotalBBox(),r);
1480                 
1481         return r;
1482 }
1483
1484 QRectF BranchObj::getBBoxSizeWithChilds()
1485 {
1486         return bboxTotal;
1487 }
1488
1489 void BranchObj::calcBBoxSizeWithChilds()
1490 {       
1491         // This is initially called only from reposition and
1492         // and only for mapcenter. So it won't be
1493         // called more than once for a single user 
1494         // action
1495         
1496
1497         // Calculate size of LMO including all childs (to align them later)
1498         bboxTotal.setX(bbox.x() );
1499         bboxTotal.setY(bbox.y() );
1500
1501         // if branch is scrolled, ignore childs, but still consider floatimages
1502         if (scrolled)
1503         {
1504                 bboxTotal.setWidth (bbox.width());
1505                 bboxTotal.setHeight(bbox.height());
1506                 return;
1507         }
1508         
1509         if (hidden)
1510         {
1511                 bboxTotal.setWidth (0);
1512                 bboxTotal.setHeight(0);
1513                 if (parObj)
1514                 {
1515                         bboxTotal.setX (parObj->x());
1516                         bboxTotal.setY (parObj->y());
1517                 } else
1518                 {
1519                         bboxTotal.setX (bbox.x());
1520                         bboxTotal.setY (bbox.y());
1521                 }
1522                 return;
1523         }
1524         
1525         QRectF r(0,0,0,0);
1526         QRectF br;
1527         // Now calculate recursivly
1528         // sum of heights 
1529         // maximum of widths 
1530         // minimum of y
1531         for (int i=0; i<branch.size(); ++i)
1532         {
1533                 if (!branch.at(i)->isHidden())
1534                 {
1535                         branch.at(i)->calcBBoxSizeWithChilds();
1536                         br=branch.at(i)->getBBoxSizeWithChilds();
1537                         r.setWidth( max (br.width(), r.width() ));
1538                         r.setHeight(br.height() + r.height() );
1539                         if (br.y()<bboxTotal.y()) bboxTotal.setY(br.y());
1540                 }
1541         }
1542         // Add myself and also
1543         // add width of link to sum if necessary
1544         if (branch.isEmpty())
1545                 bboxTotal.setWidth (bbox.width() + r.width() );
1546         else    
1547                 bboxTotal.setWidth (bbox.width() + r.width() + linkwidth);
1548         
1549         bboxTotal.setHeight(max (r.height(),  bbox.height()));
1550 }
1551
1552 void BranchObj::select()
1553 {
1554         // update NoteEditor
1555         textEditor->setText(note.getNote() );
1556         QString fnh=note.getFilenameHint();
1557         if (fnh!="")
1558                 textEditor->setFilenameHint(note.getFilenameHint() );
1559         else    
1560                 textEditor->setFilenameHint(getHeading() );
1561         textEditor->setFontHint (note.getFontHint() );
1562         isNoteInEditor=true;
1563
1564         // set selected and visible
1565     LinkableMapObj::select();
1566
1567         // Tell parent that I am selected now:
1568         BranchObj* po=(BranchObj*)(parObj);
1569     if (po)     // TODO     Try to get rid of this cast...
1570         po->setLastSelectedBranch(this);
1571                 
1572         // temporary unscroll, if we have scrolled parents somewhere
1573         if (parObj) ((BranchObj*)(parObj))->tmpUnscroll();
1574
1575         // Show URL and link in statusbar
1576         QString status;
1577         if (!url.isEmpty()) status+="URL: "+url+"  ";
1578         if (!vymLink.isEmpty()) status+="Link: "+vymLink;
1579         if (!status.isEmpty()) mainWindow->statusMessage (status);
1580
1581         // Update Toolbar
1582         updateFlagsToolbar();
1583
1584         // Update actions
1585         mapEditor->updateActions();
1586 }
1587
1588 void BranchObj::unselect()
1589 {
1590         LinkableMapObj::unselect();
1591         // Delete any messages like vymLink in StatusBar
1592         mainWindow->statusMessage ("");
1593
1594         // Save current note
1595         if (isNoteInEditor) getNoteFromTextEditor();
1596         isNoteInEditor=false;
1597
1598         // reset temporary unscroll, if we have scrolled parents somewhere
1599         if (parObj) ((BranchObj*)(parObj))->resetTmpUnscroll();
1600
1601         // Erase content of editor 
1602         textEditor->setInactive();
1603
1604         // unselect all buttons in toolbar
1605         standardFlagsDefault->updateToolbar();
1606 }
1607
1608 QString BranchObj::getSelectString()
1609 {
1610         return mapEditor->getModel()->getSelectString (this);
1611 }
1612
1613 void BranchObj::setAnimation(const AnimPoint &ap)
1614 {
1615         anim=ap;
1616 }
1617
1618 bool BranchObj::animate()
1619 {
1620         anim.animate ();
1621         if ( anim.isAnimated() )
1622         {
1623                 setRelPos (anim);
1624                 return true;
1625         }
1626         parObj->reposition();   // object might have been relinked meanwhile
1627         return false;
1628 }
1629