1 /*
2     TikZiT - a GUI diagram editor for TikZ
3     Copyright (C) 2018 Aleks Kissinger
4 
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 
19 #include "tikzit.h"
20 #include "util.h"
21 #include "tikzscene.h"
22 #include "undocommands.h"
23 #include "tikzassembler.h"
24 
25 #include <QPen>
26 #include <QBrush>
27 #include <QDebug>
28 #include <QClipboard>
29 #include <QInputDialog>
30 #include <QMessageBox>
31 #include <cmath>
32 #include <delimitedstringvalidator.h>
33 #include <QSettings>
34 
35 
TikzScene(TikzDocument * tikzDocument,ToolPalette * tools,StylePalette * styles,QObject * parent)36 TikzScene::TikzScene(TikzDocument *tikzDocument, ToolPalette *tools,
37                      StylePalette *styles, QObject *parent) :
38     QGraphicsScene(parent), _tikzDocument(tikzDocument), _tools(tools), _styles(styles)
39 {
40     _modifyEdgeItem = nullptr;
41     _edgeStartNodeItem = nullptr;
42     _drawNodeLabels = true;
43     _drawEdgeItem = new QGraphicsLineItem();
44     _rubberBandItem = new QGraphicsRectItem();
45     _enabled = true;
46     //setSceneRect(-310,-230,620,450);
47     //setSceneRect(-2000,-1500,4000,3000);
48     refreshSceneBounds();
49 
50     QPen pen;
51     pen.setColor(QColor::fromRgbF(0.5, 0.0, 0.5));
52     //pen.setWidth(3.0f);
53     pen.setCosmetic(true);
54     _drawEdgeItem->setPen(pen);
55     _drawEdgeItem->setLine(0,0,0,0);
56     _drawEdgeItem->setVisible(false);
57     addItem(_drawEdgeItem);
58 
59     pen.setColor(QColor::fromRgbF(0.6, 0.6, 0.8));
60     //pen.setWidth(3.0f);
61     //QVector<qreal> dash;
62     //dash << 4.0 << 4.0;
63     pen.setStyle(Qt::DashLine);
64     //pen.setDashPattern(dash);
65     _rubberBandItem->setPen(pen);
66 
67     QBrush brush(QColor::fromRgbF(0.6,0.6,0.8,0.2));
68     _rubberBandItem->setBrush(brush);
69 
70     _rubberBandItem->setVisible(false);
71     addItem(_rubberBandItem);
72 
73     _highlightHeads = false;
74     _highlightTails = false;
75 }
76 
~TikzScene()77 TikzScene::~TikzScene() {
78 }
79 
graph()80 Graph *TikzScene::graph()
81 {
82     return _tikzDocument->graph();
83 }
84 
graphReplaced()85 void TikzScene::graphReplaced()
86 {
87 
88 	foreach (NodeItem *ni, _nodeItems) {
89         removeItem(ni);
90         delete ni;
91     }
92     _nodeItems.clear();
93 
94     foreach (EdgeItem *ei, _edgeItems) {
95         removeItem(ei);
96         delete ei;
97     }
98     _edgeItems.clear();
99 
100     foreach (PathItem *pi, _pathItems) {
101         removeItem(pi);
102         delete pi;
103     }
104     _pathItems.clear();
105 
106     foreach (Edge *e, graph()->edges()) {
107 		//e->attachStyle();
108         //e->updateControls();
109         EdgeItem *ei = new EdgeItem(e);
110         _edgeItems.insert(e, ei);
111         addItem(ei);
112 
113         Path *p = e->path();
114         if (p && p->edges().first() == e) {
115             PathItem *pi = new PathItem(p);
116             _pathItems.insert(p, pi);
117             addItem(pi);
118         }
119     }
120 
121     foreach (Node *n, graph()->nodes()) {
122         //n->attachStyle();
123         NodeItem *ni = new NodeItem(n);
124         _nodeItems.insert(n, ni);
125         addItem(ni);
126     }
127 
128     refreshZIndices();
129     refreshSceneBounds();
130 }
131 
extendSelectionUp()132 void TikzScene::extendSelectionUp()
133 {
134     bool found = false;
135     qreal m = 0.0;
136     foreach (Node *n, getSelectedNodes()) {
137         if (!found) {
138             m = n->point().y();
139             found = true;
140         } else {
141             if (n->point().y() > m) m = n->point().y();
142         }
143     }
144 
145     foreach (NodeItem *ni, nodeItems().values()) {
146         if (ni->node()->point().y() >= m) ni->setSelected(true);
147     }
148 }
149 
extendSelectionDown()150 void TikzScene::extendSelectionDown()
151 {
152     bool found = false;
153     qreal m = 0.0;
154     foreach (Node *n, getSelectedNodes()) {
155         if (!found) {
156             m = n->point().y();
157             found = true;
158         } else {
159             if (n->point().y() < m) m = n->point().y();
160         }
161     }
162 
163     foreach (NodeItem *ni, nodeItems().values()) {
164         if (ni->node()->point().y() <= m) ni->setSelected(true);
165     }
166 }
167 
extendSelectionLeft()168 void TikzScene::extendSelectionLeft()
169 {
170     bool found = false;
171     qreal m = 0.0;
172     foreach (Node *n, getSelectedNodes()) {
173         if (!found) {
174             m = n->point().x();
175             found = true;
176         } else {
177             if (n->point().x() < m) m = n->point().x();
178         }
179     }
180 
181     foreach (NodeItem *ni, nodeItems().values()) {
182         if (ni->node()->point().x() <= m) ni->setSelected(true);
183     }
184 }
185 
extendSelectionRight()186 void TikzScene::extendSelectionRight()
187 {
188     bool found = false;
189     qreal m = 0.0;
190     foreach (Node *n, getSelectedNodes()) {
191         if (!found) {
192             m = n->point().x();
193             found = true;
194         } else {
195             if (n->point().x() < m) m = n->point().x();
196         }
197     }
198 
199     foreach (NodeItem *ni, nodeItems().values()) {
200         if (ni->node()->point().x() >= m) ni->setSelected(true);
201     }
202 }
203 
mergeNodes()204 void TikzScene::mergeNodes()
205 {
206     refreshZIndices();
207     QSet<Node*> selNodes;
208     QSet<Edge*> selEdges;
209     getSelection(selNodes, selEdges);
210 
211     // build a map from locations to a chosen node at that location
212     QMap<QPair<int,int>,Node*> m;
213     foreach (Node *n, selNodes) {
214         // used fixed precision for hashing/comparing locations
215         QPair<int,int> fpPoint(
216           static_cast<int>(n->point().x() * 1000.0),
217           static_cast<int>(n->point().y() * 1000.0));
218         if (!m.contains(fpPoint) ||
219             _nodeItems[m[fpPoint]]->zValue() < _nodeItems[n]->zValue())
220         {
221             m.insert(fpPoint, n);
222         }
223     }
224 
225     // build a second map from nodes to the node they will be merged with
226     QMap<Node*,Node*> m1;
227     foreach (Node *n, graph()->nodes()) {
228         QPair<int,int> fpPoint(
229           static_cast<int>(n->point().x() * 1000.0),
230           static_cast<int>(n->point().y() * 1000.0));
231         Node *n1 = m[fpPoint];
232         if (n1 != nullptr && n1 != n) m1.insert(n, n1);
233     }
234 
235     _tikzDocument->undoStack()->beginMacro("Merge nodes");
236 
237     // copy adjacent edges from nodes that will be deleted
238     foreach (Edge *e, graph()->edges()) {
239         if (m1.contains(e->source()) || m1.contains(e->target())) {
240             Edge *e1 = e->copy(&m1);
241             AddEdgeCommand *cmd = new AddEdgeCommand(this, e1);
242             _tikzDocument->undoStack()->push(cmd);
243         }
244     }
245 
246     // delete nodes
247     QMap<int,Node*> delNodes;
248     QMap<int,Edge*> delEdges;
249     for (int i = 0; i < _tikzDocument->graph()->nodes().length(); ++i) {
250         Node *n = _tikzDocument->graph()->nodes()[i];
251         if (m1.contains(n)) delNodes.insert(i, n);
252     }
253 
254     QSet<Path*> delPaths;
255     for (int i = 0; i < _tikzDocument->graph()->edges().length(); ++i) {
256         Edge *e = _tikzDocument->graph()->edges()[i];
257         if (m1.contains(e->source()) || m1.contains(e->target())) {
258             delEdges.insert(i, e);
259             if (e->path()) delPaths << e->path();
260         }
261     }
262     _tikzDocument->undoStack()->push(new SplitPathCommand(this, delPaths));
263     _tikzDocument->undoStack()->push(new DeleteCommand(this, delNodes, delEdges,
264                                                        selNodes, selEdges));
265 
266     _tikzDocument->undoStack()->endMacro();
267 }
268 
reorderSelection(bool toFront)269 void TikzScene::reorderSelection(bool toFront)
270 {
271     QVector<Node*> nodeOrd, nodeOrd1;
272     QVector<Edge*> edgeOrd, edgeOrd1;
273     QSet<Node*> selNodes;
274     QSet<Edge*> selEdges;
275     getSelection(selNodes, selEdges);
276     foreach (Node *n, graph()->nodes()) {
277         if (selNodes.contains(n)) nodeOrd1 << n;
278         else nodeOrd << n;
279     }
280 
281     foreach (Edge *e, graph()->edges()) {
282         if (selEdges.contains(e)) edgeOrd1 << e;
283         else edgeOrd << e;
284     }
285 
286     if (toFront) {
287         nodeOrd += nodeOrd1;
288         edgeOrd += edgeOrd1;
289     } else {
290         nodeOrd = nodeOrd1 + nodeOrd;
291         edgeOrd = edgeOrd1 + edgeOrd;
292     }
293 
294     ReorderCommand *cmd = new ReorderCommand(this, graph()->nodes(), nodeOrd, graph()->edges(), edgeOrd);
295     _tikzDocument->undoStack()->push(cmd);
296 }
297 
reverseSelectedEdges()298 void TikzScene::reverseSelectedEdges()
299 {
300     // grab all the edges which are either selected themselves, or where
301     // both their source and target nodes are selected
302     QSet<Edge*> es;
303     foreach (Edge *e, graph()->edges()) {
304         if ((_edgeItems[e] && _edgeItems[e]->isSelected()) ||
305             (_nodeItems[e->source()] && _nodeItems[e->target()] &&
306              _nodeItems[e->source()]->isSelected() &&
307              _nodeItems[e->target()]->isSelected()))
308         {
309             es << e;
310         }
311     }
312 
313     ReverseEdgesCommand *cmd = new ReverseEdgesCommand(this, es);
314     _tikzDocument->undoStack()->push(cmd);
315 }
316 
makePath(bool duplicateEdges)317 void TikzScene::makePath(bool duplicateEdges)
318 {
319     QSet<Node*> selNodes;
320     QSet<Edge*> selEdges;
321     QSet<Edge*> edges;
322     getSelection(selNodes, selEdges);
323 
324     edges = selEdges;
325 
326     // if no edges are selected, try to infer edges from nodes
327     if (edges.isEmpty()) {
328         foreach(Edge *e, graph()->edges()) {
329             if (selNodes.contains(e->source()) && selNodes.contains(e->target()))
330                 edges << e;
331         }
332     }
333 
334     if (edges.size() < 2) {
335         //QMessageBox::warning(nullptr, "Error", "Paths must contain at least 2 edges.");
336         return;
337     }
338 
339     foreach (Edge *e, edges) {
340         if (e->path() != nullptr && !duplicateEdges) {
341             //QMessageBox::warning(nullptr, "Error", "Edges must not already be in another path.");
342             // TODO: maybe we want to automatically split paths if edges are in a path already?
343             return;
344         }
345     }
346 
347     _tikzDocument->undoStack()->beginMacro("Make Path");
348 
349     QVector<Edge *> oldEdgeOrder = graph()->edges();
350     QSet<Edge *> oldEdges, newEdges;
351     oldEdges = edges;
352 
353     if (duplicateEdges) {
354         foreach (Edge *e, edges) {
355             Edge *e1 = e->copy();
356             _tikzDocument->undoStack()->push(new AddEdgeCommand(this, e1, false, selNodes, selEdges));
357             newEdges << e1;
358             oldEdgeOrder << e1;
359         }
360         edges = newEdges;
361     }
362 
363     // try to turn selected edges into one contiguous chain or cycle, recording
364     // which edges need to be flipped.
365 
366     // n.b. this is O(n^2) in path length. This could be optimised by saving
367     // vertex neighbourhoods, but probably doesn't win anything for n < 100.
368 
369     QSet<Edge*> flip;
370     QVector<Edge*> p;
371     int pLen = -1;
372 
373     // keep going as long as 'p' grows
374     while (pLen < p.length()) {
375         pLen = p.length();
376         Edge *e = nullptr;
377         foreach (e, edges) {
378             Node *s = e->source();
379             Node *t = e->target();
380             if (p.isEmpty()) {
381                 p.append(e);
382                 break;
383             }
384 
385             Node *head = (flip.contains(p.first())) ? p.first()->target() : p.first()->source();
386             Node *tail = (flip.contains(p.last())) ? p.last()->source() : p.last()->target();
387 
388             if (s == head || t == head) {
389                 if (s == head) flip << e;
390                 p.prepend(e);
391                 break;
392             }
393 
394             if (s == tail || t == tail) {
395                 if (t == tail) flip << e;
396                 p.append(e);
397                 break;
398             }
399         }
400 
401         if (e) edges.remove(e);
402     }
403 
404     if (!edges.isEmpty()) {
405         QMessageBox::warning(nullptr, "Error", "Selected edges do not form a path.");
406         return;
407     }
408 
409     _tikzDocument->undoStack()->push(new ReverseEdgesCommand(this, flip));
410 
411     // order all of the edges together, and in the case of
412     // duplicate edges, just below the first original.
413     QVector<Edge*> newEdgeOrder;
414     bool firstEdge = true;
415     foreach (Edge *e, oldEdgeOrder) {
416         if (oldEdges.contains(e)) {
417             if (firstEdge) {
418                 newEdgeOrder += p;
419                 firstEdge = false;
420             }
421 
422             if (duplicateEdges) newEdgeOrder << e;
423         } else if (!newEdges.contains(e)) {
424             newEdgeOrder << e;
425         }
426     }
427 
428     _tikzDocument->undoStack()->push(new ReorderCommand(this,
429         graph()->nodes(), graph()->nodes(), oldEdgeOrder, newEdgeOrder));
430 
431     QMap<Edge*, GraphElementData*> oldEdgeData;
432     foreach (Edge *e, p) {
433         if (e != p.first()) oldEdgeData[e] = e->data()->copy();
434     }
435 
436     _tikzDocument->undoStack()->push(new MakePathCommand(this, p, oldEdgeData));
437     _tikzDocument->undoStack()->endMacro();
438 }
439 
splitPath()440 void TikzScene::splitPath()
441 {
442     QSet<Node*> selNodes;
443     QSet<Edge*> edges;
444     getSelection(selNodes, edges);
445 
446     // if no edges are selected, try to infer edges from nodes
447     if (edges.isEmpty()) {
448         foreach(Edge *e, graph()->edges()) {
449             if (selNodes.contains(e->source()) && selNodes.contains(e->target()))
450                 edges << e;
451         }
452     }
453 
454     QSet<Path*> paths;
455     foreach (Edge *e, edges) {
456         if (e->path()) paths << e->path();
457     }
458 
459     _tikzDocument->undoStack()->push(new SplitPathCommand(this, paths));
460 }
461 
refreshZIndices()462 void TikzScene::refreshZIndices()
463 {
464     qreal z = 0.0;
465     foreach (Edge *e, graph()->edges()) {
466         if (e->path() && e == e->path()->edges().first()) {
467             pathItems()[e->path()]->setZValue(z);
468             edgeItems()[e]->setZValue(z + 0.1);
469         } else {
470             edgeItems()[e]->setZValue(z);
471         }
472         z += 1.0;
473     }
474 
475     foreach (Node *n, graph()->nodes()) {
476         nodeItems()[n]->setZValue(z);
477         z += 1.0;
478     }
479 }
480 
mousePressEvent(QGraphicsSceneMouseEvent * event)481 void TikzScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
482 {
483     QSettings settings("tikzit", "tikzit");
484     if (!_enabled) return;
485 
486     // current mouse position, in scene coordinates
487     _mouseDownPos = event->scenePos();
488 
489     _draggingNodes = false;
490     _selectingEdge = nullptr;
491 
492     // radius of a control point for bezier edges, in scene coordinates
493     qreal cpR = GLOBAL_SCALEF * (0.1);
494     qreal cpR2 = cpR * cpR;
495 
496     if (event->button() == Qt::RightButton &&
497         _tools->currentTool() == ToolPalette::SELECT &&
498         settings.value("smart-tool-enabled", true).toBool())
499     {
500         _smartTool = true;
501         if (!items(_mouseDownPos).isEmpty() &&
502             dynamic_cast<NodeItem*>(items(_mouseDownPos)[0]))
503         {
504             _tools->setCurrentTool(ToolPalette::EDGE);
505         } else {
506             _tools->setCurrentTool(ToolPalette::VERTEX);
507         }
508     }
509 
510     switch (_tools->currentTool()) {
511     case ToolPalette::SELECT:
512         // check if we grabbed a control point of an edge
513         foreach (QGraphicsItem *gi, selectedItems()) {
514             if (EdgeItem *ei = dynamic_cast<EdgeItem*>(gi)) {
515                 qreal dx, dy;
516 
517                 dx = ei->cp1Item()->pos().x() - _mouseDownPos.x();
518                 dy = ei->cp1Item()->pos().y() - _mouseDownPos.y();
519 
520                 if (dx*dx + dy*dy <= cpR2) {
521                     _modifyEdgeItem = ei;
522                     _firstControlPoint = true;
523                     break;
524                 }
525 
526                 dx = ei->cp2Item()->pos().x() - _mouseDownPos.x();
527                 dy = ei->cp2Item()->pos().y() - _mouseDownPos.y();
528 
529                 if (dx*dx + dy*dy <= cpR2) {
530                     _modifyEdgeItem = ei;
531                     _firstControlPoint = false;
532                     break;
533                 }
534             }
535         }
536 
537         if (_modifyEdgeItem != nullptr) {
538             // store for undo purposes
539             Edge *e = _modifyEdgeItem->edge();
540             _oldBend = e->bend();
541             _oldInAngle = e->inAngle();
542             _oldOutAngle = e->outAngle();
543             _oldWeight = e->weight();
544         } else {
545             // since we are not dragging a control point, process the click normally
546             //views()[0]->setDragMode(QGraphicsView::RubberBandDrag);
547             QGraphicsScene::mousePressEvent(event);
548 
549             if (items(_mouseDownPos).isEmpty()) {
550                 _rubberBandItem->setRect(QRectF(_mouseDownPos,_mouseDownPos));
551                 _rubberBandItem->setVisible(true);
552                 //qDebug() << "starting rubber band drag";
553             }
554 
555 //            foreach (QGraphicsItem *gi, items()) {
556 //                if (EdgeItem *ei = dynamic_cast<EdgeItem*>(gi)) {
557 //                    //qDebug() << "got an edge item: " << ei;
558 //                    ei->setFlag(QGraphicsItem::ItemIsSelectable, false);
559 //                    //ei->setSelected(true);
560 //                }
561 //            }
562 
563             // save current node positions for undo support
564             _oldNodePositions.clear();
565             foreach (QGraphicsItem *gi, selectedItems()) {
566                 if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)) {
567                     _oldNodePositions.insert(ni->node(), ni->node()->point());
568                 }
569             }
570 
571             QList<QGraphicsItem*> its = items(_mouseDownPos);
572             if (!its.isEmpty()) {
573                 if (dynamic_cast<NodeItem*>(its[0])) {
574                     _draggingNodes = true;
575                 } else {
576                     foreach (QGraphicsItem *gi, its) {
577                         if (EdgeItem *ei = dynamic_cast<EdgeItem*>(gi)) {
578                             _selectingEdge = ei->edge();
579                             break;
580                         }
581                     }
582                 }
583             }
584         }
585 
586         break;
587     case ToolPalette::VERTEX:
588         break;
589     case ToolPalette::EDGE:
590         foreach (QGraphicsItem *gi, items(_mouseDownPos)) {
591             if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)){
592                 _edgeStartNodeItem = ni;
593                 _edgeEndNodeItem = ni;
594                 QLineF line(toScreen(ni->node()->point()), _mouseDownPos);
595                 _drawEdgeItem->setLine(line);
596                 _drawEdgeItem->setVisible(true);
597                 break;
598             }
599         }
600         break;
601     case ToolPalette::CROP:
602         break;
603     }
604 }
605 
mouseMoveEvent(QGraphicsSceneMouseEvent * event)606 void TikzScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
607 {
608     if (!_enabled) return;
609 
610     // current mouse position, in scene coordinates
611     QPointF mousePos = event->scenePos();
612     //QRectF rb = views()[0]->rubberBandRect();
613     //invalidate(-800,-800,1600,1600);
614     //invalidate(QRectF(), QGraphicsScene::BackgroundLayer);
615 
616     switch (_tools->currentTool()) {
617     case ToolPalette::SELECT:
618         if (_modifyEdgeItem != nullptr) {
619             Edge *e = _modifyEdgeItem->edge();
620 
621             // dragging a control point
622             QPointF src = toScreen(e->source()->point());
623             QPointF targ = toScreen(e->target()->point());
624             qreal dx1 = targ.x() - src.x();
625             qreal dy1 = targ.y() - src.y();
626             qreal dx2, dy2;
627             if (_firstControlPoint) {
628                 dx2 = mousePos.x() - src.x();
629                 dy2 = mousePos.y() - src.y();
630             } else {
631                 dx2 = mousePos.x() - targ.x();
632                 dy2 = mousePos.y() - targ.y();
633             }
634 
635             qreal baseDist = sqrt(dx1*dx1 + dy1*dy1);
636             qreal handleDist = sqrt(dx2*dx2 + dy2*dy2);
637             qreal wcoarseness = 0.1;
638 
639             if (!e->isSelfLoop()) {
640                 if (baseDist != 0.0) {
641                     e->setWeight(roundToNearest(wcoarseness, handleDist/baseDist));
642                 } else {
643                     e->setWeight(roundToNearest(wcoarseness, handleDist/GLOBAL_SCALEF));
644                 }
645             }
646 
647             qreal control_angle = atan2(-dy2, dx2);
648 
649             int bcoarseness = 15;
650             qreal bcoarsenessi = 1.0/15.0;
651 
652             if(e->basicBendMode()) {
653                 qreal bnd;
654                 qreal base_angle = atan2(-dy1, dx1);
655                 if (_firstControlPoint) {
656                     bnd = base_angle - control_angle;
657                 } else {
658                     bnd = control_angle - base_angle + M_PI;
659                     if (bnd > M_PI) bnd -= 2*M_PI;
660                 }
661 
662                 e->setBend(static_cast<int>(round(bnd * (180.0 / M_PI) * bcoarsenessi)) * bcoarseness);
663 
664             } else {
665                 int bnd = static_cast<int>(round(control_angle * (180.0 / M_PI) * bcoarsenessi)) * bcoarseness;
666                 if (_firstControlPoint) {
667                     // TODO: enable moving both control points
668 //                    if ([theEvent modifierFlags] & NSAlternateKeyMask) {
669 //                        if ([modifyEdge isSelfLoop]) {
670 //                            [modifyEdge setInAngle:[modifyEdge inAngle] +
671 //                             (bnd - [modifyEdge outAngle])];
672 //                        } else {
673 //                            [modifyEdge setInAngle:[modifyEdge inAngle] -
674 //                             (bnd - [modifyEdge outAngle])];
675 //                        }
676 //                    }
677 
678                     e->setOutAngle(bnd);
679                 } else {
680 //                    if (theEvent.modifierFlags & NSAlternateKeyMask) {
681 //                        if ([modifyEdge isSelfLoop]) {
682 //                            [modifyEdge setOutAngle:[modifyEdge outAngle] +
683 //                             (bnd - [modifyEdge inAngle])];
684 //                        } else {
685 //                            [modifyEdge setOutAngle:[modifyEdge outAngle] -
686 //                             (bnd - [modifyEdge inAngle])];
687 //                        }
688 //                    }
689 
690                     e->setInAngle(bnd);
691                 }
692             }
693 
694             _modifyEdgeItem->readPos();
695             Path *p = _modifyEdgeItem->edge()->path();
696             if (p) pathItems()[p]->readPos();
697 
698         } else if (_draggingNodes) { // nodes being dragged
699             QGraphicsScene::mouseMoveEvent(event);
700 
701             // apply the same offset to all nodes, otherwise we get odd rounding behaviour with
702             // multiple selection.
703             QPointF shift = mousePos - _mouseDownPos;
704             shift = QPointF(round(shift.x()/GRID_SEP)*GRID_SEP, round(shift.y()/GRID_SEP)*GRID_SEP);
705 
706             foreach (Node *n, _oldNodePositions.keys()) {
707                 NodeItem *ni = _nodeItems[n];
708 
709 				// in (rare) cases, the graph can change while we are dragging
710 				if (ni != nullptr) {
711 					ni->setPos(toScreen(_oldNodePositions[n]) + shift);
712 					ni->writePos();
713 				}
714             }
715 
716             refreshAdjacentEdges(_oldNodePositions.keys());
717         } else {
718             // otherwise, process mouse move normally
719             QGraphicsScene::mouseMoveEvent(event);
720 
721             if (_rubberBandItem->isVisible()) {
722                 qreal left = std::min(_mouseDownPos.x(), mousePos.x());
723                 qreal top = std::min(_mouseDownPos.y(), mousePos.y());
724                 qreal w = std::abs(_mouseDownPos.x() - mousePos.x());
725                 qreal h = std::abs(_mouseDownPos.y() - mousePos.y());
726 
727                 _rubberBandItem->setRect(QRectF(left, top, w, h));
728             }
729         }
730 
731         break;
732     case ToolPalette::VERTEX:
733         break;
734     case ToolPalette::EDGE:
735         if (_drawEdgeItem->isVisible()) {
736             _edgeEndNodeItem = nullptr;
737             foreach (QGraphicsItem *gi, items(mousePos)) {
738                 if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)){
739                     _edgeEndNodeItem = ni;
740                     break;
741                 }
742             }
743             QPointF p1 = _drawEdgeItem->line().p1();
744             QPointF p2 = (_edgeEndNodeItem != nullptr) ? toScreen(_edgeEndNodeItem->node()->point()) : mousePos;
745             QLineF line(p1, p2);
746 
747             _drawEdgeItem->setLine(line);
748         }
749         break;
750     case ToolPalette::CROP:
751         break;
752     }
753 }
754 
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)755 void TikzScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
756 {
757     if (!_enabled) return;
758     QSettings settings("tikzit", "tikzit");
759 
760     // current mouse position, in scene coordinates
761     QPointF mousePos = event->scenePos();
762 
763     switch (_tools->currentTool()) {
764     case ToolPalette::SELECT:
765         if (_modifyEdgeItem != nullptr) {
766             // finished dragging a control point
767             Edge *e = _modifyEdgeItem->edge();
768 
769             if (!almostEqual(_oldWeight, e->weight()) ||
770                 _oldBend != e->bend() ||
771                 _oldInAngle != e->inAngle() ||
772                 _oldOutAngle != e->outAngle())
773             {
774                 EdgeBendCommand *cmd = new EdgeBendCommand(this, e, _oldWeight, _oldBend, _oldInAngle, _oldOutAngle);
775                 _tikzDocument->undoStack()->push(cmd);
776             }
777 
778             _modifyEdgeItem = nullptr;
779         } else {
780             // otherwise, process mouse move normally
781             QGraphicsScene::mouseReleaseEvent(event);
782 
783             if (_selectingEdge) {
784                 bool sel = edgeItems()[_selectingEdge]->isSelected();
785                 Path *p = _selectingEdge->path();
786                 if (p) {
787                     foreach (Edge *e, p->edges()) {
788                         if (e != _selectingEdge)
789                             edgeItems()[e]->setSelected(sel);
790                         nodeItems()[e->source()]->setSelected(sel);
791                         nodeItems()[e->target()]->setSelected(sel);
792                     }
793                 }
794 //                else {
795 //                    nodeItems()[_selectingEdge->source()]->setSelected(sel);
796 //                    nodeItems()[_selectingEdge->target()]->setSelected(sel);
797 //                }
798             }
799 
800             if (_rubberBandItem->isVisible()) {
801                 QPainterPath sel;
802                 sel.addRect(_rubberBandItem->rect());
803                 foreach (QGraphicsItem *gi, items()) {
804                     if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)) {
805                         if (sel.contains(toScreen(ni->node()->point()))) ni->setSelected(true);
806                     }
807                 }
808                 //setSelectionArea(sel);
809             }
810 
811             _rubberBandItem->setVisible(false);
812 
813             if (!_oldNodePositions.empty()) {
814                 QPointF shift = mousePos - _mouseDownPos;
815                 shift = QPointF(round(shift.x()/GRID_SEP)*GRID_SEP, round(shift.y()/GRID_SEP)*GRID_SEP);
816 
817                 if (shift.x() != 0.0 || shift.y() != 0.0) {
818                     QMap<Node*,QPointF> newNodePositions;
819 
820                     foreach (QGraphicsItem *gi, selectedItems()) {
821                         if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)) {
822                             ni->writePos();
823                             newNodePositions.insert(ni->node(), ni->node()->point());
824                         }
825                     }
826 
827                     //qDebug() << _oldNodePositions;
828                     //qDebug() << newNodePositions;
829 
830                     _tikzDocument->undoStack()->push(new MoveCommand(this, _oldNodePositions, newNodePositions));
831                 }
832 
833                 _oldNodePositions.clear();
834             }
835         }
836 
837         break;
838     case ToolPalette::VERTEX:
839         {
840             QPointF gridPos(round(mousePos.x()/GRID_SEP)*GRID_SEP, round(mousePos.y()/GRID_SEP)*GRID_SEP);
841             Node *n = new Node(_tikzDocument);
842             n->setName(graph()->freshNodeName());
843             n->setPoint(fromScreen(gridPos));
844             n->setStyleName(_styles->activeNodeStyleName());
845 
846             QRectF grow(gridPos.x() - GLOBAL_SCALEF, gridPos.y() - GLOBAL_SCALEF, 2 * GLOBAL_SCALEF, 2 * GLOBAL_SCALEF);
847             QRectF newBounds = sceneRect().united(grow);
848             //qDebug() << grow;
849             //qDebug() << newBounds;
850 
851             AddNodeCommand *cmd = new AddNodeCommand(this, n, newBounds);
852             _tikzDocument->undoStack()->push(cmd);
853         }
854         break;
855     case ToolPalette::EDGE:
856         // add an edge
857         if (_edgeStartNodeItem != nullptr && _edgeEndNodeItem != nullptr) {
858             Edge *e = new Edge(_edgeStartNodeItem->node(), _edgeEndNodeItem->node(), _tikzDocument);
859 			e->setStyleName(_styles->activeEdgeStyleName());
860 
861             bool selectEdge = settings.value("select-new-edges", false).toBool();
862             QSet<Node*> selNodes;
863             QSet<Edge*> selEdges;
864             if (selectEdge) getSelection(selNodes, selEdges);
865             AddEdgeCommand *cmd = new AddEdgeCommand(this, e, selectEdge,
866                                                      selNodes, selEdges);
867             _tikzDocument->undoStack()->push(cmd);
868         }
869         _edgeStartNodeItem = nullptr;
870         _edgeEndNodeItem = nullptr;
871         _drawEdgeItem->setVisible(false);
872         break;
873     case ToolPalette::CROP:
874         break;
875     }
876 
877     if (_smartTool) {
878         _tools->setCurrentTool(ToolPalette::SELECT);
879     }
880 
881     _smartTool = false;
882 
883     // clear artefacts from rubber band selection
884     invalidate(QRect(), QGraphicsScene::BackgroundLayer);
885 }
886 
887 
888 
keyReleaseEvent(QKeyEvent * event)889 void TikzScene::keyReleaseEvent(QKeyEvent *event)
890 {
891     //qDebug() << "keyrelease:" << QString::number(event->key(), 16);
892     //qDebug() << "modifiers:" << QString::number(QApplication::queryKeyboardModifiers(), 16);
893     if (!_enabled) return;
894 
895     // slower, but seems to be more reliable than event->modifiers()
896     Qt::KeyboardModifiers mod = QApplication::queryKeyboardModifiers();
897 
898     // clear highlighting for edge bends (if there was any)
899     if (mod & Qt::ControlModifier) {
900         // it could be the case the user has released shift and is still holding control
901         bool head = !(mod & Qt::ShiftModifier);
902         _highlightHeads = head;
903         _highlightTails = !head;
904     } else {
905         _highlightHeads = false;
906         _highlightTails = false;
907     }
908 
909 
910 
911     foreach (QGraphicsItem *it, selectedItems()) it->update();
912 }
913 
keyPressEvent(QKeyEvent * event)914 void TikzScene::keyPressEvent(QKeyEvent *event)
915 {
916     //qDebug() << "keypress:" << QString::number(event->key(), 16);
917     //qDebug() << "modifiers:" << QString::number(QApplication::queryKeyboardModifiers(), 16);
918     bool capture = false;
919 
920     // slower, but seems to be more reliable than event->modifiers()
921     Qt::KeyboardModifiers mod = QApplication::queryKeyboardModifiers();
922 
923     if (mod & Qt::ControlModifier) {
924         QSet<Node*> selNodes;
925         QSet<Edge*> selEdges;
926         getSelection(selNodes, selEdges);
927 
928         if (!selNodes.isEmpty()) {
929             QPointF delta(0,0);
930             qreal shift = (mod & Qt::ShiftModifier) ? 1.0 : 10.0;
931             switch(event->key()) {
932             case Qt::Key_Left:
933                 delta.setX(-0.025 * shift);
934                 break;
935             case Qt::Key_Right:
936                 delta.setX(0.025 * shift);
937                 break;
938             case Qt::Key_Up:
939                 delta.setY(0.025 * shift);
940                 break;
941             case Qt::Key_Down:
942                 delta.setY(-0.025 * shift);
943                 break;
944             }
945 
946             if (!delta.isNull()) {
947                 capture = true;
948                 QMap<Node*,QPointF> oldNodePositions;
949                 QMap<Node*,QPointF> newNodePositions;
950                 QPointF pos;
951 
952                 foreach (QGraphicsItem *gi, selectedItems()) {
953                     if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)) {
954                         pos = ni->node()->point();
955                         oldNodePositions.insert(ni->node(), pos);
956                         newNodePositions.insert(ni->node(), pos + delta);
957                     }
958                 }
959 
960                 MoveCommand *cmd = new MoveCommand(this, oldNodePositions, newNodePositions);
961                 _tikzDocument->undoStack()->push(cmd);
962             }
963         } else if (!selEdges.isEmpty()) {
964             int deltaAngle = 0;
965             qreal deltaWeight = 0.0;
966 
967             bool head = !(mod & Qt::ShiftModifier);
968             _highlightHeads = head;
969             _highlightTails = !head;
970 
971             switch(event->key()) {
972             case Qt::Key_Left:
973                 deltaAngle = 15;
974                 break;
975             case Qt::Key_Right:
976                 deltaAngle = -15;
977                 break;
978             case Qt::Key_Down:
979                 deltaWeight = -0.1;
980                 break;
981             case Qt::Key_Up:
982                 deltaWeight = 0.1;
983                 break;
984             }
985 
986             if (deltaAngle != 0) {
987                 capture = true;
988                 _tikzDocument->undoStack()->beginMacro("Bend edges");
989 
990                 // shift bend by deltaAngle or -deltaAngle (see below)
991                 int sign = 1;
992 
993                 foreach (Edge *e, selEdges) {
994                     if (e->basicBendMode()) {
995                         _tikzDocument->undoStack()->push(new ChangeEdgeModeCommand(this, e));
996                     }
997 
998                     if (head) {
999                         int oldInAngle = e->inAngle();
1000                         e->setInAngle(oldInAngle + sign * deltaAngle);
1001                         EdgeBendCommand *cmd = new EdgeBendCommand(this, e,
1002                                                                    e->weight(),
1003                                                                    e->bend(),
1004                                                                    oldInAngle,
1005                                                                    e->outAngle());
1006                         _tikzDocument->undoStack()->push(cmd);
1007                     } else {
1008                         int oldOutAngle = e->outAngle();
1009                         e->setOutAngle(oldOutAngle + sign * deltaAngle);
1010                         EdgeBendCommand *cmd = new EdgeBendCommand(this, e,
1011                                                                    e->weight(),
1012                                                                    e->bend(),
1013                                                                    e->inAngle(),
1014                                                                    oldOutAngle);
1015                         _tikzDocument->undoStack()->push(cmd);
1016                     }
1017 
1018                     // in the special case where 2 edges are selected, bend in opposite directions
1019                     if (selEdges.size() == 2) sign *= -1;
1020                 }
1021 
1022                 _tikzDocument->undoStack()->endMacro();
1023             } else if (!almostZero(deltaWeight)) {
1024                 capture = true;
1025                 _tikzDocument->undoStack()->beginMacro("Adjust edges");
1026 
1027                 foreach (Edge *e, selEdges) {
1028                     qreal oldWeight = e->weight();
1029                     // don't let weight drop below 0.1
1030                     if (oldWeight + deltaWeight > 0.099) {
1031                         e->setWeight(oldWeight + deltaWeight);
1032                         EdgeBendCommand *cmd = new EdgeBendCommand(this, e,
1033                                                                    oldWeight,
1034                                                                    e->bend(),
1035                                                                    e->inAngle(),
1036                                                                    e->outAngle());
1037                         _tikzDocument->undoStack()->push(cmd);
1038                     }
1039                 }
1040 
1041                 _tikzDocument->undoStack()->endMacro();
1042             }
1043         }
1044     } else { // no CTRL key
1045         if (event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete) {
1046             deleteSelectedItems();
1047         } else if (!event->isAutoRepeat()) {
1048             switch(event->key()) {
1049             case Qt::Key_S:
1050                 tikzit->activeWindow()->toolPalette()->setCurrentTool(ToolPalette::SELECT);
1051                 break;
1052             case Qt::Key_V:
1053             case Qt::Key_N:
1054                 tikzit->activeWindow()->toolPalette()->setCurrentTool(ToolPalette::VERTEX);
1055                 break;
1056             case Qt::Key_E:
1057                 tikzit->activeWindow()->toolPalette()->setCurrentTool(ToolPalette::EDGE);
1058                 break;
1059             case Qt::Key_B:
1060                 tikzit->activeWindow()->toolPalette()->setCurrentTool(ToolPalette::CROP);
1061                 break;
1062             }
1063         }
1064     }
1065 
1066     foreach (QGraphicsItem *it, selectedItems()) it->update();
1067     if (!capture) QGraphicsScene::keyPressEvent(event);
1068 }
1069 
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)1070 void TikzScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
1071 {
1072     if (!_enabled) return;
1073 
1074     QPointF mousePos = event->scenePos();
1075 
1076     foreach (QGraphicsItem *it, items(mousePos)) {
1077         if (EdgeItem *ei = dynamic_cast<EdgeItem*>(it)) {
1078             if (!ei->edge()->isSelfLoop()) {
1079                 ChangeEdgeModeCommand *cmd = new ChangeEdgeModeCommand(this, ei->edge());
1080                 _tikzDocument->undoStack()->push(cmd);
1081             }
1082 			break;
1083         } else if (NodeItem *ni = dynamic_cast<NodeItem*>(it)) {
1084             QInputDialog *d = new QInputDialog(views()[0]);
1085             d->setLabelText(tr("Label:"));
1086             d->setTextValue(ni->node()->label());
1087             d->setWindowTitle(tr("Node label"));
1088 
1089             if (QLineEdit *le = d->findChild<QLineEdit*>()) {
1090                 le->setValidator(new DelimitedStringValidator(le));
1091             }
1092 
1093             if (d->exec()) {
1094                 QMap<Node*,QString> oldLabels;
1095                 oldLabels.insert(ni->node(), ni->node()->label());
1096                 ChangeLabelCommand *cmd = new ChangeLabelCommand(this, oldLabels, d->textValue());
1097                 _tikzDocument->undoStack()->push(cmd);
1098             }
1099 
1100             d->deleteLater();
1101 			break;
1102         }
1103     }
1104 }
1105 
drawNodeLabels() const1106 bool TikzScene::drawNodeLabels() const
1107 {
1108     return _drawNodeLabels;
1109 }
1110 
setDrawNodeLabels(bool drawNodeLabels)1111 void TikzScene::setDrawNodeLabels(bool drawNodeLabels)
1112 {
1113     _drawNodeLabels = drawNodeLabels;
1114 }
1115 
highlightTails() const1116 bool TikzScene::highlightTails() const
1117 {
1118     return _highlightTails && getSelectedNodes().isEmpty();
1119 }
1120 
highlightHeads() const1121 bool TikzScene::highlightHeads() const
1122 {
1123     return _highlightHeads && getSelectedNodes().isEmpty();
1124 }
1125 
enabled() const1126 bool TikzScene::enabled() const
1127 {
1128     return _enabled;
1129 }
1130 
setEnabled(bool enabled)1131 void TikzScene::setEnabled(bool enabled)
1132 {
1133     _enabled = enabled;
1134     update();
1135 }
1136 
lineNumberForSelection()1137 int TikzScene::lineNumberForSelection()
1138 {
1139     foreach (QGraphicsItem *gi, selectedItems()) {
1140         if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)) return ni->node()->tikzLine();
1141         if (EdgeItem *ei = dynamic_cast<EdgeItem*>(gi)) return ei->edge()->tikzLine();
1142     }
1143     return 0;
1144 }
1145 
1146 
applyActiveStyleToNodes()1147 void TikzScene::applyActiveStyleToNodes() {
1148     ApplyStyleToNodesCommand *cmd = new ApplyStyleToNodesCommand(this, _styles->activeNodeStyleName());
1149     _tikzDocument->undoStack()->push(cmd);
1150 }
1151 
applyActiveStyleToEdges()1152 void TikzScene::applyActiveStyleToEdges() {
1153 	ApplyStyleToEdgesCommand *cmd = new ApplyStyleToEdgesCommand(this, _styles->activeEdgeStyleName());
1154 	_tikzDocument->undoStack()->push(cmd);
1155 }
1156 
deleteSelectedItems()1157 void TikzScene::deleteSelectedItems()
1158 {
1159     QSet<Node*> selNodes;
1160     QSet<Edge*> selEdges;
1161     getSelection(selNodes, selEdges);
1162 
1163     QMap<int,Node*> deleteNodes;
1164     QMap<int,Edge*> deleteEdges;
1165     QSet<Path*> deletePaths;
1166 
1167     for (int i = 0; i < _tikzDocument->graph()->nodes().length(); ++i) {
1168         Node *n = _tikzDocument->graph()->nodes()[i];
1169         if (selNodes.contains(n)) deleteNodes.insert(i, n);
1170     }
1171 
1172     for (int i = 0; i < _tikzDocument->graph()->edges().length(); ++i) {
1173         Edge *e = _tikzDocument->graph()->edges()[i];
1174         if (selEdges.contains(e) ||
1175             selNodes.contains(e->source()) ||
1176             selNodes.contains(e->target()))
1177         {
1178             if (e->path()) deletePaths << e->path();
1179             deleteEdges.insert(i, e);
1180         }
1181     }
1182 
1183     //qDebug() << "nodes:" << deleteNodes;
1184     //qDebug() << "edges:" << deleteEdges;
1185     _tikzDocument->undoStack()->beginMacro("Delete");
1186     _tikzDocument->undoStack()->push(new SplitPathCommand(this, deletePaths));
1187     _tikzDocument->undoStack()->push(new DeleteCommand(this, deleteNodes, deleteEdges,
1188                                                        selNodes, selEdges));
1189     _tikzDocument->undoStack()->endMacro();
1190 }
1191 
copyToClipboard()1192 void TikzScene::copyToClipboard()
1193 {
1194     Graph *g = graph()->copyOfSubgraphWithNodes(getSelectedNodes());
1195     //qDebug() << g->tikz();
1196     QGuiApplication::clipboard()->setText(g->tikz());
1197     delete g;
1198 }
1199 
cutToClipboard()1200 void TikzScene::cutToClipboard()
1201 {
1202     copyToClipboard();
1203     deleteSelectedItems();
1204 }
1205 
1206 
pasteFromClipboard()1207 void TikzScene::pasteFromClipboard()
1208 {
1209     QString tikz = QGuiApplication::clipboard()->text();
1210     Graph *g = new Graph();
1211     TikzAssembler ass(g);
1212 
1213     // attempt to parse whatever's on the clipboard, if we get a
1214     // non-empty tikz graph, insert it.
1215     if (ass.parse(tikz) && !g->nodes().isEmpty()) {
1216         // make sure names in the new subgraph are fresh
1217         g->renameApart(graph());
1218 
1219         QRectF srcRect = g->realBbox();
1220         QRectF tgtRect = graph()->realBbox();
1221         QPointF shift(tgtRect.right() - srcRect.left(), 0.0);
1222 
1223         if (shift.x() > 0) {
1224             foreach (Node *n, g->nodes()) {
1225                 n->setPoint(n->point() + shift);
1226             }
1227         }
1228 
1229         PasteCommand *cmd = new PasteCommand(this, g);
1230         _tikzDocument->undoStack()->push(cmd);
1231     }
1232 }
1233 
selectAllNodes()1234 void TikzScene::selectAllNodes()
1235 {
1236     foreach (NodeItem *ni, _nodeItems.values()) {
1237         ni->setSelected(true);
1238     }
1239 }
1240 
deselectAll()1241 void TikzScene::deselectAll()
1242 {
1243     foreach (NodeItem *ni, _nodeItems.values()) {
1244         ni->setSelected(false);
1245     }
1246 
1247     foreach (EdgeItem *ei, _edgeItems.values()) {
1248         ei->setSelected(false);
1249     }
1250 }
1251 
parseTikz(QString tikz)1252 bool TikzScene::parseTikz(QString tikz)
1253 {
1254     Graph *newGraph = new Graph(this);
1255     TikzAssembler ass(newGraph);
1256     if (ass.parse(tikz)) {
1257         ReplaceGraphCommand *cmd = new ReplaceGraphCommand(this, graph(), newGraph);
1258         tikzDocument()->undoStack()->push(cmd);
1259         setEnabled(true);
1260         views()[0]->setFocus();
1261         return true;
1262     } else {
1263         return false;
1264     }
1265 }
1266 
reflectNodes(bool horizontal)1267 void TikzScene::reflectNodes(bool horizontal)
1268 {
1269     ReflectNodesCommand *cmd = new ReflectNodesCommand(this, getSelectedNodes(), horizontal);
1270     tikzDocument()->undoStack()->push(cmd);
1271 }
1272 
rotateNodes(bool clockwise)1273 void TikzScene::rotateNodes(bool clockwise)
1274 {
1275     RotateNodesCommand *cmd = new RotateNodesCommand(this, getSelectedNodes(), clockwise);
1276     tikzDocument()->undoStack()->push(cmd);
1277 }
1278 
1279 
getSelection(QSet<Node * > & selNodes,QSet<Edge * > & selEdges) const1280 void TikzScene::getSelection(QSet<Node *> &selNodes, QSet<Edge *> &selEdges) const
1281 {
1282     foreach (QGraphicsItem *gi, selectedItems()) {
1283         if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)) selNodes << ni->node();
1284         if (EdgeItem *ei = dynamic_cast<EdgeItem*>(gi)) selEdges << ei->edge();
1285     }
1286 }
1287 
getSelectedNodes() const1288 QSet<Node *> TikzScene::getSelectedNodes() const
1289 {
1290     QSet<Node*> selNodes;
1291     foreach (QGraphicsItem *gi, selectedItems()) {
1292         if (NodeItem *ni = dynamic_cast<NodeItem*>(gi)) selNodes << ni->node();
1293     }
1294     return selNodes;
1295 }
1296 
1297 
tikzDocument() const1298 TikzDocument *TikzScene::tikzDocument() const
1299 {
1300     return _tikzDocument;
1301 }
1302 
setTikzDocument(TikzDocument * tikzDocument)1303 void TikzScene::setTikzDocument(TikzDocument *tikzDocument)
1304 {
1305     _tikzDocument = tikzDocument;
1306     graphReplaced();
1307 }
1308 
reloadStyles()1309 void TikzScene::reloadStyles()
1310 {
1311     _styles->reloadStyles();
1312 	foreach(EdgeItem *ei, _edgeItems) {
1313 		ei->edge()->attachStyle();
1314 		ei->readPos(); // trigger a repaint
1315 	}
1316 
1317     foreach (NodeItem *ni, _nodeItems) {
1318         ni->node()->attachStyle();
1319         ni->readPos(); // trigger a repaint
1320     }
1321 }
1322 
refreshSceneBounds()1323 void TikzScene::refreshSceneBounds() {
1324     qreal maxX = 30.0, maxY = 30.0;
1325     qreal increment = 20.0;
1326 
1327     foreach (Node *n, graph()->nodes()) {
1328         while (n->point().x() - increment < -maxX || n->point().x() + increment > maxX) {
1329             maxX += increment;
1330         }
1331 
1332         while (n->point().y() - increment < -maxY || n->point().y() + increment > maxY) {
1333             maxY += increment;
1334         }
1335     }
1336 
1337     QRectF rect(-GLOBAL_SCALEF * maxX, -GLOBAL_SCALEF * maxY, 2.0 * GLOBAL_SCALEF * maxX, 2.0 * GLOBAL_SCALEF * maxY);
1338 
1339     if (rect != sceneRect()) {
1340         setSceneRect(rect);
1341         invalidate();
1342     }
1343 }
1344 
1345 // void TikzScene::refreshSceneBounds()
1346 // {
1347 // //    if (!views().empty()) {
1348 // //        QGraphicsView *v = views().first();
1349 // //        QRectF viewB = v->mapToScene(v->viewport()->rect()).boundingRect();
1350 // //        //QPointF tl = v->mapToScene(viewB.topLeft());
1351 // //        //viewB.setTopLeft(tl);
1352 //
1353 // //        QRectF bounds = viewB.united(rectToScreen(graph()->realBbox().adjusted(-1.0f, -1.0f, 1.0f, 1.0f)));
1354 // //        //qDebug() << viewB;
1355 //
1356 // //        if (bounds != sceneRect()) {
1357 // //            QPointF c = viewB.center();
1358 // //            setSceneRect(bounds);
1359 // //            v->centerOn(c);
1360 // //        }
1361 // //    }
1362 //     //setBounds(graphB);
1363 // }
1364 
refreshAdjacentEdges(QList<Node * > nodes)1365 void TikzScene::refreshAdjacentEdges(QList<Node*> nodes)
1366 {
1367     if (nodes.empty()) return;
1368 
1369     QSet<Path*> paths;
1370     foreach (Edge *e, _edgeItems.keys()) {
1371 		EdgeItem *ei = _edgeItems[e];
1372 
1373 		// the list "nodes" can be out of date, e.g. if the graph changes while dragging
1374 		if (ei != nullptr) {
1375 			if (nodes.contains(ei->edge()->source()) || nodes.contains(ei->edge()->target())) {
1376 				ei->edge()->updateControls();
1377 				ei->readPos();
1378 			}
1379 		}
1380 
1381         // only update paths once
1382         Path *p = ei->edge()->path();
1383         if (p && !paths.contains(p)) {
1384             pathItems()[p]->readPos();
1385             paths << p;
1386         }
1387     }
1388 }
1389 
1390 //void TikzScene::setBounds(QRectF bounds)
1391 //{
1392 //    if (bounds != sceneRect()) {
1393 //        if (!views().empty()) {
1394 //            QGraphicsView *v = views().first();
1395 //            QPointF c = v->mapToScene(v->viewport()->rect().center());
1396 //            setSceneRect(bounds);
1397 //            v->centerOn(c);
1398 //        } else {
1399 //            setSceneRect(bounds);
1400 //        }
1401 //    }
1402 //}
1403 
nodeItems()1404 QMap<Node*,NodeItem *> &TikzScene::nodeItems()
1405 {
1406     return _nodeItems;
1407 }
1408 
edgeItems()1409 QMap<Edge*,EdgeItem*> &TikzScene::edgeItems()
1410 {
1411     return _edgeItems;
1412 }
1413 
pathItems()1414 QMap<Path *, PathItem *> &TikzScene::pathItems()
1415 {
1416     return _pathItems;
1417 }
1418