--- /dev/null
+#include "mapeditor.h"
+
+#include <QGraphicsProxyWidget>
+#include <QMenuBar>
+#include <QObject>
+#include <QPrintDialog>
+#include <QPrinter>
+#include <QScrollBar>
+
+#include "branchitem.h"
+#include "geometry.h"
+#include "mainwindow.h"
+#include "misc.h"
+#include "shortcuts.h"
+#include "warningdialog.h"
+#include "winter.h"
+#include "xlinkitem.h"
+
+extern Main *mainWindow;
+extern QString clipboardDir;
+extern QString clipboardFile;
+extern bool debug;
+extern QPrinter *printer;
+
+extern QMenu *branchContextMenu;
+extern QMenu *canvasContextMenu;
+extern QMenu *floatimageContextMenu;
+extern QMenu *taskContextMenu;
+
+extern Switchboard switchboard;
+extern Settings settings;
+
+extern QTextStream vout;
+
+extern QString editorFocusStyle;
+
+extern FlagRowMaster *systemFlagsMaster;
+
+///////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////
+MapEditor::MapEditor(VymModel *vm)
+{
+ // qDebug() << "Constructor ME " << this;
+
+ QString shortcutScope = tr("Map Editor", "Shortcut scope");
+ mapScene = new QGraphicsScene(NULL);
+ mapScene->setBackgroundBrush(QBrush(Qt::white, Qt::SolidPattern));
+
+ zoomFactor = zoomFactorTarget = 1;
+ angle = angleTarget = 0;
+
+ model = vm;
+ model->registerMapEditor(this);
+
+ setScene(mapScene);
+
+ setStyleSheet("QGraphicsView:focus {" + editorFocusStyle + "}");
+
+ // Create bitmap cursors, platform dependant
+ HandOpenCursor = QCursor(QPixmap(":/mode-move-view.png"), 1, 1);
+ PickColorCursor = QCursor(QPixmap(":/cursorcolorpicker.png"), 5, 27);
+ XLinkCursor = QCursor(QPixmap(":/cursorxlink.png"), 1, 7);
+
+ editingBO = NULL;
+
+ printFrame = true;
+ printFooter = true;
+
+ setAcceptDrops(true);
+
+ // Shortcuts and actions
+ QAction *a;
+
+ a = new QAction("Select upper branch", this);
+ a->setShortcut(Qt::Key_Up);
+ a->setShortcutContext(Qt::WidgetShortcut);
+ connect(a, SIGNAL(triggered()), this, SLOT(cursorUp()));
+ addAction(a);
+
+ a = new QAction("Add upper branch to selection", this);
+ a->setShortcut(Qt::Key_Up + Qt::SHIFT);
+ a->setShortcutContext(Qt::WidgetShortcut);
+ addAction(a);
+ connect(a, SIGNAL(triggered()), this, SLOT(cursorUpToggleSelection()));
+
+ a = new QAction("Select lower branch", this);
+ a->setShortcut(Qt::Key_Down);
+ a->setShortcutContext(Qt::WidgetShortcut);
+ addAction(a);
+ connect(a, SIGNAL(triggered()), this, SLOT(cursorDown()));
+
+ a = new QAction("Add lower branch to selection", this);
+ a->setShortcut(Qt::Key_Down + Qt::SHIFT);
+ a->setShortcutContext(Qt::WidgetShortcut);
+ addAction(a);
+ connect(a, SIGNAL(triggered()), this, SLOT(cursorDownToggleSelection()));
+
+ a = new QAction("Select left branch", this);
+ a->setShortcut(Qt::Key_Left);
+ // a->setShortcutContext (Qt::WidgetWithChildrenShortcut);
+ addAction(a);
+ connect(a, SIGNAL(triggered()), this, SLOT(cursorLeft()));
+
+ a = new QAction("Select child branch", this);
+ a->setShortcut(Qt::Key_Right);
+ // a->setShortcutContext (Qt::WidgetWithChildrenShortcut);
+ addAction(a);
+ connect(a, SIGNAL(triggered()), this, SLOT(cursorRight()));
+
+ a = new QAction("Select first branch", this);
+ a->setShortcut(Qt::Key_Home);
+ a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ addAction(a);
+ connect(a, SIGNAL(triggered()), this, SLOT(cursorFirst()));
+
+ a = new QAction("Select last branch", this);
+ a->setShortcut(Qt::Key_End);
+ a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ addAction(a);
+ connect(a, SIGNAL(triggered()), this, SLOT(cursorLast()));
+
+ // Action to embed LineEdit for heading in Scene
+ lineEdit = NULL;
+
+ a = new QAction(tr("Edit heading", "MapEditor"), this);
+ a->setShortcut(Qt::Key_Return); // Edit heading
+ a->setShortcutContext(Qt::WidgetShortcut);
+ addAction(a);
+ connect(a, SIGNAL(triggered()), this, SLOT(editHeading()));
+ a = new QAction(tr("Edit heading", "MapEditor"), this);
+ a->setShortcut(Qt::Key_Enter); // Edit heading
+ a->setShortcutContext(Qt::WidgetShortcut);
+ addAction(a);
+ connect(a, SIGNAL(triggered()), this, SLOT(editHeading()));
+
+ // Selections
+ selectionPen = QPen(QColor(255,255,0), 1);
+ selectionBrush = QBrush(QColor(255,255,0));
+
+ // Panning
+ panningTimer = new QTimer(this);
+ vPan = QPointF();
+ connect(panningTimer, SIGNAL(timeout()), this, SLOT(panView()));
+
+ // Clone actions defined in MainWindow
+ foreach (QAction *qa, mainWindow->mapEditorActions) {
+ a = new QAction(this);
+ a->setShortcut(qa->shortcut());
+ a->setShortcutContext(qa->shortcutContext());
+ connect(a, SIGNAL(triggered()), qa, SLOT(trigger()));
+ addAction(a);
+ }
+
+ setState(Neutral);
+
+ winter = NULL;
+}
+
+MapEditor::~MapEditor()
+{
+ // qDebug ()<<"Destr MapEditor this="<<this;
+
+ if (winter) {
+ delete winter;
+ winter = NULL;
+ }
+}
+
+VymModel *MapEditor::getModel() { return model; }
+
+QGraphicsScene *MapEditor::getScene() { return mapScene; }
+
+void MapEditor::panView()
+{
+ if (!vPan.isNull()) {
+ // Scroll if needed
+ // To avoid jumping of the sceneView, only
+ // show selection, if not tmp linked
+ qreal px = 0;
+ qreal py = 0;
+ if (vPan.x() < 0)
+ px = vPan.x();
+ else if (vPan.x() > 0)
+ px = width() + vPan.x();
+ if (vPan.y() < 0)
+ py = vPan.y();
+ else if (vPan.y() > 0)
+ py = height() + vPan.y();
+
+ QPointF q = mapToScene(QPoint(px, py));
+ QRectF r = QRectF(q, QPointF(q.x() + 1, q.y() + 1));
+
+ // Expand view if necessary
+ setScrollBarPosTarget(r); // FIXME-2 mapToScene first?
+
+ // Stop possible other animations
+ if (scrollBarPosAnimation.state() == QAbstractAnimation::Running)
+ scrollBarPosAnimation.stop();
+
+ // Do linear animation
+ horizontalScrollBar()->setValue(horizontalScrollBar()->value() +
+ vPan.x());
+ verticalScrollBar()->setValue(verticalScrollBar()->value() + vPan.y());
+
+ // Update currently moving object
+ moveObject();
+ }
+}
+
+void MapEditor::ensureAreaVisibleAnimated(const QRectF &area, bool maximizeArea) // FIXME-2 zooming in not working yet (fit to selection)
+{
+ // Changes viewCenter to make sure that
+ // r is within the margins of the viewport
+ //
+ // Only zooms, if r NOT fit into viewport
+ // view is centered then on bounding box.
+ //
+ // Similar to QGraphicsItem::ensureVisible,
+ // but with animation and (if necessary)
+ // zooming
+
+ int xmargin = settings.value("/mapeditor/scrollToMarginX/", 50).toInt();
+ int ymargin = settings.value("/mapeditor/scrollToMarginY/", 50).toInt();
+
+ // Do we need to zoom out to show area?
+ QRect areaViewCoord = mapFromScene(area).boundingRect();
+
+ // Visible area within margins
+ QRect visibleViewCoord = rect();
+ visibleViewCoord -= QMargins(xmargin, ymargin, xmargin, ymargin);
+
+
+ // Calculate required width and height considering rotation of view
+ qreal a = angle / 180 * M_PI;
+ qreal area_w_viewCoord = abs(sin(a) * area.height()) + abs(cos(a) * area.width());
+ qreal area_h_viewCoord = abs(sin(a) * area.width()) + abs(cos(a) * area.height());
+ qreal z_x = 1.0 * visibleViewCoord.width() / area_w_viewCoord;
+ qreal z_y = 1.0 * visibleViewCoord.height() / area_h_viewCoord;
+
+ qreal zf = min (z_x, z_y);
+
+ bool zoomOutRequired =
+ (visibleViewCoord.width() < areaViewCoord.width() ||
+ visibleViewCoord.height() < areaViewCoord.height());
+ bool zoomInRequired =
+ (visibleViewCoord.width() > areaViewCoord.width() &&
+ visibleViewCoord.height() > areaViewCoord.height());
+
+ //qDebug() << " zoom out: " << zoomOutRequired;
+ //qDebug() << " zoom in: " << zoomInRequired << " zoomFactor=" << zoomFactor << " zf=" << zf;
+ if (zoomOutRequired || maximizeArea) {
+ setViewCenterTarget(area.center(), zf, angle);
+ return;
+ }
+
+
+ // After zooming bbox would fit into margins of viewport
+ long view_dx = 0;
+ long view_dy = 0;
+ if (areaViewCoord.left() < xmargin)
+ // move left
+ view_dx = areaViewCoord.left() - xmargin;
+ else if (areaViewCoord.right() > viewport()->width())
+ // move right
+ view_dx = areaViewCoord.x() + areaViewCoord.width() - viewport()->width() + xmargin;
+
+ if (areaViewCoord.top() < ymargin)
+ // move up
+ view_dy = areaViewCoord.top() - ymargin;
+ else if (areaViewCoord.bottom() > viewport()->height() - ymargin)
+ // move down
+ view_dy = areaViewCoord.y() + areaViewCoord.height() - viewport()->height() + ymargin;
+
+ if (abs(view_dx) > 5 || abs(view_dy) > 5)
+ setViewCenterTarget(
+ mapToScene(viewport()->geometry().center() + QPoint (view_dx, view_dy)),
+ zoomFactor,
+ angle,
+ 2000,
+ QEasingCurve::OutQuint);
+}
+
+void MapEditor::ensureSelectionVisibleAnimated(bool maximizeArea)
+{
+ // Changes viewCenter to make sure that bounding box of all currently
+ // selected items is within the margins of the viewport
+ //
+ // Only zooms, if bounding box of items does NOT fit into viewport
+ // view is centered then on bounding box. (Useful also for big images)
+ //
+ // Similar to QGraphicsItem::ensureVisible, but with animation and (if necessary)
+ // zooming
+
+ QList <TreeItem*> selis = model->getSelectedItems();
+
+ // Nothing to do, if nothing is selected
+ if (selis.isEmpty()) return;
+
+ // Calculate total bounding box
+ QRectF bbox;
+ bool firstIteration = true;
+
+ foreach (TreeItem *ti, selis) {
+ LinkableMapObj *lmo = nullptr;
+ if (ti->getType() == TreeItem::Image || ti->isBranchLikeType())
+ lmo = ((MapItem *)ti)->getLMO();
+ if (lmo) {
+ if (firstIteration) {
+ bbox = lmo->getBBox();
+ firstIteration = false;
+ } else
+ bbox = bbox.united(lmo->getBBox());
+ }
+ }
+
+ ensureAreaVisibleAnimated(bbox, maximizeArea);
+}
+
+void MapEditor::scrollTo(const QModelIndex &index)
+{
+ if (index.isValid()) {
+ LinkableMapObj *lmo = NULL;
+ TreeItem *ti = static_cast<TreeItem *>(index.internalPointer());
+ if (ti->getType() == TreeItem::Image || ti->isBranchLikeType())
+ lmo = ((MapItem *)ti)->getLMO();
+ if (lmo) {
+ QRectF r = lmo->getBBox();
+ setScrollBarPosTarget(r);
+ animateScrollBars();
+ }
+ }
+}
+
+void MapEditor::setScrollBarPosTarget(QRectF rect)
+{
+ // Expand viewport, if rect is not contained
+ if (!sceneRect().contains(rect))
+ setSceneRect(sceneRect().united(rect));
+
+ int xmargin = settings.value("/mapeditor/scrollToMarginX/", 80).toInt();
+ int ymargin = settings.value("/mapeditor/scrollToMarginX/", 80).toInt();
+
+ // Prepare scrolling
+ qreal width = viewport()->width();
+ qreal height = viewport()->height();
+ QRectF viewRect = transform().scale(zoomFactorTarget, zoomFactorTarget).mapRect(rect);
+
+ qreal left = horizontalScrollBar()->value();
+ qreal right = left + width;
+ qreal top = verticalScrollBar()->value();
+ qreal bottom = top + height;
+
+ scrollBarPosTarget = getScrollBarPos();
+
+ if (viewRect.left() <= left + xmargin) {
+ // need to scroll from the left
+ scrollBarPosTarget.setX(int(viewRect.left() - xmargin - 0.5));
+ }
+ if (viewRect.right() >= right - xmargin) {
+ // need to scroll from the right
+ scrollBarPosTarget.setX(int(viewRect.right() - width + xmargin + 0.5));
+ }
+ if (viewRect.top() <= top + ymargin) {
+ // need to scroll from the top
+ scrollBarPosTarget.setY(int(viewRect.top() - ymargin - 0.5));
+ }
+ if (viewRect.bottom() >= bottom - ymargin) {
+ // need to scroll from the bottom
+ scrollBarPosTarget.setY(
+ int(viewRect.bottom() - height + ymargin + 0.5));
+ }
+}
+
+QPointF MapEditor::getScrollBarPosTarget() { return scrollBarPosTarget; }
+
+void MapEditor::setScrollBarPos(const QPointF &p)
+{
+ scrollBarPos = p;
+ horizontalScrollBar()->setValue(int(p.x()));
+ verticalScrollBar()->setValue(int(p.y()));
+}
+
+QPointF MapEditor::getScrollBarPos()
+{
+ return QPointF(horizontalScrollBar()->value(),
+ verticalScrollBar()->value());
+ // return scrollBarPos;
+}
+
+void MapEditor::animateScrollBars()
+{
+ if (scrollBarPosAnimation.state() == QAbstractAnimation::Running)
+ scrollBarPosAnimation.stop();
+
+ if (settings.value("/animation/use/", true).toBool()) {
+ scrollBarPosAnimation.setTargetObject(this);
+ scrollBarPosAnimation.setPropertyName("scrollBarPos");
+ scrollBarPosAnimation.setDuration(
+ settings.value("/animation/duration/scrollbar", 2000).toInt());
+ scrollBarPosAnimation.setEasingCurve(QEasingCurve::OutQuint);
+ scrollBarPosAnimation.setStartValue(QPointF(
+ horizontalScrollBar()->value(), verticalScrollBar()->value()));
+ scrollBarPosAnimation.setEndValue(scrollBarPosTarget);
+ scrollBarPosAnimation.start();
+ }
+ else
+ setScrollBarPos(scrollBarPosTarget);
+}
+
+void MapEditor::setZoomFactorTarget(const qreal &zft)
+{
+ zoomFactorTarget = zft;
+ if (zoomAnimation.state() == QAbstractAnimation::Running)
+ zoomAnimation.stop();
+ if (settings.value("/animation/use/", true).toBool()) {
+ zoomAnimation.setTargetObject(this);
+ zoomAnimation.setPropertyName("zoomFactor");
+ zoomAnimation.setDuration(
+ settings.value("/animation/duration/zoom", 2000).toInt());
+ zoomAnimation.setEasingCurve(QEasingCurve::OutQuint);
+ zoomAnimation.setStartValue(zoomFactor);
+ zoomAnimation.setEndValue(zft);
+ zoomAnimation.start();
+ }
+ else
+ setZoomFactor(zft);
+}
+
+qreal MapEditor::getZoomFactorTarget() { return zoomFactorTarget; }
+
+void MapEditor::setZoomFactor(const qreal &zf)
+{
+ zoomFactor = zf;
+ updateMatrix();
+}
+
+qreal MapEditor::getZoomFactor() { return zoomFactor; }
+
+void MapEditor::setAngleTarget(const qreal &at)
+{
+ angleTarget = at;
+ if (rotationAnimation.state() == QAbstractAnimation::Running)
+ rotationAnimation.stop();
+ if (settings.value("/animation/use/", true).toBool()) {
+ rotationAnimation.setTargetObject(this);
+ rotationAnimation.setPropertyName("angle");
+ rotationAnimation.setDuration(
+ settings.value("/animation/duration/rotation", 2000).toInt());
+ rotationAnimation.setEasingCurve(QEasingCurve::OutQuint);
+ rotationAnimation.setStartValue(angle);
+ rotationAnimation.setEndValue(at);
+ rotationAnimation.start();
+ }
+ else
+ setAngle(angleTarget);
+}
+
+qreal MapEditor::getAngleTarget() { return angleTarget; }
+
+void MapEditor::setAngle(const qreal &a)
+{
+ angle = a;
+ updateMatrix();
+ if (winter)
+ winter->updateView();
+}
+
+qreal MapEditor::getAngle() { return angle; }
+
+void MapEditor::setViewCenterTarget(const QPointF &p, const qreal &zft,
+ const qreal &at, const int duration,
+ const QEasingCurve &easingCurve)
+{
+ viewCenterTarget = p;
+ zoomFactorTarget = zft;
+ angleTarget = at;
+
+ viewCenter = mapToScene(viewport()->geometry()).boundingRect().center();
+
+ if (viewCenterAnimation.state() == QAbstractAnimation::Running)
+ viewCenterAnimation.stop();
+ if (rotationAnimation.state() == QAbstractAnimation::Running)
+ rotationAnimation.stop();
+ if (zoomAnimation.state() == QAbstractAnimation::Running)
+ zoomAnimation.stop();
+
+ if (settings.value("/animation/use/", true).toBool()) {
+ viewCenterAnimation.setTargetObject(this);
+ viewCenterAnimation.setPropertyName("viewCenter");
+ viewCenterAnimation.setDuration(
+ settings.value("/animation/duration/scrollbar", duration).toInt());
+ viewCenterAnimation.setEasingCurve(easingCurve);
+ viewCenterAnimation.setStartValue(viewCenter);
+ viewCenterAnimation.setEndValue(viewCenterTarget);
+ viewCenterAnimation.start();
+
+ rotationAnimation.setTargetObject(this);
+ rotationAnimation.setPropertyName("angle");
+ rotationAnimation.setDuration(
+ settings.value("/animation/duration/rotation", duration).toInt());
+ rotationAnimation.setEasingCurve(easingCurve);
+ rotationAnimation.setStartValue(angle);
+ rotationAnimation.setEndValue(angleTarget);
+ rotationAnimation.start();
+
+ zoomAnimation.setTargetObject(this);
+ zoomAnimation.setPropertyName("zoomFactor");
+ zoomAnimation.setDuration(
+ settings.value("/animation/duration/zoom", duration).toInt());
+ zoomAnimation.setEasingCurve(easingCurve);
+ zoomAnimation.setStartValue(zoomFactor);
+ zoomAnimation.setEndValue(zoomFactorTarget);
+ zoomAnimation.start();
+ }
+ else {
+ setAngle(angleTarget);
+ setZoomFactor(zft);
+ setViewCenter(viewCenterTarget);
+ }
+}
+
+void MapEditor::setViewCenterTarget()
+{
+ MapItem *selti = (MapItem *)(model->getSelectedItem());
+ if (selti) {
+ LinkableMapObj *lmo = selti->getLMO();
+ if (lmo)
+ setViewCenterTarget(lmo->getBBox().center(), 1, 0);
+ }
+}
+
+QPointF MapEditor::getViewCenterTarget() { return viewCenterTarget; }
+
+void MapEditor::setViewCenter(const QPointF &vc) { centerOn(vc); }
+
+QPointF MapEditor::getViewCenter() { return viewCenter; }
+
+void MapEditor::updateMatrix()
+{
+ QTransform t_zoom;
+ t_zoom.scale(zoomFactor, zoomFactor);
+ QTransform t_rot;
+ t_rot.rotate(angle);
+ setTransform(t_zoom * t_rot);
+}
+
+void MapEditor::minimizeView() {
+ // If we only would set scene rectangle to existing items, then
+ // view fould "jump", when Qt automatically tries to center.
+ // Better consider the currently visible viewport (with slight offset)
+ QRectF r = mapToScene(viewport()->geometry()).boundingRect();
+ r.translate(-2,-3);
+ setSceneRect(scene()->itemsBoundingRect().united(r));
+}
+
+void MapEditor::print()
+{
+ QRectF totalBBox = getTotalBBox();
+
+ if (!printer)
+ printer = mainWindow->setupPrinter();
+
+ // Try to set orientation automagically
+ // Note: Interpretation of generated postscript is amibiguous, if
+ // there are problems with landscape mode, see
+ // http://sdb.suse.de/de/sdb/html/jsmeix_print-cups-landscape-81.html
+
+ if (totalBBox.width() > totalBBox.height())
+ // recommend landscape
+ printer->setPageOrientation(QPageLayout::Landscape);
+ else
+ // recommend portrait
+ printer->setPageOrientation(QPageLayout::Portrait);
+
+ QPrintDialog dialog(printer, this);
+ dialog.setWindowTitle(tr("Print vym map", "MapEditor"));
+ if (dialog.exec() == QDialog::Accepted) {
+ QPainter pp(printer);
+
+ pp.setRenderHint(QPainter::Antialiasing, true);
+
+ // Don't print the visualisation of selection
+ model->unselectAll();
+
+ QRectF mapRect = totalBBox;
+ QGraphicsRectItem *frame = NULL;
+
+ if (printFrame) {
+ // Print frame around map
+ mapRect.setRect(totalBBox.x() - 10, totalBBox.y() - 10,
+ totalBBox.width() + 20, totalBBox.height() + 20);
+ frame = mapScene->addRect(mapRect, QPen(Qt::black),
+ QBrush(Qt::NoBrush));
+ frame->setZValue(0);
+ frame->show();
+ }
+
+ double paperAspect =
+ (double)printer->width() / (double)printer->height();
+ double mapAspect = (double)mapRect.width() / (double)mapRect.height();
+ int viewBottom;
+ if (mapAspect >= paperAspect) {
+ // Fit horizontally to paper width
+ // pp.setViewport(0,0,
+ // printer->width(),(int)(printer->width()/mapAspect) );
+ viewBottom = (int)(printer->width() / mapAspect);
+ }
+ else {
+ // Fit vertically to paper height
+ // pp.setViewport(0,0,(int)(printer->height()*mapAspect),printer->height());
+ viewBottom = printer->height();
+ }
+
+ if (printFooter) {
+ // Print footer below map
+ QFont font;
+ font.setPointSize(10);
+ pp.setFont(font);
+ QRectF footerBox(0, viewBottom, printer->width(), 15);
+ pp.drawText(footerBox, Qt::AlignLeft,
+ "VYM - " + model->getFileName());
+ pp.drawText(footerBox, Qt::AlignRight,
+ QDate::currentDate().toString(Qt::TextDate));
+ }
+ mapScene->render(&pp,
+ QRectF(0, 0, printer->width(), printer->height() - 15),
+ QRectF(mapRect.x(), mapRect.y(), mapRect.width(),
+ mapRect.height()));
+
+ // Viewport has paper dimension
+ if (frame)
+ delete (frame);
+
+ // Restore selection
+ model->reselect();
+ }
+}
+
+QRectF MapEditor::getTotalBBox() // FIXME-2 really needed? Overlaps with scene and VM...
+{
+ minimizeView();
+ return sceneRect();
+}
+
+QImage MapEditor::getImage(QPointF &offset)
+{
+ QRectF mapRect = getTotalBBox(); // minimized sceneRect
+
+ int d = 10; // border
+ offset = QPointF(mapRect.x() - d / 2, mapRect.y() - d / 2);
+ QImage pix(mapRect.width() + d, mapRect.height() + d, QImage::Format_RGB32);
+
+ QPainter pp(&pix);
+ pp.setRenderHints(renderHints());
+ mapScene->render(&pp,
+ // Destination:
+ QRectF(0, 0, mapRect.width() + d, mapRect.height() + d),
+ // Source in scene:
+ QRectF(mapRect.x() - d / 2, mapRect.y() - d / 2,
+ mapRect.width() + d, mapRect.height() + d));
+ return pix;
+}
+
+void MapEditor::setAntiAlias(bool b)
+{
+ setRenderHint(QPainter::Antialiasing, b);
+}
+
+void MapEditor::setSmoothPixmap(bool b)
+{
+ setRenderHint(QPainter::SmoothPixmapTransform, b);
+}
+
+void MapEditor::autoLayout()
+{
+ // Create list with all bounding polygons
+ QList<LinkableMapObj *> mapobjects;
+ QList<ConvexPolygon> polys;
+ ConvexPolygon p;
+ QList<Vector> vectors;
+ QList<Vector> orgpos;
+ QStringList headings; // FIXME-3 testing only
+ Vector v;
+ BranchItem *bi;
+ BranchItem *bi2;
+ BranchObj *bo;
+
+ // Outer loop: Iterate until we no more changes in orientation
+ bool orientationChanged = true;
+ while (orientationChanged) {
+ BranchItem *ri = model->getRootItem();
+ for (int i = 0; i < ri->branchCount(); ++i) {
+ bi = ri->getBranchNum(i);
+ bo = (BranchObj *)bi->getLMO();
+ if (bo) {
+ mapobjects.append(bo);
+ p = bo->getBoundingPolygon();
+ p.calcCentroid();
+ polys.append(p);
+ vectors.append(QPointF(0, 0));
+ orgpos.append(p.at(0));
+ headings.append(bi->getHeadingPlain());
+ }
+ for (int j = 0; j < bi->branchCount(); ++j) {
+ bi2 = bi->getBranchNum(j);
+ bo = (BranchObj *)bi2->getLMO();
+ if (bo) {
+ mapobjects.append(bo);
+ p = bo->getBoundingPolygon();
+ p.calcCentroid();
+ polys.append(p);
+ vectors.append(QPointF(0, 0));
+ orgpos.append(p.at(0));
+ headings.append(bi2->getHeadingPlain());
+ }
+ }
+ }
+
+ // Iterate moving bounding polygons until we have no more collisions
+ int collisions = 1;
+ while (collisions > 0) {
+ collisions = 0;
+ for (int i = 0; i < polys.size() - 1; ++i) {
+ for (int j = i + 1; j < polys.size(); ++j) {
+ if (polygonCollision(polys.at(i), polys.at(j),
+ QPointF(0, 0))
+ .intersect) {
+ collisions++;
+ if (debug)
+ qDebug() << "Collision: " << headings[i] << " - "
+ << headings[j];
+ v = polys.at(j).centroid() - polys.at(i).centroid();
+ v.normalize();
+ // Add random direction, if only two polygons with
+ // identical y or x
+ if (v.x() == 0 || v.y() == 0) {
+ Vector w(cos(double((int)rand() % 1000)),
+ sin(double((int)rand() % 1000)));
+ w.normalize();
+ v = v + w;
+ }
+
+ // Scale translation vector by area of polygons
+ vectors[j] = v * 10000 / polys.at(j).weight();
+ vectors[i] = v * 10000 / polys.at(i).weight();
+ vectors[i].invert();
+ // FIXME-3 outer loop, "i" get's changed several
+ // times...
+ // Better not move away from centroid of 2 colliding
+ // polys, but from centroid of _all_
+ }
+ }
+ }
+ for (int i = 0; i < vectors.size(); i++) {
+ // qDebug() << " v="<<vectors[i]<<" "<<headings[i];
+ if (!vectors[i].isNull())
+ polys[i].translate(vectors[i]);
+ }
+ // if (debug) qDebug()<< "Collisions total: "<<collisions;
+ // collisions=0;
+ }
+
+ // Finally move the real objects and update
+ QList<LinkableMapObj::Orientation> orients;
+ for (int i = 0; i < polys.size(); i++) {
+ Vector v = polys[i].at(0) - orgpos[i];
+ orients.append(mapobjects[i]->getOrientation());
+ if (!v.isNull()) {
+ if (debug)
+ qDebug() << " Moving " << polys.at(i).weight() << " "
+ << mapobjects[i]->getAbsPos() << " -> "
+ << mapobjects[i]->getAbsPos() + v << " "
+ << headings[i];
+ // mapobjects[i]->moveBy(v.x(),v.y() );
+ // mapobjects[i]->setRelPos();
+ model->startAnimation((BranchObj *)mapobjects[i], v);
+ if (debug)
+ qDebug() << i << " Weight: " << polys.at(i).weight() << " "
+ << v << " " << headings.at(i);
+ }
+ }
+ /*
+ model->reposition();
+ orientationChanged=false;
+ for (int i=0;i<polys.size();i++)
+ if (orients[i]!=mapobjects[i]->getOrientation())
+ {
+ orientationChanged=true;
+ break;
+ }
+ */
+
+ break;
+
+ // orientationChanged=false;
+ } // loop if orientation has changed
+
+ model->emitSelectionChanged();
+}
+
+TreeItem *MapEditor::findMapItem(QPointF p, TreeItem *exclude)
+{
+ // Search XLinks
+ Link *link;
+ for (int i = 0; i < model->xlinkCount(); i++) {
+ link = model->getXLinkNum(i);
+ if (link) {
+ XLinkObj *xlo = link->getXLinkObj();
+ if (xlo && xlo->isInClickBox(p)) {
+ // Found XLink, now return the nearest XLinkItem of p
+ qreal d0 = Geometry::distance(p, xlo->getBeginPos());
+ qreal d1 = Geometry::distance(p, xlo->getEndPos());
+ if (d0 > d1)
+ return link->getBeginLinkItem();
+ else
+ return link->getEndLinkItem();
+ }
+ }
+ }
+
+ // Search branches (and their childs, e.g. images
+ // Start with mapcenter, no images allowed at rootItem
+ int i = 0;
+ BranchItem *bi = model->getRootItem()->getFirstBranch();
+ TreeItem *found = NULL;
+ while (bi) {
+ found = bi->findMapItem(p, exclude);
+ if (found)
+ return found;
+ i++;
+ bi = model->getRootItem()->getBranchNum(i);
+ }
+ return NULL;
+}
+
+void MapEditor::testFunction1() {}
+
+void MapEditor::testFunction2() { autoLayout(); }
+
+void MapEditor::toggleWinter()
+{
+ if (winter) {
+ delete winter;
+ winter = NULL;
+ }
+ else {
+ winter = new Winter(this);
+ QList<QRectF> obstacles;
+ BranchObj *bo;
+ BranchItem *cur = NULL;
+ BranchItem *prev = NULL;
+ model->nextBranch(cur, prev);
+ while (cur) {
+ if (!cur->hasHiddenExportParent()) {
+ // Branches
+ bo = (BranchObj *)(cur->getLMO());
+ if (bo && bo->isVisibleObj())
+ obstacles.append(bo->getBBox());
+ }
+ model->nextBranch(cur, prev);
+ }
+ winter->setObstacles(obstacles);
+ }
+}
+
+BranchItem *MapEditor::getBranchDirectAbove(BranchItem *bi)
+{
+ if (bi) {
+ int i = bi->num();
+ if (i > 0)
+ return bi->parent()->getBranchNum(i - 1);
+ }
+ return NULL;
+}
+
+BranchItem *MapEditor::getBranchAbove(BranchItem *selbi)
+{
+ if (selbi) {
+ int dz = selbi->depth(); // original depth
+ bool invert = false;
+ if (selbi->getLMO()->getOrientation() == LinkableMapObj::LeftOfCenter)
+ invert = true;
+
+ BranchItem *bi;
+
+ // Look for branch with same parent but directly above
+ if (dz == 1 && invert)
+ bi = getBranchDirectBelow(selbi);
+ else
+ bi = getBranchDirectAbove(selbi);
+
+ if (bi)
+ // direct predecessor
+ return bi;
+
+ // Go towards center and look for predecessor
+ while (selbi->depth() > 0) {
+ selbi = (BranchItem *)(selbi->parent());
+ if (selbi->depth() == 1 && invert)
+ bi = getBranchDirectBelow(selbi);
+ else
+ bi = getBranchDirectAbove(selbi);
+ if (bi) {
+ // turn
+ selbi = bi;
+ while (selbi->depth() < dz) {
+ // try to get back to original depth dz
+ bi = selbi->getLastBranch();
+ if (!bi) {
+ return selbi;
+ }
+ selbi = bi;
+ }
+ return selbi;
+ }
+ }
+ }
+ return NULL;
+}
+
+BranchItem *MapEditor::getBranchDirectBelow(BranchItem *bi)
+{
+ if (bi) {
+ int i = bi->num();
+ if (i + 1 < bi->parent()->branchCount())
+ return bi->parent()->getBranchNum(i + 1);
+ }
+ return NULL;
+}
+
+BranchItem *MapEditor::getBranchBelow(BranchItem *selbi)
+{
+ if (selbi) {
+ BranchItem *bi;
+ int dz = selbi->depth(); // original depth
+ bool invert = false;
+ if (selbi->getLMO()->getOrientation() == LinkableMapObj::LeftOfCenter)
+ invert = true;
+
+ // Look for branch with same parent but directly below
+ if (dz == 1 && invert)
+ bi = getBranchDirectAbove(selbi);
+ else
+ bi = getBranchDirectBelow(selbi);
+ if (bi)
+ // direct successor
+ return bi;
+
+ // Go towards center and look for neighbour
+ while (selbi->depth() > 0) {
+ selbi = (BranchItem *)(selbi->parent());
+ if (selbi->depth() == 1 && invert)
+ bi = getBranchDirectAbove(selbi);
+ else
+ bi = getBranchDirectBelow(selbi);
+ if (bi) {
+ // turn
+ selbi = bi;
+ while (selbi->depth() < dz) {
+ // try to get back to original depth dz
+ bi = selbi->getFirstBranch();
+ if (!bi) {
+ return selbi;
+ }
+ selbi = bi;
+ }
+ return selbi;
+ }
+ }
+ }
+ return NULL;
+}
+
+BranchItem *MapEditor::getLeftBranch(TreeItem *ti)
+{
+ if (!ti)
+ return NULL;
+
+ if (ti->isBranchLikeType()) {
+ BranchItem *bi = (BranchItem *)ti;
+ if (bi->depth() == 0) {
+ // Special case: use alternative selection index
+ BranchItem *newbi = bi->getLastSelectedBranchAlt();
+ if (!newbi) {
+ BranchObj *bo;
+ // Try to find a mainbranch left of center
+ for (int i = 0; i < bi->branchCount(); i++) {
+ newbi = bi->getBranchNum(i);
+ bo = newbi->getBranchObj();
+ if (bo &&
+ bo->getOrientation() == LinkableMapObj::LeftOfCenter)
+ break;
+ }
+ }
+ return newbi;
+ }
+ if (bi->getBranchObj()->getOrientation() ==
+ LinkableMapObj::RightOfCenter)
+ // right of center
+ return (BranchItem *)(bi->parent());
+ else
+ // left of center
+ if (bi->getType() == TreeItem::Branch)
+ return bi->getLastSelectedBranch();
+ }
+
+ if (ti->parent() && ti->parent()->isBranchLikeType())
+ return (BranchItem *)(ti->parent());
+ return NULL;
+}
+
+BranchItem *MapEditor::getRightBranch(TreeItem *ti)
+{
+ if (!ti)
+ return NULL;
+
+ if (ti->isBranchLikeType()) {
+ BranchItem *bi = (BranchItem *)ti;
+ if (bi->depth() == 0) {
+ // Special case: use alternative selection index
+ BranchItem *newbi = bi->getLastSelectedBranch();
+ if (!newbi) {
+ BranchObj *bo;
+ // Try to find a mainbranch right of center
+ for (int i = 0; i < bi->branchCount(); i++) {
+ newbi = bi->getBranchNum(i);
+ bo = newbi->getBranchObj();
+ if (bo &&
+ bo->getOrientation() == LinkableMapObj::RightOfCenter)
+ qDebug()
+ << "BI found right: " << newbi->getHeadingPlain();
+ }
+ }
+ return newbi;
+ }
+ if (bi->getBranchObj()->getOrientation() ==
+ LinkableMapObj::LeftOfCenter)
+ // left of center
+ return (BranchItem *)(bi->parent());
+ else
+ // right of center
+ if (bi->getType() == TreeItem::Branch)
+ return (BranchItem *)bi->getLastSelectedBranch();
+ }
+
+ if (ti->parent() && ti->parent()->isBranchLikeType())
+ return (BranchItem *)(ti->parent());
+
+ return NULL;
+}
+
+void MapEditor::cursorUp()
+{
+ if (state == MapEditor::EditingHeading)
+ return;
+
+ BranchItem *selbi = model->getSelectedBranch();
+ BranchItem *bi;
+ if (selbi) {
+ // Exactly one branch is currently selected
+ bi = getBranchAbove(selbi);
+ if (bi) {
+ model->select(bi);
+ }
+ } else {
+ // Nothing selected or already multiple selections
+ TreeItem *ti = model->lastToggledItem();
+ if (ti && ti->isBranchLikeType()) {
+ bi = getBranchAbove( (BranchItem*)ti);
+ if (bi)
+ model->select(bi);
+ }
+ }
+}
+
+void MapEditor::cursorUpToggleSelection()
+{
+ if (state == MapEditor::EditingHeading)
+ return;
+
+ BranchItem *selbi = model->getSelectedBranch();
+ BranchItem *bi;
+
+ if (selbi) {
+ // Exactly one branch is currently selected
+ bi = getBranchAbove(selbi);
+ if (bi) model->selectToggle(bi);
+ } else {
+ // Nothing selected or already multiple selections
+ TreeItem *ti = model->lastToggledItem();
+ if (ti && ti->isBranchLikeType()) {
+ if (lastToggleDirection == toggleUp)
+ bi = getBranchAbove( (BranchItem*)ti);
+ else
+ bi = (BranchItem*)ti;
+
+ if (bi)
+ model->selectToggle(bi);
+ }
+ }
+ lastToggleDirection = toggleUp;
+}
+
+void MapEditor::cursorDown()
+{
+ if (state == MapEditor::EditingHeading)
+ return;
+
+ BranchItem *selbi = model->getSelectedBranch();
+ BranchItem *bi;
+ if (selbi) {
+ // Exactly one branch is currently selected
+ bi = getBranchBelow(selbi);
+ if (bi) {
+ model->select(bi);
+ }
+ } else {
+ // Nothing selected or already multiple selections
+ TreeItem *ti = model->lastToggledItem();
+ if (ti && ti->isBranchLikeType()) {
+ bi = getBranchBelow( (BranchItem*)ti);
+
+ if (bi)
+ model->select(bi);
+ }
+ }
+}
+
+void MapEditor::cursorDownToggleSelection()
+{
+ if (state == MapEditor::EditingHeading)
+ return;
+
+ BranchItem *selbi = model->getSelectedBranch();
+ BranchItem *bi;
+ if (selbi) {
+ // Exactly one branch is currently selected
+ bi = getBranchBelow(selbi);
+ if (bi) {
+ model->selectToggle(bi);
+ }
+ } else {
+ // Nothing selected or already multiple selections
+ TreeItem *ti = model->lastToggledItem();
+ if (ti && ti->isBranchLikeType()) {
+ if (lastToggleDirection == toggleDown)
+ bi = getBranchBelow( (BranchItem*)ti);
+ else
+ bi = (BranchItem*)ti;
+
+ if (bi)
+ model->selectToggle(bi);
+ }
+ }
+ lastToggleDirection = toggleDown;
+}
+
+void MapEditor::cursorLeft()
+{
+ TreeItem *ti = model->getSelectedItem();
+ if (!ti) {
+ ti = model->lastToggledItem();
+ if (!ti) return;
+ }
+
+ BranchItem *bi = getLeftBranch(ti);
+ if (bi)
+ model->select(bi);
+ else {
+ ImageItem *ii = ti->getFirstImage();
+ if (ii)
+ model->select(ii);
+ }
+}
+
+void MapEditor::cursorRight()
+{
+ TreeItem *ti = model->getSelectedItem();
+ if (!ti) {
+ ti = model->lastToggledItem();
+ if (!ti) return;
+ }
+
+ BranchItem *bi = getRightBranch(ti);
+ if (bi)
+ model->select(bi);
+ else {
+ ImageItem *ii = ti->getFirstImage();
+ if (ii)
+ model->select(ii);
+ }
+}
+
+void MapEditor::cursorFirst() { model->selectFirstBranch(); }
+
+void MapEditor::cursorLast() { model->selectLastBranch(); }
+
+void MapEditor::editHeading()
+{
+ if (state == EditingHeading) {
+ editHeadingFinished();
+ return;
+ }
+
+ BranchObj *bo = model->getSelectedBranchObj();
+ BranchItem *bi = model->getSelectedBranch();
+ if (bo && bo) {
+ VymText heading = bi->getHeading();
+ if (heading.isRichText() || bi->getHeadingPlain().contains("\n")) {
+ mainWindow->windowShowHeadingEditor();
+ ensureSelectionVisibleAnimated();
+ return;
+ }
+ model->setSelectionBlocked(true);
+
+ lineEdit = new QLineEdit;
+ QGraphicsProxyWidget *pw = mapScene->addWidget(lineEdit);
+ pw->setZValue(Z_LINEEDIT);
+ lineEdit->setCursor(Qt::IBeamCursor);
+ lineEdit->setCursorPosition(1);
+
+#if defined(Q_OS_WINDOWS)
+ QFont font = lineEdit->font();
+ font.setPointSize(font.pointSize() + 4);
+ lineEdit->setFont(font);
+#endif
+
+ QPointF tl;
+ QPointF br;
+ qreal w = 230;
+ qreal h = 30;
+ if (bo->getOrientation() != LinkableMapObj::LeftOfCenter) {
+ tl = bo->getOrnamentsBBox().topLeft();
+ br = tl + QPointF(w, h);
+ }
+ else {
+ br = bo->getOrnamentsBBox().bottomRight();
+ tl = br - QPointF(w, h);
+ }
+ QRectF r(tl, br);
+ lineEdit->setGeometry(r.toRect());
+ pw->setGeometry(r.toRect());
+
+ minimizeView();
+
+ // Set focus to MapEditor first
+ // To avoid problems with Cursor up/down
+ setFocus();
+
+ ensureAreaVisibleAnimated(r);
+
+ lineEdit->setText(heading.getTextASCII());
+ lineEdit->setFocus();
+ lineEdit->selectAll(); // Hack to enable cursor in lineEdit
+ lineEdit->deselect(); // probably a Qt bug...
+
+ setState(EditingHeading);
+ }
+}
+
+void MapEditor::editHeadingFinished()
+{
+ if (state != EditingHeading || !lineEdit ) {
+ qWarning() << "ME::editHeadingFinished not editing heading!";
+ } else {
+ lineEdit->clearFocus();
+ QString s = lineEdit->text();
+ s.replace(QRegExp("\\n"), " "); // Don't paste newline chars
+ if (s.length() == 0)
+ s = " "; // Don't allow empty lines, which would screw up drawing
+ model->setHeadingPlainText(s);
+ delete (lineEdit);
+ lineEdit = nullptr;
+
+ // FIXME-2 ensureAreaVisible like in starting editing?
+
+ // Maybe reselect previous branch
+ mainWindow->editHeadingFinished(model);
+
+ // Autolayout to avoid overlapping branches with longer headings
+ if (settings.value("/mainwindow/autoLayout/use", "true") == "true")
+ autoLayout();
+ }
+
+ model->setSelectionBlocked(false);
+ setState(Neutral);
+}
+
+void MapEditor::contextMenuEvent(QContextMenuEvent *e)
+{
+ // Lineedits are already closed by preceding
+ // mouseEvent, we don't need to close here.
+
+ QPointF p = mapToScene(e->pos());
+ TreeItem *ti = findMapItem(p, NULL);
+
+ if (ti) { // MapObj was found
+ model->select(ti);
+
+ LinkableMapObj *lmo = NULL;
+ BranchItem *selbi = model->getSelectedBranch();
+ if (ti)
+ lmo = ((MapItem *)ti)->getLMO();
+
+ // Context Menu
+ if (lmo && selbi) {
+ QString sysFlagName;
+ QUuid uid = ((BranchObj *)lmo)->findSystemFlagUidByPos(p);
+ if (!uid.isNull()) {
+ Flag *flag = systemFlagsMaster->findFlagByUid(uid);
+ if (flag)
+ sysFlagName = flag->getName();
+ }
+
+ if (sysFlagName.startsWith("system-task"))
+ taskContextMenu->popup(e->globalPos());
+ else
+ // Context Menu on branch or mapcenter
+ branchContextMenu->popup(e->globalPos());
+ }
+ else {
+ if (model->getSelectedImage()) {
+ // Context Menu on floatimage
+ floatimageContextMenu->popup(e->globalPos());
+ }
+ else {
+ if (model->getSelectedXLink())
+ // Context Menu on XLink
+ model->editXLink();
+ }
+ }
+ }
+ else { // No MapObj found, we are on the Canvas itself
+ // Context Menu on scene
+
+ // Open context menu synchronously to position new mapcenter
+ model->setContextPos(p);
+ canvasContextMenu->exec(e->globalPos());
+ model->unsetContextPos();
+ }
+ e->accept();
+}
+
+void MapEditor::keyPressEvent(QKeyEvent *e)
+{
+ if (e->key() == Qt::Key_PageUp || e->key() == Qt::Key_PageDown)
+ // Ignore PageUP/Down to avoid scrolling with keys
+ return;
+
+ if (e->modifiers() & Qt::ShiftModifier) {
+ switch (mainWindow->getModMode()) {
+ case Main::ModModePoint:
+ setCursor(Qt::ArrowCursor);
+ break;
+ case Main::ModModeColor:
+ setCursor(PickColorCursor);
+ break;
+ case Main::ModModeXLink:
+ setCursor(XLinkCursor);
+ break;
+ case Main::ModModeMoveObject:
+ setCursor(Qt::PointingHandCursor);
+ break;
+ case Main::ModModeMoveView:
+ setCursor(QPixmap(":/mode-move-view.png"));
+ break;
+ default:
+ setCursor(Qt::ArrowCursor);
+ break;
+ }
+ }
+ QGraphicsView::keyPressEvent(e);
+}
+
+void MapEditor::keyReleaseEvent(QKeyEvent *e)
+{
+ if (!(e->modifiers() & Qt::ControlModifier))
+ setCursor(Qt::ArrowCursor);
+}
+
+void MapEditor::startMovingView(QMouseEvent *e)
+{
+ setState(MovingView);
+ movingObj = NULL; // move Content not Obj
+ movingObj_offset = e->globalPos();
+ movingCont_start =
+ QPointF(horizontalScrollBar()->value(), verticalScrollBar()->value());
+ movingVec = QPointF(0, 0);
+ setCursor(HandOpenCursor);
+}
+
+void MapEditor::mousePressEvent(QMouseEvent *e)
+{
+ // Ignore right clicks
+ if (e->button() == Qt::RightButton) {
+ e->ignore();
+ QGraphicsView::mousePressEvent(e);
+ return;
+ }
+
+ // Check if we need to reset zoomFactor for middle button + Ctrl
+ if (e->button() == Qt::MidButton && e->modifiers() & Qt::ControlModifier) {
+ setZoomFactorTarget(1);
+ setAngleTarget(0);
+ return;
+ }
+
+ QPointF p = mapToScene(e->pos());
+ TreeItem *ti_found = findMapItem(p, NULL);
+ LinkableMapObj *lmo_found = NULL;
+ if (ti_found)
+ lmo_found = ((MapItem *)ti_found)->getLMO();
+
+
+ // Allow selecting text in QLineEdit if necessary
+ if (model->isSelectionBlocked()) {
+ e->ignore();
+ QGraphicsView::mousePressEvent(e);
+ return;
+ }
+
+ // Stop editing in LineEdit
+ if (state == EditingHeading) editHeadingFinished();
+
+ QString sysFlagName;
+ QUuid uid;
+ if (lmo_found) {
+ uid = ((BranchObj *)lmo_found)->findSystemFlagUidByPos(p);
+ if (!uid.isNull()) {
+ Flag *flag = systemFlagsMaster->findFlagByUid(uid);
+ if (flag)
+ sysFlagName = flag->getName();
+ }
+ }
+
+ /*
+ qDebug() << "ME::mouse pressed\n";
+ qDebug() << " lmo_found=" << lmo_found;
+ qDebug() << " ti_found=" << ti_found;
+ //if (ti_found) qDebug() << " ti_found="<<ti_found->getHeading();
+ qDebug() << " flag=" << sysFlagName;
+ */
+
+ // Check modifier key (before selecting object!)
+ if (ti_found && (e->modifiers() & Qt::ShiftModifier)) {
+ if (mainWindow->getModMode() == Main::ModModeColor) {
+ setState(PickingColor);
+ mainWindow->setCurrentColor(ti_found->getHeadingColor());
+ if (e->modifiers() & Qt::ControlModifier)
+ model->colorBranch(ti_found->getHeadingColor());
+ else
+ model->colorSubtree(ti_found->getHeadingColor());
+ return;
+ }
+
+ if (mainWindow->getModMode() == Main::ModModeMoveView) {
+ startMovingView(e);
+ return;
+ }
+ }
+
+ // Check vymlink modifier (before selecting object!)
+ if (ti_found && sysFlagName == "system-vymLink") {
+ model->select(ti_found);
+ if (e->modifiers() & Qt::ControlModifier) {
+ if (e->modifiers() & Qt::ShiftModifier)
+ model->deleteVymLink();
+ else
+ mainWindow->editOpenVymLink(true);
+ } else
+ mainWindow->editOpenVymLink(false);
+ return;
+ }
+
+ // Select the clicked object, if not moving without linking
+ if (ti_found && (e->modifiers() & Qt::ShiftModifier)) {
+ if (mainWindow->getModMode() == Main::ModModePoint) {
+ model->selectToggle(ti_found);
+ lastToggleDirection = toggleUndefined;
+ }
+ }
+ else
+ model->select(ti_found);
+
+ e->accept();
+
+ // Take care of remaining system flags _or_ modifier modes
+ if (lmo_found) {
+ if (!sysFlagName.isEmpty()) {
+ // systemFlag clicked
+ if (sysFlagName.contains("system-url")) {
+ if (e->modifiers() & Qt::ControlModifier)
+ mainWindow->editOpenURLTab();
+ else
+ mainWindow->editOpenURL();
+ }
+ else if (sysFlagName == "system-note")
+ mainWindow->windowToggleNoteEditor();
+ else if (sysFlagName == "hideInExport")
+ model->toggleHideExport();
+ else if (sysFlagName.startsWith("system-task-"))
+ model->cycleTaskStatus();
+ return;
+ }
+ else {
+ // Take care of xLink: Open context menu with targets
+ // if clicked near to begin of xlink
+ if (ti_found->xlinkCount() > 0 &&
+ ti_found->getType() != TreeItem::MapCenter &&
+ lmo_found->getBBox().width() > 30) {
+ if ((lmo_found->getOrientation() !=
+ LinkableMapObj::RightOfCenter &&
+ p.x() < lmo_found->getBBox().left() + 10) ||
+ (lmo_found->getOrientation() !=
+ LinkableMapObj::LeftOfCenter &&
+ p.x() > lmo_found->getBBox().right() - 10)) {
+ // FIXME-4 similar code in mainwindow::updateActions
+ QMenu menu;
+ QList<QAction *> alist;
+ QList<BranchItem *> blist;
+ for (int i = 0; i < ti_found->xlinkCount(); i++) {
+ XLinkItem *xli = ti_found->getXLinkItemNum(i);
+ BranchItem *bit = xli->getPartnerBranch();
+ if (bit)
+ alist.append(
+ new QAction(ti_found->getXLinkItemNum(i)
+ ->getPartnerBranch()
+ ->getHeadingPlain(),
+ &menu));
+ }
+ menu.addActions(alist);
+ QAction *ra = menu.exec(e->globalPos());
+ if (ra)
+ model->select(blist.at(alist.indexOf(ra)));
+ while (!alist.isEmpty()) {
+ QAction *a = alist.takeFirst();
+ delete a;
+ }
+ return;
+ }
+ }
+ }
+ }
+
+ // XLink modifier, create new XLink
+ BranchItem *selbi = model->getSelectedBranch();
+ if (selbi && mainWindow->getModMode() == Main::ModModeXLink &&
+ (e->modifiers() & Qt::ShiftModifier)) {
+ setState(DrawingLink);
+ tmpLink = new Link(model);
+ tmpLink->setBeginBranch(selbi);
+ tmpLink->createMapObj();
+ tmpLink->setStyleBegin("None");
+ tmpLink->setStyleEnd("None");
+ tmpLink->setEndPoint(mapToScene(e->pos()));
+ tmpLink->updateLink();
+ return;
+ }
+
+ // Start moving around
+ if (lmo_found) {
+ // Left Button Move Branches
+ if (e->button() == Qt::LeftButton) {
+ // No system flag clicked, take care of moving modes or simply
+ // moving
+ movingObj_offset.setX(p.x() - lmo_found->x());
+ movingObj_offset.setY(p.y() - lmo_found->y());
+ movingObj_orgPos.setX(lmo_found->x());
+ movingObj_orgPos.setY(lmo_found->y());
+ if (ti_found->depth() > 0) {
+ lmo_found->setRelPos();
+ movingObj_orgRelPos = lmo_found->getRelPos();
+ }
+
+ if (mainWindow->getModMode() == Main::ModModeMoveObject &&
+ e->modifiers() & Qt::ShiftModifier) {
+ setState(MovingObjectWithoutLinking);
+ }
+ else
+ setState(MovingObject);
+
+ movingObj = model->getSelectedLMO();
+ }
+ else
+ // Middle Button Toggle Scroll
+ // (On Mac OS X this won't work, but we still have
+ // a button in the toolbar)
+ if (e->button() == Qt::MidButton)
+ model->toggleScroll();
+ }
+ else { // No lmo found, check XLinks
+ if (ti_found) {
+ if (ti_found->getType() == TreeItem::XLink) {
+ XLinkObj *xlo = (XLinkObj *)((MapItem *)ti_found)->getMO();
+ if (xlo) {
+ setState(DrawingXLink);
+ int i = xlo->ctrlPointInClickBox(p);
+ if (i >= 0)
+ xlo->setSelection(i);
+ movingObj_offset.setX(p.x() - xlo->x());
+ movingObj_offset.setY(p.y() - xlo->y());
+ movingObj_orgPos.setX(xlo->x());
+ movingObj_orgPos.setY(xlo->y());
+ }
+ }
+ }
+ else { // No MapObj found, we are on the scene itself
+ // Left Button move Pos of sceneView
+ if (e->button() == Qt::LeftButton ||
+ e->button() == Qt::MiddleButton) {
+ startMovingView(e);
+ return;
+ }
+ }
+ }
+}
+
+void MapEditor::mouseMoveEvent(QMouseEvent *e)
+{
+ // Show mouse position for debugging in statusBar
+ if (debug && e->modifiers() & Qt::ControlModifier)
+ mainWindow->statusMessage(
+ QString("ME::mousePressEvent Scene: %1 widget: %2")
+ .arg(qpointFToString(mapToScene(e->pos())))
+ .arg(qpointFToString(e->pos())));
+
+ // Allow selecting text in QLineEdit if necessary
+ if (model->isSelectionBlocked()) {
+ e->ignore();
+ QGraphicsView::mouseMoveEvent(e);
+ return;
+ }
+
+ // Move sceneView
+ if (state == MovingView &&
+ (e->buttons() == Qt::LeftButton || e->buttons() == Qt::MiddleButton)) {
+ QPointF p = e->globalPos();
+ movingVec.setX(-p.x() + movingObj_offset.x());
+ movingVec.setY(-p.y() + movingObj_offset.y());
+ horizontalScrollBar()->setSliderPosition(
+ (int)(movingCont_start.x() + movingVec.x()));
+ verticalScrollBar()->setSliderPosition(
+ (int)(movingCont_start.y() + movingVec.y()));
+ // Avoid flickering
+ scrollBarPosAnimation.stop();
+ viewCenterAnimation.stop();
+ rotationAnimation.stop();
+ // zoomAnimation.stop();
+
+ return;
+ }
+
+ TreeItem *seli = model->getSelectedItem();
+
+ MapObj *mosel = NULL;
+ if (seli)
+ mosel = ((MapItem *)seli)->getMO();
+
+ // If not already happened during mousepress, we might need to switch state
+ if (mainWindow->getModMode() == Main::ModModeMoveObject &&
+ e->modifiers() & Qt::ShiftModifier && e->buttons() == Qt::LeftButton) {
+ state = MovingObjectWithoutLinking;
+ }
+
+ // Move the selected MapObj
+ if (mosel &&
+ (state == MovingObject || state == MovingObjectWithoutLinking ||
+ state == DrawingXLink)) {
+ int margin = 50;
+
+ // Check if we have to scroll
+ vPan.setX(0);
+ vPan.setY(0);
+ if (e->y() >= 0 && e->y() <= margin)
+ vPan.setY(e->y() - margin);
+ else if (e->y() <= height() && e->y() > height() - margin)
+ vPan.setY(e->y() - height() + margin);
+ if (e->x() >= 0 && e->x() <= margin)
+ vPan.setX(e->x() - margin);
+ else if (e->x() <= width() && e->x() > width() - margin)
+ vPan.setX(e->x() - width() + margin);
+
+ pointerPos = e->pos();
+ pointerMod = e->modifiers();
+ moveObject();
+ } // selection && moving_obj
+
+ // Draw a link from one branch to another
+ if (state == DrawingLink) {
+ tmpLink->setEndPoint(mapToScene(e->pos()));
+ tmpLink->updateLink();
+ }
+}
+
+void MapEditor::moveObject()
+{
+ if (!panningTimer->isActive())
+ panningTimer->start(50);
+
+ QPointF p = mapToScene(pointerPos);
+ TreeItem *seli = model->getSelectedItem();
+ LinkableMapObj *lmosel = NULL;
+ if (seli)
+ lmosel = ((MapItem *)seli)->getLMO();
+
+ objectMoved = true;
+
+ // reset cursor if we are moving and don't copy
+
+ // Check if we could link
+ TreeItem *ti_found = findMapItem(p, seli);
+ BranchItem *bi_dst = NULL;
+ LinkableMapObj *lmo_dst = NULL;
+ if (ti_found && ti_found != seli && ti_found->isBranchLikeType()) {
+ bi_dst = (BranchItem *)ti_found;
+ lmo_dst = bi_dst->getLMO();
+ }
+ else
+ bi_dst = NULL;
+
+ if (lmosel) {
+ if (seli->getType() == TreeItem::Image) {
+ FloatImageObj *fio = (FloatImageObj *)lmosel;
+ fio->moveCenter(p.x() - movingObj_offset.x(),
+ p.y() - movingObj_offset.y());
+ fio->setRelPos();
+ fio->updateLinkGeometry(); // no need for reposition, if we update
+ // link here
+ model->emitSelectionChanged(); // position has changed
+
+ // Relink float to new mapcenter or branch, if shift is pressed
+ // Only relink, if selection really has a new parent
+ if (pointerMod == Qt::ShiftModifier && bi_dst &&
+ bi_dst != seli->parent()) {
+ // Also save the move which was done so far
+ QString pold = qpointFToString(movingObj_orgRelPos);
+ QString pnow = qpointFToString(fio->getRelPos());
+ model->saveState(seli, "moveRel " + pold, seli,
+ "moveRel " + pnow,
+ QString("Move %1 to relative position %2")
+ .arg(model->getObjectName(lmosel))
+ .arg(pnow));
+ model->reposition();
+
+ model->relinkImage((ImageItem *)seli, bi_dst);
+ model->select(seli);
+ }
+ }
+ else if (seli->isBranchLikeType()) { // selection != a FloatObj
+ if (seli->depth() == 0) {
+ // Move mapcenter
+ lmosel->move(p - movingObj_offset);
+ if (pointerMod == Qt::ControlModifier) {
+ // Move only mapcenter, leave its children where they are
+ QPointF v;
+ v = lmosel->getAbsPos();
+ for (int i = 0; i < seli->branchCount(); ++i) {
+ seli->getBranchObjNum(i)->setRelPos();
+ seli->getBranchObjNum(i)->setOrientation();
+ }
+ }
+ }
+ else {
+ if (seli->depth() == 1) {
+ // Move mainbranch
+ if (!lmosel->hasParObjTmp())
+ lmosel->move(p - movingObj_offset);
+ lmosel->setRelPos();
+ }
+ else {
+ // d>1, move ordinary branch
+ if (lmosel->getOrientation() ==
+ LinkableMapObj::LeftOfCenter)
+ // Add width of bbox here, otherwise alignRelTo will
+ // cause jumping around
+ lmosel->move(p.x() - movingObj_offset.x(),
+ p.y() - movingObj_offset.y() +
+ lmosel->getTopPad());
+ else
+ lmosel->move(p.x() - movingObj_offset.x(),
+ p.y() - movingObj_offset.y() -
+ lmosel->getTopPad());
+ BranchItem *selbi = ((BranchItem *)seli);
+ if (selbi->parentBranch()->getChildrenLayout() ==
+ BranchItem::FreePositioning)
+ lmosel->setRelPos();
+ }
+
+ } // depth>0
+
+ // Maybe we can relink temporary?
+ if (bi_dst && state != MovingObjectWithoutLinking) {
+ if (pointerMod == Qt::ControlModifier) {
+ // Special case: CTRL to link below dst
+ lmosel->setParObjTmp(lmo_dst, p, +1);
+ }
+ else if (pointerMod == Qt::ShiftModifier)
+ lmosel->setParObjTmp(lmo_dst, p, -1);
+ else
+ lmosel->setParObjTmp(lmo_dst, p, 0);
+ }
+ else
+ lmosel->unsetParObjTmp();
+
+ // reposition subbranch
+ lmosel->reposition();
+
+ QItemSelection sel = model->getSelectionModel()->selection();
+ updateSelection(sel, sel); // position has changed
+
+ // In winter mode shake snow from heading
+ if (winter)
+ model->emitDataChanged(seli);
+ } // Moving branchLikeType
+ } // End of lmosel != NULL
+ else if (seli && seli->getType() == TreeItem::XLink) {
+ // Move XLink control point
+ MapObj *mosel = ((MapItem *)seli)->getMO();
+ if (mosel) {
+ mosel->move(p - movingObj_offset); // FIXME-3 Missing savestate
+ model->setChanged();
+ model->emitSelectionChanged();
+ }
+ }
+ else
+ qWarning("ME::moveObject Huh? I'm confused.");
+
+ scene()->update();
+
+ return;
+}
+
+void MapEditor::mouseReleaseEvent(QMouseEvent *e)
+{
+ // Allow selecting text in QLineEdit if necessary
+ if (model->isSelectionBlocked()) {
+ e->ignore();
+ QGraphicsView::mouseReleaseEvent(e);
+ return;
+ }
+
+ QPointF p = mapToScene(e->pos());
+ TreeItem *seli = model->getSelectedItem();
+
+ TreeItem *dsti = NULL;
+ if (seli)
+ dsti = findMapItem(p, seli);
+ LinkableMapObj *dst = NULL;
+ BranchItem *selbi = model->getSelectedBranch();
+ if (dsti && dsti->isBranchLikeType())
+ dst = ((MapItem *)dsti)->getLMO();
+ else
+ dsti = NULL;
+
+ // Have we been picking color?
+ if (state == PickingColor) {
+ setCursor(Qt::ArrowCursor);
+ // Check if we are over another branch
+ if (dst) {
+ if (e->modifiers() & Qt::ShiftModifier)
+ model->colorBranch(mainWindow->getCurrentColor());
+ else
+ model->colorSubtree(mainWindow->getCurrentColor());
+ }
+ setState(Neutral);
+ return;
+ }
+
+ // Have we been drawing a link?
+ if (state == DrawingLink) {
+ setState(Neutral);
+ // Check if we are over another branch
+ if (dsti) {
+ tmpLink->setEndBranch(((BranchItem *)dsti));
+ tmpLink->activate();
+ tmpLink->updateLink();
+ if (model->createLink(tmpLink)) {
+ model->saveState(
+ tmpLink->getBeginLinkItem(), "remove ()", seli,
+ QString("addXLink (\"%1\",\"%2\",%3,\"%4\",\"%5\")")
+ .arg(model->getSelectString(tmpLink->getBeginBranch()))
+ .arg(model->getSelectString(tmpLink->getEndBranch()))
+ .arg(tmpLink->getPen().width())
+ .arg(tmpLink->getPen().color().name())
+ .arg(penStyleToString(tmpLink->getPen().style())),
+ QString("Adding Link from %1 to %2")
+ .arg(model->getObjectName(seli))
+ .arg(model->getObjectName(dsti)));
+ return;
+ }
+ }
+ delete (tmpLink);
+ tmpLink = NULL;
+ return;
+ }
+
+ // Have we been moving something?
+ if (seli && state == MovingObject) {
+ panningTimer->stop();
+ if (seli->getType() == TreeItem::Image) {
+ FloatImageObj *fio = (FloatImageObj *)(((MapItem *)seli)->getLMO());
+ if (fio) {
+ // Moved Image, we need to reposition
+ QString pold = qpointFToString(movingObj_orgRelPos);
+ QString pnow = qpointFToString(fio->getRelPos());
+ model->saveState(seli, "moveRel " + pold, seli,
+ "moveRel " + pnow,
+ QString("Move %1 to relative position %2")
+ .arg(model->getObjectName(seli))
+ .arg(pnow));
+
+ model->emitDataChanged(
+ seli->parent()); // Parent of image has changed
+ model->reposition();
+ }
+ }
+
+ if (selbi && selbi->depth() == 0) {
+ if (movingObj_orgPos != selbi->getBranchObj()->getAbsPos()) {
+ QString pold = qpointFToString(movingObj_orgPos);
+ QString pnow =
+ qpointFToString(selbi->getBranchObj()->getAbsPos());
+
+ model->saveState(selbi, "move " + pold, selbi, "move " + pnow,
+ QString("Move mapcenter %1 to position %2")
+ .arg(model->getObjectName(selbi))
+ .arg(pnow));
+ }
+ }
+
+ if (seli->isBranchLikeType()) //(seli->getType() == TreeItem::Branch )
+ { // A branch was moved
+ LinkableMapObj *lmosel = NULL;
+ lmosel = ((MapItem *)seli)->getLMO();
+
+ // save the position in case we link to mapcenter
+ QPointF savePos = QPointF(lmosel->getAbsPos());
+
+ // Reset the temporary drawn link to the original one
+ lmosel->unsetParObjTmp();
+
+ // For Redo we may need to save original selection
+ QString preSelStr = model->getSelectString(seli);
+
+ if (dsti && objectMoved && state != MovingObjectWithoutLinking) {
+ // We have a destination, relink to that
+ BranchObj *selbo = model->getSelectedBranchObj();
+
+ QString preParStr = model->getSelectString(seli->parent());
+ QString preNum = QString::number(seli->num(), 10);
+ QString preDstParStr;
+
+ if (e->modifiers() & Qt::ShiftModifier &&
+ dsti->parent()) { // Link above dst
+ preDstParStr = model->getSelectString(dsti->parent());
+ model->relinkBranch((BranchItem *)seli,
+ (BranchItem *)dsti->parent(),
+ ((BranchItem *)dsti)->num(), true);
+ }
+ else if (e->modifiers() & Qt::ControlModifier &&
+ dsti->parent()) {
+ // Link below dst
+ preDstParStr = model->getSelectString(dsti->parent());
+ model->relinkBranch((BranchItem *)seli,
+ (BranchItem *)dsti->parent(),
+ ((BranchItem *)dsti)->num() + 1, true);
+ }
+ else { // Append to dst
+ preDstParStr = model->getSelectString(dsti);
+ model->relinkBranch((BranchItem *)seli, (BranchItem *)dsti,
+ -1, true, movingObj_orgPos);
+ if (dsti->depth() == 0)
+ selbo->move(savePos);
+ }
+ }
+ else {
+ // No destination, undo temporary move
+
+ if (seli->depth() == 1) {
+ // The select string might be different _after_ moving
+ // around. Therefor reposition and then use string of old
+ // selection, too
+ model->reposition();
+
+ QPointF rp(lmosel->getRelPos());
+ if (rp != movingObj_orgRelPos) {
+ QString ps = qpointFToString(rp);
+ model->saveState(
+ model->getSelectString(lmosel),
+ "moveRel " + qpointFToString(movingObj_orgRelPos),
+ preSelStr, "moveRel " + ps,
+ QString("Move %1 to relative position %2")
+ .arg(model->getObjectName(lmosel))
+ .arg(ps));
+ }
+ }
+
+ if (selbi->parentBranch()->getChildrenLayout() ==
+ BranchItem::FreePositioning) {
+ lmosel->setRelPos();
+ model->reposition();
+ }
+ else {
+
+ // Draw the original link, before selection was moved around
+ if (settings.value("/animation/use", true).toBool() &&
+ seli->depth() > 1
+ // && distance
+ //(lmosel->getRelPos(),movingObj_orgRelPos)<3
+ ) {
+ lmosel->setRelPos(); // calc relPos first for starting
+ // point
+
+ model->startAnimation((BranchObj *)lmosel,
+ lmosel->getRelPos(),
+ movingObj_orgRelPos);
+ }
+ else
+ model->reposition();
+ }
+ }
+ }
+ // Finally resize scene, if needed
+ scene()->update();
+ movingObj = NULL;
+ objectMoved = false;
+ vPan = QPoint();
+ }
+ else
+ // maybe we moved View: set old cursor
+ setCursor(Qt::ArrowCursor);
+
+ if (state != EditingHeading)
+ setState(Neutral); // Continue editing after double click!
+
+ QGraphicsView::mouseReleaseEvent(e);
+}
+
+void MapEditor::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ // Allow selecting text in QLineEdit if necessary
+ if (model->isSelectionBlocked()) {
+ e->ignore();
+ QGraphicsView::mouseDoubleClickEvent(e);
+ return;
+ }
+
+ if (e->button() == Qt::LeftButton) {
+ QPointF p = mapToScene(e->pos());
+ TreeItem *ti = findMapItem(p, NULL);
+ LinkableMapObj *lmo;
+ if (ti) {
+ if (state == EditingHeading)
+ editHeadingFinished();
+ model->select(ti);
+ BranchItem *selbi = model->getSelectedBranch();
+ if (selbi) {
+ lmo = ((MapItem *)ti)->getLMO();
+ if (lmo) {
+ QUuid uid = ((BranchObj *)lmo)->findSystemFlagUidByPos(p);
+
+ // Don't edit heading when double clicking system flag:
+ if (!uid.isNull())
+ return;
+ }
+ }
+ e->accept();
+ editHeading();
+ }
+ }
+}
+
+void MapEditor::wheelEvent(QWheelEvent *e)
+{
+ if (e->modifiers() & Qt::ControlModifier &&
+ e->angleDelta().y() != 0) {
+ QPointF p = mapToScene(e->position().toPoint());
+ if (e->angleDelta().y() > 0)
+ // setZoomFactorTarget (zoomFactorTarget*1.15);
+ setViewCenterTarget(p, zoomFactorTarget * 1.15, angleTarget);
+ else
+ // setZoomFactorTarget (zoomFactorTarget*0.85);
+ setViewCenterTarget(p, zoomFactorTarget * 0.85, angleTarget);
+ }
+ else {
+ scrollBarPosAnimation.stop();
+ QGraphicsView::wheelEvent(e);
+ }
+}
+
+void MapEditor::focusOutEvent(QFocusEvent *)
+{
+ // qDebug()<<"ME::focusOutEvent"<<e->reason();
+ if (state == EditingHeading)
+ editHeadingFinished();
+}
+
+void MapEditor::resizeEvent(QResizeEvent *e) { QGraphicsView::resizeEvent(e); }
+
+void MapEditor::dragEnterEvent(QDragEnterEvent *event)
+{
+ // for (unsigned int i=0;event->format(i);i++) // Debug mime type
+ // cerr << event->format(i) << endl;
+
+ if (event->mimeData()->hasImage())
+ event->acceptProposedAction();
+ else if (event->mimeData()->hasUrls())
+ event->acceptProposedAction();
+}
+
+void MapEditor::dragMoveEvent(QDragMoveEvent *) {}
+
+void MapEditor::dragLeaveEvent(QDragLeaveEvent *event) { event->accept(); }
+
+void MapEditor::dropEvent(QDropEvent *event)
+{
+ BranchItem *selbi = model->getSelectedBranch();
+ if (selbi) {
+ if (debug) {
+ foreach (QString format, event->mimeData()->formats())
+ qDebug() << "MapEditor: Dropped format: " << qPrintable(format);
+ foreach (QUrl url, event->mimeData()->urls()) {
+ qDebug() << " URL-path:" << url.path();
+ qDebug() << "URL-string:" << url.toString();
+ qDebug() << " enc:" << url.toEncoded();
+ qDebug() << " valid:" << url.isValid();
+ }
+ qDebug() << "============== mimeData ===================";
+ qDebug() << "has-img : " << event->mimeData()->hasImage();
+ qDebug() << "has-urls: " << event->mimeData()->hasUrls();
+ qDebug() << " text: " << event->mimeData()->text();
+ qDebug() << "===========================================";
+ }
+
+ if (event->mimeData()->hasUrls()) {
+ // Try text representation first, which works on windows, but in
+ // Linux only for https, not local images
+ QString url = event->mimeData()->text();
+ if (url.isEmpty()) {
+ QByteArray ba =
+ event->mimeData()->urls().first().path().toLatin1();
+ QByteArray ba2;
+ for (int i = 0; i < ba.count(); i++)
+ if (ba.at(i) != 0)
+ ba2.append(ba.at(i));
+ url = ba2;
+ }
+
+ BranchItem *bi = NULL;
+ // Workaround to avoid adding empty branches
+ if (!url.isEmpty()) {
+ if (url.startsWith("file://"))
+ url.remove(0, 7);
+
+#if defined(Q_OS_WIN32)
+ if (url.startsWith("/"))
+ url.remove(0, 1);
+#endif
+ if (isImage(url)) {
+ if (debug)
+ qDebug() << "dropped url seems to be image: " << url;
+ // Image, try to download or set image from local file
+ if (url.startsWith("http"))
+ model->downloadImage(url);
+ else
+ model->loadImage(bi, url);
+ if (debug)
+ qDebug() << "finished loading image";
+ }
+ else {
+ bi = model->addNewBranch();
+ if (bi) {
+ model->select(bi);
+ if (url.endsWith(".vym", Qt::CaseInsensitive))
+ model->setVymLink(url);
+ else {
+ model->setURL(url);
+ model->setHeadingPlainText(url);
+ }
+
+ model->select(bi->parent());
+ }
+ }
+ }
+ }
+ }
+ event->acceptProposedAction();
+}
+
+void MapEditor::setState(EditorState s)
+{
+ if (state != Neutral && s != Neutral)
+ qWarning() << "MapEditor::setState switching directly from " << state
+ << " to " << s;
+ state = s;
+ /* if (debug)
+ {
+ QString s;
+ switch (state)
+ {
+ case Neutral:
+ s = "Neutral";
+ break;
+ case EditingHeading:
+ s = "EditingHeading";
+ break;
+ case EditingLink:
+ s = "EditingLink";
+ break;
+ case MovingObject:
+ s = "MovingObject";
+ break;
+ case MovingObjectWithoutLinking:
+ s = "MovingObjectWithoutLinking";
+ break;
+ case MovingView:
+ s = "MovingView";
+ break;
+ case PickingColor:
+ s = "PickingColor";
+ break;
+ case DrawingLink:
+ s = "DrawingLink";
+ break;
+ }
+ qDebug() << "MapEditor: State " << s << " of " << model->getMapName();
+ }
+ */
+}
+
+MapEditor::EditorState MapEditor::getState() { return state; }
+
+void MapEditor::updateSelection(QItemSelection nsel, QItemSelection dsel)
+{
+ Q_UNUSED(nsel);
+
+ QList<MapItem *> itemsSelected;
+ QList<MapItem *> itemsDeselected;
+
+ QItemSelection sel = model->getSelectionModel()->selection();
+
+ LinkableMapObj *lmo;
+
+ // Add new selected objects
+ if (sel.indexes().count() > 1)
+ mainWindow->statusMessage(
+ tr("%1 items selected").arg(sel.indexes().count()));
+
+ foreach (QModelIndex ix, sel.indexes()) {
+ MapItem *mi = static_cast<MapItem *>(ix.internalPointer());
+ if (mi->isBranchLikeType() || mi->getType() == TreeItem::Image ||
+ mi->getType() == TreeItem::XLink)
+ if (!itemsSelected.contains(mi))
+ itemsSelected.append(mi);
+ lmo = mi->getLMO();
+ if (lmo)
+ mi->getLMO()->updateVisibility();
+ }
+
+ // Delete objects meanwhile removed from selection
+ foreach (QModelIndex ix, dsel.indexes()) {
+ MapItem *mi = static_cast<MapItem *>(ix.internalPointer());
+ if (mi->isBranchLikeType() || mi->getType() == TreeItem::Image ||
+ mi->getType() == TreeItem::XLink)
+ if (!itemsDeselected.contains(mi))
+ itemsDeselected.append(mi);
+ lmo = mi->getLMO(); // FIXME-2 xlink does return nullptr
+ if (lmo)
+ mi->getLMO()->updateVisibility();
+ }
+
+ // Trim list of selection paths
+ while (itemsSelected.count() < selPathList.count())
+ delete selPathList.takeFirst();
+
+ // Reduce polygons
+ while (itemsSelected.count() < selPathList.count())
+ delete selPathList.takeFirst();
+
+ // Add additonal polygons
+ QGraphicsPathItem *sp;
+ while (itemsSelected.count() > selPathList.count()) {
+ sp = mapScene->addPath(QPainterPath());
+ sp->show();
+ selPathList.append(sp);
+ }
+
+ // Reposition polygons
+ for (int i = 0; i < itemsSelected.count(); ++i) {
+ MapObj *mo = itemsSelected.at(i)->getMO();
+ sp = selPathList.at(i);
+ sp->setPath(mo->getSelectionPath());
+ sp->setPen(selectionPen);
+ sp->setBrush(selectionBrush);
+ sp->setParentItem(mo);
+ sp->setZValue(dZ_SELBOX);
+
+ // Reposition also LineEdit for heading during animation
+ if (lineEdit)
+ lineEdit->move(mo->getAbsPos().toPoint());
+ }
+
+ scene()->update();
+}
+
+void MapEditor::updateData(const QModelIndex &sel)
+{
+ TreeItem *ti = static_cast<TreeItem *>(sel.internalPointer());
+
+ /* testing
+ qDebug() << "ME::updateData";
+ if (!ti)
+ {
+ qDebug() << " ti=NULL";
+ return;
+ }
+ qDebug() << " ti="<<ti;
+ qDebug() << " h="<<ti->getHeadingPlain();
+ */
+
+ if (ti && ti->isBranchLikeType()) {
+ BranchObj *bo = (BranchObj *)(((MapItem *)ti)->getLMO());
+ bo->updateVisuals();
+ }
+
+ if (winter) {
+ QList<QRectF> obstacles;
+ BranchObj *bo;
+ BranchItem *cur = NULL;
+ BranchItem *prev = NULL;
+ model->nextBranch(cur, prev);
+ while (cur) {
+ if (!cur->hasHiddenExportParent()) {
+ // Branches
+ bo = (BranchObj *)(cur->getLMO());
+ if (bo && bo->isVisibleObj())
+ obstacles.append(bo->getBBox());
+ }
+ model->nextBranch(cur, prev);
+ }
+ winter->setObstacles(obstacles);
+ }
+}
+
+void MapEditor::togglePresentationMode()
+{
+ mainWindow->togglePresentationMode();
+}
+
+void MapEditor::setSelectionPen(const QPen &p)
+{
+ selectionPen = p;
+ QItemSelection sel = model->getSelectionModel()->selection();
+ updateSelection(sel, sel);
+}
+
+QPen MapEditor::getSelectionPen() { return selectionPen; }
+
+void MapEditor::setSelectionBrush(const QBrush &b)
+{
+ selectionBrush = b;
+ QItemSelection sel = model->getSelectionModel()->selection();
+ updateSelection(sel, sel);
+}
+
+QBrush MapEditor::getSelectionBrush() { return selectionBrush; }