Merge from emacs--rel--22
[bpt/emacs.git] / lisp / play / bubbles.el
index a3faecb..b4997ce 100644 (file)
@@ -1,36 +1,28 @@
 ;;; bubbles.el --- Puzzle game for Emacs.
 
-;; Copyright (C) 2007 Free Software Foundation, Inc.
-
-;; This file is NOT part of GNU Emacs.
+;; Copyright (C) 2007, 2008  Free Software Foundation, Inc.
 
 ;; Author:      Ulf Jasper <ulf.jasper@web.de>
-;; Filename:    bubbles.el
 ;; URL:         http://ulf.epplejasper.de/
 ;; Created:     5. Feb. 2007
-;; Keywords:    Games
-;; Time-stamp:  "27. August 2007, 19:51:08 (ulf)"
-;; CVS-Version: $Id: bubbles.el,v 1.16 2007-08-27 17:51:29 ulf Exp $
+;; Keywords:    games
 
-;; ======================================================================
+;; This file is part of GNU Emacs.
 
-;; This program is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2 of the License, or (at
-;; your option) any later version.
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
 
-;; This program is distributed in the hope that it will be useful, but
-;; WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-;; General Public License for more details.
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with this program; if not, write to the Free Software Foundation,
-;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-(defconst bubbles-version "0.4" "Version number of bubbles.el.")
-
-;; ======================================================================
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
@@ -38,7 +30,7 @@
 ;; possible in as few moves as possible.
 
 ;; Bubbles is an implementation of the "Same Game", similar to "Same
-;; GNOME" and many others, see http://en.wikipedia.org/wiki/SameGame.
+;; GNOME" and many others, see <http://en.wikipedia.org/wiki/SameGame>.
 
 ;; Installation
 ;; ------------
@@ -51,6 +43,9 @@
 
 ;;; History:
 
+;; 0.5 (2007-09-14)
+;;     - Minor bugfixes.
+
 ;; 0.4 (2007-08-27)
 ;;     - Allow for undoing last move.
 ;;     - Bonus for removing all bubbles.
@@ -86,6 +81,8 @@
 
 ;;; Code:
 
+(defconst bubbles-version "0.5" "Version number of bubbles.el.")
+
 (require 'gamegrid)
 (require 'cl)
 
@@ -830,89 +827,98 @@ static char * dot3d_xpm[] = {
   (bubbles--initialize-images)
   (bubbles--update-faces-or-images))
 
-;; bubbles mode map
-(defvar bubbles-mode-map
-  (make-keymap 'bubbles-mode-map))
-(define-key bubbles-mode-map "q" 'bubbles-quit)
-(define-key bubbles-mode-map "\n" 'bubbles-plop)
-(define-key bubbles-mode-map " " 'bubbles-plop)
-(define-key bubbles-mode-map [double-down-mouse-1] 'bubbles-plop)
-(define-key bubbles-mode-map [mouse-2] 'bubbles-plop)
-(define-key bubbles-mode-map "\C-m" 'bubbles-plop)
-(define-key bubbles-mode-map "u" 'bubbles-undo)
-(define-key bubbles-mode-map "p" 'previous-line)
-(define-key bubbles-mode-map "n" 'next-line)
-(define-key bubbles-mode-map "f" 'forward-char)
-(define-key bubbles-mode-map "b" 'backward-char)
-
-
 ;; game theme menu
-(defvar bubbles-game-theme-menu (make-sparse-keymap "Game Theme"))
-(define-key bubbles-game-theme-menu [bubbles-set-game-userdefined]
-  (list 'menu-item "User defined" 'bubbles-set-game-userdefined
-        :button '(:radio . (eq bubbles-game-theme 'user-defined))))
-(define-key bubbles-game-theme-menu [bubbles-set-game-hard]
-  (list 'menu-item "Hard" 'bubbles-set-game-hard
-        :button '(:radio . (eq bubbles-game-theme 'hard))))
-(define-key bubbles-game-theme-menu [bubbles-set-game-difficult]
-  (list 'menu-item "Difficult" 'bubbles-set-game-difficult
-        :button '(:radio . (eq bubbles-game-theme 'difficult))))
-(define-key bubbles-game-theme-menu [bubbles-set-game-medium]
-  (list 'menu-item "Medium" 'bubbles-set-game-medium
-        :button '(:radio . (eq bubbles-game-theme 'medium))))
-(define-key bubbles-game-theme-menu [bubbles-set-game-easy]
-  (list 'menu-item "Easy" 'bubbles-set-game-easy
-        :button '(:radio . (eq bubbles-game-theme 'easy))))
+(defvar bubbles-game-theme-menu
+  (let ((menu (make-sparse-keymap "Game Theme")))
+    (define-key menu [bubbles-set-game-userdefined]
+      (list 'menu-item "User defined" 'bubbles-set-game-userdefined
+            :button '(:radio . (eq bubbles-game-theme 'user-defined))))
+    (define-key menu [bubbles-set-game-hard]
+      (list 'menu-item "Hard" 'bubbles-set-game-hard
+            :button '(:radio . (eq bubbles-game-theme 'hard))))
+    (define-key menu [bubbles-set-game-difficult]
+      (list 'menu-item "Difficult" 'bubbles-set-game-difficult
+            :button '(:radio . (eq bubbles-game-theme 'difficult))))
+    (define-key menu [bubbles-set-game-medium]
+      (list 'menu-item "Medium" 'bubbles-set-game-medium
+            :button '(:radio . (eq bubbles-game-theme 'medium))))
+    (define-key menu [bubbles-set-game-easy]
+      (list 'menu-item "Easy" 'bubbles-set-game-easy
+            :button '(:radio . (eq bubbles-game-theme 'easy))))
+    menu)
+  "Map for bubbles game theme menu.")
 
 ;; graphics theme menu
