PageRenderTime 47ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/source/src/Data/History/archiver.cpp

http://itexmacs.googlecode.com/
C++ | 609 lines | 478 code | 63 blank | 68 comment | 148 complexity | 15a48a418f0885a47ca993a371f01087 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0, MPL-2.0-no-copyleft-exception, LGPL-2.0
  1. /******************************************************************************
  2. * MODULE : archiver.cpp
  3. * DESCRIPTION: manage undo/redo history
  4. * COPYRIGHT : (C) 2009 Joris van der Hoeven
  5. *******************************************************************************
  6. * This software falls under the GNU general public license version 3 or later.
  7. * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
  8. * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
  9. ******************************************************************************/
  10. #include "archiver.hpp"
  11. #include "hashset.hpp"
  12. #include "iterator.hpp"
  13. extern tree the_et;
  14. array<patch> singleton (patch p);
  15. static patch make_compound (array<patch> a);
  16. static patch make_branches (array<patch> a);
  17. static hashset<double> genuine_authors;
  18. static hashset<pointer> archs;
  19. static hashset<pointer> pending_archs;
  20. /******************************************************************************
  21. * Constructors, destructors, printing and announcements
  22. ******************************************************************************/
  23. archiver_rep::archiver_rep (double author, path rp2):
  24. archive (make_branches (0)),
  25. current (make_compound (0)),
  26. depth (0),
  27. last_save (0),
  28. last_autosave (0),
  29. the_author (author),
  30. the_owner (0),
  31. rp (rp2),
  32. undo_obs (undo_observer (this)),
  33. versioning (false)
  34. {
  35. archs->insert ((pointer) this);
  36. attach_observer (subtree (the_et, rp), undo_obs);
  37. genuine_authors->insert (the_author);
  38. }
  39. archiver_rep::~archiver_rep () {
  40. genuine_authors->remove (the_author);
  41. detach_observer (subtree (the_et, rp), undo_obs);
  42. archs->remove ((pointer) this);
  43. pending_archs->remove ((pointer) this);
  44. }
  45. archiver::archiver (double author, path rp):
  46. rep (tm_new<archiver_rep> (author, rp)) {}
  47. void
  48. archiver_rep::clear () {
  49. archive= make_branches (0);
  50. current= make_compound (0);
  51. the_owner= 0;
  52. depth= 0;
  53. last_save= -1;
  54. last_autosave= -1;
  55. }
  56. void
  57. archiver_rep::show_all () {
  58. cout << HRULE << archive << LF << HRULE << LF;
  59. }
  60. void
  61. archive_announce (archiver_rep* arch, modification mod) {
  62. //cout << "Archive " << mod << "\n";
  63. if (DEBUG_HISTORY) cout << "Archive " << mod << "\n";
  64. ASSERT (arch->rp <= mod->p, "invalid modification");
  65. if (!arch->versioning) {
  66. arch->add (mod);
  67. pending_archs->insert ((pointer) arch);
  68. }
  69. }
  70. void
  71. global_clear_history () {
  72. iterator<pointer> it = iterate (archs);
  73. while (it->busy()) {
  74. archiver_rep* arch= (archiver_rep*) it->next();
  75. arch->clear ();
  76. }
  77. }
  78. void
  79. global_confirm () {
  80. iterator<pointer> it = iterate (pending_archs);
  81. while (it->busy()) {
  82. archiver_rep* arch= (archiver_rep*) it->next();
  83. arch->confirm ();
  84. arch->simplify ();
  85. }
  86. pending_archs= hashset<pointer> ();
  87. }
  88. /******************************************************************************
  89. * Useful subroutines
  90. ******************************************************************************/
  91. static patch
  92. make_compound (array<patch> a) {
  93. if (N(a) == 1) return a[0];
  94. else return patch (false, a);
  95. }
  96. static patch
  97. make_branches (array<patch> a) {
  98. if (N(a) == 1) return a[0];
  99. else return patch (true, a);
  100. }
  101. static patch
  102. append_branches (patch p1, patch p2) {
  103. return make_branches (append (branches (p1), branches (p2)));
  104. }
  105. static patch
  106. make_history (patch undo, patch redo) {
  107. array<patch> a;
  108. a << undo << branches (redo);
  109. return make_branches (a);
  110. }
  111. static int
  112. nr_undo (patch p) {
  113. if (nr_branches (p) == 0 || nr_children (branch (p, 0)) != 2) return 0;
  114. return 1;
  115. }
  116. static int
  117. nr_redo (patch p) {
  118. return max (0, nr_branches (p) - 1);
  119. }
  120. static patch
  121. get_undo (patch p) {
  122. ASSERT (nr_branches (p) > 0, "undo part unavailable");
  123. return branch (p, 0);
  124. }
  125. static patch
  126. get_redo (patch p) {
  127. if (nr_branches (p) == 0) return make_branches (array<patch> ());
  128. return make_branches (branches (p, 1, nr_branches (p)));
  129. }
  130. static patch
  131. car (patch p) {
  132. ASSERT (nr_children (branch (p, 0)) == 2, "car unavailable")
  133. return child (p, 0);
  134. }
  135. static patch
  136. cdr (patch p) {
  137. ASSERT (nr_children (branch (p, 0)) == 2, "cdr unavailable")
  138. return child (p, 1);
  139. }
  140. /******************************************************************************
  141. * Internal subroutines
  142. ******************************************************************************/
  143. void
  144. archiver_rep::apply (patch p) {
  145. // apply a patch, while disabling versioning during the modifications
  146. // cout << "Apply " << p << "\n";
  147. ASSERT (is_applicable (p, the_et), "invalid history");
  148. bool old= versioning;
  149. versioning= true;
  150. ::apply (p, the_et);
  151. versioning= old;
  152. }
  153. void
  154. archiver_rep::split (patch p1, patch p2, patch& re1, patch& re2) {
  155. //cout << "p1= " << p1 << "\n";
  156. //cout << "p2= " << p2 << "\n";
  157. array<patch> a= branches (p2);
  158. array<patch> a1;
  159. array<patch> a2;
  160. for (int i=0; i<N(a); i++) {
  161. patch q1= p1;
  162. patch q2= car (a[i]);
  163. if (get_author (q2) != the_author || !swap (q1, q2)) a1 << a[i];
  164. else a2 << patch (q1, make_future (q2, cdr (a[i])));
  165. }
  166. re1= make_branches (a1);
  167. re2= make_branches (a2);
  168. //cout << "re1= " << re1 << "\n";
  169. //cout << "re2= " << re2 << "\n";
  170. }
  171. patch
  172. archiver_rep::make_future (patch p1, patch p2) {
  173. if (nr_branches (p2) == 0 || get_author (p1) == the_author)
  174. return patch (p1, p2);
  175. patch re1, re2;
  176. split (p1, p2, re1, re2);
  177. if (nr_branches (re1) != 0) re1= patch (p1, re1);
  178. return append_branches (re1, re2);
  179. }
  180. patch
  181. archiver_rep::expose (patch archive) {
  182. if (nr_undo (archive) != 0 &&
  183. get_author (car (get_undo (archive))) != the_author &&
  184. nr_undo (cdr (get_undo (archive))) != 0)
  185. {
  186. patch nx1= expose (cdr (get_undo (archive)));
  187. if (get_author (car (get_undo (nx1))) != the_author) return archive;
  188. patch un1= car (get_undo (archive));
  189. patch un2= car (get_undo (nx1));
  190. patch re1= get_redo (archive);
  191. patch re2= get_redo (nx1);
  192. patch nx2= cdr (get_undo (nx1));
  193. patch fut= make_branches (0);
  194. if (nr_branches (re2) != 0) fut= make_future (un1, re2);
  195. if (!swap (un1, un2)) return archive;
  196. patch nx= make_history (patch (un2, nx2), make_branches (0));
  197. patch un= patch (un1, nx);
  198. patch re= append_branches (re1, fut);
  199. last_save= last_autosave= -1;
  200. return make_history (un, re);
  201. }
  202. else return archive;
  203. }
  204. void
  205. archiver_rep::expose () {
  206. archive= expose (archive);
  207. }
  208. void
  209. archiver_rep::normalize () {
  210. if (nr_undo (archive) != 0 && nr_redo (cdr (get_undo (archive))) != 0) {
  211. patch un1= get_undo (archive);
  212. patch re1= get_redo (archive);
  213. patch p1 = car (un1);
  214. patch nx1= cdr (un1);
  215. patch un2= get_undo (nx1);
  216. patch re2= get_redo (nx1);
  217. patch Re1, Re2;
  218. if (get_author (p1) == the_author) return;
  219. split (p1, re2, Re1, Re2);
  220. patch ar2= make_history (un2, Re1);
  221. archive= make_history (patch (p1, ar2), append_branches (re1, Re2));
  222. last_save= last_autosave= -1;
  223. }
  224. }
  225. /******************************************************************************
  226. * Routines concerning the current modifications
  227. ******************************************************************************/
  228. void
  229. archiver_rep::add (modification m) {
  230. m= copy (m);
  231. if (the_owner != 0 && the_owner != get_author ()) {
  232. //cout << "Change " << the_owner << " -> " << get_author () << "\n";
  233. confirm ();
  234. }
  235. else the_owner= get_author ();
  236. modification i= invert (m, the_et);
  237. patch q (i, m);
  238. //cout << "Add [" << the_owner << "] " << q << "\n";
  239. current= patch (q, current);
  240. }
  241. void
  242. archiver_rep::start_slave (double a) {
  243. if (the_owner != 0 && the_owner != get_author ()) {
  244. //cout << "Change " << the_owner << " -> " << get_author () << "\n";
  245. confirm ();
  246. }
  247. else the_owner= get_author ();
  248. patch q (a, false);
  249. //cout << "Add [" << the_owner << "] " << q << "\n";
  250. current= patch (q, current);
  251. }
  252. bool
  253. archiver_rep::active () {
  254. return nr_children (current) != 0;
  255. }
  256. bool
  257. archiver_rep::has_history () {
  258. return nr_undo (archive) == 1;
  259. }
  260. void
  261. archiver_rep::cancel () {
  262. if (active ()) {
  263. //cout << "Cancel " << current << "\n";
  264. apply (current);
  265. current= make_compound (0);
  266. the_owner= 0;
  267. }
  268. }
  269. void
  270. archiver_rep::confirm () {
  271. if (active ()) {
  272. current= patch (the_owner, compactify (current));
  273. if (nr_children (remove_set_cursor (current)) == 0)
  274. current= make_compound (0);
  275. if (active ()) {
  276. //cout << "Confirm " << current << "\n";
  277. archive= patch (current, archive);
  278. current= make_compound (0);
  279. the_owner= 0;
  280. depth++;
  281. if (depth <= last_save) last_save= -1;
  282. if (depth <= last_autosave) last_autosave= -1;
  283. normalize ();
  284. //show_all ();
  285. }
  286. }
  287. }
  288. bool
  289. archiver_rep::retract () {
  290. if (!has_history ()) return false;
  291. if (the_owner != 0 && the_owner != the_author) return false;
  292. expose ();
  293. patch un= car (get_undo (archive));
  294. if (get_author (un) != the_author) return false;
  295. patch re= get_redo (archive);
  296. patch nx= cdr (get_undo (archive));
  297. //cout << "Retract " << un << "\n";
  298. if (active ()) current= compactify (patch (current, un));
  299. else current= un;
  300. the_owner= the_author;
  301. if (nr_branches (re) != 0) {
  302. patch q= invert (current, the_et);
  303. re= patch (q, re);
  304. }
  305. if (nr_branches (nx) != 0) nx= get_undo (nx);
  306. archive= make_history (nx, append_branches (re, get_redo (nx)));
  307. depth--;
  308. //show_all ();
  309. return true;
  310. }
  311. bool
  312. archiver_rep::forget () {
  313. cancel ();
  314. bool r= retract ();
  315. if (r) cancel ();
  316. return r;
  317. }
  318. void
  319. archiver_rep::forget_cursor () {
  320. current= remove_set_cursor (current);
  321. }
  322. /******************************************************************************
  323. * Simplification of the history
  324. ******************************************************************************/
  325. void
  326. archiver_rep::simplify () {
  327. if (has_history () &&
  328. nr_undo (cdr (get_undo (archive))) == 1 &&
  329. nr_redo (cdr (get_undo (archive))) == 0 &&
  330. depth != last_save + 1)
  331. {
  332. patch p1= car (get_undo (archive));
  333. patch p2= car (get_undo (cdr (get_undo (archive))));
  334. //cout << "p1= " << p1 << "\n";
  335. //cout << "p2= " << p2 << "\n";
  336. bool r= join (p1, p2, the_et);
  337. //cout << "pr= " << p1 << "\n";
  338. if (r) {
  339. //cout << "\n\nSimplify\n";
  340. //show_all ();
  341. patch un= patch (p1, cdr (get_undo (cdr (get_undo (archive)))));
  342. patch re= get_redo (archive);
  343. archive= make_history (un, re);
  344. //show_all ();
  345. //cout << "\n";
  346. if (depth == last_autosave + 1) last_autosave= -1;
  347. depth--;
  348. simplify ();
  349. }
  350. }
  351. }
  352. /******************************************************************************
  353. * Undo and redo
  354. ******************************************************************************/
  355. int
  356. archiver_rep::undo_possibilities () {
  357. return nr_undo (archive);
  358. }
  359. int
  360. archiver_rep::redo_possibilities () {
  361. return nr_redo (archive);
  362. }
  363. path
  364. archiver_rep::undo_one (int i) {
  365. if (active ()) return path ();
  366. if (undo_possibilities () != 0) {
  367. ASSERT (i == 0, "index out of range");
  368. patch p= car (get_undo (archive));
  369. ASSERT (is_applicable (p, the_et), "history corrupted");
  370. patch q= invert (p, the_et);
  371. apply (p);
  372. patch re1= patch (q, get_redo (archive));
  373. patch nx = cdr (get_undo (archive));
  374. patch re2= get_redo (nx);
  375. patch re = append_branches (re1, re2);
  376. patch un = (nr_branches (nx) == 0? nx: get_undo (nx));
  377. archive= make_history (un, re);
  378. depth--;
  379. //show_all ();
  380. return cursor_hint (q, the_et);
  381. }
  382. return path ();
  383. }
  384. path
  385. archiver_rep::redo_one (int i) {
  386. if (active ()) return path ();
  387. int n= redo_possibilities ();
  388. if (n != 0) {
  389. ASSERT (i >= 0 && i < n, "index out of range");
  390. patch un= get_undo (archive);
  391. patch re= get_redo (archive);
  392. patch p= car (branch (re, i));
  393. //cout << "p= " << p << "\n";
  394. ASSERT (is_applicable (p, the_et), "future corrupted");
  395. patch q= invert (p, the_et);
  396. //cout << "q= " << q << "\n";
  397. apply (p);
  398. patch other= make_branches (append (branches (re, 0, i),
  399. branches (re, i+1, n)));
  400. //cout << "other= " << other << "\n";
  401. patch nx= make_history (un, other);
  402. archive= make_history (patch (q, nx), cdr (branch (re, i)));
  403. if (depth <= last_save && i != 0) last_save= -1;
  404. if (depth <= last_autosave && i != 0) last_autosave= -1;
  405. depth++;
  406. normalize ();
  407. //show_all ();
  408. return cursor_hint (q, the_et);
  409. }
  410. return path ();
  411. }
  412. path
  413. archiver_rep::undo (int i) {
  414. if (active ()) return path ();
  415. path r;
  416. while (undo_possibilities () != 0) {
  417. ASSERT (i == 0, "index out of range");
  418. expose ();
  419. if (get_author (car (get_undo (archive))) == the_author)
  420. return undo_one (i);
  421. else {
  422. r= undo_one (i);
  423. i= 0;
  424. }
  425. }
  426. return r;
  427. }
  428. path
  429. archiver_rep::redo (int i) {
  430. if (active ()) return path ();
  431. path r;
  432. bool first= true;
  433. while (redo_possibilities () != 0) {
  434. ASSERT (i >= 0 && i < redo_possibilities (), "index out of range");
  435. patch re= branch (get_redo (archive), i);
  436. bool done= (get_author (car (re)) == the_author);
  437. r= redo_one (i);
  438. if (done && !first) break;
  439. if (nr_redo (archive) != 1) break;
  440. i= 0;
  441. first= false;
  442. re= branch (get_redo (archive), i);
  443. if (get_author (car (re)) == the_author) break;
  444. if (done && genuine_authors->contains (get_author (car (re)))) break;
  445. }
  446. return r;
  447. }
  448. /******************************************************************************
  449. * Marking blocks for grouped modifications or canceling
  450. ******************************************************************************/
  451. static bool
  452. is_marker (patch p, double m, bool birth) {
  453. if (get_type (p) == PATCH_AUTHOR)
  454. return is_marker (p[0], m, birth);
  455. else if (get_type (p) == PATCH_BIRTH)
  456. return get_author (p) == m && get_birth (p) == birth;
  457. else return false;
  458. }
  459. static patch
  460. remove_marker (patch archive, double m) {
  461. ASSERT (nr_undo (archive) != 0, "marker not found");
  462. if (is_marker (car (get_undo (archive)), m, false)) {
  463. ASSERT (nr_redo (archive) == 0, "cannot remove marker");
  464. return cdr (get_undo (archive));
  465. }
  466. else {
  467. patch un= get_undo (archive);
  468. patch re= get_redo (archive);
  469. return make_history (patch (car (un), remove_marker (cdr (un), m)), re);
  470. }
  471. }
  472. void
  473. archiver_rep::mark_start (double m) {
  474. //cout << "Mark start " << m << "\n";
  475. confirm ();
  476. start_slave (m);
  477. confirm ();
  478. //show_all ();
  479. }
  480. void
  481. archiver_rep::mark_end (double m) {
  482. //cout << "Mark end " << m << "\n";
  483. archive= remove_marker (archive, m);
  484. depth--;
  485. simplify ();
  486. //show_all ();
  487. }
  488. bool
  489. archiver_rep::mark_cancel (double m) {
  490. //cout << "Mark cancel " << m << "\n";
  491. cancel ();
  492. while (nr_undo (archive) != 0) {
  493. expose ();
  494. if (is_marker (car (get_undo (archive)), m, false)) {
  495. archive= remove_marker (archive, m);
  496. depth--;
  497. simplify ();
  498. return true;
  499. }
  500. if (get_author (car (get_undo (archive))) != the_author) {
  501. archive= remove_marker (archive, m);
  502. depth--;
  503. return false;
  504. }
  505. retract ();
  506. cancel ();
  507. }
  508. return false;
  509. }
  510. /******************************************************************************
  511. * Check changes since last save/autosave
  512. ******************************************************************************/
  513. int
  514. archiver_rep::corrected_depth () {
  515. // NOTE : fix depth due to presence of marker
  516. // FIXME: implement a more robust check for conformity with saved state
  517. if (nr_undo (archive) == 0) return depth;
  518. patch p= car (get_undo (archive));
  519. if (get_type (p) == PATCH_AUTHOR) p= p[0];
  520. if (get_type (p) == PATCH_BIRTH && get_birth (p) == false) return depth - 1;
  521. return depth;
  522. }
  523. void
  524. archiver_rep::require_save () {
  525. last_save= -1;
  526. }
  527. void
  528. archiver_rep::notify_save () {
  529. last_save= corrected_depth ();
  530. }
  531. bool
  532. archiver_rep::conform_save () {
  533. return last_save == corrected_depth ();
  534. }
  535. void
  536. archiver_rep::require_autosave () {
  537. last_autosave= -1;
  538. }
  539. void
  540. archiver_rep::notify_autosave () {
  541. last_autosave= depth;
  542. }
  543. bool
  544. archiver_rep::conform_autosave () {
  545. return last_autosave == depth;
  546. }