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