-(defvar bubbles-graphics-theme-menu (make-sparse-keymap "Graphics Theme"))
-(define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-ascii]
-  (list 'menu-item "ASCII" 'bubbles-set-graphics-theme-ascii
-        :button '(:radio . (eq bubbles-graphics-theme 'ascii))))
-(define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-emacs]
-  (list 'menu-item "Emacs" 'bubbles-set-graphics-theme-emacs
-        :button '(:radio . (eq bubbles-graphics-theme 'emacs))))
-(define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-balls]
-  (list 'menu-item "Balls" 'bubbles-set-graphics-theme-balls
-        :button '(:radio . (eq bubbles-graphics-theme 'balls))))
-(define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-diamonds]
-  (list 'menu-item "Diamonds" 'bubbles-set-graphics-theme-diamonds
-        :button '(:radio . (eq bubbles-graphics-theme 'diamonds))))
-(define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-squares]
-  (list 'menu-item "Squares" 'bubbles-set-graphics-theme-squares
-        :button '(:radio . (eq bubbles-graphics-theme 'squares))))
-(define-key bubbles-graphics-theme-menu [bubbles-set-graphics-theme-circles]
-  (list 'menu-item "Circles" 'bubbles-set-graphics-theme-circles
-        :button '(:radio . (eq bubbles-graphics-theme 'circles))))
+(defvar bubbles-graphics-theme-menu
+  (let ((menu (make-sparse-keymap "Graphics Theme")))
+    (define-key menu [bubbles-set-graphics-theme-ascii]
+      (list 'menu-item "ASCII" 'bubbles-set-graphics-theme-ascii
+            :button '(:radio . (eq bubbles-graphics-theme 'ascii))))
+    (define-key menu [bubbles-set-graphics-theme-emacs]
+      (list 'menu-item "Emacs" 'bubbles-set-graphics-theme-emacs
+            :button '(:radio . (eq bubbles-graphics-theme 'emacs))))
+    (define-key menu [bubbles-set-graphics-theme-balls]
+      (list 'menu-item "Balls" 'bubbles-set-graphics-theme-balls
+            :button '(:radio . (eq bubbles-graphics-theme 'balls))))
+    (define-key menu [bubbles-set-graphics-theme-diamonds]
+      (list 'menu-item "Diamonds" 'bubbles-set-graphics-theme-diamonds
+            :button '(:radio . (eq bubbles-graphics-theme 'diamonds))))
+    (define-key menu [bubbles-set-graphics-theme-squares]
+      (list 'menu-item "Squares" 'bubbles-set-graphics-theme-squares
+            :button '(:radio . (eq bubbles-graphics-theme 'squares))))
+    (define-key menu [bubbles-set-graphics-theme-circles]
+      (list 'menu-item "Circles" 'bubbles-set-graphics-theme-circles
+            :button '(:radio . (eq bubbles-graphics-theme 'circles))))
+    menu)
+    "Map for bubbles graphics theme menu.")
 
 ;; menu
