Name change
[texmacs.git] / src / src / Data / History / archiver.cpp
blob373e4355557903b687f9b1673d5e0d5b93cee5d9
2 /******************************************************************************
3 * MODULE : archiver.cpp
4 * DESCRIPTION: undo/redo history
5 * COPYRIGHT : (C) 2009 Joris van der Hoeven
6 *******************************************************************************
7 * This software falls under the GNU general public license version 3 or later.
8 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
9 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
10 ******************************************************************************/
12 #include "archiver.hpp"
14 extern tree the_et;
15 static patch make_compound (array<patch> a);
16 static patch make_branches (array<patch> a);
18 /******************************************************************************
19 * Constructors and destructors
20 ******************************************************************************/
22 archiver_rep::archiver_rep ():
23 archive (make_branches (0)),
24 current (make_compound (0)),
25 depth (0),
26 last_save (0),
27 last_autosave (0) {}
28 archiver_rep::~archiver_rep () {}
29 archiver::archiver (): rep (tm_new<archiver_rep> ()) {}
31 void
32 archiver_rep::clear () {
33 archive= make_branches (0);
34 current= make_compound (0);
35 depth= 0;
36 last_save= -1;
37 last_autosave= -1;
40 /******************************************************************************
41 * Useful subroutines
42 ******************************************************************************/
44 static patch
45 make_compound (array<patch> a) {
46 if (N(a) == 1) return a[0];
47 else return patch (false, a);
50 static patch
51 make_branches (array<patch> a) {
52 if (N(a) == 1) return a[0];
53 else return patch (true, a);
56 static patch
57 append_branches (patch p1, patch p2) {
58 return make_branches (append (branches (p1), branches (p2)));
61 static patch
62 make_history (patch undo, patch redo) {
63 array<patch> a;
64 a << undo << branches (redo);
65 return make_branches (a);
68 static patch
69 get_undo (patch p) {
70 ASSERT (nr_branches (p) > 0, "undo part unavailable");
71 return branch (p, 0);
74 static patch
75 get_redo (patch p) {
76 if (nr_branches (p) == 0) return make_branches (array<patch> ());
77 return make_branches (branches (p, 1, nr_branches (p)));
80 static patch
81 car (patch p) {
82 ASSERT (nr_children (branch (p, 0)) == 2, "car unavailable")
83 return child (p, 0);
86 static patch
87 cdr (patch p) {
88 ASSERT (nr_children (branch (p, 0)) == 2, "cdr unavailable")
89 return child (p, 1);
92 /******************************************************************************
93 * Internal subroutines
94 ******************************************************************************/
96 void
97 archiver_rep::apply (patch p) {
98 // apply a patch, while disabling versioning during the modifications
99 // cout << "Apply " << p << "\n";
100 ASSERT (is_applicable (p, the_et), "invalid history");
101 bool old= versioning_busy;
102 versioning_busy= true;
103 ::apply (p, the_et);
104 versioning_busy= old;
108 static patch
109 expose (patch history, double author) {
110 if (N (history) == 2 &&
111 get_author (history[0]) != author &&
112 N (history[1]) == 2)
114 patch p1= history[0];
115 patch hh= expose (history[1], author);
116 patch p2= hh[0];
117 if (get_author (p2) != author) return history;
118 cout << "p1 < " << p1 << "\n";
119 cout << "p2 < " << p2 << "\n";
120 if (!swap (p1, p2)) return history;
121 cout << "p1 > " << p1 << "\n";
122 cout << "p2 > " << p2 << "\n";
123 return patch (p1, patch (p2, hh[1]));
125 else return history;
129 void
130 archiver_rep::show_all () {
131 cout << HRULE << archive << LF << HRULE << LF;
134 /******************************************************************************
135 * Routines concerning the current modifications
136 ******************************************************************************/
138 void
139 archiver_rep::add (patch p) {
140 // cout << "Add [" << get_author () << "] " << p << "\n";
141 patch q= copy (invert (p, the_et));
142 current= patch (q, current);
145 void
146 archiver_rep::start_slave (double a) {
147 patch q (a, false);
148 current= patch (q, current);
151 bool
152 archiver_rep::active () {
153 return nr_children (current) != 0;
156 bool
157 archiver_rep::has_history () {
158 if (nr_branches (archive) == 0) return false;
159 else return nr_branches (get_undo (archive)) != 0;
162 void
163 archiver_rep::cancel () {
164 if (active ()) {
165 // cout << "Cancel " << current << "\n";
166 apply (current);
167 current= make_compound (0);
171 void
172 archiver_rep::confirm () {
173 if (active ()) {
174 current= patch (get_author (), compactify (current));
175 // cout << "Confirm " << current << "\n";
176 archive= patch (current, archive);
177 current= make_compound (0);
178 depth++;
179 if (depth <= last_save) last_save= -1;
180 if (depth <= last_autosave) last_autosave= -1;
181 //show_all ();
185 void
186 archiver_rep::retract () {
187 if (has_history ()) {
188 patch un= car (get_undo (archive));
189 patch re= get_redo (archive);
190 patch nx= cdr (get_undo (archive));
191 //cout << "Retract " << un << "\n";
192 if (active ()) current= compactify (patch (current, un));
193 else current= un;
194 if (nr_branches (re) != 0) {
195 patch q= invert (current, the_et);
196 re= patch (q, re);
198 if (nr_branches (nx) != 0) nx= get_undo (nx);
199 archive= make_history (nx, append_branches (re, get_redo (nx)));
200 depth--;
201 //show_all ();
205 void
206 archiver_rep::forget () {
207 cancel ();
208 retract ();
209 cancel ();
212 /******************************************************************************
213 * History simplification
214 ******************************************************************************/
216 void
217 archiver_rep::simplify () {
218 if (has_history () &&
219 nr_branches (cdr (get_undo (archive))) == 1 &&
220 nr_children (get_undo (cdr (get_undo (archive)))) == 2)
222 patch p1= car (get_undo (archive));
223 patch p2= car (get_undo (cdr (get_undo (archive))));
224 //cout << "p1= " << p1 << "\n";
225 //cout << "p2= " << p2 << "\n";
226 bool r= join (p1, p2, the_et);
227 //cout << "pr= " << p1 << "\n";
228 if (r) {
229 patch un= patch (p1, cdr (get_undo (cdr (get_undo (archive)))));
230 patch re= get_redo (archive);
231 archive= make_history (un, re);
232 //show_all ();
233 depth--;
234 simplify ();
239 /******************************************************************************
240 * Undo and redo
241 ******************************************************************************/
244 archiver_rep::undo_possibilities () {
245 if (has_history ()) return 1;
246 else return 0;
250 archiver_rep::redo_possibilities () {
251 if (nr_branches (archive) == 0) return 0;
252 else return nr_branches (get_redo (archive));
255 path
256 archiver_rep::undo_one (int i) {
257 if (active ()) return path ();
258 if (undo_possibilities () != 0) {
259 ASSERT (i == 0, "index out of range");
260 patch p= car (get_undo (archive));
261 //cout << "p= " << p << "\n";
262 ASSERT (is_applicable (p, the_et), "history corrupted");
263 patch q= invert (p, the_et);
264 //cout << "q= " << q << "\n";
265 apply (p);
266 patch r= patch (q, get_redo (archive));
267 //cout << "r= " << r << "\n";
268 patch nx= cdr (get_undo (archive));
269 if (nr_branches (nx) != 0) nx= get_undo (nx);
270 patch s= append_branches (r, get_redo (nx));
271 archive= make_history (nx, s);
272 depth--;
273 //show_all ();
274 return cursor_hint (q, the_et);
276 return path ();
279 path
280 archiver_rep::redo_one (int i) {
281 if (active ()) return path ();
282 int n= redo_possibilities ();
283 if (n != 0) {
284 ASSERT (i >= 0 && i < n, "index out of range");
285 patch un= get_undo (archive);
286 patch re= get_redo (archive);
287 patch p= car (branch (re, i));
288 //cout << "p= " << p << "\n";
289 ASSERT (is_applicable (p, the_et), "future corrupted");
290 patch q= invert (p, the_et);
291 //cout << "q= " << q << "\n";
292 apply (p);
293 patch other= make_branches (append (branches (re, 0, i),
294 branches (re, i+1, n)));
295 //cout << "other= " << other << "\n";
296 patch next= make_history (un, other);
297 //cout << "next= " << next << "\n";
298 archive= make_history (patch (q, next), cdr (branch (re, i)));
299 if (depth <= last_save && i != 0) last_save= -1;
300 if (depth <= last_autosave && i != 0) last_autosave= -1;
301 depth++;
302 //show_all ();
303 return cursor_hint (q, the_et);
305 return path ();
308 path
309 archiver_rep::undo (int i) {
310 if (active ()) return path ();
311 double author= get_author ();
312 //before= expose (before, author);
313 while (undo_possibilities () != 0) {
314 ASSERT (i == 0, "index out of range");
315 if (get_author (car (get_undo (archive))) == author)
316 return undo_one (i);
317 else {
318 (void) undo_one (i);
319 i= 0;
322 return path ();
325 path
326 archiver_rep::redo (int i) {
327 if (active ()) return path ();
328 path r;
329 double author= get_author ();
330 while (redo_possibilities () != 0) {
331 ASSERT (i >= 0 && i < redo_possibilities (), "index out of range");
332 bool ok= (get_author (car (branch (get_redo (archive), i))) == author);
333 path p= redo_one (i);
334 if (ok) r= p;
335 i= 0;
336 if (redo_possibilities () == 0) break;
337 if (get_author (car (branch (get_redo (archive), i))) == author) break;
339 return r;
342 /******************************************************************************
343 * Check changes since last save/autosave
344 ******************************************************************************/
346 void
347 archiver_rep::require_save () {
348 last_save= -1;
351 void
352 archiver_rep::notify_save () {
353 last_save= depth;
356 bool
357 archiver_rep::conform_save () {
358 return last_save == depth;
361 void
362 archiver_rep::require_autosave () {
363 last_autosave= -1;
366 void
367 archiver_rep::notify_autosave () {
368 last_autosave= depth;
371 bool
372 archiver_rep::conform_autosave () {
373 return last_autosave == depth;