1// SuperTux
2// Copyright (C) 2018 Ingo Ruhnke <grumbel@gmail.com>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17#include "editor/undo_manager.hpp"
18
19#include <sstream>
20#include <iostream>
21
22#include "editor/editor.hpp"
23#include "supertux/level.hpp"
24#include "supertux/level_parser.hpp"
25#include "util/log.hpp"
26#include "util/reader_mapping.hpp"
27
28UndoManager::UndoManager() :
29 m_max_snapshots(100),
30 m_index_pos(),
31 m_undo_stack(),
32 m_redo_stack()
33{
34}
35
36void
37UndoManager::try_snapshot(Level& level)
38{
39 std::ostringstream out;
40 level.save(out);
41 std::string level_snapshot = out.str();
42
43 if (m_undo_stack.empty())
44 {
45 push_undo_stack(std::move(level_snapshot));
46 }
47 else if (level_snapshot == m_undo_stack.back())
48 {
49 log_debug << "skipping snapshot as nothing has changed" << std::endl;
50 }
51 else // level_snapshot changed
52 {
53 push_undo_stack(std::move(level_snapshot));
54 }
55}
56
57void
58UndoManager::debug_print(const char* action)
59{
60 if ((false))
61 {
62 std::cout << action << std::endl;
63 std::cout << "undo_stack: ";
64 for(size_t i = 0; i < m_undo_stack.size(); ++i) {
65 std::cout << static_cast<const void*>(m_undo_stack[i].data()) << " ";
66 }
67 std::cout << std::endl;
68
69 std::cout << "redo_stack: ";
70 for(size_t i = 0; i < m_redo_stack.size(); ++i) {
71 std::cout << static_cast<const void*>(m_redo_stack[i].data()) << " ";
72 }
73 std::cout << std::endl;
74 std::cout << std::endl;
75 }
76}
77
78void
79UndoManager::push_undo_stack(std::string&& level_snapshot)
80{
81 log_info << "doing snapshot" << std::endl;
82
83 m_redo_stack.clear();
84 m_undo_stack.push_back(std::move(level_snapshot));
85 m_index_pos += 1;
86
87 cleanup();
88
89 debug_print("snapshot");
90}
91
92void
93UndoManager::cleanup()
94{
95 while (m_undo_stack.size() > m_max_snapshots) {
96 m_undo_stack.erase(m_undo_stack.end() - m_max_snapshots,
97 m_undo_stack.end());
98 }
99}
100
101std::unique_ptr<Level>
102UndoManager::undo()
103{
104 if (m_undo_stack.size() < 2) return {};
105
106 m_redo_stack.push_back(std::move(m_undo_stack.back()));
107 m_undo_stack.pop_back();
108
109 std::istringstream in(m_undo_stack.back());
110 ReaderMapping::s_translations_enabled = false;
111 auto level = LevelParser::from_stream(in, "<undo_stack>", Editor::current()->get_level()->is_worldmap(), true);
112 ReaderMapping::s_translations_enabled = true;
113
114 m_index_pos -= 1;
115
116 debug_print("undo");
117
118 return level;
119}
120
121std::unique_ptr<Level>
122UndoManager::redo()
123{
124 if (m_redo_stack.empty()) return {};
125
126 m_undo_stack.push_back(std::move(m_redo_stack.back()));
127 m_redo_stack.pop_back();
128
129 m_index_pos += 1;
130
131 std::istringstream in(m_undo_stack.back());
132 ReaderMapping::s_translations_enabled = false;
133 auto level = LevelParser::from_stream(in, "<redo_stack>", Editor::current()->get_level()->is_worldmap(), true);
134 ReaderMapping::s_translations_enabled = true;
135
136 debug_print("redo");
137
138 return level;
139}
140
141/* EOF */
142