-(defvar bubbles-menu (make-sparse-keymap "Bubbles"))
-(define-key bubbles-menu [bubbles-quit]
-  (list 'menu-item "Quit" 'bubbles-quit))
-(define-key bubbles-menu [bubbles]
-  (list 'menu-item "New game" 'bubbles))
-(define-key bubbles-menu [bubbles-separator-1]
-  '("--"))
-(define-key bubbles-menu [bubbles-save-settings]
-  (list 'menu-item "Save all settings" 'bubbles-save-settings))
-(define-key bubbles-menu [bubbles-customize]
-  (list 'menu-item "Edit all settings" 'bubbles-customize))
-(define-key bubbles-menu [bubbles-game-theme-menu]
-  (list 'menu-item "Game Theme" bubbles-game-theme-menu))
-(define-key bubbles-menu [bubbles-graphics-theme-menu]
-  (list 'menu-item "Graphics Theme" bubbles-graphics-theme-menu
-        :enable 'bubbles--playing))
-(define-key bubbles-menu [bubbles-separator-2]
-  '("--"))
-(define-key bubbles-menu [bubbles-undo]
-  (list 'menu-item "Undo last move" 'bubbles-undo
-        :enable '(and bubbles--playing bubbles--save-data)))
-
-;; bind menu to mouse
-(define-key bubbles-mode-map [down-mouse-3] bubbles-menu)
-;; Put menu in menu-bar
-(define-key bubbles-mode-map [menu-bar Bubbles]
-  (cons "Bubbles" bubbles-menu))
+(defvar bubbles-menu
+  (let ((menu (make-sparse-keymap "Bubbles")))
+    (define-key menu [bubbles-quit]
+      (list 'menu-item "Quit" 'bubbles-quit))
+    (define-key menu [bubbles]
+      (list 'menu-item "New game" 'bubbles))
+    (define-key menu [bubbles-separator-1]
+      '("--"))
+    (define-key menu [bubbles-save-settings]
+      (list 'menu-item "Save all settings" 'bubbles-save-settings))
+    (define-key menu [bubbles-customize]
+      (list 'menu-item "Edit all settings" 'bubbles-customize))
+    (define-key menu [bubbles-game-theme-menu]
+      (list 'menu-item "Game Theme" bubbles-game-theme-menu))
+    (define-key menu [bubbles-graphics-theme-menu]
+      (list 'menu-item "Graphics Theme" bubbles-graphics-theme-menu
+            :enable 'bubbles--playing))
+    (define-key menu [bubbles-separator-2]
+      '("--"))
+    (define-key menu [bubbles-undo]
+      (list 'menu-item "Undo last move" 'bubbles-undo
+            :enable '(and bubbles--playing (listp buffer-undo-list))))
+    menu)
+  "Map for bubbles menu.")
+
+;; bubbles mode map
+(defvar bubbles-mode-map
+  (let ((map (make-sparse-keymap 'bubbles-mode-map)))
+;;    (suppress-keymap map t)
+    (define-key map "q" 'bubbles-quit)
+    (define-key map "\n" 'bubbles-plop)
+    (define-key map " " 'bubbles-plop)
+    (define-key map [double-down-mouse-1] 'bubbles-plop)
+    (define-key map [mouse-2] 'bubbles-plop)
+    (define-key map "\C-m" 'bubbles-plop)
+    (define-key map "u" 'bubbles-undo)
+    (define-key map "p" 'previous-line)
+    (define-key map "n" 'next-line)
+    (define-key map "f" 'forward-char)
+    (define-key map "b" 'backward-char)
+    ;; bind menu to mouse
+    (define-key map [down-mouse-3] bubbles-menu)
+    ;; Put menu in menu-bar
+    (define-key map [menu-bar Bubbles] (cons "Bubbles" bubbles-menu))
+    map)
+  "Mode map for bubbles.")
 
 (defun bubbles-mode ()
   "Major mode for playing bubbles.
@@ -922,7 +928,10 @@ static char * dot3d_xpm[] = {
   (setq major-mode 'bubbles-mode)
   (setq mode-name "Bubbles")
   (setq buffer-read-only t)
-  (buffer-enable-undo)
+  (buffer-disable-undo)
+  (setq buffer-undo-list t)
+  (force-mode-line-update)
+  (redisplay)
   (add-hook 'post-command-hook 'bubbles--mark-neighbourhood t t)
   (run-hooks 'bubbles-mode-hook))
 
@@ -1020,7 +1029,10 @@ Set `bubbles--col-offset' and `bubbles--row-offset'."
   (bubbles-mode)
   (bubbles--reset-score)
   (bubbles--update-faces-or-images)
-  (bubbles--goto 0 0))
+  (bubbles--goto 0 0)
+  (setq buffer-undo-list t)
+  (force-mode-line-update)
+  (redisplay))
 
 (defun bubbles--initialize-faces ()
   "Prepare faces for playing `bubbles'."
@@ -1202,7 +1214,6 @@ Use optional parameter POS instead of point if given."
   (when (and bubbles--playing
              (> bubbles--neighbourhood-score 0))
     (setq bubbles--save-data (list bubbles--score (buffer-string)))
-    (setq buffer-undo-list '(apply bubbles-undo . nil))
     (let ((inhibit-read-only t))
       ;; blank out current neighbourhood
       (let ((row (bubbles--row (point)))
@@ -1251,7 +1262,8 @@ Use optional parameter POS instead of point if given."
                    (dotimes (j (bubbles--grid-width))
                      (bubbles--goto i j)
                      (while (get-text-property (point) 'removed)
-                       (setq shifted (or (bubbles--shift 'right i j) shifted))))))
+                       (setq shifted (or (bubbles--shift 'right i j)
+                                         shifted))))))
                (bubbles--update-faces-or-images)
                (sleep-for 0.5))
               (t ;; default shift-mode
@@ -1265,7 +1277,8 @@ Use optional parameter POS instead of point if given."
                      (dotimes (k shifted-cols)
                        (let ((i (- (bubbles--grid-height) 2)))
                          (while (>= i 0)
-                           (setq shifted (or (bubbles--shift 'right i j) shifted))
+                           (setq shifted (or (bubbles--shift 'right i j)
+                                             shifted))
                            (setq i (1- i))))))))))
         (when shifted
           ;;(sleep-for 0.5)
@@ -1273,7 +1286,11 @@ Use optional parameter POS instead of point if given."
           (sit-for 0)))
       (put-text-property (point-min) (point-max) 'removed nil)
       (unless (bubbles--neighbourhood-available)
-        (bubbles--game-over)))))
+        (bubbles--game-over)))
+    ;; undo
+    (setq buffer-undo-list '((apply bubbles-undo . nil)))
+    (force-mode-line-update)
+    (redisplay)))
 
 (defun bubbles-undo ()
   "Undo last move."
@@ -1285,7 +1302,10 @@ Use optional parameter POS instead of point if given."
       (insert (cadr bubbles--save-data))
       (bubbles--update-faces-or-images)
       (setq bubbles--score (car bubbles--save-data))
-      (goto-char pos))))
+      (goto-char pos))
+    (setq buffer-undo-list t)
+    (force-mode-line-update)
+    (redisplay)))
 
 (defun bubbles--shift (from row col)
   "Move bubbles FROM one side to position ROW COL.
@@ -1329,7 +1349,7 @@ Return t if new char is non-empty."
       (setq bubbles--empty-image
             (create-image (replace-regexp-in-string
                            "^\"\\(.*\\)\t.*c .*\",$"
-                           "\"\\1\tc #FFFFFF\"," template)
+                           "\"\\1\tc None\"," template)
                           'xpm t
                           ;;:mask 'heuristic
                           :margin '(2 . 1)))
@@ -1414,7 +1434,7 @@ Return t if new char is non-empty."
           (dotimes (i (bubbles--grid-height))
             (dotimes (j (bubbles--grid-width))
               (forward-char 1)
-              (let ((index (get-text-property (point) 'index)))
+              (let ((index (or (get-text-property (point) 'index) -1)))
                 (let ((img bubbles--empty-image))
                   (if (>= index 0)
                       (setq img (nth index bubbles--images)))
@@ -1435,4 +1455,5 @@ Return t if new char is non-empty."
 
 (provide 'bubbles)
 
+;; arch-tag: 2cd7237a-b0ad-400d-a7fd-75f676dceb70
 ;;; bubbles.el ends here