1 /*
2 * Copyright 2012, 2013 Thomas Schöps
3 * Copyright 2014 Kai Pastor
4 *
5 * This file is part of OpenOrienteering.
6 *
7 * OpenOrienteering is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * OpenOrienteering is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with OpenOrienteering. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21
22 #include "undo.h"
23
24 #include <vector>
25
26 #include <QXmlStreamReader>
27
28 #include "object_undo.h"
29 #include "map_part_undo.h"
30 #include "util/xml_stream_util.h"
31
32
33 namespace literal
34 {
35 const QLatin1String step("step");
36 const QLatin1String steps("steps");
37 }
38
39
40
41 namespace OpenOrienteering {
42
43 // ### UndoStep ###
44
45 // static
getUndoStepForType(Type type,Map * map)46 UndoStep* UndoStep::getUndoStepForType(Type type, Map* map)
47 {
48 switch (type)
49 {
50 case CombinedUndoStepType:
51 return new CombinedUndoStep(map);
52
53 case ReplaceObjectsUndoStepType:
54 return new ReplaceObjectsUndoStep(map);
55
56 case DeleteObjectsUndoStepType:
57 return new DeleteObjectsUndoStep(map);
58
59 case AddObjectsUndoStepType:
60 return new AddObjectsUndoStep(map);
61
62 case SwitchSymbolUndoStepType:
63 return new SwitchSymbolUndoStep(map);
64
65 case SwitchDashesUndoStepType:
66 return new SwitchDashesUndoStep(map);
67
68 case ValidNoOpUndoStepType:
69 return new NoOpUndoStep(map, true);
70
71 case ObjectTagsUndoStepType:
72 return new ObjectTagsUndoStep(map);
73
74 case SwitchPartUndoStepType:
75 return new SwitchPartUndoStep(map);
76
77 case MapPartUndoStepType:
78 return new MapPartUndoStep(map);
79
80 default:
81 qWarning("Undefined undo step type");
82 Q_FALLTHROUGH();
83 case SwitchPartUndoStepTypeV0:
84 return new NoOpUndoStep(map, false);
85 }
86 }
87
88
UndoStep(Type type,Map * map)89 UndoStep::UndoStep(Type type, Map* map)
90 : type(type)
91 , map(map)
92 {
93 ; // nothing else
94 }
95
~UndoStep()96 UndoStep::~UndoStep()
97 {
98 ; // nothing
99 }
100
isValid() const101 bool UndoStep::isValid() const
102 {
103 return true;
104 }
105
getModifiedParts(PartSet & out) const106 bool UndoStep::getModifiedParts(PartSet& out) const
107 {
108 Q_UNUSED(out);
109 return false;
110 }
111
getModifiedObjects(int,ObjectSet &) const112 void UndoStep::getModifiedObjects(int, ObjectSet&) const
113 {
114 ; // nothing
115 }
116
117 // static
load(QXmlStreamReader & xml,Map * map,SymbolDictionary & symbol_dict)118 UndoStep* UndoStep::load(QXmlStreamReader& xml, Map* map, SymbolDictionary& symbol_dict)
119 {
120 Q_ASSERT(xml.name() == QLatin1String("step"));
121
122 int type = xml.attributes().value(QLatin1String("type")).toInt();
123 UndoStep* step = UndoStep::getUndoStepForType((Type)type, map);
124 while (xml.readNextStartElement())
125 step->loadImpl(xml, symbol_dict);
126
127 return step;
128 }
129
save(QXmlStreamWriter & xml) const130 void UndoStep::save(QXmlStreamWriter& xml) const
131 {
132 XmlElementWriter element(xml, QLatin1String("step"));
133 element.writeAttribute(QLatin1String("type"), type);
134 saveImpl(xml);
135 }
136
saveImpl(QXmlStreamWriter & xml) const137 void UndoStep::saveImpl(QXmlStreamWriter& xml) const
138 {
139 Q_UNUSED(xml);
140 ; // nothing yet
141 }
142
loadImpl(QXmlStreamReader & xml,SymbolDictionary & symbol_dict)143 void UndoStep::loadImpl(QXmlStreamReader& xml, SymbolDictionary &symbol_dict)
144 {
145 Q_UNUSED(symbol_dict);
146
147 xml.skipCurrentElement(); // unknown
148 }
149
150
151
152 // ### CombinedUndoStep ###
153
CombinedUndoStep(Map * map)154 CombinedUndoStep::CombinedUndoStep(Map* map)
155 : UndoStep(CombinedUndoStepType, map)
156 {
157 ; // nothing else
158 }
159
~CombinedUndoStep()160 CombinedUndoStep::~CombinedUndoStep()
161 {
162 for (auto* step : steps)
163 delete step;
164 }
165
isValid() const166 bool CombinedUndoStep::isValid() const
167 {
168 return std::all_of(begin(steps), end(steps), [](const auto& step) {
169 return step->isValid();
170 });
171 }
172
undo()173 UndoStep* CombinedUndoStep::undo()
174 {
175 CombinedUndoStep* undo_step = new CombinedUndoStep(map);
176 undo_step->steps.reserve(steps.size());
177 std::for_each(steps.rbegin(), steps.rend(), [undo_step](auto step) {
178 undo_step->push(step->undo());
179 });
180 return undo_step;
181 }
182
getModifiedParts(PartSet & out) const183 bool CombinedUndoStep::getModifiedParts(PartSet &out) const
184 {
185 for (const auto* step : steps)
186 {
187 step->getModifiedParts(out);
188 }
189 return !out.empty();
190 }
191
getModifiedObjects(int part_index,ObjectSet & out) const192 void CombinedUndoStep::getModifiedObjects(int part_index, ObjectSet &out) const
193 {
194 for (const auto* step : steps)
195 {
196 step->getModifiedObjects(part_index, out);
197 }
198 }
199
200
201
saveImpl(QXmlStreamWriter & xml) const202 void CombinedUndoStep::saveImpl(QXmlStreamWriter& xml) const
203 {
204 UndoStep::saveImpl(xml);
205
206 // From Mapper 0.6, use the same XML element and order as UndoManager.
207 // (A barrier element prevents older versions from loading this element.)
208 XmlElementWriter steps_element(xml, literal::steps);
209 steps_element.writeAttribute(XmlStreamLiteral::count, steps.size());
210 for (const auto* step : steps)
211 step->save(xml);
212 }
213
loadImpl(QXmlStreamReader & xml,SymbolDictionary & symbol_dict)214 void CombinedUndoStep::loadImpl(QXmlStreamReader& xml, SymbolDictionary& symbol_dict)
215 {
216 if (xml.name() == QLatin1String("substeps"))
217 {
218 // Mapper before 0.6
219 // @todo Remove in a future version
220 int size = xml.attributes().value(QLatin1String("count")).toInt();
221 steps.reserve(qMin(size, 10)); // 10 is not a limit
222 while (xml.readNextStartElement())
223 {
224 if (xml.name() == literal::step)
225 steps.insert(steps.begin(), UndoStep::load(xml, map, symbol_dict));
226 else
227 xml.skipCurrentElement(); // unknown
228 }
229 }
230 else if (xml.name() == literal::steps)
231 {
232 // From Mapper 0.6, use the same XML element and order as UndoManager.
233 XmlElementReader steps_element(xml);
234 std::size_t size = steps_element.attribute<std::size_t>(XmlStreamLiteral::count);
235 steps.reserve(qMin(size, (std::size_t)50)); // 50 is not a limit
236 while (xml.readNextStartElement())
237 {
238 if (xml.name() == literal::step)
239 steps.push_back(UndoStep::load(xml, map, symbol_dict));
240 else
241 xml.skipCurrentElement(); // unknown
242 }
243 }
244 else
245 UndoStep::loadImpl(xml, symbol_dict);
246 }
247
248
249
250 // ### InvalidUndoStep ###
251
NoOpUndoStep(Map * map,bool valid)252 NoOpUndoStep::NoOpUndoStep(Map *map, bool valid)
253 : UndoStep(valid ? ValidNoOpUndoStepType : InvalidUndoStepType, map)
254 , valid(valid)
255 {
256 // nothing else
257 }
258
~NoOpUndoStep()259 NoOpUndoStep::~NoOpUndoStep()
260 {
261 // nothing
262 }
263
isValid() const264 bool NoOpUndoStep::isValid() const
265 {
266 return valid;
267 }
268
undo()269 UndoStep* NoOpUndoStep::undo()
270 {
271 if (!valid)
272 qWarning("InvalidUndoStep::undo() must not be called");
273
274 return new NoOpUndoStep(map, true);
275 }
276
277
278 } // namespace OpenOrienteering
279
280