initial import
[vym] / linkablemapobj.cpp
1 #include <math.h>
2
3 #include "linkablemapobj.h"
4 #include "branchobj.h"
5 #include "mapeditor.h"
6
7 /////////////////////////////////////////////////////////////////
8 // LinkableMapObj
9 /////////////////////////////////////////////////////////////////
10
11 LinkableMapObj::LinkableMapObj():MapObj()
12 {
13   //  cout << "Const LinkableMapObj ()\n";
14     init ();
15 }
16
17 LinkableMapObj::LinkableMapObj(QGraphicsScene* s) :MapObj(s)
18 {
19 //    cout << "Const LinkableMapObj (s)\n";
20     init ();
21 }
22
23 LinkableMapObj::LinkableMapObj (LinkableMapObj* lmo) : MapObj (lmo->scene)
24 {
25     copy (lmo);
26 }
27
28 LinkableMapObj::~LinkableMapObj()
29 {
30     delete (bottomline);
31         delLink();
32 }
33
34 void LinkableMapObj::delLink()
35 {
36         switch (style)
37         {
38                 case Line:
39                         delete (l);
40                         break;
41                 case Parabel:
42                         while (!segment.isEmpty()) delete segment.takeFirst();
43                         break;
44                 case PolyLine:
45                         delete (p);
46                         break;
47                 case PolyParabel:
48                         delete (p);
49                         break;
50                 default:
51                         break;
52         }               
53 }
54
55 void LinkableMapObj::init ()
56 {
57     depth=-1;   
58         mapEditor=NULL;
59     childObj=NULL;
60     parObj=NULL;
61     parObjTmpBuf=NULL;
62     parPos=QPointF(0,0);
63     childPos=QPointF(0,0);
64         link2ParPos=false;
65     l=NULL;
66     orientation=UndefinedOrientation;
67     linkwidth=20;               
68         thickness_start=8;
69     style=UndefinedStyle;
70         linkpos=Bottom;
71     arcsegs=13;
72     
73 // TODO instead of linkcolor pen.color() could be used  all around
74         pen.setWidth (1);
75         pen.setColor (linkcolor);
76         pen.setCapStyle ( Qt::RoundCap );
77         bottomline=scene->addLine(QLineF(1,1,1,1),pen);
78     bottomline->setZValue(Z_LINK);
79     bottomline->show();
80
81     // Prepare showing the selection of a MapObj
82     selected=false;
83
84         hideLinkUnselected=false;
85
86         topPad=botPad=leftPad=rightPad=0;
87
88         repositionRequest=false;
89
90         // Rel Positions
91         relPos=QPointF(0,0);
92         useRelPos=false;
93         useOrientation=true;
94
95         // Reset ID
96         objID="";
97 }
98
99 void LinkableMapObj::copy (LinkableMapObj* other)
100 {
101     MapObj::copy(other);
102         bboxTotal=other->bboxTotal;
103     setLinkStyle(other->style);
104     setLinkColor (other->linkcolor);
105         relPos=other->relPos;
106         useOrientation=other->useOrientation;
107         objID=other->objID;
108 }
109
110 void LinkableMapObj::setChildObj(LinkableMapObj* o)
111 {
112     childObj=o;
113 }
114
115 void LinkableMapObj::setParObj(LinkableMapObj* o)
116 {
117     parObj=o;
118         mapEditor=parObj->getMapEditor();
119 }
120
121 void LinkableMapObj::setParObjTmp(LinkableMapObj*,QPointF,int)
122 {
123 }
124
125 void LinkableMapObj::unsetParObjTmp()
126 {
127 }
128
129 bool LinkableMapObj::hasParObjTmp()
130 {
131         if (parObjTmpBuf) return true;
132         return false;
133 }
134
135 void LinkableMapObj::setUseRelPos (const bool &b)
136 {
137         useRelPos=b;
138 }
139
140 void LinkableMapObj::setRelPos()
141 {
142         if (parObj)
143         {       
144                 relPos.setX (absPos.x() - parObj->getChildPos().x() );
145                 relPos.setY (absPos.y() - parObj->getChildPos().y() );
146                 parObj->calcBBoxSize();
147         }       
148 }
149
150 void LinkableMapObj::setRelPos(const QPointF &p)
151 {
152         relPos=p;
153         if (parObj)
154         {               
155                 parObj->calcBBoxSize();
156                 requestReposition();
157         }
158 }
159
160 QPointF LinkableMapObj::getRelPos()
161 {
162         if (!parObj) return QPointF();
163         return relPos;
164 }
165
166 qreal LinkableMapObj::getTopPad()
167 {
168         return topPad;
169 }
170
171 qreal LinkableMapObj::getLeftPad()
172 {
173         return leftPad;
174 }
175
176 qreal LinkableMapObj::getRightPad()
177 {
178         return rightPad;
179 }
180
181 LinkableMapObj::Style LinkableMapObj::getDefLinkStyle ()
182 {
183         if (!mapEditor) return UndefinedStyle;
184         Style ls=mapEditor->getMapLinkStyle();
185         switch (ls)
186         {
187                 case Line: 
188                         return ls;
189                         break;
190                 case Parabel:
191                         return ls;
192                         break;
193                 case PolyLine:  
194                         if (depth>1)
195                                 return Line;
196                         else    
197                                 return ls;
198                         break;
199                 case PolyParabel:       
200                         if (depth>1)
201                                 return Parabel;
202                         else    
203                                 return ls;
204                         break;
205                 default: 
206                         break;  
207         }       
208         return UndefinedStyle;
209 }
210
211 void LinkableMapObj::setLinkStyle(Style newstyle)
212 {
213         //if (newstyle=style) return;
214         delLink();
215                 
216         style=newstyle;
217
218     if (childObj!=NULL && parObj != NULL)
219     {
220                 QGraphicsLineItem *cl;
221                 switch (style)
222                 {
223                         case UndefinedStyle:
224                                 bottomline->hide();
225                                 break;
226                         case Line: 
227                                 l = scene->addLine(QLineF(1,1,1,1),pen);
228                                 l->setZValue(Z_LINK);
229                                 if (visible)
230                                         l->show();
231                                 else
232                                         l->hide();
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(Z_LINK);
239                                         if (visible)
240                                                 cl->show();
241                                         else
242                                                 cl->hide();
243                                         segment.append(cl);
244                                 }
245                                 pa0.resize (arcsegs+1);
246                                 break;
247                         case PolyLine:  
248                                 p =scene->addPolygon(QPolygonF(),pen,linkcolor);
249                                 p->setZValue(Z_LINK);
250                                 if (visible)
251                                         p->show();
252                                 else
253                                         p->hide();
254                                 pa0.resize (3);
255                                 break;
256                         case PolyParabel:       
257                                 p = scene->addPolygon(QPolygonF(),pen,linkcolor);
258                                 p->setZValue(Z_LINK);
259                                 if (visible)
260                                         p->show();
261                                 else
262                                         p->hide();
263                                 pa0.resize (arcsegs*2+2);
264                                 pa1.resize (arcsegs+1);
265                                 pa2.resize (arcsegs+1);
266                                 break;
267                         default: 
268                                 break;  
269                 }       
270         } 
271 }
272
273 LinkableMapObj::Style LinkableMapObj::getLinkStyle()
274 {
275         return style;
276 }
277
278 void LinkableMapObj::setHideLinkUnselected(bool b)
279 {
280         hideLinkUnselected=b;
281         setVisibility (visible);
282         updateLink();
283 }
284
285 bool LinkableMapObj::getHideLinkUnselected()
286 {
287         return hideLinkUnselected;
288 }
289
290 void LinkableMapObj::setLinkPos(Position lp)
291 {
292         linkpos=lp;
293 }
294
295 LinkableMapObj::Position LinkableMapObj::getLinkPos()
296 {
297         return linkpos;
298 }
299
300 void LinkableMapObj::setID (const QString &s)
301 {
302         objID=s;
303 }
304
305 QString LinkableMapObj::getID()
306 {
307         return objID;
308 }
309
310 void LinkableMapObj::setLinkColor()
311 {
312         // Overloaded in BranchObj and childs
313         // here only set default color
314         if (mapEditor)
315                 setLinkColor (mapEditor->getMapDefLinkColor());
316 }
317
318 void LinkableMapObj::setLinkColor(QColor col)
319 {
320         linkcolor=col;
321         pen.setColor(col);
322     bottomline->setPen( pen );
323         switch (style)
324         {
325                 case Line:
326                         l->setPen( pen);
327                         break;  
328                 case Parabel:   
329                         for (int i=0; i<segment.size(); ++i)
330                                 segment.at(i)->setPen( pen);
331                         break;
332                 case PolyLine:
333                         p->setBrush( QBrush(col));
334                         p->setPen( pen);
335                         break;
336                 case PolyParabel:       
337                         p->setBrush( QBrush(col));
338                         p->setPen( pen);
339                         break;
340                 default:
341                         break;
342         } // switch (style)     
343 }
344
345 QColor LinkableMapObj::getLinkColor()
346 {
347         return linkcolor;
348 }
349
350 void LinkableMapObj::setVisibility (bool v)
351 {
352         MapObj::setVisibility (v);
353         bool visnow=visible;
354
355         // We can hide the link, while object is not selected
356         if (hideLinkUnselected && !selected)
357                 visnow=false;
358
359         if (visnow) 
360         {
361                 bottomline->show();
362                 switch (style)
363                 {
364                         case Line:
365                                 if (l) l->show();
366                                 break;
367                         case Parabel:   
368                                 for (int i=0; i<segment.size(); ++i)
369                                         segment.at(i)->show();
370                                 break;  
371                         case PolyLine:
372                                 if (p) p->show();
373                                 break;
374                         case PolyParabel:       
375                                 if (p) p->show();
376                                 break;
377                         default:
378                                 break;
379                 }
380         } else 
381         {
382                 bottomline->hide();
383                 switch (style)
384                 {
385                         case Line:
386                                 if (l) l->hide();
387                                 break;
388                         case Parabel:   
389                                 for (int i=0; i<segment.size(); ++i)
390                                         segment.at(i)->hide();
391                                 break;  
392                         case PolyLine:
393                                 if (p) p->hide();
394                                 break;
395                         case PolyParabel:       
396                                 if (p) p->hide();
397                                 break;
398                         default:
399                                 break;
400                 }
401         }       
402 }
403
404 void LinkableMapObj::setOrientation()
405 {
406         Orientation orientOld=orientation;
407
408         if (!parObj) 
409         {
410                 orientation=UndefinedOrientation;
411                 return;
412         }
413                 
414     // Set orientation, first look for orientation of parent
415     if (parObj->getOrientation() != UndefinedOrientation ) 
416                 // use the orientation of the parent:
417                 orientation=parObj->getOrientation();
418     else
419     {
420                 // calc orientation depending on position rel to parent
421                 if (absPos.x() < QPointF(parObj->getChildPos() ).x() )
422                         orientation=LeftOfCenter; 
423                 else
424                         orientation=RightOfCenter;
425     }
426         if (orientOld!=orientation) requestReposition();
427 }
428
429 void LinkableMapObj::updateLink()
430 {
431     // needs:
432     //  childPos of parent
433     //  orient   of parent
434     //  style
435     // 
436     // sets:
437     //  orientation
438     //  childPos        (by calling setDockPos())
439     //  parPos          (by calling setDockPos())
440         //  bottomlineY
441     //  drawing of the link itself
442
443         // updateLink is called from move, but called from constructor we don't
444         // have parents yet...
445         if (style==UndefinedStyle) return;      
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                         break;
455         }
456         
457     double p2x,p2y;                                                             // Set P2 Before setting
458         if (!link2ParPos)
459         {
460                 p2x=QPointF( parObj->getChildPos() ).x();       // P1, we have to look at
461                 p2y=QPointF( parObj->getChildPos() ).y();       // orientation
462         } else  
463         {
464                 p2x=QPointF( parObj->getParPos() ).x(); 
465                 p2y=QPointF( parObj->getParPos() ).y();
466         } 
467
468         setDockPos(); // Call overloaded method
469         setOrientation();
470
471         double p1x=parPos.x();  // Link is drawn from P1 to P2
472         double p1y=parPos.y();
473
474         double vx=p2x - p1x;    // V=P2-P1
475         double vy=p2y - p1y;
476
477         // Draw the horizontal line below heading (from ChildPos to ParPos)
478         //bottomline->prepareGeometryChange();
479         bottomline->setLine (QLine (qRound(childPos.x()),
480                 qRound(childPos.y()),
481                 qRound(p1x),
482                 qRound(p1y) ));
483
484         double a;       // angle
485         if (vx > -0.000001 && vx < 0.000001)
486                 a=M_PI_2;
487         else
488                 a=atan( vy / vx );
489         // "turning point" for drawing polygonal links
490         QPointF tp (-qRound(sin (a)*thickness_start), qRound(cos (a)*thickness_start)); 
491         
492     // Draw the link
493         switch (style)
494         {
495                 case Line:
496                         //l->prepareGeometryChange();
497                         l->setLine( QLine(qRound (parPos.x()),
498                                 qRound(parPos.y()),
499                                 qRound(p2x),
500                                 qRound(p2y) ));
501                         break;  
502                 case Parabel:   
503                         parabel (pa0, p1x,p1y,p2x,p2y);
504                         for (int i=0; i<segment.size(); ++i)
505                         {
506                                 //segment.at(i)->prepareGeometryChange();
507                                 segment.at(i)->setLine(QLineF( pa0.at(i).x(), pa0.at(i).y(),pa0.at(i+1).x(),pa0.at(i+1).y()));
508                         }       
509                         break;
510                 case PolyLine:
511                         pa0.clear();
512                         pa0<<QPointF (qRound(p2x+tp.x()), qRound(p2y+tp.y()));
513                         pa0<<QPointF (qRound(p2x-tp.x()), qRound(p2y-tp.y()));
514                         pa0<<QPointF (qRound (parPos.x()), qRound(parPos.y()) );
515                         //p->prepareGeometryChange();
516                         p->setPolygon(QPolygonF (pa0));
517                         break;
518                 case PolyParabel:       
519                         parabel (pa1, p1x,p1y,p2x+tp.x(),p2y+tp.y());
520                         parabel (pa2, p1x,p1y,p2x-tp.x(),p2y-tp.y());
521                         pa0.clear();
522                         for (int i=0;i<=arcsegs;i++)
523                                 pa0 << QPointF (pa1.at(i));
524                         for (int i=0;i<=arcsegs;i++)
525                                 pa0 << QPointF (pa2.at(arcsegs-i));
526                         //p->prepareGeometryChange();
527                         p->setPolygon(QPolygonF (pa0));
528                         break;
529                 default:
530                         break;
531         } // switch (style)     
532 }
533         
534 LinkableMapObj* LinkableMapObj::getChildObj()
535 {
536     return childObj;
537 }
538
539 LinkableMapObj* LinkableMapObj::getParObj()
540 {
541     return parObj;
542 }
543
544 LinkableMapObj* LinkableMapObj::findObjBySelect (QString s)
545 {
546         LinkableMapObj *lmo=this;
547         QString part;
548         QString typ;
549         QString num;
550         while (!s.isEmpty() )
551         {
552                 part=s.section(",",0,0);
553                 typ=part.left (3);
554                 num=part.right(part.length() - 3);
555                 if (typ=="mc:")
556                 {
557                         if (depth>0)
558                                 return false;   // in a subtree there is no center
559                         else
560                                 break;
561                 } else
562                         if (typ=="bo:")
563                                 lmo=((BranchObj*)lmo)->getBranchNum (num.toInt());
564                         else
565                                 if (typ=="fi:")
566                                         lmo=((BranchObj*)lmo)->getFloatImageNum (num.toUInt());
567                 if (!lmo) break;
568                 
569                 if (s.contains(","))
570                         s=s.right(s.length() - part.length() -1 );
571                 else    
572                         break;
573         }
574         return lmo;
575 }
576
577 QPointF LinkableMapObj::getChildPos()
578 {
579     return childPos;
580 }
581
582 QPointF LinkableMapObj::getParPos()
583 {
584     return parPos;
585 }
586
587 void LinkableMapObj::setUseOrientation (const bool &b)
588 {       
589         if (useOrientation!=b)
590         {
591                 useOrientation=b;
592                 requestReposition();
593         }       
594 }
595
596 LinkableMapObj::Orientation LinkableMapObj::getOrientation()
597 {
598     return orientation;
599 }
600
601 int LinkableMapObj::getDepth()
602 {
603     return depth;
604 }
605
606 void LinkableMapObj::setMapEditor (MapEditor *me)
607 {
608         mapEditor=me;
609 }
610
611 MapEditor* LinkableMapObj::getMapEditor ()
612 {
613         return mapEditor;
614 }
615
616 QPointF LinkableMapObj::getRandPos()
617 {
618         // Choose a random position with given distance to parent:
619         double a=rand()%360 * 2 * M_PI / 360;
620     return QPointF ( (int)( + 150*cos (a)),
621                     (int)( + 150*sin (a)));
622 }
623
624 void LinkableMapObj::reposition()
625 {
626 }
627
628 void LinkableMapObj::requestReposition()
629 {
630         if (!repositionRequest)
631         {
632                 // Pass on the request to parental objects, if this hasn't
633                 // been done yet
634                 repositionRequest=true;
635                 if (parObj) parObj->requestReposition();
636         }
637 }
638
639 void LinkableMapObj::forceReposition()
640 {
641         // Sometimes a reposition has to be done immediatly: For example
642         // if the note editor flag changes, there is no user event in mapeditor
643         // which could collect requests for a reposition.
644         // Then we have to call forceReposition()
645         // But no rule without exception: While loading a map or undoing it,
646         // we want to block expensive repositioning, but just do it once at
647         // the end, thus check first:
648
649         if (mapEditor->isRepositionBlocked()) return;
650         
651         // Pass on the request to parental objects, if this hasn't been done yet
652         
653         if (parObj) 
654                 parObj->forceReposition(); 
655         else 
656                 reposition(); 
657 }
658
659 bool LinkableMapObj::repositionRequested()
660 {
661         return repositionRequest;
662 }
663
664
665 void LinkableMapObj::select()
666 {
667         // select and unselect are still needed to
668         // handle hiding of links
669     selected=true;
670         setVisibility (visible);
671 }
672
673
674 void LinkableMapObj::unselect()
675 {
676     selected=false;
677         // Maybe we have to hide the link:
678         setVisibility (visible);
679 }
680
681 void LinkableMapObj::parabel (QPolygonF &ya, double p1x, double p1y, double p2x, double p2y)
682
683 {
684         double vx=p2x - p1x;    // V=P2-P1
685         double vy=p2y - p1y;
686
687         double dx;                              // delta x during calculation of parabel
688         
689         double pnx;                             // next point
690         double pny;
691         double m;
692
693         if (vx > -0.0001 && vx < 0.0001)
694                 m=0;
695         else    
696                 m=(vy / (vx*vx));
697         dx=vx/(arcsegs);
698         ya.clear();
699         ya<<QPointF (p1x,p1y);
700         for (int i=1;i<=arcsegs;i++)
701         {       
702                 pnx=p1x+dx;
703                 pny=m*(pnx-parPos.x())*(pnx-parPos.x())+parPos.y();
704                 ya<<QPointF (pnx,pny);
705                 p1x=pnx;
706                 p1y=pny;
707         }       
708 }
709
710 QString LinkableMapObj::getLinkAttr ()
711 {
712         if (hideLinkUnselected)
713                 return attribut ("hideLink","true");
714         else
715                 return attribut ("hideLink","false");
716         
717 }
718