]> git.sven.stormbind.net Git - sven/vym.git/blobdiff - src/branchobj.cpp
New upstream version 2.9.22
[sven/vym.git] / src / branchobj.cpp
diff --git a/src/branchobj.cpp b/src/branchobj.cpp
new file mode 100644 (file)
index 0000000..ffae47b
--- /dev/null
@@ -0,0 +1,672 @@
+#include <QDebug>
+
+#include "branchobj.h"
+
+#include "attributeitem.h"
+#include "branchitem.h"
+#include "geometry.h"
+#include "mainwindow.h"
+#include "mapeditor.h"
+#include "misc.h"
+
+extern FlagRowMaster *standardFlagsMaster;
+extern FlagRowMaster *userFlagsMaster;
+extern FlagRowMaster *systemFlagsMaster;
+extern bool debug;
+extern bool usingDarkTheme;
+
+/////////////////////////////////////////////////////////////////
+// BranchObj
+/////////////////////////////////////////////////////////////////
+
+BranchObj::BranchObj(QGraphicsItem *parent, TreeItem *ti)
+    : OrnamentedObj(parent, ti)
+{
+    // qDebug ()<< "Const BranchObj  (s,ti) ti="<<ti;
+    treeItem = ti;
+    BranchItem *pi = (BranchItem *)(ti->parent());
+    if (pi && pi != ti->getModel()->getRootItem())
+        parObj = pi->getLMO();
+    else
+        parObj = NULL;
+    init();
+}
+
+BranchObj::~BranchObj()
+{
+    // qDebug()<< "Destr BranchObj  of "<<this;
+
+    // If I'm animated, I need to un-animate myself first
+    if (anim.isAnimated()) {
+        anim.setAnimated(false);
+        VymModel *model = treeItem->getModel();
+        model->stopAnimation(this);
+    }
+
+    clear();
+}
+
+void BranchObj::init()
+{
+    if (parObj)
+        absPos = parObj->getChildRefPos();
+}
+
+void BranchObj::copy(BranchObj *other)
+{
+    OrnamentedObj::copy(other);
+
+    setVisibility(other->visible);
+
+    positionBBox();
+}
+
+void BranchObj::clear() {}
+
+void BranchObj::setParObjTmp(LinkableMapObj *dst, QPointF m, int off)
+{
+    // Temporary link to dst
+    // m is position of mouse pointer
+    // offset 0: default 1: below dst   -1 above dst  (if possible)
+
+    BranchItem *dsti = (BranchItem *)(dst->getTreeItem());
+
+    BranchItem *pi = (BranchItem *)(dsti->parent());
+    int pi_depth = pi->depth();
+    BranchObj *bodst = (BranchObj *)dst;
+
+    if (!tmpParent) {
+        tmpParent = true;
+        parObjTmpBuf = parObj;
+    }
+
+    if (pi_depth < 1)
+        off = 0;
+    if (off == 0)
+        link2ParPos = false;
+    else
+        link2ParPos = true;
+    parObj = bodst;
+
+    setLinkStyle(dst->getDefLinkStyle(dsti));
+
+    // Move temporary to new position at destination
+    // Usually the positioning would be done by reposition(),
+    // but then also the destination branch would "Jump" around...
+    // Better just do it approximately
+    if (dsti->depth() == 0) { // new parent is a mapcenter
+        Vector v = (m - bodst->getChildRefPos());
+        v.normalize();
+        v.scale(150);
+        move2RelPos(v.toQPointF());
+    }
+    else {
+        qreal y;
+        if (off == 0) {
+            // Below is needed e.g. in a freshly loaded map,
+            // bboxTotal seems not to be correct yet
+            // relinking positions too far below then
+            calcBBoxSizeWithChildren();
+
+            // new parent is just a branch, link to it
+            bodst->calcBBoxSizeWithChildren();
+            QRectF t = bodst->getTotalBBox();
+            if (dsti->getLastBranch())
+                // Move below children of destination
+                y = t.y() + t.height();
+            else
+                // Move left or right to destination
+                y = t.y();
+        }
+        else {
+            if (off < 0)
+                // we want to link above dst
+                y = bodst->y() - height() + 12;
+            else
+                // we want to link below dst
+                // Bottom of sel should be 5 pixels above
+                // the bottom of the branch _below_ the target:
+                // Don't try to find that branch, guess 12 pixels
+                y = bodst->getChildRefPos().y() - height() + 12;
+        }
+        if (bodst->getOrientation() == LinkableMapObj::LeftOfCenter)
+            move(bodst->getChildRefPos().x() - linkwidth - bboxTotal.width(),
+                 y);
+        else
+            move(bodst->getChildRefPos().x() + linkwidth, y);
+    }
+
+    // updateLinkGeometry is called implicitly in move
+    requestReposition();
+}
+
+void BranchObj::unsetParObjTmp()
+{
+    if (tmpParent) {
+        tmpParent = false;
+        link2ParPos = false;
+        parObj = parObjTmpBuf;
+        parObjTmpBuf = NULL;
+        setLinkStyle(getDefLinkStyle(treeItem->parent()));
+        updateLinkGeometry();
+    }
+}
+
+void BranchObj::setVisibility(bool v, int toDepth)
+{
+    BranchItem *bi = (BranchItem *)treeItem;
+    if (bi->depth() <= toDepth) {
+        frame->setVisibility(v);
+        heading->setVisibility(v);
+        systemFlagRowObj->setVisibility(v);
+        standardFlagRowObj->setVisibility(v);
+        LinkableMapObj::setVisibility(v);
+        int i;
+        for (i = 0; i < treeItem->imageCount(); ++i)
+            treeItem->getImageObjNum(i)->setVisibility(v);
+        for (i = 0; i < treeItem->xlinkCount(); ++i)
+            treeItem->getXLinkObjNum(i)->setVisibility();
+
+        // Only change children, if I am not scrolled
+        if (!bi->isScrolled() && (bi->depth() < toDepth)) {
+            // Now go recursivly through all children
+            for (i = 0; i < treeItem->branchCount(); ++i)
+                treeItem->getBranchObjNum(i)->setVisibility(v, toDepth);
+        }
+    }
+}
+
+void BranchObj::setVisibility(bool v) { setVisibility(v, MAX_DEPTH); }
+
+void BranchObj::positionContents()
+{
+    OrnamentedObj::positionContents();
+    updateLinkGeometry(); // required before positioning images
+    for (int i = 0; i < treeItem->imageCount(); ++i)
+        treeItem->getImageObjNum(i)->reposition();
+}
+
+void BranchObj::move(double x, double y) { OrnamentedObj::move(x, y); }
+
+void BranchObj::move(QPointF p) { move(p.x(), p.y()); }
+
+void BranchObj::moveBy(double x, double y)
+{
+    OrnamentedObj::moveBy(x, y);
+    for (int i = 0; i < treeItem->branchCount(); ++i)
+        treeItem->getBranchObjNum(i)->moveBy(x, y);
+    positionBBox();
+}
+
+void BranchObj::moveBy(QPointF p) { moveBy(p.x(), p.y()); }
+
+void BranchObj::positionBBox() // FIXME-3 consider dimensions of frame
+                               // (thickness, geometry, padding...
+{
+    QPointF ap = getAbsPos();
+    bbox.moveTopLeft(ap);
+    positionContents(); // this positions FIOs
+
+    // Update links to other branches
+    XLinkObj *xlo;
+    for (int i = 0; i < treeItem->xlinkCount(); ++i) {
+        xlo = treeItem->getXLinkObjNum(i);
+        if (xlo)
+            xlo->updateXLink();
+    }
+}
+
+void BranchObj::calcBBoxSize()
+{
+    QSizeF heading_r = heading->getSize();
+    qreal heading_w = (qreal)heading_r.width();
+    qreal heading_h = (qreal)heading_r.height();
+    QSizeF sysflags_r = systemFlagRowObj->getSize();
+    qreal sysflags_h = sysflags_r.height();
+    qreal sysflags_w = sysflags_r.width();
+    QSizeF stanflags_r = standardFlagRowObj->getSize();
+    qreal stanflags_h = stanflags_r.height();
+    qreal stanflags_w = stanflags_r.width();
+    qreal w;
+    qreal h;
+
+    // set width to sum of all widths
+    w = heading_w + sysflags_w + stanflags_w;
+
+    // set height to maximum needed height
+    h = max(sysflags_h, stanflags_h);
+    h = max(h, heading_h);
+
+    // Save the dimension of flags and heading
+    ornamentsBBox.setSize(QSizeF(w, h));
+
+    // clickBox includes Flags and Heading
+    clickPoly = QPolygonF(ornamentsBBox);
+
+    // Floatimages
+    QPointF rp;
+
+    topPad = botPad = leftPad = rightPad = 0;
+    bool incV = ((BranchItem *)treeItem)->getIncludeImagesVer();
+    bool incH = ((BranchItem *)treeItem)->getIncludeImagesHor();
+    if (incH || incV) {
+        FloatImageObj *fio;
+        for (int i = 0; i < treeItem->imageCount(); ++i) {
+            fio = treeItem->getImageObjNum(i);
+            rp = fio->getRelPos();
+            if (incV) {
+                qreal y;
+                if (rp.y() > 0) {
+                    y = rp.y() + fio->height() / 2 - ornamentsBBox.height() / 2;
+                    botPad = max(botPad, y);
+                }
+                else {
+                    y = -rp.y() + fio->height() / 2 -
+                        ornamentsBBox.height() / 2;
+                    topPad = max(topPad, y);
+                }
+            }
+            if (incH) {
+                qreal x;
+                if (rp.x() > 0) {
+                    x = rp.x() + fio->width() / 2 - ornamentsBBox.width() / 2;
+                    rightPad = max(rightPad, x);
+                }
+                else {
+                    x = -rp.x() + fio->width() / 2 - ornamentsBBox.width() / 2;
+                    leftPad = max(leftPad, x);
+                }
+            }
+        }
+        h += topPad + botPad;
+        w += leftPad + rightPad;
+    }
+
+    // Frame thickness
+    w += frame->getTotalPadding() * 2;
+    h += frame->getTotalPadding() * 2;
+
+    // Finally set size
+    bbox.setSize(QSizeF(w, h));
+    // if (debug) qDebug()<<"BO: calcBBox "<<treeItem->getHeading()<<"
+    // bbox="<<bbox;
+}
+
+void BranchObj::setDockPos()
+{
+    floatRefPos = ornamentsBBox.center();
+
+    if (treeItem->getType() == TreeItem::MapCenter) {
+        // set childRefPos to middle of MapCenterObj
+        QRectF r = clickPoly.boundingRect();
+        childRefPos.setX(r.topLeft().x() + r.width() / 2);
+        childRefPos.setY(r.topLeft().y() + r.height() / 2);
+        parPos = childRefPos;
+        for (int i = 0; i < treeItem->branchCount(); ++i)
+            treeItem->getBranchObjNum(i)->updateLinkGeometry();
+    }
+    else {
+        if (orientation == LinkableMapObj::LeftOfCenter) {
+            // Left of center
+            if (((BranchItem *)treeItem)->getFrameIncludeChildren()) {
+                childRefPos = QPointF(ornamentsBBox.bottomLeft().x() - leftPad,
+                                      bottomlineY);
+                parPos = QPointF(bboxTotal.bottomRight().x() -
+                                     frame->getPadding() / 2,
+                                 bottomlineY);
+            }
+            else {
+                childRefPos = QPointF(ornamentsBBox.bottomLeft().x() -
+                                          frame->getPadding(),
+                                      bottomlineY);
+                parPos = QPointF(ornamentsBBox.bottomRight().x(), bottomlineY);
+            }
+        }
+        else {
+            // Right of center
+            if (((BranchItem *)treeItem)->getFrameIncludeChildren()) {
+                childRefPos = QPointF(
+                    ornamentsBBox.bottomRight().x() + rightPad, bottomlineY);
+                parPos = QPointF(bboxTotal.bottomLeft().x() +
+                                     frame->getPadding() / 2,
+                                 bottomlineY);
+            }
+            else {
+                childRefPos = QPointF(ornamentsBBox.bottomRight().x() +
+                                          frame->getPadding(),
+                                      bottomlineY);
+                parPos = QPointF(ornamentsBBox.bottomLeft().x(), bottomlineY);
+            }
+        }
+    }
+}
+
+void BranchObj::updateVisuals()
+{
+    if (!treeItem) {
+        qWarning("BranchObj::udpateHeading treeItem==NULL");
+        return;
+    }
+    QString s = treeItem->getHeadingText();
+    if (s != heading->text())
+        heading->setText(s);
+
+    // Update standard flags active in TreeItem
+    QList<QUuid> TIactiveFlagUids = treeItem->activeFlagUids();
+    standardFlagRowObj->updateActiveFlagObjs(
+        TIactiveFlagUids, standardFlagsMaster, userFlagsMaster);
+
+    // Add missing system flags active in TreeItem
+    TIactiveFlagUids = treeItem->activeSystemFlagUids();
+    systemFlagRowObj->updateActiveFlagObjs(TIactiveFlagUids, systemFlagsMaster);
+
+    calcBBoxSize();
+}
+
+void BranchObj::setDefAttr(BranchModification mod, bool keepFrame)
+{
+
+    // Note: not needed in 3.x.0 versions, 
+    // where MapDesign will be used
+    QFont font = treeItem->getModel()->getMapDefaultFont();
+    qreal fontsize = font.pointSizeF();
+    switch (treeItem->depth()) {
+    case 0:
+        break;
+    case 1:
+        fontsize = fontsize - 2;
+        break;
+    case 2:
+        fontsize = fontsize - 4;
+        break;
+    default:
+        fontsize = fontsize - 6;
+        break;
+    }
+    setLinkStyle(getDefLinkStyle(treeItem->parent()));
+    setLinkColor();
+    font.setPointSizeF(fontsize);
+    heading->setFont(font);
+
+    if (mod == NewBranch && !keepFrame) {
+        if (treeItem->depth() == 0) {
+            setFrameType(FrameObj::RoundedRectangle);
+            setFrameBorderWidth(2);
+            if (usingDarkTheme) {
+                setFramePenColor(QColor(Qt::white));
+                setFrameBrushColor(QColor(85, 85, 127));
+                treeItem->setHeadingColor(QColor(Qt::white));
+            } else {
+                setFramePenColor(QColor(Qt::black));
+                setFrameBrushColor(QColor(Qt::white));
+            }
+        } else
+            setFrameType(FrameObj::NoFrame);
+    }
+    if (mod == NewBranch)
+        setColor(treeItem->getHeadingColor());
+    else {
+        // Relinked mapcenters
+        if (!keepFrame && getFrameType() != FrameObj::NoFrame)
+            setFrameType(FrameObj::NoFrame);
+
+        // Also set styles for children
+        for (int i = 0; i < treeItem->branchCount(); ++i)
+            treeItem->getBranchObjNum(i)->setDefAttr(MovedBranch, keepFrame);
+    }
+    calcBBoxSize();
+}
+
+void BranchObj::alignRelativeTo(QPointF ref, bool alignSelf)
+{
+    // Define some heights
+    qreal th = bboxTotal.height();
+    qreal ch = 0; // Sum of childrens heights
+    for (int i = 0; i < treeItem->branchCount(); ++i)
+        ch += treeItem->getBranchObjNum(i)->getTotalBBox().height();
+
+    int depth = 0;
+    BranchItem::LayoutHint layoutHint = BranchItem::AutoPositioning;
+    if (parObj) {
+        TreeItem *pi = parObj->getTreeItem();
+        depth = 1 + pi->depth();
+        layoutHint =
+            ((BranchItem *)treeItem)->parentBranch()->getChildrenLayout();
+    }
+
+    // set useRelPos, depending on layout
+    if (depth > 1) {
+        if (layoutHint == BranchItem::FreePositioning) {
+            if (!useRelPos) {
+                useRelPos = true;
+                // if we used relPos before, set known positions
+                // "known" means any position != (0,0)
+                if (relPos == QPointF(0, 0))
+                    // use current position to get relPos()
+                    setRelPos();
+            }
+        }
+        else
+            useRelPos = false;
+    }
+
+    // TODO testing
+    /*
+        if (debug)
+        {
+            QString o;
+            switch (orientation)
+            {
+                case UndefinedOrientation: o = "UndefOrientation"; break;
+                case LeftOfCenter: o = "LeftOfCenter"; break;
+                case RightOfCenter: o = "RightOfCenter"; break;
+            }
+
+            QString h=QString (depth+1,' ');
+            h += treeItem->getHeadingPlain();
+            h += QString (25,' ');
+            h.truncate (25);
+            QPointF pp;
+            if (parObj) pp = parObj->getChildRefPos();
+            qDebug() << "BO::alignRelTo for "<<h
+        //    qDebug() << "    d="<<depth;
+        //    qDebug() <<"   ref="<<ref;
+        //    qDebug() <<"    th="<<th;
+        //    qDebug() <<"    ch="<<ch;
+        //    if (ch < th) qDebug()<<"   ch<th !";
+        //    qDebug() <<"  parO="<<parObj;
+            //qDebug() <<   "  bbox.tL="<<bboxTotal.topLeft();
+            << "  useRelPos=" << useRelPos
+            << " layoutHint= " << layoutHint
+        //    qDebug() <<"absPos="<<absPos
+            << "  relPos="<<relPos
+        //     << "  parPos="<<pp
+        //     << "  bbox="<<bbox
+            << "  orient="<<o<<" "<<orientation;
+        //     << "  alignSelf="<<alignSelf
+        //     << "  scrolled="<<((BranchItem*)treeItem)->isScrolled()
+        //     << "  pad="<<topPad<<","<<botPad<<","<<leftPad<<","<<rightPad
+        //     << "  hidden="<<hidden
+        //     << "  th="<<th
+            ;
+        }
+       */
+
+    setOrientation();
+
+    // Align myself
+    if (depth == 0)
+        move(getAbsPos()); // Trigger update of frames etc.
+    else if (depth == 1)
+        move2RelPos(getRelPos());
+    else if (depth > 1) {
+        if (layoutHint == BranchItem::FreePositioning)
+            move2RelPos(getRelPos());
+        else {
+            if (anim.isAnimated())
+                move2RelPos(anim);
+            else {
+                if (alignSelf)
+                    switch (orientation) {
+                    case LinkableMapObj::LeftOfCenter:
+                        move(ref.x() - bbox.width(),
+                             ref.y() + (th - bbox.height()) / 2);
+                        break;
+                    case LinkableMapObj::RightOfCenter:
+                        move(ref.x(), ref.y() + (th - bbox.height()) / 2);
+                        break;
+                    default:
+                        qWarning("LMO::alignRelativeTo: oops, no orientation "
+                                 "given for BO...");
+                        break;
+                    }
+            }
+        }
+    }
+
+    // Without ancestors I am done
+    if (((BranchItem *)treeItem)->isScrolled())
+        return;
+
+    // Set reference point for alignment of children
+    QPointF ref2;
+    if (orientation == LinkableMapObj::LeftOfCenter)
+        ref2.setX(childRefPos.x() - linkwidth);
+    else
+        ref2.setX(childRefPos.x() + linkwidth);
+
+    if (depth == 1)
+        ref2.setY(absPos.y() + (bbox.height() - ch) / 2);
+    else {
+        if (ch > th)
+            ref2.setY(ref.y() + frame->getPadding());
+        else
+            // Parent is bigger than all of childs, center childs vertically
+            ref2.setY(ref.y() + (th - ch) / 2);
+    }
+
+    // Align the branch children depending on reference point
+    for (int i = 0; i < treeItem->branchCount(); ++i) {
+        if (!treeItem->getBranchNum(i)->isHidden()) {
+            treeItem->getBranchObjNum(i)->alignRelativeTo(ref2, true);
+
+            // append next branch below current one
+            ref2.setY(ref2.y() +
+                      treeItem->getBranchObjNum(i)->getTotalBBox().height());
+        }
+    }
+}
+
+void BranchObj::reposition()
+{
+    /* TODO testing only
+        if (debug)
+        {
+            if (!treeItem->getHeading().isEmpty())
+                qDebug()<< "  BO::reposition  a) d="<<treeItem->depth()<<"
+       "<<treeItem->getHeading(); else qDebug()<< "  BO::reposition  a)
+       d="<<treeItem->depth()<<" ???";
+        }
+    */
+
+    if (treeItem->depth() == 0)
+        // only calculate the sizes once. If the deepest LMO
+        // changes its height,
+        // all upper LMOs have to change, too.
+        calcBBoxSizeWithChildren();
+
+    alignRelativeTo(QPointF(
+        absPos.x(), absPos.y() - (bboxTotal.height() - bbox.height()) / 2));
+}
+
+void BranchObj::unsetAllRepositionRequests()
+{
+    repositionRequest = false;
+    for (int i = 0; i < treeItem->branchCount(); ++i)
+        treeItem->getBranchObjNum(i)->unsetAllRepositionRequests();
+}
+
+QRectF BranchObj::getTotalBBox() { return bboxTotal; }
+
+ConvexPolygon BranchObj::getBoundingPolygon()
+{
+    if (treeItem->branchCount() == 0 || treeItem->depth() == 0) {
+        return MapObj::getBoundingPolygon();
+    }
+
+    QPolygonF p;
+    p << bboxTotal.topLeft();
+    p << bboxTotal.topRight();
+    p << bboxTotal.bottomRight();
+    p << bboxTotal.bottomLeft();
+    return p;
+}
+
+void BranchObj::calcBBoxSizeWithChildren()
+{
+    // if branch is scrolled, ignore children, but still consider floatimages
+    BranchItem *bi = (BranchItem *)treeItem;
+    if (bi->isScrolled()) {
+        bboxTotal.setWidth(bbox.width());
+        bboxTotal.setHeight(bbox.height());
+        return;
+    }
+
+    if (bi->isHidden()) {
+        bboxTotal.setWidth(0);
+        bboxTotal.setHeight(0);
+        return;
+    }
+
+    QRectF r(0, 0, 0, 0);
+    QRectF br;
+
+    // Now calculate
+    // sum of heights
+    // maximum of widths
+    // minimum of y
+    for (int i = 0; i < treeItem->branchCount(); i++) {
+        if (!bi->getBranchNum(i)->isHidden()) {
+            BranchObj *bo = bi->getBranchObjNum(i);
+            bo->calcBBoxSizeWithChildren();
+            br = bo->getTotalBBox();
+            r.setWidth(max(br.width(), r.width()));
+            r.setHeight(br.height() + r.height());
+        }
+    }
+
+    // Add myself and also
+    // add width of link to sum if necessary
+    if (bi->branchCount() < 1)
+        bboxTotal.setWidth(bbox.width() + r.width());
+    else
+        bboxTotal.setWidth(bbox.width() + r.width() + linkwidth);
+
+    // bbox already contains frame->padding()*2
+    bboxTotal.setHeight(
+        max(r.height() + frame->getPadding() * 2, bbox.height()));
+}
+
+void BranchObj::setAnimation(const AnimPoint &ap) { anim = ap; }
+
+void BranchObj::stopAnimation()
+{
+    anim.stop();
+    if (useRelPos)
+        setRelPos(anim);
+    else
+        move(anim);
+}
+
+bool BranchObj::animate()
+{
+    anim.animate();
+    if (anim.isAnimated()) {
+        if (useRelPos)
+            setRelPos(anim);
+        else
+            move(anim);
+        return true;
+    }
+    return false;
+}