Document scoring functionality.
[bpt/emacs.git] / lisp / play / gametree.el
CommitLineData
747c1a5e
RS
1;;; gametree.el --- manage game analysis trees in Emacs
2
3;; Copyright (C) 1997 Free Software Foundation, Inc
4
5;; Author: Ian T Zimmerman <itz@rahul.net>
6;; Created: Wed Dec 10 07:41:46 PST 1997
7;; Keywords: games
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
25
26;;; Commentary:
27
28;; This little hack has enabled me to keep track of my email chess
29;; games in Emacs. For a long time I dreamt about writing a real,
30;; graphical tree editor; but, then the idea struck me, why do it
31;; graphically, when it can be done in Emacs? :-) And in fact Emacs
32;; almost had what I needed out of the box, namely the powerful
33;; Outline mode. This code is built entirely on Outline mode, it
34;; only adds two commands that I found indispensable when dealing
35;; with the special kind of trees that analysis trees comprise.
36
37;; The built-in documentation should be enough to explain the use,
38;; along with the following example (yes, this is a real game).
39
40;; *** 23. f4 ef 24. Nf3 Rf3 -/+
41;; 25. Rf3 Qh2 26. Kh1 Qh1 27. Ke2 Qg2 -+
42;; ******* 25. gf3 Nce3 26. Qe2 Nf1 -/+
43;; 27. Qe5 Ne5 28. Kf1 Nf3 -/+
44;; 27. Kf1 Nh2 28. Kf2 Ng4 29. fg4 Rf8 30. Ke1 Qg3 31. Kd1 Rf2 -+
45
46;; Place the preceding in a scratch buffer, load this code, and do
47;; M-x gametree-mode. Now place the cursor just after the `Nf3' and
48;; before the `Rf3' on the first line, and do C-c C-j. The result is
49
50;; *** 23. f4 ef 24. Nf3
51;; ****** 24: Rf3 -/+
52;; 25. Rf3 Qh2 26. Kh1 Qh1 27. Ke2 Qg2 -+
53;; ******* 25. gf3 Nce3 26. Qe2 Nf1 -/+
54;; 27. Qe5 Ne5 28. Kf1 Nf3 -/+
55;; 27. Kf1 Nh2 28. Kf2 Ng4 29. fg4 Rf8 30. Ke1 Qg3 31. Kd1 Rf2 -+
56
57;; Now you can add another subvariation on Black's 24th move: with
58;; the cursor still on the first line, do C-c C-v, and voila
59
60;; *** 23. f4 ef 24. Nf3
61;; 24:
62;; ****** 24: Rf3 -/+
63;; 25. Rf3 Qh2 26. Kh1 Qh1 27. Ke2 Qg2 -+
64;; ******* 25. gf3 Nce3 26. Qe2 Nf1 -/+
65;; 27. Qe5 Ne5 28. Kf1 Nf3 -/+
66;; 27. Kf1 Nh2 28. Kf2 Ng4 29. fg4 Rf8 30. Ke1 Qg3 31. Kd1 Rf2 -+
67
68;; and the cursor is positioned on the new line just after the move
69;; number, so you can start typing the new analysis. That's it,
3bc5a5ee
RS
70;; quite simple.
71
72;; As of version 1.1, a simple score reducer has been implemented.
73;; As you type in leaf variations, you can add a numerical score tag
74;; to them with C-c ; . Then, with the cursor on a variation higher
75;; up in the tree, you can do C-c ^ and the program will compute the
76;; reduced score of the internal variation based on the scores of its
77;; children (which are recursively computed). You can use any range
78;; of numbers you wish as scores, maybe -1000 to 1000 or 0 to 100,
79;; all that matters to the program is that higher means better for
80;; White, lower means better for Black.
747c1a5e
RS
81
82;;; Code:
83
84(require 'derived)
85(require 'outline)
86
87;;;; Configuration variables
88
323f7c49
SE
89(defgroup gametree nil
90 "Manage game analysis trees in Emacs."
91 :prefix "gametree-"
42dfe0ad
DN
92 :group 'games
93 :version "20.3")
323f7c49
SE
94
95(defcustom gametree-half-ply-regexp (regexp-quote ":")
747c1a5e
RS
96 "*Matches ends of numbers of moves by the \"second\" player.
97For instance, it is an almost universal convention in chess to postfix
98numbers of moves by Black (if considered in isolation) by the ellipsis
99\"...\". This is NOT a good choice for this program, though, because it
100conflicts with the use of ellipsis by Outline mode to denote collapsed
101subtrees. The author uses \":\" because it agrees nicely with a set of
323f7c49
SE
102LaTeX macros he uses for typesetting annotated games."
103 :type 'regexp
104 :group 'gametree)
747c1a5e 105
323f7c49 106(defcustom gametree-full-ply-regexp (regexp-quote ".")
747c1a5e
RS
107 "*Matches ends of numbers of moves by the \"first\" player.
108For instance, it is an almost universal convention in chess to postfix
323f7c49
SE
109numbers of moves by White (if considered in isolation) by the dot \".\"."
110 :type 'regexp
111 :group 'gametree)
747c1a5e 112
323f7c49 113(defcustom gametree-half-ply-format "%d:"
747c1a5e 114 "*Output format for move numbers of moves by the \"second\" player.
323f7c49
SE
115Has to contain \"%d\" to output the actual number."
116 :type 'string
117 :group 'gametree)
747c1a5e 118
323f7c49 119(defcustom gametree-full-ply-format "%d."
747c1a5e 120 "*Output format for move numbers of moves by the \"first\" player.
323f7c49
SE
121Has to contain \"%d\" to output the actual number."
122 :type 'string
123 :group 'gametree)
747c1a5e 124
323f7c49 125(defcustom gametree-make-heading-function
747c1a5e
RS
126 (function (lambda (level)
127 (insert (make-string level ?*))))
128 "A function of one numeric argument, LEVEL, to insert a heading at point.
323f7c49
SE
129You should change this if you change `outline-regexp'."
130 :type 'function
131 :group 'gametree)
747c1a5e
RS
132
133(defvar gametree-local-layout nil
134 "A list encoding the layout (i.e. the show or hide state) of the file.
135If Emacs notices a local variable specification of this variable in
136the first line of the buffer while saving the buffer to the visited
137file, the local value will be saved there and restored the next time
138the file is visited (subject to the usual restriction via
139`enable-local-variables'), and the layout will be set accordingly.")
140
3bc5a5ee
RS
141(defvar gametree-score-opener "{score="
142 "*The string which opens a score tag, and precedes the actual score.")
143
144(defvar gametree-score-manual-flag "!"
145 "*String marking the line as manually (as opposed to automatically) scored.")
146
147(defvar gametree-score-closer "}"
148 "*The string which closes a score tag, and follows the actual score.")
149
150(defvar gametree-score-regexp
151 (concat "[^\n\^M]*\\("
152 (regexp-quote gametree-score-opener)
153 "[ ]*\\("
154 (regexp-quote gametree-score-manual-flag)
155 "[ ]*\\)?\\([-+]?[0-9]+\\)"
156 (regexp-quote gametree-score-closer)
157 "[ ]*\\)[\n\^M]")
158 "*Regular expression matching lines that guide the program in scoring.
159Its third parenthetical group should match the actual score. Its
160first parenthetical group should match the entire score tag. Its
161second parenthetical group should be an optional flag that marks the
162line as *manually* (as opposed to automatically) scored, which
163prevents the program from recursively applying the scoring algorithm
164on the subtree headed by the marked line, and makes it use the manual
165score instead.")
166
167(defvar gametree-default-score 0
168 "*Score to assume for branches lacking score tags.")
169\f
747c1a5e
RS
170;;;; Helper functions
171
172(defun gametree-prettify-heading ()
173 "Insert/delete space between leading asterisks and heading text.
174If the current variation is an internal node (i.e. starts with one or
175more asterisks), ensure there's at least one space between the
176asterisks and the text. If on the other hand this is a leaf, there
177should be no leading white space."
178 (save-excursion
179 (beginning-of-line 1)
180 (if (re-search-forward (concat "\\=" outline-regexp) nil t)
181 (if (not (looking-at "[ \t]+")) (insert " "))
182 (delete-char (save-excursion (skip-chars-forward " \t"))))
183 (if (re-search-forward (concat "\\=[ \t]*[1-9][0-9]*\\("
184 gametree-full-ply-regexp "\\|"
185 gametree-half-ply-regexp "\\)") nil t)
186 (if (not (looking-at "[ \t]+")) (insert " ")
187 (delete-char (1- (save-excursion (skip-chars-forward " \t"))))))))
188
189(defun gametree-looking-at-ply ()
190 "Read and return the number of the ply under point."
191 (if (eobp) 0
192 (let ((boundary (concat "[ \t]*\\([1-9][0-9]*\\)\\("
193 gametree-full-ply-regexp "\\|"
194 gametree-half-ply-regexp "\\)"))
195 (limit (save-excursion (beginning-of-line 1) (point))))
196 (if (looking-at boundary)
197 (+ (* 2 (string-to-int (match-string 1)))
198 (if (string-match gametree-half-ply-regexp (match-string 2)) 1 0))
199 (save-excursion
200 (re-search-backward boundary limit)
201 (skip-chars-backward "0123456789")
202 (1+ (* 2 (string-to-int
203 (buffer-substring (point) (match-end 1))))))))))
204
205(defun gametree-current-branch-ply ()
206 "Return the ply number of the first move of the current variation."
207 (save-excursion
208 (beginning-of-line 1)
209 (re-search-forward (concat "\\=" outline-regexp) nil t)
210 (gametree-looking-at-ply)))
211
3bc5a5ee
RS
212(defsubst gametree-forward-line ()
213 (re-search-forward "[\n\^M]" nil 'move))
214
747c1a5e
RS
215(defun gametree-current-branch-depth ()
216 "Return the depth of the current variation in the analysis tree.
217This value is simply the outline heading level of the current line."
218 (save-excursion
219 (beginning-of-line 1)
220 (if (looking-at outline-regexp)
221 (outline-level) 0)))
222
3bc5a5ee
RS
223(defun gametree-transpose-following-leaves ()
224 "Move the current leaf variation behind all others on the same level."
225 (let ((following-leaves
226 (save-excursion
227 (gametree-forward-line)
228 (let ((p (point)))
229 (while (and (not (eobp))
230 (= 0 (gametree-current-branch-depth)))
231 (gametree-forward-line))
232 (prog1 (buffer-substring p (point))
233 (delete-region p (point)))))))
234 (save-excursion
235 (beginning-of-line 1)
236 (insert following-leaves))))
237
238\f
747c1a5e
RS
239;;;; Functions related to the task of saving and restoring current
240;;;; outline layout
241
3bc5a5ee 242(defsubst gametree-show-children-and-entry ()
747c1a5e
RS
243 (show-children)
244 (show-entry))
245
246(defun gametree-entry-shown-p ()
247 (save-excursion
248 (forward-line 1)
249 (and (bolp) (not (eobp)) (not (looking-at outline-regexp)))))
250
251(defun gametree-children-shown-p ()
252 (save-excursion
253 (condition-case nil
254 (let ((depth (gametree-current-branch-depth)))
255 (outline-next-visible-heading 1)
256 (< depth (gametree-current-branch-depth)))
257 (error nil))))
258
259(defun gametree-current-layout (depth &optional top-level)
260 (let ((layout nil) (first-time t))
261 (while (save-excursion
262 (condition-case nil
263 (progn
264 (or (and first-time top-level
265 (bolp) (looking-at outline-regexp))
266 (setq first-time nil)
267 (outline-next-visible-heading 1))
268 (< depth (gametree-current-branch-depth)))
269 (error nil)))
270 (if (not first-time)
271 (outline-next-visible-heading 1))
272 (setq first-time nil)
273 (if (not (gametree-children-shown-p))
274 (setq layout
275 (nconc layout
276 (if (gametree-entry-shown-p)
277 (list 'show-entry)
278 (list nil))))
279 (setq layout (nconc layout (if (gametree-entry-shown-p)
280 (list 'gametree-show-children-and-entry)
281 (list 'show-children))))
282 (let ((sub-layout
283 (gametree-current-layout (gametree-current-branch-depth))))
284 (setq layout (nconc layout (list sub-layout))))))
285 layout))
286
287(defun gametree-save-layout ()
288 (save-excursion
289 (goto-char (point-min))
290 (setq gametree-local-layout (gametree-current-layout 0 t))))
291
292(defun gametree-apply-layout (layout depth &optional top-level)
293 (let ((first-time t))
294 (while (and layout
295 (save-excursion
296 (condition-case nil
297 (progn
298 (or (and first-time top-level
299 (bolp) (looking-at outline-regexp))
300 (setq first-time nil)
301 (outline-next-visible-heading 1))
302 (< depth (gametree-current-branch-depth)))
303 (error nil))))
304 (if (not first-time)
305 (outline-next-visible-heading 1))
306 (setq first-time nil)
307 (hide-subtree)
308 (if (nth 0 layout)
309 (funcall (nth 0 layout)))
310 (if (not (and (nth 1 layout) (listp (nth 1 layout))))
311 (setq layout (cdr layout))
312 (gametree-apply-layout (nth 1 layout)
313 (gametree-current-branch-depth))
314 (setq layout (cdr (cdr layout)))))))
315
316(defun gametree-restore-layout ()
317 (save-excursion
318 (goto-char (point-min))
319 (gametree-apply-layout gametree-local-layout 0 t)))
320
321(defun gametree-hack-file-layout ()
322 (save-excursion
323 (goto-char (point-min))
324 (if (looking-at "[^\n]*-\*-[^\n]*gametree-local-layout: \\([^;\n]*\\);")
325 (progn
326 (goto-char (match-beginning 1))
327 (delete-region (point) (match-end 1))
328 (let ((standard-output (current-buffer)))
329 (princ gametree-local-layout))))))
330
3bc5a5ee
RS
331\f
332;;;; Scoring functions
333
334(defun gametree-current-branch-score ()
335 "Return score of current variation according to its score tag.
336When no score tag is present, use the value of `gametree-default-score'."
337 (if (looking-at gametree-score-regexp)
338 (string-to-int (match-string 3))
339 gametree-default-score))
340
341(defun gametree-compute-reduced-score ()
342 "Return current internal node score computed recursively from subnodes.
343Subnodes which have been manually scored are honored."
344 (if (or
345 (= 0 (gametree-current-branch-depth))
346 (save-excursion (gametree-forward-line) (eobp))
347 (and (looking-at gametree-score-regexp)
348 (not (null (match-string 2)))))
349 (gametree-current-branch-score)
350 (let ((depth (gametree-current-branch-depth)))
351 (save-excursion
352 (gametree-forward-line)
353 ;; the case of a leaf node has already been handled, so here I
354 ;; know I am on the 1st line of the current subtree. This can
355 ;; be either a leaf child, or a subheading.
356 (let ((running gametree-default-score)
357 (minmax
358 (if (= 0 (mod (gametree-current-branch-ply) 2))
359 'max 'min)))
360 (while (and (not (eobp))
361 (= 0 (gametree-current-branch-depth))) ;handle leaves
362 (setq running (funcall minmax running
363 (gametree-current-branch-score)))
364 (gametree-forward-line))
365 (let ((done (and (not (eobp))
366 (< depth (gametree-current-branch-depth)))))
367 (while (not done) ;handle subheadings
368 (setq running (funcall minmax running
369 (gametree-compute-reduced-score)))
370 (setq done (condition-case nil
371 (outline-forward-same-level 1)
372 (error nil)))))
373 running)))))
374\f
747c1a5e
RS
375;;;; Commands
376
377(defun gametree-insert-new-leaf (&optional at-depth)
378 "Start a new leaf variation under the current branching point.
379The new variation can later be split to be a branching point itself,
380with \\[gametree-break-line-here]. If the point is currently on a
381leaf variation, this command won't work; use \\[gametree-break-line-here]
382on the current line first.
383
384With a numeric arg AT-DEPTH, first go up the tree until a node of
385depth AT-DEPTH or smaller is found."
3bc5a5ee 386 (interactive "*P")
747c1a5e
RS
387 (if (zerop (gametree-current-branch-depth))
388 (outline-up-heading 0))
389 (if at-depth
390 (while (> (gametree-current-branch-depth)
391 (prefix-numeric-value at-depth))
392 (outline-up-heading 1)))
393 (beginning-of-line 1)
394 (let ((parent-depth (gametree-current-branch-depth)))
395 (show-entry)
396 (condition-case nil
397 (outline-next-visible-heading 1)
398 (error
399 (goto-char (point-max))
400 (if (not (bolp)) (insert "\n"))))
401 (let ((starting-plys
402 (if (> (gametree-current-branch-depth) parent-depth)
403 (gametree-current-branch-ply)
404 (save-excursion (forward-line -1)
405 (gametree-current-branch-ply)))))
406 (goto-char (1- (point)))
407 (insert "\n")
408 (insert (format (if (= 0 (mod starting-plys 2))
409 gametree-full-ply-format
410 gametree-half-ply-format)
411 (/ starting-plys 2))))))
412
413(defun gametree-break-line-here (&optional at-move)
414 "Split the variation node at the point position.
415This command works whether the current variation node is a leaf, or is
416already branching at its end. The new node is created at a level that
417reflects the number of game plys between the beginning of the current
418variation and the breaking point.
419
420With a numerical argument AT-MOVE, split the variation before
421White's AT-MOVEth move, or Black's if negative. The last option will
422only work of Black's moves are explicitly numbered, for instance
423`1. e4 1: e5'."
3bc5a5ee 424 (interactive "*P")
747c1a5e
RS
425 (if at-move (progn
426 (end-of-line 1)
427 (let ((limit (point)))
428 (beginning-of-line 1)
429 (re-search-forward
430 (concat
431 (regexp-quote
432 (int-to-string (abs (prefix-numeric-value at-move))))
433 (if (> at-move 0) gametree-full-ply-regexp
434 gametree-half-ply-regexp)) limit))
435 (goto-char (match-beginning 0))))
3bc5a5ee 436 (gametree-transpose-following-leaves)
747c1a5e
RS
437 (let* ((pt (set-marker (make-marker) (point)))
438 (plys (gametree-current-branch-ply))
439 (depth (gametree-current-branch-depth))
440 (old-depth depth))
441 (if (= depth 0)
442 (progn
443 (save-excursion
444 (outline-previous-visible-heading 1)
445 (setq depth
446 (let ((old-branch-ply
447 (condition-case nil
448 (gametree-current-branch-ply)
449 (error 0))))
450 (if (zerop old-branch-ply)
451 (1+ (gametree-current-branch-depth))
452 (+ (gametree-current-branch-depth)
453 (- plys old-branch-ply))))))
454 (save-excursion
455 (beginning-of-line 1)
456 (funcall gametree-make-heading-function depth)
457 (gametree-prettify-heading))))
458 (save-excursion
459 (if (not (looking-at (concat "[ \t]*[1-9][0-9]*\\("
460 gametree-full-ply-regexp "\\|"
461 gametree-half-ply-regexp "\\)")))
462 (progn
463 (insert (format (if (= 0 (mod (gametree-looking-at-ply) 2))
464 gametree-full-ply-format
465 gametree-half-ply-format)
466 (/ (gametree-looking-at-ply) 2)))
467 (gametree-prettify-heading)
468 (beginning-of-line 1)))
469 (goto-char pt)
470 (insert "\n")
471 (if (not (= 0 old-depth))
472 (funcall gametree-make-heading-function
473 (+ depth (- (gametree-current-branch-ply) plys))))
474 (gametree-prettify-heading))))
475
476(defun gametree-merge-line ()
477 "Merges a variation with its only child.
478Does *not* check if the variation has in fact a unique child; users beware."
3bc5a5ee 479 (interactive "*")
747c1a5e
RS
480 (if (zerop (gametree-current-branch-depth))
481 (outline-up-heading 0))
3bc5a5ee
RS
482 (if (looking-at gametree-score-regexp)
483 (delete-region (match-beginning 1) (match-end 1)))
747c1a5e
RS
484 (end-of-line 1)
485 (let ((prev-depth (save-excursion (forward-line 1)
486 (gametree-current-branch-depth))))
487 (delete-char (1+ prev-depth))
488 (if (zerop prev-depth)
489 (save-excursion
490 (beginning-of-line 1)
491 (delete-char (gametree-current-branch-depth))
492 (gametree-prettify-heading)))))
493
3bc5a5ee
RS
494(defun gametree-insert-score (score &optional auto)
495 "Insert a score tag with value SCORE at the end of the current line.
496If this line already has a score tag, just jump to it and alter it.
497When called from a program, optional AUTO flag tells if the score is
498being entered automatically (and thus should lack the manual mark)."
499 (interactive "*P")
500 (beginning-of-line 1)
501 (if (looking-at gametree-score-regexp)
502 (progn
503 (goto-char (match-beginning 3))
504 (if (and auto (not (null (match-string 2))))
505 (delete-region (match-beginning 2) (match-end 2)))
506 (if (not (null score))
507 (delete-region (match-beginning 3) (match-end 3)))
508 (if (and (not auto) (null (match-string 2)))
509 (insert gametree-score-manual-flag)))
510 (end-of-line 1)
511 (if (= 0 (save-excursion (skip-chars-backward " \t")))
512 (insert " "))
513 (insert gametree-score-opener)
514 (if (not auto) (insert gametree-score-manual-flag))
515 (save-excursion (insert gametree-score-closer)))
516 (if (not (null score))
517 (save-excursion
518 (insert (int-to-string (prefix-numeric-value score))))))
519
520(defun gametree-compute-and-insert-score ()
521 "Compute current node score, maybe recursively from subnodes. Insert it.
522Subnodes which have been manually scored are honored."
523 (interactive "*")
524 (let ((auto (not (and (looking-at gametree-score-regexp)
525 (not (null (match-string 2))))))
526 (score (gametree-compute-reduced-score)))
527 (gametree-insert-score score auto)))
528
529
747c1a5e
RS
530(defun gametree-layout-to-register (register)
531 "Store current tree layout in register REGISTER.
532Use \\[gametree-apply-register-layout] to restore that configuration.
533Argument is a character, naming the register."
534 (interactive "cLayout to register: ")
535 (save-excursion
536 (goto-char (point-min))
537 (set-register register
538 (gametree-current-layout 0 t))))
539
540(defun gametree-apply-register-layout (char)
541 "Return to a tree layout stored in a register.
542Argument is a character, naming the register."
3bc5a5ee 543 (interactive "*cApply layout from register: ")
747c1a5e
RS
544 (save-excursion
545 (goto-char (point-min))
546 (gametree-apply-layout (get-register char) 0 t)))
547
548(defun gametree-save-and-hack-layout ()
549 "Save the current tree layout and hack the file local variable spec.
550This function saves the current layout in `gametree-local-layout' and,
551if a local file varible specification for this variable exists in the
552buffer, it is replaced by the new value. See the documentation for
553`gametree-local-layout' for more information."
554 (interactive)
555 (gametree-save-layout)
3bc5a5ee
RS
556 (let ((inhibit-read-only t))
557 (gametree-hack-file-layout))
747c1a5e
RS
558 nil)
559
560(define-derived-mode gametree-mode outline-mode "GameTree"
561 "Major mode for managing game analysis trees.
562Useful to postal and email chess (and, it is hoped, also checkers, go,
563shogi, etc.) players, it is a slightly modified version of Outline mode.
564
565\\{gametree-mode-map}"
566(auto-fill-mode 0)
567(make-variable-buffer-local 'write-contents-hooks)
568(add-hook 'write-contents-hooks 'gametree-save-and-hack-layout))
569
570;;;; Key bindings
571
572(define-key gametree-mode-map "\C-c\C-j" 'gametree-break-line-here)
573(define-key gametree-mode-map "\C-c\C-v" 'gametree-insert-new-leaf)
574(define-key gametree-mode-map "\C-c\C-m" 'gametree-merge-line)
575(define-key gametree-mode-map "\C-c\C-r " 'gametree-layout-to-register)
576(define-key gametree-mode-map "\C-c\C-r/" 'gametree-layout-to-register)
577(define-key gametree-mode-map "\C-c\C-rj" 'gametree-apply-register-layout)
578(define-key gametree-mode-map "\C-c\C-y" 'gametree-save-and-hack-layout)
3bc5a5ee
RS
579(define-key gametree-mode-map "\C-c;" 'gametree-insert-score)
580(define-key gametree-mode-map "\C-c^" 'gametree-compute-and-insert-score)
747c1a5e
RS
581
582;;;; Goodies for mousing users
583(and (fboundp 'track-mouse)
584 (defun gametree-mouse-break-line-here (event)
585 (interactive "e")
586 (mouse-set-point event)
587 (gametree-break-line-here))
588 (defun gametree-mouse-show-children-and-entry (event)
589 (interactive "e")
590 (mouse-set-point event)
591 (gametree-show-children-and-entry))
592 (defun gametree-mouse-show-subtree (event)
593 (interactive "e")
594 (mouse-set-point event)
595 (show-subtree))
596 (defun gametree-mouse-hide-subtree (event)
597 (interactive "e")
598 (mouse-set-point event)
599 (hide-subtree))
600 (define-key gametree-mode-map [M-down-mouse-2 M-mouse-2]
601 'gametree-mouse-break-line-here)
602 (define-key gametree-mode-map [S-down-mouse-1 S-mouse-1]
603 'gametree-mouse-show-children-and-entry)
604 (define-key gametree-mode-map [S-down-mouse-2 S-mouse-2]
605 'gametree-mouse-show-subtree)
606 (define-key gametree-mode-map [S-down-mouse-3 S-mouse-3]
607 'gametree-mouse-hide-subtree))
608
609(provide 'gametree)
610
611;;; gametree.el ends here