]> git.sven.stormbind.net Git - sven/vym.git/blob - src/xlinkobj.cpp
Replace Pierre as the maintainer
[sven/vym.git] / src / xlinkobj.cpp
1 #include <QDebug>
2
3 #include "xlinkobj.h"
4
5 #include "branchitem.h"
6 #include "branchobj.h"
7 #include "math.h" // atan
8 #include "misc.h" // max
9
10 /////////////////////////////////////////////////////////////////
11 // XLinkObj
12 /////////////////////////////////////////////////////////////////
13
14 int XLinkObj::arrowSize = 6; // make instances
15 int XLinkObj::clickBorder = 8;
16 int XLinkObj::pointRadius = 10;
17 int XLinkObj::d_control = 300;
18
19 XLinkObj::XLinkObj(QGraphicsItem *parent, Link *l) : MapObj(parent)
20 {
21     // qDebug()<< "Const XLinkObj (parent,Link)";
22     link = l;
23     init();
24 }
25
26 XLinkObj::~XLinkObj()
27 {
28     // qDebug() << "Destr XLinkObj";
29     delete (poly);
30     delete (path);
31     delete (ctrl_p0);
32     delete (ctrl_p1);
33     delete (pointerEnd);
34     delete (pointerBegin);
35 }
36
37 void XLinkObj::init()
38 {
39     visBranch = NULL;
40
41     stateVis = Hidden;
42
43     QPen pen = link->getPen();
44
45     path = scene()->addPath(QPainterPath(), pen, Qt::NoBrush);
46     path->setZValue(dZ_XLINK);
47
48     pointerBegin = new ArrowObj(this);
49     pointerBegin->setPen(pen);
50     pointerBegin->setUseFixedLength(true);
51     pointerBegin->setFixedLength(0);
52
53     pointerEnd = new ArrowObj(this);
54     pointerEnd->setPen(pen);
55     pointerEnd->setUseFixedLength(true);
56     pointerEnd->setFixedLength(0);
57
58     pen.setStyle(Qt::SolidLine);
59     poly = scene()->addPolygon(QPolygonF(), pen, pen.color());
60     poly->setZValue(dZ_XLINK);
61
62     // Control points for bezier path
63     // (We have at least a begin branch, consider its orientation)
64     initC0();
65     initC1();
66
67     ctrl_p0 = scene()->addEllipse(c0.x(), c0.y(), clickBorder * 2,
68                                   clickBorder * 2, pen, pen.color());
69     ctrl_p1 = scene()->addEllipse(c1.x(), c1.y(), clickBorder * 2,
70                                   clickBorder * 2, pen, pen.color());
71
72     beginOrient = endOrient = LinkableMapObj::UndefinedOrientation;
73     pen.setWidth(1);
74     pen.setStyle(Qt::DashLine);
75
76     curSelection = Unselected;
77
78     setVisibility(true);
79 }
80
81 QPointF XLinkObj::getAbsPos()
82 {
83     switch (curSelection) {
84     case C0:
85         return c0;
86         break;
87     case C1:
88         return c1;
89         break;
90     default:
91         return QPointF();
92         break;
93     }
94 }
95
96 void XLinkObj::setStyleBegin(const QString &s) { pointerBegin->setStyleEnd(s); }
97
98 void XLinkObj::setStyleBegin(ArrowObj::OrnamentStyle os)
99 {
100     pointerBegin->setStyleEnd(os);
101 }
102
103 ArrowObj::OrnamentStyle XLinkObj::getStyleBegin()
104 {
105     return pointerBegin->getStyleEnd();
106 }
107
108 void XLinkObj::setStyleEnd(const QString &s) { pointerEnd->setStyleEnd(s); }
109
110 void XLinkObj::setStyleEnd(ArrowObj::OrnamentStyle os)
111 {
112     pointerEnd->setStyleEnd(os);
113 }
114
115 ArrowObj::OrnamentStyle XLinkObj::getStyleEnd()
116 {
117     return pointerEnd->getStyleEnd();
118 }
119
120 QPointF XLinkObj::getBeginPos() { return beginPos; }
121
122 QPointF XLinkObj::getEndPos() { return endPos; }
123
124 void XLinkObj::move(QPointF p)
125 {
126     switch (curSelection) {
127     case C0:
128         c0 = p;
129         break;
130     case C1:
131         c1 = p;
132         break;
133     default:
134         break;
135     }
136     updateXLink();
137 }
138
139 void XLinkObj::setEnd(QPointF p) { endPos = p; }
140
141 void XLinkObj::setSelection(CurrentSelection s)
142 {
143     curSelection = s;
144     setVisibility();
145 }
146
147 void XLinkObj::setSelection(int cp)
148 {
149     if (cp == 0)
150         setSelection(C0);
151     else if (cp == 1)
152         setSelection(C1);
153     else
154         qWarning() << "XLO::setSelection cp=" << cp;
155 }
156
157 void XLinkObj::updateXLink()
158 {
159     QPointF a, b;
160     QPolygonF pa;
161
162     BranchObj *beginBO = NULL;
163     BranchObj *endBO = NULL;
164     BranchItem *bi = link->getBeginBranch();
165     if (bi)
166         beginBO = (BranchObj *)(bi->getLMO());
167     bi = link->getEndBranch();
168     if (bi)
169         endBO = (BranchObj *)(bi->getLMO());
170
171     if (beginBO) {
172         if (beginOrient != LinkableMapObj::UndefinedOrientation &&
173             beginOrient != beginBO->getOrientation())
174             c0.setX(-c0.x());
175         beginOrient = beginBO->getOrientation();
176     }
177     if (endBO) {
178         if (endOrient != LinkableMapObj::UndefinedOrientation &&
179             endOrient != endBO->getOrientation())
180             c1.setX(-c1.x());
181         endOrient = endBO->getOrientation();
182     }
183
184     if (visBranch) {
185         // Only one of the linked branches is visible
186         // Draw arrowhead   //FIXME-3 missing shaft of arrow
187         BranchObj *bo = (BranchObj *)(visBranch->getLMO());
188         if (!bo)
189             return;
190
191         a = b = bo->getChildRefPos();
192
193         if (bo->getOrientation() == LinkableMapObj::RightOfCenter) {
194             b.setX(b.x() + 2 * arrowSize);
195             pa.clear();
196             pa << a << b << QPointF(b.x(), b.y() - arrowSize)
197                << QPointF(b.x() + arrowSize, b.y())
198                << QPointF(b.x(), b.y() + arrowSize) << b << a;
199             poly->setPolygon(pa);
200         }
201         else {
202             b.setX(b.x() - 2 * arrowSize);
203             pa.clear();
204             pa << a << b << QPointF(b.x(), b.y() - arrowSize)
205                << QPointF(b.x() - arrowSize, b.y())
206                << QPointF(b.x(), b.y() + arrowSize) << b << a;
207             poly->setPolygon(pa);
208         }
209     }
210     else {
211         // Both linked branches are visible
212
213         // If a link is just drawn in the editor,
214         // we have already a beginBranch
215         if (beginBO)
216             beginPos = beginBO->getChildRefPos();
217         if (endBO)
218             endPos = endBO->getChildRefPos();
219
220         if (beginBO && endBO) {
221             pointerBegin->move(beginPos + c0);
222             pointerBegin->setEndPoint(beginPos);
223
224             pointerEnd->move(endPos + c1);
225             pointerEnd->setEndPoint(endPos);
226         }
227     }
228
229     // Update control points for bezier
230     QPainterPath p(beginPos);
231     p.cubicTo(beginPos + c0, endPos + c1, endPos);
232
233     clickPath = p;
234     path->setPath(p);
235
236     // Go back to create closed curve,
237     // needed for intersection check:
238     clickPath.cubicTo(endPos + c1, beginPos + c0, beginPos);
239
240     QPen pen = link->getPen();
241     path->setPen(pen);
242     poly->setBrush(pen.color());
243
244     pointerBegin->setPen(pen);
245     pointerEnd->setPen(pen);
246
247     pen.setStyle(Qt::SolidLine);
248
249     ctrl_p0->setRect(beginPos.x() + c0.x() - pointRadius / 2,
250                      beginPos.y() + c0.y() - pointRadius / 2, pointRadius,
251                      pointRadius);
252     ctrl_p0->setPen(pen);
253     ctrl_p0->setBrush(pen.color());
254
255     ctrl_p1->setRect(endPos.x() + c1.x() - pointRadius / 2,
256                      endPos.y() + c1.y() - pointRadius / 2, pointRadius,
257                      pointRadius);
258     ctrl_p1->setPen(pen);
259     ctrl_p1->setBrush(pen.color());
260
261     BranchItem *bi_begin = link->getBeginBranch();
262     BranchItem *bi_end = link->getEndBranch();
263     if (bi_begin && bi_end && link->getState() == Link::activeXLink)
264         // Note: with MapObj being a GraphicsItem now, maybe better reparent the
265         // xlinkobj line->setZValue (dZ_DEPTH *
266         // max(bi_begin->depth(),bi_end->depth()) + dZ_XLINK);
267         path->setZValue(dZ_XLINK);
268     else
269         path->setZValue(dZ_XLINK);
270
271     setVisibility();
272 }
273
274 void XLinkObj::positionBBox() {}
275
276 void XLinkObj::calcBBoxSize() {}
277
278 void XLinkObj::setVisibility(bool b)
279 {
280     if (stateVis == FullShowControls) {
281         ctrl_p0->show();
282         ctrl_p1->show();
283         pointerBegin->setUseFixedLength(false);
284         pointerEnd->setUseFixedLength(false);
285     }
286     else {
287         ctrl_p0->hide();
288         ctrl_p1->hide();
289         pointerBegin->setUseFixedLength(true);
290         pointerBegin->setFixedLength(0);
291         pointerEnd->setUseFixedLength(true);
292         pointerEnd->setFixedLength(0);
293     }
294
295     MapObj::setVisibility(b);
296     if (b) {
297         if (stateVis == OnlyBegin) {
298             path->hide();
299             poly->show();
300             pointerBegin->hide();
301             pointerEnd->hide();
302         }
303         else if (stateVis == OnlyEnd) {
304             path->hide();
305             poly->show();
306             pointerBegin->hide();
307             pointerEnd->hide();
308         }
309         else {
310             path->show();
311             poly->hide();
312             pointerBegin->show();
313             pointerEnd->show();
314         }
315     }
316     else {
317         poly->hide();
318         path->hide();
319         pointerBegin->hide();
320         pointerEnd->hide();
321     }
322 }
323
324 void XLinkObj::setVisibility()
325 {
326     BranchItem *beginBI = link->getBeginBranch();
327     BranchObj *beginBO = NULL;
328     if (beginBI)
329         beginBO = (BranchObj *)(beginBI->getLMO());
330
331     BranchObj *endBO = NULL;
332     BranchItem *endBI = link->getEndBranch();
333     if (endBI)
334         endBO = (BranchObj *)(endBI->getLMO());
335     if (beginBO && endBO) {
336         if (beginBO->isVisibleObj() &&
337             endBO->isVisibleObj()) { // Both ends are visible
338             visBranch = NULL;
339             if (curSelection != Unselected)
340                 stateVis = FullShowControls;
341             else
342                 stateVis = Full;
343             setVisibility(true);
344         }
345         else {
346             if (!beginBO->isVisibleObj() &&
347                 !endBO->isVisibleObj()) { // None of the ends is visible
348                 visBranch = NULL;
349                 stateVis = Hidden;
350                 setVisibility(false);
351             }
352             else { // Just one end is visible, draw a symbol that shows
353                 // that there is a link to a scrolled branch
354                 if (beginBO->isVisibleObj()) {
355                     stateVis = OnlyBegin;
356                     visBranch = beginBI;
357                 }
358                 else {
359                     visBranch = endBI;
360                     stateVis = OnlyEnd;
361                 }
362                 setVisibility(true);
363             }
364         }
365     }
366 }
367
368 void XLinkObj::initC0()
369 {
370     if (!link)
371         return;
372     BranchItem *beginBranch = link->getBeginBranch();
373     if (!beginBranch)
374         return;
375     BranchObj *bo = beginBranch->getBranchObj();
376     if (!bo)
377         return;
378     if (bo->getOrientation() == LinkableMapObj::RightOfCenter)
379         c0 = QPointF(d_control, 0);
380     else
381         c0 = QPointF(-d_control, 0);
382 }
383
384 void XLinkObj::initC1()
385 {
386     if (!link)
387         return;
388     BranchItem *endBranch = link->getEndBranch();
389     if (!endBranch)
390         return;
391     BranchObj *bo = endBranch->getBranchObj();
392     if (!bo)
393         return;
394     if (bo->getOrientation() == LinkableMapObj::RightOfCenter)
395         c1 = QPointF(d_control, 0);
396     else
397         c1 = QPointF(-d_control, 0);
398 }
399
400 void XLinkObj::setC0(const QPointF &p) { c0 = p; }
401
402 QPointF XLinkObj::getC0() { return c0; }
403
404 void XLinkObj::setC1(const QPointF &p) { c1 = p; }
405
406 QPointF XLinkObj::getC1() { return c1; }
407
408 int XLinkObj::ctrlPointInClickBox(const QPointF &p)
409 {
410     CurrentSelection oldSel = curSelection;
411     int ret = -1;
412
413     QRectF r(p.x() - clickBorder, p.y() - clickBorder, clickBorder * 2,
414              clickBorder * 2);
415
416     if (curSelection == C0 || curSelection == C1) {
417         // If Cx selected, check both ctrl points
418         curSelection = C0;
419         if (getClickPath().intersects(r))
420             ret = 0;
421         curSelection = C1;
422         if (getClickPath().intersects(r))
423             ret = 1;
424     }
425     curSelection = oldSel;
426     return ret;
427 }
428
429 bool XLinkObj::isInClickBox(const QPointF &p)
430 {
431     // Return, if not visible at all...
432     if (stateVis == Hidden)
433         return false;
434
435     CurrentSelection oldSel = curSelection;
436     bool b = false;
437
438     QRectF r(p.x() - clickBorder, p.y() - clickBorder, clickBorder * 2,
439              clickBorder * 2);
440
441     switch (stateVis) {
442     case FullShowControls:
443         // If Cx selected, check both ctrl points
444         if (ctrlPointInClickBox(p) > -1)
445             b = true;
446
447         // Enable selecting the path, when a ctrl point is already selected
448         if (!b && curSelection != Unselected && clickPath.intersects(r))
449             b = true;
450         break;
451     case OnlyBegin:
452     case OnlyEnd:
453         // not selected, only partially visible
454         if (poly->boundingRect().contains(p))
455             b = true;
456         break;
457     default:
458         // not selected, but path is fully visible
459         curSelection = Path;
460         if (getClickPath().intersects(r))
461             b = true;
462         break;
463     }
464     curSelection = oldSel;
465     return b;
466 }
467
468 QPainterPath
469 XLinkObj::getClickPath() // also needs mirroring if oriented left. Create method
470                          // to generate the coordinates
471 {
472     QPainterPath p;
473     switch (curSelection) {
474     case C0:
475         p.addEllipse(beginPos + c0, 15, 15);
476         return p;
477         break;
478     case C1:
479         p.addEllipse(endPos + c1, 15, 15);
480         return p;
481         break;
482     default:
483         return clickPath;
484         break;
485     }
486 }
487
488 QPainterPath XLinkObj::getSelectionPath()
489 {
490     return getClickPath();
491 }