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