]> git.sven.stormbind.net Git - sven/vym.git/blob - linkablemapobj.cpp
Import Upstream version 2.6.11
[sven/vym.git] / linkablemapobj.cpp
1 #include <iostream>
2 #include <math.h>
3 #include <cstdlib>
4
5 #include "linkablemapobj.h"
6 #include "branchobj.h"
7 #include "vymmodel.h"
8
9 extern bool debug;
10
11 /////////////////////////////////////////////////////////////////
12 // LinkableMapObj
13 /////////////////////////////////////////////////////////////////
14
15 LinkableMapObj::LinkableMapObj(QGraphicsItem* parent, TreeItem *ti) :MapObj(parent,ti)
16 {
17     //qDebug() << "Const LinkableMapObj this="<<this<<"  ti="<<ti<<"  treeItem="<<treeItem;
18     parObj=(LinkableMapObj*)parent; //FIXME-4 try to get rid of parObj and use parentItem() instead
19     init ();
20 }
21
22 LinkableMapObj::~LinkableMapObj()
23 {
24     //qDebug()<< "Destructor LMO  this="<<this<<" style="<<style<<" l="<<l<<"  p="<<p<<"  segment="<<segment.count();
25     delLink();
26 }
27
28 void LinkableMapObj::init ()
29 {
30     parObjTmpBuf=NULL;
31     tmpParent=false;
32     parPos=QPointF(0,0);
33     childRefPos=QPointF(0,0);
34     floatRefPos=QPointF(0,0);
35     link2ParPos=false;
36     l=NULL;
37     p=NULL;
38     orientation=UndefinedOrientation;
39     linkwidth=20;       
40     thickness_start=8;
41     style=UndefinedStyle;
42     linkpos=Bottom;
43     arcsegs=13;
44     
45 // TODO instead of linkcolor pen.color() could be used  all around
46     pen.setWidth (1);
47     pen.setColor (linkcolor);
48     pen.setCapStyle ( Qt::RoundCap );
49
50     useBottomline=false;
51     bottomline=NULL;
52
53     topPad=botPad=leftPad=rightPad=0;
54
55     repositionRequest=false;
56
57     // Rel Positions
58     relPos=QPointF(0,0);
59     useRelPos=false;
60 }
61
62 void LinkableMapObj::createBottomLine()
63 {
64     bottomline=scene()->addLine(QLineF(1,1,1,1),pen);
65     bottomline->setZValue(dZ_LINK);
66 }
67
68 void LinkableMapObj::delLink()
69 {
70     if (bottomline)
71     {
72         delete (bottomline);
73         bottomline=NULL;
74     }   
75     switch (style)
76     {
77         case Line:
78             delete (l);
79             break;
80         case Parabel:
81             while (!segment.isEmpty()) delete segment.takeFirst();
82             break;
83         case PolyLine:
84             delete (p);
85             break;
86         case PolyParabel:
87             delete (p);
88             break;
89         default:
90             break;
91     }       
92 }
93
94 void LinkableMapObj::copy (LinkableMapObj* other)
95 {
96     MapObj::copy(other);
97     bboxTotal=other->bboxTotal;
98     setLinkStyle(other->style);
99     setLinkColor (other->linkcolor);
100     relPos=other->relPos;
101     treeItem=other->treeItem;
102 }
103
104 void LinkableMapObj::setParObj(LinkableMapObj* o)
105 {
106     parObj=o;
107     setParentItem (parObj);
108 }
109
110 void LinkableMapObj::setParObjTmp(LinkableMapObj*,QPointF,int)  
111 {
112 }
113
114 void LinkableMapObj::unsetParObjTmp()
115 {
116 }
117
118 bool LinkableMapObj::hasParObjTmp()
119 {
120     return tmpParent;
121 }
122
123 void LinkableMapObj::setUseRelPos (const bool &b)
124 {
125     useRelPos=b;
126 }
127
128 bool LinkableMapObj::getUseRelPos ()
129 {
130     return useRelPos;
131 }
132
133 void LinkableMapObj::setRelPos()
134 {
135     if (parObj)
136         setRelPos (absPos - parObj->getChildRefPos() );
137     else
138         qWarning()<<"LMO::setRelPos parObj==0   this="<<this;
139 }
140
141 void LinkableMapObj::setRelPos(const QPointF &p)
142 {
143     if (parObj)
144     {       
145         relPos=p;
146         useRelPos=true;
147         setOrientation();
148     }   else
149         qWarning()<<"LMO::setRelPos (p)  parObj==0   this="<<this;
150 }
151
152 QPointF LinkableMapObj::getRelPos()
153 {
154     if (!parObj) 
155     {
156         qWarning()<<"LMO::getRelPos parObj==0   this="<<this;
157         return QPointF();
158     }
159     return relPos;
160 }
161
162 qreal LinkableMapObj::getTopPad()
163 {
164     return topPad;
165 }
166
167 qreal LinkableMapObj::getLeftPad()
168 {
169     return leftPad;
170 }
171
172 qreal LinkableMapObj::getRightPad()
173 {
174     return rightPad;
175 }
176
177 LinkableMapObj::Style LinkableMapObj::getDefLinkStyle (TreeItem *parent)
178 {
179     VymModel *model=treeItem->getModel();
180     if (!model)
181     {
182         qWarning ("LMO::getDefLinkStyle   model=NULL");
183         //return UndefinedStyle;
184     }
185     Style ls=model->getMapLinkStyle();
186     int depth=1+parent->depth();
187     if (depth==0) return UndefinedStyle;
188     switch (ls)
189     {
190         case Line: 
191             return ls;
192             break;
193         case Parabel:
194             return ls;
195             break;
196         case PolyLine:  
197             if (depth>1)
198                 return Line;
199             else    
200                 return ls;
201             break;
202         case PolyParabel:   
203             if (depth>1)
204                 return Parabel;
205             else    
206                 return ls;
207             break;
208         default: 
209             break;  
210     }   
211     return UndefinedStyle;
212 }
213
214 void LinkableMapObj::setLinkStyle(Style newstyle)
215 {
216     //qDebug()<<"LMO::setLinkStyle s="<<newstyle;       //FIXME-4 called very often?!?!
217     //qDebug()<<"LMO::setLinkStyle s="<<newstyle<<" for "<<this<<" "<<treeItem->getHeading()<<"  parObj="<<parObj;
218     delLink();
219         
220     style=newstyle;
221
222     QGraphicsLineItem *cl;
223     switch (style)
224     {
225         case Line: 
226             l = scene()->addLine(QLineF(1,1,1,1),pen);
227             l->setZValue(dZ_LINK);
228             if (visible)
229                 l->show();
230             else
231                 l->hide();
232             createBottomLine();
233             break;
234         case Parabel:
235             for (int i=0;i<arcsegs;i++)
236             {
237                 cl = scene()->addLine(QLineF(i*5,0,i*10,100),pen);
238                 cl->setZValue(dZ_LINK);
239                 if (visible)
240                     cl->show();
241                 else
242                     cl->hide();
243                 segment.append(cl);
244             }
245             pa0.resize (arcsegs+1);
246             createBottomLine();
247             break;
248         case PolyLine:  
249             p =scene()->addPolygon(QPolygonF(),pen,linkcolor);
250             p->setZValue(dZ_LINK);
251             if (visible)
252                 p->show();
253             else
254                 p->hide();
255             pa0.resize (3);
256             createBottomLine();
257             break;
258         case PolyParabel:       
259             p = scene()->addPolygon(QPolygonF(),pen,linkcolor);
260             p->setZValue(dZ_LINK);
261             if (visible)
262                 p->show();
263             else
264                 p->hide();
265             pa0.resize (arcsegs*2+2);
266             pa1.resize (arcsegs+1);
267             pa2.resize (arcsegs+1);
268             createBottomLine();
269             break;
270         default: 
271             break;      
272     }   
273 }
274
275 LinkableMapObj::Style LinkableMapObj::getLinkStyle()
276 {
277     return style;
278 }
279
280 void LinkableMapObj::setLinkPos(Position lp)
281 {
282     linkpos=lp;
283 }
284
285 LinkableMapObj::Position LinkableMapObj::getLinkPos()
286 {
287     return linkpos;
288 }
289
290 void LinkableMapObj::setLinkColor()
291 {
292     // Overloaded in BranchObj and children
293     // here only set default color
294     VymModel *model=treeItem->getModel();
295     if (model)
296         setLinkColor (model->getMapDefLinkColor());
297 }
298
299 void LinkableMapObj::setLinkColor(QColor col)
300 {
301     linkcolor=col;
302     pen.setColor(col);
303     if (bottomline) bottomline->setPen( pen );
304     switch (style)
305     {
306         case Line:
307             l->setPen( pen);
308             break;  
309         case Parabel:   
310             for (int i=0; i<segment.size(); ++i)
311                 segment.at(i)->setPen( pen);
312             break;
313         case PolyLine:
314             p->setBrush( QBrush(col));
315             p->setPen( pen);
316             break;
317         case PolyParabel:   
318             p->setBrush( QBrush(col));
319             p->setPen( pen);
320             break;
321         default:
322             break;
323     } 
324 }
325
326 QColor LinkableMapObj::getLinkColor()
327 {
328     return linkcolor;
329 }
330
331 void LinkableMapObj::setVisibility (bool v)
332 {
333     MapObj::setVisibility (v);
334     updateVisibility();
335 }
336
337 void LinkableMapObj::setOrientation()
338 {
339     if (!parObj)
340     {
341         orientation = UndefinedOrientation;
342         return;
343     }
344
345     // calc orientation depending on position rel to parent
346     if (useRelPos)
347     {
348         if (relPos.x() < 0)
349             orientation = LeftOfCenter;
350         else
351             orientation = RightOfCenter;
352     }  else
353         // use the orientation of the parent:
354         orientation = parObj->getOrientation();
355 }
356
357 void LinkableMapObj::updateVisibility()
358 {
359     bool visnow=visible;
360
361     // Hide links of unselected objects (if wanted)
362     if (((MapItem*)treeItem)->getHideLinkUnselected()
363         && !treeItem->getModel()->isSelected(treeItem) )
364         visnow=false;
365
366     if (visnow) 
367     {
368         if (bottomline)
369         {
370             if (useBottomline)
371                 bottomline->show();
372             else        
373                 bottomline->hide();
374         }
375
376         switch (style)
377         {
378             case Line:
379                 if (l) l->show();
380                 break;
381             case Parabel:   
382                 for (int i=0; i<segment.size(); ++i)
383                     segment.at(i)->show();
384                 break;  
385             case PolyLine:
386                 if (p) 
387                     p->show();
388                 else 
389                     qDebug()<< "LMO::updateVis p==0 (PolyLine)"; //FIXME-4
390                 break;
391             case PolyParabel:   
392                 if (p) 
393                     p->show();
394                 else
395             qDebug()<< "LMO::updateVis p==0 (PolyParabel) "<<treeItem->getHeadingPlain(); //FIXME-4
396                 break;
397             default:
398                 break;
399         }
400     } else 
401     {
402         if (bottomline) bottomline->hide();
403         switch (style)
404         {
405             case Line:
406                 if (l) l->hide();
407                 break;
408             case Parabel:   
409                 for (int i=0; i<segment.size(); ++i)
410                     segment.at(i)->hide();
411                 break;  
412             case PolyLine:
413                 if (p) p->hide();
414                 break;
415             case PolyParabel:   
416                 if (p) p->hide();
417                 break;
418             default:
419                 break;
420         }
421     }   
422 }
423
424 void LinkableMapObj::updateLinkGeometry()
425 {
426     // needs:
427     //  childRefPos of parent
428     //  orient   of parent
429     //  style
430     //
431     // sets:
432     //  orientation
433     //  childRefPos    (by calling setDockPos())
434     //  parPos      (by calling setDockPos())
435     //  bottomlineY
436     //  drawing of the link itself
437
438     // updateLinkGeometry is called from move, but called from constructor we don't
439     // have parents yet...
440
441     if (style == UndefinedStyle)
442     {
443         setDockPos();
444         return;
445     }
446
447     switch (linkpos)
448     {
449         case Middle:
450             bottomlineY = bbox.top() + bbox.height()/2; // draw link to middle (of frame)
451             break;
452         case Bottom:
453             //bottomlineY = bbox.bottom()-1;  // draw link to bottom of box
454             bottomlineY = bbox.bottom()  - botPad;  
455             break;
456     }
457     
458     double p2x, p2y;                            // Set P2 Before setting
459     if (!link2ParPos)
460     {
461         p2x = QPointF( parObj->getChildRefPos() ).x();   // P1, we have to look at
462         p2y = QPointF( parObj->getChildRefPos() ).y();   // orientation
463     } else
464     {
465         p2x = QPointF( parObj->getParPos() ).x();
466         p2y = QPointF( parObj->getParPos() ).y();
467     }
468
469     setOrientation();
470     setDockPos(); // Call overloaded method
471
472     double p1x = parPos.x();  // Link is drawn from P1 to P2
473     double p1y = parPos.y();
474
475     double vx = p2x - p1x;    // V=P2-P1
476     double vy = p2y - p1y;
477
478     int z;
479     // Hack to z-move links to MapCenter (d==1) below MCOs frame (d==0) //FIXME-4 no longer used?
480     if (treeItem->depth()<2)
481         //z=(treeItem->depth() -2)*dZ_DEPTH + dZ_LINK;
482         z =- dZ_LINK;
483     else
484         z = dZ_LINK;
485
486     //qDebug()<<"LMO::updateGeo d="<<treeItem->depth()<<"  this="<<this<<"  "<<treeItem->getHeading();
487
488     // Draw the horizontal line below heading (from childRefPos to ParPos)
489     if (bottomline) 
490     {
491         bottomline->setLine (QLineF (childRefPos.x(), childRefPos.y(), p1x, p1y) );
492         bottomline->setZValue (z);
493     }
494
495     double a;   // angle
496     if (vx > -0.000001 && vx < 0.000001)
497         a = M_PI_2;
498     else
499         a = atan( vy / vx );
500     // "turning point" for drawing polygonal links
501     QPointF tp ( -qRound(sin (a) * thickness_start), qRound(cos (a) * thickness_start));
502     
503     // Draw the link
504     switch (style)
505     {
506     case Line:
507         l->setLine( QLine(qRound (parPos.x()),
508                           qRound(parPos.y()),
509                           qRound(p2x),
510                           qRound(p2y) ));
511         l->setZValue (z);
512         break;
513     case Parabel:
514         parabel (pa0, p1x,p1y,p2x,p2y);
515         for (int i=0; i<segment.size(); ++i)
516         {
517             segment.at(i)->setLine(QLineF( pa0.at(i).x(), pa0.at(i).y(), pa0.at(i + 1).x(),pa0.at(i + 1).y()));
518             segment.at(i)->setZValue (z);
519         }
520         break;
521     case PolyLine:
522         pa0.clear();
523         pa0 << QPointF (qRound(p2x + tp.x()), qRound(p2y + tp.y()));
524         pa0 << QPointF (qRound(p2x - tp.x()), qRound(p2y - tp.y()));
525         pa0 << QPointF (qRound (parPos.x()), qRound(parPos.y()) );
526         p->setPolygon(QPolygonF (pa0));
527         p->setZValue (z);
528         break;
529     case PolyParabel:
530         parabel (pa1, p1x,p1y,p2x+tp.x(),p2y+tp.y());
531         parabel (pa2, p1x,p1y,p2x-tp.x(),p2y-tp.y());
532         pa0.clear();
533         for (int i = 0; i <= arcsegs; i++)
534             pa0 << QPointF (pa1.at(i));
535         for (int i = 0; i<= arcsegs; i++)
536             pa0 << QPointF (pa2.at(arcsegs-i));
537         p->setPolygon(QPolygonF (pa0));
538         p->setZValue (z);
539         break;
540     default:
541         break;
542     }
543 }
544     
545 QPointF LinkableMapObj::getChildRefPos()
546 {
547     return childRefPos;
548 }
549
550 QPointF LinkableMapObj::getFloatRefPos()
551 {
552     return floatRefPos;
553 }
554
555 QPointF LinkableMapObj::getParPos()
556 {
557     return parPos;
558 }
559
560 LinkableMapObj::Orientation LinkableMapObj::getOrientation()
561 {
562     return orientation;
563 }
564
565 void LinkableMapObj::reposition()  // virtual
566 {
567 }
568
569 void LinkableMapObj::requestReposition()   
570 {
571     if (!repositionRequest)
572     {
573         // Pass on the request to parental objects, if this hasn't
574         // been done yet
575         repositionRequest=true;
576         if (parObj) parObj->requestReposition();
577     }
578 }
579
580 void LinkableMapObj::forceReposition()
581 {
582     // Sometimes a reposition has to be done immediatly: For example
583     // if the note editor flag changes, there is no user event in mapeditor
584     // which could collect requests for a reposition.
585     // Then we have to call forceReposition()
586     // But no rule without exception: While loading a map or undoing it,
587     // we want to block expensive repositioning, but just do it once at
588     // the end, thus check first:
589
590     VymModel *model=treeItem->getModel();
591     if (model->isRepositionBlocked()) return;   
592     
593     // Pass on the request to parent objects, if this hasn't been done yet
594     if (parObj) 
595         parObj->forceReposition(); 
596     else 
597         reposition(); 
598 }
599
600 bool LinkableMapObj::repositionRequested()
601 {
602     return repositionRequest;
603 }
604
605 void LinkableMapObj::parabel (QPolygonF &ya, qreal p1x, qreal p1y, qreal p2x, qreal p2y)
606
607 {
608     qreal vx = p2x - p1x;    // V=P2-P1
609     qreal vy = p2y - p1y;
610
611     qreal dx;               // delta x during calculation of parabel
612     
613     qreal pnx;              // next point
614     qreal pny;
615     qreal m;
616
617     if (vx > -0.0001 && vx < 0.0001)
618         m = 0;
619     else    
620         m = (vy / (vx*vx));
621     dx = vx/(arcsegs);
622     ya.clear();
623     ya << QPointF (p1x,p1y);
624     for (int i=1; i <= arcsegs; i++)
625     {   
626         pnx = p1x + dx;
627         pny = m * (pnx - parPos.x()) * (pnx - parPos.x()) + parPos.y();
628         ya << QPointF (pnx, pny);
629         p1x = pnx;
630         p1y = pny;
631     }   
632 }
633