]> git.sven.stormbind.net Git - sven/vym.git/blob - src/mapeditor.cpp
Replace Pierre as the maintainer
[sven/vym.git] / src / mapeditor.cpp
1 #include "mapeditor.h"
2
3 #include <QGraphicsProxyWidget>
4 #include <QMenuBar>
5 #include <QObject>
6 #include <QPrintDialog>
7 #include <QPrinter>
8 #include <QScrollBar>
9
10 #include "branchitem.h"
11 #include "geometry.h"
12 #include "mainwindow.h"
13 #include "misc.h"
14 #include "shortcuts.h"
15 #include "warningdialog.h"
16 #include "winter.h"
17 #include "xlinkitem.h"
18
19 extern Main *mainWindow;
20 extern QString clipboardDir;
21 extern QString clipboardFile;
22 extern bool debug;
23 extern QPrinter *printer;
24
25 extern QMenu *branchContextMenu;
26 extern QMenu *canvasContextMenu;
27 extern QMenu *floatimageContextMenu;
28 extern QMenu *taskContextMenu;
29
30 extern Switchboard switchboard;
31 extern Settings settings;
32
33 extern QTextStream vout;
34
35 extern QString editorFocusStyle;
36
37 extern FlagRowMaster *systemFlagsMaster;
38
39 ///////////////////////////////////////////////////////////////////////
40 ///////////////////////////////////////////////////////////////////////
41 MapEditor::MapEditor(VymModel *vm)
42 {
43     // qDebug() << "Constructor ME " << this;
44
45     QString shortcutScope = tr("Map Editor", "Shortcut scope");
46     mapScene = new QGraphicsScene(NULL);
47     mapScene->setBackgroundBrush(QBrush(Qt::white, Qt::SolidPattern));
48     mapScene->setItemIndexMethod(QGraphicsScene::NoIndex);  // FIXME-2 Avoiding crashes...
49                                                             // Alternatively call removeFromIndex() in destructor
50                                                             // or maybe also prepareGeometryChange()
51
52     zoomFactor = zoomFactorTarget = 1;
53     angle = angleTarget = 0;
54
55     model = vm;
56     model->registerMapEditor(this);
57
58     setScene(mapScene);
59
60     setStyleSheet("QGraphicsView:focus {" + editorFocusStyle + "}");
61
62     // Create bitmap cursors, platform dependant
63     HandOpenCursor = QCursor(QPixmap(":/mode-move-view.png"), 1, 1);
64     PickColorCursor = QCursor(QPixmap(":/cursorcolorpicker.png"), 5, 27);
65     XLinkCursor = QCursor(QPixmap(":/cursorxlink.png"), 1, 7);
66
67     editingBO = NULL;
68
69     printFrame = true;
70     printFooter = true;
71
72     setAcceptDrops(true);
73
74     // Shortcuts and actions
75     QAction *a;
76
77     a = new QAction("Select upper branch", this);
78     a->setShortcut(Qt::Key_Up);
79     a->setShortcutContext(Qt::WidgetShortcut);
80     connect(a, SIGNAL(triggered()), this, SLOT(cursorUp()));
81     addAction(a);
82
83     a = new QAction("Add upper branch to selection", this);
84     a->setShortcut(Qt::Key_Up + Qt::SHIFT);
85     a->setShortcutContext(Qt::WidgetShortcut);
86     addAction(a);
87     connect(a, SIGNAL(triggered()), this, SLOT(cursorUpToggleSelection()));
88
89     a = new QAction("Select lower branch", this);
90     a->setShortcut(Qt::Key_Down);
91     a->setShortcutContext(Qt::WidgetShortcut);
92     addAction(a);
93     connect(a, SIGNAL(triggered()), this, SLOT(cursorDown()));
94
95     a = new QAction("Add lower branch to selection", this);
96     a->setShortcut(Qt::Key_Down + Qt::SHIFT);
97     a->setShortcutContext(Qt::WidgetShortcut);
98     addAction(a);
99     connect(a, SIGNAL(triggered()), this, SLOT(cursorDownToggleSelection()));
100
101     a = new QAction("Select left branch", this);
102     a->setShortcut(Qt::Key_Left);
103     //  a->setShortcutContext (Qt::WidgetWithChildrenShortcut);
104     addAction(a);
105     connect(a, SIGNAL(triggered()), this, SLOT(cursorLeft()));
106
107     a = new QAction("Select child branch", this);
108     a->setShortcut(Qt::Key_Right);
109     //  a->setShortcutContext (Qt::WidgetWithChildrenShortcut);
110     addAction(a);
111     connect(a, SIGNAL(triggered()), this, SLOT(cursorRight()));
112
113     a = new QAction("Select first branch", this);
114     a->setShortcut(Qt::Key_Home);
115     a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
116     addAction(a);
117     connect(a, SIGNAL(triggered()), this, SLOT(cursorFirst()));
118
119     a = new QAction("Select last branch", this);
120     a->setShortcut(Qt::Key_End);
121     a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
122     addAction(a);
123     connect(a, SIGNAL(triggered()), this, SLOT(cursorLast()));
124
125     // Action to embed LineEdit for heading in Scene
126     lineEdit = NULL;
127
128     a = new QAction(tr("Edit heading", "MapEditor"), this);
129     a->setShortcut(Qt::Key_Return); // Edit heading
130     a->setShortcutContext(Qt::WidgetShortcut);
131     addAction(a);
132     connect(a, SIGNAL(triggered()), this, SLOT(editHeading()));
133     a = new QAction(tr("Edit heading", "MapEditor"), this);
134     a->setShortcut(Qt::Key_Enter); // Edit heading
135     a->setShortcutContext(Qt::WidgetShortcut);
136     addAction(a);
137     connect(a, SIGNAL(triggered()), this, SLOT(editHeading()));
138
139     // Selections
140     selectionPen = QPen(QColor(255,255,0), 1);
141     selectionBrush = QBrush(QColor(255,255,0));
142
143     // Panning
144     panningTimer = new QTimer(this);
145     vPan = QPointF();
146     connect(panningTimer, SIGNAL(timeout()), this, SLOT(panView()));
147
148     // Clone actions defined in MainWindow
149     foreach (QAction *qa, mainWindow->mapEditorActions) {
150         a = new QAction(this);
151         a->setShortcut(qa->shortcut());
152         a->setShortcutContext(qa->shortcutContext());
153         connect(a, SIGNAL(triggered()), qa, SLOT(trigger()));
154         addAction(a);
155     }
156
157     setState(Neutral);
158
159     winter = NULL;
160 }
161
162 MapEditor::~MapEditor()
163 {
164     // qDebug ()<<"Destr MapEditor this="<<this;
165
166     if (winter) {
167         delete winter;
168         winter = NULL;
169     }
170 }
171
172 VymModel *MapEditor::getModel() { return model; }
173
174 QGraphicsScene *MapEditor::getScene() { return mapScene; }
175
176 void MapEditor::panView()
177 {
178     if (!vPan.isNull()) {
179         // Scroll if needed
180         // To avoid jumping of the sceneView, only
181         // show selection, if not tmp linked
182         qreal px = 0;
183         qreal py = 0;
184         if (vPan.x() < 0)
185             px = vPan.x();
186         else if (vPan.x() > 0)
187             px = width() + vPan.x();
188         if (vPan.y() < 0)
189             py = vPan.y();
190         else if (vPan.y() > 0)
191             py = height() + vPan.y();
192
193         QPointF q = mapToScene(QPoint(px, py));
194         QRectF r = QRectF(q, QPointF(q.x() + 1, q.y() + 1));
195
196         // Expand view if necessary
197         setScrollBarPosTarget(r);   // FIXME-2   mapToScene first?   
198
199         // Stop possible other animations
200         if (scrollBarPosAnimation.state() == QAbstractAnimation::Running)
201             scrollBarPosAnimation.stop();
202
203         // Do linear animation
204         horizontalScrollBar()->setValue(horizontalScrollBar()->value() +
205                                         vPan.x());
206         verticalScrollBar()->setValue(verticalScrollBar()->value() + vPan.y());
207
208         // Update currently moving object
209         moveObject();
210     }
211 }
212
213 void MapEditor::ensureAreaVisibleAnimated(const QRectF &area, bool maximizeArea) // FIXME-2 zooming in not working yet (fit to selection)
214 {
215     // Changes viewCenter to make sure that 
216     // r is  within the margins of the viewport
217     //
218     // Only zooms, if r NOT fit into viewport 
219     // view is centered then on bounding box.
220     //
221     // Similar to QGraphicsItem::ensureVisible, 
222     // but with animation and (if necessary)
223     // zooming
224
225     int xmargin = settings.value("/mapeditor/scrollToMarginX/", 50).toInt();
226     int ymargin = settings.value("/mapeditor/scrollToMarginY/", 50).toInt();
227
228     // Do we need to zoom out to show area?
229     QRect areaViewCoord = mapFromScene(area).boundingRect();
230
231     // Visible area within margins
232     QRect visibleViewCoord = rect();
233     visibleViewCoord -= QMargins(xmargin, ymargin, xmargin, ymargin);
234
235
236     // Calculate required width and height considering rotation of view
237     qreal a = angle / 180 * M_PI;
238     qreal area_w_viewCoord = abs(sin(a) * area.height()) + abs(cos(a) * area.width());
239     qreal area_h_viewCoord = abs(sin(a) * area.width()) + abs(cos(a) * area.height());
240     qreal z_x = 1.0 * visibleViewCoord.width() / area_w_viewCoord;
241     qreal z_y = 1.0 * visibleViewCoord.height() / area_h_viewCoord;
242
243     qreal zf = min (z_x, z_y);
244
245     bool zoomOutRequired = 
246         (visibleViewCoord.width() < areaViewCoord.width() ||
247          visibleViewCoord.height() < areaViewCoord.height());
248     bool zoomInRequired = 
249         (visibleViewCoord.width() > areaViewCoord.width() &&
250          visibleViewCoord.height() > areaViewCoord.height());
251
252     //qDebug() << " zoom out: " << zoomOutRequired;
253     //qDebug() << " zoom  in: " << zoomInRequired << " zoomFactor=" << zoomFactor << " zf=" << zf;
254     if (zoomOutRequired || maximizeArea) {
255         setViewCenterTarget(area.center(), zf, angle);
256         return;
257     }
258
259
260     // After zooming bbox would fit into margins of viewport
261     long view_dx = 0;
262     long view_dy = 0;
263     if (areaViewCoord.left() < xmargin)
264         // move left
265         view_dx = areaViewCoord.left() - xmargin;
266     else if (areaViewCoord.right() > viewport()->width())
267         // move right
268         view_dx = areaViewCoord.x() + areaViewCoord.width() - viewport()->width() + xmargin;
269
270     if (areaViewCoord.top() < ymargin)
271         // move up
272         view_dy = areaViewCoord.top() - ymargin;
273     else if (areaViewCoord.bottom() > viewport()->height() - ymargin)
274         // move down
275         view_dy = areaViewCoord.y() + areaViewCoord.height() - viewport()->height() + ymargin;
276
277     if (abs(view_dx) > 5 || abs(view_dy) > 5)
278         setViewCenterTarget(
279                 mapToScene(viewport()->geometry().center() + QPoint (view_dx, view_dy)),
280                 zoomFactor,
281                 angle,
282                 2000,
283                 QEasingCurve::OutQuint);
284 }
285
286 void MapEditor::ensureSelectionVisibleAnimated(bool maximizeArea)
287 {
288     // Changes viewCenter to make sure that bounding box of all currently
289     // selected items is  within the margins of the viewport
290     //
291     // Only zooms, if bounding box of items does NOT fit into viewport 
292     // view is centered then on bounding box. (Useful also for big images)
293     //
294     // Similar to QGraphicsItem::ensureVisible, but with animation and (if necessary)
295     // zooming
296
297     QList <TreeItem*> selis = model->getSelectedItems();
298
299     // Nothing to do, if nothing is selected
300     if (selis.isEmpty()) return;
301
302     // Calculate total bounding box
303     QRectF bbox;
304     bool firstIteration = true;
305
306     foreach (TreeItem *ti, selis) {
307         LinkableMapObj *lmo = nullptr;
308         if (ti->getType() == TreeItem::Image || ti->isBranchLikeType())
309             lmo = ((MapItem *)ti)->getLMO();
310         if (lmo) {
311             if (firstIteration) {
312                 bbox = lmo->getBBox();
313                 firstIteration = false;
314             } else
315                 bbox = bbox.united(lmo->getBBox());
316         }
317     }
318
319     ensureAreaVisibleAnimated(bbox, maximizeArea);
320 }
321
322 void MapEditor::scrollTo(const QModelIndex &index)
323 {
324     if (index.isValid()) {
325         LinkableMapObj *lmo = NULL;
326         TreeItem *ti = static_cast<TreeItem *>(index.internalPointer());
327         if (ti->getType() == TreeItem::Image || ti->isBranchLikeType())
328             lmo = ((MapItem *)ti)->getLMO();
329         if (lmo) {
330             QRectF r = lmo->getBBox();
331             setScrollBarPosTarget(r);
332             animateScrollBars();
333         }
334     }
335 }
336
337 void MapEditor::setScrollBarPosTarget(QRectF rect)
338 {
339     // Expand viewport, if rect is not contained
340     if (!sceneRect().contains(rect))
341         setSceneRect(sceneRect().united(rect));
342
343     int xmargin = settings.value("/mapeditor/scrollToMarginX/", 80).toInt();
344     int ymargin = settings.value("/mapeditor/scrollToMarginX/", 80).toInt();
345
346     // Prepare scrolling
347     qreal width = viewport()->width();
348     qreal height = viewport()->height();
349     QRectF viewRect = transform().scale(zoomFactorTarget, zoomFactorTarget).mapRect(rect);
350
351     qreal left = horizontalScrollBar()->value();
352     qreal right = left + width;
353     qreal top = verticalScrollBar()->value();
354     qreal bottom = top + height;
355
356     scrollBarPosTarget = getScrollBarPos();
357
358     if (viewRect.left() <= left + xmargin) {
359         // need to scroll from the left
360         scrollBarPosTarget.setX(int(viewRect.left() - xmargin - 0.5));
361     }
362     if (viewRect.right() >= right - xmargin) {
363         // need to scroll from the right
364         scrollBarPosTarget.setX(int(viewRect.right() - width + xmargin + 0.5));
365     }
366     if (viewRect.top() <= top + ymargin) {
367         // need to scroll from the top
368         scrollBarPosTarget.setY(int(viewRect.top() - ymargin - 0.5));
369     }
370     if (viewRect.bottom() >= bottom - ymargin) {
371         // need to scroll from the bottom
372         scrollBarPosTarget.setY(
373             int(viewRect.bottom() - height + ymargin + 0.5));
374     }
375 }
376
377 QPointF MapEditor::getScrollBarPosTarget() { return scrollBarPosTarget; }
378
379 void MapEditor::setScrollBarPos(const QPointF &p)
380 {
381     scrollBarPos = p;
382     horizontalScrollBar()->setValue(int(p.x()));
383     verticalScrollBar()->setValue(int(p.y()));
384 }
385
386 QPointF MapEditor::getScrollBarPos()
387 {
388     return QPointF(horizontalScrollBar()->value(),
389                    verticalScrollBar()->value());
390     // return scrollBarPos;
391 }
392
393 void MapEditor::animateScrollBars()
394 {
395     if (scrollBarPosAnimation.state() == QAbstractAnimation::Running)
396         scrollBarPosAnimation.stop();
397
398     if (settings.value("/animation/use/", true).toBool()) {
399         scrollBarPosAnimation.setTargetObject(this);
400         scrollBarPosAnimation.setPropertyName("scrollBarPos");
401         scrollBarPosAnimation.setDuration(
402             settings.value("/animation/duration/scrollbar", 2000).toInt());
403         scrollBarPosAnimation.setEasingCurve(QEasingCurve::OutQuint);
404         scrollBarPosAnimation.setStartValue(QPointF(
405             horizontalScrollBar()->value(), verticalScrollBar()->value()));
406         scrollBarPosAnimation.setEndValue(scrollBarPosTarget);
407         scrollBarPosAnimation.start();
408     }
409     else
410         setScrollBarPos(scrollBarPosTarget);
411 }
412
413 void MapEditor::setZoomFactorTarget(const qreal &zft)
414 {
415     zoomFactorTarget = zft;
416     if (zoomAnimation.state() == QAbstractAnimation::Running)
417         zoomAnimation.stop();
418     if (settings.value("/animation/use/", true).toBool()) {
419         zoomAnimation.setTargetObject(this);
420         zoomAnimation.setPropertyName("zoomFactor");
421         zoomAnimation.setDuration(
422             settings.value("/animation/duration/zoom", 2000).toInt());
423         zoomAnimation.setEasingCurve(QEasingCurve::OutQuint);
424         zoomAnimation.setStartValue(zoomFactor);
425         zoomAnimation.setEndValue(zft);
426         zoomAnimation.start();
427     }
428     else
429         setZoomFactor(zft);
430 }
431
432 qreal MapEditor::getZoomFactorTarget() { return zoomFactorTarget; }
433
434 void MapEditor::setZoomFactor(const qreal &zf)
435 {
436     zoomFactor = zf;
437     updateMatrix();
438 }
439
440 qreal MapEditor::getZoomFactor() { return zoomFactor; }
441
442 void MapEditor::setAngleTarget(const qreal &at)
443 {
444     angleTarget = at;
445     if (rotationAnimation.state() == QAbstractAnimation::Running)
446         rotationAnimation.stop();
447     if (settings.value("/animation/use/", true).toBool()) {
448         rotationAnimation.setTargetObject(this);
449         rotationAnimation.setPropertyName("angle");
450         rotationAnimation.setDuration(
451             settings.value("/animation/duration/rotation", 2000).toInt());
452         rotationAnimation.setEasingCurve(QEasingCurve::OutQuint);
453         rotationAnimation.setStartValue(angle);
454         rotationAnimation.setEndValue(at);
455         rotationAnimation.start();
456     }
457     else
458         setAngle(angleTarget);
459 }
460
461 qreal MapEditor::getAngleTarget() { return angleTarget; }
462
463 void MapEditor::setAngle(const qreal &a)
464 {
465     angle = a;
466     updateMatrix();
467     if (winter)
468         winter->updateView();
469 }
470
471 qreal MapEditor::getAngle() { return angle; }
472
473 void MapEditor::setViewCenterTarget(const QPointF &p, const qreal &zft,
474                                     const qreal &at, const int duration,
475                                     const QEasingCurve &easingCurve)
476 {
477     viewCenterTarget = p;
478     zoomFactorTarget = zft;
479     angleTarget = at;
480
481     viewCenter = mapToScene(viewport()->geometry()).boundingRect().center();
482
483     if (viewCenterAnimation.state() == QAbstractAnimation::Running)
484         viewCenterAnimation.stop();
485     if (rotationAnimation.state() == QAbstractAnimation::Running)
486         rotationAnimation.stop();
487     if (zoomAnimation.state() == QAbstractAnimation::Running)
488         zoomAnimation.stop();
489
490     if (settings.value("/animation/use/", true).toBool()) {
491         viewCenterAnimation.setTargetObject(this);
492         viewCenterAnimation.setPropertyName("viewCenter");
493         viewCenterAnimation.setDuration(
494             settings.value("/animation/duration/scrollbar", duration).toInt());
495         viewCenterAnimation.setEasingCurve(easingCurve);
496         viewCenterAnimation.setStartValue(viewCenter);
497         viewCenterAnimation.setEndValue(viewCenterTarget);
498         viewCenterAnimation.start();
499
500         rotationAnimation.setTargetObject(this);
501         rotationAnimation.setPropertyName("angle");
502         rotationAnimation.setDuration(
503             settings.value("/animation/duration/rotation", duration).toInt());
504         rotationAnimation.setEasingCurve(easingCurve);
505         rotationAnimation.setStartValue(angle);
506         rotationAnimation.setEndValue(angleTarget);
507         rotationAnimation.start();
508
509         zoomAnimation.setTargetObject(this);
510         zoomAnimation.setPropertyName("zoomFactor");
511         zoomAnimation.setDuration(
512             settings.value("/animation/duration/zoom", duration).toInt());
513         zoomAnimation.setEasingCurve(easingCurve);
514         zoomAnimation.setStartValue(zoomFactor);
515         zoomAnimation.setEndValue(zoomFactorTarget);
516         zoomAnimation.start();
517     }
518     else {
519         setAngle(angleTarget);
520         setZoomFactor(zft);
521         setViewCenter(viewCenterTarget);
522     }
523 }
524
525 void MapEditor::setViewCenterTarget()
526 {
527     MapItem *selti = (MapItem *)(model->getSelectedItem());
528     if (selti) {
529         LinkableMapObj *lmo = selti->getLMO();
530         if (lmo)
531             setViewCenterTarget(lmo->getBBox().center(), 1, 0);
532     }
533 }
534
535 QPointF MapEditor::getViewCenterTarget() { return viewCenterTarget; }
536
537 void MapEditor::setViewCenter(const QPointF &vc) { centerOn(vc); }
538
539 QPointF MapEditor::getViewCenter() { return viewCenter; }
540
541 void MapEditor::updateMatrix()
542 {
543     QTransform t_zoom;
544     t_zoom.scale(zoomFactor, zoomFactor);
545     QTransform t_rot;
546     t_rot.rotate(angle);
547     setTransform(t_zoom * t_rot);
548 }
549
550 void MapEditor::minimizeView() {
551     // If we only would set scene rectangle to existing items, then 
552     // view fould "jump", when Qt automatically tries to center. 
553     // Better consider the currently visible viewport (with slight offset)
554     QRectF r = mapToScene(viewport()->geometry()).boundingRect();
555     r.translate(-2,-3);
556     setSceneRect(scene()->itemsBoundingRect().united(r));
557 }
558
559 void MapEditor::print()
560 {
561     QRectF totalBBox = getTotalBBox();
562
563     if (!printer)
564         printer = mainWindow->setupPrinter();
565
566     // Try to set orientation automagically
567     // Note: Interpretation of generated postscript is amibiguous, if
568     // there are problems with landscape mode, see
569     // http://sdb.suse.de/de/sdb/html/jsmeix_print-cups-landscape-81.html
570
571     if (totalBBox.width() > totalBBox.height())
572         // recommend landscape
573         printer->setPageOrientation(QPageLayout::Landscape);
574     else
575         // recommend portrait
576         printer->setPageOrientation(QPageLayout::Portrait);
577
578     QPrintDialog dialog(printer, this);
579     dialog.setWindowTitle(tr("Print vym map", "MapEditor"));
580     if (dialog.exec() == QDialog::Accepted) {
581         QPainter pp(printer);
582
583         pp.setRenderHint(QPainter::Antialiasing, true);
584
585         // Don't print the visualisation of selection
586         model->unselectAll();
587
588         QRectF mapRect = totalBBox;
589         QGraphicsRectItem *frame = NULL;
590
591         if (printFrame) {
592             // Print frame around map
593             mapRect.setRect(totalBBox.x() - 10, totalBBox.y() - 10,
594                             totalBBox.width() + 20, totalBBox.height() + 20);
595             frame = mapScene->addRect(mapRect, QPen(Qt::black),
596                                       QBrush(Qt::NoBrush));
597             frame->setZValue(0);
598             frame->show();
599         }
600
601         double paperAspect =
602             (double)printer->width() / (double)printer->height();
603         double mapAspect = (double)mapRect.width() / (double)mapRect.height();
604         int viewBottom;
605         if (mapAspect >= paperAspect) {
606             // Fit horizontally to paper width
607             // pp.setViewport(0,0,
608             // printer->width(),(int)(printer->width()/mapAspect) );
609             viewBottom = (int)(printer->width() / mapAspect);
610         }
611         else {
612             // Fit vertically to paper height
613             // pp.setViewport(0,0,(int)(printer->height()*mapAspect),printer->height());
614             viewBottom = printer->height();
615         }
616
617         if (printFooter) {
618             // Print footer below map
619             QFont font;
620             font.setPointSize(10);
621             pp.setFont(font);
622             QRectF footerBox(0, viewBottom, printer->width(), 15);
623             pp.drawText(footerBox, Qt::AlignLeft,
624                         "VYM - " + model->getFileName());
625             pp.drawText(footerBox, Qt::AlignRight,
626                         QDate::currentDate().toString(Qt::TextDate));
627         }
628         mapScene->render(&pp,
629                          QRectF(0, 0, printer->width(), printer->height() - 15),
630                          QRectF(mapRect.x(), mapRect.y(), mapRect.width(),
631                                 mapRect.height()));
632
633         // Viewport has paper dimension
634         if (frame)
635             delete (frame);
636
637         // Restore selection
638         model->reselect();
639     }
640 }
641
642 QRectF MapEditor::getTotalBBox()    // FIXME-2 really needed? Overlaps with scene and VM...
643 {
644     minimizeView();
645     return sceneRect();
646 }
647
648 QImage MapEditor::getImage(QPointF &offset)
649 {
650     QRectF mapRect = getTotalBBox(); // minimized sceneRect
651
652     int d = 10; // border
653     offset = QPointF(mapRect.x() - d / 2, mapRect.y() - d / 2);
654     QImage pix(mapRect.width() + d, mapRect.height() + d, QImage::Format_RGB32);
655
656     QPainter pp(&pix);
657     pp.setRenderHints(renderHints());
658     mapScene->render(&pp,
659                      // Destination:
660                      QRectF(0, 0, mapRect.width() + d, mapRect.height() + d),
661                      // Source in scene:
662                      QRectF(mapRect.x() - d / 2, mapRect.y() - d / 2,
663                             mapRect.width() + d, mapRect.height() + d));
664     return pix;
665 }
666
667 void MapEditor::setAntiAlias(bool b)
668 {
669     setRenderHint(QPainter::Antialiasing, b);
670 }
671
672 void MapEditor::setSmoothPixmap(bool b)
673 {
674     setRenderHint(QPainter::SmoothPixmapTransform, b);
675 }
676
677 void MapEditor::autoLayout()
678 {
679     // Create list with all bounding polygons
680     QList<LinkableMapObj *> mapobjects;
681     QList<ConvexPolygon> polys;
682     ConvexPolygon p;
683     QList<Vector> vectors;
684     QList<Vector> orgpos;
685     QStringList headings; // FIXME-3 testing only
686     Vector v;
687     BranchItem *bi;
688     BranchItem *bi2;
689     BranchObj *bo;
690
691     // Outer loop: Iterate until we no more changes in orientation
692     bool orientationChanged = true;
693     while (orientationChanged) {
694         BranchItem *ri = model->getRootItem();
695         for (int i = 0; i < ri->branchCount(); ++i) {
696             bi = ri->getBranchNum(i);
697             bo = (BranchObj *)bi->getLMO();
698             if (bo) {
699                 mapobjects.append(bo);
700                 p = bo->getBoundingPolygon();
701                 p.calcCentroid();
702                 polys.append(p);
703                 vectors.append(QPointF(0, 0));
704                 orgpos.append(p.at(0));
705                 headings.append(bi->getHeadingPlain());
706             }
707             for (int j = 0; j < bi->branchCount(); ++j) {
708                 bi2 = bi->getBranchNum(j);
709                 bo = (BranchObj *)bi2->getLMO();
710                 if (bo) {
711                     mapobjects.append(bo);
712                     p = bo->getBoundingPolygon();
713                     p.calcCentroid();
714                     polys.append(p);
715                     vectors.append(QPointF(0, 0));
716                     orgpos.append(p.at(0));
717                     headings.append(bi2->getHeadingPlain());
718                 }
719             }
720         }
721
722         // Iterate moving bounding polygons until we have no more collisions
723         int collisions = 1;
724         while (collisions > 0) {
725             collisions = 0;
726             for (int i = 0; i < polys.size() - 1; ++i) {
727                 for (int j = i + 1; j < polys.size(); ++j) {
728                     if (polygonCollision(polys.at(i), polys.at(j),
729                                          QPointF(0, 0))
730                             .intersect) {
731                         collisions++;
732                         if (debug)
733                             qDebug() << "Collision: " << headings[i] << " - "
734                                      << headings[j];
735                         v = polys.at(j).centroid() - polys.at(i).centroid();
736                         v.normalize();
737                         // Add random direction, if only two polygons with
738                         // identical y or x
739                         if (v.x() == 0 || v.y() == 0) {
740                             Vector w(cos(double((int)rand() % 1000)),
741                                      sin(double((int)rand() % 1000)));
742                             w.normalize();
743                             v = v + w;
744                         }
745
746                         // Scale translation vector by area of polygons
747                         vectors[j] = v * 10000 / polys.at(j).weight();
748                         vectors[i] = v * 10000 / polys.at(i).weight();
749                         vectors[i].invert();
750                         // FIXME-3 outer loop, "i" get's changed several
751                         // times...
752                         // Better not move away from centroid of 2 colliding
753                         // polys, but from centroid of _all_
754                     }
755                 }
756             }
757             for (int i = 0; i < vectors.size(); i++) {
758                 // qDebug() << " v="<<vectors[i]<<" "<<headings[i];
759                 if (!vectors[i].isNull())
760                     polys[i].translate(vectors[i]);
761             }
762             // if (debug) qDebug()<< "Collisions total: "<<collisions;
763             // collisions=0;
764         }
765
766         // Finally move the real objects and update
767         QList<LinkableMapObj::Orientation> orients;
768         for (int i = 0; i < polys.size(); i++) {
769             Vector v = polys[i].at(0) - orgpos[i];
770             orients.append(mapobjects[i]->getOrientation());
771             if (!v.isNull()) {
772                 if (debug)
773                     qDebug() << " Moving " << polys.at(i).weight() << " "
774                              << mapobjects[i]->getAbsPos() << " -> "
775                              << mapobjects[i]->getAbsPos() + v << "  "
776                              << headings[i];
777                 // mapobjects[i]->moveBy(v.x(),v.y() );
778                 // mapobjects[i]->setRelPos();
779                 model->startAnimation((BranchObj *)mapobjects[i], v);
780                 if (debug)
781                     qDebug() << i << " Weight: " << polys.at(i).weight() << " "
782                              << v << " " << headings.at(i);
783             }
784         }
785         /*
786         model->reposition();
787         orientationChanged=false;
788         for (int i=0;i<polys.size();i++)
789             if (orients[i]!=mapobjects[i]->getOrientation())
790             {
791             orientationChanged=true;
792             break;
793             }
794         */
795
796         break;
797
798         // orientationChanged=false;
799     } // loop if orientation has changed
800
801     model->emitSelectionChanged();
802 }
803
804 TreeItem *MapEditor::findMapItem(QPointF p, TreeItem *exclude)
805 {
806     // Search XLinks
807     Link *link;
808     for (int i = 0; i < model->xlinkCount(); i++) {
809         link = model->getXLinkNum(i);
810         if (link) {
811             XLinkObj *xlo = link->getXLinkObj();
812             if (xlo && xlo->isInClickBox(p)) {
813                 // Found XLink, now return the nearest XLinkItem of p
814                 qreal d0 = Geometry::distance(p, xlo->getBeginPos());
815                 qreal d1 = Geometry::distance(p, xlo->getEndPos());
816                 if (d0 > d1)
817                     return link->getBeginLinkItem();
818                 else
819                     return link->getEndLinkItem();
820             }
821         }
822     }
823
824     // Search branches (and their childs, e.g. images
825     // Start with mapcenter, no images allowed at rootItem
826     int i = 0;
827     BranchItem *bi = model->getRootItem()->getFirstBranch();
828     TreeItem *found = NULL;
829     while (bi) {
830         found = bi->findMapItem(p, exclude);
831         if (found)
832             return found;
833         i++;
834         bi = model->getRootItem()->getBranchNum(i);
835     }
836     return NULL;
837 }
838
839 void MapEditor::testFunction1() {}
840
841 void MapEditor::testFunction2() { autoLayout(); }
842
843 void MapEditor::toggleWinter()
844 {
845     if (winter) {
846         delete winter;
847         winter = NULL;
848     }
849     else {
850         winter = new Winter(this);
851         QList<QRectF> obstacles;
852         BranchObj *bo;
853         BranchItem *cur = NULL;
854         BranchItem *prev = NULL;
855         model->nextBranch(cur, prev);
856         while (cur) {
857             if (!cur->hasHiddenExportParent()) {
858                 // Branches
859                 bo = (BranchObj *)(cur->getLMO());
860                 if (bo && bo->isVisibleObj())
861                     obstacles.append(bo->getBBox());
862             }
863             model->nextBranch(cur, prev);
864         }
865         winter->setObstacles(obstacles);
866     }
867 }
868
869 BranchItem *MapEditor::getBranchDirectAbove(BranchItem *bi)
870 {
871     if (bi) {
872         int i = bi->num();
873         if (i > 0)
874             return bi->parent()->getBranchNum(i - 1);
875     }
876     return NULL;
877 }
878
879 BranchItem *MapEditor::getBranchAbove(BranchItem *selbi)
880 {
881     if (selbi) {
882         int dz = selbi->depth(); // original depth
883         bool invert = false;
884         if (selbi->getLMO()->getOrientation() == LinkableMapObj::LeftOfCenter)
885             invert = true;
886
887         BranchItem *bi;
888
889         // Look for branch with same parent but directly above
890         if (dz == 1 && invert)
891             bi = getBranchDirectBelow(selbi);
892         else
893             bi = getBranchDirectAbove(selbi);
894
895         if (bi)
896             // direct predecessor
897             return bi;
898
899         // Go towards center and look for predecessor
900         while (selbi->depth() > 0) {
901             selbi = (BranchItem *)(selbi->parent());
902             if (selbi->depth() == 1 && invert)
903                 bi = getBranchDirectBelow(selbi);
904             else
905                 bi = getBranchDirectAbove(selbi);
906             if (bi) {
907                 // turn
908                 selbi = bi;
909                 while (selbi->depth() < dz) {
910                     // try to get back to original depth dz
911                     bi = selbi->getLastBranch();
912                     if (!bi) {
913                         return selbi;
914                     }
915                     selbi = bi;
916                 }
917                 return selbi;
918             }
919         }
920     }
921     return NULL;
922 }
923
924 BranchItem *MapEditor::getBranchDirectBelow(BranchItem *bi)
925 {
926     if (bi) {
927         int i = bi->num();
928         if (i + 1 < bi->parent()->branchCount())
929             return bi->parent()->getBranchNum(i + 1);
930     }
931     return NULL;
932 }
933
934 BranchItem *MapEditor::getBranchBelow(BranchItem *selbi)
935 {
936     if (selbi) {
937         BranchItem *bi;
938         int dz = selbi->depth(); // original depth
939         bool invert = false;
940         if (selbi->getLMO()->getOrientation() == LinkableMapObj::LeftOfCenter)
941             invert = true;
942
943         // Look for branch with same parent but directly below
944         if (dz == 1 && invert)
945             bi = getBranchDirectAbove(selbi);
946         else
947             bi = getBranchDirectBelow(selbi);
948         if (bi)
949             // direct successor
950             return bi;
951
952         // Go towards center and look for neighbour
953         while (selbi->depth() > 0) {
954             selbi = (BranchItem *)(selbi->parent());
955             if (selbi->depth() == 1 && invert)
956                 bi = getBranchDirectAbove(selbi);
957             else
958                 bi = getBranchDirectBelow(selbi);
959             if (bi) {
960                 // turn
961                 selbi = bi;
962                 while (selbi->depth() < dz) {
963                     // try to get back to original depth dz
964                     bi = selbi->getFirstBranch();
965                     if (!bi) {
966                         return selbi;
967                     }
968                     selbi = bi;
969                 }
970                 return selbi;
971             }
972         }
973     }
974     return NULL;
975 }
976
977 BranchItem *MapEditor::getLeftBranch(TreeItem *ti)
978 {
979     if (!ti)
980         return NULL;
981
982     if (ti->isBranchLikeType()) {
983         BranchItem *bi = (BranchItem *)ti;
984         if (bi->depth() == 0) {
985             // Special case: use alternative selection index
986             BranchItem *newbi = bi->getLastSelectedBranchAlt();
987             if (!newbi) {
988                 BranchObj *bo;
989                 // Try to find a mainbranch left of center
990                 for (int i = 0; i < bi->branchCount(); i++) {
991                     newbi = bi->getBranchNum(i);
992                     bo = newbi->getBranchObj();
993                     if (bo &&
994                         bo->getOrientation() == LinkableMapObj::LeftOfCenter)
995                         break;
996                 }
997             }
998             return newbi;
999         }
1000         if (bi->getBranchObj()->getOrientation() ==
1001             LinkableMapObj::RightOfCenter)
1002             // right of center
1003             return (BranchItem *)(bi->parent());
1004         else
1005             // left of center
1006             if (bi->getType() == TreeItem::Branch)
1007             return bi->getLastSelectedBranch();
1008     }
1009
1010     if (ti->parent() && ti->parent()->isBranchLikeType())
1011         return (BranchItem *)(ti->parent());
1012     return NULL;
1013 }
1014
1015 BranchItem *MapEditor::getRightBranch(TreeItem *ti)
1016 {
1017     if (!ti)
1018         return NULL;
1019
1020     if (ti->isBranchLikeType()) {
1021         BranchItem *bi = (BranchItem *)ti;
1022         if (bi->depth() == 0) {
1023             // Special case: use alternative selection index
1024             BranchItem *newbi = bi->getLastSelectedBranch();
1025             if (!newbi) {
1026                 BranchObj *bo;
1027                 // Try to find a mainbranch right of center
1028                 for (int i = 0; i < bi->branchCount(); i++) {
1029                     newbi = bi->getBranchNum(i);
1030                     bo = newbi->getBranchObj();
1031                     if (bo &&
1032                         bo->getOrientation() == LinkableMapObj::RightOfCenter)
1033                         qDebug()
1034                             << "BI found right: " << newbi->getHeadingPlain();
1035                 }
1036             }
1037             return newbi;
1038         }
1039         if (bi->getBranchObj()->getOrientation() ==
1040             LinkableMapObj::LeftOfCenter)
1041             // left of center
1042             return (BranchItem *)(bi->parent());
1043         else
1044             // right of center
1045             if (bi->getType() == TreeItem::Branch)
1046             return (BranchItem *)bi->getLastSelectedBranch();
1047     }
1048
1049     if (ti->parent() && ti->parent()->isBranchLikeType())
1050         return (BranchItem *)(ti->parent());
1051
1052     return NULL;
1053 }
1054
1055 void MapEditor::cursorUp()
1056 {
1057     if (state == MapEditor::EditingHeading)
1058         return;
1059
1060     BranchItem *selbi = model->getSelectedBranch();
1061     BranchItem *bi;
1062     if (selbi) {
1063         // Exactly one branch is currently selected
1064         bi = getBranchAbove(selbi);
1065         if (bi) {
1066             model->select(bi);
1067         } 
1068     } else {
1069         // Nothing selected or already multiple selections
1070         TreeItem *ti = model->lastToggledItem();
1071         if (ti && ti->isBranchLikeType()) {
1072             bi = getBranchAbove( (BranchItem*)ti);
1073             if (bi) 
1074                 model->select(bi);
1075         }
1076     }
1077 }
1078
1079 void MapEditor::cursorUpToggleSelection()
1080 {
1081     if (state == MapEditor::EditingHeading)
1082         return;
1083
1084     BranchItem *selbi = model->getSelectedBranch();
1085     BranchItem *bi;
1086
1087     if (selbi) {
1088         // Exactly one branch is currently selected
1089         bi = getBranchAbove(selbi);
1090         if (bi) model->selectToggle(bi);
1091     } else {
1092         // Nothing selected or already multiple selections
1093         TreeItem *ti = model->lastToggledItem();
1094         if (ti && ti->isBranchLikeType()) {
1095             if (lastToggleDirection == toggleUp)
1096                 bi = getBranchAbove( (BranchItem*)ti);
1097             else
1098                 bi = (BranchItem*)ti;
1099
1100             if (bi) 
1101                 model->selectToggle(bi);
1102         }
1103     }
1104     lastToggleDirection = toggleUp;
1105 }
1106
1107 void MapEditor::cursorDown()
1108 {
1109     if (state == MapEditor::EditingHeading)
1110         return;
1111
1112     BranchItem *selbi = model->getSelectedBranch();
1113     BranchItem *bi;
1114     if (selbi) {
1115         // Exactly one branch is currently selected
1116         bi = getBranchBelow(selbi);
1117         if (bi) {
1118             model->select(bi);
1119         } 
1120     } else {
1121         // Nothing selected or already multiple selections
1122         TreeItem *ti = model->lastToggledItem();
1123         if (ti && ti->isBranchLikeType()) {
1124             bi = getBranchBelow( (BranchItem*)ti);
1125
1126             if (bi) 
1127                 model->select(bi);
1128         }
1129     }
1130 }
1131
1132 void MapEditor::cursorDownToggleSelection()
1133 {
1134     if (state == MapEditor::EditingHeading)
1135         return;
1136
1137     BranchItem *selbi = model->getSelectedBranch();
1138     BranchItem *bi;
1139     if (selbi) {
1140         // Exactly one branch is currently selected
1141         bi = getBranchBelow(selbi);
1142         if (bi) {
1143             model->selectToggle(bi);
1144         } 
1145     } else {
1146         // Nothing selected or already multiple selections
1147         TreeItem *ti = model->lastToggledItem();
1148         if (ti && ti->isBranchLikeType()) {
1149             if (lastToggleDirection == toggleDown)
1150                 bi = getBranchBelow( (BranchItem*)ti);
1151             else
1152                 bi = (BranchItem*)ti;
1153
1154             if (bi) 
1155                 model->selectToggle(bi);
1156         }
1157     }
1158     lastToggleDirection = toggleDown;
1159 }
1160
1161 void MapEditor::cursorLeft()
1162 {
1163     TreeItem *ti = model->getSelectedItem();
1164     if (!ti) {
1165         ti = model->lastToggledItem();
1166         if (!ti) return;
1167     }
1168
1169     BranchItem *bi = getLeftBranch(ti);
1170     if (bi)
1171         model->select(bi);
1172     else {
1173         ImageItem *ii = ti->getFirstImage();
1174         if (ii)
1175             model->select(ii);
1176     }
1177 }
1178
1179 void MapEditor::cursorRight()
1180 {
1181     TreeItem *ti = model->getSelectedItem();
1182     if (!ti) {
1183         ti = model->lastToggledItem();
1184         if (!ti) return;
1185     }
1186
1187     BranchItem *bi = getRightBranch(ti);
1188     if (bi)
1189         model->select(bi);
1190     else {
1191         ImageItem *ii = ti->getFirstImage();
1192         if (ii)
1193             model->select(ii);
1194     }
1195 }
1196
1197 void MapEditor::cursorFirst() { model->selectFirstBranch(); }
1198
1199 void MapEditor::cursorLast() { model->selectLastBranch(); }
1200
1201 void MapEditor::editHeading()
1202 {
1203     if (state == EditingHeading) {
1204         editHeadingFinished();
1205         return;
1206     }
1207
1208     BranchObj *bo = model->getSelectedBranchObj();
1209     BranchItem *bi = model->getSelectedBranch();
1210     if (bo && bi) {
1211         VymText heading = bi->getHeading();
1212         if (heading.isRichText() || bi->getHeadingPlain().contains("\n")) {
1213             mainWindow->windowShowHeadingEditor();
1214             ensureSelectionVisibleAnimated();
1215             return;
1216         }
1217         model->setSelectionBlocked(true);
1218
1219         lineEdit = new QLineEdit;
1220         QGraphicsProxyWidget *pw = mapScene->addWidget(lineEdit);
1221         pw->setZValue(Z_LINEEDIT);
1222         lineEdit->setCursor(Qt::IBeamCursor);
1223         lineEdit->setCursorPosition(1);
1224
1225 #if defined(Q_OS_WINDOWS)
1226         QFont font = lineEdit->font();
1227         font.setPointSize(font.pointSize() + 4);
1228         lineEdit->setFont(font);
1229 #endif
1230
1231         QPointF tl;
1232         QPointF br;
1233         qreal w = 230;
1234         qreal h = 30;
1235         if (bo->getOrientation() != LinkableMapObj::LeftOfCenter) {
1236             tl = bo->getOrnamentsBBox().topLeft();
1237             br = tl + QPointF(w, h);
1238         }
1239         else {
1240             br = bo->getOrnamentsBBox().bottomRight();
1241             tl = br - QPointF(w, h);
1242         }
1243         QRectF r(tl, br);
1244         lineEdit->setGeometry(r.toRect());
1245         pw->setGeometry(r.toRect());
1246
1247         minimizeView();
1248
1249         // Set focus to MapEditor first
1250         // To avoid problems with Cursor up/down
1251         setFocus();
1252
1253         ensureAreaVisibleAnimated(r);
1254
1255         if (heading.getTextASCII() == " ")
1256             heading.setPlainText("");
1257         lineEdit->setText(heading.getTextASCII());
1258         lineEdit->setFocus();
1259         lineEdit->selectAll(); // Hack to enable cursor in lineEdit
1260         lineEdit->deselect();  // probably a Qt bug...
1261
1262         setState(EditingHeading);
1263     }
1264 }
1265
1266 void MapEditor::editHeadingFinished()
1267 {
1268     if (state != EditingHeading || !lineEdit ) {
1269         qWarning() << "ME::editHeadingFinished not editing heading!";
1270     } else {
1271         lineEdit->clearFocus();
1272         QString s = lineEdit->text();
1273         s.replace(QRegExp("\\n"), " "); // Don't paste newline chars
1274         if (s.length() == 0)
1275             s = " "; // Don't allow empty lines, which would screw up drawing
1276         model->setHeadingPlainText(s);
1277         delete (lineEdit);
1278         lineEdit = nullptr;
1279
1280         // FIXME-2 ensureAreaVisible like in starting editing?
1281
1282         // Maybe reselect previous branch
1283         mainWindow->editHeadingFinished(model);
1284
1285         // Autolayout to avoid overlapping branches with longer headings
1286         if (settings.value("/mainwindow/autoLayout/use", "true") == "true")
1287             autoLayout();
1288     }
1289
1290     model->setSelectionBlocked(false);
1291     setState(Neutral);
1292 }
1293
1294 void MapEditor::contextMenuEvent(QContextMenuEvent *e)
1295 {
1296     // Lineedits are already closed by preceding
1297     // mouseEvent, we don't need to close here.
1298
1299     QPointF p = mapToScene(e->pos());
1300     TreeItem *ti = findMapItem(p, NULL);
1301
1302     if (ti) { // MapObj was found
1303         model->select(ti);
1304
1305         LinkableMapObj *lmo = NULL;
1306         BranchItem *selbi = model->getSelectedBranch();
1307         if (ti)
1308             lmo = ((MapItem *)ti)->getLMO();
1309
1310         // Context Menu
1311         if (lmo && selbi) {
1312             QString sysFlagName;
1313             QUuid uid = ((BranchObj *)lmo)->findSystemFlagUidByPos(p);
1314             if (!uid.isNull()) {
1315                 Flag *flag = systemFlagsMaster->findFlagByUid(uid);
1316                 if (flag)
1317                     sysFlagName = flag->getName();
1318             }
1319
1320             if (sysFlagName.startsWith("system-task"))
1321                 taskContextMenu->popup(e->globalPos());
1322             else
1323                 // Context Menu on branch or mapcenter
1324                 branchContextMenu->popup(e->globalPos());
1325         }
1326         else {
1327             if (model->getSelectedImage()) {
1328                 // Context Menu on floatimage
1329                 floatimageContextMenu->popup(e->globalPos());
1330             }
1331             else {
1332                 if (model->getSelectedXLink())
1333                     // Context Menu on XLink
1334                     model->editXLink();
1335             }
1336         }
1337     }
1338     else { // No MapObj found, we are on the Canvas itself
1339         // Context Menu on scene
1340
1341         // Open context menu synchronously to position new mapcenter
1342         model->setContextPos(p);
1343         canvasContextMenu->exec(e->globalPos());
1344         model->unsetContextPos();
1345     }
1346     e->accept();
1347 }
1348
1349 void MapEditor::keyPressEvent(QKeyEvent *e)
1350 {
1351     if (e->key() == Qt::Key_PageUp || e->key() == Qt::Key_PageDown)
1352         // Ignore PageUP/Down to avoid scrolling with keys
1353         return;
1354
1355     if (e->modifiers() & Qt::ShiftModifier) {
1356         switch (mainWindow->getModMode()) {
1357         case Main::ModModePoint:
1358             setCursor(Qt::ArrowCursor);
1359             break;
1360         case Main::ModModeColor:
1361             setCursor(PickColorCursor);
1362             break;
1363         case Main::ModModeXLink:
1364             setCursor(XLinkCursor);
1365             break;
1366         case Main::ModModeMoveObject:
1367             setCursor(Qt::PointingHandCursor);
1368             break;
1369         case Main::ModModeMoveView:
1370             setCursor(QPixmap(":/mode-move-view.png"));
1371             break;
1372         default:
1373             setCursor(Qt::ArrowCursor);
1374             break;
1375         }
1376     }
1377     QGraphicsView::keyPressEvent(e);
1378 }
1379
1380 void MapEditor::keyReleaseEvent(QKeyEvent *e)
1381 {
1382     if (!(e->modifiers() & Qt::ControlModifier))
1383         setCursor(Qt::ArrowCursor);
1384 }
1385
1386 void MapEditor::startMovingView(QMouseEvent *e)
1387 {
1388     setState(MovingView);
1389     movingObj = NULL; // move Content not Obj
1390     movingObj_offset = e->globalPos();
1391     movingCont_start =
1392         QPointF(horizontalScrollBar()->value(), verticalScrollBar()->value());
1393     movingVec = QPointF(0, 0);
1394     setCursor(HandOpenCursor);
1395 }
1396
1397 void MapEditor::mousePressEvent(QMouseEvent *e)
1398 {
1399     // Ignore right clicks
1400     if (e->button() == Qt::RightButton) {
1401         e->ignore();
1402         QGraphicsView::mousePressEvent(e);
1403         return;
1404     }
1405
1406     // Check if we need to reset zoomFactor for middle button + Ctrl
1407     if (e->button() == Qt::MidButton && e->modifiers() & Qt::ControlModifier) {
1408         setZoomFactorTarget(1);
1409         setAngleTarget(0);
1410         return;
1411     }
1412
1413     QPointF p = mapToScene(e->pos());
1414     TreeItem *ti_found = findMapItem(p, NULL);
1415     LinkableMapObj *lmo_found = NULL;
1416     if (ti_found)
1417         lmo_found = ((MapItem *)ti_found)->getLMO();
1418
1419
1420     // Allow selecting text in QLineEdit if necessary
1421     if (model->isSelectionBlocked()) {
1422         e->ignore();
1423         QGraphicsView::mousePressEvent(e);
1424         return;
1425     }
1426
1427     // Stop editing in LineEdit
1428     if (state == EditingHeading) editHeadingFinished();
1429
1430     QString sysFlagName;
1431     QUuid uid;
1432     if (lmo_found) {
1433         uid = ((BranchObj *)lmo_found)->findSystemFlagUidByPos(p);
1434         if (!uid.isNull()) {
1435             Flag *flag = systemFlagsMaster->findFlagByUid(uid);
1436             if (flag)
1437                 sysFlagName = flag->getName();
1438         }
1439     }
1440
1441     /*
1442     qDebug() << "ME::mouse pressed\n";
1443     qDebug() << "  lmo_found=" << lmo_found;
1444     qDebug() << "   ti_found=" << ti_found;
1445     //if (ti_found) qDebug() << "   ti_found="<<ti_found->getHeading();
1446     qDebug() << " flag=" << sysFlagName;
1447     */
1448
1449     // Check modifier key (before selecting object!)
1450     if (ti_found && (e->modifiers() & Qt::ShiftModifier)) {
1451         if (mainWindow->getModMode() == Main::ModModeColor) {
1452             setState(PickingColor);
1453             mainWindow->setCurrentColor(ti_found->getHeadingColor());
1454             if (e->modifiers() & Qt::ControlModifier)
1455                 model->colorBranch(ti_found->getHeadingColor());
1456             else
1457                 model->colorSubtree(ti_found->getHeadingColor());
1458             return;
1459         }
1460
1461         if (mainWindow->getModMode() == Main::ModModeMoveView) {
1462             startMovingView(e);
1463             return;
1464         }
1465     }
1466
1467     // Check vymlink  modifier (before selecting object!)
1468     if (ti_found && sysFlagName == "system-vymLink") {
1469         model->select(ti_found);
1470         if (e->modifiers() & Qt::ControlModifier) {
1471             if (e->modifiers() & Qt::ShiftModifier)
1472                 model->deleteVymLink();
1473             else
1474                 mainWindow->editOpenVymLink(true);
1475         } else
1476             mainWindow->editOpenVymLink(false);
1477         return;
1478     }
1479
1480     // Select the clicked object, if not moving without linking
1481     if (ti_found && (e->modifiers() & Qt::ShiftModifier)) {
1482         if (mainWindow->getModMode() == Main::ModModePoint) {
1483             model->selectToggle(ti_found);
1484             lastToggleDirection = toggleUndefined;
1485         }
1486     }
1487     else
1488         model->select(ti_found);
1489
1490     e->accept();
1491
1492     // Take care of  remaining system flags _or_ modifier modes
1493     if (lmo_found) {
1494         if (!sysFlagName.isEmpty()) {
1495             // systemFlag clicked
1496             if (sysFlagName.contains("system-url")) {
1497                 if (e->modifiers() & Qt::ControlModifier)
1498                     mainWindow->editOpenURLTab();
1499                 else
1500                     mainWindow->editOpenURL();
1501             }
1502             else if (sysFlagName == "system-note")
1503                 mainWindow->windowToggleNoteEditor();
1504             else if (sysFlagName == "hideInExport")
1505                 model->toggleHideExport();
1506             else if (sysFlagName.startsWith("system-task-"))
1507                 model->cycleTaskStatus();
1508             return;
1509         }
1510         else {
1511             // Take care of xLink: Open context menu with targets
1512             // if clicked near to begin of xlink
1513             if (ti_found->xlinkCount() > 0 &&
1514                 ti_found->getType() != TreeItem::MapCenter &&
1515                 lmo_found->getBBox().width() > 30) {
1516                 if ((lmo_found->getOrientation() !=
1517                          LinkableMapObj::RightOfCenter &&
1518                      p.x() < lmo_found->getBBox().left() + 10) ||
1519                     (lmo_found->getOrientation() !=
1520                          LinkableMapObj::LeftOfCenter &&
1521                      p.x() > lmo_found->getBBox().right() - 10)) {
1522                     // FIXME-4 similar code in mainwindow::updateActions
1523                     QMenu menu;
1524                     QList<QAction *> alist;
1525                     QList<BranchItem *> blist;
1526                     for (int i = 0; i < ti_found->xlinkCount(); i++) {
1527                         XLinkItem *xli = ti_found->getXLinkItemNum(i);
1528                         BranchItem *bit = xli->getPartnerBranch();
1529                         if (bit)
1530                             alist.append(
1531                                 new QAction(ti_found->getXLinkItemNum(i)
1532                                                 ->getPartnerBranch()
1533                                                 ->getHeadingPlain(),
1534                                             &menu));
1535                     }
1536                     menu.addActions(alist);
1537                     QAction *ra = menu.exec(e->globalPos());
1538                     if (ra)
1539                         model->select(blist.at(alist.indexOf(ra)));
1540                     while (!alist.isEmpty()) {
1541                         QAction *a = alist.takeFirst();
1542                         delete a;
1543                     }
1544                     return;
1545                 }
1546             }
1547         }
1548     }
1549
1550     // XLink modifier, create new XLink
1551     BranchItem *selbi = model->getSelectedBranch();
1552     if (selbi && mainWindow->getModMode() == Main::ModModeXLink &&
1553         (e->modifiers() & Qt::ShiftModifier)) {
1554         setState(DrawingLink);
1555         tmpLink = new Link(model);
1556         tmpLink->setBeginBranch(selbi);
1557         tmpLink->createMapObj();
1558         tmpLink->setStyleBegin("None");
1559         tmpLink->setStyleEnd("None");
1560         tmpLink->setEndPoint(mapToScene(e->pos()));
1561         tmpLink->updateLink();
1562         return;
1563     }
1564
1565     // Start moving around
1566     if (lmo_found) {
1567         // Left Button      Move Branches
1568         if (e->button() == Qt::LeftButton) {
1569             // No system flag clicked, take care of moving modes or simply
1570             // moving
1571             movingObj_offset.setX(p.x() - lmo_found->x());
1572             movingObj_offset.setY(p.y() - lmo_found->y());
1573             movingObj_orgPos.setX(lmo_found->x());
1574             movingObj_orgPos.setY(lmo_found->y());
1575             if (ti_found->depth() > 0) {
1576                 lmo_found->setRelPos();
1577                 movingObj_orgRelPos = lmo_found->getRelPos();
1578             }
1579
1580             if (mainWindow->getModMode() == Main::ModModeMoveObject &&
1581                 e->modifiers() & Qt::ShiftModifier) {
1582                 setState(MovingObjectWithoutLinking);
1583             }
1584             else
1585                 setState(MovingObject);
1586
1587             movingObj = model->getSelectedLMO();
1588         }
1589         else
1590             // Middle Button    Toggle Scroll
1591             // (On Mac OS X this won't work, but we still have
1592             // a button in the toolbar)
1593             if (e->button() == Qt::MidButton)
1594             model->toggleScroll();
1595     }
1596     else { // No lmo found, check XLinks
1597         if (ti_found) {
1598             if (ti_found->getType() == TreeItem::XLink) {
1599                 XLinkObj *xlo = (XLinkObj *)((MapItem *)ti_found)->getMO();
1600                 if (xlo) {
1601                     setState(DrawingXLink);
1602                     int i = xlo->ctrlPointInClickBox(p);
1603                     if (i >= 0)
1604                         xlo->setSelection(i);
1605                     movingObj_offset.setX(p.x() - xlo->x());
1606                     movingObj_offset.setY(p.y() - xlo->y());
1607                     movingObj_orgPos.setX(xlo->x());
1608                     movingObj_orgPos.setY(xlo->y());
1609                 }
1610             }
1611         }
1612         else { // No MapObj found, we are on the scene itself
1613             // Left Button          move Pos of sceneView
1614             if (e->button() == Qt::LeftButton ||
1615                 e->button() == Qt::MiddleButton) {
1616                 startMovingView(e);
1617                 return;
1618             }
1619         }
1620     }
1621 }
1622
1623 void MapEditor::mouseMoveEvent(QMouseEvent *e)
1624 {
1625     // Show mouse position for debugging in statusBar
1626     if (debug && e->modifiers() & Qt::ControlModifier)
1627         mainWindow->statusMessage(
1628             QString("ME::mousePressEvent  Scene: %1  widget: %2")
1629                 .arg(qpointFToString(mapToScene(e->pos())))
1630                 .arg(qpointFToString(e->pos())));
1631
1632     // Allow selecting text in QLineEdit if necessary
1633     if (model->isSelectionBlocked()) {
1634         e->ignore();
1635         QGraphicsView::mouseMoveEvent(e);
1636         return;
1637     }
1638
1639     // Move sceneView
1640     if (state == MovingView &&
1641         (e->buttons() == Qt::LeftButton || e->buttons() == Qt::MiddleButton)) {
1642         QPointF p = e->globalPos();
1643         movingVec.setX(-p.x() + movingObj_offset.x());
1644         movingVec.setY(-p.y() + movingObj_offset.y());
1645         horizontalScrollBar()->setSliderPosition(
1646             (int)(movingCont_start.x() + movingVec.x()));
1647         verticalScrollBar()->setSliderPosition(
1648             (int)(movingCont_start.y() + movingVec.y()));
1649         // Avoid flickering
1650         scrollBarPosAnimation.stop();
1651         viewCenterAnimation.stop();
1652         rotationAnimation.stop();
1653         // zoomAnimation.stop();
1654
1655         return;
1656     }
1657
1658     TreeItem *seli = model->getSelectedItem();
1659
1660     MapObj *mosel = NULL;
1661     if (seli)
1662         mosel = ((MapItem *)seli)->getMO();
1663
1664     // If not already happened during mousepress, we might need to switch state
1665     if (mainWindow->getModMode() == Main::ModModeMoveObject &&
1666         e->modifiers() & Qt::ShiftModifier && e->buttons() == Qt::LeftButton) {
1667         state = MovingObjectWithoutLinking;
1668     }
1669
1670     // Move the selected MapObj
1671     if (mosel &&
1672         (state == MovingObject || state == MovingObjectWithoutLinking ||
1673          state == DrawingXLink)) {
1674         int margin = 50;
1675
1676         // Check if we have to scroll
1677         vPan.setX(0);
1678         vPan.setY(0);
1679         if (e->y() >= 0 && e->y() <= margin)
1680             vPan.setY(e->y() - margin);
1681         else if (e->y() <= height() && e->y() > height() - margin)
1682             vPan.setY(e->y() - height() + margin);
1683         if (e->x() >= 0 && e->x() <= margin)
1684             vPan.setX(e->x() - margin);
1685         else if (e->x() <= width() && e->x() > width() - margin)
1686             vPan.setX(e->x() - width() + margin);
1687
1688         pointerPos = e->pos();
1689         pointerMod = e->modifiers();
1690         moveObject();
1691     } // selection && moving_obj
1692
1693     // Draw a link from one branch to another
1694     if (state == DrawingLink) {
1695         tmpLink->setEndPoint(mapToScene(e->pos()));
1696         tmpLink->updateLink();
1697     }
1698 }
1699
1700 void MapEditor::moveObject()
1701 {
1702     if (!panningTimer->isActive())
1703         panningTimer->start(50);
1704
1705     QPointF p = mapToScene(pointerPos);
1706     TreeItem *seli = model->getSelectedItem();
1707     LinkableMapObj *lmosel = NULL;
1708     if (seli)
1709         lmosel = ((MapItem *)seli)->getLMO();
1710
1711     objectMoved = true;
1712
1713     // reset cursor if we are moving and don't copy
1714
1715     // Check if we could link
1716     TreeItem *ti_found = findMapItem(p, seli);
1717     BranchItem *bi_dst = NULL;
1718     LinkableMapObj *lmo_dst = NULL;
1719     if (ti_found && ti_found != seli && ti_found->isBranchLikeType()) {
1720         bi_dst = (BranchItem *)ti_found;
1721         lmo_dst = bi_dst->getLMO();
1722     }
1723     else
1724         bi_dst = NULL;
1725
1726     if (lmosel) {
1727         if (seli->getType() == TreeItem::Image) {
1728             FloatImageObj *fio = (FloatImageObj *)lmosel;
1729             fio->moveCenter(p.x() - movingObj_offset.x(),
1730                             p.y() - movingObj_offset.y());
1731             fio->setRelPos();
1732             fio->updateLinkGeometry(); // no need for reposition, if we update
1733                                        // link here
1734             model->emitSelectionChanged(); // position has changed
1735
1736             // Relink float to new mapcenter or branch, if shift is pressed
1737             // Only relink, if selection really has a new parent
1738             if (pointerMod == Qt::ShiftModifier && bi_dst &&
1739                 bi_dst != seli->parent()) {
1740                 // Also save the move which was done so far
1741                 QString pold = qpointFToString(movingObj_orgRelPos);
1742                 QString pnow = qpointFToString(fio->getRelPos());
1743                 model->saveState(seli, "moveRel " + pold, seli,
1744                                  "moveRel " + pnow,
1745                                  QString("Move %1 to relative position %2")
1746                                      .arg(model->getObjectName(lmosel))
1747                                      .arg(pnow));
1748                 model->reposition();
1749
1750                 model->relinkImage((ImageItem *)seli, bi_dst);
1751                 model->select(seli);
1752             }
1753         }
1754         else if (seli->isBranchLikeType()) { // selection != a FloatObj
1755             if (seli->depth() == 0) {
1756                 // Move mapcenter
1757                 lmosel->move(p - movingObj_offset);
1758                 if (pointerMod == Qt::ControlModifier) {
1759                     // Move only mapcenter, leave its children where they are
1760                     QPointF v;
1761                     v = lmosel->getAbsPos();
1762                     for (int i = 0; i < seli->branchCount(); ++i) {
1763                         seli->getBranchObjNum(i)->setRelPos();
1764                         seli->getBranchObjNum(i)->setOrientation();
1765                     }
1766                 }
1767             }
1768             else {
1769                 if (seli->depth() == 1) {
1770                     // Move mainbranch
1771                     if (!lmosel->hasParObjTmp())
1772                         lmosel->move(p - movingObj_offset);
1773                     lmosel->setRelPos();
1774                 }
1775                 else {
1776                     // d>1, move ordinary branch
1777                     if (lmosel->getOrientation() ==
1778                         LinkableMapObj::LeftOfCenter)
1779                         // Add width of bbox here, otherwise alignRelTo will
1780                         // cause jumping around
1781                         lmosel->move(p.x() - movingObj_offset.x(),
1782                                      p.y() - movingObj_offset.y() +
1783                                          lmosel->getTopPad());
1784                     else
1785                         lmosel->move(p.x() - movingObj_offset.x(),
1786                                      p.y() - movingObj_offset.y() -
1787                                          lmosel->getTopPad());
1788                     BranchItem *selbi = ((BranchItem *)seli);
1789                     if (selbi->parentBranch()->getChildrenLayout() ==
1790                         BranchItem::FreePositioning)
1791                         lmosel->setRelPos();
1792                 }
1793
1794             } // depth>0
1795
1796             // Maybe we can relink temporary?
1797             if (bi_dst && state != MovingObjectWithoutLinking) {
1798                 if (pointerMod == Qt::ControlModifier) {
1799                     // Special case: CTRL to link below dst
1800                     lmosel->setParObjTmp(lmo_dst, p, +1);
1801                 }
1802                 else if (pointerMod == Qt::ShiftModifier)
1803                     lmosel->setParObjTmp(lmo_dst, p, -1);
1804                 else
1805                     lmosel->setParObjTmp(lmo_dst, p, 0);
1806             }
1807             else
1808                 lmosel->unsetParObjTmp();
1809
1810             // reposition subbranch
1811             lmosel->reposition();
1812
1813             QItemSelection sel = model->getSelectionModel()->selection();
1814             updateSelection(sel, sel); // position has changed
1815
1816             // In winter mode shake snow from heading
1817             if (winter)
1818                 model->emitDataChanged(seli);
1819         } // Moving branchLikeType
1820     }     // End of lmosel != NULL
1821     else if (seli && seli->getType() == TreeItem::XLink) {
1822         // Move XLink control point
1823         MapObj *mosel = ((MapItem *)seli)->getMO();
1824         if (mosel) {
1825             mosel->move(p - movingObj_offset); // FIXME-3 Missing savestate
1826             model->setChanged();
1827             model->emitSelectionChanged();
1828         }
1829     }
1830     else
1831         qWarning("ME::moveObject  Huh? I'm confused.");
1832
1833     scene()->update();
1834
1835     return;
1836 }
1837
1838 void MapEditor::mouseReleaseEvent(QMouseEvent *e)
1839 {
1840     // Allow selecting text in QLineEdit if necessary
1841     if (model->isSelectionBlocked()) {
1842         e->ignore();
1843         QGraphicsView::mouseReleaseEvent(e);
1844         return;
1845     }
1846
1847     QPointF p = mapToScene(e->pos());
1848     TreeItem *seli = model->getSelectedItem();
1849
1850     TreeItem *dsti = NULL;
1851     if (seli)
1852         dsti = findMapItem(p, seli);
1853     LinkableMapObj *dst = NULL;
1854     BranchItem *selbi = model->getSelectedBranch();
1855     if (dsti && dsti->isBranchLikeType())
1856         dst = ((MapItem *)dsti)->getLMO();
1857     else
1858         dsti = NULL;
1859
1860     // Have we been picking color?
1861     if (state == PickingColor) {
1862         setCursor(Qt::ArrowCursor);
1863         // Check if we are over another branch
1864         if (dst) {
1865             if (e->modifiers() & Qt::ShiftModifier)
1866                 model->colorBranch(mainWindow->getCurrentColor());
1867             else
1868                 model->colorSubtree(mainWindow->getCurrentColor());
1869         }
1870         setState(Neutral);
1871         return;
1872     }
1873
1874     // Have we been drawing a link?
1875     if (state == DrawingLink) {
1876         setState(Neutral);
1877         // Check if we are over another branch
1878         if (dsti) {
1879             tmpLink->setEndBranch(((BranchItem *)dsti));
1880             tmpLink->activate();
1881             tmpLink->updateLink();
1882             if (model->createLink(tmpLink)) {
1883                 model->saveState(
1884                     tmpLink->getBeginLinkItem(), "remove ()", seli,
1885                     QString("addXLink (\"%1\",\"%2\",%3,\"%4\",\"%5\")")
1886                         .arg(model->getSelectString(tmpLink->getBeginBranch()))
1887                         .arg(model->getSelectString(tmpLink->getEndBranch()))
1888                         .arg(tmpLink->getPen().width())
1889                         .arg(tmpLink->getPen().color().name())
1890                         .arg(penStyleToString(tmpLink->getPen().style())),
1891                     QString("Adding Link from %1 to %2")
1892                         .arg(model->getObjectName(seli))
1893                         .arg(model->getObjectName(dsti)));
1894                 return;
1895             }
1896         }
1897         delete (tmpLink);
1898         tmpLink = NULL;
1899         return;
1900     }
1901
1902     // Have we been moving something?
1903     if (seli && state == MovingObject) {
1904         panningTimer->stop();
1905         if (seli->getType() == TreeItem::Image) {
1906             FloatImageObj *fio = (FloatImageObj *)(((MapItem *)seli)->getLMO());
1907             if (fio) {
1908                 // Moved Image, we need to reposition
1909                 QString pold = qpointFToString(movingObj_orgRelPos);
1910                 QString pnow = qpointFToString(fio->getRelPos());
1911                 model->saveState(seli, "moveRel " + pold, seli,
1912                                  "moveRel " + pnow,
1913                                  QString("Move %1 to relative position %2")
1914                                      .arg(model->getObjectName(seli))
1915                                      .arg(pnow));
1916
1917                 model->emitDataChanged(
1918                     seli->parent()); // Parent of image has changed
1919                 model->reposition();
1920             }
1921         }
1922
1923         if (selbi && selbi->depth() == 0) {
1924             if (movingObj_orgPos != selbi->getBranchObj()->getAbsPos()) {
1925                 QString pold = qpointFToString(movingObj_orgPos);
1926                 QString pnow =
1927                     qpointFToString(selbi->getBranchObj()->getAbsPos());
1928
1929                 model->saveState(selbi, "move " + pold, selbi, "move " + pnow,
1930                                  QString("Move mapcenter %1 to position %2")
1931                                      .arg(model->getObjectName(selbi))
1932                                      .arg(pnow));
1933             }
1934         }
1935
1936         if (seli->isBranchLikeType()) //(seli->getType() == TreeItem::Branch )
1937         {                             // A branch was moved
1938             LinkableMapObj *lmosel = NULL;
1939             lmosel = ((MapItem *)seli)->getLMO();
1940
1941             // save the position in case we link to mapcenter
1942             QPointF savePos = QPointF(lmosel->getAbsPos());
1943
1944             // Reset the temporary drawn link to the original one
1945             lmosel->unsetParObjTmp();
1946
1947             // For Redo we may need to save original selection
1948             QString preSelStr = model->getSelectString(seli);
1949
1950             if (dsti && objectMoved && state != MovingObjectWithoutLinking) {
1951                 // We have a destination, relink to that
1952                 BranchObj *selbo = model->getSelectedBranchObj();
1953
1954                 QString preParStr = model->getSelectString(seli->parent());
1955                 QString preNum = QString::number(seli->num(), 10);
1956                 QString preDstParStr;
1957
1958                 if (e->modifiers() & Qt::ShiftModifier &&
1959                     dsti->parent()) { // Link above dst
1960                     preDstParStr = model->getSelectString(dsti->parent());
1961                     model->relinkBranch((BranchItem *)seli,
1962                                         (BranchItem *)dsti->parent(),
1963                                         ((BranchItem *)dsti)->num(), true);
1964                 }
1965                 else if (e->modifiers() & Qt::ControlModifier &&
1966                          dsti->parent()) {
1967                     // Link below dst
1968                     preDstParStr = model->getSelectString(dsti->parent());
1969                     model->relinkBranch((BranchItem *)seli,
1970                                         (BranchItem *)dsti->parent(),
1971                                         ((BranchItem *)dsti)->num() + 1, true);
1972                 }
1973                 else { // Append to dst
1974                     preDstParStr = model->getSelectString(dsti);
1975                     model->relinkBranch((BranchItem *)seli, (BranchItem *)dsti,
1976                                         -1, true, movingObj_orgPos);
1977                     if (dsti->depth() == 0)
1978                         selbo->move(savePos);
1979                 }
1980             }
1981             else {
1982                 // No destination, undo  temporary move
1983
1984                 if (seli->depth() == 1) {
1985                     // The select string might be different _after_ moving
1986                     // around. Therefor reposition and then use string of old
1987                     // selection, too
1988                     model->reposition();
1989
1990                     QPointF rp(lmosel->getRelPos());
1991                     if (rp != movingObj_orgRelPos) {
1992                         QString ps = qpointFToString(rp);
1993                         model->saveState(
1994                             model->getSelectString(lmosel),
1995                             "moveRel " + qpointFToString(movingObj_orgRelPos),
1996                             preSelStr, "moveRel " + ps,
1997                             QString("Move %1 to relative position %2")
1998                                 .arg(model->getObjectName(lmosel))
1999                                 .arg(ps));
2000                     }
2001                 }
2002
2003                 if (selbi->parentBranch()->getChildrenLayout() ==
2004                     BranchItem::FreePositioning) {
2005                     lmosel->setRelPos();
2006                     model->reposition();
2007                 }
2008                 else {
2009
2010                     // Draw the original link, before selection was moved around
2011                     if (settings.value("/animation/use", true).toBool() &&
2012                         seli->depth() > 1
2013                         //                  && distance
2014                         //(lmosel->getRelPos(),movingObj_orgRelPos)<3
2015                     ) {
2016                         lmosel->setRelPos(); // calc relPos first for starting
2017                                              // point
2018
2019                         model->startAnimation((BranchObj *)lmosel,
2020                                               lmosel->getRelPos(),
2021                                               movingObj_orgRelPos);
2022                     }
2023                     else
2024                         model->reposition();
2025                 }
2026             }
2027         }
2028         // Finally resize scene, if needed
2029         scene()->update();
2030         movingObj = NULL;
2031         objectMoved = false;
2032         vPan = QPoint();
2033     }
2034     else
2035         // maybe we moved View: set old cursor
2036         setCursor(Qt::ArrowCursor);
2037
2038     if (state != EditingHeading)
2039         setState(Neutral); // Continue editing after double click!
2040
2041     QGraphicsView::mouseReleaseEvent(e);
2042 }
2043
2044 void MapEditor::mouseDoubleClickEvent(QMouseEvent *e)
2045 {
2046     // Allow selecting text in QLineEdit if necessary
2047     if (model->isSelectionBlocked()) {
2048         e->ignore();
2049         QGraphicsView::mouseDoubleClickEvent(e);
2050         return;
2051     }
2052
2053     if (e->button() == Qt::LeftButton) {
2054         QPointF p = mapToScene(e->pos());
2055         TreeItem *ti = findMapItem(p, NULL);
2056         LinkableMapObj *lmo;
2057         if (ti) {
2058             if (state == EditingHeading)
2059                 editHeadingFinished();
2060             model->select(ti);
2061             BranchItem *selbi = model->getSelectedBranch();
2062             if (selbi) {
2063                 lmo = ((MapItem *)ti)->getLMO();
2064                 if (lmo) {
2065                     QUuid uid = ((BranchObj *)lmo)->findSystemFlagUidByPos(p);
2066
2067                     // Don't edit heading when double clicking system flag:
2068                     if (!uid.isNull())
2069                         return;
2070                 }
2071             }
2072             e->accept();
2073             editHeading();
2074         }
2075     }
2076 }
2077
2078 void MapEditor::wheelEvent(QWheelEvent *e)
2079 {
2080     if (e->modifiers() & Qt::ControlModifier &&
2081         e->angleDelta().y() != 0) {
2082         QPointF p = mapToScene(e->position().toPoint());
2083         if (e->angleDelta().y() > 0)
2084             // setZoomFactorTarget (zoomFactorTarget*1.15);
2085             setViewCenterTarget(p, zoomFactorTarget * 1.15, angleTarget);
2086         else
2087             // setZoomFactorTarget (zoomFactorTarget*0.85);
2088             setViewCenterTarget(p, zoomFactorTarget * 0.85, angleTarget);
2089     }
2090     else {
2091         scrollBarPosAnimation.stop();
2092         QGraphicsView::wheelEvent(e);
2093     }
2094 }
2095
2096 void MapEditor::focusOutEvent(QFocusEvent *)
2097 {
2098     // qDebug()<<"ME::focusOutEvent"<<e->reason();
2099     if (state == EditingHeading)
2100         editHeadingFinished();
2101 }
2102
2103 void MapEditor::resizeEvent(QResizeEvent *e) { QGraphicsView::resizeEvent(e); }
2104
2105 void MapEditor::dragEnterEvent(QDragEnterEvent *event)
2106 {
2107     // for (unsigned int i=0;event->format(i);i++) // Debug mime type
2108     //  cerr << event->format(i) << endl;
2109
2110     if (event->mimeData()->hasImage())
2111         event->acceptProposedAction();
2112     else if (event->mimeData()->hasUrls())
2113         event->acceptProposedAction();
2114 }
2115
2116 void MapEditor::dragMoveEvent(QDragMoveEvent *) {}
2117
2118 void MapEditor::dragLeaveEvent(QDragLeaveEvent *event) { event->accept(); }
2119
2120 void MapEditor::dropEvent(QDropEvent *event)
2121 {
2122     BranchItem *selbi = model->getSelectedBranch();
2123     if (selbi) {
2124         if (debug) {
2125             foreach (QString format, event->mimeData()->formats())
2126                 qDebug() << "MapEditor: Dropped format: " << qPrintable(format);
2127             foreach (QUrl url, event->mimeData()->urls()) {
2128                 qDebug() << "  URL-path:" << url.path();
2129                 qDebug() << "URL-string:" << url.toString();
2130                 qDebug() << "       enc:" << url.toEncoded();
2131                 qDebug() << "     valid:" << url.isValid();
2132             }
2133             qDebug() << "============== mimeData ===================";
2134             qDebug() << "has-img : " << event->mimeData()->hasImage();
2135             qDebug() << "has-urls: " << event->mimeData()->hasUrls();
2136             qDebug() << "    text: " << event->mimeData()->text();
2137             qDebug() << "===========================================";
2138         }
2139
2140         if (event->mimeData()->hasUrls()) {
2141             // Try text representation first, which works on windows, but in
2142             // Linux only for https, not local images
2143             QString url = event->mimeData()->text();
2144             if (url.isEmpty()) {
2145                 QByteArray ba =
2146                     event->mimeData()->urls().first().path().toLatin1();
2147                 QByteArray ba2;
2148                 for (int i = 0; i < ba.count(); i++)
2149                     if (ba.at(i) != 0)
2150                         ba2.append(ba.at(i));
2151                 url = ba2;
2152             }
2153
2154             BranchItem *bi = NULL;
2155             // Workaround to avoid adding empty branches
2156             if (!url.isEmpty()) {
2157                 if (url.startsWith("file://"))
2158                     url.remove(0, 7);
2159
2160 #if defined(Q_OS_WIN32)
2161                 if (url.startsWith("/"))
2162                     url.remove(0, 1);
2163 #endif
2164                 if (isImage(url)) {
2165                     if (debug)
2166                         qDebug() << "dropped url seems to be image: " << url;
2167                     // Image, try to download or set image from local file
2168                     if (url.startsWith("http"))
2169                         model->downloadImage(url);
2170                     else
2171                         model->loadImage(bi, url);
2172                     if (debug)
2173                         qDebug() << "finished loading image";
2174                 }
2175                 else {
2176                     bi = model->addNewBranch();
2177                     if (bi) {
2178                         model->select(bi);
2179                         if (url.endsWith(".vym", Qt::CaseInsensitive))
2180                             model->setVymLink(url);
2181                         else {
2182                             model->setURL(url);
2183
2184                             // Shorten long URLs for heading
2185                             int i = url.indexOf("?");
2186                             QString url_short = url.left(i);
2187                             if (i > 0) 
2188                                 url_short = url_short + "...";
2189                             model->setHeadingPlainText(url_short);
2190                         }
2191
2192                         model->select(bi->parent());
2193                     }
2194                 }
2195             }
2196         }
2197     }
2198     event->acceptProposedAction();
2199 }
2200
2201 void MapEditor::setState(EditorState s)
2202 {
2203     if (state != Neutral && s != Neutral)
2204         qWarning() << "MapEditor::setState  switching directly from " << state
2205                    << " to " << s;
2206     state = s;
2207     /* if (debug)
2208     {
2209         QString s;
2210         switch (state)
2211         {
2212         case Neutral:
2213             s = "Neutral";
2214             break;
2215         case EditingHeading:
2216             s = "EditingHeading";
2217             break;
2218         case EditingLink:
2219             s = "EditingLink";
2220             break;
2221         case MovingObject:
2222             s = "MovingObject";
2223             break;
2224         case MovingObjectWithoutLinking:
2225             s = "MovingObjectWithoutLinking";
2226             break;
2227         case MovingView:
2228             s = "MovingView";
2229             break;
2230         case PickingColor:
2231             s = "PickingColor";
2232             break;
2233         case DrawingLink:
2234             s = "DrawingLink";
2235             break;
2236         }
2237         qDebug() << "MapEditor: State " << s << " of " << model->getMapName();
2238     }
2239     */
2240 }
2241
2242 MapEditor::EditorState MapEditor::getState() { return state; }
2243
2244 void MapEditor::updateSelection(QItemSelection nsel, QItemSelection dsel)
2245 {
2246     Q_UNUSED(nsel);
2247
2248     QList<MapItem *> itemsSelected;
2249     QList<MapItem *> itemsDeselected;
2250
2251     QItemSelection sel = model->getSelectionModel()->selection();
2252
2253     LinkableMapObj *lmo;
2254
2255     // Add new selected objects
2256     if (sel.indexes().count() > 1)
2257         mainWindow->statusMessage(
2258             tr("%1 items selected").arg(sel.indexes().count()));
2259
2260     foreach (QModelIndex ix, sel.indexes()) {
2261         MapItem *mi = static_cast<MapItem *>(ix.internalPointer());
2262         if (mi->isBranchLikeType() || mi->getType() == TreeItem::Image ||
2263             mi->getType() == TreeItem::XLink)
2264             if (!itemsSelected.contains(mi))
2265                 itemsSelected.append(mi);
2266         lmo = mi->getLMO();
2267         if (lmo)
2268             mi->getLMO()->updateVisibility();
2269     }
2270
2271     // Delete objects meanwhile removed from selection
2272     foreach (QModelIndex ix, dsel.indexes()) {
2273         MapItem *mi = static_cast<MapItem *>(ix.internalPointer());
2274         if (mi->isBranchLikeType() || mi->getType() == TreeItem::Image ||
2275             mi->getType() == TreeItem::XLink)
2276             if (!itemsDeselected.contains(mi))
2277                 itemsDeselected.append(mi);
2278         lmo = mi->getLMO(); // FIXME-2 xlink does return nullptr
2279         if (lmo)
2280             mi->getLMO()->updateVisibility();
2281     }
2282
2283     // Trim list of selection paths
2284     while (itemsSelected.count() < selPathList.count())
2285         delete selPathList.takeFirst();
2286
2287     // Reduce polygons
2288     while (itemsSelected.count() < selPathList.count())
2289         delete selPathList.takeFirst();
2290
2291     // Add additonal polygons
2292     QGraphicsPathItem *sp;
2293     while (itemsSelected.count() > selPathList.count()) {
2294         sp = mapScene->addPath(QPainterPath());
2295         sp->show();
2296         selPathList.append(sp);
2297     }
2298
2299     // Reposition polygons
2300     for (int i = 0; i < itemsSelected.count(); ++i) {
2301         MapObj *mo = itemsSelected.at(i)->getMO();
2302         sp = selPathList.at(i);
2303         sp->setPath(mo->getSelectionPath());
2304         sp->setPen(selectionPen);
2305         sp->setBrush(selectionBrush);
2306         sp->setParentItem(mo);
2307         sp->setZValue(dZ_SELBOX);
2308
2309         // Reposition also LineEdit for heading during animation
2310         if (lineEdit)
2311             lineEdit->move(mo->getAbsPos().toPoint());
2312     }
2313
2314     scene()->update();
2315 }
2316
2317 void MapEditor::updateData(const QModelIndex &sel)
2318 {
2319     TreeItem *ti = static_cast<TreeItem *>(sel.internalPointer());
2320
2321     /* testing
2322         qDebug() << "ME::updateData";
2323         if (!ti)
2324         {
2325         qDebug() << "  ti=NULL";
2326         return;
2327         }
2328         qDebug() << "  ti="<<ti;
2329         qDebug() << "  h="<<ti->getHeadingPlain();
2330     */
2331
2332     if (ti && ti->isBranchLikeType()) {
2333         BranchObj *bo = (BranchObj *)(((MapItem *)ti)->getLMO());
2334         bo->updateVisuals();
2335     }
2336
2337     if (winter) {
2338         QList<QRectF> obstacles;
2339         BranchObj *bo;
2340         BranchItem *cur = NULL;
2341         BranchItem *prev = NULL;
2342         model->nextBranch(cur, prev);
2343         while (cur) {
2344             if (!cur->hasHiddenExportParent()) {
2345                 // Branches
2346                 bo = (BranchObj *)(cur->getLMO());
2347                 if (bo && bo->isVisibleObj())
2348                     obstacles.append(bo->getBBox());
2349             }
2350             model->nextBranch(cur, prev);
2351         }
2352         winter->setObstacles(obstacles);
2353     }
2354 }
2355
2356 void MapEditor::togglePresentationMode()
2357 {
2358     mainWindow->togglePresentationMode();
2359 }
2360
2361 void MapEditor::setSelectionPen(const QPen &p)
2362 {
2363     selectionPen = p;
2364     QItemSelection sel = model->getSelectionModel()->selection();
2365     updateSelection(sel, sel);
2366 }
2367
2368 QPen MapEditor::getSelectionPen() { return selectionPen; }
2369
2370 void MapEditor::setSelectionBrush(const QBrush &b)
2371 {
2372     selectionBrush = b;
2373     QItemSelection sel = model->getSelectionModel()->selection();
2374     updateSelection(sel, sel);
2375 }
2376
2377 QBrush MapEditor::getSelectionBrush() { return selectionBrush; }