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