diff --git a/game/arcade/dustrac/files/editor.patch b/game/arcade/dustrac/files/editor.patch new file mode 100644 index 0000000000..49201b9753 --- /dev/null +++ b/game/arcade/dustrac/files/editor.patch @@ -0,0 +1,5016 @@ +diff -Nuar "a/data/levels/Desert Storm.trk" "b/data/levels/Desert Storm.trk" +--- "a/data/levels/Desert Storm.trk" 2017-12-30 16:09:21.000000000 +0300 ++++ "b/data/levels/Desert Storm.trk" 2018-02-11 08:22:08.000000000 +0300 +@@ -1,993 +1,993 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + +diff -Nuar a/README.md b/README.md +--- a/README.md 2017-12-30 16:09:21.000000000 +0300 ++++ b/README.md 2018-02-11 08:22:08.000000000 +0300 +@@ -11,7 +11,7 @@ + + + +-Video of the game ++Video of the game + + Dust Racing (Dustrac) is a tile-based, cross-platform 2D racing game written + in Qt (C++) and OpenGL. +diff -Nuar a/src/editor/application.cpp b/src/editor/application.cpp +--- a/src/editor/application.cpp 2017-12-30 16:09:21.000000000 +0300 ++++ b/src/editor/application.cpp 2018-02-11 08:22:08.000000000 +0300 +@@ -58,7 +58,7 @@ + QString lang = ""; + + const std::vector args(argv, argv + argc); +- for (unsigned int i = 0; i < args.size(); i++) ++ for (unsigned int i = 1; i < args.size(); i++) + { + if (args[i] == "-h" || args[i] == "--help") + { +diff -Nuar a/src/editor/CMakeLists.txt b/src/editor/CMakeLists.txt +--- a/src/editor/CMakeLists.txt 2017-12-30 16:09:21.000000000 +0300 ++++ b/src/editor/CMakeLists.txt 2018-02-11 08:22:08.000000000 +0300 +@@ -10,12 +10,15 @@ + set(SRC + aboutdlg.cpp + application.cpp ++ draganddropstore.cpp + editordata.cpp + editorview.cpp +- editorscene.cpp ++ editormode.hpp ++ floodfill.cpp + main.cpp + mainwindow.cpp + map.cpp ++ mediator.cpp + newtrackdialog.cpp + object.cpp + objectfactory.cpp +diff -Nuar a/src/editor/draganddropstore.cpp b/src/editor/draganddropstore.cpp +--- a/src/editor/draganddropstore.cpp 1970-01-01 02:00:00.000000000 +0200 ++++ b/src/editor/draganddropstore.cpp 2018-02-11 08:22:08.000000000 +0300 +@@ -0,0 +1,69 @@ ++// This file is part of Dust Racing 2D. ++// Copyright (C) 2018 Jussi Lind ++// ++// Dust Racing 2D is free software: you can redistribute it and/or modify ++// it under the terms of the GNU General Public License as published by ++// the Free Software Foundation, either version 3 of the License, or ++// (at your option) any later version. ++// Dust Racing 2D is distributed in the hope that it will be useful, ++// but WITHOUT ANY WARRANTY; without even the implied warranty of ++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++// GNU General Public License for more details. ++// ++// You should have received a copy of the GNU General Public License ++// along with Dust Racing 2D. If not, see . ++ ++#include "draganddropstore.hpp" ++ ++DragAndDropStore::DragAndDropStore() ++{ ++} ++ ++void DragAndDropStore::clear() ++{ ++ m_dragAndDropSourceTile = nullptr; ++ ++ m_dragAndDropObject = nullptr; ++ ++ m_dragAndDropTargetNode = nullptr; ++} ++ ++void DragAndDropStore::setDragAndDropSourceTile(TrackTile * tile) ++{ ++ m_dragAndDropSourceTile = tile; ++} ++ ++TrackTile * DragAndDropStore::dragAndDropSourceTile() const ++{ ++ return m_dragAndDropSourceTile; ++} ++ ++void DragAndDropStore::setDragAndDropObject(Object * object) ++{ ++ m_dragAndDropObject = object; ++} ++ ++Object * DragAndDropStore::dragAndDropObject() const ++{ ++ return m_dragAndDropObject; ++} ++ ++void DragAndDropStore::setDragAndDropTargetNode(TargetNode * tnode) ++{ ++ m_dragAndDropTargetNode = tnode; ++} ++ ++TargetNode * DragAndDropStore::dragAndDropTargetNode() const ++{ ++ return m_dragAndDropTargetNode; ++} ++ ++void DragAndDropStore::setDragAndDropSourcePos(QPointF pos) ++{ ++ m_dragAndDropSourcePos = pos; ++} ++ ++QPointF DragAndDropStore::dragAndDropSourcePos() const ++{ ++ return m_dragAndDropSourcePos; ++} +diff -Nuar a/src/editor/draganddropstore.hpp b/src/editor/draganddropstore.hpp +--- a/src/editor/draganddropstore.hpp 1970-01-01 02:00:00.000000000 +0200 ++++ b/src/editor/draganddropstore.hpp 2018-02-11 08:22:08.000000000 +0300 +@@ -0,0 +1,60 @@ ++// This file is part of Dust Racing 2D. ++// Copyright (C) 2018 Jussi Lind ++// ++// Dust Racing 2D is free software: you can redistribute it and/or modify ++// it under the terms of the GNU General Public License as published by ++// the Free Software Foundation, either version 3 of the License, or ++// (at your option) any later version. ++// Dust Racing 2D is distributed in the hope that it will be useful, ++// but WITHOUT ANY WARRANTY; without even the implied warranty of ++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++// GNU General Public License for more details. ++// ++// You should have received a copy of the GNU General Public License ++// along with Dust Racing 2D. If not, see . ++ ++#ifndef DRAGANDDROPSTORE_HPP ++#define DRAGANDDROPSTORE_HPP ++ ++#include ++ ++class Object; ++class TargetNode; ++class TrackTile; ++ ++class DragAndDropStore ++{ ++public: ++ ++ DragAndDropStore(); ++ ++ void clear(); ++ ++ void setDragAndDropTargetNode(TargetNode * tnode); ++ ++ TargetNode * dragAndDropTargetNode() const; ++ ++ QPointF dragAndDropSourcePos() const; ++ ++ void setDragAndDropSourcePos(QPointF pos); ++ ++ void setDragAndDropSourceTile(TrackTile * tile); ++ ++ TrackTile * dragAndDropSourceTile() const; ++ ++ void setDragAndDropObject(Object * object); ++ ++ Object * dragAndDropObject() const; ++ ++private: ++ ++ TrackTile * m_dragAndDropSourceTile = nullptr; ++ ++ Object * m_dragAndDropObject = nullptr; ++ ++ TargetNode * m_dragAndDropTargetNode = nullptr; ++ ++ QPointF m_dragAndDropSourcePos; ++}; ++ ++#endif // DRAGANDDROPSTORE_HPP +diff -Nuar a/src/editor/editordata.cpp b/src/editor/editordata.cpp +--- a/src/editor/editordata.cpp 2017-12-30 16:09:21.000000000 +0300 ++++ b/src/editor/editordata.cpp 2018-02-11 08:22:08.000000000 +0300 +@@ -14,9 +14,9 @@ + // along with Dust Racing 2D. If not, see . + + #include "editordata.hpp" +-#include "editorscene.hpp" + #include "editorview.hpp" + #include "mainwindow.hpp" ++#include "mediator.hpp" + #include "object.hpp" + #include "objectmodelloader.hpp" + #include "targetnode.hpp" +@@ -28,10 +28,8 @@ + + using std::dynamic_pointer_cast; + +-EditorData::EditorData(MainWindow * mainWindow) +-: m_mode(EditorMode::None) +-, m_dragAndDropSourcePos() +-, m_mainWindow(mainWindow) ++EditorData::EditorData(Mediator & mediator) ++ : m_mediator(mediator) + {} + + void EditorData::clearScene() +@@ -41,6 +39,11 @@ + removeTargetNodesFromScene(); + } + ++DragAndDropStore & EditorData::dadStore() ++{ ++ return m_dadStore; ++} ++ + bool EditorData::loadTrackData(QString fileName) + { + m_undoStack.clear(); +@@ -60,16 +63,12 @@ + { + if (m_undoStack.isUndoable()) + { +- m_dragAndDropSourceTile = nullptr; +- +- m_dragAndDropObject = nullptr; ++ m_dadStore.clear(); + + m_selectedObject = nullptr; + + m_selectedTargetNode = nullptr; + +- m_dragAndDropTargetNode = nullptr; +- + saveRedoPoint(); + + clearScene(); +@@ -87,16 +86,12 @@ + { + if (m_undoStack.isRedoable()) + { +- m_dragAndDropSourceTile = nullptr; +- +- m_dragAndDropObject = nullptr; ++ m_dadStore.clear(); + + m_selectedObject = nullptr; + + m_selectedTargetNode = nullptr; + +- m_dragAndDropTargetNode = nullptr; +- + saveUndoPoint(); + + clearScene(); +@@ -116,7 +111,7 @@ + assert(m_trackData); + m_undoStack.pushUndoPoint(m_trackData); + +- m_mainWindow->enableUndo(m_undoStack.isUndoable()); ++ m_mediator.enableUndo(m_undoStack.isUndoable()); + } + + void EditorData::saveRedoPoint() +@@ -151,17 +146,11 @@ + void EditorData::beginSetRoute() + { + assert(m_trackData); +- setMode(EditorMode::SetRoute); ++ m_mediator.setMode(EditorMode::SetRoute); + removeRouteFromScene(); + m_trackData->route().clear(); + } + +-void EditorData::endSetRoute() +-{ +- // Reset the editing mode. +- setMode(EditorMode::None); +-} +- + void EditorData::addExistingRouteToScene() + { + // Re-push existing (loaded) target nodes so that route lines +@@ -204,8 +193,8 @@ + auto routeLine = new QGraphicsLineItem; + node->setRouteLine(routeLine); + +- m_mainWindow->editorScene().addItem(node.get()); // The scene wants a raw pointer +- m_mainWindow->editorScene().addItem(routeLine); ++ m_mediator.addItem(node.get()); // The scene wants a raw pointer ++ m_mediator.addItem(routeLine); + + const int routeLineZ = 10; + node->setZValue(routeLineZ); +@@ -214,8 +203,7 @@ + // Check if we might have a loop => end + if (loopClosed) + { +- setMode(EditorMode::None); +- m_mainWindow->endSetRoute(); ++ m_mediator.endSetRoute(); + + auto firstNode = route.get(0); + route.get(route.numNodes() - 1)->setLocation(firstNode->location()); +@@ -232,13 +220,13 @@ + auto node = dynamic_pointer_cast(tnode); + assert(node); + +- m_mainWindow->editorScene().removeItem(node.get()); // The scene wants a raw pointer +- m_mainWindow->editorScene().removeItem(node->routeLine()); ++ m_mediator.removeItem(node.get()); // The scene wants a raw pointer ++ m_mediator.removeItem(node->routeLine()); + + delete node->routeLine(); + } + +- m_mainWindow->editorView().update(); ++ m_mediator.updateView(); + } + + TrackDataPtr EditorData::trackData() +@@ -246,36 +234,6 @@ + return m_trackData; + } + +-EditorData::EditorMode EditorData::mode() const +-{ +- return m_mode; +-} +- +-void EditorData::setMode(EditorData::EditorMode newMode) +-{ +- m_mode = newMode; +-} +- +-void EditorData::setDragAndDropSourceTile(TrackTile * tile) +-{ +- m_dragAndDropSourceTile = tile; +-} +- +-TrackTile * EditorData::dragAndDropSourceTile() const +-{ +- return m_dragAndDropSourceTile; +-} +- +-void EditorData::setDragAndDropObject(Object * object) +-{ +- m_dragAndDropObject = object; +-} +- +-Object * EditorData::dragAndDropObject() const +-{ +- return m_dragAndDropObject; +-} +- + void EditorData::setSelectedObject(Object * object) + { + m_selectedObject = object; +@@ -296,26 +254,6 @@ + return m_selectedTargetNode; + } + +-void EditorData::setDragAndDropTargetNode(TargetNode * tnode) +-{ +- m_dragAndDropTargetNode = tnode; +-} +- +-TargetNode * EditorData::dragAndDropTargetNode() const +-{ +- return m_dragAndDropTargetNode; +-} +- +-void EditorData::setDragAndDropSourcePos(QPointF pos) +-{ +- m_dragAndDropSourcePos = pos; +-} +- +-QPointF EditorData::dragAndDropSourcePos() const +-{ +- return m_dragAndDropSourcePos; +-} +- + void EditorData::addTilesToScene() + { + assert(m_trackData); +@@ -331,7 +269,7 @@ + + if (!tile->added()) + { +- m_mainWindow->editorScene().addItem(tile.get()); // The scene wants a raw pointer ++ m_mediator.addItem(tile.get()); // The scene wants a raw pointer + + tile->setAdded(true); + } +@@ -351,7 +289,7 @@ + auto object = dynamic_pointer_cast(m_trackData->objects().object(i)); + assert(object); + +- m_mainWindow->editorScene().addItem(object.get()); // The scene wants a raw pointer ++ m_mediator.addItem(object.get()); // The scene wants a raw pointer + + object->setZValue(10); + } +@@ -364,7 +302,7 @@ + auto tile = dynamic_pointer_cast(trackTile); + assert(tile); + +- m_mainWindow->editorScene().removeItem(tile.get()); // The scene wants a raw pointer ++ m_mediator.removeItem(tile.get()); // The scene wants a raw pointer + } + + void EditorData::removeTilesFromScene() +@@ -380,7 +318,7 @@ + auto tile = dynamic_pointer_cast(m_trackData->map().getTile(i, j)); + assert(tile); + +- m_mainWindow->editorScene().removeItem(tile.get()); // The scene wants a raw pointer ++ m_mediator.removeItem(tile.get()); // The scene wants a raw pointer + } + } + } +@@ -395,12 +333,13 @@ + auto object = dynamic_pointer_cast(m_trackData->objects().object(i)); + assert(object); + +- m_mainWindow->editorScene().removeItem(object.get()); // The scene wants a raw pointer ++ m_mediator.removeItem(object.get()); // The scene wants a raw pointer + } + } + + m_selectedObject = nullptr; +- m_dragAndDropObject = nullptr; ++ ++ m_dadStore.clear(); + } + + void EditorData::removeTargetNodesFromScene() +@@ -412,11 +351,21 @@ + auto node = dynamic_pointer_cast(tnode); + assert(node); + +- m_mainWindow->editorScene().removeItem(node.get()); // The scene wants a raw pointer ++ m_mediator.removeItem(node.get()); // The scene wants a raw pointer + } + } + } + ++int EditorData::currentScale() const ++{ ++ return m_currentScale; ++} ++ ++void EditorData::setCurrentScale(int currentScale) ++{ ++ m_currentScale = currentScale; ++} ++ + void EditorData::clearRoute() + { + assert(m_trackData); +@@ -424,7 +373,7 @@ + removeRouteFromScene(); + m_trackData->route().clear(); + +- m_mainWindow->console(QString(QObject::tr("Route cleared."))); ++ m_mediator.console(QString(QObject::tr("Route cleared."))); + } + + void EditorData::setActiveColumn(unsigned int column) +diff -Nuar a/src/editor/editordata.hpp b/src/editor/editordata.hpp +--- a/src/editor/editordata.hpp 2017-12-30 16:09:21.000000000 +0300 ++++ b/src/editor/editordata.hpp 2018-02-11 08:22:08.000000000 +0300 +@@ -22,11 +22,13 @@ + #include + #include + ++#include "draganddropstore.hpp" ++#include "editormode.hpp" + #include "trackdata.hpp" + #include "trackio.hpp" + #include "undostack.hpp" + +-class MainWindow; ++class Mediator; + class Object; + class ObjectModelLoader; + class TargetNode; +@@ -40,19 +42,10 @@ + { + public: + +- //! Editing modes. +- enum class EditorMode +- { +- None, +- SetRoute, +- SetTileType, +- AddObject, +- EraseObject +- }; +- + //! Constructor. +- //! \param mainWindow Pointer to the main window. +- EditorData(MainWindow * mainWindow); ++ explicit EditorData(Mediator & mediator); ++ ++ DragAndDropStore & dadStore(); + + //! Load track from the given file. + bool loadTrackData(QString fileName); +@@ -89,10 +82,6 @@ + //! setting the route. + void beginSetRoute(); + +- //! This should be called after user ends +- //! setting the route. +- void endSetRoute(); +- + //! Adds current target node objects to the scene. + void addExistingRouteToScene(); + +@@ -108,24 +97,6 @@ + //! Returns current track data object. Returns NULL if not set. + TrackDataPtr trackData(); + +- //! Returns current editing mode. +- EditorMode mode() const; +- +- //! Sets the current editing mode. +- void setMode(EditorMode newMode); +- +- //! Set tile that is being drag'n'dropped. +- void setDragAndDropSourceTile(TrackTile * tile); +- +- //! Returns the tile being drag'n'dropped or nullptr. +- TrackTile * dragAndDropSourceTile() const; +- +- //! Set object that is being drag'n'dropped. +- void setDragAndDropObject(Object * object); +- +- //! Returns the object being drag'n'dropped or nullptr. +- Object * dragAndDropObject() const; +- + //! Set selected object. + void setSelectedObject(Object * object); + +@@ -138,18 +109,6 @@ + //! Returns the selected target node or nullptr. + TargetNode * selectedTargetNode() const; + +- //! Set target node that is being drag'n'dropped. +- void setDragAndDropTargetNode(TargetNode * tnode); +- +- //! Returns the target node being drag'n'dropped or nullptr. +- TargetNode * dragAndDropTargetNode() const; +- +- //! Get source pos of the drag'n'drop. +- QPointF dragAndDropSourcePos() const; +- +- //! Set source pos of the drag'n'drop. +- void setDragAndDropSourcePos(QPointF pos); +- + //! Add tiles in current track data object to the scene. + void addTilesToScene(); + +@@ -167,6 +126,10 @@ + + unsigned int activeRow() const; + ++ void setCurrentScale(int currentScale); ++ ++ int currentScale() const; ++ + private: + + EditorData(const EditorData & e); +@@ -182,33 +145,28 @@ + + void removeTargetNodesFromScene(); + ++ DragAndDropStore m_dadStore; ++ + TrackIO m_trackIO; + + TrackDataPtr m_trackData; + + UndoStack m_undoStack; + +- EditorMode m_mode; +- +- TrackTile * m_dragAndDropSourceTile = nullptr; +- +- Object * m_dragAndDropObject = nullptr; +- + Object * m_selectedObject = nullptr; + + TargetNode * m_selectedTargetNode = nullptr; + +- TargetNode * m_dragAndDropTargetNode = nullptr; +- +- QPointF m_dragAndDropSourcePos; +- +- MainWindow * m_mainWindow = nullptr; ++ Mediator & m_mediator; + + std::vector m_targetNodes; + + unsigned int m_activeColumn = 0; + + unsigned int m_activeRow = 0; ++ ++ // Scale percentage ++ int m_currentScale = 50; + }; + + #endif // EDITORDATA_HPP +diff -Nuar a/src/editor/editormode.hpp b/src/editor/editormode.hpp +--- a/src/editor/editormode.hpp 1970-01-01 02:00:00.000000000 +0200 ++++ b/src/editor/editormode.hpp 2018-02-11 08:22:08.000000000 +0300 +@@ -0,0 +1,29 @@ ++// This file is part of Dust Racing 2D. ++// Copyright (C) 2018 Jussi Lind ++// ++// Dust Racing 2D is free software: you can redistribute it and/or modify ++// it under the terms of the GNU General Public License as published by ++// the Free Software Foundation, either version 3 of the License, or ++// (at your option) any later version. ++// Dust Racing 2D is distributed in the hope that it will be useful, ++// but WITHOUT ANY WARRANTY; without even the implied warranty of ++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++// GNU General Public License for more details. ++// ++// You should have received a copy of the GNU General Public License ++// along with Dust Racing 2D. If not, see . ++ ++#ifndef EDITORMODE_HPP ++#define EDITORMODE_HPP ++ ++//! Editing modes. ++enum class EditorMode ++{ ++ None, ++ SetRoute, ++ SetTileType, ++ AddObject, ++ EraseObject ++}; ++ ++#endif // EDITORMODE_HPP +diff -Nuar a/src/editor/editor.pro b/src/editor/editor.pro +--- a/src/editor/editor.pro 2017-12-30 16:09:21.000000000 +0300 ++++ b/src/editor/editor.pro 2018-02-11 08:22:08.000000000 +0300 +@@ -29,11 +29,14 @@ + ../common/userexception.hpp \ + aboutdlg.hpp \ + application.hpp \ ++ draganddropstore.hpp \ + editordata.hpp \ +- editorscene.hpp \ + editorview.hpp \ ++ editormode.hpp \ ++ floodfill.hpp \ + mainwindow.hpp \ + map.hpp \ ++ mediator.hpp \ + newtrackdialog.hpp \ + object.hpp \ + objectfactory.hpp \ +@@ -60,12 +63,14 @@ + ../common/tracktilebase.cpp \ + aboutdlg.cpp \ + application.cpp \ ++ draganddropstore.cpp \ + editordata.cpp \ +- editorscene.cpp \ + editorview.cpp \ ++ floodfill.cpp \ + main.cpp \ + mainwindow.cpp \ + map.cpp \ ++ mediator.cpp \ + newtrackdialog.cpp \ + object.cpp \ + objectfactory.cpp \ +diff -Nuar a/src/editor/editorscene.cpp b/src/editor/editorscene.cpp +--- a/src/editor/editorscene.cpp 2017-12-30 16:09:21.000000000 +0300 ++++ b/src/editor/editorscene.cpp 1970-01-01 02:00:00.000000000 +0200 +@@ -1,20 +0,0 @@ +-// This file is part of Dust Racing 2D. +-// Copyright (C) 2011 Jussi Lind +-// +-// Dust Racing 2D is free software: you can redistribute it and/or modify +-// it under the terms of the GNU General Public License as published by +-// the Free Software Foundation, either version 3 of the License, or +-// (at your option) any later version. +-// Dust Racing 2D is distributed in the hope that it will be useful, +-// but WITHOUT ANY WARRANTY; without even the implied warranty of +-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-// GNU General Public License for more details. +-// +-// You should have received a copy of the GNU General Public License +-// along with Dust Racing 2D. If not, see . +- +-#include "editorscene.hpp" +- +-EditorScene::EditorScene(QObject * parent) : +- QGraphicsScene(parent) +-{} +diff -Nuar a/src/editor/editorscene.hpp b/src/editor/editorscene.hpp +--- a/src/editor/editorscene.hpp 2017-12-30 16:09:21.000000000 +0300 ++++ b/src/editor/editorscene.hpp 1970-01-01 02:00:00.000000000 +0200 +@@ -1,32 +0,0 @@ +-// This file is part of Dust Racing 2D. +-// Copyright (C) 2011 Jussi Lind +-// +-// Dust Racing 2D is free software: you can redistribute it and/or modify +-// it under the terms of the GNU General Public License as published by +-// the Free Software Foundation, either version 3 of the License, or +-// (at your option) any later version. +-// Dust Racing 2D is distributed in the hope that it will be useful, +-// but WITHOUT ANY WARRANTY; without even the implied warranty of +-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-// GNU General Public License for more details. +-// +-// You should have received a copy of the GNU General Public License +-// along with Dust Racing 2D. If not, see . +- +-#ifndef EDITORSCENE_HPP +-#define EDITORSCENE_HPP +- +-#include +- +-//! Graphics scene of the editor. +-class EditorScene : public QGraphicsScene +-{ +- Q_OBJECT +- +-public: +- +- //! Constructor. +- explicit EditorScene(QObject * parent = nullptr); +-}; +- +-#endif // EDITORSCENE_HPP +diff -Nuar a/src/editor/editorview.cpp b/src/editor/editorview.cpp +--- a/src/editor/editorview.cpp 2017-12-30 16:09:21.000000000 +0300 ++++ b/src/editor/editorview.cpp 2018-02-11 08:22:08.000000000 +0300 +@@ -20,9 +20,11 @@ + #include + #include + +-#include "editordata.hpp" + #include "editorview.hpp" +-#include "mainwindow.hpp" ++ ++#include "draganddropstore.hpp" ++#include "editormode.hpp" ++#include "mediator.hpp" + #include "object.hpp" + #include "objectfactory.hpp" + #include "objectmodelloader.hpp" +@@ -35,9 +37,8 @@ + #include + #include + +-EditorView::EditorView(EditorData & editorData, QWidget * parent) +-: QGraphicsView(parent) +-, m_editorData(editorData) ++EditorView::EditorView(Mediator & mediator) ++ : m_mediator(mediator) + { + createTileContextMenuActions(); + createObjectContextMenuActions(); +@@ -49,8 +50,8 @@ + const QRectF newSceneRect( + 0, + 0, +- m_editorData.trackData()->map().cols() * TrackTile::TILE_W, +- m_editorData.trackData()->map().rows() * TrackTile::TILE_H); ++ m_mediator.cols() * TrackTile::TILE_W, ++ m_mediator.rows() * TrackTile::TILE_H); + + setSceneRect(newSceneRect); + +@@ -69,45 +70,22 @@ + } + + // Tile drag'n'drop active? +- if (auto sourceTile = m_editorData.dragAndDropSourceTile()) ++ if (auto sourceTile = m_mediator.dadStore().dragAndDropSourceTile()) + { + sourceTile->setPos(mappedPos); + } + // Object drag'n'drop active? +- else if (auto object = m_editorData.dragAndDropObject()) ++ else if (auto object = m_mediator.dadStore().dragAndDropObject()) + { + object->setLocation(mappedPos); + } + // Target node drag'n'drop active? +- else if (auto tnode = m_editorData.dragAndDropTargetNode()) ++ else if (auto tnode = m_mediator.dadStore().dragAndDropTargetNode()) + { + tnode->setLocation(mappedPos); + } + +- updateCoordinates(mappedPos); +- } +-} +- +-void EditorView::updateCoordinates(QPointF mappedPos) +-{ +- if (m_editorData.trackData()) +- { +- const int maxCols = static_cast(m_editorData.trackData()->map().cols()); +- int column = mappedPos.x() / TrackTile::TILE_W; +- column = column >= maxCols ? maxCols - 1 : column; +- column = column < 0 ? 0 : column; +- +- const int maxRows = static_cast(m_editorData.trackData()->map().rows()); +- int row = mappedPos.y() / TrackTile::TILE_H; +- row = row >= maxRows ? maxRows - 1 : row; +- row = row < 0 ? 0 : row; +- +- m_editorData.setActiveRow(static_cast(row)); +- m_editorData.setActiveColumn(static_cast(column)); +- +- QString coordinates("X: %1 Y: %2 I: %3 J: %4"); +- coordinates = coordinates.arg(mappedPos.x()).arg(mappedPos.y()).arg(column).arg(row); +- MainWindow::instance()->statusBar()->showMessage(coordinates); ++ m_mediator.updateCoordinates(mappedPos); + } + } + +@@ -121,7 +99,7 @@ + if (TrackTile * tile = + dynamic_cast(scene()->itemAt(mapToScene(m_clickedPos), QTransform()))) + { +- m_editorData.saveUndoPoint(); ++ m_mediator.saveUndoPoint(); + tile->rotate90CW(); + } + }); +@@ -133,28 +111,28 @@ + if (auto tile = + dynamic_cast(scene()->itemAt(mapToScene(m_clickedPos), QTransform()))) + { +- m_editorData.saveUndoPoint(); ++ m_mediator.saveUndoPoint(); + tile->rotate90CCW(); + } + }); + + m_clearComputerHint = new QAction(QWidget::tr("Clear computer hint"), &m_tileContextMenu); + QObject::connect(m_clearComputerHint, &QAction::triggered, [this] () { +- m_editorData.saveUndoPoint(); ++ m_mediator.saveUndoPoint(); + setComputerHint(TrackTileBase::CH_NONE); + }); + + m_setComputerHintBrakeHard = new QAction( + QWidget::tr("Set computer hint 'brake hard'.."), &m_tileContextMenu); + QObject::connect(m_setComputerHintBrakeHard, &QAction::triggered, [this] () { +- m_editorData.saveUndoPoint(); ++ m_mediator.saveUndoPoint(); + setComputerHint(TrackTileBase::CH_BRAKE_HARD); + }); + + m_setComputerHintBrake = new QAction( + QWidget::tr("Set computer hint 'brake'.."), &m_tileContextMenu); + QObject::connect(m_setComputerHintBrake, &QAction::triggered, [this] () { +- m_editorData.saveUndoPoint(); ++ m_mediator.saveUndoPoint(); + setComputerHint(TrackTileBase::CH_BRAKE); + }); + +@@ -164,7 +142,7 @@ + QObject::connect(m_excludeFromMinimap, &QAction::changed, [this] () { + if (auto tile = dynamic_cast(scene()->itemAt(mapToScene(this->m_clickedPos), QTransform()))) + { +- m_editorData.saveUndoPoint(); ++ m_mediator.saveUndoPoint(); + tile->setExcludeFromMinimap(this->m_excludeFromMinimap->isChecked()); + } + }); +@@ -172,9 +150,8 @@ + auto insertRowBefore = new QAction( + QWidget::tr("Insert row before.."), &m_tileContextMenu); + QObject::connect(insertRowBefore, &QAction::triggered, [this] () { +- m_editorData.saveUndoPoint(); +- m_editorData.trackData()->insertRow(m_editorData.activeRow(), MapBase::InsertDirection::Before); +- m_editorData.addTilesToScene(); ++ m_mediator.saveUndoPoint(); ++ m_mediator.insertRowBefore(); + updateSceneRect(); + update(); + }); +@@ -182,9 +159,8 @@ + auto insertRowAfter = new QAction( + QWidget::tr("Insert row after.."), &m_tileContextMenu); + QObject::connect(insertRowAfter, &QAction::triggered, [this] () { +- m_editorData.saveUndoPoint(); +- m_editorData.trackData()->insertRow(m_editorData.activeRow(), MapBase::InsertDirection::After); +- m_editorData.addTilesToScene(); ++ m_mediator.saveUndoPoint(); ++ m_mediator.insertRowAfter(); + updateSceneRect(); + update(); + }); +@@ -192,11 +168,8 @@ + m_deleteRow = new QAction( + QWidget::tr("Delete row.."), &m_tileContextMenu); + QObject::connect(m_deleteRow, &QAction::triggered, [this] () { +- m_editorData.saveUndoPoint(); +- auto deleted = m_editorData.trackData()->deleteRow(m_editorData.activeRow()); +- for (auto && tile : deleted) { +- m_editorData.removeTileFromScene(tile); +- } ++ m_mediator.saveUndoPoint(); ++ m_mediator.deleteRow(); + updateSceneRect(); + update(); + }); +@@ -204,9 +177,8 @@ + auto insertColBefore = new QAction( + QWidget::tr("Insert column before.."), &m_tileContextMenu); + QObject::connect(insertColBefore, &QAction::triggered, [this] () { +- m_editorData.saveUndoPoint(); +- m_editorData.trackData()->insertColumn(m_editorData.activeColumn(), MapBase::InsertDirection::Before); +- m_editorData.addTilesToScene(); ++ m_mediator.saveUndoPoint(); ++ m_mediator.insertColumnBefore(); + updateSceneRect(); + update(); + }); +@@ -214,9 +186,8 @@ + auto insertColAfter = new QAction( + QWidget::tr("Insert column after.."), &m_tileContextMenu); + QObject::connect(insertColAfter, &QAction::triggered, [this] () { +- m_editorData.saveUndoPoint(); +- m_editorData.trackData()->insertColumn(m_editorData.activeColumn(), MapBase::InsertDirection::After); +- m_editorData.addTilesToScene(); ++ m_mediator.saveUndoPoint(); ++ m_mediator.insertColumnAfter(); + updateSceneRect(); + update(); + }); +@@ -224,11 +195,8 @@ + m_deleteCol = new QAction( + QWidget::tr("Delete column.."), &m_tileContextMenu); + QObject::connect(m_deleteCol, &QAction::triggered, [this] () { +- m_editorData.saveUndoPoint(); +- auto deleted = m_editorData.trackData()->deleteColumn(m_editorData.activeColumn()); +- for (auto && tile : deleted) { +- m_editorData.removeTileFromScene(tile); +- } ++ m_mediator.saveUndoPoint(); ++ m_mediator.deleteColumn(); + updateSceneRect(); + update(); + }); +@@ -260,9 +228,9 @@ + RotateDialog dialog; + if (dialog.exec() == QDialog::Accepted) + { +- if (auto object = m_editorData.selectedObject()) ++ if (auto object = m_mediator.selectedObject()) + { +- m_editorData.saveUndoPoint(); ++ m_mediator.saveUndoPoint(); + + object->setRotation(static_cast(dialog.angle() + object->rotation()) % 360); + } +@@ -273,9 +241,9 @@ + m_forceStationaryAction = new QAction(dummy2, &m_tileContextMenu); + m_forceStationaryAction->setCheckable(true); + QObject::connect(m_forceStationaryAction, &QAction::changed, [this] () { +- if (auto object = m_editorData.selectedObject()) ++ if (auto object = m_mediator.selectedObject()) + { +- m_editorData.saveUndoPoint(); ++ m_mediator.saveUndoPoint(); + object->setForceStationary(m_forceStationaryAction->isChecked()); + } + }); +@@ -291,12 +259,12 @@ + const QString dummy1(QString(QWidget::tr("Set size.."))); + auto setSize = new QAction(dummy1, &m_targetNodeContextMenu); + QObject::connect(setSize, &QAction::triggered, [this] () { +- if (auto tnode = m_editorData.selectedTargetNode()) ++ if (auto tnode = m_mediator.selectedTargetNode()) + { + TargetNodeSizeDlg dialog(tnode->size()); + if (dialog.exec() == QDialog::Accepted) + { +- m_editorData.saveUndoPoint(); ++ m_mediator.saveUndoPoint(); + + tnode->setSize(dialog.targetNodeSize()); + } +@@ -311,12 +279,11 @@ + { + if (scene()) + { +- m_clickedPos = event->pos(); ++ m_clickedPos = event->pos(); + m_clickedScenePos = mapToScene(m_clickedPos); + + // Fetch all items at the location +- QList items = scene()->items( +- m_clickedScenePos, Qt::IntersectsItemShape, Qt::DescendingOrder); ++ QList items = scene()->items(m_clickedScenePos, Qt::IntersectsItemShape, Qt::DescendingOrder); + + if (items.size()) + { +@@ -351,7 +318,7 @@ + + void EditorView::eraseObjectAtCurrentClickedPos() + { +- m_editorData.saveUndoPoint(); ++ m_mediator.saveUndoPoint(); + + // Fetch all items at the location + QList items = scene()->items( +@@ -363,9 +330,8 @@ + { + if (auto object = dynamic_cast(item)) + { +- scene()->removeItem(object); +- m_editorData.trackData()->objects().remove(*object); +- m_editorData.setSelectedObject(nullptr); ++ m_mediator.removeObject(*object); ++ m_mediator.setSelectedObject(nullptr); + break; + } + } +@@ -373,26 +339,7 @@ + + void EditorView::addCurrentToolBarObjectToScene() + { +- if (QAction * action = MainWindow::instance()->currentToolBarAction()) +- { +- if (scene()) +- { +- m_editorData.saveUndoPoint(); +- +- Object & object = ObjectFactory::createObject(action->data().toString()); +- object.setLocation(m_clickedScenePos); +- +- if (MainWindow::instance()->randomlyRotateObjects()) +- { +- object.setRotation(std::rand() % 360); +- } +- +- scene()->addItem(&object); +- +- m_editorData.trackData()->objects().add(ObjectBasePtr(&object)); +- m_editorData.setSelectedObject(&object); +- } +- } ++ m_mediator.addCurrentToolBarObjectToScene(m_clickedScenePos); + } + + void EditorView::handleMousePressEventOnObject(QMouseEvent & event, Object & object) +@@ -404,12 +351,12 @@ + else if (event.button() == Qt::LeftButton) + { + // User is adding an object +- if (m_editorData.mode() == EditorData::EditorMode::AddObject) ++ if (m_mediator.mode() == EditorMode::AddObject) + { + addCurrentToolBarObjectToScene(); + } + // User is erasing an object +- else if (m_editorData.mode() == EditorData::EditorMode::EraseObject) ++ else if (m_mediator.mode() == EditorMode::EraseObject) + { + eraseObjectAtCurrentClickedPos(); + } +@@ -432,7 +379,7 @@ + else if (event.button() == Qt::LeftButton) + { + // User is adding an object +- if (m_editorData.mode() == EditorData::EditorMode::AddObject) ++ if (m_mediator.mode() == EditorMode::AddObject) + { + addCurrentToolBarObjectToScene(); + } +@@ -456,7 +403,7 @@ + else if (event.button() == Qt::LeftButton) + { + // User is adding an object +- if (m_editorData.mode() == EditorData::EditorMode::AddObject) ++ if (m_mediator.mode() == EditorMode::AddObject) + { + addCurrentToolBarObjectToScene(); + } +@@ -472,13 +419,13 @@ + void EditorView::handleLeftButtonClickOnObject(Object & object) + { + // User is initiating a drag'n'drop +- if (m_editorData.mode() == EditorData::EditorMode::None) ++ if (m_mediator.mode() == EditorMode::None) + { +- m_editorData.saveUndoPoint(); ++ m_mediator.saveUndoPoint(); + + object.setZValue(object.zValue() + 1); +- m_editorData.setDragAndDropObject(&object); +- m_editorData.setSelectedObject(&object); ++ m_mediator.dadStore().setDragAndDropObject(&object); ++ m_mediator.setSelectedObject(&object); + + // Change cursor to the closed hand cursor. + QApplication::setOverrideCursor(QCursor(Qt::ClosedHandCursor)); +@@ -488,34 +435,34 @@ + void EditorView::handleLeftButtonClickOnTargetNode(TargetNode & tnode) + { + // User is initiating a drag'n'drop +- if (m_editorData.mode() == EditorData::EditorMode::None) ++ if (m_mediator.mode() == EditorMode::None) + { +- m_editorData.saveUndoPoint(); ++ m_mediator.saveUndoPoint(); + + tnode.setZValue(tnode.zValue() + 1); +- m_editorData.setDragAndDropTargetNode(&tnode); ++ m_mediator.dadStore().setDragAndDropTargetNode(&tnode); + + // Change cursor to the closed hand cursor. + QApplication::setOverrideCursor(QCursor(Qt::ClosedHandCursor)); + } + // It's not possible to make nodes overlap if not handled also here. +- else if (m_editorData.mode() == EditorData::EditorMode::SetRoute) ++ else if (m_mediator.mode() == EditorMode::SetRoute) + { +- m_editorData.pushNewTargetNodeToRoute(m_clickedScenePos); ++ m_mediator.pushNewTargetNodeToRoute(m_clickedScenePos); + } + } + + void EditorView::handleLeftButtonClickOnTile(TrackTile & tile) + { + // User is defining the route +- if (m_editorData.mode() == EditorData::EditorMode::SetRoute) ++ if (m_mediator.mode() == EditorMode::SetRoute) + { +- m_editorData.pushNewTargetNodeToRoute(m_clickedScenePos); ++ m_mediator.pushNewTargetNodeToRoute(m_clickedScenePos); + } + // User is setting the tile type +- else if (m_editorData.mode() == EditorData::EditorMode::SetTileType) ++ else if (m_mediator.mode() == EditorMode::SetTileType) + { +- if (QAction * action = MainWindow::instance()->currentToolBarAction()) ++ if (QAction * action = m_mediator.currentToolBarAction()) + { + Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers(); + +@@ -525,7 +472,7 @@ + + if (typeToFill != action->data().toString()) + { +- doFloodFill(tile, action, typeToFill); ++ m_mediator.floodFill(tile, action, typeToFill); + } + } + else +@@ -535,11 +482,11 @@ + } + } + // User is initiating a drag'n'drop +- else if (m_editorData.mode() == EditorData::EditorMode::None) ++ else if (m_mediator.mode() == EditorMode::None) + { + tile.setZValue(tile.zValue() + 1); +- m_editorData.setDragAndDropSourceTile(&tile); +- m_editorData.setDragAndDropSourcePos(tile.pos()); ++ m_mediator.dadStore().setDragAndDropSourceTile(&tile); ++ m_mediator.dadStore().setDragAndDropSourcePos(tile.pos()); + + // Change cursor to the closed hand cursor. + QApplication::setOverrideCursor(QCursor(Qt::ClosedHandCursor)); +@@ -570,11 +517,8 @@ + + m_excludeFromMinimap->setChecked(tile.excludeFromMinimap()); + +- if (m_editorData.trackData()) +- { +- m_deleteCol->setEnabled(m_editorData.trackData()->map().cols() > 2); +- m_deleteRow->setEnabled(m_editorData.trackData()->map().rows() > 2); +- } ++ m_deleteCol->setEnabled(m_mediator.cols()); ++ m_deleteRow->setEnabled(m_mediator.rows()); + + const QPoint globalPos = mapToGlobal(m_clickedPos); + m_tileContextMenu.exec(globalPos); +@@ -593,7 +537,7 @@ + + void EditorView::handleRightButtonClickOnObject(Object & object) + { +- m_editorData.setSelectedObject(&object); ++ m_mediator.setSelectedObject(&object); + + m_forceStationaryAction->setChecked(object.forceStationary()); + +@@ -608,7 +552,7 @@ + + void EditorView::handleRightButtonClickOnTargetNode(TargetNode & tnode) + { +- m_editorData.setSelectedTargetNode(&tnode); ++ m_mediator.setSelectedTargetNode(&tnode); + + openTargetNodeContextMenu(); + } +@@ -622,26 +566,26 @@ + + void EditorView::keyPressEvent(QKeyEvent * event) + { +- if (auto object = m_editorData.selectedObject()) ++ if (auto object = m_mediator.selectedObject()) + { + if (!event->isAutoRepeat()) + { + switch (event->key()) + { + case Qt::Key_Left: +- m_editorData.saveUndoPoint(); ++ m_mediator.saveUndoPoint(); + object->setLocation(QPointF(object->location().x() - 1, object->location().y())); + break; + case Qt::Key_Right: +- m_editorData.saveUndoPoint(); ++ m_mediator.saveUndoPoint(); + object->setLocation(QPointF(object->location().x() + 1, object->location().y())); + break; + case Qt::Key_Up: +- m_editorData.saveUndoPoint(); ++ m_mediator.saveUndoPoint(); + object->setLocation(QPointF(object->location().x(), object->location().y() - 1)); + break; + case Qt::Key_Down: +- m_editorData.saveUndoPoint(); ++ m_mediator.saveUndoPoint(); + object->setLocation(QPointF(object->location().x(), object->location().y() + 1)); + break; + default: +@@ -653,81 +597,72 @@ + + void EditorView::handleTileDragRelease(QMouseEvent * event) + { +- if (scene()) ++ // Tile drag'n'drop active? ++ if (auto sourceTile = m_mediator.dadStore().dragAndDropSourceTile()) + { +- // Tile drag'n'drop active? +- if (auto sourceTile = m_editorData.dragAndDropSourceTile()) ++ // Determine the dest tile ++ auto destTile = sourceTile; ++ QList items = scene()->items(mapToScene(event->pos())); ++ for (QGraphicsItem * item : items) + { +- // Determine the dest tile +- auto destTile = sourceTile; +- QList items = scene()->items(mapToScene(event->pos())); +- for (QGraphicsItem * item : items) ++ auto testTile = dynamic_cast(item); ++ if (testTile && testTile != sourceTile) + { +- auto testTile = dynamic_cast(item); +- if (testTile && testTile != sourceTile) +- { +- destTile = testTile; +- break; +- } ++ destTile = testTile; ++ break; + } ++ } + +- // Swap tiles +- sourceTile->swap(*destTile); ++ // Swap tiles ++ sourceTile->swap(*destTile); + +- // Restore position +- sourceTile->setPos(m_editorData.dragAndDropSourcePos()); +- sourceTile->setZValue(sourceTile->zValue() - 1); +- destTile->setZValue(sourceTile->zValue()); ++ // Restore position ++ sourceTile->setPos(m_mediator.dadStore().dragAndDropSourcePos()); ++ sourceTile->setZValue(sourceTile->zValue() - 1); ++ destTile->setZValue(sourceTile->zValue()); + +- update(); ++ update(); + +- m_editorData.setDragAndDropSourceTile(nullptr); ++ m_mediator.dadStore().clear(); + +- // Restore the cursor. +- QApplication::restoreOverrideCursor(); +- } ++ // Restore the cursor. ++ QApplication::restoreOverrideCursor(); + } + } + + void EditorView::handleObjectDragRelease(QMouseEvent * event) + { +- if (scene()) ++ // Object drag'n'drop active? ++ if (auto object = m_mediator.dadStore().dragAndDropObject()) + { +- // Object drag'n'drop active? +- if (auto object = m_editorData.dragAndDropObject()) +- { +- // Set the new position position +- object->setLocation(mapToScene(event->pos())); +- object->setZValue(object->zValue() - 1); ++ // Set the new position position ++ object->setLocation(mapToScene(event->pos())); ++ object->setZValue(object->zValue() - 1); + +- update(); ++ update(); + +- m_editorData.setDragAndDropObject(nullptr); ++ m_mediator.dadStore().clear(); + +- // Restore the cursor. +- QApplication::restoreOverrideCursor(); +- } ++ // Restore the cursor. ++ QApplication::restoreOverrideCursor(); + } + } + + void EditorView::handleTargetNodeDragRelease(QMouseEvent * event) + { +- if (scene()) ++ // Target node drag'n'drop active? ++ if (auto tnode = m_mediator.dadStore().dragAndDropTargetNode()) + { +- // Target node drag'n'drop active? +- if (auto tnode = m_editorData.dragAndDropTargetNode()) +- { +- // Set the new position position +- tnode->setLocation(mapToScene(event->pos())); +- tnode->setZValue(tnode->zValue() - 1); ++ // Set the new position position ++ tnode->setLocation(mapToScene(event->pos())); ++ tnode->setZValue(tnode->zValue() - 1); + +- update(); ++ update(); + +- m_editorData.setDragAndDropTargetNode(nullptr); ++ m_mediator.dadStore().clear(); + +- // Restore the cursor. +- QApplication::restoreOverrideCursor(); +- } ++ // Restore the cursor. ++ QApplication::restoreOverrideCursor(); + } + } + +@@ -735,58 +670,14 @@ + { + if (auto tile = dynamic_cast(scene()->itemAt(mapToScene(m_clickedPos), QTransform()))) + { +- m_editorData.saveUndoPoint(); +- ++ m_mediator.saveUndoPoint(); + tile->setComputerHint(hint); + } + } + +-void EditorView::doFloodFill(TrackTile & tile, QAction * action, QString typeToFill) +-{ +- m_editorData.saveUndoPoint(); +- +- floodFill(tile, action, typeToFill); +-} +- +-void EditorView::floodFill(TrackTile & tile, QAction * action, const QString & typeToFill) +-{ +- static const int DIRECTION_COUNT = 4; +- +- // Coordinates of neighbor tiles can be calculated by adding these +- // adjustments to tile coordinates. +- static const QPoint neighborAdjustments[DIRECTION_COUNT] = +- { +- QPoint( 1, 0), // right +- QPoint( 0, -1), // up +- QPoint(-1, 0), // left +- QPoint( 0, 1) // down +- }; +- +- setTileType(tile, action); +- +- MapBase & map = m_editorData.trackData()->map(); +- QPoint location = tile.matrixLocation(); +- +- for (int i = 0; i < DIRECTION_COUNT; ++i) +- { +- int x = location.x() + neighborAdjustments[i].x(); +- int y = location.y() + neighborAdjustments[i].y(); +- +- if (x >= 0 && y >= 0) +- { +- auto tile = std::dynamic_pointer_cast(map.getTile(x, y)); +- if (tile && tile->tileType() == typeToFill) +- { +- floodFill(*(tile.get()), action, typeToFill); +- } +- } +- } +-} +- + void EditorView::changeTileType(TrackTile & tile, QAction * action) + { +- m_editorData.saveUndoPoint(); +- ++ m_mediator.saveUndoPoint(); + setTileType(tile, action); + } + +@@ -795,3 +686,15 @@ + tile.setTileType(action->data().toString()); + tile.setPixmap(action->icon().pixmap(TrackTile::TILE_W, TrackTile::TILE_H)); + } ++ ++void EditorView::wheelEvent(QWheelEvent * event) ++{ ++ if (event->modifiers() & Qt::ControlModifier) ++ { ++ m_mediator.mouseWheelZoom(event->delta()); ++ } ++ else ++ { ++ QGraphicsView::wheelEvent(event); ++ } ++} +diff -Nuar a/src/editor/editorview.hpp b/src/editor/editorview.hpp +--- a/src/editor/editorview.hpp 2017-12-30 16:09:21.000000000 +0300 ++++ b/src/editor/editorview.hpp 2018-02-11 08:22:08.000000000 +0300 +@@ -20,10 +20,10 @@ + #include + #include "../common/tracktilebase.hpp" + ++class Mediator; + class QAction; + class QMouseEvent; + class QPaintEvent; +-class EditorData; + class Object; + class ObjectModelLoaderoader; + class TargetNode; +@@ -35,7 +35,7 @@ + + public: + +- explicit EditorView(EditorData & editorData, QWidget * parent = 0); ++ explicit EditorView(Mediator & mediator); + + void updateSceneRect(); + +@@ -49,6 +49,8 @@ + + void keyPressEvent(QKeyEvent * event) override; + ++ void wheelEvent(QWheelEvent * event) override; ++ + private: + + void addCurrentToolBarObjectToScene(); +@@ -61,12 +63,8 @@ + + void createTargetNodeContextMenuActions(); + +- void doFloodFill(TrackTile & tile, QAction * action, QString typeToFill); +- + void eraseObjectAtCurrentClickedPos(); + +- void floodFill(TrackTile & tile, QAction * action, const QString & typeToFill); +- + void handleMousePressEventOnTile(QMouseEvent & event, TrackTile & tile); + + void handleLeftButtonClickOnTile(TrackTile & tile); +@@ -99,8 +97,6 @@ + + void setComputerHint(TrackTileBase::ComputerHint hint); + +- void updateCoordinates(QPointF mappedPos); +- + static void setTileType(TrackTile & tile, QAction * action); + + QMenu m_tileContextMenu; +@@ -127,7 +123,7 @@ + + QAction * m_forceStationaryAction = nullptr; + +- EditorData & m_editorData; ++ Mediator & m_mediator; + }; + + #endif // EDITORVIEW_HPP +diff -Nuar a/src/editor/floodfill.cpp b/src/editor/floodfill.cpp +--- a/src/editor/floodfill.cpp 1970-01-01 02:00:00.000000000 +0200 ++++ b/src/editor/floodfill.cpp 2018-02-11 08:22:08.000000000 +0300 +@@ -0,0 +1,62 @@ ++// This file is part of Dust Racing 2D. ++// Copyright (C) 2018 Jussi Lind ++// ++// Dust Racing 2D is free software: you can redistribute it and/or modify ++// it under the terms of the GNU General Public License as published by ++// the Free Software Foundation, either version 3 of the License, or ++// (at your option) any later version. ++// Dust Racing 2D is distributed in the hope that it will be useful, ++// but WITHOUT ANY WARRANTY; without even the implied warranty of ++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++// GNU General Public License for more details. ++// ++// You should have received a copy of the GNU General Public License ++// along with Dust Racing 2D. If not, see . ++ ++#include "floodfill.hpp" ++#include "map.hpp" ++#include "tracktile.hpp" ++ ++#include ++ ++namespace FloodFill { ++void setTileType(TrackTile & tile, QAction * action) ++{ ++ tile.setTileType(action->data().toString()); ++ tile.setPixmap(action->icon().pixmap(TrackTile::TILE_W, TrackTile::TILE_H)); ++} ++} ++ ++void FloodFill::floodFill(TrackTile & tile, QAction * action, const QString & typeToFill, MapBase & map) ++{ ++ static const int DIRECTION_COUNT = 4; ++ ++ // Coordinates of neighbor tiles can be calculated by adding these ++ // adjustments to tile coordinates. ++ static const QPoint neighborAdjustments[DIRECTION_COUNT] = ++ { ++ QPoint( 1, 0), // right ++ QPoint( 0, -1), // up ++ QPoint(-1, 0), // left ++ QPoint( 0, 1) // down ++ }; ++ ++ setTileType(tile, action); ++ ++ QPoint location = tile.matrixLocation(); ++ ++ for (int i = 0; i < DIRECTION_COUNT; ++i) ++ { ++ int x = location.x() + neighborAdjustments[i].x(); ++ int y = location.y() + neighborAdjustments[i].y(); ++ ++ if (x >= 0 && y >= 0) ++ { ++ auto tile = std::dynamic_pointer_cast(map.getTile(x, y)); ++ if (tile && tile->tileType() == typeToFill) ++ { ++ FloodFill::floodFill(*(tile.get()), action, typeToFill, map); ++ } ++ } ++ } ++} +diff -Nuar a/src/editor/floodfill.hpp b/src/editor/floodfill.hpp +--- a/src/editor/floodfill.hpp 1970-01-01 02:00:00.000000000 +0200 ++++ b/src/editor/floodfill.hpp 2018-02-11 08:22:08.000000000 +0300 +@@ -0,0 +1,28 @@ ++// This file is part of Dust Racing 2D. ++// Copyright (C) 2018 Jussi Lind ++// ++// Dust Racing 2D is free software: you can redistribute it and/or modify ++// it under the terms of the GNU General Public License as published by ++// the Free Software Foundation, either version 3 of the License, or ++// (at your option) any later version. ++// Dust Racing 2D is distributed in the hope that it will be useful, ++// but WITHOUT ANY WARRANTY; without even the implied warranty of ++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++// GNU General Public License for more details. ++// ++// You should have received a copy of the GNU General Public License ++// along with Dust Racing 2D. If not, see . ++ ++#ifndef FLOODFILL_HPP ++#define FLOODFILL_HPP ++ ++class MapBase; ++class QAction; ++class QString; ++class TrackTile; ++ ++namespace FloodFill { ++void floodFill(TrackTile & tile, QAction * action, const QString & typeToFill, MapBase & map); ++} ++ ++#endif // FLOODFILL_HPP +diff -Nuar a/src/editor/mainwindow.cpp b/src/editor/mainwindow.cpp +--- a/src/editor/mainwindow.cpp 2017-12-30 16:09:21.000000000 +0300 ++++ b/src/editor/mainwindow.cpp 2018-02-11 08:22:08.000000000 +0300 +@@ -26,7 +26,7 @@ + #include "tracktile.hpp" + #include "editordata.hpp" + #include "editorview.hpp" +-#include "editorscene.hpp" ++#include "mediator.hpp" + #include "newtrackdialog.hpp" + + #include +@@ -55,26 +55,15 @@ + + MainWindow * MainWindow::m_instance = nullptr; + +-namespace +-{ +- const char * SETTINGS_GROUP = "MainWindow"; +- const unsigned int MIN_ZOOM = 0; +- const unsigned int MAX_ZOOM = 200; +- const unsigned int INI_ZOOM = 100; +- const int CONSOLE_HEIGHT = 64; +-} +- + MainWindow::MainWindow(QString trackFile) + : m_objectModelLoader(new ObjectModelLoader) + , m_aboutDlg(new AboutDlg(this)) +-, m_editorData(new EditorData(this)) +-, m_editorScene(new EditorScene(this)) +-, m_editorView(new EditorView(*m_editorData, this)) + , m_console(new QTextEdit(this)) + , m_scaleSlider(new QSlider(Qt::Horizontal, this)) + , m_toolBar(new QToolBar(this)) + , m_randomRotationCheck(new QCheckBox(tr("Randomly rotate objects"), this)) + , m_argTrackFile(trackFile) ++, m_mediator(new Mediator(*this)) + { + if (!m_instance) + { +@@ -122,6 +111,11 @@ + } + } + ++void MainWindow::updateScaleSlider(int value) ++{ ++ m_scaleSlider->setValue(value); ++} ++ + void MainWindow::showTip() + { + QMessageBox::information( +@@ -138,7 +132,7 @@ + QSettings settings; + + // Read dialog size data +- settings.beginGroup(SETTINGS_GROUP); ++ settings.beginGroup(m_settingsGroup); + resize(settings.value("size", QSize(640, 480)).toSize()); + settings.endGroup(); + +@@ -147,15 +141,15 @@ + move(geometry.width() / 2 - width() / 2, + geometry.height() / 2 - height() / 2); + +- // Populate menu bar with actions ++ m_mediator->initScene(); ++ + populateMenuBar(); + +- // Set scene to the view +- m_editorView->setScene(m_editorScene); +- m_editorView->setSizePolicy(QSizePolicy::Preferred, +- QSizePolicy::Expanding); +- m_editorView->setMouseTracking(true); ++ createWidgets(); ++} + ++void MainWindow::createWidgets() ++{ + // Create a splitter + QSplitter * splitter = new QSplitter(this); + splitter->setOrientation(Qt::Vertical); +@@ -164,7 +158,7 @@ + QVBoxLayout * centralLayout = new QVBoxLayout; + QHBoxLayout * viewToolBarLayout = new QHBoxLayout; + m_toolBar->setOrientation(Qt::Vertical); +- viewToolBarLayout->addWidget(m_editorView); ++ m_mediator->addViewToLayout(viewToolBarLayout); + viewToolBarLayout->addWidget(m_toolBar); + centralLayout->addLayout(viewToolBarLayout); + +@@ -172,8 +166,8 @@ + populateToolBar(); + + // Add zoom slider to the layout +- m_scaleSlider->setRange(MIN_ZOOM, MAX_ZOOM); +- m_scaleSlider->setValue(INI_ZOOM); ++ m_scaleSlider->setRange(m_minZoom, m_maxZoom); ++ m_scaleSlider->setValue(m_initZoom); + m_scaleSlider->setTracking(false); + m_scaleSlider->setTickInterval(10); + m_scaleSlider->setTickPosition(QSlider::TicksBelow); +@@ -207,7 +201,7 @@ + setCentralWidget(splitter); + + QList sizes; +- sizes << height() - CONSOLE_HEIGHT << CONSOLE_HEIGHT; ++ sizes << height() - m_consoleHeight << m_consoleHeight; + splitter->setSizes(sizes); + } + +@@ -268,21 +262,6 @@ + return MainWindow::m_instance; + } + +-EditorView & MainWindow::editorView() const +-{ +- return *m_editorView; +-} +- +-EditorScene & MainWindow::editorScene() const +-{ +- return *m_editorScene; +-} +- +-EditorData & MainWindow::editorData() const +-{ +- return *m_editorData; +-} +- + ObjectModelLoader & MainWindow::objectModelLoader() const + { + return *m_objectModelLoader; +@@ -295,11 +274,7 @@ + + void MainWindow::updateScale(int value) + { +- qreal scale = static_cast(value) / 100; +- +- QTransform transform; +- transform.scale(scale, scale); +- m_editorView->setTransform(transform); ++ m_mediator->setScale(value); + + console(QString(tr("Scale set to %1%")).arg(value)); + } +@@ -310,7 +285,7 @@ + QSettings settings; + + // Save window size +- settings.beginGroup(SETTINGS_GROUP); ++ settings.beginGroup(m_settingsGroup); + settings.setValue("size", size()); + settings.endGroup(); + +@@ -362,12 +337,12 @@ + m_undoAction->setShortcut(QKeySequence("Ctrl+Z")); + editMenu->addAction(m_undoAction); + connect(m_undoAction, &QAction::triggered, [this](){ +- m_editorData->undo(); ++ m_mediator->undo(); + + setupTrackAfterUndoOrRedo(); + +- m_undoAction->setEnabled(m_editorData->isUndoable()); +- m_redoAction->setEnabled(m_editorData->isRedoable()); ++ m_undoAction->setEnabled(m_mediator->isUndoable()); ++ m_redoAction->setEnabled(m_mediator->isRedoable()); + }); + m_undoAction->setEnabled(false); + +@@ -376,27 +351,15 @@ + m_redoAction->setShortcut(QKeySequence("Ctrl+Shift+Z")); + editMenu->addAction(m_redoAction); + connect(m_redoAction, &QAction::triggered, [this](){ +- m_editorData->redo(); ++ m_mediator->redo(); + + setupTrackAfterUndoOrRedo(); + +- m_undoAction->setEnabled(m_editorData->isUndoable()); +- m_redoAction->setEnabled(m_editorData->isRedoable()); ++ m_undoAction->setEnabled(m_mediator->isUndoable()); ++ m_redoAction->setEnabled(m_mediator->isRedoable()); + }); + m_redoAction->setEnabled(false); + +- // Add "enlarge hor size"-action +- m_enlargeHorSize = new QAction(tr("Enlarge hor size"), this); +- editMenu->addAction(m_enlargeHorSize); +- connect(m_enlargeHorSize, SIGNAL(triggered()), this, SLOT(enlargeHorSize())); +- m_enlargeHorSize->setEnabled(false); +- +- // Add "enlarge ver size"-action +- m_enlargeVerSize = new QAction(tr("Enlarge ver size"), this); +- editMenu->addAction(m_enlargeVerSize); +- connect(m_enlargeVerSize, SIGNAL(triggered()), this, SLOT(enlargeVerSize())); +- m_enlargeVerSize->setEnabled(false); +- + // Add "Set track properties"-action + m_setTrackPropertiesAction = new QAction(tr("&Set properties.."), this); + editMenu->addAction(m_setTrackPropertiesAction); +@@ -460,8 +423,6 @@ + { + if (action != m_currentToolBarAction) + { +- assert(m_editorData); +- + m_currentToolBarAction = action; + + // Select-action +@@ -474,14 +435,14 @@ + { + QApplication::restoreOverrideCursor(); + QApplication::setOverrideCursor(QCursor(action->icon().pixmap(32, 32))); +- m_editorData->setMode(EditorData::EditorMode::EraseObject); ++ m_mediator->setMode(EditorMode::EraseObject); + } + // The user wants to clear a tile. + else if (action->data() == "clear") + { + QApplication::restoreOverrideCursor(); + QApplication::setOverrideCursor(QCursor(action->icon().pixmap(32, 32))); +- m_editorData->setMode(EditorData::EditorMode::SetTileType); ++ m_mediator->setMode(EditorMode::SetTileType); + } + // The user wants to set a tile type or clear it. + else if (m_objectModelLoader->getCategoryByRole( +@@ -489,7 +450,7 @@ + { + QApplication::restoreOverrideCursor(); + QApplication::setOverrideCursor(QCursor(action->icon().pixmap(32, 32))); +- m_editorData->setMode(EditorData::EditorMode::SetTileType); ++ m_mediator->setMode(EditorMode::SetTileType); + } + // The user wants to add an object to the scene. + else if (m_objectModelLoader->getCategoryByRole( +@@ -497,7 +458,7 @@ + { + QApplication::restoreOverrideCursor(); + QApplication::setOverrideCursor(QCursor(QPixmap(":/cursor2.png"))); +- m_editorData->setMode(EditorData::EditorMode::AddObject); ++ m_mediator->setMode(EditorMode::AddObject); + } + } + else +@@ -514,7 +475,7 @@ + // Load recent path + QSettings settings; + +- settings.beginGroup(SETTINGS_GROUP); ++ settings.beginGroup(m_settingsGroup); + QString path = settings.value("recentPath", + QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).toString(); + settings.endGroup(); +@@ -539,9 +500,8 @@ + + void MainWindow::clearRoute() + { +- assert(m_editorData); +- m_editorData->saveUndoPoint(); +- m_editorData->clearRoute(); ++ m_mediator->clearRoute(); ++ + m_clearRouteAction->setEnabled(false); + } + +@@ -553,13 +513,11 @@ + return false; + } + +- assert(m_editorData); +- + // Undo stack will be cleared. + m_undoAction->setEnabled(false); + m_redoAction->setEnabled(false); + +- if (m_editorData->loadTrackData(fileName)) ++ if (m_mediator->openTrack(fileName)) + { + console(QString(tr("Track '%1' opened.").arg(fileName))); + +@@ -568,7 +526,7 @@ + // Save recent path + QSettings settings; + +- settings.beginGroup(SETTINGS_GROUP); ++ settings.beginGroup(m_settingsGroup); + settings.setValue("recentPath", fileName); + settings.endGroup(); + +@@ -576,21 +534,11 @@ + + setActionStatesOnNewTrack(); + +- delete m_editorScene; +- m_editorScene = new EditorScene; +- +- m_editorView->setScene(m_editorScene); +- m_editorView->updateSceneRect(); +- +- m_editorData->addTilesToScene(); +- m_editorData->addObjectsToScene(); +- m_editorData->addExistingRouteToScene(); +- + fitScale(); + + m_saved = true; + +- m_clearRouteAction->setEnabled(m_editorData->trackData()->route().numNodes()); ++ m_clearRouteAction->setEnabled(m_mediator->routeHasNodes()); + + return true; + } +@@ -603,23 +551,87 @@ + return false; + } + +-void MainWindow::setupTrackAfterUndoOrRedo() ++void MainWindow::beginSetRoute() + { +- m_saveAction->setEnabled(true); ++ if (m_mediator->beginSetRoute()) ++ { ++ console(tr("Set route: begin.")); ++ QMessageBox::information( ++ this, ++ tr("Set route, checkpoints and driving lines."), ++ tr("Setting the route defines checkpoints for the cars so\n" ++ "that no shortcuts can be taken. It also defines\n" ++ "driving lines for the computer players.\n\n" ++ "Click on the tiles one by one and make a closed loop\n" ++ "with the target nodes. You can adjust the nodes afterwads.\n" ++ "Start from the first tile after the finish line tile\n" ++ "to make the lap detection and timing work correctly.\n" ++ "Click on the first node again to finish.")); ++ } ++ else ++ { ++ console(tr("Set route: not a valid track.")); ++ QMessageBox::critical(this, tr("Set route"), tr("Invalid track. Route cannot be set.")); ++ } ++} + +- setActionStatesOnNewTrack(); ++void MainWindow::clearEditMode() ++{ ++ m_mediator->clearEditMode(); ++} ++ ++void MainWindow::console(QString text) ++{ ++ QDateTime date = QDateTime::currentDateTime(); ++ m_console->append(QString("(") + date.toString("hh:mm:ss") + "): " + text); ++} ++ ++void MainWindow::enableUndo(bool enable) ++{ ++ m_undoAction->setEnabled(enable); ++} ++ ++void MainWindow::endSetRoute() ++{ ++ m_mediator->endSetRoute(); ++ ++ m_clearRouteAction->setEnabled(m_mediator->routeHasNodes()); ++ ++ console(tr("Set route: route finished.")); ++} + +- delete m_editorScene; +- m_editorScene = new EditorScene; ++void MainWindow::fitScale() ++{ ++ m_scaleSlider->setValue(m_mediator->fitScale()); ++} + +- m_editorView->setScene(m_editorScene); +- m_editorView->updateSceneRect(); ++void MainWindow::initializeNewTrack() ++{ ++ QString nameOut; ++ int colsOut, rowsOut; ++ if (m_mediator->initializeNewTrack(nameOut, colsOut, rowsOut)) ++ { ++ // Undo stack has been cleared. ++ m_undoAction->setEnabled(false); ++ m_redoAction->setEnabled(false); + +- m_editorData->addTilesToScene(); +- m_editorData->addObjectsToScene(); +- m_editorData->addExistingRouteToScene(); ++ fitScale(); + +- m_clearRouteAction->setEnabled(m_editorData->trackData()->route().numNodes()); ++ setActionStatesOnNewTrack(); ++ ++ setTitle(tr("New file")); ++ ++ m_clearRouteAction->setEnabled(false); ++ ++ m_saved = false; ++ ++ console(QString(tr("A new track '%1' created. Columns: %2, Rows: %3.")) ++ .arg(nameOut) ++ .arg(colsOut) ++ .arg(rowsOut)); ++ } ++ ++ QApplication::restoreOverrideCursor(); + } + + void MainWindow::saveTrack() +@@ -632,18 +644,15 @@ + } + else + { +- assert(m_editorData); +- if (m_editorData->saveTrackData()) ++ if (m_mediator->saveTrackData()) + { +- console(QString( +- tr("Track '")) + m_editorData->trackData()->fileName() + tr("' saved.")); +- setTitle(m_editorData->trackData()->fileName()); ++ console(QString(tr("Track '")) + m_mediator->currentFileName() + tr("' saved.")); ++ setTitle(m_mediator->currentFileName()); + m_saved = true; + } + else + { +- console(QString( +- tr("Failed to save track '")) + m_editorData->trackData()->fileName() + "'."); ++ console(QString(tr("Failed to save track '")) + m_mediator->currentFileName() + "'."); + } + } + +@@ -665,8 +674,7 @@ + fileName += trackFileExtension; + } + +- assert(m_editorData); +- if (m_editorData->saveTrackDataAs(fileName)) ++ if (m_mediator->saveTrackDataAs(fileName)) + { + console(QString(tr("Track '")) + fileName + tr("' saved.")); + setTitle(fileName); +@@ -680,51 +688,15 @@ + QApplication::restoreOverrideCursor(); + } + +-void MainWindow::initializeNewTrack() ++void MainWindow::setupTrackAfterUndoOrRedo() + { +- QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor)); +- +- // Show a dialog asking some questions about the track +- NewTrackDialog dialog(this); +- if (dialog.exec() == QDialog::Accepted) +- { +- const unsigned int cols = dialog.cols(); +- const unsigned int rows = dialog.rows(); +- +- assert(m_editorData); +- +- m_editorData->setTrackData(TrackDataPtr(new TrackData(dialog.name(), dialog.isUserTrack(), cols, rows))); +- +- delete m_editorScene; +- m_editorScene = new EditorScene; +- +- m_editorView->setScene(m_editorScene); +- m_editorView->updateSceneRect(); +- +- // Undo stack has been cleared. +- m_undoAction->setEnabled(false); +- m_redoAction->setEnabled(false); +- +- m_editorData->addTilesToScene(); +- m_editorData->addObjectsToScene(); +- +- fitScale(); +- +- setActionStatesOnNewTrack(); +- +- console(QString(tr("A new track '%1' created. Columns: %2, Rows: %3.")) +- .arg(m_editorData->trackData()->name()) +- .arg(m_editorData->trackData()->map().cols()) +- .arg(m_editorData->trackData()->map().rows())); +- +- setTitle(tr("New file")); ++ m_saveAction->setEnabled(true); + +- m_clearRouteAction->setEnabled(false); ++ setActionStatesOnNewTrack(); + +- m_saved = false; +- } ++ m_mediator->setupTrackAfterUndoOrRedo(); + +- QApplication::restoreOverrideCursor(); ++ m_clearRouteAction->setEnabled(m_mediator->routeHasNodes()); + } + + void MainWindow::setActionStatesOnNewTrack() +@@ -732,114 +704,20 @@ + m_saveAction->setEnabled(true); + m_saveAsAction->setEnabled(true); + m_toolBar->setEnabled(true); +- m_enlargeHorSize->setEnabled(true); +- m_enlargeVerSize->setEnabled(true); + m_setRouteAction->setEnabled(true); + m_setTrackPropertiesAction->setEnabled(true); + } + + void MainWindow::setTrackProperties() + { +- // Show a dialog to set some properties e.g. lap count. +- assert(m_editorData); +- TrackPropertiesDialog dialog(m_editorData->trackData()->name(), m_editorData->trackData()->index(), m_editorData->trackData()->isUserTrack(), this); +- if (dialog.exec() == QDialog::Accepted) +- { +- m_editorData->trackData()->setName(dialog.name()); +- m_editorData->trackData()->setIndex(dialog.index()); +- m_editorData->trackData()->setUserTrack(dialog.isUserTrack()); +- console(QString(tr("Track properties updated."))); +- } +-} +- +-void MainWindow::enlargeHorSize() +-{ +- assert(m_editorData); +- if (m_editorData->trackData()) ++ if (m_mediator->setTrackProperties()) + { +- m_editorData->trackData()->enlargeHorSize(); +- m_editorData->addTilesToScene(); +- +- m_editorView->updateSceneRect(); +- } +-} +- +-void MainWindow::enlargeVerSize() +-{ +- assert(m_editorData); +- if (m_editorData->trackData()) +- { +- m_editorData->trackData()->enlargeVerSize(); +- m_editorData->addTilesToScene(); +- +- m_editorView->updateSceneRect(); +- } +-} +- +-void MainWindow::enableUndo(bool enable) +-{ +- m_undoAction->setEnabled(enable); +-} +- +-void MainWindow::beginSetRoute() +-{ +- QApplication::restoreOverrideCursor(); +- +- assert(m_editorData); +- +- m_editorData->saveUndoPoint(); +- +- if (m_editorData->canRouteBeSet()) +- { +- console(tr("Set route: begin.")); +- QMessageBox::information( +- this, +- tr("Set route, checkpoints and driving lines."), +- tr("Setting the route defines checkpoints for the cars so\n" +- "that no shortcuts can be taken. It also defines\n" +- "driving lines for the computer players.\n\n" +- "Click on the tiles one by one and make a closed loop\n" +- "with the target nodes. You can adjust the nodes afterwads.\n" +- "Start from the first tile after the finish line tile\n" +- "to make the lap detection and timing work correctly.\n" +- "Click on the first node again to finish.")); +- m_editorData->beginSetRoute(); +- } +- else +- { +- QMessageBox::critical(this, tr("Set route"), tr("Invalid track. Route cannot be set.")); +- console(tr("Set route: not a valid track.")); ++ console(QString(tr("Track properties updated."))); + } + } + +-void MainWindow::endSetRoute() +-{ +- assert(m_editorData); +- m_editorData->endSetRoute(); +- m_clearRouteAction->setEnabled(m_editorData->trackData()->route().numNodes()); +- console(tr("Set route: route finished.")); +-} +- +-void MainWindow::console(QString text) +-{ +- QDateTime date = QDateTime::currentDateTime(); +- m_console->append(QString("(") + date.toString("hh:mm:ss") + "): " + text); +-} +- +-void MainWindow::clearEditMode() +-{ +- QApplication::restoreOverrideCursor(); +- m_editorData->setMode(EditorData::EditorMode::None); +-} +- +-void MainWindow::fitScale() +-{ +- m_editorView->centerOn(m_editorView->sceneRect().center()); +- m_scaleSlider->setValue(m_editorView->viewport()->height() * 100 / m_editorView->sceneRect().height()); +-} +- + MainWindow::~MainWindow() + { +- delete m_editorData; ++ delete m_mediator; + delete m_objectModelLoader; + } +diff -Nuar a/src/editor/mainwindow.hpp b/src/editor/mainwindow.hpp +--- a/src/editor/mainwindow.hpp 2017-12-30 16:09:21.000000000 +0300 ++++ b/src/editor/mainwindow.hpp 2018-02-11 08:22:08.000000000 +0300 +@@ -21,15 +21,14 @@ + #include + + class AboutDlg; +-class EditorData; +-class EditorView; +-class EditorScene; + class ObjectModelLoader; + class QAction; + class QCheckBox; + class QSlider; + class QTextEdit; + class QToolBar; ++ ++class Mediator; + class TrackData; + + /*! \class MainWindow +@@ -59,15 +58,6 @@ + //! Print text to the console. + void console(QString text); + +- //! Get reference to the view. +- EditorView & editorView() const; +- +- //! Get reference to the scene. +- EditorScene & editorScene() const; +- +- //! Get reference to the data. +- EditorData & editorData() const; +- + //! Get reference to the object loaded. + ObjectModelLoader & objectModelLoader() const; + +@@ -89,6 +79,8 @@ + + void setVisible(bool visible) override; + ++ void updateScaleSlider(int value); ++ + protected: + + void closeEvent(QCloseEvent * event) override; +@@ -99,10 +91,6 @@ + + bool doOpenTrack(QString fileName); + +- void enlargeHorSize(); +- +- void enlargeVerSize(); +- + void handleToolBarActionClick(QAction * action); + + void initializeNewTrack(); +@@ -133,6 +121,8 @@ + + void clearEditMode(); + ++ void createWidgets(); ++ + void fitScale(); + + void init(); +@@ -149,12 +139,6 @@ + + AboutDlg * m_aboutDlg; + +- EditorData * m_editorData; +- +- EditorScene * m_editorScene; +- +- EditorView * m_editorView; +- + QTextEdit * m_console; + + QAction * m_saveAction = nullptr; +@@ -167,10 +151,6 @@ + + QAction * m_redoAction = nullptr; + +- QAction * m_enlargeHorSize = nullptr; +- +- QAction * m_enlargeVerSize = nullptr; +- + QAction * m_clearRouteAction = nullptr; + + QAction * m_setRouteAction = nullptr; +@@ -187,6 +167,18 @@ + + bool m_saved = false; + ++ const char * m_settingsGroup = "MainWindow"; ++ ++ const unsigned int m_minZoom = 0; ++ ++ const unsigned int m_maxZoom = 200; ++ ++ const unsigned int m_initZoom = 100; ++ ++ const int m_consoleHeight = 64; ++ ++ Mediator * m_mediator = nullptr; ++ + static MainWindow * m_instance; + }; + +diff -Nuar a/src/editor/mediator.cpp b/src/editor/mediator.cpp +--- a/src/editor/mediator.cpp 1970-01-01 02:00:00.000000000 +0200 ++++ b/src/editor/mediator.cpp 2018-02-11 08:22:08.000000000 +0300 +@@ -0,0 +1,475 @@ ++// This file is part of Dust Racing 2D. ++// Copyright (C) 2018 Jussi Lind ++// ++// Dust Racing 2D is free software: you can redistribute it and/or modify ++// it under the terms of the GNU General Public License as published by ++// the Free Software Foundation, either version 3 of the License, or ++// (at your option) any later version. ++// Dust Racing 2D is distributed in the hope that it will be useful, ++// but WITHOUT ANY WARRANTY; without even the implied warranty of ++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++// GNU General Public License for more details. ++// ++// You should have received a copy of the GNU General Public License ++// along with Dust Racing 2D. If not, see . ++ ++#include "mediator.hpp" ++ ++#include "draganddropstore.hpp" ++#include "editordata.hpp" ++#include "editorview.hpp" ++#include "floodfill.hpp" ++#include "mainwindow.hpp" ++#include "newtrackdialog.hpp" ++#include "object.hpp" ++#include "objectfactory.hpp" ++#include "trackdata.hpp" ++#include "tracktile.hpp" ++#include "trackpropertiesdialog.hpp" ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++Mediator::Mediator(MainWindow & mainWindow) ++ : m_editorData(new EditorData(*this)) ++ , m_editorScene(new QGraphicsScene) ++ , m_editorView(new EditorView(*this)) ++ , m_mainWindow(mainWindow) ++{ ++ m_editorView->setParent(&mainWindow); ++} ++ ++void Mediator::addCurrentToolBarObjectToScene(QPointF clickedScenePos) ++{ ++ if (QAction * action = MainWindow::instance()->currentToolBarAction()) ++ { ++ saveUndoPoint(); ++ ++ Object & object = ObjectFactory::createObject(action->data().toString()); ++ object.setLocation(clickedScenePos); ++ ++ if (MainWindow::instance()->randomlyRotateObjects()) ++ { ++ object.setRotation(std::rand() % 360); ++ } ++ ++ addObject(object); ++ setSelectedObject(&object); ++ } ++} ++ ++void Mediator::addItem(QGraphicsItem * item) ++{ ++ m_editorScene->addItem(item); ++} ++ ++void Mediator::addObject(Object & object) ++{ ++ m_editorScene->addItem(&object); ++ m_editorData->trackData()->objects().add(ObjectBasePtr(&object)); ++} ++ ++void Mediator::addViewToLayout(QLayout * layout) ++{ ++ layout->addWidget(m_editorView); ++} ++ ++bool Mediator::beginSetRoute() ++{ ++ QApplication::restoreOverrideCursor(); ++ ++ assert(m_editorData); ++ ++ m_editorData->saveUndoPoint(); ++ ++ if (m_editorData->canRouteBeSet()) ++ { ++ m_editorData->beginSetRoute(); ++ ++ return true; ++ } ++ ++ return false; ++} ++ ++void Mediator::console(QString message) ++{ ++ m_mainWindow.console(message); ++} ++ ++QString Mediator::currentFileName() const ++{ ++ return m_editorData->trackData()->fileName(); ++} ++ ++QAction * Mediator::currentToolBarAction() const ++{ ++ return m_mainWindow.currentToolBarAction(); ++} ++ ++DragAndDropStore & Mediator::dadStore() ++{ ++ return m_editorData->dadStore(); ++} ++ ++void Mediator::deleteColumn() ++{ ++ auto deleted = m_editorData->trackData()->deleteColumn(m_editorData->activeColumn()); ++ for (auto && tile : deleted) { ++ m_editorData->removeTileFromScene(tile); ++ } ++} ++ ++void Mediator::deleteRow() ++{ ++ auto deleted = m_editorData->trackData()->deleteRow(m_editorData->activeRow()); ++ for (auto && tile : deleted) { ++ m_editorData->removeTileFromScene(tile); ++ } ++} ++ ++void Mediator::floodFill(TrackTile & tile, QAction * action, const QString & typeToFill) ++{ ++ saveUndoPoint(); ++ ++ FloodFill::floodFill(tile, action, typeToFill, m_editorData->trackData()->map()); ++} ++ ++void Mediator::endSetRoute() ++{ ++ setMode(EditorMode::None); ++} ++ ++void Mediator::enableUndo(bool enable) ++{ ++ m_mainWindow.enableUndo(enable); ++} ++ ++int Mediator::fitScale() ++{ ++ m_editorView->centerOn(m_editorView->sceneRect().center()); ++ return m_editorView->viewport()->height() * 100 / m_editorView->sceneRect().height(); ++} ++ ++bool Mediator::initializeNewTrack(QString & name, int & cols, int & rows) ++{ ++ assert(m_editorData); ++ ++ // Show a dialog asking some questions about the track ++ NewTrackDialog dialog(&m_mainWindow); ++ if (dialog.exec() == QDialog::Accepted) ++ { ++ cols = dialog.cols(); ++ rows = dialog.rows(); ++ name = dialog.name(); ++ ++ m_editorData->setTrackData(TrackDataPtr(new TrackData(name, dialog.isUserTrack(), cols, rows))); ++ ++ delete m_editorScene; ++ m_editorScene = new QGraphicsScene; ++ ++ m_editorView->setScene(m_editorScene); ++ m_editorView->updateSceneRect(); ++ ++ m_editorData->addTilesToScene(); ++ m_editorData->addObjectsToScene(); ++ ++ return true; ++ } ++ ++ return false; ++} ++ ++void Mediator::insertColumnAfter() ++{ ++ m_editorData->trackData()->insertColumn(m_editorData->activeColumn(), MapBase::InsertDirection::After); ++ m_editorData->addTilesToScene(); ++} ++ ++void Mediator::insertColumnBefore() ++{ ++ m_editorData->trackData()->insertColumn(m_editorData->activeColumn(), MapBase::InsertDirection::Before); ++ m_editorData->addTilesToScene(); ++} ++ ++void Mediator::insertRowAfter() ++{ ++ m_editorData->trackData()->insertRow(m_editorData->activeRow(), MapBase::InsertDirection::After); ++ m_editorData->addTilesToScene(); ++} ++ ++void Mediator::insertRowBefore() ++{ ++ m_editorData->trackData()->insertRow(m_editorData->activeRow(), MapBase::InsertDirection::Before); ++ m_editorData->addTilesToScene(); ++} ++ ++void Mediator::initScene() ++{ ++ m_editorView->setScene(m_editorScene); ++ m_editorView->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); ++ m_editorView->setMouseTracking(true); ++} ++ ++bool Mediator::isUndoable() const ++{ ++ return m_editorData->isUndoable(); ++} ++ ++void Mediator::clearEditMode() ++{ ++ QApplication::restoreOverrideCursor(); ++ setMode(EditorMode::None); ++} ++ ++void Mediator::clearRoute() ++{ ++ assert(m_editorData); ++ ++ m_editorData->saveUndoPoint(); ++ m_editorData->clearRoute(); ++} ++ ++int Mediator::cols() ++{ ++ if (m_editorData->trackData()) ++ { ++ return m_editorData->trackData()->map().cols(); ++ } ++ return 0; ++} ++ ++bool Mediator::openTrack(QString fileName) ++{ ++ assert(m_editorData); ++ ++ if (m_editorData->loadTrackData(fileName)) ++ { ++ delete m_editorScene; ++ m_editorScene = new QGraphicsScene; ++ ++ m_editorView->setScene(m_editorScene); ++ m_editorView->updateSceneRect(); ++ ++ m_editorData->addTilesToScene(); ++ m_editorData->addObjectsToScene(); ++ m_editorData->addExistingRouteToScene(); ++ ++ return true; ++ } ++ ++ return false; ++} ++ ++bool Mediator::isRedoable() const ++{ ++ return m_editorData->isRedoable(); ++} ++ ++void Mediator::mouseWheelZoom(int delta) ++{ ++ const int sensitivity = 10; ++ const int currentScale = m_editorData->currentScale(); ++ const int maxScale = 200; ++ ++ int newScale = currentScale; ++ ++ if (delta < 0) ++ { ++ if (currentScale > sensitivity) ++ { ++ newScale = currentScale - sensitivity; ++ } ++ else ++ { ++ newScale = 0; ++ } ++ } ++ else if (delta > 0) ++ { ++ if (currentScale + sensitivity < maxScale) ++ { ++ newScale = currentScale + sensitivity; ++ } ++ else ++ { ++ newScale = maxScale; ++ } ++ } ++ ++ if (newScale != currentScale) ++ { ++ setScale(newScale); ++ m_mainWindow.updateScaleSlider(newScale); ++ } ++} ++ ++void Mediator::pushNewTargetNodeToRoute(QPointF pos) ++{ ++ m_editorData->pushNewTargetNodeToRoute(pos); ++} ++ ++void Mediator::redo() ++{ ++ m_editorData->redo(); ++} ++ ++void Mediator::removeItem(QGraphicsItem * item) ++{ ++ m_editorScene->removeItem(item); ++} ++ ++void Mediator::removeObject(Object & object) ++{ ++ m_editorScene->removeItem(&object); ++ m_editorData->trackData()->objects().remove(object); ++} ++ ++bool Mediator::routeHasNodes() const ++{ ++ return m_editorData->trackData()->route().numNodes(); ++} ++ ++void Mediator::saveUndoPoint() ++{ ++ m_editorData->saveUndoPoint(); ++} ++ ++bool Mediator::saveTrackData() ++{ ++ return m_editorData->saveTrackData(); ++} ++ ++bool Mediator::saveTrackDataAs(QString fileName) ++{ ++ return m_editorData->saveTrackDataAs(fileName); ++} ++ ++void Mediator::setMode(EditorMode mode) ++{ ++ m_mode = mode; ++} ++ ++void Mediator::setSelectedObject(Object * object) ++{ ++ m_editorData->setSelectedObject(object); ++} ++ ++Object * Mediator::selectedObject() const ++{ ++ return m_editorData->selectedObject(); ++} ++ ++void Mediator::setSelectedTargetNode(TargetNode * tnode) ++{ ++ m_editorData->setSelectedTargetNode(tnode); ++} ++ ++TargetNode * Mediator::selectedTargetNode() const ++{ ++ return m_editorData->selectedTargetNode(); ++} ++ ++void Mediator::setScale(int value) ++{ ++ const qreal scale = static_cast(value) / 100; ++ QTransform transform; ++ transform.scale(scale, scale); ++ m_editorView->setTransform(transform); ++ m_editorData->setCurrentScale(value); ++} ++ ++EditorMode Mediator::mode() const ++{ ++ return m_mode; ++} ++ ++int Mediator::rows() ++{ ++ if (m_editorData->trackData()) ++ { ++ return m_editorData->trackData()->map().rows(); ++ } ++ return 0; ++} ++ ++bool Mediator::setTrackProperties() ++{ ++ assert(m_editorData); ++ ++ // Show a dialog to set some properties e.g. lap count. ++ TrackPropertiesDialog dialog( ++ m_editorData->trackData()->name(), m_editorData->trackData()->index(), m_editorData->trackData()->isUserTrack(), &m_mainWindow); ++ if (dialog.exec() == QDialog::Accepted) ++ { ++ m_editorData->trackData()->setName(dialog.name()); ++ m_editorData->trackData()->setIndex(dialog.index()); ++ m_editorData->trackData()->setUserTrack(dialog.isUserTrack()); ++ return true; ++ } ++ ++ return false; ++} ++ ++void Mediator::showStatusBarMessage(QString message) ++{ ++ m_mainWindow.statusBar()->showMessage(message); ++} ++ ++void Mediator::setupTrackAfterUndoOrRedo() ++{ ++ delete m_editorScene; ++ m_editorScene = new QGraphicsScene; ++ ++ m_editorView->setScene(m_editorScene); ++ m_editorView->updateSceneRect(); ++ ++ m_editorData->addTilesToScene(); ++ m_editorData->addObjectsToScene(); ++ m_editorData->addExistingRouteToScene(); ++} ++ ++void Mediator::undo() ++{ ++ m_editorData->undo(); ++} ++ ++void Mediator::updateCoordinates(QPointF mappedPos) ++{ ++ if (m_editorData->trackData()) ++ { ++ const int maxCols = static_cast(m_editorData->trackData()->map().cols()); ++ int column = mappedPos.x() / TrackTile::TILE_W; ++ column = column >= maxCols ? maxCols - 1 : column; ++ column = column < 0 ? 0 : column; ++ ++ const int maxRows = static_cast(m_editorData->trackData()->map().rows()); ++ int row = mappedPos.y() / TrackTile::TILE_H; ++ row = row >= maxRows ? maxRows - 1 : row; ++ row = row < 0 ? 0 : row; ++ ++ m_editorData->setActiveRow(static_cast(row)); ++ m_editorData->setActiveColumn(static_cast(column)); ++ ++ QString coordinates("X: %1 Y: %2 I: %3 J: %4"); ++ coordinates = coordinates.arg(mappedPos.x()).arg(mappedPos.y()).arg(column).arg(row); ++ ++ showStatusBarMessage(coordinates); ++ } ++} ++ ++void Mediator::updateView() ++{ ++ m_editorView->update(); ++} ++ ++Mediator::~Mediator() ++{ ++ delete m_editorData; ++} ++ +diff -Nuar a/src/editor/mediator.hpp b/src/editor/mediator.hpp +--- a/src/editor/mediator.hpp 1970-01-01 02:00:00.000000000 +0200 ++++ b/src/editor/mediator.hpp 2018-02-11 08:22:08.000000000 +0300 +@@ -0,0 +1,157 @@ ++// This file is part of Dust Racing 2D. ++// Copyright (C) 2018 Jussi Lind ++// ++// Dust Racing 2D is free software: you can redistribute it and/or modify ++// it under the terms of the GNU General Public License as published by ++// the Free Software Foundation, either version 3 of the License, or ++// (at your option) any later version. ++// Dust Racing 2D is distributed in the hope that it will be useful, ++// but WITHOUT ANY WARRANTY; without even the implied warranty of ++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++// GNU General Public License for more details. ++// ++// You should have received a copy of the GNU General Public License ++// along with Dust Racing 2D. If not, see . ++ ++#ifndef MEDIATOR_HPP ++#define MEDIATOR_HPP ++ ++#include ++ ++#include "editormode.hpp" ++#include "targetnode.hpp" ++ ++class DragAndDropStore; ++class EditorData; ++class EditorView; ++class MainWindow; ++class TrackTile; ++class Object; ++class QAction; ++class QGraphicsScene; ++class QLayout; ++ ++/*! Acts as a communication channel between MainWindow and editor components: ++ * ++ * - MainWindow <-> Mediator <-> QGraphicsScene / EditorView / EditorData ++ * - EditorView <-> Mediator <-> EditorData ++ */ ++class Mediator ++{ ++public: ++ ++ explicit Mediator(MainWindow & mainWindow); ++ ++ ~Mediator(); ++ ++ void addCurrentToolBarObjectToScene(QPointF clickedScenePos); ++ ++ void addItem(QGraphicsItem * item); ++ ++ void addObject(Object & object); ++ ++ void addViewToLayout(QLayout * layout); ++ ++ bool beginSetRoute(); ++ ++ void clearEditMode(); ++ ++ void clearRoute(); ++ ++ int cols(); ++ ++ void console(QString message); ++ ++ QString currentFileName() const; ++ ++ QAction * currentToolBarAction() const; ++ ++ DragAndDropStore & dadStore(); ++ ++ void deleteColumn(); ++ ++ void deleteRow(); ++ ++ void endSetRoute(); ++ ++ void enableUndo(bool enable); ++ ++ void floodFill(TrackTile & tile, QAction * action, const QString & typeToFill); ++ ++ int fitScale(); ++ ++ void initScene(); ++ ++ bool initializeNewTrack(QString & name, int & cols, int & rows); ++ ++ void insertColumnAfter(); ++ ++ void insertColumnBefore(); ++ ++ void insertRowAfter(); ++ ++ void insertRowBefore(); ++ ++ bool isUndoable() const; ++ ++ bool isRedoable() const; ++ ++ void mouseWheelZoom(int delta); ++ ++ bool openTrack(QString fileName); ++ ++ void pushNewTargetNodeToRoute(QPointF pos); ++ ++ void redo(); ++ ++ void removeItem(QGraphicsItem * item); ++ ++ void removeObject(Object & object); ++ ++ bool routeHasNodes() const; ++ ++ int rows(); ++ ++ void saveUndoPoint(); ++ ++ bool saveTrackData(); ++ ++ bool saveTrackDataAs(QString fileName); ++ ++ void setScale(int value); ++ ++ void setMode(EditorMode newMode); ++ EditorMode mode() const; ++ ++ void setSelectedObject(Object * object); ++ Object * selectedObject() const; ++ ++ void setSelectedTargetNode(TargetNode * tnode); ++ TargetNode * selectedTargetNode() const; ++ ++ bool setTrackProperties(); ++ ++ void showStatusBarMessage(QString message); ++ ++ void setupTrackAfterUndoOrRedo(); ++ ++ void undo(); ++ ++ void updateCoordinates(QPointF mappedPos); ++ ++ void updateView(); ++ ++private: ++ ++ EditorData * m_editorData; ++ ++ QGraphicsScene * m_editorScene; ++ ++ EditorView * m_editorView; ++ ++ MainWindow & m_mainWindow; ++ ++ EditorMode m_mode = EditorMode::None; ++}; ++ ++#endif // MEDIATOR_HPP +diff -Nuar a/src/editor/trackdata.cpp b/src/editor/trackdata.cpp +--- a/src/editor/trackdata.cpp 2017-12-30 16:09:21.000000000 +0300 ++++ b/src/editor/trackdata.cpp 2018-02-11 08:22:08.000000000 +0300 +@@ -104,16 +104,6 @@ + return m_objects; + } + +-void TrackData::enlargeHorSize() +-{ +- m_map.resize(m_map.cols() + 1, m_map.rows()); +-} +- +-void TrackData::enlargeVerSize() +-{ +- m_map.resize(m_map.cols(), m_map.rows() + 1); +-} +- + void TrackData::insertColumn(unsigned int at, MapBase::InsertDirection insertDirection) + { + at = m_map.insertColumn(at, insertDirection); +diff -Nuar a/src/editor/trackdata.hpp b/src/editor/trackdata.hpp +--- a/src/editor/trackdata.hpp 2017-12-30 16:09:21.000000000 +0300 ++++ b/src/editor/trackdata.hpp 2018-02-11 08:22:08.000000000 +0300 +@@ -64,12 +64,6 @@ + //! Get objects object. + const Objects & objects() const; + +- //! Enlarge horizontal size. +- void enlargeHorSize(); +- +- //! Enlarge vertical size. +- void enlargeVerSize(); +- + //! Insert tile column at given index and move objects. + void insertColumn(unsigned int at, MapBase::InsertDirection insertDirection); + +diff -Nuar a/src/game/renderer.cpp b/src/game/renderer.cpp +--- a/src/game/renderer.cpp 2017-12-30 16:09:21.000000000 +0300 ++++ b/src/game/renderer.cpp 2018-02-11 08:22:08.000000000 +0300 +@@ -55,7 +55,7 @@ + , m_eventHandler(nullptr) + , m_viewAngle(22.5f) + , m_zNear(10.0f) +-, m_zFar(1500.0f) ++, m_zFar(10000.0f) // See: https://github.com/juzzlin/DustRacing2D/issues/30 + , m_fadeValue(1.0f) + , m_enabled(false) + , m_hRes(hRes)