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"
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)),
28 archiver_rep::~archiver_rep () {}
29 archiver::archiver (): rep (tm_new
<archiver_rep
> ()) {}
32 archiver_rep::clear () {
33 archive
= make_branches (0);
34 current
= make_compound (0);
40 /******************************************************************************
42 ******************************************************************************/
45 make_compound (array
<patch
> a
) {
46 if (N(a
) == 1) return a
[0];
47 else return patch (false, a
);
51 make_branches (array
<patch
> a
) {
52 if (N(a
) == 1) return a
[0];
53 else return patch (true, a
);
57 append_branches (patch p1
, patch p2
) {
58 return make_branches (append (branches (p1
), branches (p2
)));
62 make_history (patch undo
, patch redo
) {
64 a
<< undo
<< branches (redo
);
65 return make_branches (a
);
70 ASSERT (nr_branches (p
) > 0, "undo part unavailable");
76 if (nr_branches (p
) == 0) return make_branches (array
<patch
> ());
77 return make_branches (branches (p
, 1, nr_branches (p
)));
82 ASSERT (nr_children (branch (p
, 0)) == 2, "car unavailable")
88 ASSERT (nr_children (branch (p
, 0)) == 2, "cdr unavailable")
92 /******************************************************************************
93 * Internal subroutines
94 ******************************************************************************/
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;
104 versioning_busy
= old
;
109 expose (patch history, double author) {
110 if (N (history) == 2 &&
111 get_author (history[0]) != author &&
114 patch p1= history[0];
115 patch hh= expose (history[1], author);
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]));
130 archiver_rep::show_all () {
131 cout
<< HRULE
<< archive
<< LF
<< HRULE
<< LF
;
134 /******************************************************************************
135 * Routines concerning the current modifications
136 ******************************************************************************/
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
);
146 archiver_rep::start_slave (double a
) {
148 current
= patch (q
, current
);
152 archiver_rep::active () {
153 return nr_children (current
) != 0;
157 archiver_rep::has_history () {
158 if (nr_branches (archive
) == 0) return false;
159 else return nr_branches (get_undo (archive
)) != 0;
163 archiver_rep::cancel () {
165 // cout << "Cancel " << current << "\n";
167 current
= make_compound (0);
172 archiver_rep::confirm () {
174 current
= patch (get_author (), compactify (current
));
175 // cout << "Confirm " << current << "\n";
176 archive
= patch (current
, archive
);
177 current
= make_compound (0);
179 if (depth
<= last_save
) last_save
= -1;
180 if (depth
<= last_autosave
) last_autosave
= -1;
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
));
194 if (nr_branches (re
) != 0) {
195 patch q
= invert (current
, the_et
);
198 if (nr_branches (nx
) != 0) nx
= get_undo (nx
);
199 archive
= make_history (nx
, append_branches (re
, get_redo (nx
)));
206 archiver_rep::forget () {
212 /******************************************************************************
213 * History simplification
214 ******************************************************************************/
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";
229 patch un
= patch (p1
, cdr (get_undo (cdr (get_undo (archive
)))));
230 patch re
= get_redo (archive
);
231 archive
= make_history (un
, re
);
239 /******************************************************************************
241 ******************************************************************************/
244 archiver_rep::undo_possibilities () {
245 if (has_history ()) return 1;
250 archiver_rep::redo_possibilities () {
251 if (nr_branches (archive
) == 0) return 0;
252 else return nr_branches (get_redo (archive
));
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";
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
);
274 return cursor_hint (q
, the_et
);
280 archiver_rep::redo_one (int i
) {
281 if (active ()) return path ();
282 int n
= redo_possibilities ();
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";
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;
303 return cursor_hint (q
, the_et
);
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
)
326 archiver_rep::redo (int i
) {
327 if (active ()) return path ();
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
);
336 if (redo_possibilities () == 0) break;
337 if (get_author (car (branch (get_redo (archive
), i
))) == author
) break;
342 /******************************************************************************
343 * Check changes since last save/autosave
344 ******************************************************************************/
347 archiver_rep::require_save () {
352 archiver_rep::notify_save () {
357 archiver_rep::conform_save () {
358 return last_save
== depth
;
362 archiver_rep::require_autosave () {
367 archiver_rep::notify_autosave () {
368 last_autosave
= depth
;
372 archiver_rep::conform_autosave () {
373 return last_autosave
== depth
;