1 /* Copyright (C) 2010 Wildfire Games.
2 * This file is part of 0 A.D.
3 *
4 * 0 A.D. 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 2 of the License, or
7 * (at your option) any later version.
8 *
9 * 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "precompiled.h"
19
20 #include "CommandProc.h"
21
22 #include <cassert>
23 #include <algorithm>
24
25 //////////////////////////////////////////////////////////////////////////
26
next_it(T x)27 template<typename T> T next_it(T x) { T t = x; return ++t; }
28
delete_fn(T * v)29 template<typename T> void delete_fn(T* v) { delete v; }
30
31 //////////////////////////////////////////////////////////////////////////
32
33 using namespace AtlasMessage;
34
35 namespace AtlasMessage {
36
GetCommandProc()37 CommandProc& GetCommandProc()
38 {
39 static CommandProc commandProc;
40 return commandProc;
41 }
42
GetCmdHandlers()43 cmdHandlers& GetCmdHandlers()
44 {
45 static cmdHandlers h;
46 return h;
47 }
48 }
49
50
CommandProc()51 CommandProc::CommandProc()
52 {
53 // Start the list with a NULL, so m_CurrentCommand can point at
54 // something even when the command stack is empty
55 m_Commands.push_back(NULL);
56
57 m_CurrentCommand = m_Commands.begin();
58 }
59
~CommandProc()60 CommandProc::~CommandProc()
61 {
62 // Make sure Destroy has been called before now (to avoid
63 // problems from the destruction order of static variables)
64 ENSURE(!m_Commands.size());
65 }
66
Destroy()67 void CommandProc::Destroy()
68 {
69 std::for_each(m_Commands.begin(), m_Commands.end(), delete_fn<Command>);
70 m_Commands.clear();
71 }
72
Submit(Command * cmd)73 void CommandProc::Submit(Command* cmd)
74 {
75 // If some commands have been undone at the time we insert this new one,
76 // delete and remove them all.
77 std::for_each(next_it(m_CurrentCommand), m_Commands.end(), delete_fn<Command>);
78 m_Commands.erase(next_it(m_CurrentCommand), m_Commands.end());
79 assert(next_it(m_CurrentCommand) == m_Commands.end());
80
81 m_CurrentCommand = m_Commands.insert(next_it(m_CurrentCommand), cmd);
82
83 (*m_CurrentCommand)->Do();
84 }
85
Undo()86 void CommandProc::Undo()
87 {
88 if (m_CurrentCommand != m_Commands.begin())
89 {
90 (*m_CurrentCommand)->Undo();
91 --m_CurrentCommand;
92 }
93 }
94
Redo()95 void CommandProc::Redo()
96 {
97 if (next_it(m_CurrentCommand) != m_Commands.end())
98 {
99 ++m_CurrentCommand;
100 (*m_CurrentCommand)->Redo();
101 }
102 }
103
Merge()104 void CommandProc::Merge()
105 {
106 if (m_CurrentCommand == m_Commands.begin())
107 {
108 debug_warn(L"Merge illogic: no commands");
109 return;
110 }
111
112 if (next_it(m_CurrentCommand) != m_Commands.end())
113 {
114 debug_warn(L"Merge illogic: not at stack top");
115 return;
116 }
117
118 cmdIt prev = m_CurrentCommand;
119 --prev;
120
121 if (prev == m_Commands.begin())
122 {
123 debug_warn(L"Merge illogic: only 1 command");
124 return;
125 }
126
127 if ((*prev)->GetType() != (*m_CurrentCommand)->GetType())
128 {
129 const char* a = (*prev)->GetType();
130 const char* b = (*m_CurrentCommand)->GetType();
131 debug_printf("[incompatible: %s -> %s]\n", a, b);
132 debug_warn(L"Merge illogic: incompatible command");
133 return;
134 }
135
136 (*m_CurrentCommand)->Merge(*prev);
137
138 delete *m_CurrentCommand;
139 m_Commands.erase(m_CurrentCommand);
140
141 m_CurrentCommand = prev;
142 }
143