#include #include "linkablemapobj.h" #include "branchobj.h" #include "mapeditor.h" #include "version.h" ///////////////////////////////////////////////////////////////// // LinkableMapObj ///////////////////////////////////////////////////////////////// LinkableMapObj::LinkableMapObj():MapObj() { // cout << "Const LinkableMapObj ()\n"; init (); } LinkableMapObj::LinkableMapObj(QCanvas* c) :MapObj(c) { // cout << "Const LinkableMapObj\n"; init (); } LinkableMapObj::LinkableMapObj (LinkableMapObj* lmo) : MapObj (lmo->canvas) { copy (lmo); } LinkableMapObj::~LinkableMapObj() { delete (bottomline); delete (selbox); delete (frame); delLink(); } void LinkableMapObj::delLink() { switch (style) { case StyleLine: delete (l); break; case StyleParabel: segment.clear(); break; case StylePolyLine: delete (p); delete (l); break; case StylePolyParabel: delete (p); segment.clear(); break; default: break; } } void LinkableMapObj::init () { depth=-1; mapEditor=NULL; childObj=NULL; parObj=NULL; parObjTmpBuf=NULL; parPos=QPoint(0,0); childPos=QPoint(0,0); link2ParPos=false; l=NULL; orientation=OrientUndef; linkwidth=20; thickness_start=8; style=StyleUndef; linkpos=LinkBottom; segment.setAutoDelete (TRUE); arcsegs=13; QPointArray pa(arcsegs*2+2); bottomline=new QCanvasLine(canvas); bottomline->setPen( QPen(linkcolor, 1) ); bottomline->setZ(Z_LINK); bottomline->show(); // Prepare showing the selection of a MapObj selbox = new QCanvasRectangle (canvas); selbox->setZ(Z_SELBOX); selbox->setBrush( QColor(255,255,0) ); selbox->setPen( QPen(QColor(255,255,0) )); selbox->hide(); selected=false; hideLinkUnselected=false; topPad=botPad=leftPad=rightPad=0; // initialize frame frame = new FrameObj (canvas); repositionRequest=false; // Rel Positions relPos=QPoint(0,0); useRelPos=false; useOrientation=true; } void LinkableMapObj::copy (LinkableMapObj* other) { MapObj::copy(other); bboxTotal=other->bboxTotal; setLinkStyle(other->style); setLinkColor (other->linkcolor); relPos=other->relPos; useOrientation=other->useOrientation; } void LinkableMapObj::setChildObj(LinkableMapObj* o) { childObj=o; } void LinkableMapObj::setParObj(LinkableMapObj* o) { parObj=o; mapEditor=parObj->getMapEditor(); } void LinkableMapObj::setParObjTmp(LinkableMapObj*,QPoint,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(); parObj->requestReposition(); } } void LinkableMapObj::setRelPos(const QPoint &p) { relPos=p; if (parObj) { parObj->calcBBoxSize(); parObj->requestReposition(); } } int LinkableMapObj::getTopPad() { return topPad; } int LinkableMapObj::getLeftPad() { return leftPad; } int LinkableMapObj::getRightPad() { return rightPad; } LinkStyle LinkableMapObj::getDefLinkStyle () { if (!mapEditor) return StyleUndef; LinkStyle ls=mapEditor->getLinkStyle(); switch (ls) { case StyleLine: return ls; break; case StyleParabel: return ls; break; case StylePolyLine: if (depth>1) return StyleLine; else return ls; break; case StylePolyParabel: if (depth>1) return StyleParabel; else return ls; break; default: break; } return StyleUndef; } void LinkableMapObj::setLinkStyle(LinkStyle newstyle) { //if (newstyle=style) return; delLink(); style=newstyle; if (childObj!=NULL && parObj != NULL) { int i; QCanvasLine* cl; switch (style) { case StyleUndef: bottomline->hide(); break; case StyleLine: l = new QCanvasLine(canvas); l->setPen( QPen(linkcolor, 1) ); l->setZ(Z_LINK); if (visible) l->show(); else l->hide(); break; case StyleParabel: for (i=0;isetPen( QPen(linkcolor, 1) ); cl->setPoints( 0,0,i*10,100); cl->setZ(Z_LINK); if (visible) cl->show(); else cl->hide(); segment.append(cl); } pa0.resize (arcsegs+1); break; case StylePolyLine: p = new QCanvasPolygon(canvas); p->setBrush( linkcolor ); p->setZ(Z_LINK); if (visible) p->show(); else p->hide(); pa0.resize (3); // TODO a bit awkward: draw the lines additionally to polygon, to avoid // missing pixels, when polygon is extremly flat l = new QCanvasLine(canvas); l->setPen( QPen(linkcolor, 1) ); l->setZ(Z_LINK); if (visible) l->show(); else l->hide(); break; case StylePolyParabel: p = new QCanvasPolygon(canvas); p->setBrush( linkcolor ); p->setZ(Z_LINK); if (visible) p->show(); else p->hide(); pa0.resize (arcsegs*2+2); pa1.resize (arcsegs+1); pa2.resize (arcsegs+1); // TODO a bit awkward: draw the lines additionally // to polygon, to avoid missing pixels, // if polygon is extremly flat for (i=0;isetPen( QPen(linkcolor, 1) ); cl->setPoints( 0,0,i*10,100); cl->setZ(Z_LINK); if (visible) cl->show(); else cl->hide(); segment.append(cl); } break; default: break; } } } LinkStyle LinkableMapObj::getLinkStyle() { return style; } void LinkableMapObj::setHideLinkUnselected(bool b) { hideLinkUnselected=b; setVisibility (visible); updateLink(); } bool LinkableMapObj::getHideLinkUnselected() { return hideLinkUnselected; } void LinkableMapObj::setLinkPos(LinkPos lp) { linkpos=lp; } LinkPos LinkableMapObj::getLinkPos() { return linkpos; } void LinkableMapObj::setLinkColor() { // Overloaded in BranchObj and childs // here only set default color if (mapEditor) setLinkColor (mapEditor->getDefLinkColor()); } void LinkableMapObj::setLinkColor(QColor col) { linkcolor=col; bottomline->setPen( QPen(linkcolor, 1) ); QCanvasLine *cl; switch (style) { case StyleLine: l->setPen( QPen(col,1)); break; case StyleParabel: for (cl=segment.first(); cl; cl=segment.next() ) cl->setPen( QPen(col,1)); break; case StylePolyLine: p->setBrush( QBrush(col)); l->setPen( QPen(col,1)); break; case StylePolyParabel: p->setBrush( QBrush(col)); for (cl=segment.first(); cl; cl=segment.next() ) cl->setPen( QPen(col,1)); break; default: break; } // switch (style) } QColor LinkableMapObj::getLinkColor() { return linkcolor; } FrameType LinkableMapObj::getFrameType() { return frame->getFrameType(); } void LinkableMapObj::setFrameType(const FrameType &t) { frame->setFrameType(t); calcBBoxSize(); positionBBox(); requestReposition(); } void LinkableMapObj::setFrameType(const QString &t) { frame->setFrameType(t); calcBBoxSize(); positionBBox(); requestReposition(); } void LinkableMapObj::setVisibility (bool v) { QCanvasLine* cl; 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 StyleLine: if (l) l->show(); break; case StyleParabel: for (cl=segment.first(); cl; cl=segment.next() ) cl->show(); break; case StylePolyLine: if (p) p->show(); if (l) l->show(); break; case StylePolyParabel: for (cl=segment.first(); cl; cl=segment.next() ) cl->show(); if (p) p->show(); break; default: break; } } else { bottomline->hide(); switch (style) { case StyleLine: if (l) l->hide(); break; case StyleParabel: for (cl=segment.first(); cl; cl=segment.next() ) cl->hide(); break; case StylePolyLine: if (p) p->hide(); if (l) l->hide(); break; case StylePolyParabel: for (cl=segment.first(); cl; cl=segment.next() ) cl->hide(); if (p) p->hide(); break; default: break; } } } 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==StyleUndef) return; if (frame->getFrameType() == NoFrame) linkpos=LinkBottom; else linkpos=LinkMiddle; switch (linkpos) { case LinkMiddle: bottomlineY=bbox.top()+bbox.height() /2; // draw link to middle (of frame) break; default : bottomlineY=bbox.bottom()-1; // draw link to bottom of box break; } double p2x,p2y; // Set P2 Before setting if (!link2ParPos) { p2x=QPoint( parObj->getChildPos() ).x(); // P1, we have to look at p2y=QPoint( parObj->getChildPos() ).y(); // orientation } else { p2x=QPoint( parObj->getParPos() ).x(); p2y=QPoint( parObj->getParPos() ).y(); } LinkOrient orientOld=orientation; // Set orientation, first look for orientation of parent if (parObj->getOrientation() != OrientUndef ) // use the orientation of the parent: orientation=parObj->getOrientation(); else { // calc orientation depending on position rel to mapCenter if (absPos.x() < QPoint(parObj->getChildPos() ).x() ) orientation=OrientLeftOfCenter; else orientation=OrientRightOfCenter; } if ((orientation!=orientOld) && (orientOld!= OrientUndef)) { // Orientation just changed. Reorient this subbranch, because move is called // before updateLink => Position is still the old one, which could lead to // linking of subranch to itself => segfault // // Also possible: called in BranchObj::init(), then orientOld==OrientUndef, // no need to reposition now reposition(); } setDockPos(); 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->setPoints (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 QPoint tp (-qRound(sin (a)*thickness_start), qRound(cos (a)*thickness_start)); QCanvasLine *cl; int i; // Draw the link switch (style) { case StyleLine: l->setPoints( qRound (parPos.x()), qRound(parPos.y()), qRound(p2x), qRound(p2y) ); break; case StyleParabel: parabel (pa0, p1x,p1y,p2x,p2y); i=0; for (cl=segment.first(); cl; cl=segment.next() ) { cl->setPoints( pa0.point(i).x(), pa0.point(i).y(),pa0.point(i+1).x(),pa0.point(i+1).y()); i++; } break; case StylePolyLine: pa0[0]=QPoint (qRound(p2x+tp.x()), qRound(p2y+tp.y())); pa0[1]=QPoint (qRound(p2x-tp.x()), qRound(p2y-tp.y())); pa0[2]=QPoint (qRound (parPos.x()), qRound(parPos.y()) ); p->setPoints (pa0); // here too, draw line to avoid missing pixels l->setPoints( qRound (parPos.x()), qRound(parPos.y()), qRound(p2x), qRound(p2y) ); break; case StylePolyParabel: parabel (pa1, p1x,p1y,p2x+tp.x(),p2y+tp.y()); parabel (pa2, p1x,p1y,p2x-tp.x(),p2y-tp.y()); for (i=0;i<=arcsegs;i++) { // Combine the arrays to a single one pa0[i]=pa1[i]; pa0[i+arcsegs+1]=pa2[arcsegs-i]; } p->setPoints (pa0); i=0; for (cl=segment.first(); cl; cl=segment.next() ) { cl->setPoints( pa1.point(i).x(), pa1.point(i).y(),pa1.point(i+1).x(),pa1.point(i+1).y()); i++; } 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.toUInt()); 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; } void LinkableMapObj::setDockPos() { cout <<"LMO::setDockPos()\n"; } QPoint LinkableMapObj::getChildPos() { return childPos; } QPoint LinkableMapObj::getParPos() { return parPos; } QPoint LinkableMapObj::getRelPos() { return relPos; /* FIXME not needed? relPos was moved in 1.7.10 from floatobj to linkablemapobj. Before we had: if (!parObj) return QPoint (0,0); return QPoint( absPos.x() - parObj->x(), absPos.y() - parObj->y() ); */ } void LinkableMapObj::setUseOrientation (const bool &b) { if (useOrientation!=b) { useOrientation=b; requestReposition(); } } LinkOrient LinkableMapObj::getOrientation() { return orientation; } int LinkableMapObj::getDepth() { return depth; } void LinkableMapObj::setMapEditor (MapEditor *me) { mapEditor=me; } MapEditor* LinkableMapObj::getMapEditor () { return mapEditor; } QPoint LinkableMapObj::getRandPos() { // Choose a random position with given distance to parent: double a=rand()%360 * 2 * M_PI / 360; return QPoint ( (int)( + 150*cos (a)), (int)( + 150*sin (a))); } void LinkableMapObj::alignRelativeTo (QPoint ref) { // FIXME testing, seems not to be used right now... cout << "LMO::alignRelTo ref="<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::setSelBox() { selbox->setX (clickBox.x() ); selbox->setY (clickBox.y() ); selbox->setSize (clickBox.width(), clickBox.height() ); } void LinkableMapObj::select() { setSelBox(); selected=true; selbox->show(); // FIXME not needed? setVisibility (visible); } void LinkableMapObj::unselect() { selected=false; selbox->hide(); // Maybe we have to hide the link: setVisibility (visible); } void LinkableMapObj::parabel (QPointArray &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); int i; ya.setPoint (0,QPoint (qRound(p1x),qRound(p1y))); for (i=1;i<=arcsegs;i++) { pnx=p1x+dx; pny=m*(pnx-parPos.x())*(pnx-parPos.x())+parPos.y(); ya.setPoint (i,QPoint (qRound(pnx),qRound(pny))); p1x=pnx; p1y=pny; } } QString LinkableMapObj::getLinkAttr () { if (hideLinkUnselected) return attribut ("hideLink","true"); else return attribut ("hideLink","false"); }