1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;; Code shared by all chess displays
6 (require 'chess-module)
10 (defgroup chess-display nil
11 "Common code used by chess displays."
14 (defcustom chess-display-popup t
15 "If non-nil, popup displays whenever a significant event occurs."
17 :group 'chess-display)
19 (make-variable-buffer-local 'chess-display-popup)
21 (defcustom chess-display-highlight-legal nil
22 "If non-nil, highlight legal target squares when a piece is selected."
24 :group 'chess-display)
26 (chess-message-catalog 'english
27 '((mode-white . "White")
28 (mode-black . "Black")
29 (mode-start . "START")
30 (mode-checkmate . "CHECKMATE")
31 (mode-aborted . "ABORTED")
32 (mode-resigned . "RESIGNED")
33 (mode-stalemate . "STALEMATE")
34 (mode-flag-fell . "FLAG FELL")
35 (mode-drawn . "DRAWN")
36 (mode-edit . "EDIT")))
38 (defcustom chess-display-mode-line-format
39 '(" " chess-display-side-to-move " "
40 chess-display-move-text " "
41 (:eval (chess-display-clock-string))
42 "(" (:eval (chess-game-tag chess-module-game "White")) "-"
43 (:eval (chess-game-tag chess-module-game "Black")) ", "
44 (:eval (chess-game-tag chess-module-game "Site"))
45 (:eval (let ((date (chess-game-tag chess-module-game "Date")))
46 (and (string-match "\\`\\([0-9]\\{4\\}\\)" date)
47 (concat " " (match-string 1 date))))) ")")
48 "The format of a chess display's modeline.
49 See `mode-line-format' for syntax details."
51 :group 'chess-display)
53 (defface chess-display-black-face
54 '((t (:background "Black" :foreground "White")))
55 "*The face used for the word Black in the mode-line."
56 :group 'chess-display)
58 (defface chess-display-white-face
59 '((t (:background "White" :foreground "Black")))
60 "*The face used for the word White in the mode-line."
61 :group 'chess-display)
65 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
70 (defvar chess-display-index)
71 (defvar chess-display-move-text)
72 (defvar chess-display-side-to-move)
73 (defvar chess-display-perspective)
74 (defvar chess-display-event-handler nil)
75 (defvar chess-display-edit-mode nil)
76 (defvar chess-display-index-positions nil)
78 (make-variable-buffer-local 'chess-display-index)
79 (make-variable-buffer-local 'chess-display-move-text)
80 (make-variable-buffer-local 'chess-display-side-to-move)
81 (make-variable-buffer-local 'chess-display-perspective)
82 (make-variable-buffer-local 'chess-display-event-handler)
83 (make-variable-buffer-local 'chess-display-edit-mode)
84 (make-variable-buffer-local 'chess-display-index-positions)
86 (defvar chess-display-handling-event nil
87 "If non-nil, chess-display is aleady handling the event. This variable
88 is used to avoid reentrancy.")
90 (defvar chess-display-style)
92 (chess-message-catalog 'english
93 '((no-such-style . "There is no such chessboard display style '%s'")
94 (cannot-yet-add . "Cannot insert moves into a game (yet)")))
96 (defun chess-display-create (game style perspective)
97 "Create a chess display, for displaying chess objects."
98 (interactive (list (if current-prefix-arg
99 (chess-game-create (chess-fen-to-pos
100 (read-string "FEN: ")))
103 (concat "chess-" (completing-read "Display style: "
107 (y-or-n-p "View from White's perspective? ")))
108 (if (require style nil t)
109 (let* ((chess-display-style style)
110 (display (chess-module-create 'chess-display game "*Chessboard*"
114 (chess-display-update display)
115 (chess-display-popup display))
118 (defalias 'chess-display-destroy 'chess-module-destroy)
120 (defun chess-display-clone (display style perspective)
121 (let ((new-display (chess-display-create chess-module-game
123 ;; the display will have already been updated by the `set-' calls,
124 ;; it's just not visible yet
125 (chess-display-popup new-display)
128 (defsubst chess-display-perspective (display)
129 "Return the current perspective of DISPLAY."
130 (chess-with-current-buffer display
131 chess-display-perspective))
133 (defun chess-display-set-perspective* (display perspective)
134 (chess-with-current-buffer display
135 (setq chess-display-perspective perspective
136 chess-display-index-positions nil)
137 (erase-buffer))) ; force a complete redraw
139 (defun chess-display-set-perspective (display perspective)
140 "Set PERSPECTIVE of DISPLAY."
141 (chess-with-current-buffer display
142 (chess-display-set-perspective* nil perspective)
143 (chess-display-update nil)))
145 (defun chess-display-set-position (display &optional position my-color)
146 "Set the game associated with DISPLAY to use POSITION and MY-COLOR."
147 (chess-with-current-buffer display
150 (chess-game-set-start-position chess-module-game position)
151 (chess-game-set-data chess-module-game 'my-color my-color))
152 (chess-game-set-start-position chess-module-game
153 chess-starting-position)
154 (chess-game-set-data chess-module-game 'my-color t))
155 (chess-display-set-index nil 0)))
157 (defun chess-display-position (display)
158 "Return the position currently viewed on DISPLAY."
159 (chess-with-current-buffer display
160 (if chess-display-edit-mode
161 chess-display-edit-position
162 (chess-game-pos chess-module-game chess-display-index))))
164 (defun chess-display-set-ply (display ply)
165 (chess-with-current-buffer display
166 (let ((chess-game-inhibit-events t))
167 (chess-display-set-index nil 1))
168 (chess-game-set-plies chess-module-game
169 (list ply (chess-ply-create*
170 (chess-ply-next-pos ply))))))
172 (defun chess-display-ply (display)
173 (chess-with-current-buffer display
174 (chess-game-ply chess-module-game chess-display-index)))
176 (defun chess-display-set-variation (display variation &optional index)
177 "Set DISPLAY VARIATION.
178 If INDEX is not specified, this will cause the first ply in the variation
179 to be displayed, with the user able to scroll back and forth through the
180 moves in the variation. Any moves made on the board will extend/change the
181 variation that was passed in."
182 (chess-with-current-buffer display
183 (let ((chess-game-inhibit-events t))
184 (chess-display-set-index nil (or index (chess-var-index variation))))
185 (chess-game-set-plies chess-module-game variation)))
187 (defun chess-display-variation (display)
188 (chess-with-current-buffer display
189 (chess-game-main-var chess-module-game)))
191 (defun chess-display-set-game* (display game &optional index)
192 "Set the game associated with the given DISPLAY."
193 (chess-with-current-buffer display
194 (chess-module-set-game* display game)
195 (chess-display-set-index nil (or index (chess-game-index game)))))
197 (defun chess-display-set-game (display game &optional index)
198 "Set the given DISPLAY to display the GAME object, optionally at INDEX.
199 This is the function to call to cause a display to view a game. It
200 will also update all of the listening engines and other displays to
201 also view the same game."
202 (chess-with-current-buffer display
203 (chess-game-copy-game chess-module-game game)
204 (chess-display-set-index nil (or index (chess-game-index game)))))
206 (defalias 'chess-display-game 'chess-module-game)
208 (defun chess-display-clock-string ()
209 (let ((white (chess-game-data chess-module-game 'white-remaining))
210 (black (chess-game-data chess-module-game 'black-remaining)))
211 (if (not (and white black))
212 (let ((last-ply (chess-game-ply chess-module-game
213 (1- chess-display-index))))
214 (setq white (chess-ply-keyword last-ply :white)
215 black (chess-ply-keyword last-ply :black))))
216 (if (and white black)
217 (format "W %s%02d:%02d B %s%02d:%02d "
218 (if (and (< white 0) (= 0 (floor white))) "-" "")
219 (/ (floor white) 60) (% (abs (floor white)) 60)
220 (if (and (< black 0) (= 0 (floor black))) "-" "")
221 (/ (floor black) 60) (% (abs (floor black)) 60)))))
223 (defun chess-display-set-index (display index)
224 (chess-with-current-buffer display
225 (unless (or (not (integerp index))
227 (> index (chess-game-index chess-module-game)))
228 (chess-game-run-hooks chess-module-game 'set-index index))))
230 (defun chess-display-set-index* (display index)
231 (chess-with-current-buffer display
232 (setq chess-display-index index
233 chess-display-move-text
235 (chess-string 'mode-start)
236 (concat (int-to-string (if (> index 1)
237 (if (= (mod index 2) 0)
241 ". " (and (= 0 (mod index 2)) "... ")
242 (chess-ply-to-algebraic
243 (chess-game-ply chess-module-game (1- index)))))
244 chess-display-side-to-move
245 (let ((status (chess-game-status chess-module-game index)))
247 ((eq status :aborted) (chess-string 'mode-aborted))
248 ((eq status :resign) (chess-string 'mode-resigned))
249 ((eq status :drawn) (chess-string 'mode-drawn))
250 ((eq status :checkmate) (chess-string 'mode-checkmate))
251 ((eq status :stalemate) (chess-string 'mode-stalemate))
252 ((eq status :flag-fell) (chess-string 'mode-flag-fell))
254 (let* ((color (or chess-pos-always-white
255 (chess-game-side-to-move chess-module-game
257 (str (format " %s " (if color
258 (chess-string 'mode-white)
259 (chess-string 'mode-black)))))
261 0 (length str) (list 'face (if color
262 'chess-display-white-face
263 'chess-display-black-face)) str)
265 (force-mode-line-update)))
267 (defsubst chess-display-index (display)
268 (chess-with-current-buffer display
269 chess-display-index))
271 (defun chess-display-update (display &optional popup)
272 "Update the chessboard DISPLAY. POPUP too, if that arg is non-nil."
273 (chess-with-current-buffer display
274 (funcall chess-display-event-handler 'draw
275 (chess-display-position nil) chess-display-perspective)
276 (if (and popup chess-display-popup
277 (chess-module-leader-p nil))
278 (chess-display-popup nil))))
280 (defun chess-display-redraw (&optional display)
281 "Just redraw the current display."
283 (chess-with-current-buffer display
284 (let ((here (point)))
286 (chess-display-update nil)
289 (defun chess-display-index-pos (display index)
290 (chess-with-current-buffer display
291 (unless chess-display-index-positions
292 (setq chess-display-index-positions (make-vector 64 nil))
293 (let ((pos (next-single-property-change (point-min) 'chess-coord))
296 (if (setq pos-index (get-text-property pos 'chess-coord))
297 (aset chess-display-index-positions pos-index pos))
298 (setq pos (next-single-property-change pos 'chess-coord)))
299 (unless (aref chess-display-index-positions 0)
300 (aset chess-display-index-positions 0
301 (if chess-display-perspective
304 (unless (aref chess-display-index-positions 63)
305 (aset chess-display-index-positions 63
306 (if chess-display-perspective
309 (aref chess-display-index-positions index)))
311 (defun chess-display-paint-move (display ply)
312 (chess-with-current-buffer display
313 (let ((position (chess-ply-pos ply))
314 (ch (chess-ply-changes ply)))
316 (if (symbolp (car ch))
318 (let ((from (car ch))
320 (funcall chess-display-event-handler 'draw-square
321 (chess-display-index-pos nil from) ? from)
322 (let ((new-piece (chess-ply-keyword ply :promote)))
324 (funcall chess-display-event-handler 'draw-square
325 (chess-display-index-pos nil to)
326 (if (chess-pos-side-to-move position)
328 (downcase new-piece)) to)
329 (funcall chess-display-event-handler 'draw-square
330 (chess-display-index-pos nil to)
331 (chess-pos-piece position from) to))))
332 (setq ch (cddr ch)))))))
334 (chess-message-catalog 'english
335 '((not-your-move . "It is not your turn to move")
336 (game-is-over . "This game is over")))
338 (defun chess-display-move (display ply &optional prev-pos pos)
339 "Move a piece on DISPLAY, by applying the given PLY.
340 The position of PLY must match the currently displayed position.
341 If only START is given, it must be in algebraic move notation."
342 (chess-with-current-buffer display
343 (if (and (chess-display-active-p)
344 ;; `active' means we're playing against an engine
345 (chess-game-data chess-module-game 'active)
346 (not (eq (chess-game-data chess-module-game 'my-color)
347 (chess-game-side-to-move chess-module-game))))
348 (chess-error 'not-your-move)
349 (if (and (= chess-display-index
350 (chess-game-index chess-module-game))
351 (chess-game-over-p chess-module-game))
352 (chess-error 'game-is-over)))
353 (if (= chess-display-index (chess-game-index chess-module-game))
354 (let ((chess-display-handling-event t))
355 (chess-display-paint-move nil ply)
356 (chess-game-move chess-module-game ply)
357 (chess-display-set-index* nil (chess-game-index chess-module-game)))
358 ;; jww (2002-03-28): This should beget a variation within the
359 ;; game, or alter the game, just as SCID allows
360 (chess-error 'cannot-yet-add))))
362 (defun chess-display-highlight (display &rest args)
363 "Highlight the square at INDEX on the current position.
364 The given highlighting MODE is used, or the default if the style you
365 are displaying with doesn't support that mode. `selected' is a mode
366 that is supported by most displays, and is the default mode."
367 (chess-with-current-buffer display
368 (let ((mode :selected))
370 (if (or (symbolp arg) (stringp arg))
372 (funcall chess-display-event-handler 'highlight arg mode))))))
374 (defun chess-display-highlight-legal (display pos)
375 "Highlight all legal move targets from POS."
376 (chess-with-current-buffer display
377 (dolist (ply (chess-legal-plies (chess-display-position nil)
379 (chess-display-highlight nil "pale green"
380 (chess-ply-target ply)))))
382 (defun chess-display-popup (display)
383 "Popup the given DISPLAY, so that it's visible to the user."
384 (chess-with-current-buffer display
385 (unless (eq (get-buffer-window (current-buffer))
387 (funcall chess-display-event-handler 'popup))))
389 (defun chess-display-enable-popup (display)
390 "Popup the given DISPLAY, so that it's visible to the user."
391 (chess-with-current-buffer display
392 (setq chess-display-popup nil)))
394 (defun chess-display-disable-popup (display)
395 "Popup the given DISPLAY, so that it's visible to the user."
396 (chess-with-current-buffer display
397 (setq chess-display-popup t)))
399 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
401 ;; Default window and frame popup functions
404 (defun chess-display-popup-in-window ()
405 "Popup the given DISPLAY, so that it's visible to the user."
406 (unless (get-buffer-window (current-buffer))
407 (if (> (length (window-list)) 1)
408 (fit-window-to-buffer (display-buffer (current-buffer)))
409 (display-buffer (current-buffer)))))
411 (defun chess-display-popup-in-frame (height width &optional
412 display no-minibuffer)
413 "Popup the given DISPLAY, so that it's visible to the user."
414 (let ((window (get-buffer-window (current-buffer) t)))
416 (let ((frame (window-frame window)))
417 (unless (eq frame (selected-frame))
418 (raise-frame frame)))
419 (let ((params (list (cons 'name "*Chessboard*")
420 (cons 'height height)
421 (cons 'width width))))
423 (push (cons 'display display) params))
425 (push (cons 'minibuffer nil) params))
426 (select-frame (make-frame params))
427 (set-window-dedicated-p (selected-window) t)))))
429 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
434 (defcustom chess-display-interesting-events
436 "Events which will cause a display refresh."
437 :type '(repeat symbol)
438 :group 'chess-display)
440 (defcustom chess-display-momentous-events
441 '(orient post-undo setup-game pass move resign abort)
442 "Events that will refresh, and cause 'main' displays to popup.
443 These are displays for which `chess-display-set-main' has been
445 :type '(repeat symbol)
446 :group 'chess-display)
448 (defun chess-display-handler (game event &rest args)
449 "This display module presents a standard chessboard.
450 See `chess-display-type' for the different kinds of displays."
451 (unless chess-display-handling-event
452 (if (eq event 'initialize)
455 (setq chess-display-index (chess-game-index game)
456 chess-display-side-to-move
457 (if (chess-pos-side-to-move (chess-game-pos game))
458 (chess-string 'mode-white)
459 (chess-string 'mode-black))
460 chess-display-move-text (chess-string 'mode-start)
461 chess-display-perspective (car args)
462 chess-display-event-handler
463 (intern-soft (concat (symbol-name chess-display-style)
465 (and chess-display-event-handler
466 (funcall chess-display-event-handler 'initialize)))
469 (let ((my-color (chess-game-data game 'my-color)))
470 (chess-game-set-data game 'my-color (not my-color))
471 (chess-display-set-perspective* nil (not my-color))))
473 ((eq event 'set-index)
474 (chess-display-set-index* nil (car args)))
477 (let ((my-color (chess-game-data game 'my-color)))
478 ;; Set the display's perspective to whichever color I'm
480 (chess-display-set-perspective* nil my-color))))
482 (if (memq event chess-display-momentous-events)
484 (chess-display-set-index* nil (chess-game-index game))
487 (chess-display-paint-move nil (car args))
488 (if chess-display-popup
489 (chess-display-popup nil)))
490 (chess-display-update nil chess-display-popup)))
491 (if (memq event chess-display-interesting-events)
492 (chess-display-update nil))))))
494 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
496 ;; chess-display-mode
499 (defvar chess-display-safe-map
500 (let ((map (make-keymap)))
501 (suppress-keymap map)
502 (set-keymap-parent map nil)
504 (define-key map [(control ?i)] 'chess-display-invert)
505 (define-key map [tab] 'chess-display-invert)
507 (define-key map [??] 'describe-mode)
508 (define-key map [?L] 'chess-display-list-buffers)
509 ;;(define-key map [?C] 'chess-display-duplicate)
510 (define-key map [?I] 'chess-display-invert)
512 (define-key map [?<] 'chess-display-move-first)
513 (define-key map [?,] 'chess-display-move-backward)
514 (define-key map [(meta ?<)] 'chess-display-move-first)
515 (define-key map [?>] 'chess-display-move-last)
516 (define-key map [?.] 'chess-display-move-forward)
517 (define-key map [(meta ?>)] 'chess-display-move-last)
519 (define-key map [(meta ?w)] 'chess-display-kill-board)
521 (define-key map [(control ?l)] 'chess-display-redraw)
524 "The mode map used in read-only display buffers.")
526 (defvar chess-display-mode-map
527 (let ((map (copy-keymap chess-display-safe-map)))
528 (define-key map [space] 'chess-display-pass)
529 (define-key map [? ] 'chess-display-pass)
530 (define-key map [??] 'describe-mode)
531 (define-key map [?@] 'chess-display-remote)
532 (define-key map [?A] 'chess-display-manual-move)
533 (define-key map [(control ?c) (control ?a)] 'chess-display-abort)
534 (define-key map [?C] 'chess-display-duplicate)
535 (define-key map [?D] 'chess-display-decline)
536 (define-key map [(control ?c) (control ?c)] 'chess-display-force)
537 (define-key map [(control ?c) (control ?d)] 'chess-display-draw)
538 (define-key map [?E] 'chess-display-edit-board)
539 (define-key map [?F] 'chess-display-set-from-fen)
540 (define-key map [(control ?c) (control ?f)] 'chess-display-call-flag)
541 (define-key map [?M] 'chess-display-match)
542 (define-key map [(control ?c) (control ?r)] 'chess-display-resign)
543 (define-key map [?R] 'chess-display-retract)
544 (define-key map [?S] 'chess-display-shuffle)
545 (define-key map [(control ?c) (control ?t)] 'chess-display-undo)
546 (define-key map [?X] 'chess-display-quit)
547 (define-key map [?Y] 'chess-display-accept)
549 (define-key map [?\{] 'chess-display-annotate)
550 (define-key map [?\"] 'chess-display-chat)
551 (define-key map [?\'] 'chess-display-chat)
552 (define-key map [?\~] 'chess-display-check-autosave)
554 (define-key map [(control ?r)] 'chess-display-search-backward)
555 (define-key map [(control ?s)] 'chess-display-search-forward)
556 (define-key map [(control ?y)] 'chess-display-yank-board)
558 (dolist (key '(?a ?b ?c ?d ?e ?f ?g ?h
559 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8
563 (define-key map (vector key) 'chess-input-shortcut))
564 (define-key map [backspace] 'chess-input-shortcut-delete)
566 (define-key map [(control ?m)] 'chess-display-select-piece)
567 (define-key map [return] 'chess-display-select-piece)
570 (define-key map [(button1)] 'chess-display-mouse-select-piece)
571 (define-key map [(button2)] 'chess-display-mouse-select-piece)
572 (define-key map [(button3)] 'ignore))
574 (define-key map [down-mouse-1] 'chess-display-mouse-select-piece)
575 (define-key map [drag-mouse-1] 'chess-display-mouse-select-piece)
577 (define-key map [down-mouse-2] 'chess-display-mouse-select-piece)
578 (define-key map [drag-mouse-2] 'chess-display-mouse-select-piece)
580 (define-key map [mouse-3] 'ignore)))
582 (define-key map [menu-bar files] 'undefined)
583 (define-key map [menu-bar edit] 'undefined)
584 (define-key map [menu-bar options] 'undefined)
585 (define-key map [menu-bar buffer] 'undefined)
586 (define-key map [menu-bar tools] 'undefined)
587 (define-key map [menu-bar help-menu] 'undefined)
590 "The mode map used in a chessboard display buffer.")
592 (defvar chess-display-move-menu nil)
593 (unless chess-display-move-menu
595 chess-display-move-menu chess-display-mode-map ""
597 ["First" chess-display-move-first t]
598 ["Previous" chess-display-move-backward t]
599 ["Next" chess-display-move-forward t]
600 ["Last" chess-display-move-last t])))
602 (defun chess-display-mode ()
603 "A mode for displaying and interacting with a chessboard.
604 The key bindings available in this mode are:
605 \\{chess-display-mode-map}"
607 (setq major-mode 'chess-display-mode
608 mode-name "Chessboard")
609 (use-local-map chess-display-mode-map)
610 (buffer-disable-undo)
611 (setq buffer-auto-save-file-name nil
612 mode-line-format 'chess-display-mode-line-format)
613 (setq chess-input-position-function
616 (chess-display-position nil))))
617 (setq chess-input-move-function 'chess-display-move))
619 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
621 ;; Commands used by the keyboard bindings above
624 (defsubst chess-display-active-p ()
625 "Return non-nil if the displayed chessboard reflects an active game.
626 Basically, it means we are playing, not editing or reviewing."
627 (and (chess-game-data chess-module-game 'active)
628 (= chess-display-index
629 (chess-game-index chess-module-game))
630 (not (chess-game-over-p chess-module-game))
631 (not chess-display-edit-mode)))
633 (defun chess-display-invert ()
634 "Invert the perspective of the current chess board."
636 (chess-display-set-perspective nil (not chess-display-perspective)))
638 (defun chess-display-set-from-fen (fen)
639 "Send the current board configuration to the user."
640 (interactive "sSet from FEN string: ")
641 (chess-display-set-position nil (chess-fen-to-pos fen)))
643 (defun chess-display-kill-board (&optional arg)
644 "Send the current board configuration to the user."
646 (let ((x-select-enable-clipboard t)
647 (game chess-module-game))
649 (kill-new (with-temp-buffer
650 (chess-game-to-pgn game)
652 (kill-new (chess-pos-to-fen (chess-display-position nil))))))
654 (defun chess-display-yank-board ()
655 "Send the current board configuration to the user."
657 (let ((x-select-enable-clipboard t)
658 (display (current-buffer))
659 (text (current-kill 0)))
662 (goto-char (point-max))
663 (while (and (bolp) (not (bobp)))
664 (delete-backward-char 1))
665 (goto-char (point-min))
667 ((search-forward "[Event " nil t)
668 (goto-char (match-beginning 0))
669 (chess-game-copy-game chess-module-game (chess-pgn-to-game)))
670 ((looking-at (concat chess-algebraic-regexp "$"))
671 (let ((move (buffer-string)))
672 (with-current-buffer display
673 (chess-display-manual-move move))))
675 (with-current-buffer display
676 (chess-display-set-from-fen (buffer-string))))))))
678 (defvar chess-display-search-map
679 (let ((map (copy-keymap minibuffer-local-map)))
680 (dolist (key '(?a ?b ?c ?d ?e ?f ?g ?h
681 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8
685 (define-key map (vector key) 'chess-display-search-key))
686 (define-key map [backspace] 'chess-display-search-delete)
687 (define-key map [delete] 'chess-display-search-delete)
688 (define-key map [(control ?h)] 'chess-display-search-delete)
689 (define-key map [(control ?r)] 'chess-display-search-again)
690 (define-key map [(control ?s)] 'chess-display-search-again)
693 (defvar chess-display-search-direction nil)
694 (defvar chess-current-display nil)
695 (defvar chess-display-previous-index nil)
697 (make-variable-buffer-local 'chess-display-previous-index)
699 (chess-message-catalog 'english
700 '((san-not-found . "Could not find a matching move")))
702 (defun chess-display-search (&optional reset again)
704 (let ((str (concat "\\`" (minibuffer-contents)))
706 (with-current-buffer chess-current-display
707 (setq index (if reset
708 chess-display-previous-index
709 chess-display-index))
711 (setq index (if chess-display-search-direction
715 (while (if chess-display-search-direction
718 (chess-game-index chess-module-game))))
720 (let* ((ply (chess-game-ply chess-module-game index))
721 (san (chess-ply-keyword ply :san))
722 (case-fold-search t))
723 (when (and san (string-match str san))
724 (chess-display-set-index nil (1+ index))
726 (setq index (funcall (if chess-display-search-direction '1+ '1-)
728 (chess-error 'san-not-found)))))
730 (defun chess-display-search-again ()
732 (chess-display-search nil t))
734 (defun chess-display-search-key ()
736 (call-interactively 'self-insert-command)
737 (chess-display-search))
739 (defun chess-display-search-delete ()
741 (call-interactively 'delete-backward-char)
742 (chess-display-search t))
744 (defun chess-display-search-backward (&optional direction)
746 (setq chess-display-previous-index chess-display-index)
748 (let ((chess-display-search-direction direction)
749 (chess-current-display (current-buffer)))
750 (read-from-minibuffer "Find algebraic move: " nil
751 chess-display-search-map))
753 (chess-display-set-index nil chess-display-previous-index))))
755 (defun chess-display-search-forward ()
757 (chess-display-search-backward t))
759 (chess-message-catalog 'english
760 '((illegal-notation . "Illegal move notation: %s")
761 (want-to-quit . "Do you really want to quit? ")))
763 (defun chess-display-quit ()
764 "Quit the game associated with the current display."
766 (if (or (not (chess-module-leader-p nil))
767 (yes-or-no-p (chess-string 'want-to-quit)))
768 (chess-module-destroy nil)))
770 (defun chess-display-annotate ()
772 (chess-game-run-hooks chess-module-game 'switch-to-annotations))
774 (defun chess-display-chat ()
776 (chess-game-run-hooks chess-module-game 'switch-to-chat))
778 (defun chess-display-manual-move (move)
779 "Move a piece manually, using chess notation."
783 (if (chess-pos-side-to-move (chess-display-position nil))
785 (1+ (/ (or chess-display-index 0) 2))))))
786 (let ((ply (chess-algebraic-to-ply (chess-display-position nil) move)))
788 (chess-error 'illegal-notation move))
789 (chess-display-move nil ply)))
791 (defun chess-display-remote (display)
792 (interactive "sDisplay this game on X server: ")
793 (require 'chess-images)
794 (let ((chess-images-separate-frame display))
795 (chess-display-clone (current-buffer) 'chess-images
796 chess-display-perspective)))
798 (defun chess-display-duplicate (style)
800 (list (concat "chess-"
801 (read-from-minibuffer "Create new display using style: "))))
802 (chess-display-clone (current-buffer) (intern-soft style)
803 chess-display-perspective))
805 (defun chess-display-pass ()
806 "Pass the move to your opponent. Only valid on the first move."
808 (if (chess-display-active-p)
809 (chess-game-run-hooks chess-module-game 'pass)
812 (defun chess-display-shuffle ()
813 "Generate a shuffled opening position."
815 (require 'chess-random)
816 (if (and (chess-display-active-p)
817 (= 0 chess-display-index))
818 (chess-game-set-start-position chess-module-game
819 (chess-fischer-random-position))
822 (defun chess-display-match ()
823 "Request a match with any listening engine."
825 (chess-game-run-hooks chess-module-game 'match))
827 (defun chess-display-accept ()
829 (if (chess-display-active-p)
830 (chess-game-run-hooks chess-module-game 'accept)
833 (defun chess-display-decline ()
835 (if (chess-display-active-p)
836 (chess-game-run-hooks chess-module-game 'decline)
839 (defun chess-display-retract ()
841 (if (chess-display-active-p)
842 (chess-game-run-hooks chess-module-game 'retract)
845 (defun chess-display-call-flag ()
847 (if (chess-display-active-p)
848 (chess-game-run-hooks chess-module-game 'call-flag)
851 (defun chess-display-force ()
853 (if (chess-display-active-p)
854 (chess-game-run-hooks chess-module-game 'force)
857 (defun chess-display-check-autosave ()
859 (if (chess-display-active-p)
860 (chess-game-run-hooks chess-module-game 'check-autosave)
863 (defun chess-display-resign ()
864 "Resign the current game."
866 (if (chess-display-active-p)
867 (chess-game-end chess-module-game :resign)
870 (defun chess-display-abort ()
871 "Abort the current game."
873 (if (chess-display-active-p)
874 (chess-game-run-hooks chess-module-game 'abort)
877 (chess-message-catalog 'english
878 '((draw-offer . "You offer a draw")))
880 (defun chess-display-draw ()
881 "Offer to draw the current game."
883 (if (chess-display-active-p)
885 (chess-message 'draw-offer)
886 (chess-game-run-hooks chess-module-game 'draw))
889 (defun chess-display-undo (count)
890 "Abort the current game."
892 (if (chess-display-active-p)
894 ;; we can't call `chess-game-undo' directly, because not all
895 ;; engines will accept it right away! So we just signal the
899 (prefix-numeric-value count)
900 (if (eq (chess-pos-side-to-move (chess-display-position nil))
901 (chess-game-data chess-module-game 'my-color))
903 (chess-game-run-hooks chess-module-game 'undo count))
906 (defun chess-display-list-buffers ()
907 "List all buffers related to this display's current game."
909 (let ((buffer-list-func (symbol-function 'buffer-list)))
911 (let ((chess-game chess-module-game)
912 (lb-command (lookup-key ctl-x-map [(control ?b)]))
913 (ibuffer-maybe-show-regexps nil))
920 (and (bufferp (cdr cell))
921 (buffer-live-p (cdr cell))
923 (chess-game-hooks chess-game))))))
924 (call-interactively lb-command))
925 (fset 'buffer-list buffer-list-func))))
927 (chess-message-catalog 'english
928 '((return-to-current . "Use '>' to return to the current position")))
930 (defun chess-display-set-current (dir)
931 "Change the currently displayed board.
932 Direction may be - or +, to move forward or back, or t or nil to jump
933 to the end or beginning."
934 (let ((index (cond ((eq dir ?-) (1- chess-display-index))
935 ((eq dir ?+) (1+ chess-display-index))
938 (chess-display-set-index
939 nil (or index (chess-game-index chess-module-game)))
940 (unless (chess-display-active-p)
941 (chess-message 'return-to-current))))
943 (defun chess-display-move-backward ()
945 (chess-display-set-current ?-))
947 (defun chess-display-move-forward ()
949 (chess-display-set-current ?+))
951 (defun chess-display-move-first ()
953 (chess-display-set-current nil))
955 (defun chess-display-move-last ()
957 (chess-display-set-current t))
959 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
961 ;; chess-display-edit-mode (for editing the position directly)
964 (defvar chess-display-edit-position nil)
966 (make-variable-buffer-local 'chess-display-edit-position)
968 (defvar chess-display-edit-mode-map
969 (let ((map (make-keymap)))
970 (suppress-keymap map)
972 (define-key map [(control ?l)] 'chess-display-redraw)
973 (define-key map [(control ?i)] 'chess-display-invert)
974 (define-key map [tab] 'chess-display-invert)
976 (define-key map [??] 'describe-mode)
977 (define-key map [?L] 'chess-display-list-buffers)
978 ;;(define-key map [?C] 'chess-display-duplicate)
979 (define-key map [?I] 'chess-display-invert)
981 (define-key map [?C] 'chess-display-clear-board)
982 (define-key map [?G] 'chess-display-restore-board)
983 (define-key map [?S] 'chess-display-send-board)
984 (define-key map [?X] 'chess-display-quit)
986 (let ((keys '(? ?p ?r ?n ?b ?q ?k ?P ?R ?N ?B ?Q ?K)))
988 (define-key map (vector (car keys)) 'chess-display-set-piece)
989 (setq keys (cdr keys))))
993 (define-key map [(button1)] 'chess-display-mouse-select-piece)
994 (define-key map [(button2)] 'chess-display-mouse-set-piece)
995 (define-key map [(button3)] 'chess-display-mouse-set-piece))
997 (define-key map [down-mouse-1] 'chess-display-mouse-select-piece)
998 (define-key map [drag-mouse-1] 'chess-display-mouse-select-piece)
1000 (define-key map [mouse-2] 'chess-display-mouse-set-piece)
1001 (define-key map [down-mouse-2] 'chess-display-mouse-set-piece)
1002 (define-key map [mouse-3] 'chess-display-mouse-set-piece)
1003 (define-key map [down-mouse-3] 'chess-display-mouse-set-piece)))
1006 "The mode map used for editing a chessboard position.")
1008 (chess-message-catalog 'english
1010 . "Now editing position directly, use S when complete...")
1011 (clear-chessboard-q . "Really clear the chessboard? ")))
1013 (defun chess-display-edit-board ()
1014 "Setup the current board for editing."
1016 (setq chess-display-edit-position
1017 (chess-pos-copy (chess-display-position nil))
1018 chess-display-edit-mode t
1019 chess-display-side-to-move (chess-string 'mode-edit))
1020 (force-mode-line-update)
1021 (use-local-map chess-display-edit-mode-map)
1022 (funcall chess-display-event-handler 'start-edit)
1023 (chess-message 'editing-directly))
1025 (defun chess-display-end-edit-mode ()
1026 (setq chess-display-edit-mode nil)
1027 (funcall chess-display-event-handler 'end-edit)
1028 (use-local-map chess-display-mode-map))
1030 (defun chess-display-send-board ()
1031 "Send the current board configuration to the user."
1033 (chess-display-end-edit-mode)
1034 (chess-game-set-start-position chess-module-game
1035 chess-display-edit-position))
1037 (defun chess-display-restore-board ()
1038 "Setup the current board for editing."
1040 (chess-display-end-edit-mode)
1041 ;; reset the modeline
1042 (chess-display-set-index* nil chess-display-index)
1043 (chess-display-update nil))
1045 (defun chess-display-clear-board ()
1046 "Setup the current board for editing."
1048 (when (y-or-n-p (chess-string 'clear-chessboard-q))
1049 (let ((position (chess-display-position nil)))
1052 (chess-pos-set-piece position (cons rank file) ? ))))
1053 (chess-display-update nil)))
1055 (defun chess-display-set-piece (&optional piece)
1056 "Set the piece under point to command character, or space for clear."
1058 (if (or (null piece) (char-valid-p piece))
1059 (let ((index (get-text-property (point) 'chess-coord)))
1060 (chess-pos-set-piece chess-display-edit-position index
1061 (or piece last-command-char))
1062 (funcall chess-display-event-handler 'draw-square
1063 (point) (or piece last-command-char) index))))
1065 (defun chess-display-mouse-set-piece (event)
1066 "Select the piece the user clicked on."
1068 (if (fboundp 'event-window) ; XEmacs
1070 (set-buffer (window-buffer (event-window event)))
1071 (and (event-point event) (goto-char (event-point event))))
1072 (set-buffer (window-buffer (posn-window (event-start event))))
1073 (goto-char (posn-point (event-start event))))
1074 (let ((pieces (if (memq (car event) '(down-mouse-3 mouse-3))
1089 (chess-display-set-piece (x-popup-menu t pieces))))
1091 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1093 ;; Mousing around on the chess-display
1096 (defvar chess-display-last-selected nil)
1098 (make-variable-buffer-local 'chess-display-last-selected)
1100 (chess-message-catalog 'english
1101 '((cannot-mount . "You cannot move pieces on top of each other")
1102 (move-not-legal . "That is not a legal move")
1103 (not-your-move . "It is not your turn to move")
1104 (wrong-color . "You cannot move your opponent's pieces")
1105 (selected-empty . "You cannot select an empty square")
1106 (piece-immobile . "That piece cannot move now")))
1108 (defun chess-display-select-piece ()
1109 "Select the piece under the cursor.
1110 Clicking once on a piece selects it; then click on the target location."
1112 (let ((coord (get-text-property (point) 'chess-coord))
1113 (position (chess-display-position nil))
1118 (if chess-display-last-selected
1119 (let ((last-sel chess-display-last-selected))
1120 ;; if they select the same square again, just deselect
1121 ;; it by redrawing the display and removing all
1123 (if (= (point) (car last-sel))
1124 (funcall chess-display-event-handler 'draw-square
1126 (chess-pos-piece position (cdr last-sel))
1128 (let ((s-piece (chess-pos-piece position (cdr last-sel)))
1129 (t-piece (chess-pos-piece position coord)) ply)
1130 (if chess-display-edit-mode
1132 (chess-pos-set-piece position (cdr last-sel) ? )
1133 (chess-pos-set-piece position coord s-piece)
1134 (chess-display-update nil))
1135 (if (and (/= t-piece ? )
1136 (or (and (< t-piece ?a)
1140 (throw 'message (chess-string 'cannot-mount)))
1141 (unless (setq ply (chess-ply-create position nil
1144 (throw 'message (chess-string 'move-not-legal)))
1146 (chess-display-move nil ply
1147 (car last-sel) (point))
1149 (throw 'message (error-message-string err)))))))
1150 (setq chess-display-last-selected nil))
1151 (let ((piece (chess-pos-piece position coord)))
1154 (throw 'message (chess-string 'selected-empty)))
1155 ((not (or chess-display-edit-mode
1156 (not (chess-display-active-p))
1157 (eq (chess-pos-side-to-move position)
1158 (chess-game-data chess-module-game
1160 (throw 'message (chess-string 'not-your-move)))
1161 ((and (not chess-display-edit-mode)
1162 (if (chess-pos-side-to-move position)
1165 (throw 'message (chess-string 'wrong-color)))
1166 ((and (not chess-display-edit-mode)
1167 chess-display-highlight-legal
1168 (null (chess-legal-plies position :any :index coord)))
1169 (throw 'message (chess-string 'piece-immobile))))
1170 (setq chess-display-last-selected (cons (point) coord))
1171 (chess-display-highlight nil coord)
1172 (if (and (not chess-display-edit-mode)
1173 chess-display-highlight-legal)
1174 (chess-display-highlight-legal nil coord))))))
1176 (when chess-display-last-selected
1177 (funcall chess-display-event-handler 'draw-square
1178 (car chess-display-last-selected)
1179 (chess-pos-piece position
1180 (cdr chess-display-last-selected))
1181 (cdr chess-display-last-selected))
1182 (setq chess-display-last-selected nil))
1183 (message message)))))
1185 (defun chess-display-mouse-select-piece (event)
1186 "Select the piece the user clicked on."
1188 (if (fboundp 'event-window) ; XEmacs
1190 (set-buffer (window-buffer (event-window event)))
1191 (and (event-point event) (goto-char (event-point event))))
1192 (if (equal (event-start event) (event-end event))
1194 (set-buffer (window-buffer (posn-window (event-start event))))
1195 (goto-char (posn-point (event-start event))))
1196 (goto-char (posn-point (event-end event)))))
1197 (chess-display-select-piece))
1199 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1201 ;; Maintain a face cache for given color strings
1204 (defvar chess-display-face-cache '((t . t)))
1206 (defun chess-display-get-face (color)
1207 (or (cdr (assoc color chess-display-face-cache))
1208 (let ((face (make-face 'chess-display-highlight)))
1209 (set-face-attribute face nil :background color)
1210 (add-to-list 'chess-display-face-cache (cons color face))
1213 (provide 'chess-display)
1215 ;;; chess-display.el ends here