Documentation Source Text

Hex Artifact Content
Login

Artifact dfef4d465590378bc5eb3a8e96739f5115a25b53213a6f7d8164292230fc805b:


0000: 3c 74 63 6c 3e 68 64 5f 6b 65 79 77 6f 72 64 73  <tcl>hd_keywords
0010: 20 2a 75 6e 64 6f 72 65 64 6f 20 7b 61 75 74 6f   *undoredo {auto
0020: 6d 61 74 65 64 20 75 6e 64 6f 2f 72 65 64 6f 20  mated undo/redo 
0030: 73 74 61 63 6b 7d 20 7b 75 6e 64 6f 2f 72 65 64  stack} {undo/red
0040: 6f 7d 3c 2f 74 63 6c 3e 0a 3c 74 69 74 6c 65 3e  o}</tcl>.<title>
0050: 41 75 74 6f 6d 61 74 69 63 20 55 6e 64 6f 2f 52  Automatic Undo/R
0060: 65 64 6f 20 57 69 74 68 20 53 51 4c 69 74 65 3c  edo With SQLite<
0070: 2f 74 69 74 6c 65 3e 0a 0a 3c 68 31 20 61 6c 69  /title>..<h1 ali
0080: 67 6e 3d 22 63 65 6e 74 65 72 22 3e 0a 41 75 74  gn="center">.Aut
0090: 6f 6d 61 74 69 63 20 55 6e 64 6f 2f 52 65 64 6f  omatic Undo/Redo
00a0: 20 55 73 69 6e 67 20 53 51 4c 69 74 65 0a 3c 2f   Using SQLite.</
00b0: 68 31 3e 0a 0a 3c 70 3e 0a 54 68 69 73 20 70 61  h1>..<p>.This pa
00c0: 67 65 20 64 65 6d 6f 6e 73 74 72 61 74 65 73 20  ge demonstrates 
00d0: 68 6f 77 20 74 6f 20 75 73 65 20 74 72 69 67 67  how to use trigg
00e0: 65 72 73 20 74 6f 20 69 6d 70 6c 65 6d 65 6e 74  ers to implement
00f0: 20 75 6e 64 6f 2f 72 65 64 6f 20 0a 6c 6f 67 69   undo/redo .logi
0100: 63 20 66 6f 72 20 61 6e 20 61 70 70 6c 69 63 61  c for an applica
0110: 74 69 6f 6e 20 74 68 61 74 20 75 73 65 73 20 53  tion that uses S
0120: 51 4c 69 74 65 20 61 73 20 69 74 73 20 0a 5b 61  QLite as its .[a
0130: 70 70 6c 69 63 61 74 69 6f 6e 20 66 69 6c 65 20  pplication file 
0140: 66 6f 72 6d 61 74 5d 2e 0a 0a 3c 68 32 3e 4f 62  format]...<h2>Ob
0150: 6a 65 63 74 2d 4f 72 69 65 6e 74 65 64 20 44 65  ject-Oriented De
0160: 73 69 67 6e 3c 2f 68 32 3e 0a 0a 3c 70 3e 0a 54  sign</h2>..<p>.T
0170: 68 69 73 20 64 65 73 69 67 6e 20 6e 6f 74 65 20  his design note 
0180: 63 6f 6e 73 69 64 65 72 73 20 74 68 65 20 64 61  considers the da
0190: 74 61 62 61 73 65 20 74 6f 20 62 65 20 61 20 63  tabase to be a c
01a0: 6f 6c 6c 65 63 74 69 6f 6e 20 6f 66 20 6f 62 6a  ollection of obj
01b0: 65 63 74 73 2e 0a 45 61 63 68 20 53 51 4c 20 74  ects..Each SQL t
01c0: 61 62 6c 65 20 69 73 20 61 20 63 6c 61 73 73 2e  able is a class.
01d0: 0a 45 61 63 68 20 72 6f 77 20 69 73 20 61 6e 20  .Each row is an 
01e0: 69 6e 73 74 61 6e 63 65 20 6f 66 20 74 68 61 74  instance of that
01f0: 20 63 6c 61 73 73 2e 0a 54 68 65 72 65 20 61 72   class..There ar
0200: 65 2c 20 6f 66 20 63 6f 75 72 73 65 2c 20 6f 74  e, of course, ot
0210: 68 65 72 20 77 61 79 73 20 74 6f 20 69 6e 74 65  her ways to inte
0220: 72 70 72 65 74 20 61 6e 20 53 51 4c 20 64 61 74  rpret an SQL dat
0230: 61 62 61 73 65 20 73 63 68 65 6d 61 2c 0a 61 6e  abase schema,.an
0240: 64 20 74 68 65 20 74 65 63 68 6e 69 71 75 65 73  d the techniques
0250: 20 64 65 73 63 72 69 62 65 64 20 68 65 72 65 20   described here 
0260: 77 6f 72 6b 20 65 71 75 61 6c 6c 79 20 77 65 6c  work equally wel
0270: 6c 20 75 6e 64 65 72 20 61 6c 74 65 72 6e 61 74  l under alternat
0280: 69 76 65 0a 69 6e 74 65 72 70 72 65 74 61 74 69  ive.interpretati
0290: 6f 6e 73 2c 20 62 75 74 20 61 6e 20 6f 62 6a 65  ons, but an obje
02a0: 63 74 2d 6f 72 69 65 6e 74 65 64 20 76 69 65 77  ct-oriented view
02b0: 20 73 65 65 6d 73 20 62 65 20 6d 6f 72 65 20 6e   seems be more n
02c0: 61 74 75 72 61 6c 0a 74 6f 20 6d 6f 73 74 20 63  atural.to most c
02d0: 6f 6e 74 65 6d 70 6f 72 61 72 79 20 70 72 6f 67  ontemporary prog
02e0: 72 61 6d 6d 65 72 73 2e 0a 0a 3c 68 32 3e 43 61  rammers...<h2>Ca
02f0: 70 74 75 72 65 20 43 68 61 6e 67 65 73 20 55 73  pture Changes Us
0300: 69 6e 67 20 54 72 69 67 67 65 72 73 3c 2f 68 32  ing Triggers</h2
0310: 3e 0a 0a 3c 70 3e 0a 54 68 65 20 63 6f 72 65 20  >..<p>.The core 
0320: 69 64 65 61 20 69 73 20 74 6f 20 63 72 65 61 74  idea is to creat
0330: 65 20 61 20 73 70 65 63 69 61 6c 20 74 61 62 6c  e a special tabl
0340: 65 20 28 6e 61 6d 65 64 20 22 55 4e 44 4f 4c 4f  e (named "UNDOLO
0350: 47 22 20 69 6e 20 74 68 65 20 65 78 61 6d 70 6c  G" in the exampl
0360: 65 29 0a 74 68 61 74 20 68 6f 6c 64 73 20 69 6e  e).that holds in
0370: 66 6f 72 6d 61 74 69 6f 6e 20 6e 65 65 64 65 64  formation needed
0380: 20 74 6f 20 75 6e 64 6f 2f 72 65 64 6f 20 63 68   to undo/redo ch
0390: 61 6e 67 65 73 20 74 6f 20 74 68 65 20 64 61 74  anges to the dat
03a0: 61 62 61 73 65 2e 20 0a 46 6f 72 20 65 61 63 68  abase. .For each
03b0: 20 63 6c 61 73 73 20 28 74 61 62 6c 65 29 20 69   class (table) i
03c0: 6e 20 74 68 65 20 64 61 74 61 62 61 73 65 20 74  n the database t
03d0: 68 61 74 20 77 61 6e 74 73 20 74 6f 20 70 61 72  hat wants to par
03e0: 74 69 63 69 70 61 74 65 20 69 6e 20 0a 74 68 65  ticipate in .the
03f0: 20 75 6e 64 6f 2f 72 65 64 6f 2c 20 74 72 69 67   undo/redo, trig
0400: 67 65 72 73 20 61 72 65 20 63 72 65 61 74 65 64  gers are created
0410: 20 74 68 61 74 20 63 61 75 73 65 20 65 6e 74 72   that cause entr
0420: 69 65 73 20 74 6f 20 62 65 20 6d 61 64 65 20 69  ies to be made i
0430: 6e 20 0a 74 68 65 20 55 4e 44 4f 4c 4f 47 20 74  n .the UNDOLOG t
0440: 61 62 6c 65 20 66 6f 72 20 65 61 63 68 20 44 45  able for each DE
0450: 4c 45 54 45 2c 20 49 4e 53 45 52 54 2c 20 61 6e  LETE, INSERT, an
0460: 64 20 55 50 44 41 54 45 20 6f 66 20 74 68 65 20  d UPDATE of the 
0470: 70 61 72 74 69 63 69 70 61 74 69 6e 67 0a 63 6c  participating.cl
0480: 61 73 73 2e 0a 54 68 65 20 55 4e 44 4f 4c 4f 47  ass..The UNDOLOG
0490: 20 65 6e 74 72 69 65 73 20 63 6f 6e 73 69 73 74   entries consist
04a0: 20 6f 66 20 6f 72 64 69 6e 61 72 79 20 53 51 4c   of ordinary SQL
04b0: 20 73 74 61 74 65 6d 65 6e 74 73 20 74 68 65 20   statements the 
04c0: 63 61 6e 20 62 65 0a 70 6c 61 79 65 64 20 62 61  can be.played ba
04d0: 63 6b 20 74 6f 20 72 65 76 65 72 73 65 20 74 68  ck to reverse th
04e0: 65 20 63 68 61 6e 67 65 73 2e 0a 0a 3c 70 3e 0a  e changes...<p>.
04f0: 46 6f 72 20 65 78 61 6d 70 6c 65 2c 20 73 75 70  For example, sup
0500: 70 6f 73 65 20 79 6f 75 20 77 61 6e 74 65 64 20  pose you wanted 
0510: 75 6e 64 6f 2f 72 65 64 6f 20 6f 6e 20 61 20 63  undo/redo on a c
0520: 6c 61 73 73 20 28 74 61 62 6c 65 29 0a 74 68 61  lass (table).tha
0530: 74 20 6c 6f 6f 6b 73 20 6c 69 6b 65 20 74 68 69  t looks like thi
0540: 73 3a 0a 0a 3c 62 6c 6f 63 6b 71 75 6f 74 65 3e  s:..<blockquote>
0550: 3c 70 72 65 3e 0a 43 52 45 41 54 45 20 54 41 42  <pre>.CREATE TAB
0560: 4c 45 20 65 78 31 28 61 2c 62 2c 63 29 3b 0a 3c  LE ex1(a,b,c);.<
0570: 2f 70 72 65 3e 3c 2f 62 6c 6f 63 6b 71 75 6f 74  /pre></blockquot
0580: 65 3e 0a 0a 3c 70 3e 0a 54 72 69 67 67 65 72 73  e>..<p>.Triggers
0590: 20 74 6f 20 72 65 63 6f 72 64 20 63 68 61 6e 67   to record chang
05a0: 65 73 20 74 6f 20 74 61 62 6c 65 20 45 58 31 20  es to table EX1 
05b0: 6d 69 67 68 74 20 6c 6f 6f 6b 20 6c 69 6b 65 20  might look like 
05c0: 74 68 69 73 3a 0a 0a 3c 62 6c 6f 63 6b 71 75 6f  this:..<blockquo
05d0: 74 65 3e 3c 70 72 65 3e 0a 43 52 45 41 54 45 20  te><pre>.CREATE 
05e0: 54 45 4d 50 20 54 52 49 47 47 45 52 20 65 78 31  TEMP TRIGGER ex1
05f0: 5f 69 74 20 41 46 54 45 52 20 49 4e 53 45 52 54  _it AFTER INSERT
0600: 20 4f 4e 20 65 78 31 20 42 45 47 49 4e 0a 20 20   ON ex1 BEGIN.  
0610: 49 4e 53 45 52 54 20 49 4e 54 4f 20 75 6e 64 6f  INSERT INTO undo
0620: 6c 6f 67 20 56 41 4c 55 45 53 28 4e 55 4c 4c 2c  log VALUES(NULL,
0630: 27 44 45 4c 45 54 45 20 46 52 4f 4d 20 65 78 31  'DELETE FROM ex1
0640: 20 57 48 45 52 45 20 72 6f 77 69 64 3d 27 7c 7c   WHERE rowid='||
0650: 6e 65 77 2e 72 6f 77 69 64 29 3b 0a 45 4e 44 3b  new.rowid);.END;
0660: 0a 43 52 45 41 54 45 20 54 45 4d 50 20 54 52 49  .CREATE TEMP TRI
0670: 47 47 45 52 20 65 78 31 5f 75 74 20 41 46 54 45  GGER ex1_ut AFTE
0680: 52 20 55 50 44 41 54 45 20 4f 4e 20 65 78 31 20  R UPDATE ON ex1 
0690: 42 45 47 49 4e 0a 20 20 49 4e 53 45 52 54 20 49  BEGIN.  INSERT I
06a0: 4e 54 4f 20 75 6e 64 6f 6c 6f 67 20 56 41 4c 55  NTO undolog VALU
06b0: 45 53 28 4e 55 4c 4c 2c 27 55 50 44 41 54 45 20  ES(NULL,'UPDATE 
06c0: 65 78 31 0a 20 20 20 20 20 53 45 54 20 61 3d 27  ex1.     SET a='
06d0: 7c 7c 71 75 6f 74 65 28 6f 6c 64 2e 61 29 7c 7c  ||quote(old.a)||
06e0: 27 2c 62 3d 27 7c 7c 71 75 6f 74 65 28 6f 6c 64  ',b='||quote(old
06f0: 2e 62 29 7c 7c 27 2c 63 3d 27 7c 7c 71 75 6f 74  .b)||',c='||quot
0700: 65 28 6f 6c 64 2e 63 29 7c 7c 27 0a 20 20 20 57  e(old.c)||'.   W
0710: 48 45 52 45 20 72 6f 77 69 64 3d 27 7c 7c 6f 6c  HERE rowid='||ol
0720: 64 2e 72 6f 77 69 64 29 3b 0a 45 4e 44 3b 0a 43  d.rowid);.END;.C
0730: 52 45 41 54 45 20 54 45 4d 50 20 54 52 49 47 47  REATE TEMP TRIGG
0740: 45 52 20 65 78 31 5f 64 74 20 42 45 46 4f 52 45  ER ex1_dt BEFORE
0750: 20 44 45 4c 45 54 45 20 4f 4e 20 65 78 31 20 42   DELETE ON ex1 B
0760: 45 47 49 4e 0a 20 20 49 4e 53 45 52 54 20 49 4e  EGIN.  INSERT IN
0770: 54 4f 20 75 6e 64 6f 6c 6f 67 20 56 41 4c 55 45  TO undolog VALUE
0780: 53 28 4e 55 4c 4c 2c 27 49 4e 53 45 52 54 20 49  S(NULL,'INSERT I
0790: 4e 54 4f 20 65 78 31 28 72 6f 77 69 64 2c 61 2c  NTO ex1(rowid,a,
07a0: 62 2c 63 29 0a 20 20 20 20 56 41 4c 55 45 53 28  b,c).    VALUES(
07b0: 27 7c 7c 6f 6c 64 2e 72 6f 77 69 64 7c 7c 27 2c  '||old.rowid||',
07c0: 27 7c 7c 71 75 6f 74 65 28 6f 6c 64 2e 61 29 7c  '||quote(old.a)|
07d0: 7c 27 2c 27 7c 7c 71 75 6f 74 65 28 6f 6c 64 2e  |','||quote(old.
07e0: 62 29 7c 7c 0a 20 20 20 20 20 20 20 20 20 20 20  b)||.           
07f0: 27 2c 27 7c 7c 71 75 6f 74 65 28 6f 6c 64 2e 63  ','||quote(old.c
0800: 29 7c 7c 27 29 27 29 3b 0a 45 4e 44 3b 0a 3c 2f  )||')');.END;.</
0810: 70 72 65 3e 3c 2f 62 6c 6f 63 6b 71 75 6f 74 65  pre></blockquote
0820: 3e 0a 0a 3c 70 3e 0a 41 66 74 65 72 20 65 61 63  >..<p>.After eac
0830: 68 20 49 4e 53 45 52 54 20 6f 6e 20 65 78 31 2c  h INSERT on ex1,
0840: 20 74 68 65 20 65 78 31 5f 69 74 20 74 72 69 67   the ex1_it trig
0850: 67 65 72 20 63 6f 6e 73 74 72 75 63 74 73 20 74  ger constructs t
0860: 65 78 74 20 6f 66 20 61 20 0a 44 45 4c 45 54 45  ext of a .DELETE
0870: 20 73 74 61 74 65 6d 65 6e 74 20 74 68 61 74 20   statement that 
0880: 77 69 6c 6c 20 75 6e 64 6f 20 74 68 65 20 49 4e  will undo the IN
0890: 53 45 52 54 2e 20 54 68 65 20 65 78 31 5f 75 74  SERT. The ex1_ut
08a0: 20 74 72 69 67 67 65 72 20 63 6f 6e 73 74 72 75   trigger constru
08b0: 63 74 73 20 0a 61 6e 20 55 50 44 41 54 45 20 73  cts .an UPDATE s
08c0: 74 61 74 65 6d 65 6e 74 20 74 68 61 74 20 77 69  tatement that wi
08d0: 6c 6c 20 75 6e 64 6f 20 74 68 65 20 65 66 66 65  ll undo the effe
08e0: 63 74 73 20 6f 66 20 61 6e 20 55 50 44 41 54 45  cts of an UPDATE
08f0: 2e 20 0a 41 6e 64 20 74 68 65 20 65 78 31 5f 64  . .And the ex1_d
0900: 74 20 74 72 69 67 67 65 72 20 63 6f 6e 73 74 72  t trigger constr
0910: 75 63 74 73 20 61 20 73 74 61 74 65 6d 65 6e 74  ucts a statement
0920: 20 74 68 61 74 20 77 69 6c 6c 20 75 6e 64 6f 20   that will undo 
0930: 74 68 65 20 0a 65 66 66 65 63 74 73 20 6f 66 20  the .effects of 
0940: 61 20 44 45 4c 45 54 45 2e 0a 0a 3c 70 3e 0a 4e  a DELETE...<p>.N
0950: 6f 74 65 20 74 68 65 20 75 73 65 20 6f 66 20 74  ote the use of t
0960: 68 65 20 5b 71 75 6f 74 65 28 29 20 53 51 4c 20  he [quote() SQL 
0970: 66 75 6e 63 74 69 6f 6e 5d 20 69 6e 20 74 68 65  function] in the
0980: 73 65 20 74 72 69 67 67 65 72 73 2e 0a 54 68 65  se triggers..The
0990: 20 71 75 6f 74 65 28 29 20 66 75 6e 63 74 69 6f   quote() functio
09a0: 6e 20 63 6f 6e 76 65 72 74 73 20 69 74 73 20 61  n converts its a
09b0: 72 67 75 6d 65 6e 74 20 69 6e 74 6f 20 61 20 66  rgument into a f
09c0: 6f 72 6d 20 0a 74 68 61 74 20 69 73 20 61 70 70  orm .that is app
09d0: 72 6f 70 72 69 61 74 65 20 66 6f 72 20 69 6e 63  ropriate for inc
09e0: 6c 75 73 69 6f 6e 20 69 6e 20 61 6e 20 53 51 4c  lusion in an SQL
09f0: 20 73 74 61 74 65 6d 65 6e 74 2e 20 4e 75 6d 65   statement. Nume
0a00: 72 69 63 20 76 61 6c 75 65 73 20 0a 63 6f 6d 65  ric values .come
0a10: 20 74 68 72 6f 75 67 68 20 75 6e 63 68 61 6e 67   through unchang
0a20: 65 64 2e 20 53 69 6e 67 6c 65 20 71 75 6f 74 65  ed. Single quote
0a30: 73 20 61 72 65 20 61 64 64 65 64 20 62 65 66 6f  s are added befo
0a40: 72 65 20 61 6e 64 20 61 66 74 65 72 20 0a 73 74  re and after .st
0a50: 72 69 6e 67 73 20 61 6e 64 20 61 6e 79 20 69 6e  rings and any in
0a60: 74 65 72 6e 61 6c 20 73 69 6e 67 6c 65 20 71 75  ternal single qu
0a70: 6f 74 65 73 20 61 72 65 20 65 73 63 61 70 65 64  otes are escaped
0a80: 2e 20 20 42 4c 4f 42 20 76 61 6c 75 65 73 0a 61  .  BLOB values.a
0a90: 72 65 20 72 65 6e 64 65 72 65 64 20 75 73 69 6e  re rendered usin
0aa0: 67 20 53 51 4c 2d 73 74 61 6e 64 61 72 64 20 68  g SQL-standard h
0ab0: 65 78 61 64 65 63 69 6d 61 6c 20 42 4c 4f 42 20  exadecimal BLOB 
0ac0: 6e 6f 74 61 74 69 6f 6e 2e 20 20 54 68 65 0a 75  notation.  The.u
0ad0: 73 65 20 6f 66 20 74 68 65 20 71 75 6f 74 65 28  se of the quote(
0ae0: 29 20 66 75 6e 63 74 69 6f 6e 20 65 6e 73 75 72  ) function ensur
0af0: 65 73 20 74 68 61 74 20 74 68 65 20 53 51 4c 20  es that the SQL 
0b00: 73 74 61 74 65 6d 65 6e 74 73 20 75 73 65 64 20  statements used 
0b10: 74 6f 0a 75 6e 64 6f 20 61 6e 64 20 72 65 64 6f  to.undo and redo
0b20: 20 61 72 65 20 61 6c 77 61 79 73 20 73 61 66 65   are always safe
0b30: 20 66 72 6f 6d 20 53 51 4c 20 69 6e 6a 65 63 74   from SQL inject
0b40: 69 6f 6e 2e 0a 0a 3c 68 32 3e 41 75 74 6f 6d 61  ion...<h2>Automa
0b50: 74 69 63 20 43 72 65 61 74 69 6f 6e 20 4f 66 20  tic Creation Of 
0b60: 54 72 69 67 67 65 72 73 3c 2f 68 32 3e 0a 0a 3c  Triggers</h2>..<
0b70: 70 3e 0a 54 72 69 67 67 65 72 73 20 73 75 63 68  p>.Triggers such
0b80: 20 61 73 20 74 68 65 20 61 62 6f 76 65 20 63 6f   as the above co
0b90: 75 6c 64 20 62 65 20 65 6e 74 65 72 65 64 20 6d  uld be entered m
0ba0: 61 6e 75 61 6c 6c 79 2c 20 62 75 74 20 74 68 61  anually, but tha
0bb0: 74 20 69 73 20 74 65 64 69 6f 75 73 2e 0a 41 6e  t is tedious..An
0bc0: 20 69 6d 70 6f 72 74 61 6e 74 20 66 65 61 74 75   important featu
0bd0: 72 65 20 6f 66 20 74 68 65 20 74 65 63 68 6e 69  re of the techni
0be0: 71 75 65 20 64 65 6d 6f 6e 73 74 72 61 74 65 64  que demonstrated
0bf0: 20 62 65 6c 6f 77 20 69 73 20 0a 74 68 61 74 20   below is .that 
0c00: 74 68 65 20 74 72 69 67 67 65 72 73 20 61 72 65  the triggers are
0c10: 20 67 65 6e 65 72 61 74 65 64 20 61 75 74 6f 6d   generated autom
0c20: 61 74 69 63 61 6c 6c 79 2e 0a 0a 3c 70 3e 0a 54  atically...<p>.T
0c30: 68 65 20 69 6d 70 6c 65 6d 65 6e 74 61 74 69 6f  he implementatio
0c40: 6e 20 6c 61 6e 67 75 61 67 65 20 66 6f 72 20 74  n language for t
0c50: 68 65 20 65 78 61 6d 70 6c 65 20 63 6f 64 65 20  he example code 
0c60: 69 73 20 0a 5b 68 74 74 70 3a 2f 2f 77 77 77 2e  is .[http://www.
0c70: 74 63 6c 2d 6c 61 6e 67 2e 6f 72 67 7c 54 43 4c  tcl-lang.org|TCL
0c80: 5d 2c 20 74 68 6f 75 67 68 20 79 6f 75 20 63 61  ], though you ca
0c90: 6e 20 65 61 73 69 6c 79 20 64 6f 20 74 68 65 20  n easily do the 
0ca0: 73 61 6d 65 20 74 68 69 6e 67 20 0a 69 6e 20 61  same thing .in a
0cb0: 6e 6f 74 68 65 72 20 70 72 6f 67 72 61 6d 6d 69  nother programmi
0cc0: 6e 67 20 6c 61 6e 67 75 61 67 65 2e 0a 52 65 6d  ng language..Rem
0cd0: 65 6d 62 65 72 20 74 68 61 74 20 74 68 65 20 63  ember that the c
0ce0: 6f 64 65 20 68 65 72 65 20 69 73 20 61 20 64 65  ode here is a de
0cf0: 6d 6f 6e 73 74 72 61 74 69 6f 6e 20 6f 66 20 74  monstration of t
0d00: 68 65 20 74 65 63 68 6e 69 71 75 65 2c 0a 6e 6f  he technique,.no
0d10: 74 20 61 20 64 72 6f 70 2d 69 6e 20 6d 6f 64 75  t a drop-in modu
0d20: 6c 65 20 74 68 61 74 20 77 69 6c 6c 20 61 75 74  le that will aut
0d30: 6f 6d 61 74 69 63 61 6c 6c 79 20 64 6f 20 65 76  omatically do ev
0d40: 65 72 79 74 68 69 6e 67 20 66 6f 72 20 79 6f 75  erything for you
0d50: 2e 0a 54 68 65 20 64 65 6d 6f 6e 73 74 72 61 74  ..The demonstrat
0d60: 69 6f 6e 20 63 6f 64 65 20 73 68 6f 77 6e 20 62  ion code shown b
0d70: 65 6c 6f 77 20 69 73 20 64 65 72 69 76 65 64 20  elow is derived 
0d80: 66 72 6f 6d 20 61 63 74 75 61 6c 20 63 6f 64 65  from actual code
0d90: 20 0a 69 6e 20 70 72 6f 64 75 63 74 69 6f 6e 20   .in production 
0da0: 75 73 65 2e 20 42 75 74 20 79 6f 75 20 77 69 6c  use. But you wil
0db0: 6c 20 6e 65 65 64 20 74 6f 20 6d 61 6b 65 20 63  l need to make c
0dc0: 68 61 6e 67 65 73 20 74 6f 20 74 61 69 6c 6f 72  hanges to tailor
0dd0: 20 69 74 20 0a 74 6f 20 79 6f 75 72 20 61 70 70   it .to your app
0de0: 6c 69 63 61 74 69 6f 6e 2e 0a 0a 3c 70 3e 0a 54  lication...<p>.T
0df0: 6f 20 61 63 74 69 76 61 74 65 20 74 68 65 20 75  o activate the u
0e00: 6e 64 6f 2f 72 65 64 6f 20 6c 6f 67 69 63 2c 20  ndo/redo logic, 
0e10: 69 6e 76 6f 6b 65 20 74 68 65 20 75 6e 64 6f 3a  invoke the undo:
0e20: 3a 61 63 74 69 76 61 74 65 20 63 6f 6d 6d 61 6e  :activate comman
0e30: 64 20 0a 77 69 74 68 20 61 6c 6c 20 63 6c 61 73  d .with all clas
0e40: 73 65 73 20 28 74 61 62 6c 65 73 29 20 74 68 61  ses (tables) tha
0e50: 74 20 61 72 65 20 74 6f 20 70 61 72 74 69 63 69  t are to partici
0e60: 70 61 74 65 20 69 6e 20 74 68 65 20 75 6e 64 6f  pate in the undo
0e70: 2f 72 65 64 6f 20 0a 61 73 20 61 72 67 75 6d 65  /redo .as argume
0e80: 6e 74 73 2e 20 55 73 65 20 75 6e 64 6f 3a 3a 64  nts. Use undo::d
0e90: 65 61 63 74 69 76 61 74 65 2c 20 75 6e 64 6f 3a  eactivate, undo:
0ea0: 3a 66 72 65 65 7a 65 2c 20 61 6e 64 20 75 6e 64  :freeze, and und
0eb0: 6f 3a 3a 75 6e 66 72 65 65 7a 65 20 0a 74 6f 20  o::unfreeze .to 
0ec0: 63 6f 6e 74 72 6f 6c 20 74 68 65 20 73 74 61 74  control the stat
0ed0: 65 20 6f 66 20 74 68 65 20 75 6e 64 6f 2f 72 65  e of the undo/re
0ee0: 64 6f 20 6d 65 63 68 61 6e 69 73 6d 2e 0a 0a 3c  do mechanism...<
0ef0: 70 3e 0a 54 68 65 20 75 6e 64 6f 3a 3a 61 63 74  p>.The undo::act
0f00: 69 76 61 74 65 20 63 6f 6d 6d 61 6e 64 20 63 72  ivate command cr
0f10: 65 61 74 65 73 20 74 65 6d 70 6f 72 61 72 79 20  eates temporary 
0f20: 74 72 69 67 67 65 72 73 20 69 6e 20 74 68 65 20  triggers in the 
0f30: 64 61 74 61 62 61 73 65 0a 74 68 61 74 20 72 65  database.that re
0f40: 63 6f 72 64 20 61 6c 6c 20 63 68 61 6e 67 65 73  cord all changes
0f50: 20 6d 61 64 65 20 74 6f 20 74 68 65 20 74 61 62   made to the tab
0f60: 6c 65 73 20 6e 61 6d 65 64 20 69 6e 20 74 68 65  les named in the
0f70: 20 61 72 67 75 6d 65 6e 74 73 2e 0a 0a 3c 68 32   arguments...<h2
0f80: 3e 41 70 70 6c 69 63 61 74 69 6f 6e 20 49 6e 74  >Application Int
0f90: 65 72 66 61 63 65 3c 2f 68 32 3e 0a 0a 3c 70 3e  erface</h2>..<p>
0fa0: 0a 41 66 74 65 72 20 61 20 73 65 71 75 65 6e 63  .After a sequenc
0fb0: 65 20 6f 66 20 63 68 61 6e 67 65 73 20 74 68 61  e of changes tha
0fc0: 74 20 64 65 66 69 6e 65 20 61 20 73 69 6e 67 6c  t define a singl
0fd0: 65 20 75 6e 64 6f 2f 72 65 64 6f 20 73 74 65 70  e undo/redo step
0fe0: 2c 20 0a 69 6e 76 6f 6b 65 20 74 68 65 20 75 6e  , .invoke the un
0ff0: 64 6f 3a 3a 62 61 72 72 69 65 72 20 63 6f 6d 6d  do::barrier comm
1000: 61 6e 64 20 74 6f 20 64 65 66 69 6e 65 20 74 68  and to define th
1010: 65 20 6c 69 6d 69 74 20 6f 66 20 74 68 61 74 20  e limit of that 
1020: 73 74 65 70 2e 20 0a 49 6e 20 61 6e 20 69 6e 74  step. .In an int
1030: 65 72 61 63 74 69 76 65 20 70 72 6f 67 72 61 6d  eractive program
1040: 2c 20 79 6f 75 20 63 61 6e 20 63 61 6c 6c 20 75  , you can call u
1050: 6e 64 6f 3a 3a 65 76 65 6e 74 20 61 66 74 65 72  ndo::event after
1060: 20 61 6e 79 20 63 68 61 6e 67 65 20 0a 61 6e 64   any change .and
1070: 20 75 6e 64 6f 3a 3a 62 61 72 72 69 65 72 20 77   undo::barrier w
1080: 69 6c 6c 20 62 65 20 63 61 6c 6c 65 64 20 61 75  ill be called au
1090: 74 6f 6d 61 74 69 63 61 6c 6c 79 20 61 73 20 61  tomatically as a
10a0: 6e 20 69 64 6c 65 20 63 61 6c 6c 62 61 63 6b 2e  n idle callback.
10b0: 0a 0a 3c 70 3e 0a 57 68 65 6e 20 74 68 65 20 75  ..<p>.When the u
10c0: 73 65 72 20 70 72 65 73 73 65 73 20 74 68 65 20  ser presses the 
10d0: 55 6e 64 6f 20 62 75 74 74 6f 6e 2c 20 69 6e 76  Undo button, inv
10e0: 6f 6b 65 20 75 6e 64 6f 3a 3a 75 6e 64 6f 2e 20  oke undo::undo. 
10f0: 0a 49 6e 76 6f 6b 65 20 75 6e 64 6f 3a 3a 72 65  .Invoke undo::re
1100: 64 6f 20 77 68 65 6e 20 74 68 65 20 75 73 65 72  do when the user
1110: 20 70 72 65 73 73 65 73 20 74 68 65 20 52 65 64   presses the Red
1120: 6f 20 62 75 74 74 6f 6e 2e 0a 0a 3c 70 3e 0a 4f  o button...<p>.O
1130: 6e 20 65 61 63 68 20 63 61 6c 6c 20 74 6f 20 75  n each call to u
1140: 6e 64 6f 3a 3a 75 6e 64 6f 20 6f 72 20 75 6e 64  ndo::undo or und
1150: 6f 3a 3a 72 65 64 6f 2c 20 74 68 65 20 75 6e 64  o::redo, the und
1160: 6f 2f 72 65 64 6f 20 6d 6f 64 75 6c 65 20 0a 61  o/redo module .a
1170: 75 74 6f 6d 61 74 69 63 61 6c 6c 79 20 69 6e 76  utomatically inv
1180: 6f 6b 65 73 20 6d 65 74 68 6f 64 73 20 73 74 61  okes methods sta
1190: 74 75 73 5f 72 65 66 72 65 73 68 20 61 6e 64 20  tus_refresh and 
11a0: 72 65 6c 6f 61 64 5f 61 6c 6c 20 69 6e 20 0a 61  reload_all in .a
11b0: 6c 6c 20 74 6f 70 6c 65 76 65 6c 20 6e 61 6d 65  ll toplevel name
11c0: 73 70 61 63 65 73 2e 20 54 68 65 73 65 20 6d 65  spaces. These me
11d0: 74 68 6f 64 73 20 73 68 6f 75 6c 64 20 62 65 20  thods should be 
11e0: 64 65 66 69 6e 65 64 20 74 6f 20 0a 72 65 63 6f  defined to .reco
11f0: 6e 73 74 72 75 63 74 20 74 68 65 20 64 69 73 70  nstruct the disp
1200: 6c 61 79 20 6f 72 20 6f 74 68 65 72 77 69 73 65  lay or otherwise
1210: 20 75 70 64 61 74 65 20 74 68 65 20 73 74 61 74   update the stat
1220: 65 20 6f 66 20 74 68 65 20 0a 70 72 6f 67 72 61  e of the .progra
1230: 6d 20 62 61 73 65 64 20 6f 6e 20 74 68 65 20 75  m based on the u
1240: 6e 64 6f 6e 65 2f 72 65 64 6f 6e 65 20 63 68 61  ndone/redone cha
1250: 6e 67 65 73 20 74 6f 20 74 68 65 20 64 61 74 61  nges to the data
1260: 62 61 73 65 2e 0a 0a 3c 70 3e 0a 54 68 65 20 64  base...<p>.The d
1270: 65 6d 6f 6e 73 74 72 61 74 69 6f 6e 20 63 6f 64  emonstration cod
1280: 65 20 62 65 6c 6f 77 20 69 6e 63 6c 75 64 65 73  e below includes
1290: 20 61 20 73 74 61 74 75 73 5f 72 65 66 72 65 73   a status_refres
12a0: 68 20 6d 65 74 68 6f 64 20 0a 74 68 61 74 20 67  h method .that g
12b0: 72 61 79 73 2d 6f 75 74 20 6f 72 20 61 63 74 69  rays-out or acti
12c0: 76 61 74 65 73 20 74 68 65 20 55 6e 64 6f 20 61  vates the Undo a
12d0: 6e 64 20 52 65 64 6f 20 62 75 74 74 6f 6e 73 20  nd Redo buttons 
12e0: 61 6e 64 20 6d 65 6e 75 20 0a 65 6e 74 72 69 65  and menu .entrie
12f0: 73 20 64 65 70 65 6e 64 69 6e 67 20 6f 6e 20 77  s depending on w
1300: 68 65 74 68 65 72 20 6f 72 20 6e 6f 74 20 74 68  hether or not th
1310: 65 72 65 20 69 73 20 61 6e 79 74 68 69 6e 67 20  ere is anything 
1320: 74 6f 20 62 65 20 0a 75 6e 64 6f 6e 65 20 6f 72  to be .undone or
1330: 20 72 65 64 6f 6e 65 2e 20 59 6f 75 20 77 69 6c   redone. You wil
1340: 6c 20 6e 65 65 64 20 74 6f 20 72 65 64 65 66 69  l need to redefi
1350: 6e 65 20 74 68 69 73 20 6d 65 74 68 6f 64 20 74  ne this method t
1360: 6f 20 0a 63 6f 6e 74 72 6f 6c 20 74 68 65 20 55  o .control the U
1370: 6e 64 6f 20 61 6e 64 20 52 65 64 6f 20 62 75 74  ndo and Redo but
1380: 74 6f 6e 73 20 69 6e 20 79 6f 75 72 20 61 70 70  tons in your app
1390: 6c 69 63 61 74 69 6f 6e 2e 0a 0a 3c 70 3e 0a 54  lication...<p>.T
13a0: 68 65 20 64 65 6d 6f 6e 73 74 72 61 74 69 6f 6e  he demonstration
13b0: 20 63 6f 64 65 20 61 73 73 75 6d 65 73 20 74 68   code assumes th
13c0: 61 74 20 74 68 65 20 53 51 4c 69 74 65 20 64 61  at the SQLite da
13d0: 74 61 62 61 73 65 20 69 73 20 0a 6f 70 65 6e 65  tabase is .opene
13e0: 64 20 75 73 65 64 20 61 73 20 61 20 64 61 74 61  d used as a data
13f0: 62 61 73 65 20 6f 62 6a 65 63 74 20 6e 61 6d 65  base object name
1400: 64 20 22 64 62 22 2e 0a 0a 3c 68 32 3e 45 78 61  d "db"...<h2>Exa
1410: 6d 70 6c 65 20 43 6f 64 65 3c 2f 68 32 3e 0a 0a  mple Code</h2>..
1420: 3c 62 6c 6f 63 6b 71 75 6f 74 65 3e 3c 70 72 65  <blockquote><pre
1430: 3e 0a 23 20 45 76 65 72 79 74 68 69 6e 67 20 67  >.# Everything g
1440: 6f 65 73 20 69 6e 20 61 20 70 72 69 76 61 74 65  oes in a private
1450: 20 6e 61 6d 65 73 70 61 63 65 0a 6e 61 6d 65 73   namespace.names
1460: 70 61 63 65 20 65 76 61 6c 20 3a 3a 75 6e 64 6f  pace eval ::undo
1470: 20 7b 0a 0a 23 20 70 72 6f 63 3a 20 20 3a 3a 75   {..# proc:  ::u
1480: 6e 64 6f 3a 3a 61 63 74 69 76 61 74 65 20 54 41  ndo::activate TA
1490: 42 4c 45 20 2e 2e 2e 0a 23 20 74 69 74 6c 65 3a  BLE ....# title:
14a0: 20 53 74 61 72 74 20 75 70 20 74 68 65 20 75 6e   Start up the un
14b0: 64 6f 2f 72 65 64 6f 20 73 79 73 74 65 6d 0a 23  do/redo system.#
14c0: 0a 23 20 41 72 67 75 6d 65 6e 74 73 20 73 68 6f  .# Arguments sho
14d0: 75 6c 64 20 62 65 20 6f 6e 65 20 6f 72 20 6d 6f  uld be one or mo
14e0: 72 65 20 64 61 74 61 62 61 73 65 20 74 61 62 6c  re database tabl
14f0: 65 73 20 28 69 6e 20 74 68 65 20 64 61 74 61 62  es (in the datab
1500: 61 73 65 20 61 73 73 6f 63 69 61 74 65 64 0a 23  ase associated.#
1510: 20 77 69 74 68 20 74 68 65 20 68 61 6e 64 6c 65   with the handle
1520: 20 22 64 62 22 29 20 77 68 6f 73 65 20 63 68 61   "db") whose cha
1530: 6e 67 65 73 20 61 72 65 20 74 6f 20 62 65 20 72  nges are to be r
1540: 65 63 6f 72 64 65 64 20 66 6f 72 20 75 6e 64 6f  ecorded for undo
1550: 2f 72 65 64 6f 0a 23 20 70 75 72 70 6f 73 65 73  /redo.# purposes
1560: 2e 0a 23 0a 70 72 6f 63 20 61 63 74 69 76 61 74  ..#.proc activat
1570: 65 20 7b 61 72 67 73 7d 20 7b 0a 20 20 76 61 72  e {args} {.  var
1580: 69 61 62 6c 65 20 5f 75 6e 64 6f 0a 20 20 69 66  iable _undo.  if
1590: 20 7b 24 5f 75 6e 64 6f 28 61 63 74 69 76 65 29   {$_undo(active)
15a0: 7d 20 72 65 74 75 72 6e 0a 20 20 65 76 61 6c 20  } return.  eval 
15b0: 5f 63 72 65 61 74 65 5f 74 72 69 67 67 65 72 73  _create_triggers
15c0: 20 64 62 20 24 61 72 67 73 0a 20 20 73 65 74 20   db $args.  set 
15d0: 5f 75 6e 64 6f 28 75 6e 64 6f 73 74 61 63 6b 29  _undo(undostack)
15e0: 20 7b 7d 0a 20 20 73 65 74 20 5f 75 6e 64 6f 28   {}.  set _undo(
15f0: 72 65 64 6f 73 74 61 63 6b 29 20 7b 7d 0a 20 20  redostack) {}.  
1600: 73 65 74 20 5f 75 6e 64 6f 28 61 63 74 69 76 65  set _undo(active
1610: 29 20 31 0a 20 20 73 65 74 20 5f 75 6e 64 6f 28  ) 1.  set _undo(
1620: 66 72 65 65 7a 65 29 20 2d 31 0a 20 20 5f 73 74  freeze) -1.  _st
1630: 61 72 74 5f 69 6e 74 65 72 76 61 6c 0a 7d 0a 0a  art_interval.}..
1640: 23 20 70 72 6f 63 3a 20 20 3a 3a 75 6e 64 6f 3a  # proc:  ::undo:
1650: 3a 64 65 61 63 74 69 76 61 74 65 0a 23 20 74 69  :deactivate.# ti
1660: 74 6c 65 3a 20 48 61 6c 74 20 74 68 65 20 75 6e  tle: Halt the un
1670: 64 6f 2f 72 65 64 6f 20 73 79 73 74 65 6d 20 61  do/redo system a
1680: 6e 64 20 64 65 6c 65 74 65 20 74 68 65 20 75 6e  nd delete the un
1690: 64 6f 2f 72 65 64 6f 20 73 74 61 63 6b 73 0a 23  do/redo stacks.#
16a0: 0a 70 72 6f 63 20 64 65 61 63 74 69 76 61 74 65  .proc deactivate
16b0: 20 7b 7d 20 7b 0a 20 20 76 61 72 69 61 62 6c 65   {} {.  variable
16c0: 20 5f 75 6e 64 6f 0a 20 20 69 66 20 7b 21 24 5f   _undo.  if {!$_
16d0: 75 6e 64 6f 28 61 63 74 69 76 65 29 7d 20 72 65  undo(active)} re
16e0: 74 75 72 6e 0a 20 20 5f 64 72 6f 70 5f 74 72 69  turn.  _drop_tri
16f0: 67 67 65 72 73 20 64 62 0a 20 20 73 65 74 20 5f  ggers db.  set _
1700: 75 6e 64 6f 28 75 6e 64 6f 73 74 61 63 6b 29 20  undo(undostack) 
1710: 7b 7d 0a 20 20 73 65 74 20 5f 75 6e 64 6f 28 72  {}.  set _undo(r
1720: 65 64 6f 73 74 61 63 6b 29 20 7b 7d 0a 20 20 73  edostack) {}.  s
1730: 65 74 20 5f 75 6e 64 6f 28 61 63 74 69 76 65 29  et _undo(active)
1740: 20 30 0a 20 20 73 65 74 20 5f 75 6e 64 6f 28 66   0.  set _undo(f
1750: 72 65 65 7a 65 29 20 2d 31 0a 7d 0a 0a 23 20 70  reeze) -1.}..# p
1760: 72 6f 63 3a 20 20 3a 3a 75 6e 64 6f 3a 3a 66 72  roc:  ::undo::fr
1770: 65 65 7a 65 0a 23 20 74 69 74 6c 65 3a 20 53 74  eeze.# title: St
1780: 6f 70 20 61 63 63 65 70 74 69 6e 67 20 64 61 74  op accepting dat
1790: 61 62 61 73 65 20 63 68 61 6e 67 65 73 20 69 6e  abase changes in
17a0: 74 6f 20 74 68 65 20 75 6e 64 6f 20 73 74 61 63  to the undo stac
17b0: 6b 0a 23 0a 23 20 46 72 6f 6d 20 74 68 65 20 70  k.#.# From the p
17c0: 6f 69 6e 74 20 77 68 65 6e 20 74 68 69 73 20 72  oint when this r
17d0: 6f 75 74 69 6e 65 20 69 73 20 63 61 6c 6c 65 64  outine is called
17e0: 20 75 70 20 75 6e 74 69 6c 20 74 68 65 20 6e 65   up until the ne
17f0: 78 74 20 75 6e 66 72 65 65 7a 65 2c 0a 23 20 6e  xt unfreeze,.# n
1800: 65 77 20 64 61 74 61 62 61 73 65 20 63 68 61 6e  ew database chan
1810: 67 65 73 20 61 72 65 20 72 65 6a 65 63 74 65 64  ges are rejected
1820: 20 66 72 6f 6d 20 74 68 65 20 75 6e 64 6f 20 73   from the undo s
1830: 74 61 63 6b 2e 0a 23 0a 70 72 6f 63 20 66 72 65  tack..#.proc fre
1840: 65 7a 65 20 7b 7d 20 7b 0a 20 20 76 61 72 69 61  eze {} {.  varia
1850: 62 6c 65 20 5f 75 6e 64 6f 0a 20 20 69 66 20 7b  ble _undo.  if {
1860: 21 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 5f 75  ![info exists _u
1870: 6e 64 6f 28 66 72 65 65 7a 65 29 5d 7d 20 72 65  ndo(freeze)]} re
1880: 74 75 72 6e 0a 20 20 69 66 20 7b 24 5f 75 6e 64  turn.  if {$_und
1890: 6f 28 66 72 65 65 7a 65 29 3e 3d 30 7d 20 7b 65  o(freeze)>=0} {e
18a0: 72 72 6f 72 20 22 72 65 63 75 72 73 69 76 65 20  rror "recursive 
18b0: 63 61 6c 6c 20 74 6f 20 3a 3a 75 6e 64 6f 3a 3a  call to ::undo::
18c0: 66 72 65 65 7a 65 22 7d 0a 20 20 73 65 74 20 5f  freeze"}.  set _
18d0: 75 6e 64 6f 28 66 72 65 65 7a 65 29 20 5b 64 62  undo(freeze) [db
18e0: 20 6f 6e 65 20 7b 53 45 4c 45 43 54 20 63 6f 61   one {SELECT coa
18f0: 6c 65 73 63 65 28 6d 61 78 28 73 65 71 29 2c 30  lesce(max(seq),0
1900: 29 20 46 52 4f 4d 20 75 6e 64 6f 6c 6f 67 7d 5d  ) FROM undolog}]
1910: 0a 7d 0a 0a 23 20 70 72 6f 63 3a 20 20 3a 3a 75  .}..# proc:  ::u
1920: 6e 64 6f 3a 3a 75 6e 66 72 65 65 7a 65 0a 23 20  ndo::unfreeze.# 
1930: 74 69 74 6c 65 3a 20 42 65 67 69 6e 20 61 63 63  title: Begin acc
1940: 65 70 74 69 6e 67 20 75 6e 64 6f 20 61 63 74 69  epting undo acti
1950: 6f 6e 73 20 61 67 61 69 6e 2e 0a 23 0a 70 72 6f  ons again..#.pro
1960: 63 20 75 6e 66 72 65 65 7a 65 20 7b 7d 20 7b 0a  c unfreeze {} {.
1970: 20 20 76 61 72 69 61 62 6c 65 20 5f 75 6e 64 6f    variable _undo
1980: 0a 20 20 69 66 20 7b 21 5b 69 6e 66 6f 20 65 78  .  if {![info ex
1990: 69 73 74 73 20 5f 75 6e 64 6f 28 66 72 65 65 7a  ists _undo(freez
19a0: 65 29 5d 7d 20 72 65 74 75 72 6e 0a 20 20 69 66  e)]} return.  if
19b0: 20 7b 24 5f 75 6e 64 6f 28 66 72 65 65 7a 65 29   {$_undo(freeze)
19c0: 3c 30 7d 20 7b 65 72 72 6f 72 20 22 63 61 6c 6c  <0} {error "call
19d0: 65 64 20 3a 3a 75 6e 64 6f 3a 3a 75 6e 66 72 65  ed ::undo::unfre
19e0: 65 7a 65 20 77 68 69 6c 65 20 6e 6f 74 20 66 72  eze while not fr
19f0: 6f 7a 65 6e 22 7d 0a 20 20 64 62 20 65 76 61 6c  ozen"}.  db eval
1a00: 20 22 44 45 4c 45 54 45 20 46 52 4f 4d 20 75 6e   "DELETE FROM un
1a10: 64 6f 6c 6f 67 20 57 48 45 52 45 20 73 65 71 3e  dolog WHERE seq>
1a20: 24 5f 75 6e 64 6f 28 66 72 65 65 7a 65 29 22 0a  $_undo(freeze)".
1a30: 20 20 73 65 74 20 5f 75 6e 64 6f 28 66 72 65 65    set _undo(free
1a40: 7a 65 29 20 2d 31 0a 7d 0a 0a 23 20 70 72 6f 63  ze) -1.}..# proc
1a50: 3a 20 20 3a 3a 75 6e 64 6f 3a 3a 65 76 65 6e 74  :  ::undo::event
1a60: 0a 23 20 74 69 74 6c 65 3a 20 53 6f 6d 65 74 68  .# title: Someth
1a70: 69 6e 67 20 75 6e 64 6f 61 62 6c 65 20 68 61 73  ing undoable has
1a80: 20 68 61 70 70 65 6e 65 64 0a 23 0a 23 20 54 68   happened.#.# Th
1a90: 69 73 20 72 6f 75 74 69 6e 65 20 69 73 20 63 61  is routine is ca
1aa0: 6c 6c 65 64 20 77 68 65 6e 65 76 65 72 20 61 6e  lled whenever an
1ab0: 20 75 6e 64 6f 61 62 6c 65 20 61 63 74 69 6f 6e   undoable action
1ac0: 20 6f 63 63 75 72 73 2e 20 20 41 72 72 61 6e 67   occurs.  Arrang
1ad0: 65 6d 65 6e 74 73 0a 23 20 61 72 65 20 6d 61 64  ements.# are mad
1ae0: 65 20 74 6f 20 69 6e 76 6f 6b 65 20 3a 3a 75 6e  e to invoke ::un
1af0: 64 6f 3a 3a 62 61 72 72 69 65 72 20 6e 6f 20 6c  do::barrier no l
1b00: 61 74 65 72 20 74 68 61 6e 20 74 68 65 20 6e 65  ater than the ne
1b10: 78 74 20 69 64 6c 65 20 6d 6f 6d 65 6e 74 2e 0a  xt idle moment..
1b20: 23 0a 70 72 6f 63 20 65 76 65 6e 74 20 7b 7d 20  #.proc event {} 
1b30: 7b 0a 20 20 76 61 72 69 61 62 6c 65 20 5f 75 6e  {.  variable _un
1b40: 64 6f 0a 20 20 69 66 20 7b 24 5f 75 6e 64 6f 28  do.  if {$_undo(
1b50: 70 65 6e 64 69 6e 67 29 3d 3d 22 22 7d 20 7b 0a  pending)==""} {.
1b60: 20 20 20 20 73 65 74 20 5f 75 6e 64 6f 28 70 65      set _undo(pe
1b70: 6e 64 69 6e 67 29 20 5b 61 66 74 65 72 20 69 64  nding) [after id
1b80: 6c 65 20 3a 3a 75 6e 64 6f 3a 3a 62 61 72 72 69  le ::undo::barri
1b90: 65 72 5d 0a 20 20 7d 0a 7d 0a 0a 23 20 70 72 6f  er].  }.}..# pro
1ba0: 63 3a 20 20 3a 3a 75 6e 64 6f 3a 3a 62 61 72 72  c:  ::undo::barr
1bb0: 69 65 72 0a 23 20 74 69 74 6c 65 3a 20 43 72 65  ier.# title: Cre
1bc0: 61 74 65 20 61 6e 20 75 6e 64 6f 20 62 61 72 72  ate an undo barr
1bd0: 69 65 72 20 72 69 67 68 74 20 6e 6f 77 2e 0a 23  ier right now..#
1be0: 0a 70 72 6f 63 20 62 61 72 72 69 65 72 20 7b 7d  .proc barrier {}
1bf0: 20 7b 0a 20 20 76 61 72 69 61 62 6c 65 20 5f 75   {.  variable _u
1c00: 6e 64 6f 0a 20 20 63 61 74 63 68 20 7b 61 66 74  ndo.  catch {aft
1c10: 65 72 20 63 61 6e 63 65 6c 20 24 5f 75 6e 64 6f  er cancel $_undo
1c20: 28 70 65 6e 64 69 6e 67 29 7d 0a 20 20 73 65 74  (pending)}.  set
1c30: 20 5f 75 6e 64 6f 28 70 65 6e 64 69 6e 67 29 20   _undo(pending) 
1c40: 7b 7d 0a 20 20 69 66 20 7b 21 24 5f 75 6e 64 6f  {}.  if {!$_undo
1c50: 28 61 63 74 69 76 65 29 7d 20 7b 0a 20 20 20 20  (active)} {.    
1c60: 72 65 66 72 65 73 68 0a 20 20 20 20 72 65 74 75  refresh.    retu
1c70: 72 6e 0a 20 20 7d 0a 20 20 73 65 74 20 65 6e 64  rn.  }.  set end
1c80: 20 5b 64 62 20 6f 6e 65 20 7b 53 45 4c 45 43 54   [db one {SELECT
1c90: 20 63 6f 61 6c 65 73 63 65 28 6d 61 78 28 73 65   coalesce(max(se
1ca0: 71 29 2c 30 29 20 46 52 4f 4d 20 75 6e 64 6f 6c  q),0) FROM undol
1cb0: 6f 67 7d 5d 0a 20 20 69 66 20 7b 24 5f 75 6e 64  og}].  if {$_und
1cc0: 6f 28 66 72 65 65 7a 65 29 3e 3d 30 20 26 26 20  o(freeze)>=0 && 
1cd0: 24 65 6e 64 3e 24 5f 75 6e 64 6f 28 66 72 65 65  $end>$_undo(free
1ce0: 7a 65 29 7d 20 7b 73 65 74 20 65 6e 64 20 24 5f  ze)} {set end $_
1cf0: 75 6e 64 6f 28 66 72 65 65 7a 65 29 7d 0a 20 20  undo(freeze)}.  
1d00: 73 65 74 20 62 65 67 69 6e 20 24 5f 75 6e 64 6f  set begin $_undo
1d10: 28 66 69 72 73 74 6c 6f 67 29 0a 20 20 5f 73 74  (firstlog).  _st
1d20: 61 72 74 5f 69 6e 74 65 72 76 61 6c 0a 20 20 69  art_interval.  i
1d30: 66 20 7b 24 62 65 67 69 6e 3d 3d 24 5f 75 6e 64  f {$begin==$_und
1d40: 6f 28 66 69 72 73 74 6c 6f 67 29 7d 20 7b 0a 20  o(firstlog)} {. 
1d50: 20 20 20 72 65 66 72 65 73 68 0a 20 20 20 20 72     refresh.    r
1d60: 65 74 75 72 6e 0a 20 20 7d 0a 20 20 6c 61 70 70  eturn.  }.  lapp
1d70: 65 6e 64 20 5f 75 6e 64 6f 28 75 6e 64 6f 73 74  end _undo(undost
1d80: 61 63 6b 29 20 5b 6c 69 73 74 20 24 62 65 67 69  ack) [list $begi
1d90: 6e 20 24 65 6e 64 5d 0a 20 20 73 65 74 20 5f 75  n $end].  set _u
1da0: 6e 64 6f 28 72 65 64 6f 73 74 61 63 6b 29 20 7b  ndo(redostack) {
1db0: 7d 0a 20 20 72 65 66 72 65 73 68 0a 7d 0a 0a 23  }.  refresh.}..#
1dc0: 20 70 72 6f 63 3a 20 20 3a 3a 75 6e 64 6f 3a 3a   proc:  ::undo::
1dd0: 75 6e 64 6f 0a 23 20 74 69 74 6c 65 3a 20 44 6f  undo.# title: Do
1de0: 20 61 20 73 69 6e 67 6c 65 20 73 74 65 70 20 6f   a single step o
1df0: 66 20 75 6e 64 6f 0a 23 0a 70 72 6f 63 20 75 6e  f undo.#.proc un
1e00: 64 6f 20 7b 7d 20 7b 0a 20 20 5f 73 74 65 70 20  do {} {.  _step 
1e10: 75 6e 64 6f 73 74 61 63 6b 20 72 65 64 6f 73 74  undostack redost
1e20: 61 63 6b 0a 7d 0a 0a 23 20 70 72 6f 63 3a 20 20  ack.}..# proc:  
1e30: 3a 3a 75 6e 64 6f 3a 3a 72 65 64 6f 0a 23 20 74  ::undo::redo.# t
1e40: 69 74 6c 65 3a 20 52 65 64 6f 20 61 20 73 69 6e  itle: Redo a sin
1e50: 67 6c 65 20 73 74 65 70 0a 23 0a 70 72 6f 63 20  gle step.#.proc 
1e60: 72 65 64 6f 20 7b 7d 20 7b 0a 20 20 5f 73 74 65  redo {} {.  _ste
1e70: 70 20 72 65 64 6f 73 74 61 63 6b 20 75 6e 64 6f  p redostack undo
1e80: 73 74 61 63 6b 0a 7d 0a 0a 23 20 70 72 6f 63 3a  stack.}..# proc:
1e90: 20 20 20 3a 3a 75 6e 64 6f 3a 3a 72 65 66 72 65     ::undo::refre
1ea0: 73 68 0a 23 20 74 69 74 6c 65 3a 20 20 55 70 64  sh.# title:  Upd
1eb0: 61 74 65 20 74 68 65 20 73 74 61 74 75 73 20 6f  ate the status o
1ec0: 66 20 63 6f 6e 74 72 6f 6c 73 20 61 66 74 65 72  f controls after
1ed0: 20 61 20 64 61 74 61 62 61 73 65 20 63 68 61 6e   a database chan
1ee0: 67 65 0a 23 0a 23 20 54 68 65 20 75 6e 64 6f 20  ge.#.# The undo 
1ef0: 6d 6f 64 75 6c 65 20 63 61 6c 6c 73 20 74 68 69  module calls thi
1f00: 73 20 72 6f 75 74 69 6e 65 20 61 66 74 65 72 20  s routine after 
1f10: 61 6e 79 20 75 6e 64 6f 2f 72 65 64 6f 20 69 6e  any undo/redo in
1f20: 20 6f 72 64 65 72 20 74 6f 0a 23 20 63 61 75 73   order to.# caus
1f30: 65 20 63 6f 6e 74 72 6f 6c 73 20 67 72 61 79 20  e controls gray 
1f40: 6f 75 74 20 61 70 70 72 6f 70 72 69 61 74 65 6c  out appropriatel
1f50: 79 20 64 65 70 65 6e 64 69 6e 67 20 6f 6e 20 74  y depending on t
1f60: 68 65 20 63 75 72 72 65 6e 74 20 73 74 61 74 65  he current state
1f70: 0a 23 20 6f 66 20 74 68 65 20 64 61 74 61 62 61  .# of the databa
1f80: 73 65 2e 20 20 54 68 69 73 20 72 6f 75 74 69 6e  se.  This routin
1f90: 65 20 77 6f 72 6b 73 20 62 79 20 69 6e 76 6f 6b  e works by invok
1fa0: 69 6e 67 20 74 68 65 20 73 74 61 74 75 73 5f 72  ing the status_r
1fb0: 65 66 72 65 73 68 0a 23 20 6d 6f 64 75 6c 65 20  efresh.# module 
1fc0: 69 6e 20 61 6c 6c 20 74 6f 70 2d 6c 65 76 65 6c  in all top-level
1fd0: 20 6e 61 6d 65 73 70 61 63 65 73 2e 0a 23 0a 70   namespaces..#.p
1fe0: 72 6f 63 20 72 65 66 72 65 73 68 20 7b 7d 20 7b  roc refresh {} {
1ff0: 0a 20 20 73 65 74 20 62 6f 64 79 20 7b 7d 0a 20  .  set body {}. 
2000: 20 66 6f 72 65 61 63 68 20 6e 73 20 5b 6e 61 6d   foreach ns [nam
2010: 65 73 70 61 63 65 20 63 68 69 6c 64 72 65 6e 20  espace children 
2020: 3a 3a 5d 20 7b 0a 20 20 20 20 69 66 20 7b 5b 69  ::] {.    if {[i
2030: 6e 66 6f 20 70 72 6f 63 20 24 7b 6e 73 7d 3a 3a  nfo proc ${ns}::
2040: 73 74 61 74 75 73 5f 72 65 66 72 65 73 68 5d 3d  status_refresh]=
2050: 3d 22 22 7d 20 63 6f 6e 74 69 6e 75 65 0a 20 20  =""} continue.  
2060: 20 20 61 70 70 65 6e 64 20 62 6f 64 79 20 24 7b    append body ${
2070: 6e 73 7d 3a 3a 73 74 61 74 75 73 5f 72 65 66 72  ns}::status_refr
2080: 65 73 68 5c 6e 0a 20 20 7d 0a 20 20 70 72 6f 63  esh\n.  }.  proc
2090: 20 3a 3a 75 6e 64 6f 3a 3a 72 65 66 72 65 73 68   ::undo::refresh
20a0: 20 7b 7d 20 24 62 6f 64 79 0a 20 20 72 65 66 72   {} $body.  refr
20b0: 65 73 68 0a 7d 0a 0a 23 20 70 72 6f 63 3a 20 20  esh.}..# proc:  
20c0: 20 3a 3a 75 6e 64 6f 3a 3a 72 65 6c 6f 61 64 5f   ::undo::reload_
20d0: 61 6c 6c 0a 23 20 74 69 74 6c 65 3a 20 20 52 65  all.# title:  Re
20e0: 64 72 61 77 20 65 76 65 72 79 74 68 69 6e 67 20  draw everything 
20f0: 62 61 73 65 64 20 6f 6e 20 74 68 65 20 63 75 72  based on the cur
2100: 72 65 6e 74 20 64 61 74 61 62 61 73 65 0a 23 0a  rent database.#.
2110: 23 20 54 68 65 20 75 6e 64 6f 20 6d 6f 64 75 6c  # The undo modul
2120: 65 20 63 61 6c 6c 73 20 74 68 69 73 20 72 6f 75  e calls this rou
2130: 74 69 6e 65 20 61 66 74 65 72 20 61 6e 79 20 75  tine after any u
2140: 6e 64 6f 2f 72 65 64 6f 20 69 6e 20 6f 72 64 65  ndo/redo in orde
2150: 72 20 74 6f 0a 23 20 63 61 75 73 65 20 74 68 65  r to.# cause the
2160: 20 73 63 72 65 65 6e 20 74 6f 20 62 65 20 63 6f   screen to be co
2170: 6d 70 6c 65 74 65 6c 79 20 72 65 64 72 61 77 6e  mpletely redrawn
2180: 20 62 61 73 65 64 20 6f 6e 20 74 68 65 20 63 75   based on the cu
2190: 72 72 65 6e 74 20 64 61 74 61 62 61 73 65 0a 23  rrent database.#
21a0: 20 63 6f 6e 74 65 6e 74 73 2e 20 20 54 68 69 73   contents.  This
21b0: 20 69 73 20 61 63 63 6f 6d 70 6c 69 73 68 65 64   is accomplished
21c0: 20 62 79 20 63 61 6c 6c 69 6e 67 20 74 68 65 20   by calling the 
21d0: 22 72 65 6c 6f 61 64 22 20 6d 6f 64 75 6c 65 20  "reload" module 
21e0: 69 6e 0a 23 20 65 76 65 72 79 20 74 6f 70 2d 6c  in.# every top-l
21f0: 65 76 65 6c 20 6e 61 6d 65 73 70 61 63 65 20 6f  evel namespace o
2200: 74 68 65 72 20 74 68 61 6e 20 3a 3a 75 6e 64 6f  ther than ::undo
2210: 2e 0a 23 0a 70 72 6f 63 20 72 65 6c 6f 61 64 5f  ..#.proc reload_
2220: 61 6c 6c 20 7b 7d 20 7b 0a 20 20 73 65 74 20 62  all {} {.  set b
2230: 6f 64 79 20 7b 7d 0a 20 20 66 6f 72 65 61 63 68  ody {}.  foreach
2240: 20 6e 73 20 5b 6e 61 6d 65 73 70 61 63 65 20 63   ns [namespace c
2250: 68 69 6c 64 72 65 6e 20 3a 3a 5d 20 7b 0a 20 20  hildren ::] {.  
2260: 20 20 69 66 20 7b 5b 69 6e 66 6f 20 70 72 6f 63    if {[info proc
2270: 20 24 7b 6e 73 7d 3a 3a 72 65 6c 6f 61 64 5d 3d   ${ns}::reload]=
2280: 3d 22 22 7d 20 63 6f 6e 74 69 6e 75 65 0a 20 20  =""} continue.  
2290: 20 20 61 70 70 65 6e 64 20 62 6f 64 79 20 24 7b    append body ${
22a0: 6e 73 7d 3a 3a 72 65 6c 6f 61 64 5c 6e 0a 20 20  ns}::reload\n.  
22b0: 7d 0a 20 20 70 72 6f 63 20 3a 3a 75 6e 64 6f 3a  }.  proc ::undo:
22c0: 3a 72 65 6c 6f 61 64 5f 61 6c 6c 20 7b 7d 20 24  :reload_all {} $
22d0: 62 6f 64 79 0a 20 20 72 65 6c 6f 61 64 5f 61 6c  body.  reload_al
22e0: 6c 0a 7d 0a 0a 23 23 23 23 23 23 23 23 23 23 23  l.}..###########
22f0: 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23  ################
2300: 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23  ################
2310: 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23  ################
2320: 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23  ################
2330: 23 23 23 0a 23 20 54 68 65 20 70 75 62 6c 69 63  ###.# The public
2340: 20 69 6e 74 65 72 66 61 63 65 20 74 6f 20 74 68   interface to th
2350: 69 73 20 6d 6f 64 75 6c 65 20 69 73 20 61 62 6f  is module is abo
2360: 76 65 2e 20 20 52 6f 75 74 69 6e 65 73 20 61 6e  ve.  Routines an
2370: 64 20 76 61 72 69 61 62 6c 65 73 20 74 68 61 74  d variables that
2380: 0a 23 20 66 6f 6c 6c 6f 77 20 28 61 6e 64 20 77  .# follow (and w
2390: 68 6f 73 65 20 6e 61 6d 65 73 20 62 65 67 69 6e  hose names begin
23a0: 20 77 69 74 68 20 22 5f 22 29 20 61 72 65 20 70   with "_") are p
23b0: 72 69 76 61 74 65 20 74 6f 20 74 68 69 73 20 6d  rivate to this m
23c0: 6f 64 75 6c 65 2e 0a 23 23 23 23 23 23 23 23 23  odule..#########
23d0: 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23  ################
23e0: 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23  ################
23f0: 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23  ################
2400: 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23  ################
2410: 23 23 23 23 23 0a 0a 23 20 73 74 61 74 65 20 69  #####..# state i
2420: 6e 66 6f 72 6d 61 74 69 6f 6e 0a 23 0a 73 65 74  nformation.#.set
2430: 20 5f 75 6e 64 6f 28 61 63 74 69 76 65 29 20 30   _undo(active) 0
2440: 0a 73 65 74 20 5f 75 6e 64 6f 28 75 6e 64 6f 73  .set _undo(undos
2450: 74 61 63 6b 29 20 7b 7d 0a 73 65 74 20 5f 75 6e  tack) {}.set _un
2460: 64 6f 28 72 65 64 6f 73 74 61 63 6b 29 20 7b 7d  do(redostack) {}
2470: 0a 73 65 74 20 5f 75 6e 64 6f 28 70 65 6e 64 69  .set _undo(pendi
2480: 6e 67 29 20 7b 7d 0a 73 65 74 20 5f 75 6e 64 6f  ng) {}.set _undo
2490: 28 66 69 72 73 74 6c 6f 67 29 20 31 0a 73 65 74  (firstlog) 1.set
24a0: 20 5f 75 6e 64 6f 28 73 74 61 72 74 73 74 61 74   _undo(startstat
24b0: 65 29 20 7b 7d 0a 0a 0a 23 20 70 72 6f 63 3a 20  e) {}...# proc: 
24c0: 20 3a 3a 75 6e 64 6f 3a 3a 73 74 61 74 75 73 5f   ::undo::status_
24d0: 72 65 66 72 65 73 68 0a 23 20 74 69 74 6c 65 3a  refresh.# title:
24e0: 20 45 6e 61 62 6c 65 20 61 6e 64 2f 6f 72 20 64   Enable and/or d
24f0: 69 73 61 62 6c 65 20 6d 65 6e 75 20 6f 70 74 69  isable menu opti
2500: 6f 6e 73 20 61 20 62 75 74 74 6f 6e 73 0a 23 0a  ons a buttons.#.
2510: 70 72 6f 63 20 73 74 61 74 75 73 5f 72 65 66 72  proc status_refr
2520: 65 73 68 20 7b 7d 20 7b 0a 20 20 76 61 72 69 61  esh {} {.  varia
2530: 62 6c 65 20 5f 75 6e 64 6f 0a 20 20 69 66 20 7b  ble _undo.  if {
2540: 21 24 5f 75 6e 64 6f 28 61 63 74 69 76 65 29 20  !$_undo(active) 
2550: 7c 7c 20 5b 6c 6c 65 6e 67 74 68 20 24 5f 75 6e  || [llength $_un
2560: 64 6f 28 75 6e 64 6f 73 74 61 63 6b 29 5d 3d 3d  do(undostack)]==
2570: 30 7d 20 7b 0a 20 20 20 20 2e 6d 62 2e 65 64 69  0} {.    .mb.edi
2580: 74 20 65 6e 74 72 79 63 6f 6e 66 69 67 20 55 6e  t entryconfig Un
2590: 64 6f 20 2d 73 74 61 74 65 20 64 69 73 61 62 6c  do -state disabl
25a0: 65 64 0a 20 20 20 20 2e 62 62 2e 75 6e 64 6f 20  ed.    .bb.undo 
25b0: 63 6f 6e 66 69 67 20 2d 73 74 61 74 65 20 64 69  config -state di
25c0: 73 61 62 6c 65 64 0a 20 20 7d 20 65 6c 73 65 20  sabled.  } else 
25d0: 7b 0a 20 20 20 20 2e 6d 62 2e 65 64 69 74 20 65  {.    .mb.edit e
25e0: 6e 74 72 79 63 6f 6e 66 69 67 20 55 6e 64 6f 20  ntryconfig Undo 
25f0: 2d 73 74 61 74 65 20 6e 6f 72 6d 61 6c 0a 20 20  -state normal.  
2600: 20 20 2e 62 62 2e 75 6e 64 6f 20 63 6f 6e 66 69    .bb.undo confi
2610: 67 20 2d 73 74 61 74 65 20 6e 6f 72 6d 61 6c 0a  g -state normal.
2620: 20 20 7d 0a 20 20 69 66 20 7b 21 24 5f 75 6e 64    }.  if {!$_und
2630: 6f 28 61 63 74 69 76 65 29 20 7c 7c 20 5b 6c 6c  o(active) || [ll
2640: 65 6e 67 74 68 20 24 5f 75 6e 64 6f 28 72 65 64  ength $_undo(red
2650: 6f 73 74 61 63 6b 29 5d 3d 3d 30 7d 20 7b 0a 20  ostack)]==0} {. 
2660: 20 20 20 2e 6d 62 2e 65 64 69 74 20 65 6e 74 72     .mb.edit entr
2670: 79 63 6f 6e 66 69 67 20 52 65 64 6f 20 2d 73 74  yconfig Redo -st
2680: 61 74 65 20 64 69 73 61 62 6c 65 64 0a 20 20 20  ate disabled.   
2690: 20 2e 62 62 2e 72 65 64 6f 20 63 6f 6e 66 69 67   .bb.redo config
26a0: 20 2d 73 74 61 74 65 20 64 69 73 61 62 6c 65 64   -state disabled
26b0: 0a 20 20 7d 20 65 6c 73 65 20 7b 0a 20 20 20 20  .  } else {.    
26c0: 2e 6d 62 2e 65 64 69 74 20 65 6e 74 72 79 63 6f  .mb.edit entryco
26d0: 6e 66 69 67 20 52 65 64 6f 20 2d 73 74 61 74 65  nfig Redo -state
26e0: 20 6e 6f 72 6d 61 6c 0a 20 20 20 20 2e 62 62 2e   normal.    .bb.
26f0: 72 65 64 6f 20 63 6f 6e 66 69 67 20 2d 73 74 61  redo config -sta
2700: 74 65 20 6e 6f 72 6d 61 6c 0a 20 20 7d 0a 7d 0a  te normal.  }.}.
2710: 0a 23 20 78 70 72 6f 63 3a 20 20 3a 3a 75 6e 64  .# xproc:  ::und
2720: 6f 3a 3a 5f 63 72 65 61 74 65 5f 74 72 69 67 67  o::_create_trigg
2730: 65 72 73 20 44 42 20 54 41 42 4c 45 31 20 54 41  ers DB TABLE1 TA
2740: 42 4c 45 32 20 2e 2e 2e 0a 23 20 74 69 74 6c 65  BLE2 ....# title
2750: 3a 20 20 43 72 65 61 74 65 20 63 68 61 6e 67 65  :  Create change
2760: 20 72 65 63 6f 72 64 69 6e 67 20 74 72 69 67 67   recording trigg
2770: 65 72 73 20 66 6f 72 20 61 6c 6c 20 74 61 62 6c  ers for all tabl
2780: 65 73 20 6c 69 73 74 65 64 0a 23 0a 23 20 43 72  es listed.#.# Cr
2790: 65 61 74 65 20 61 20 74 65 6d 70 6f 72 61 72 79  eate a temporary
27a0: 20 74 61 62 6c 65 20 69 6e 20 74 68 65 20 64 61   table in the da
27b0: 74 61 62 61 73 65 20 6e 61 6d 65 64 20 22 75 6e  tabase named "un
27c0: 64 6f 6c 6f 67 22 2e 20 20 43 72 65 61 74 65 0a  dolog".  Create.
27d0: 23 20 74 72 69 67 67 65 72 73 20 74 68 61 74 20  # triggers that 
27e0: 66 69 72 65 20 6f 6e 20 61 6e 79 20 69 6e 73 65  fire on any inse
27f0: 72 74 2c 20 64 65 6c 65 74 65 2c 20 6f 72 20 75  rt, delete, or u
2800: 70 64 61 74 65 20 6f 66 20 54 41 42 4c 45 31 2c  pdate of TABLE1,
2810: 20 54 41 42 4c 45 32 2c 20 2e 2e 2e 2e 0a 23 20   TABLE2, .....# 
2820: 57 68 65 6e 20 74 68 6f 73 65 20 74 72 69 67 67  When those trigg
2830: 65 72 73 20 66 69 72 65 2c 20 69 6e 73 65 72 74  ers fire, insert
2840: 20 72 65 63 6f 72 64 73 20 69 6e 20 75 6e 64 6f   records in undo
2850: 6c 6f 67 20 74 68 61 74 20 63 6f 6e 74 61 69 6e  log that contain
2860: 0a 23 20 53 51 4c 20 74 65 78 74 20 66 6f 72 20  .# SQL text for 
2870: 73 74 61 74 65 6d 65 6e 74 73 20 74 68 61 74 20  statements that 
2880: 77 69 6c 6c 20 75 6e 64 6f 20 74 68 65 20 69 6e  will undo the in
2890: 73 65 72 74 2c 20 64 65 6c 65 74 65 2c 20 6f 72  sert, delete, or
28a0: 20 75 70 64 61 74 65 2e 0a 23 0a 70 72 6f 63 20   update..#.proc 
28b0: 5f 63 72 65 61 74 65 5f 74 72 69 67 67 65 72 73  _create_triggers
28c0: 20 7b 64 62 20 61 72 67 73 7d 20 7b 0a 20 20 63   {db args} {.  c
28d0: 61 74 63 68 20 7b 24 64 62 20 65 76 61 6c 20 7b  atch {$db eval {
28e0: 44 52 4f 50 20 54 41 42 4c 45 20 75 6e 64 6f 6c  DROP TABLE undol
28f0: 6f 67 7d 7d 0a 20 20 24 64 62 20 65 76 61 6c 20  og}}.  $db eval 
2900: 7b 43 52 45 41 54 45 20 54 45 4d 50 20 54 41 42  {CREATE TEMP TAB
2910: 4c 45 20 75 6e 64 6f 6c 6f 67 28 73 65 71 20 69  LE undolog(seq i
2920: 6e 74 65 67 65 72 20 70 72 69 6d 61 72 79 20 6b  nteger primary k
2930: 65 79 2c 20 73 71 6c 20 74 65 78 74 29 7d 0a 20  ey, sql text)}. 
2940: 20 66 6f 72 65 61 63 68 20 74 62 6c 20 24 61 72   foreach tbl $ar
2950: 67 73 20 7b 0a 20 20 20 20 73 65 74 20 63 6f 6c  gs {.    set col
2960: 6c 69 73 74 20 5b 24 64 62 20 65 76 61 6c 20 22  list [$db eval "
2970: 70 72 61 67 6d 61 20 74 61 62 6c 65 5f 69 6e 66  pragma table_inf
2980: 6f 28 24 74 62 6c 29 22 5d 0a 20 20 20 20 73 65  o($tbl)"].    se
2990: 74 20 73 71 6c 20 22 43 52 45 41 54 45 20 54 45  t sql "CREATE TE
29a0: 4d 50 20 54 52 49 47 47 45 52 20 5f 24 7b 74 62  MP TRIGGER _${tb
29b0: 6c 7d 5f 69 74 20 41 46 54 45 52 20 49 4e 53 45  l}_it AFTER INSE
29c0: 52 54 20 4f 4e 20 24 74 62 6c 20 42 45 47 49 4e  RT ON $tbl BEGIN
29d0: 5c 6e 22 0a 20 20 20 20 61 70 70 65 6e 64 20 73  \n".    append s
29e0: 71 6c 20 22 20 20 49 4e 53 45 52 54 20 49 4e 54  ql "  INSERT INT
29f0: 4f 20 75 6e 64 6f 6c 6f 67 20 56 41 4c 55 45 53  O undolog VALUES
2a00: 28 4e 55 4c 4c 2c 22 0a 20 20 20 20 61 70 70 65  (NULL,".    appe
2a10: 6e 64 20 73 71 6c 20 22 27 44 45 4c 45 54 45 20  nd sql "'DELETE 
2a20: 46 52 4f 4d 20 24 74 62 6c 20 57 48 45 52 45 20  FROM $tbl WHERE 
2a30: 72 6f 77 69 64 3d 27 7c 7c 6e 65 77 2e 72 6f 77  rowid='||new.row
2a40: 69 64 29 3b 5c 6e 45 4e 44 3b 5c 6e 22 0a 0a 20  id);\nEND;\n".. 
2a50: 20 20 20 61 70 70 65 6e 64 20 73 71 6c 20 22 43     append sql "C
2a60: 52 45 41 54 45 20 54 45 4d 50 20 54 52 49 47 47  REATE TEMP TRIGG
2a70: 45 52 20 5f 24 7b 74 62 6c 7d 5f 75 74 20 41 46  ER _${tbl}_ut AF
2a80: 54 45 52 20 55 50 44 41 54 45 20 4f 4e 20 24 74  TER UPDATE ON $t
2a90: 62 6c 20 42 45 47 49 4e 5c 6e 22 0a 20 20 20 20  bl BEGIN\n".    
2aa0: 61 70 70 65 6e 64 20 73 71 6c 20 22 20 20 49 4e  append sql "  IN
2ab0: 53 45 52 54 20 49 4e 54 4f 20 75 6e 64 6f 6c 6f  SERT INTO undolo
2ac0: 67 20 56 41 4c 55 45 53 28 4e 55 4c 4c 2c 22 0a  g VALUES(NULL,".
2ad0: 20 20 20 20 61 70 70 65 6e 64 20 73 71 6c 20 22      append sql "
2ae0: 27 55 50 44 41 54 45 20 24 74 62 6c 20 22 0a 20  'UPDATE $tbl ". 
2af0: 20 20 20 73 65 74 20 73 65 70 20 22 53 45 54 20     set sep "SET 
2b00: 22 0a 20 20 20 20 66 6f 72 65 61 63 68 20 7b 78  ".    foreach {x
2b10: 31 20 6e 61 6d 65 20 78 32 20 78 33 20 78 34 20  1 name x2 x3 x4 
2b20: 78 35 7d 20 24 63 6f 6c 6c 69 73 74 20 7b 0a 20  x5} $collist {. 
2b30: 20 20 20 20 20 61 70 70 65 6e 64 20 73 71 6c 20       append sql 
2b40: 22 24 73 65 70 24 6e 61 6d 65 3d 27 7c 7c 71 75  "$sep$name='||qu
2b50: 6f 74 65 28 6f 6c 64 2e 24 6e 61 6d 65 29 7c 7c  ote(old.$name)||
2b60: 27 22 0a 20 20 20 20 20 20 73 65 74 20 73 65 70  '".      set sep
2b70: 20 22 2c 22 0a 20 20 20 20 7d 0a 20 20 20 20 61   ",".    }.    a
2b80: 70 70 65 6e 64 20 73 71 6c 20 22 20 57 48 45 52  ppend sql " WHER
2b90: 45 20 72 6f 77 69 64 3d 27 7c 7c 6f 6c 64 2e 72  E rowid='||old.r
2ba0: 6f 77 69 64 29 3b 5c 6e 45 4e 44 3b 5c 6e 22 0a  owid);\nEND;\n".
2bb0: 0a 20 20 20 20 61 70 70 65 6e 64 20 73 71 6c 20  .    append sql 
2bc0: 22 43 52 45 41 54 45 20 54 45 4d 50 20 54 52 49  "CREATE TEMP TRI
2bd0: 47 47 45 52 20 5f 24 7b 74 62 6c 7d 5f 64 74 20  GGER _${tbl}_dt 
2be0: 42 45 46 4f 52 45 20 44 45 4c 45 54 45 20 4f 4e  BEFORE DELETE ON
2bf0: 20 24 74 62 6c 20 42 45 47 49 4e 5c 6e 22 0a 20   $tbl BEGIN\n". 
2c00: 20 20 20 61 70 70 65 6e 64 20 73 71 6c 20 22 20     append sql " 
2c10: 20 49 4e 53 45 52 54 20 49 4e 54 4f 20 75 6e 64   INSERT INTO und
2c20: 6f 6c 6f 67 20 56 41 4c 55 45 53 28 4e 55 4c 4c  olog VALUES(NULL
2c30: 2c 22 0a 20 20 20 20 61 70 70 65 6e 64 20 73 71  ,".    append sq
2c40: 6c 20 22 27 49 4e 53 45 52 54 20 49 4e 54 4f 20  l "'INSERT INTO 
2c50: 24 7b 74 62 6c 7d 28 72 6f 77 69 64 22 0a 20 20  ${tbl}(rowid".  
2c60: 20 20 66 6f 72 65 61 63 68 20 7b 78 31 20 6e 61    foreach {x1 na
2c70: 6d 65 20 78 32 20 78 33 20 78 34 20 78 35 7d 20  me x2 x3 x4 x5} 
2c80: 24 63 6f 6c 6c 69 73 74 20 7b 61 70 70 65 6e 64  $collist {append
2c90: 20 73 71 6c 20 2c 24 6e 61 6d 65 7d 0a 20 20 20   sql ,$name}.   
2ca0: 20 61 70 70 65 6e 64 20 73 71 6c 20 22 29 20 56   append sql ") V
2cb0: 41 4c 55 45 53 28 27 7c 7c 6f 6c 64 2e 72 6f 77  ALUES('||old.row
2cc0: 69 64 7c 7c 27 22 0a 20 20 20 20 66 6f 72 65 61  id||'".    forea
2cd0: 63 68 20 7b 78 31 20 6e 61 6d 65 20 78 32 20 78  ch {x1 name x2 x
2ce0: 33 20 78 34 20 78 35 7d 20 24 63 6f 6c 6c 69 73  3 x4 x5} $collis
2cf0: 74 20 7b 61 70 70 65 6e 64 20 73 71 6c 20 2c 27  t {append sql ,'
2d00: 7c 7c 71 75 6f 74 65 28 6f 6c 64 2e 24 6e 61 6d  ||quote(old.$nam
2d10: 65 29 7c 7c 27 7d 0a 20 20 20 20 61 70 70 65 6e  e)||'}.    appen
2d20: 64 20 73 71 6c 20 22 29 27 29 3b 5c 6e 45 4e 44  d sql ")');\nEND
2d30: 3b 5c 6e 22 0a 0a 20 20 20 20 24 64 62 20 65 76  ;\n"..    $db ev
2d40: 61 6c 20 24 73 71 6c 0a 20 20 7d 0a 7d 0a 0a 23  al $sql.  }.}..#
2d50: 20 78 70 72 6f 63 3a 20 20 3a 3a 75 6e 64 6f 3a   xproc:  ::undo:
2d60: 3a 5f 64 72 6f 70 5f 74 72 69 67 67 65 72 73 20  :_drop_triggers 
2d70: 44 42 0a 23 20 74 69 74 6c 65 3a 20 20 44 72 6f  DB.# title:  Dro
2d80: 70 20 61 6c 6c 20 6f 66 20 74 68 65 20 74 72 69  p all of the tri
2d90: 67 67 65 72 73 20 74 68 61 74 20 5f 63 72 65 61  ggers that _crea
2da0: 74 65 5f 74 72 69 67 67 65 72 73 20 63 72 65 61  te_triggers crea
2db0: 74 65 64 0a 23 0a 70 72 6f 63 20 5f 64 72 6f 70  ted.#.proc _drop
2dc0: 5f 74 72 69 67 67 65 72 73 20 7b 64 62 7d 20 7b  _triggers {db} {
2dd0: 0a 20 20 73 65 74 20 74 6c 69 73 74 20 5b 24 64  .  set tlist [$d
2de0: 62 20 65 76 61 6c 20 7b 53 45 4c 45 43 54 20 6e  b eval {SELECT n
2df0: 61 6d 65 20 46 52 4f 4d 20 73 71 6c 69 74 65 5f  ame FROM sqlite_
2e00: 74 65 6d 70 5f 6d 61 73 74 65 72 0a 20 20 20 20  temp_master.    
2e10: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
2e20: 20 20 20 57 48 45 52 45 20 74 79 70 65 3d 27 74     WHERE type='t
2e30: 72 69 67 67 65 72 27 7d 5d 0a 20 20 66 6f 72 65  rigger'}].  fore
2e40: 61 63 68 20 74 72 69 67 67 65 72 20 24 74 6c 69  ach trigger $tli
2e50: 73 74 20 7b 0a 20 20 20 20 69 66 20 7b 21 5b 72  st {.    if {![r
2e60: 65 67 65 78 70 20 7b 5e 5f 2e 2a 5f 28 69 7c 75  egexp {^_.*_(i|u
2e70: 7c 64 29 74 24 7d 20 24 74 72 69 67 67 65 72 5d  |d)t$} $trigger]
2e80: 7d 20 63 6f 6e 74 69 6e 75 65 0a 20 20 20 20 24  } continue.    $
2e90: 64 62 20 65 76 61 6c 20 22 44 52 4f 50 20 54 52  db eval "DROP TR
2ea0: 49 47 47 45 52 20 24 74 72 69 67 67 65 72 3b 22  IGGER $trigger;"
2eb0: 0a 20 20 7d 0a 20 20 63 61 74 63 68 20 7b 24 64  .  }.  catch {$d
2ec0: 62 20 65 76 61 6c 20 7b 44 52 4f 50 20 54 41 42  b eval {DROP TAB
2ed0: 4c 45 20 75 6e 64 6f 6c 6f 67 7d 7d 0a 7d 0a 0a  LE undolog}}.}..
2ee0: 23 20 78 70 72 6f 63 3a 20 3a 3a 75 6e 64 6f 3a  # xproc: ::undo:
2ef0: 3a 5f 73 74 61 72 74 5f 69 6e 74 65 72 76 61 6c  :_start_interval
2f00: 0a 23 20 74 69 74 6c 65 3a 20 52 65 63 6f 72 64  .# title: Record
2f10: 20 74 68 65 20 73 74 61 72 74 69 6e 67 20 63 6f   the starting co
2f20: 6e 64 69 74 69 6f 6e 73 20 6f 66 20 61 6e 20 75  nditions of an u
2f30: 6e 64 6f 20 69 6e 74 65 72 76 61 6c 0a 23 0a 70  ndo interval.#.p
2f40: 72 6f 63 20 5f 73 74 61 72 74 5f 69 6e 74 65 72  roc _start_inter
2f50: 76 61 6c 20 7b 7d 20 7b 0a 20 20 76 61 72 69 61  val {} {.  varia
2f60: 62 6c 65 20 5f 75 6e 64 6f 0a 20 20 73 65 74 20  ble _undo.  set 
2f70: 5f 75 6e 64 6f 28 66 69 72 73 74 6c 6f 67 29 20  _undo(firstlog) 
2f80: 5b 64 62 20 6f 6e 65 20 7b 53 45 4c 45 43 54 20  [db one {SELECT 
2f90: 63 6f 61 6c 65 73 63 65 28 6d 61 78 28 73 65 71  coalesce(max(seq
2fa0: 29 2c 30 29 2b 31 20 46 52 4f 4d 20 75 6e 64 6f  ),0)+1 FROM undo
2fb0: 6c 6f 67 7d 5d 0a 7d 0a 0a 23 20 78 70 72 6f 63  log}].}..# xproc
2fc0: 3a 20 3a 3a 75 6e 64 6f 3a 3a 5f 73 74 65 70 20  : ::undo::_step 
2fd0: 56 31 20 56 32 0a 23 20 74 69 74 6c 65 3a 20 44  V1 V2.# title: D
2fe0: 6f 20 61 20 73 69 6e 67 6c 65 20 73 74 65 70 20  o a single step 
2ff0: 6f 66 20 75 6e 64 6f 20 6f 72 20 72 65 64 6f 0a  of undo or redo.
3000: 23 0a 23 20 46 6f 72 20 61 6e 20 75 6e 64 6f 20  #.# For an undo 
3010: 56 31 3d 3d 22 75 6e 64 6f 73 74 61 63 6b 22 20  V1=="undostack" 
3020: 61 6e 64 20 56 32 3d 3d 22 72 65 64 6f 73 74 61  and V2=="redosta
3030: 63 6b 22 2e 20 20 46 6f 72 20 61 20 72 65 64 6f  ck".  For a redo
3040: 2c 0a 23 20 56 31 3d 3d 22 72 65 64 6f 73 74 61  ,.# V1=="redosta
3050: 63 6b 22 20 61 6e 64 20 56 32 3d 3d 22 75 6e 64  ck" and V2=="und
3060: 6f 73 74 61 63 6b 22 2e 0a 23 0a 70 72 6f 63 20  ostack"..#.proc 
3070: 5f 73 74 65 70 20 7b 76 31 20 76 32 7d 20 7b 0a  _step {v1 v2} {.
3080: 20 20 76 61 72 69 61 62 6c 65 20 5f 75 6e 64 6f    variable _undo
3090: 0a 20 20 73 65 74 20 6f 70 20 5b 6c 69 6e 64 65  .  set op [linde
30a0: 78 20 24 5f 75 6e 64 6f 28 24 76 31 29 20 65 6e  x $_undo($v1) en
30b0: 64 5d 0a 20 20 73 65 74 20 5f 75 6e 64 6f 28 24  d].  set _undo($
30c0: 76 31 29 20 5b 6c 72 61 6e 67 65 20 24 5f 75 6e  v1) [lrange $_un
30d0: 64 6f 28 24 76 31 29 20 30 20 65 6e 64 2d 31 5d  do($v1) 0 end-1]
30e0: 0a 20 20 66 6f 72 65 61 63 68 20 7b 62 65 67 69  .  foreach {begi
30f0: 6e 20 65 6e 64 7d 20 24 6f 70 20 62 72 65 61 6b  n end} $op break
3100: 0a 20 20 64 62 20 65 76 61 6c 20 42 45 47 49 4e  .  db eval BEGIN
3110: 0a 20 20 73 65 74 20 71 31 20 22 53 45 4c 45 43  .  set q1 "SELEC
3120: 54 20 73 71 6c 20 46 52 4f 4d 20 75 6e 64 6f 6c  T sql FROM undol
3130: 6f 67 20 57 48 45 52 45 20 73 65 71 3e 3d 24 62  og WHERE seq>=$b
3140: 65 67 69 6e 20 41 4e 44 20 73 65 71 3c 3d 24 65  egin AND seq<=$e
3150: 6e 64 0a 20 20 20 20 20 20 20 20 20 20 4f 52 44  nd.          ORD
3160: 45 52 20 42 59 20 73 65 71 20 44 45 53 43 22 0a  ER BY seq DESC".
3170: 20 20 73 65 74 20 73 71 6c 6c 69 73 74 20 5b 64    set sqllist [d
3180: 62 20 65 76 61 6c 20 24 71 31 5d 0a 20 20 64 62  b eval $q1].  db
3190: 20 65 76 61 6c 20 22 44 45 4c 45 54 45 20 46 52   eval "DELETE FR
31a0: 4f 4d 20 75 6e 64 6f 6c 6f 67 20 57 48 45 52 45  OM undolog WHERE
31b0: 20 73 65 71 3e 3d 24 62 65 67 69 6e 20 41 4e 44   seq>=$begin AND
31c0: 20 73 65 71 3c 3d 24 65 6e 64 22 0a 20 20 73 65   seq<=$end".  se
31d0: 74 20 5f 75 6e 64 6f 28 66 69 72 73 74 6c 6f 67  t _undo(firstlog
31e0: 29 20 5b 64 62 20 6f 6e 65 20 7b 53 45 4c 45 43  ) [db one {SELEC
31f0: 54 20 63 6f 61 6c 65 73 63 65 28 6d 61 78 28 73  T coalesce(max(s
3200: 65 71 29 2c 30 29 2b 31 20 46 52 4f 4d 20 75 6e  eq),0)+1 FROM un
3210: 64 6f 6c 6f 67 7d 5d 0a 20 20 66 6f 72 65 61 63  dolog}].  foreac
3220: 68 20 73 71 6c 20 24 73 71 6c 6c 69 73 74 20 7b  h sql $sqllist {
3230: 0a 20 20 20 20 64 62 20 65 76 61 6c 20 24 73 71  .    db eval $sq
3240: 6c 0a 20 20 7d 0a 20 20 64 62 20 65 76 61 6c 20  l.  }.  db eval 
3250: 43 4f 4d 4d 49 54 0a 20 20 72 65 6c 6f 61 64 5f  COMMIT.  reload_
3260: 61 6c 6c 0a 0a 20 20 73 65 74 20 65 6e 64 20 5b  all..  set end [
3270: 64 62 20 6f 6e 65 20 7b 53 45 4c 45 43 54 20 63  db one {SELECT c
3280: 6f 61 6c 65 73 63 65 28 6d 61 78 28 73 65 71 29  oalesce(max(seq)
3290: 2c 30 29 20 46 52 4f 4d 20 75 6e 64 6f 6c 6f 67  ,0) FROM undolog
32a0: 7d 5d 0a 20 20 73 65 74 20 62 65 67 69 6e 20 24  }].  set begin $
32b0: 5f 75 6e 64 6f 28 66 69 72 73 74 6c 6f 67 29 0a  _undo(firstlog).
32c0: 20 20 6c 61 70 70 65 6e 64 20 5f 75 6e 64 6f 28    lappend _undo(
32d0: 24 76 32 29 20 5b 6c 69 73 74 20 24 62 65 67 69  $v2) [list $begi
32e0: 6e 20 24 65 6e 64 5d 0a 20 20 5f 73 74 61 72 74  n $end].  _start
32f0: 5f 69 6e 74 65 72 76 61 6c 0a 20 20 72 65 66 72  _interval.  refr
3300: 65 73 68 0a 7d 0a 0a 0a 23 20 45 6e 64 20 6f 66  esh.}...# End of
3310: 20 74 68 65 20 3a 3a 75 6e 64 6f 20 6e 61 6d 65   the ::undo name
3320: 73 70 61 63 65 0a 7d 0a 3c 2f 70 72 65 3e 3c 2f  space.}.</pre></
3330: 62 6c 6f 63 6b 71 75 6f 74 65 3e 0a              blockquote>.