--- /dev/null
+#include <math.h>
+
+#include "linkablemapobj.h"
+#include "branchobj.h"
+#include "mapeditor.h"
+
+/////////////////////////////////////////////////////////////////
+// LinkableMapObj
+/////////////////////////////////////////////////////////////////
+
+LinkableMapObj::LinkableMapObj():MapObj()
+{
+ // cout << "Const LinkableMapObj ()\n";
+ init ();
+}
+
+LinkableMapObj::LinkableMapObj(QGraphicsScene* s) :MapObj(s)
+{
+// cout << "Const LinkableMapObj (s)\n";
+ init ();
+}
+
+LinkableMapObj::LinkableMapObj (LinkableMapObj* lmo) : MapObj (lmo->scene)
+{
+ copy (lmo);
+}
+
+LinkableMapObj::~LinkableMapObj()
+{
+ delete (bottomline);
+ delLink();
+}
+
+void LinkableMapObj::delLink()
+{
+ switch (style)
+ {
+ case Line:
+ delete (l);
+ break;
+ case Parabel:
+ while (!segment.isEmpty()) delete segment.takeFirst();
+ break;
+ case PolyLine:
+ delete (p);
+ break;
+ case PolyParabel:
+ delete (p);
+ break;
+ default:
+ break;
+ }
+}
+
+void LinkableMapObj::init ()
+{
+ depth=-1;
+ mapEditor=NULL;
+ childObj=NULL;
+ parObj=NULL;
+ parObjTmpBuf=NULL;
+ parPos=QPointF(0,0);
+ childPos=QPointF(0,0);
+ link2ParPos=false;
+ l=NULL;
+ orientation=UndefinedOrientation;
+ linkwidth=20;
+ thickness_start=8;
+ style=UndefinedStyle;
+ linkpos=Bottom;
+ arcsegs=13;
+
+// TODO instead of linkcolor pen.color() could be used all around
+ pen.setWidth (1);
+ pen.setColor (linkcolor);
+ pen.setCapStyle ( Qt::RoundCap );
+ bottomline=scene->addLine(QLineF(1,1,1,1),pen);
+ bottomline->setZValue(Z_LINK);
+ bottomline->show();
+
+ // Prepare showing the selection of a MapObj
+ selected=false;
+
+ hideLinkUnselected=false;
+
+ topPad=botPad=leftPad=rightPad=0;
+
+ repositionRequest=false;
+
+ // Rel Positions
+ relPos=QPointF(0,0);
+ useRelPos=false;
+ useOrientation=true;
+
+ // Reset ID
+ objID="";
+}
+
+void LinkableMapObj::copy (LinkableMapObj* other)
+{
+ MapObj::copy(other);
+ bboxTotal=other->bboxTotal;
+ setLinkStyle(other->style);
+ setLinkColor (other->linkcolor);
+ relPos=other->relPos;
+ useOrientation=other->useOrientation;
+ objID=other->objID;
+}
+
+void LinkableMapObj::setChildObj(LinkableMapObj* o)
+{
+ childObj=o;
+}
+
+void LinkableMapObj::setParObj(LinkableMapObj* o)
+{
+ parObj=o;
+ mapEditor=parObj->getMapEditor();
+}
+
+void LinkableMapObj::setParObjTmp(LinkableMapObj*,QPointF,int)
+{
+}
+
+void LinkableMapObj::unsetParObjTmp()
+{
+}
+
+bool LinkableMapObj::hasParObjTmp()
+{
+ if (parObjTmpBuf) return true;
+ return false;
+}
+
+void LinkableMapObj::setUseRelPos (const bool &b)
+{
+ useRelPos=b;
+}
+
+void LinkableMapObj::setRelPos()
+{
+ if (parObj)
+ {
+ relPos.setX (absPos.x() - parObj->getChildPos().x() );
+ relPos.setY (absPos.y() - parObj->getChildPos().y() );
+ parObj->calcBBoxSize();
+ }
+}
+
+void LinkableMapObj::setRelPos(const QPointF &p)
+{
+ relPos=p;
+ if (parObj)
+ {
+ parObj->calcBBoxSize();
+ requestReposition();
+ }
+}
+
+QPointF LinkableMapObj::getRelPos()
+{
+ if (!parObj) return QPointF();
+ return relPos;
+}
+
+qreal LinkableMapObj::getTopPad()
+{
+ return topPad;
+}
+
+qreal LinkableMapObj::getLeftPad()
+{
+ return leftPad;
+}
+
+qreal LinkableMapObj::getRightPad()
+{
+ return rightPad;
+}
+
+LinkableMapObj::Style LinkableMapObj::getDefLinkStyle ()
+{
+ if (!mapEditor) return UndefinedStyle;
+ Style ls=mapEditor->getMapLinkStyle();
+ switch (ls)
+ {
+ case Line:
+ return ls;
+ break;
+ case Parabel:
+ return ls;
+ break;
+ case PolyLine:
+ if (depth>1)
+ return Line;
+ else
+ return ls;
+ break;
+ case PolyParabel:
+ if (depth>1)
+ return Parabel;
+ else
+ return ls;
+ break;
+ default:
+ break;
+ }
+ return UndefinedStyle;
+}
+
+void LinkableMapObj::setLinkStyle(Style newstyle)
+{
+ //if (newstyle=style) return;
+ delLink();
+
+ style=newstyle;
+
+ if (childObj!=NULL && parObj != NULL)
+ {
+ QGraphicsLineItem *cl;
+ switch (style)
+ {
+ case UndefinedStyle:
+ bottomline->hide();
+ break;
+ case Line:
+ l = scene->addLine(QLineF(1,1,1,1),pen);
+ l->setZValue(Z_LINK);
+ if (visible)
+ l->show();
+ else
+ l->hide();
+ break;
+ case Parabel:
+ for (int i=0;i<arcsegs;i++)
+ {
+ cl = scene->addLine(QLineF(i*5,0,i*10,100),pen);
+ cl->setZValue(Z_LINK);
+ if (visible)
+ cl->show();
+ else
+ cl->hide();
+ segment.append(cl);
+ }
+ pa0.resize (arcsegs+1);
+ break;
+ case PolyLine:
+ p =scene->addPolygon(QPolygonF(),pen,linkcolor);
+ p->setZValue(Z_LINK);
+ if (visible)
+ p->show();
+ else
+ p->hide();
+ pa0.resize (3);
+ break;
+ case PolyParabel:
+ p = scene->addPolygon(QPolygonF(),pen,linkcolor);
+ p->setZValue(Z_LINK);
+ if (visible)
+ p->show();
+ else
+ p->hide();
+ pa0.resize (arcsegs*2+2);
+ pa1.resize (arcsegs+1);
+ pa2.resize (arcsegs+1);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+LinkableMapObj::Style LinkableMapObj::getLinkStyle()
+{
+ return style;
+}
+
+void LinkableMapObj::setHideLinkUnselected(bool b)
+{
+ hideLinkUnselected=b;
+ setVisibility (visible);
+ updateLink();
+}
+
+bool LinkableMapObj::getHideLinkUnselected()
+{
+ return hideLinkUnselected;
+}
+
+void LinkableMapObj::setLinkPos(Position lp)
+{
+ linkpos=lp;
+}
+
+LinkableMapObj::Position LinkableMapObj::getLinkPos()
+{
+ return linkpos;
+}
+
+void LinkableMapObj::setID (const QString &s)
+{
+ objID=s;
+}
+
+QString LinkableMapObj::getID()
+{
+ return objID;
+}
+
+void LinkableMapObj::setLinkColor()
+{
+ // Overloaded in BranchObj and childs
+ // here only set default color
+ if (mapEditor)
+ setLinkColor (mapEditor->getMapDefLinkColor());
+}
+
+void LinkableMapObj::setLinkColor(QColor col)
+{
+ linkcolor=col;
+ pen.setColor(col);
+ bottomline->setPen( pen );
+ switch (style)
+ {
+ case Line:
+ l->setPen( pen);
+ break;
+ case Parabel:
+ for (int i=0; i<segment.size(); ++i)
+ segment.at(i)->setPen( pen);
+ break;
+ case PolyLine:
+ p->setBrush( QBrush(col));
+ p->setPen( pen);
+ break;
+ case PolyParabel:
+ p->setBrush( QBrush(col));
+ p->setPen( pen);
+ break;
+ default:
+ break;
+ } // switch (style)
+}
+
+QColor LinkableMapObj::getLinkColor()
+{
+ return linkcolor;
+}
+
+void LinkableMapObj::setVisibility (bool v)
+{
+ MapObj::setVisibility (v);
+ bool visnow=visible;
+
+ // We can hide the link, while object is not selected
+ if (hideLinkUnselected && !selected)
+ visnow=false;
+
+ if (visnow)
+ {
+ bottomline->show();
+ switch (style)
+ {
+ case Line:
+ if (l) l->show();
+ break;
+ case Parabel:
+ for (int i=0; i<segment.size(); ++i)
+ segment.at(i)->show();
+ break;
+ case PolyLine:
+ if (p) p->show();
+ break;
+ case PolyParabel:
+ if (p) p->show();
+ break;
+ default:
+ break;
+ }
+ } else
+ {
+ bottomline->hide();
+ switch (style)
+ {
+ case Line:
+ if (l) l->hide();
+ break;
+ case Parabel:
+ for (int i=0; i<segment.size(); ++i)
+ segment.at(i)->hide();
+ break;
+ case PolyLine:
+ if (p) p->hide();
+ break;
+ case PolyParabel:
+ if (p) p->hide();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void LinkableMapObj::setOrientation()
+{
+ Orientation orientOld=orientation;
+
+ if (!parObj)
+ {
+ orientation=UndefinedOrientation;
+ return;
+ }
+
+ // Set orientation, first look for orientation of parent
+ if (parObj->getOrientation() != UndefinedOrientation )
+ // use the orientation of the parent:
+ orientation=parObj->getOrientation();
+ else
+ {
+ // calc orientation depending on position rel to parent
+ if (absPos.x() < QPointF(parObj->getChildPos() ).x() )
+ orientation=LeftOfCenter;
+ else
+ orientation=RightOfCenter;
+ }
+ if (orientOld!=orientation) requestReposition();
+}
+
+void LinkableMapObj::updateLink()
+{
+ // needs:
+ // childPos of parent
+ // orient of parent
+ // style
+ //
+ // sets:
+ // orientation
+ // childPos (by calling setDockPos())
+ // parPos (by calling setDockPos())
+ // bottomlineY
+ // drawing of the link itself
+
+ // updateLink is called from move, but called from constructor we don't
+ // have parents yet...
+ if (style==UndefinedStyle) return;
+
+ switch (linkpos)
+ {
+ case Middle:
+ bottomlineY=bbox.top() + bbox.height()/2; // draw link to middle (of frame)
+ break;
+ case Bottom:
+ bottomlineY=bbox.bottom()-1; // draw link to bottom of box
+ break;
+ }
+
+ double p2x,p2y; // Set P2 Before setting
+ if (!link2ParPos)
+ {
+ p2x=QPointF( parObj->getChildPos() ).x(); // P1, we have to look at
+ p2y=QPointF( parObj->getChildPos() ).y(); // orientation
+ } else
+ {
+ p2x=QPointF( parObj->getParPos() ).x();
+ p2y=QPointF( parObj->getParPos() ).y();
+ }
+
+ setDockPos(); // Call overloaded method
+ setOrientation();
+
+ double p1x=parPos.x(); // Link is drawn from P1 to P2
+ double p1y=parPos.y();
+
+ double vx=p2x - p1x; // V=P2-P1
+ double vy=p2y - p1y;
+
+ // Draw the horizontal line below heading (from ChildPos to ParPos)
+ //bottomline->prepareGeometryChange();
+ bottomline->setLine (QLine (qRound(childPos.x()),
+ qRound(childPos.y()),
+ qRound(p1x),
+ qRound(p1y) ));
+
+ double a; // angle
+ if (vx > -0.000001 && vx < 0.000001)
+ a=M_PI_2;
+ else
+ a=atan( vy / vx );
+ // "turning point" for drawing polygonal links
+ QPointF tp (-qRound(sin (a)*thickness_start), qRound(cos (a)*thickness_start));
+
+ // Draw the link
+ switch (style)
+ {
+ case Line:
+ //l->prepareGeometryChange();
+ l->setLine( QLine(qRound (parPos.x()),
+ qRound(parPos.y()),
+ qRound(p2x),
+ qRound(p2y) ));
+ break;
+ case Parabel:
+ parabel (pa0, p1x,p1y,p2x,p2y);
+ for (int i=0; i<segment.size(); ++i)
+ {
+ //segment.at(i)->prepareGeometryChange();
+ segment.at(i)->setLine(QLineF( pa0.at(i).x(), pa0.at(i).y(),pa0.at(i+1).x(),pa0.at(i+1).y()));
+ }
+ break;
+ case PolyLine:
+ pa0.clear();
+ pa0<<QPointF (qRound(p2x+tp.x()), qRound(p2y+tp.y()));
+ pa0<<QPointF (qRound(p2x-tp.x()), qRound(p2y-tp.y()));
+ pa0<<QPointF (qRound (parPos.x()), qRound(parPos.y()) );
+ //p->prepareGeometryChange();
+ p->setPolygon(QPolygonF (pa0));
+ break;
+ case PolyParabel:
+ parabel (pa1, p1x,p1y,p2x+tp.x(),p2y+tp.y());
+ parabel (pa2, p1x,p1y,p2x-tp.x(),p2y-tp.y());
+ pa0.clear();
+ for (int i=0;i<=arcsegs;i++)
+ pa0 << QPointF (pa1.at(i));
+ for (int i=0;i<=arcsegs;i++)
+ pa0 << QPointF (pa2.at(arcsegs-i));
+ //p->prepareGeometryChange();
+ p->setPolygon(QPolygonF (pa0));
+ break;
+ default:
+ break;
+ } // switch (style)
+}
+
+LinkableMapObj* LinkableMapObj::getChildObj()
+{
+ return childObj;
+}
+
+LinkableMapObj* LinkableMapObj::getParObj()
+{
+ return parObj;
+}
+
+LinkableMapObj* LinkableMapObj::findObjBySelect (QString s)
+{
+ LinkableMapObj *lmo=this;
+ QString part;
+ QString typ;
+ QString num;
+ while (!s.isEmpty() )
+ {
+ part=s.section(",",0,0);
+ typ=part.left (3);
+ num=part.right(part.length() - 3);
+ if (typ=="mc:")
+ {
+ if (depth>0)
+ return false; // in a subtree there is no center
+ else
+ break;
+ } else
+ if (typ=="bo:")
+ lmo=((BranchObj*)lmo)->getBranchNum (num.toInt());
+ else
+ if (typ=="fi:")
+ lmo=((BranchObj*)lmo)->getFloatImageNum (num.toUInt());
+ if (!lmo) break;
+
+ if (s.contains(","))
+ s=s.right(s.length() - part.length() -1 );
+ else
+ break;
+ }
+ return lmo;
+}
+
+QPointF LinkableMapObj::getChildPos()
+{
+ return childPos;
+}
+
+QPointF LinkableMapObj::getParPos()
+{
+ return parPos;
+}
+
+void LinkableMapObj::setUseOrientation (const bool &b)
+{
+ if (useOrientation!=b)
+ {
+ useOrientation=b;
+ requestReposition();
+ }
+}
+
+LinkableMapObj::Orientation LinkableMapObj::getOrientation()
+{
+ return orientation;
+}
+
+int LinkableMapObj::getDepth()
+{
+ return depth;
+}
+
+void LinkableMapObj::setMapEditor (MapEditor *me)
+{
+ mapEditor=me;
+}
+
+MapEditor* LinkableMapObj::getMapEditor ()
+{
+ return mapEditor;
+}
+
+QPointF LinkableMapObj::getRandPos()
+{
+ // Choose a random position with given distance to parent:
+ double a=rand()%360 * 2 * M_PI / 360;
+ return QPointF ( (int)( + 150*cos (a)),
+ (int)( + 150*sin (a)));
+}
+
+void LinkableMapObj::reposition()
+{
+}
+
+void LinkableMapObj::requestReposition()
+{
+ if (!repositionRequest)
+ {
+ // Pass on the request to parental objects, if this hasn't
+ // been done yet
+ repositionRequest=true;
+ if (parObj) parObj->requestReposition();
+ }
+}
+
+void LinkableMapObj::forceReposition()
+{
+ // Sometimes a reposition has to be done immediatly: For example
+ // if the note editor flag changes, there is no user event in mapeditor
+ // which could collect requests for a reposition.
+ // Then we have to call forceReposition()
+ // But no rule without exception: While loading a map or undoing it,
+ // we want to block expensive repositioning, but just do it once at
+ // the end, thus check first:
+
+ if (mapEditor->isRepositionBlocked()) return;
+
+ // Pass on the request to parental objects, if this hasn't been done yet
+
+ if (parObj)
+ parObj->forceReposition();
+ else
+ reposition();
+}
+
+bool LinkableMapObj::repositionRequested()
+{
+ return repositionRequest;
+}
+
+
+void LinkableMapObj::select()
+{
+ // select and unselect are still needed to
+ // handle hiding of links
+ selected=true;
+ setVisibility (visible);
+}
+
+
+void LinkableMapObj::unselect()
+{
+ selected=false;
+ // Maybe we have to hide the link:
+ setVisibility (visible);
+}
+
+void LinkableMapObj::parabel (QPolygonF &ya, double p1x, double p1y, double p2x, double p2y)
+
+{
+ double vx=p2x - p1x; // V=P2-P1
+ double vy=p2y - p1y;
+
+ double dx; // delta x during calculation of parabel
+
+ double pnx; // next point
+ double pny;
+ double m;
+
+ if (vx > -0.0001 && vx < 0.0001)
+ m=0;
+ else
+ m=(vy / (vx*vx));
+ dx=vx/(arcsegs);
+ ya.clear();
+ ya<<QPointF (p1x,p1y);
+ for (int i=1;i<=arcsegs;i++)
+ {
+ pnx=p1x+dx;
+ pny=m*(pnx-parPos.x())*(pnx-parPos.x())+parPos.y();
+ ya<<QPointF (pnx,pny);
+ p1x=pnx;
+ p1y=pny;
+ }
+}
+
+QString LinkableMapObj::getLinkAttr ()
+{
+ if (hideLinkUnselected)
+ return attribut ("hideLink","true");
+ else
+ return attribut ("hideLink","false");
+
+}
+