summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2015-03-17 13:58:32 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2015-03-17 13:58:32 -0400
commite882367d80a0bcdd09b5412d908b0fdb6b6bfe34 (patch)
treee5a023fc51f02cf37b97bfbb9ef09d2ddfc6591e
parent29f818c314f86f9a842840c20d9634f0711507a6 (diff)
downloadtherapy-e882367d80a0bcdd09b5412d908b0fdb6b6bfe34.tar.gz
therapy-e882367d80a0bcdd09b5412d908b0fdb6b6bfe34.tar.bz2
therapy-e882367d80a0bcdd09b5412d908b0fdb6b6bfe34.zip
Implemented undo/redo framework in map editor
-rw-r--r--tools/mapedit/CMakeLists.txt1
-rw-r--r--tools/mapedit/src/frame.cpp197
-rw-r--r--tools/mapedit/src/frame.h30
-rw-r--r--tools/mapedit/src/map.cpp16
-rw-r--r--tools/mapedit/src/map.h9
-rw-r--r--tools/mapedit/src/undo.cpp95
-rw-r--r--tools/mapedit/src/undo.h63
-rw-r--r--tools/mapedit/src/widget.cpp110
-rw-r--r--tools/mapedit/src/widget.h4
-rw-r--r--tools/mapedit/src/world.cpp4
10 files changed, 482 insertions, 47 deletions
diff --git a/tools/mapedit/CMakeLists.txt b/tools/mapedit/CMakeLists.txt index 12af38c..ca44fc5 100644 --- a/tools/mapedit/CMakeLists.txt +++ b/tools/mapedit/CMakeLists.txt
@@ -45,6 +45,7 @@ add_executable(AromatherapyMapEditor
45 src/tile_widget.cpp 45 src/tile_widget.cpp
46 src/object.cpp 46 src/object.cpp
47 src/world.cpp 47 src/world.cpp
48 src/undo.cpp
48) 49)
49target_link_libraries(AromatherapyMapEditor ${ALL_LIBS}) 50target_link_libraries(AromatherapyMapEditor ${ALL_LIBS})
50install(TARGETS AromatherapyMapEditor RUNTIME DESTINATION ${BIN_DIR}) 51install(TARGETS AromatherapyMapEditor RUNTIME DESTINATION ${BIN_DIR})
diff --git a/tools/mapedit/src/frame.cpp b/tools/mapedit/src/frame.cpp index 66775e1..3646d4e 100644 --- a/tools/mapedit/src/frame.cpp +++ b/tools/mapedit/src/frame.cpp
@@ -19,6 +19,8 @@ enum {
19 MENU_FILE_CLOSE, 19 MENU_FILE_CLOSE,
20 MENU_MAP_ADD_ROOT, 20 MENU_MAP_ADD_ROOT,
21 MENU_MAP_ADD_CHILD, 21 MENU_MAP_ADD_CHILD,
22 MENU_EDIT_UNDO,
23 MENU_EDIT_REDO,
22 TOOL_FILE_NEW, 24 TOOL_FILE_NEW,
23 TOOL_FILE_OPEN, 25 TOOL_FILE_OPEN,
24 TOOL_FILE_SAVE, 26 TOOL_FILE_SAVE,
@@ -43,6 +45,8 @@ wxBEGIN_EVENT_TABLE(MapeditFrame, wxFrame)
43 EVT_MENU(MENU_FILE_CLOSE, MapeditFrame::OnClose) 45 EVT_MENU(MENU_FILE_CLOSE, MapeditFrame::OnClose)
44 EVT_MENU(MENU_MAP_ADD_ROOT, MapeditFrame::OnAddRoot) 46 EVT_MENU(MENU_MAP_ADD_ROOT, MapeditFrame::OnAddRoot)
45 EVT_MENU(MENU_MAP_ADD_CHILD, MapeditFrame::OnAddChild) 47 EVT_MENU(MENU_MAP_ADD_CHILD, MapeditFrame::OnAddChild)
48 EVT_MENU(MENU_EDIT_UNDO, MapeditFrame::OnUndo)
49 EVT_MENU(MENU_EDIT_REDO, MapeditFrame::OnRedo)
46 EVT_TOOL(TOOL_FILE_NEW, MapeditFrame::OnNew) 50 EVT_TOOL(TOOL_FILE_NEW, MapeditFrame::OnNew)
47 EVT_TOOL(TOOL_FILE_OPEN, MapeditFrame::OnOpen) 51 EVT_TOOL(TOOL_FILE_OPEN, MapeditFrame::OnOpen)
48 EVT_TOOL(TOOL_FILE_SAVE, MapeditFrame::OnSave) 52 EVT_TOOL(TOOL_FILE_SAVE, MapeditFrame::OnSave)
@@ -51,7 +55,6 @@ wxBEGIN_EVENT_TABLE(MapeditFrame, wxFrame)
51 EVT_CLOSE(MapeditFrame::OnExit) 55 EVT_CLOSE(MapeditFrame::OnExit)
52 EVT_NOTEBOOK_PAGE_CHANGED(MAP_EDITOR_NOTEBOOK, MapeditFrame::OnTabChange) 56 EVT_NOTEBOOK_PAGE_CHANGED(MAP_EDITOR_NOTEBOOK, MapeditFrame::OnTabChange)
53 EVT_NOTEBOOK_PAGE_CHANGING(MAP_EDITOR_NOTEBOOK, MapeditFrame::OnTabChanging) 57 EVT_NOTEBOOK_PAGE_CHANGING(MAP_EDITOR_NOTEBOOK, MapeditFrame::OnTabChanging)
54 EVT_TREE_SEL_CHANGING(MAP_EDITOR_TREE, MapeditFrame::OnWillSelectMap)
55 EVT_TREE_SEL_CHANGED(MAP_EDITOR_TREE, MapeditFrame::OnDidSelectMap) 58 EVT_TREE_SEL_CHANGED(MAP_EDITOR_TREE, MapeditFrame::OnDidSelectMap)
56 EVT_TREE_BEGIN_DRAG(MAP_EDITOR_TREE, MapeditFrame::OnWillDragMap) 59 EVT_TREE_BEGIN_DRAG(MAP_EDITOR_TREE, MapeditFrame::OnWillDragMap)
57 EVT_TREE_END_DRAG(MAP_EDITOR_TREE, MapeditFrame::OnDidDragMap) 60 EVT_TREE_END_DRAG(MAP_EDITOR_TREE, MapeditFrame::OnDidDragMap)
@@ -80,12 +83,18 @@ MapeditFrame::MapeditFrame(std::unique_ptr<World> world) : wxFrame(NULL, wxID_AN
80 menuFile->AppendSeparator(); 83 menuFile->AppendSeparator();
81 menuFile->Append(wxID_EXIT); 84 menuFile->Append(wxID_EXIT);
82 85
86 menuEdit = new wxMenu;
87 menuEdit->Append(MENU_EDIT_UNDO, "Undo\tCtrl-Z");
88 menuEdit->Append(MENU_EDIT_REDO, "Redo\tCtrl-Shift-Z");
89 UpdateUndoLabels();
90
83 wxMenu* menuView = new wxMenu; 91 wxMenu* menuView = new wxMenu;
84 menuView->Append(MENU_VIEW_ZOOM_IN, "Zoom In\tCtrl-+"); 92 menuView->Append(MENU_VIEW_ZOOM_IN, "Zoom In\tCtrl-+");
85 menuView->Append(MENU_VIEW_ZOOM_OUT, "Zoom Out\tCtrl--"); 93 menuView->Append(MENU_VIEW_ZOOM_OUT, "Zoom Out\tCtrl--");
86 94
87 wxMenuBar* menuBar = new wxMenuBar; 95 wxMenuBar* menuBar = new wxMenuBar;
88 menuBar->Append(menuFile, "&File"); 96 menuBar->Append(menuFile, "&File");
97 menuBar->Append(menuEdit, "&Edit");
89 menuBar->Append(menuView, "&View"); 98 menuBar->Append(menuView, "&View");
90 99
91 SetMenuBar(menuBar); 100 SetMenuBar(menuBar);
@@ -118,7 +127,7 @@ MapeditFrame::MapeditFrame(std::unique_ptr<World> world) : wxFrame(NULL, wxID_AN
118 127
119 // Set up property editor 128 // Set up property editor
120 wxPanel* propertyEditor = new wxPanel(layout3, wxID_ANY); 129 wxPanel* propertyEditor = new wxPanel(layout3, wxID_ANY);
121 titleBox = new wxTextCtrl(propertyEditor, MAP_TITLE_TEXTBOX, currentMap->getTitle()); 130 titleBox = new UndoableTextBox(propertyEditor, MAP_TITLE_TEXTBOX, currentMap->getTitle(), "Edit Map Title", this);
122 titleBox->SetMaxLength(40); 131 titleBox->SetMaxLength(40);
123 132
124 wxStaticText* titleLabel = new wxStaticText(propertyEditor, wxID_ANY, "Title:"); 133 wxStaticText* titleLabel = new wxStaticText(propertyEditor, wxID_ANY, "Title:");
@@ -216,8 +225,9 @@ MapeditFrame::MapeditFrame(std::unique_ptr<World> world) : wxFrame(NULL, wxID_AN
216 toolbar->EnableTool(TOOL_FILE_SAVE, this->world->getDirty()); 225 toolbar->EnableTool(TOOL_FILE_SAVE, this->world->getDirty());
217 toolbar->Realize(); 226 toolbar->Realize();
218 227
219 mapTree->SetFocusedItem(currentMap->getTreeItemId()); 228 dontSelectMap = true;
220 SelectMap(currentMap); 229 mapTree->SelectItem(currentMap->getTreeItemId());
230 dontSelectMap = false;
221 231
222 Maximize(true); 232 Maximize(true);
223} 233}
@@ -324,10 +334,12 @@ void MapeditFrame::OnQuit(wxCommandEvent&)
324 } 334 }
325} 335}
326 336
327void MapeditFrame::OnTitleChange(wxCommandEvent&) 337void MapeditFrame::OnTitleChange(wxCommandEvent& event)
328{ 338{
329 currentMap->setTitle(titleBox->GetValue().ToStdString()); 339 currentMap->setTitle(titleBox->GetValue().ToStdString());
330 mapTree->SetItemText(currentMap->getTreeItemId(), currentMap->getTitle()); 340 mapTree->SetItemText(currentMap->getTreeItemId(), titleBox->GetValue());
341
342 event.Skip();
331} 343}
332 344
333void MapeditFrame::OnTabChange(wxBookCtrlEvent& event) 345void MapeditFrame::OnTabChange(wxBookCtrlEvent& event)
@@ -378,33 +390,83 @@ void MapeditFrame::OnCancelAddEntity(wxCommandEvent&)
378void MapeditFrame::OnAddRoot(wxCommandEvent&) 390void MapeditFrame::OnAddRoot(wxCommandEvent&)
379{ 391{
380 auto map = world->newMap(); 392 auto map = world->newMap();
381 wxTreeItemId node = mapTree->AppendItem(mapTree->GetItemParent(mapTree->GetSelection()), map->getTitle()); 393
382 map->setTreeItemId(node); 394 commitAction(std::make_shared<Undoable>("New Map", [=] () {
383 mapTree->SetItemData(node, new MapPtrCtr(map.get())); 395 map->setHidden(false);
384 mapTree->SetFocusedItem(node); 396
385 SelectMap(map.get()); 397 wxTreeItemId sel = mapTree->GetSelection();
398 wxTreeItemId par = mapTree->GetItemParent(sel);
399 wxTreeItemId node = mapTree->AppendItem(par, map->getTitle());
400 map->setTreeItemId(node);
401 mapTree->SetItemData(node, new MapPtrCtr(map.get()));
402
403 dontSelectMap = true;
404 mapTree->SelectItem(node);
405 dontSelectMap = false;
406 }, [=] () {
407 map->setHidden(true);
408
409 wxTreeItemId sel = mapTree->GetPrevSibling(map->getTreeItemId());
410 mapTree->Delete(map->getTreeItemId());
411
412 dontSelectMap = true;
413 mapTree->SelectItem(sel);
414 dontSelectMap = false;
415 }));
416
386} 417}
387 418
388void MapeditFrame::OnAddChild(wxCommandEvent&) 419void MapeditFrame::OnAddChild(wxCommandEvent&)
389{ 420{
390 auto map = world->newMap(); 421 auto map = world->newMap();
391 wxTreeItemId node = mapTree->AppendItem(mapTree->GetSelection(), map->getTitle()); 422
392 map->setTreeItemId(node); 423 commitAction(std::make_shared<Undoable>("New Map", [=] () {
393 mapTree->SetItemData(node, new MapPtrCtr(map.get())); 424 map->setHidden(false);
394 mapTree->SetFocusedItem(node); 425
395 mapTree->Expand(mapTree->GetSelection()); 426 wxTreeItemId sel = mapTree->GetSelection();
396 SelectMap(map.get()); 427 wxTreeItemId node = mapTree->AppendItem(sel, map->getTitle());
428 map->setTreeItemId(node);
429 mapTree->SetItemData(node, new MapPtrCtr(map.get()));
430
431 mapTree->Expand(sel);
432
433 dontSelectMap = true;
434 mapTree->SelectItem(node);
435 dontSelectMap = false;
436 }, [=] () {
437 map->setHidden(true);
438
439 wxTreeItemId sel = mapTree->GetItemParent(map->getTreeItemId());
440 mapTree->Delete(map->getTreeItemId());
441
442 dontSelectMap = true;
443 mapTree->SelectItem(sel);
444 dontSelectMap = false;
445 }));
397} 446}
398 447
399void MapeditFrame::OnDidSelectMap(wxTreeEvent& event) 448void MapeditFrame::OnDidSelectMap(wxTreeEvent& event)
400{ 449{
401 MapPtrCtr* data = (MapPtrCtr*) mapTree->GetItemData(event.GetItem()); 450 MapPtrCtr* data = (MapPtrCtr*) mapTree->GetItemData(event.GetItem());
402 SelectMap(data->map); 451 SelectMap(data->map);
403} 452
404 453 if (!dontSelectMap)
405void MapeditFrame::OnWillSelectMap(wxTreeEvent& event) 454 {
406{ 455 commitAfter(std::make_shared<Undoable>("Selecting " + data->map->getTitle(), [=] () {
407 event.Skip(); 456 wxTreeItemId toSelect = event.GetItem();
457 dontSelectMap = true;
458 mapTree->SelectItem(toSelect);
459 dontSelectMap = false;
460 SelectMap(data->map);
461 }, [=] () {
462 wxTreeItemId toSelect = event.GetOldItem();
463 MapPtrCtr* oldData = (MapPtrCtr*) mapTree->GetItemData(toSelect);
464 dontSelectMap = true;
465 mapTree->SelectItem(toSelect);
466 dontSelectMap = false;
467 SelectMap(oldData->map);
468 }));
469 }
408} 470}
409 471
410void MapeditFrame::OnWillDragMap(wxTreeEvent& event) 472void MapeditFrame::OnWillDragMap(wxTreeEvent& event)
@@ -426,9 +488,24 @@ void MapeditFrame::OnDidDragMap(wxTreeEvent& event)
426 newParent = mapTree->GetRootItem(); 488 newParent = mapTree->GetRootItem();
427 } 489 }
428 490
429 wxTreeItemId newChild = MoveTreeNode(dragMap, newParent); 491 wxTreeItemId curParent = mapTree->GetItemParent(event.GetItem());
492 wxTreeItemId dragMapCopy = dragMap;
430 dragMap.Unset(); 493 dragMap.Unset();
431 mapTree->SelectItem(newChild); 494
495 Map* theMap = ((MapPtrCtr*) mapTree->GetItemData(dragMap))->map;
496 commitAction(std::make_shared<Undoable>("Arranging " + theMap->getTitle(), [=] () {
497 wxTreeItemId newChild = MoveTreeNode(dragMapCopy, newParent);
498
499 dontSelectMap = true;
500 mapTree->SelectItem(newChild);
501 dontSelectMap = false;
502 }, [=] () {
503 wxTreeItemId newChild = MoveTreeNode(dragMapCopy, curParent);
504
505 dontSelectMap = true;
506 mapTree->SelectItem(newChild);
507 dontSelectMap = false;
508 }));
432} 509}
433 510
434void MapeditFrame::OnRightClickTree(wxTreeEvent& event) 511void MapeditFrame::OnRightClickTree(wxTreeEvent& event)
@@ -451,6 +528,28 @@ void MapeditFrame::OnCancelSetStartpos(wxCommandEvent&)
451 mapEditor->SetIsSettingStart(false); 528 mapEditor->SetIsSettingStart(false);
452} 529}
453 530
531void MapeditFrame::OnUndo(wxCommandEvent&)
532{
533 (*currentAction)->endChanges();
534 (*currentAction)->undo();
535 currentAction++;
536
537 UpdateUndoLabels();
538}
539
540void MapeditFrame::OnRedo(wxCommandEvent&)
541{
542 if (currentAction != end(history))
543 {
544 (*currentAction)->endChanges();
545 }
546
547 currentAction--;
548 (*currentAction)->apply();
549
550 UpdateUndoLabels();
551}
552
454void MapeditFrame::NewWorld() 553void MapeditFrame::NewWorld()
455{ 554{
456 LaunchWindow(std::unique_ptr<World>(new World())); 555 LaunchWindow(std::unique_ptr<World>(new World()));
@@ -531,6 +630,10 @@ void MapeditFrame::SelectMap(Map* map)
531{ 630{
532 currentMap = map; 631 currentMap = map;
533 mapEditor->SetMap(map); 632 mapEditor->SetMap(map);
633
634 SetIsAddingEntity(false);
635 SetIsSettingStart(false);
636
534 titleBox->ChangeValue(map->getTitle()); 637 titleBox->ChangeValue(map->getTitle());
535 world->setLastMap(map); 638 world->setLastMap(map);
536} 639}
@@ -592,3 +695,49 @@ void MapeditFrame::SetStartposLabel()
592 695
593 startposLabel->SetLabel(mappos_out.str()); 696 startposLabel->SetLabel(mappos_out.str());
594} 697}
698
699void MapeditFrame::UpdateUndoLabels()
700{
701 if (currentAction != end(history))
702 {
703 menuEdit->SetLabel(MENU_EDIT_UNDO, "Undo " + (*currentAction)->getTitle() + "\tCtrl-Z");
704 menuEdit->Enable(MENU_EDIT_UNDO, true);
705 } else {
706 menuEdit->SetLabel(MENU_EDIT_UNDO, "Undo\tCtrl-Z");
707 menuEdit->Enable(MENU_EDIT_UNDO, false);
708 }
709
710 if (currentAction != begin(history))
711 {
712 menuEdit->SetLabel(MENU_EDIT_REDO, "Redo " + (*std::prev(currentAction))->getTitle() + "\tCtrl-Shift-Z");
713 menuEdit->Enable(MENU_EDIT_REDO, true);
714 } else {
715 menuEdit->SetLabel(MENU_EDIT_REDO, "Redo\tCtrl-Shift-Z");
716 menuEdit->Enable(MENU_EDIT_REDO, false);
717 }
718}
719
720void MapeditFrame::commitAction(std::shared_ptr<Undoable> action)
721{
722 action->apply();
723
724 commitAfter(action);
725}
726
727void MapeditFrame::commitAfter(std::shared_ptr<Undoable> action)
728{
729 if (currentAction != end(history))
730 {
731 (*currentAction)->endChanges();
732 }
733
734 history.erase(begin(history), currentAction);
735 currentAction = history.insert(begin(history), action);
736
737 UpdateUndoLabels();
738
739 if (history.size() > 100)
740 {
741 history.pop_back();
742 }
743}
diff --git a/tools/mapedit/src/frame.h b/tools/mapedit/src/frame.h index 067c848..b0f27e9 100644 --- a/tools/mapedit/src/frame.h +++ b/tools/mapedit/src/frame.h
@@ -14,6 +14,7 @@
14#include <wx/notebook.h> 14#include <wx/notebook.h>
15#include <memory> 15#include <memory>
16#include <wx/treectrl.h> 16#include <wx/treectrl.h>
17#include "undo.h"
17 18
18class MapPtrCtr : public wxTreeItemData { 19class MapPtrCtr : public wxTreeItemData {
19 public: 20 public:
@@ -37,7 +38,6 @@ class MapeditFrame : public wxFrame {
37 38
38 std::list<wxWindow*>::iterator closer; 39 std::list<wxWindow*>::iterator closer;
39 40
40 private:
41 static void LaunchWindow(std::unique_ptr<World> world); 41 static void LaunchWindow(std::unique_ptr<World> world);
42 void populateMapTree(wxTreeItemId node, std::list<std::shared_ptr<Map>> maps); 42 void populateMapTree(wxTreeItemId node, std::list<std::shared_ptr<Map>> maps);
43 void SelectMap(Map* map); 43 void SelectMap(Map* map);
@@ -60,7 +60,6 @@ class MapeditFrame : public wxFrame {
60 void OnAddRoot(wxCommandEvent& event); 60 void OnAddRoot(wxCommandEvent& event);
61 void OnAddChild(wxCommandEvent& event); 61 void OnAddChild(wxCommandEvent& event);
62 void OnDidSelectMap(wxTreeEvent& event); 62 void OnDidSelectMap(wxTreeEvent& event);
63 void OnWillSelectMap(wxTreeEvent& event);
64 void OnWillDragMap(wxTreeEvent& event); 63 void OnWillDragMap(wxTreeEvent& event);
65 void OnDidDragMap(wxTreeEvent& event); 64 void OnDidDragMap(wxTreeEvent& event);
66 void OnRightClickTree(wxTreeEvent& event); 65 void OnRightClickTree(wxTreeEvent& event);
@@ -69,25 +68,44 @@ class MapeditFrame : public wxFrame {
69 68
70 std::unique_ptr<World> world; 69 std::unique_ptr<World> world;
71 Map* currentMap; 70 Map* currentMap;
71
72 MapeditWidget* mapEditor; 72 MapeditWidget* mapEditor;
73 TileWidget* tileEditor; 73 TileWidget* tileEditor;
74 wxTextCtrl* titleBox; 74 wxToolBar* toolbar;
75 std::string filename; 75 wxMenu* menuFile;
76
77 // Notebook
76 wxNotebook* notebook; 78 wxNotebook* notebook;
77 wxChoice* entityTypeBox; 79 wxChoice* entityTypeBox;
78 wxButton* addEntityButton; 80 wxButton* addEntityButton;
79 wxButton* cancelEntityButton; 81 wxButton* cancelEntityButton;
80 wxToolBar* toolbar; 82
81 wxMenu* menuFile; 83 // Map tree
82 wxTreeCtrl* mapTree; 84 wxTreeCtrl* mapTree;
83 wxTreeItemId dragMap; 85 wxTreeItemId dragMap;
84 wxMenu* mapTreePopup; 86 wxMenu* mapTreePopup;
87 bool dontSelectMap = false;
88
89 // Property editor
90 UndoableTextBox* titleBox;
91 wxString prevTitle;
85 wxStaticText* startposLabel; 92 wxStaticText* startposLabel;
86 wxButton* setStartposButton; 93 wxButton* setStartposButton;
87 wxButton* cancelStartposButton; 94 wxButton* cancelStartposButton;
88 95
96 // Undo stuff
97 wxMenu* menuEdit;
98 std::list<std::shared_ptr<Undoable>> history;
99 std::list<std::shared_ptr<Undoable>>::iterator currentAction {begin(history)};
100 void OnUndo(wxCommandEvent& event);
101 void OnRedo(wxCommandEvent& event);
102 void UpdateUndoLabels();
103 void commitAction(std::shared_ptr<Undoable> action);
104 void commitAfter(std::shared_ptr<Undoable> action);
105
89 bool addingEntity = false; 106 bool addingEntity = false;
90 107
108 private:
91 wxDECLARE_EVENT_TABLE(); 109 wxDECLARE_EVENT_TABLE();
92}; 110};
93 111
diff --git a/tools/mapedit/src/map.cpp b/tools/mapedit/src/map.cpp index 0f8826c..0db7031 100644 --- a/tools/mapedit/src/map.cpp +++ b/tools/mapedit/src/map.cpp
@@ -19,6 +19,7 @@ Map::Map(const Map& map)
19 world = map.world; 19 world = map.world;
20 treeItemId = map.treeItemId; 20 treeItemId = map.treeItemId;
21 children = map.children; 21 children = map.children;
22 hidden = map.hidden;
22} 23}
23 24
24Map::Map(Map&& map) : Map(-1, map.world) 25Map::Map(Map&& map) : Map(-1, map.world)
@@ -49,6 +50,7 @@ void swap(Map& first, Map& second)
49 std::swap(first.world, second.world); 50 std::swap(first.world, second.world);
50 std::swap(first.treeItemId, second.treeItemId); 51 std::swap(first.treeItemId, second.treeItemId);
51 std::swap(first.children, second.children); 52 std::swap(first.children, second.children);
53 std::swap(first.hidden, second.hidden);
52} 54}
53 55
54int Map::getID() const 56int Map::getID() const
@@ -118,6 +120,11 @@ World* Map::getWorld() const
118 return world; 120 return world;
119} 121}
120 122
123bool Map::getHidden() const
124{
125 return hidden;
126}
127
121void Map::setTitle(std::string title, bool dirty) 128void Map::setTitle(std::string title, bool dirty)
122{ 129{
123 this->title = title; 130 this->title = title;
@@ -149,7 +156,7 @@ void Map::setMapdata(int* mapdata, bool dirty)
149 } 156 }
150} 157}
151 158
152void Map::addObject(std::shared_ptr<MapObjectEntry>& obj, bool dirty) 159void Map::addObject(std::shared_ptr<MapObjectEntry> obj, bool dirty)
153{ 160{
154 objects.push_back(obj); 161 objects.push_back(obj);
155 162
@@ -159,7 +166,7 @@ void Map::addObject(std::shared_ptr<MapObjectEntry>& obj, bool dirty)
159 } 166 }
160} 167}
161 168
162void Map::removeObject(std::shared_ptr<MapObjectEntry>& obj, bool dirty) 169void Map::removeObject(std::shared_ptr<MapObjectEntry> obj, bool dirty)
163{ 170{
164 objects.remove(obj); 171 objects.remove(obj);
165 172
@@ -203,3 +210,8 @@ void Map::setExpanded(bool exp)
203{ 210{
204 expanded = exp; 211 expanded = exp;
205} 212}
213
214void Map::setHidden(bool hid)
215{
216 hidden = hid;
217}
diff --git a/tools/mapedit/src/map.h b/tools/mapedit/src/map.h index df3e237..34dcb33 100644 --- a/tools/mapedit/src/map.h +++ b/tools/mapedit/src/map.h
@@ -53,7 +53,7 @@ class MapWriteException: public std::exception
53 53
54struct MapObjectEntry { 54struct MapObjectEntry {
55 MapObject* object; 55 MapObject* object;
56 std::pair<double, double> position; 56 std::pair<int, int> position;
57 57
58 bool operator==(MapObjectEntry& other) const 58 bool operator==(MapObjectEntry& other) const
59 { 59 {
@@ -85,17 +85,19 @@ class Map {
85 std::list<std::shared_ptr<Map>> getChildren() const; 85 std::list<std::shared_ptr<Map>> getChildren() const;
86 bool getExpanded() const; 86 bool getExpanded() const;
87 World* getWorld() const; 87 World* getWorld() const;
88 bool getHidden() const;
88 89
89 void setTitle(std::string title, bool dirty = true); 90 void setTitle(std::string title, bool dirty = true);
90 void setTileAt(int x, int y, int tile, bool dirty = true); 91 void setTileAt(int x, int y, int tile, bool dirty = true);
91 void setMapdata(int* mapdata, bool dirty = true); 92 void setMapdata(int* mapdata, bool dirty = true);
92 void addObject(std::shared_ptr<MapObjectEntry>& obj, bool dirty = true); 93 void addObject(std::shared_ptr<MapObjectEntry> obj, bool dirty = true);
93 void removeObject(std::shared_ptr<MapObjectEntry>& obj, bool dirty = true); 94 void removeObject(std::shared_ptr<MapObjectEntry> obj, bool dirty = true);
94 void setLeftmap(int id, bool dirty = true); 95 void setLeftmap(int id, bool dirty = true);
95 void setRightmap(int id, bool dirty = true); 96 void setRightmap(int id, bool dirty = true);
96 void setTreeItemId(wxTreeItemId id); 97 void setTreeItemId(wxTreeItemId id);
97 void addChild(int id); 98 void addChild(int id);
98 void setExpanded(bool exp); 99 void setExpanded(bool exp);
100 void setHidden(bool hid);
99 101
100 private: 102 private:
101 int id; 103 int id;
@@ -108,6 +110,7 @@ class Map {
108 int rightmap = -1; 110 int rightmap = -1;
109 wxTreeItemId treeItemId; 111 wxTreeItemId treeItemId;
110 bool expanded = false; 112 bool expanded = false;
113 bool hidden = false;
111}; 114};
112 115
113#endif 116#endif
diff --git a/tools/mapedit/src/undo.cpp b/tools/mapedit/src/undo.cpp new file mode 100644 index 0000000..bcf7b92 --- /dev/null +++ b/tools/mapedit/src/undo.cpp
@@ -0,0 +1,95 @@
1#include "undo.h"
2#include "frame.h"
3
4Undoable::Undoable(std::string title, std::function<void ()> forward, std::function<void ()> backward)
5{
6 this->title = title;
7 this->forward = forward;
8 this->backward = backward;
9}
10
11std::string Undoable::getTitle() const
12{
13 return title;
14}
15
16void Undoable::apply()
17{
18 forward();
19}
20
21void Undoable::undo()
22{
23 backward();
24}
25
26wxBEGIN_EVENT_TABLE(UndoableTextBox, wxTextCtrl)
27 EVT_SET_FOCUS(UndoableTextBox::OnFocus)
28 EVT_KILL_FOCUS(UndoableTextBox::OnKillFocus)
29wxEND_EVENT_TABLE()
30
31UndoableTextBox::UndoableTextBox(wxWindow* parent, wxWindowID id, wxString startText, std::string undoType, MapeditFrame* realParent, wxPoint pos, wxSize size, long style) : wxTextCtrl(parent, id, startText, pos, size, style)
32{
33 this->undoText = undoType;
34 this->realParent = realParent;
35
36 Init();
37}
38
39void UndoableTextBox::Init()
40{
41 Bind(wxEVT_TEXT, &UndoableTextBox::OnTextChange, this);
42
43 curText = GetValue();
44}
45
46void UndoableTextBox::OnFocus(wxFocusEvent& event)
47{
48 curText = GetValue();
49
50 event.Skip();
51}
52
53void UndoableTextBox::OnKillFocus(wxFocusEvent& event)
54{
55 undo.reset();
56
57 event.Skip();
58}
59
60void UndoableTextBox::OnTextChange(wxCommandEvent& event)
61{
62 if (!undo)
63 {
64 auto tempUndo = std::make_shared<Undo>(undoText, curText, *this);
65
66 realParent->commitAfter(tempUndo);
67
68 undo = tempUndo;
69 }
70
71 undo->newText = GetValue();
72 curText = GetValue();
73
74 event.Skip();
75}
76
77UndoableTextBox::Undo::Undo(std::string title, wxString origText, UndoableTextBox& parent) : origText(origText), parent(parent)
78{
79 this->title = title;
80}
81
82void UndoableTextBox::Undo::apply()
83{
84 parent.ChangeValue(newText);
85}
86
87void UndoableTextBox::Undo::undo()
88{
89 parent.ChangeValue(origText);
90}
91
92void UndoableTextBox::Undo::endChanges()
93{
94 parent.undo.reset();
95}
diff --git a/tools/mapedit/src/undo.h b/tools/mapedit/src/undo.h new file mode 100644 index 0000000..794f993 --- /dev/null +++ b/tools/mapedit/src/undo.h
@@ -0,0 +1,63 @@
1#ifndef UNDO_H
2#define UNDO_H
3
4#include <wx/wxprec.h>
5
6#ifndef WX_PRECOMP
7#include <wx/wx.h>
8#endif
9
10#include <functional>
11#include <string>
12#include <list>
13
14class MapeditFrame;
15
16class Undoable {
17 public:
18 Undoable(std::string title, std::function<void ()> forward, std::function<void ()> backward);
19 virtual std::string getTitle() const;
20 virtual void apply();
21 virtual void undo();
22 virtual void endChanges() {}
23
24 protected:
25 Undoable() {}
26
27 std::string title;
28 std::function<void ()> forward;
29 std::function<void ()> backward;
30};
31
32class UndoableTextBox : public wxTextCtrl {
33 public:
34 UndoableTextBox();
35 UndoableTextBox(wxWindow* parent, wxWindowID id, wxString startText, std::string undoType, MapeditFrame* realParent, wxPoint pos = wxDefaultPosition, wxSize size = wxDefaultSize, long style = 0);
36
37 private:
38 class Undo : public Undoable {
39 public:
40 Undo(std::string title, wxString origText, UndoableTextBox& parent);
41 void apply();
42 void undo();
43 void endChanges();
44
45 wxString origText;
46 wxString newText;
47 UndoableTextBox& parent;
48 };
49
50 void Init();
51 void OnFocus(wxFocusEvent& event);
52 void OnKillFocus(wxFocusEvent& event);
53 void OnTextChange(wxCommandEvent& event);
54
55 MapeditFrame* realParent;
56 std::string undoText;
57 wxString curText;
58 std::shared_ptr<Undo> undo;
59
60 wxDECLARE_EVENT_TABLE();
61};
62
63#endif
diff --git a/tools/mapedit/src/widget.cpp b/tools/mapedit/src/widget.cpp index 6cbedcd..61a8d65 100644 --- a/tools/mapedit/src/widget.cpp +++ b/tools/mapedit/src/widget.cpp
@@ -55,6 +55,11 @@ void MapeditWidget::OnPaint(wxPaintEvent&)
55 for (int x=0; x<MAP_WIDTH; x++) 55 for (int x=0; x<MAP_WIDTH; x++)
56 { 56 {
57 int tile = map->getTileAt(x, y); 57 int tile = map->getTileAt(x, y);
58 if (changeBuffer.find({x,y}) != end(changeBuffer))
59 {
60 tile = tileWidget->getSelected();
61 }
62
58 dc.StretchBlit(x*TILE_WIDTH*scale-vX, y*TILE_HEIGHT*scale-vY, TILE_WIDTH*scale, TILE_HEIGHT*scale, &tiles_dc, tile%8*TILE_WIDTH, tile/8*TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT); 63 dc.StretchBlit(x*TILE_WIDTH*scale-vX, y*TILE_HEIGHT*scale-vY, TILE_WIDTH*scale, TILE_HEIGHT*scale, &tiles_dc, tile%8*TILE_WIDTH, tile/8*TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
59 } 64 }
60 } 65 }
@@ -151,6 +156,21 @@ void MapeditWidget::OnPaint(wxPaintEvent&)
151 dc.SetPen(pen); 156 dc.SetPen(pen);
152 dc.SetBrush(*wxTRANSPARENT_BRUSH); 157 dc.SetBrush(*wxTRANSPARENT_BRUSH);
153 dc.DrawRectangle(pos.x, pos.y, size.GetWidth(), size.GetHeight()); 158 dc.DrawRectangle(pos.x, pos.y, size.GetWidth(), size.GetHeight());
159 } else if ((editMode == EditEntities) && (movingEntity != nullptr))
160 {
161 wxBitmap sprite = movingEntity->object->getSprite();
162 tiles_dc.SelectObject(wxNullBitmap);
163 tiles_dc.SelectObject(sprite);
164
165 wxPoint pos {mousePos.x - movingEntity->object->getWidth()/2*scale, mousePos.y - movingEntity->object->getHeight()/2*scale};
166 wxSize size {movingEntity->object->getWidth()*scale, movingEntity->object->getHeight()*scale};
167
168 dc.StretchBlit(pos.x, pos.y, size.GetWidth(), size.GetHeight(), &tiles_dc, 0, 0, movingEntity->object->getWidth(), movingEntity->object->getHeight());
169
170 wxPen pen(*wxGREEN, 2);
171 dc.SetPen(pen);
172 dc.SetBrush(*wxTRANSPARENT_BRUSH);
173 dc.DrawRectangle(pos.x, pos.y, size.GetWidth(), size.GetHeight());
154 } 174 }
155 } 175 }
156} 176}
@@ -167,7 +187,8 @@ void MapeditWidget::SetTile(wxPoint pos)
167 int x = (pos.x + vX) / (TILE_WIDTH * scale); 187 int x = (pos.x + vX) / (TILE_WIDTH * scale);
168 int y = (pos.y + vY) / (TILE_HEIGHT * scale); 188 int y = (pos.y + vY) / (TILE_HEIGHT * scale);
169 189
170 map->setTileAt(x, y, tileWidget->getSelected()); 190 changeBuffer.insert({x,y});
191
171 Refresh(); 192 Refresh();
172} 193}
173 194
@@ -191,17 +212,42 @@ void MapeditWidget::OnClick(wxMouseEvent& event)
191 { 212 {
192 int x = (event.GetPosition().x + vX) / scale - (addingEntity->getWidth() / 2); 213 int x = (event.GetPosition().x + vX) / scale - (addingEntity->getWidth() / 2);
193 int y = (event.GetPosition().y + vY) / scale - (addingEntity->getHeight() / 2); 214 int y = (event.GetPosition().y + vY) / scale - (addingEntity->getHeight() / 2);
194 215
195 auto data = std::make_shared<MapObjectEntry>(); 216 auto data = std::make_shared<MapObjectEntry>();
196 data->object = addingEntity; 217 data->object = addingEntity;
197 data->position = std::make_pair(x,y); 218 data->position = std::make_pair(x,y);
198 map->addObject(data);
199 219
200 addingEntity = nullptr; 220 frame->commitAction(std::make_shared<Undoable>("Add " + addingEntity->getType(), [=] () {
221 map->addObject(data);
222
223 Refresh();
224 }, [=] () {
225 map->removeObject(data);
226
227 Refresh();
228 }));
201 229
202 frame->SetIsAddingEntity(false); 230 frame->SetIsAddingEntity(false);
231 addingEntity = nullptr;
232 } else if (movingEntity != nullptr)
233 {
234 int x = (event.GetPosition().x + vX) / scale - (movingEntity->object->getWidth() / 2);
235 int y = (event.GetPosition().y + vY) / scale - (movingEntity->object->getHeight() / 2);
236 auto oldPos = movingEntity->position;
237 MapObjectEntry* me = movingEntity;
203 238
204 Refresh(); 239 frame->commitAction(std::make_shared<Undoable>("Move " + movingEntity->object->getType(), [=] () {
240 me->position = std::make_pair(x,y);
241
242 Refresh();
243 }, [=] () {
244 me->position = oldPos;
245
246 Refresh();
247 }));
248
249 frame->SetIsAddingEntity(false);
250 movingEntity = nullptr;
205 } else { 251 } else {
206 int x = (event.GetPosition().x + vX) / scale; 252 int x = (event.GetPosition().x + vX) / scale;
207 int y = (event.GetPosition().y + vY) / scale; 253 int y = (event.GetPosition().y + vY) / scale;
@@ -211,9 +257,7 @@ void MapeditWidget::OnClick(wxMouseEvent& event)
211 if ((x > selectedEntity->position.first) && (x < selectedEntity->position.first + selectedEntity->object->getWidth()) 257 if ((x > selectedEntity->position.first) && (x < selectedEntity->position.first + selectedEntity->object->getWidth())
212 && (y > selectedEntity->position.second) && (y < selectedEntity->position.second + selectedEntity->object->getHeight())) 258 && (y > selectedEntity->position.second) && (y < selectedEntity->position.second + selectedEntity->object->getHeight()))
213 { 259 {
214 addingEntity = selectedEntity->object; 260 movingEntity = selectedEntity.get();
215 map->removeObject(selectedEntity);
216 selectedEntity.reset();
217 frame->SetIsAddingEntity(true); 261 frame->SetIsAddingEntity(true);
218 } else { 262 } else {
219 selectedEntity.reset(); 263 selectedEntity.reset();
@@ -241,12 +285,23 @@ void MapeditWidget::OnClick(wxMouseEvent& event)
241 { 285 {
242 int x = (event.GetPosition().x + vX) / scale - (PLAYER_WIDTH[currentPlayer] / 2); 286 int x = (event.GetPosition().x + vX) / scale - (PLAYER_WIDTH[currentPlayer] / 2);
243 int y = (event.GetPosition().y + vY) / scale - (PLAYER_HEIGHT[currentPlayer] / 2); 287 int y = (event.GetPosition().y + vY) / scale - (PLAYER_HEIGHT[currentPlayer] / 2);
288 auto oldPos = map->getWorld()->getStartingPosition();
289 auto oldSMap = map->getWorld()->getStartingMap();
290
291 frame->commitAction(std::make_shared<Undoable>("Set Starting Position", [=] () {
292 map->getWorld()->setStart(map, {x, y});
293 frame->SetStartposLabel();
294
295 Refresh();
296 }, [=] () {
297 map->getWorld()->setStart(oldSMap, oldPos);
298 frame->SetStartposLabel();
299
300 Refresh();
301 }));
244 302
245 map->getWorld()->setStart(map, {x, y});
246 isSettingPos = false; 303 isSettingPos = false;
247 frame->SetIsSettingStart(false); 304 frame->SetIsSettingStart(false);
248
249 Refresh();
250 } 305 }
251 306
252 event.Skip(); 307 event.Skip();
@@ -299,6 +354,34 @@ void MapeditWidget::OnMouseMove(wxMouseEvent& event)
299void MapeditWidget::OnMouseUp(wxMouseEvent&) 354void MapeditWidget::OnMouseUp(wxMouseEvent&)
300{ 355{
301 mouseIsDown = false; 356 mouseIsDown = false;
357
358 if (editMode == EditTiles)
359 {
360 std::map<std::pair<int, int>, int> localChangeBuffer;
361 for (auto assign : changeBuffer)
362 {
363 localChangeBuffer[assign] = map->getTileAt(assign.first, assign.second);
364 }
365
366 int localSelection = tileWidget->getSelected();
367 frame->commitAction(std::make_shared<Undoable>("Paint Map", [=] () {
368 for (auto assign : localChangeBuffer)
369 {
370 map->setTileAt(assign.first.first, assign.first.second, localSelection);
371 }
372
373 Refresh();
374 }, [=] () {
375 for (auto assign : localChangeBuffer)
376 {
377 map->setTileAt(assign.first.first, assign.first.second, assign.second);
378 }
379
380 Refresh();
381 }));
382
383 changeBuffer.clear();
384 }
302} 385}
303 386
304void MapeditWidget::OnMouseOut(wxMouseEvent&) 387void MapeditWidget::OnMouseOut(wxMouseEvent&)
@@ -350,6 +433,7 @@ void MapeditWidget::StartAddingEntity(MapObject* object)
350void MapeditWidget::CancelAddingEntity() 433void MapeditWidget::CancelAddingEntity()
351{ 434{
352 addingEntity = nullptr; 435 addingEntity = nullptr;
436 movingEntity = nullptr;
353} 437}
354 438
355void MapeditWidget::SetIsSettingStart(bool isSetting) 439void MapeditWidget::SetIsSettingStart(bool isSetting)
@@ -369,6 +453,10 @@ void MapeditWidget::SetIsSettingStart(bool isSetting)
369void MapeditWidget::SetMap(Map* map) 453void MapeditWidget::SetMap(Map* map)
370{ 454{
371 this->map = map; 455 this->map = map;
456 selectedEntity = nullptr;
457 addingEntity = nullptr;
458 movingEntity = nullptr;
459 isSettingPos = false;
372 460
373 Refresh(); 461 Refresh();
374} 462}
diff --git a/tools/mapedit/src/widget.h b/tools/mapedit/src/widget.h index 67ebc01..4ae22c7 100644 --- a/tools/mapedit/src/widget.h +++ b/tools/mapedit/src/widget.h
@@ -11,6 +11,8 @@
11#include "tile_widget.h" 11#include "tile_widget.h"
12#include <list> 12#include <list>
13#include <memory> 13#include <memory>
14#include <utility>
15#include <set>
14 16
15class MapeditFrame; 17class MapeditFrame;
16 18
@@ -58,8 +60,10 @@ class MapeditWidget : public wxScrolledWindow {
58 EditMode editMode = EditTiles; 60 EditMode editMode = EditTiles;
59 int currentPlayer = 0; 61 int currentPlayer = 0;
60 bool isSettingPos = false; 62 bool isSettingPos = false;
63 std::set<std::pair<int,int>> changeBuffer;
61 64
62 MapObject* addingEntity = nullptr; 65 MapObject* addingEntity = nullptr;
66 MapObjectEntry* movingEntity = nullptr;
63 std::shared_ptr<MapObjectEntry> selectedEntity; 67 std::shared_ptr<MapObjectEntry> selectedEntity;
64 68
65 DECLARE_DYNAMIC_CLASS(MapeditWidget) 69 DECLARE_DYNAMIC_CLASS(MapeditWidget)
diff --git a/tools/mapedit/src/world.cpp b/tools/mapedit/src/world.cpp index 3145e4e..db1201e 100644 --- a/tools/mapedit/src/world.cpp +++ b/tools/mapedit/src/world.cpp
@@ -135,7 +135,7 @@ World::World(std::string filename)
135 } else if (!xmlStrcmp(entityDataNode->name, (const xmlChar*) "entity-position")) 135 } else if (!xmlStrcmp(entityDataNode->name, (const xmlChar*) "entity-position"))
136 { 136 {
137 xmlChar* key = xmlNodeListGetString(doc, entityDataNode->xmlChildrenNode, 1); 137 xmlChar* key = xmlNodeListGetString(doc, entityDataNode->xmlChildrenNode, 1);
138 sscanf((char*) key, "%lf,%lf", &data->position.first, &data->position.second); 138 sscanf((char*) key, "%d,%d", &data->position.first, &data->position.second);
139 xmlFree(key); 139 xmlFree(key);
140 } 140 }
141 } 141 }
@@ -268,6 +268,8 @@ void World::save(std::string name, wxTreeCtrl* mapTree)
268 { 268 {
269 Map& map = *mapPair.second; 269 Map& map = *mapPair.second;
270 270
271 if (map.getHidden()) continue;
272
271 // <map> 273 // <map>
272 rc = xmlTextWriterStartElement(writer, (xmlChar*) "map"); 274 rc = xmlTextWriterStartElement(writer, (xmlChar*) "map");
273 if (rc < 0) throw MapWriteException(name); 275 if (rc < 0) throw MapWriteException(name);