]> git.sven.stormbind.net Git - sven/vym.git/blob - branchobj.cpp
Import Upstream version 2.6.11
[sven/vym.git] / branchobj.cpp
1 #include <QDebug>
2
3 #include "branchobj.h"
4
5 #include "attributeitem.h"
6 #include "branchitem.h"
7 #include "geometry.h"
8 #include "mapeditor.h"
9 #include "mainwindow.h"   
10 #include "misc.h"
11
12 extern FlagRow *standardFlagsMaster;
13 extern FlagRow *systemFlagsMaster;
14 extern bool debug;
15
16 /////////////////////////////////////////////////////////////////
17 // BranchObj
18 /////////////////////////////////////////////////////////////////
19
20 BranchObj::BranchObj (QGraphicsItem *parent,TreeItem *ti):OrnamentedObj (parent,ti)
21 {
22     //qDebug ()<< "Const BranchObj  (s,ti) ti="<<ti;
23     treeItem=ti;
24     BranchItem *pi=(BranchItem*)(ti->parent());
25     if (pi && pi!=ti->getModel()->getRootItem() )
26         parObj=pi->getLMO();
27     else
28         parObj=NULL;
29     init();
30 }
31
32 BranchObj::~BranchObj ()
33 {
34     //qDebug()<< "Destr BranchObj  of "<<this;
35
36     // If I'm animated, I need to un-animate myself first
37     if (anim.isAnimated() )
38     {
39         anim.setAnimated (false);
40         VymModel *model=treeItem->getModel();
41         model->stopAnimation (this);
42     }
43
44     clear();
45 }
46
47 void BranchObj::init () 
48 {
49     if (parObj) absPos=parObj->getChildRefPos();
50 }
51
52 void BranchObj::copy (BranchObj* other)
53 {
54     OrnamentedObj::copy(other);
55
56     setVisibility (other->visible);
57
58     positionBBox();
59 }
60
61 void BranchObj::clear() 
62 {
63 }
64
65 void BranchObj::setParObjTmp(LinkableMapObj* dst, QPointF m, int off)   
66 {
67     // Temporary link to dst
68     // m is position of mouse pointer
69     // offset 0: default 1: below dst   -1 above dst  (if possible)
70
71     BranchItem *dsti=(BranchItem*)(dst->getTreeItem());
72
73     BranchItem *pi=(BranchItem*)(dsti->parent());
74     int pi_depth=pi->depth();
75     BranchObj* bodst=(BranchObj*)dst;
76
77     if (!tmpParent)
78     {
79         tmpParent=true;
80         parObjTmpBuf=parObj;
81     }
82
83     if (pi_depth<1) off=0;
84     if (off==0)
85         link2ParPos=false;
86     else
87         link2ParPos=true;
88     parObj=bodst;
89
90     setLinkStyle (dst->getDefLinkStyle (dsti));
91
92     // Move temporary to new position at destination
93     // Usually the positioning would be done by reposition(),
94     // but then also the destination branch would "Jump" around...
95     // Better just do it approximately
96     if (dsti->depth()==0)
97     {   // new parent is a mapcenter
98         Vector v= ( m - bodst->getChildRefPos());
99         v.normalize();
100         v.scale (150);
101         move2RelPos (v.toQPointF());
102     } else
103     {
104         qreal y;
105         if (off==0)
106         {
107             // Below is needed e.g. in a freshly loaded map,
108             // bboxTotal seems not to be correct yet
109             // relinking positions too far below then
110             calcBBoxSizeWithChildren();
111
112             // new parent is just a branch, link to it
113             bodst->calcBBoxSizeWithChildren();
114             QRectF t = bodst->getTotalBBox();
115             if (dsti->getLastBranch())
116                 // Move below children of destination
117                 y = t.y() + t.height() ;
118             else
119                 // Move left or right to destination
120                 y = t.y()  ;
121         } else
122         {
123             if (off < 0)
124                 // we want to link above dst
125                 y = bodst->y() - height() + 12;
126             else
127                 // we want to link below dst
128                 // Bottom of sel should be 5 pixels above
129                 // the bottom of the branch _below_ the target:
130                 // Don't try to find that branch, guess 12 pixels
131                 y = bodst->getChildRefPos().y()  -height() + 12;
132         }
133         if (bodst->getOrientation()==LinkableMapObj::LeftOfCenter)
134             move ( bodst->getChildRefPos().x() - linkwidth - bboxTotal.width(), y );    
135         else
136             move ( bodst->getChildRefPos().x() + linkwidth, y );
137     }
138
139     // updateLinkGeometry is called implicitly in move
140     requestReposition();
141 }
142
143 void BranchObj::unsetParObjTmp()
144 {
145     if (tmpParent)
146     {
147         tmpParent=false;
148         link2ParPos=false;
149         parObj=parObjTmpBuf;
150         parObjTmpBuf=NULL;
151         setLinkStyle (getDefLinkStyle(treeItem->parent() ) );
152         updateLinkGeometry();
153     }
154 }
155
156 void BranchObj::setVisibility(bool v, int toDepth) 
157 {
158     BranchItem *bi=(BranchItem*)treeItem;
159     if (bi->depth() <= toDepth)
160     {
161         frame->setVisibility(v);
162         heading->setVisibility(v);
163         systemFlags->setVisibility(v);
164         standardFlags->setVisibility(v);
165         LinkableMapObj::setVisibility (v);
166         int i;
167         for (i=0; i<treeItem->imageCount(); ++i)
168             treeItem->getImageObjNum(i)->setVisibility (v);
169         for (i=0; i<treeItem->xlinkCount(); ++i)
170             treeItem->getXLinkObjNum(i)->setVisibility ();
171
172         // Only change children, if I am not scrolled
173         if (! bi->isScrolled() && (bi->depth() < toDepth))
174         {
175             // Now go recursivly through all children
176             for (i=0; i<treeItem->branchCount(); ++i)
177                 treeItem->getBranchObjNum(i)->setVisibility (v,toDepth);
178         }
179     }
180 }   
181
182 void BranchObj::setVisibility(bool v)
183 {
184     setVisibility (v,MAX_DEPTH);
185 }
186
187
188 void BranchObj::setLinkColor ()
189 {
190     // Overloaded from LinkableMapObj
191     // BranchObj can use color of heading
192
193     VymModel *model=treeItem->getModel();
194     if (model)
195     {
196         if (model->getMapLinkColorHint()==HeadingColor)
197             LinkableMapObj::setLinkColor (heading->getColor() );
198         else
199             LinkableMapObj::setLinkColor ();
200     }
201 }
202
203 void BranchObj::positionContents()
204 {
205     OrnamentedObj::positionContents();
206     updateLinkGeometry();// required before positioning images
207     for (int i=0; i<treeItem->imageCount(); ++i)
208         treeItem->getImageObjNum(i)->reposition();
209 }
210
211 void BranchObj::move (double x, double y)
212 {
213     OrnamentedObj::move (x,y);
214 }
215
216 void BranchObj::move (QPointF p)
217 {
218     move (p.x(), p.y());
219 }
220
221 void BranchObj::moveBy (double x, double y)
222 {
223     OrnamentedObj::moveBy (x,y);
224     for (int i=0; i<treeItem->branchCount(); ++i)
225         treeItem->getBranchObjNum(i)->moveBy (x,y);
226     positionBBox();
227 }
228     
229 void BranchObj::moveBy (QPointF p)
230 {
231     moveBy (p.x(), p.y());
232 }
233
234 void BranchObj::positionBBox() // FIXME-3 consider dimensions of frame (thickness, geometry, padding...
235 {
236     QPointF ap=getAbsPos();
237     bbox.moveTopLeft (ap);
238     positionContents();   // this positions FIOs
239
240     //Update links to other branches
241     XLinkObj *xlo;
242     for (int i=0; i<treeItem->xlinkCount(); ++i)
243     {
244         xlo=treeItem->getXLinkObjNum(i);
245         if (xlo) xlo->updateXLink();
246     }
247 }
248
249 void BranchObj::calcBBoxSize()
250 {
251     QSizeF heading_r=heading->getSize();
252     qreal heading_w=(qreal) heading_r.width() ;
253     qreal heading_h=(qreal) heading_r.height() ;
254     QSizeF sysflags_r=systemFlags->getSize();
255     qreal sysflags_h=sysflags_r.height();
256     qreal sysflags_w=sysflags_r.width();
257     QSizeF stanflags_r=standardFlags->getSize();
258     qreal stanflags_h=stanflags_r.height();
259     qreal stanflags_w=stanflags_r.width();
260     qreal w;
261     qreal h;
262
263     // set width to sum of all widths
264     w=heading_w + sysflags_w + stanflags_w;
265
266     // set height to maximum needed height
267     h=max (sysflags_h,stanflags_h);
268     h=max (h,heading_h);
269
270     // Save the dimension of flags and heading
271     ornamentsBBox.setSize ( QSizeF(w,h));
272
273     // clickBox includes Flags and Heading
274     clickPoly=QPolygonF (ornamentsBBox);
275
276     // Floatimages 
277     QPointF rp;
278
279     topPad = botPad = leftPad = rightPad = 0;
280     bool incV=((BranchItem*)treeItem)->getIncludeImagesVer();
281     bool incH=((BranchItem*)treeItem)->getIncludeImagesHor();
282     if (incH || incV)
283     {
284         FloatImageObj *fio;
285         for (int i=0; i<treeItem->imageCount(); ++i )   
286         {
287             fio=treeItem->getImageObjNum(i);
288             rp=fio->getRelPos();
289             if (incV)
290             {
291                 qreal y;
292                 if (rp.y() > 0)
293                 {
294                     y = rp.y() + fio->height()/2 - ornamentsBBox.height()/2;
295                     botPad = max(botPad, y);
296                 } else
297                 {
298                     y=-rp.y() + fio->height()/2 - ornamentsBBox.height()/2;
299                     topPad=max(topPad, y);
300                 }
301             }       
302             if (incH)
303             {
304                 qreal x;
305                 if (rp.x() > 0)
306                 {
307                     x=rp.x() + fio->width()/2 - ornamentsBBox.width()/2;
308                     rightPad=max(rightPad, x);
309                 } else
310                 {
311                     x=-rp.x() + fio->width()/2 - ornamentsBBox.width()/2;
312                     leftPad=max(leftPad, x);
313                 }
314             }       
315         }   
316         h += topPad + botPad;
317         w += leftPad + rightPad;
318     }
319
320     // Frame thickness  
321     w += frame->getTotalPadding() * 2;
322     h += frame->getTotalPadding() * 2;
323     
324     // Finally set size
325     bbox.setSize (QSizeF (w,h));
326     //if (debug) qDebug()<<"BO: calcBBox "<<treeItem->getHeading()<<" bbox="<<bbox;
327 }
328
329 void BranchObj::setDockPos()
330 {
331     floatRefPos=ornamentsBBox.center();
332
333     if (treeItem->getType() == TreeItem::MapCenter)
334     {
335         // set childRefPos to middle of MapCenterObj
336         QRectF r=clickPoly.boundingRect();
337         childRefPos.setX( r.topLeft().x() + r.width()/2 );
338         childRefPos.setY( r.topLeft().y() + r.height()/2 );
339         parPos=childRefPos;
340         for (int i=0; i<treeItem->branchCount(); ++i)
341             treeItem->getBranchObjNum(i)->updateLinkGeometry();
342     } else
343     {
344         if (orientation == LinkableMapObj::LeftOfCenter )
345         {
346             // Left of center
347             if ( ((BranchItem*)treeItem)->getFrameIncludeChildren() )
348             {
349                 childRefPos=QPointF (ornamentsBBox.bottomLeft().x() - leftPad,  bottomlineY);
350                 parPos=QPointF   (bboxTotal.bottomRight().x()-frame->getPadding()/2, bottomlineY);
351             } else
352             {
353                 childRefPos=QPointF (ornamentsBBox.bottomLeft().x() - frame->getPadding(),  bottomlineY);
354                 parPos=QPointF   (ornamentsBBox.bottomRight().x(), bottomlineY);
355             }
356         } else
357         {
358             // Right of center
359             if ( ((BranchItem*)treeItem)->getFrameIncludeChildren() )
360             {
361                 childRefPos=QPointF(ornamentsBBox.bottomRight().x() + rightPad , bottomlineY);
362                 parPos=QPointF ( bboxTotal.bottomLeft().x()+frame->getPadding()/2,  bottomlineY);
363             } else
364             {
365                 childRefPos=QPointF(ornamentsBBox.bottomRight().x() + frame->getPadding(), bottomlineY);
366                 parPos=QPointF ( ornamentsBBox.bottomLeft().x(),  bottomlineY);
367             }
368         }
369     }
370 }
371
372 void BranchObj::updateData()
373 {
374     if (!treeItem)
375     {
376         qWarning ("BranchObj::udpateHeading treeItem==NULL");
377         return;
378     }
379     QString s = treeItem->getHeadingText();
380     if ( s!=heading->text()) heading->setText (s);
381
382     QStringList TIactiveFlags=treeItem->activeStandardFlagNames();
383
384     // Add missing standard flags active in TreeItem
385     for (int i=0;i<=TIactiveFlags.size()-1;i++)
386     {
387         if (!standardFlags->isActive (TIactiveFlags.at(i) ))
388         {
389             Flag *f=standardFlagsMaster->getFlag(TIactiveFlags.at(i));
390             if (f) standardFlags->activate (f);
391         }
392     }
393     // Remove standard flags no longer active in TreeItem
394     QStringList BOactiveFlags=standardFlags->activeFlagNames();
395     for (int i=0;i<BOactiveFlags.size();++i)
396         if (!TIactiveFlags.contains (BOactiveFlags.at(i)))
397             standardFlags->deactivate (BOactiveFlags.at(i));
398
399     // Add missing system flags active in TreeItem
400     TIactiveFlags=treeItem->activeSystemFlagNames();
401     for (int i=0;i<TIactiveFlags.size();++i)
402     {
403         if (!systemFlags->isActive (TIactiveFlags.at(i) ))
404         {
405             Flag *f=systemFlagsMaster->getFlag(TIactiveFlags.at(i));
406             if (f) systemFlags->activate (f);
407         }
408     }
409     // Remove system flags no longer active in TreeItem
410     BOactiveFlags=systemFlags->activeFlagNames();
411     for (int i=0;i<BOactiveFlags.size();++i)
412     {
413         if (!TIactiveFlags.contains (BOactiveFlags.at(i)))
414             systemFlags->deactivate (BOactiveFlags.at(i));
415     }
416     calcBBoxSize();
417 }
418
419 void BranchObj::setDefAttr (BranchModification mod, bool keepFrame)
420 {
421     QFont font=treeItem->getModel()->getMapDefaultFont();
422     qreal fontsize=font.pointSizeF();
423     switch (treeItem->depth())
424     {
425     case 0:
426         break;
427     case 1:
428         fontsize=fontsize-2;
429         break;
430     case 2:
431         fontsize=fontsize-4;
432         break;
433     default:
434         fontsize=fontsize-6;
435         break;
436     }
437     setLinkStyle(getDefLinkStyle(treeItem->parent() ));
438     setLinkColor ();
439     font.setPointSizeF (fontsize);
440     heading->setFont(font );
441
442     if (mod==NewBranch && !keepFrame)
443     {
444         if (treeItem->depth()==0)
445             setFrameType (FrameObj::Rectangle);
446         else
447             setFrameType (FrameObj::NoFrame);
448     }
449     if (mod==NewBranch)
450         setColor (treeItem->getHeadingColor() );
451     else
452     {
453         // Relinked mapcenters
454         if (!keepFrame && getFrameType()!=FrameObj::NoFrame)
455             setFrameType (FrameObj::NoFrame);
456
457         // Also set styles for children
458         for (int i=0; i<treeItem->branchCount(); ++i)
459             treeItem->getBranchObjNum(i)->setDefAttr(MovedBranch, keepFrame);
460     }
461     calcBBoxSize();
462 }
463
464 void BranchObj::alignRelativeTo (QPointF ref,bool alignSelf)
465 {
466     // Define some heights
467     qreal th = bboxTotal.height();  
468     qreal ch=0; // Sum of childrens heights
469     for (int i=0; i<treeItem->branchCount(); ++i)
470         ch+=treeItem->getBranchObjNum(i)->getTotalBBox().height();
471
472     int depth = 0;
473     BranchItem::LayoutHint layoutHint = BranchItem::AutoPositioning;
474     if (parObj)
475     {
476         TreeItem *pi = parObj->getTreeItem();
477         depth = 1 + pi->depth();
478         layoutHint = ((BranchItem*)treeItem)->parentBranch()->getChildrenLayout();
479     }
480     
481     // set useRelPos, depending on layout
482     if (depth > 1)
483     {
484         if (layoutHint == BranchItem::FreePositioning)
485         {
486             if (!useRelPos) 
487             {
488                 useRelPos = true;
489                 // if we used relPos before, set known positions
490                 // "known" means any position != (0,0)
491                 if (relPos == QPointF(0,0) )
492                     // use current position to get relPos()
493                     setRelPos();
494             }
495         }
496         else
497             useRelPos = false;
498     }
499
500 // TODO testing
501 /*
502     if (debug)
503     {
504         QString o;
505         switch (orientation)
506         {       
507             case UndefinedOrientation: o = "UndefOrientation"; break;
508             case LeftOfCenter: o = "LeftOfCenter"; break;
509             case RightOfCenter: o = "RightOfCenter"; break;
510         }
511             
512         QString h=QString (depth+1,' ');
513         h += treeItem->getHeadingPlain();
514         h += QString (25,' ');
515         h.truncate (25);
516         QPointF pp; 
517         if (parObj) pp = parObj->getChildRefPos();
518         qDebug() << "BO::alignRelTo for "<<h
519     //    qDebug() << "    d="<<depth;
520     //    qDebug() <<"   ref="<<ref;
521     //    qDebug() <<"    th="<<th;
522     //    qDebug() <<"    ch="<<ch;
523     //    if (ch < th) qDebug()<<"   ch<th !";
524     //    qDebug() <<"  parO="<<parObj;
525         //qDebug() <<   "  bbox.tL="<<bboxTotal.topLeft();
526         << "  useRelPos=" << useRelPos 
527         << " layoutHint= " << layoutHint
528     //    qDebug() <<"absPos="<<absPos
529         << "  relPos="<<relPos
530     //  << "  parPos="<<pp
531     //  << "  bbox="<<bbox
532         << "  orient="<<o<<" "<<orientation;
533     //  << "  alignSelf="<<alignSelf
534     //  << "  scrolled="<<((BranchItem*)treeItem)->isScrolled()
535     //  << "  pad="<<topPad<<","<<botPad<<","<<leftPad<<","<<rightPad
536     //  << "  hidden="<<hidden
537     //  << "  th="<<th
538         ;
539     }
540    */
541
542     setOrientation();
543
544     // Align myself
545     if (depth==0)
546         move (getAbsPos()); // Trigger update of frames etc.
547     else if(depth==1)
548         move2RelPos (getRelPos() );
549     else if (depth>1)
550     {
551         if (layoutHint == BranchItem::FreePositioning)
552             move2RelPos (getRelPos() );
553         else
554         {
555             if (anim.isAnimated())
556                 move2RelPos(anim);
557             else
558             {
559                 if (alignSelf)
560                     switch (orientation)
561                     {
562                     case LinkableMapObj::LeftOfCenter:
563                         move (ref.x() - bbox.width(), ref.y() + (th-bbox.height())/2 );
564                         break;
565                     case LinkableMapObj::RightOfCenter:
566                         move (ref.x() , ref.y() + (th - bbox.height())/2 );
567                         break;
568                     default:
569                         qWarning ("LMO::alignRelativeTo: oops, no orientation given for BO...");
570                         break;
571                     }
572             }
573         }
574     }
575
576     // Without ancestors I am done
577     if ( ((BranchItem*)treeItem)->isScrolled() ) return;
578
579     // Set reference point for alignment of children
580     QPointF ref2;
581     if (orientation==LinkableMapObj::LeftOfCenter)
582         ref2.setX(childRefPos.x() - linkwidth);
583     else
584         ref2.setX(childRefPos.x() + linkwidth);
585
586     if (depth==1)
587         ref2.setY (absPos.y() + (bbox.height() - ch)/2);
588     else
589     {
590         if (ch > th)
591             ref2.setY (ref.y() + frame->getPadding());
592         else
593             // Parent is bigger than all of childs, center childs vertically
594             ref2.setY (ref.y() + (th - ch)/2 );
595     }
596
597     // Align the branch children depending on reference point
598     for (int i=0; i<treeItem->branchCount(); ++i)
599     {
600         if (!treeItem->getBranchNum(i)->isHidden())
601         {
602             treeItem->getBranchObjNum(i)->alignRelativeTo (ref2,true);
603
604             // append next branch below current one
605             ref2.setY(ref2.y() + treeItem->getBranchObjNum(i)->getTotalBBox().height() );
606         }
607     }
608 }
609
610 void BranchObj::reposition()
611 {   
612 /* TODO testing only
613     if (debug) 
614     {
615         if (!treeItem->getHeading().isEmpty())
616             qDebug()<< "  BO::reposition  a) d="<<treeItem->depth()<<" "<<treeItem->getHeading();
617         else    
618             qDebug()<< "  BO::reposition  a) d="<<treeItem->depth()<<" ???";
619     }
620 */      
621
622     if (treeItem->depth()==0)
623         // only calculate the sizes once. If the deepest LMO 
624         // changes its height,
625         // all upper LMOs have to change, too.
626         calcBBoxSizeWithChildren(); 
627
628     alignRelativeTo ( QPointF (absPos.x(),
629         absPos.y()-(bboxTotal.height()-bbox.height())/2) );     
630 }
631
632 void BranchObj::unsetAllRepositionRequests()
633 {
634     repositionRequest=false;
635     for (int i=0; i<treeItem->branchCount(); ++i)
636         treeItem->getBranchObjNum(i)->unsetAllRepositionRequests();
637 }
638
639 QRectF BranchObj::getTotalBBox()
640 {
641     return bboxTotal;
642 }
643
644 ConvexPolygon BranchObj::getBoundingPolygon()   
645 {
646     if (treeItem->branchCount()==0 || treeItem->depth()==0)
647     {
648         if (boundingPolygon)
649             boundingPolygon->setPolygon (MapObj::getBoundingPolygon() );
650         return MapObj::getBoundingPolygon();
651     }
652
653     QPolygonF p;
654     p<<bboxTotal.topLeft();
655     p<<bboxTotal.topRight();
656     p<<bboxTotal.bottomRight();
657     p<<bboxTotal.bottomLeft();
658     if (boundingPolygon) boundingPolygon->setPolygon (p );
659     return p;
660 }
661
662 void BranchObj::calcBBoxSizeWithChildren()  
663 {   
664     // if branch is scrolled, ignore children, but still consider floatimages
665     BranchItem *bi=(BranchItem*)treeItem;
666     if ( bi->isScrolled() )
667     {
668         bboxTotal.setWidth (bbox.width());
669         bboxTotal.setHeight(bbox.height());
670         return;
671     }
672     
673     if (bi->isHidden())
674     {
675         bboxTotal.setWidth (0);
676         bboxTotal.setHeight(0);
677         return;
678     }
679     
680     QRectF r(0,0,0,0);
681     QRectF br;
682
683     // Now calculate
684     // sum of heights
685     // maximum of widths
686     // minimum of y
687     for (int i=0; i<treeItem->branchCount(); i++)
688     {
689         if (!bi->getBranchNum(i)->isHidden())
690         {
691             BranchObj *bo=bi->getBranchObjNum(i);
692             bo->calcBBoxSizeWithChildren();
693             br=bo->getTotalBBox();
694             r.setWidth( max (br.width(), r.width() ));
695             r.setHeight(br.height() + r.height() );
696         }
697     }
698
699     // Add myself and also
700     // add width of link to sum if necessary
701     if (bi->branchCount()<1)
702         bboxTotal.setWidth (bbox.width() + r.width()  );
703     else
704         bboxTotal.setWidth (bbox.width() + r.width() + linkwidth );
705     
706     // bbox already contains frame->padding()*2
707     bboxTotal.setHeight(max (r.height() + frame->getPadding()*2,  bbox.height()) );
708 }
709
710 void BranchObj::setAnimation(const AnimPoint &ap)
711 {
712     anim=ap;
713 }
714
715 void BranchObj::stopAnimation()
716 {
717     anim.stop();
718     if (useRelPos)
719         setRelPos (anim);
720     else
721         move (anim);
722 }
723
724 bool BranchObj::animate()
725 {
726     anim.animate ();
727     if ( anim.isAnimated() )
728     {
729         if (useRelPos)
730             setRelPos (anim);
731         else
732             move (anim);
733         return true;
734     }
735     return false;
736 }
737