diff lisp/modes/xpm-mode.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ac2d302a0011
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/modes/xpm-mode.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,441 @@
+;;; xpm-mode.el	--- minor mode for editing XPM files
+
+;; Copyright (C) 1995 Joe Rumsey <ogre@netcom.com>
+;; Copyright (C) 1995 Rich Williams <rdw@hplb.hpl.hp.com>
+
+;; Authors: Joe Rumsey <ogre@netcom.com>
+;;	    Rich Williams <rdw@hplb.hpl.hp.com>
+;; Cleanup: Chuck Thompson <cthomp@cs.uiuc.edu>
+
+;; Version:  1.5
+;; Last Modified: Rich Williams <rdw@hplb.hpl.hp.com>, 13 July 1995
+;; Keywords: data tools
+
+;; This file is part of XEmacs.
+
+;; XEmacs 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, or (at your option)
+;; any later version.
+
+;; XEmacs 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 XEmacs; see the file COPYING.  If not, write to the Free
+;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;; Synched up with: Not in FSF.
+
+;;
+;; xpm mode:  Display xpm files in color
+;;
+;; thanks to Rich Williams for mods to do this without font-lock-mode,
+;; resulting in much improved performance and a better display
+;; (headers don't get colored strangely). Also for the palette toolbar.
+;;
+;; Non-standard minor mode in that it starts picture-mode automatically.
+;;
+;; To get this turned on automatically for .xpms, add an entry
+;;       ("\\.xpm" . xpm-mode)
+;; to your auto-mode-alist.  For example, my .emacs has this: (abbreviated)
+;; (setq auto-mode-alist (mapcar 'purecopy
+;;                               '(("\\.c$" . c-mode)
+;;                                ("\\.h$" . c-mode)
+;;                                ("\\.el$" . emacs-lisp-mode)
+;;				  ("\\.emacs$" . emacs-lisp-mode)
+;;                                ("\\.a$" . c-mode)
+;;				  ("\\.xpm" . xpm-mode))))
+;; (autoload 'xpm-mode "xpm-mode")
+;;
+;; I am a lisp newbie, practically everything in here I had to look up
+;; in the manual.  It probably shows, suggestions for coding
+;; improvements are welcomed.
+;;
+;; May fail on some xpm's.  Seems to be fine with files generated by
+;; xpaint and ppmtoxpm anyway.  Will definitely fail on xpm's with
+;; more than one character per pixel.  Not that hard to fix, but I've
+;; never seen one like that.
+;;
+;; If your default font is proportional, this will not be very useful.
+;;
+
+(require 'annotations)
+
+(defvar xpm-pixel-values nil)
+(defvar xpm-glyph nil)
+(defvar xpm-anno nil)
+(defvar xpm-paint-string nil)
+(defvar xpm-chars-per-pixel 1)
+(defvar xpm-palette nil)
+(defvar xpm-always-update-image nil
+  "If non-nil, update actual-size image after every click or drag movement.
+Otherwise, only update on button releases or when asked to.  This is slow.")
+
+(make-variable-buffer-local 'xpm-palette)
+(make-variable-buffer-local 'xpm-chars-per-pixel)
+(make-variable-buffer-local 'xpm-paint-string)
+(make-variable-buffer-local 'xpm-glyph)
+(make-variable-buffer-local 'xpm-anno)
+(make-variable-buffer-local 'xpm-pixel-values)
+;(make-variable-buffer-local 'xpm-faces-used)
+
+(defun xpm-make-face (name)
+  "Makes a face with name xpm-NAME, and colour NAME."
+  (let ((face (make-face (intern (concat "xpm-" name))
+			 "Temporary xpm-mode face" t)))
+    (set-face-background face name)
+    (set-face-foreground face "black")
+    face))
+
+(defun xpm-init ()
+  "Treat the current buffer as an xpm file and colorize it."
+  (interactive)
+  (require 'picture)
+
+  (setq xpm-pixel-values nil)
+  (xpm-clear-extents)
+  (setq xpm-palette nil)
+
+  (message "Finding number of colors...")
+  (save-excursion
+    (goto-char (point-min))
+    (beginning-of-line)
+    (next-line 1)
+    (while (not (looking-at "\\s-*\""))
+      (next-line 1))
+    (next-line 1)
+    (while (not (looking-at "\\s-*\""))
+      (next-line 1))
+    (let ((co 0))
+      (while (< co (xpm-num-colors))
+	(progn
+	  (xpm-parse-color)
+	  (setq co (1+ co))
+	  (next-line 1)
+	  (beginning-of-line)))))
+  (if (not (eq major-mode 'picture-mode))
+      (picture-mode))
+  (set-specifier left-toolbar-width (cons (selected-frame) 16))
+  (set-specifier left-toolbar (cons (current-buffer) xpm-palette))
+  (message "Parsing body...")
+  (xpm-color-data)
+  (message "Parsing body...done")
+  (xpm-show-image))
+
+(defun xpm-clear-extents ()
+  (let (cur-extent
+	next-extent)
+    (setq cur-extent (next-extent (current-buffer)))
+    (setq next-extent (next-extent cur-extent))
+    (while cur-extent
+      (delete-extent cur-extent)
+      (setq cur-extent next-extent)
+      (setq next-extent (next-extent cur-extent)))))
+
+(defun xpm-color-data ()
+  (interactive)
+  (save-excursion
+    (xpm-goto-body-line 0)
+    (let (ext
+	  pixel-chars
+	  pixel-color)
+      (while (< (point) (point-max))
+	(setq pixel-chars
+	      (buffer-substring (point) (+ (point) xpm-chars-per-pixel))
+	      pixel-color (assoc pixel-chars xpm-pixel-values)
+	      ext (make-extent (point) (+ (point) xpm-chars-per-pixel)))
+	(if pixel-color
+	    (progn
+	      (set-extent-face ext (cdr pixel-color)))
+	  (set-extent-face ext 'default))
+	(forward-char xpm-chars-per-pixel)))))
+
+(defun xpm-num-colors ()
+  (save-excursion
+    (goto-char (point-min))
+    (if (re-search-forward 
+	 "\"\\s-*\\([0-9]+\\)\\s-+\\([0-9]+\\)\\s-+\\([0-9]+\\)\\s-+\\([0-9]+\\)\\s-*"
+	 (point-max) t)
+	(string-to-int (match-string 3))
+      (error "Unable to parse xpm information"))))
+
+(defun xpm-make-solid-pixmap (colour width height)
+  (let ((x 0)
+	(y 0)
+	(line nil)
+	(total nil))
+    (setq line ",\n\"")
+    (while (< x width)
+      (setq line (concat line ".")
+	    x (+ x 1)))
+    (setq line (concat line "\"")
+	  total (format "/* XPM */\nstatic char * %s[] = {\n\"%d %d 1 1\",\n\". c %s\""
+			colour width height colour))
+    (while (< y height)
+      (setq total (concat total line)
+	    y (+ y 1)))
+    (make-glyph (concat total "};\n"))))
+
+(defun xpm-store-color (str color)
+  "Add STR to xpm-pixel-values with a new face set to background COLOR
+if STR already has an entry, the existing face will be used, with the
+new color replacing the old (on the display only, not in the xpm color
+defs!)"
+  (let (new-face)
+    (setq new-face (xpm-make-face color))
+    (set-face-background new-face color)
+    (let ((ccc (color-rgb-components (make-color-specifier color))))
+      (if (> (length ccc) 0)
+	  (if (or (or (> (elt ccc 0) 32767)
+		      (> (elt ccc 1) 32767))
+		  (> (elt ccc 2) 32767))
+	      (set-face-foreground new-face "black")
+	    (set-face-foreground new-face "white"))))
+    (setq xpm-pixel-values (cons (cons str new-face) xpm-pixel-values)
+	  xpm-palette
+	  (cons (vector 
+		 (list (xpm-make-solid-pixmap color 12 12))
+		 ;; Major cool things with quotes.....
+		 (` 
+		  (lambda (event)
+		    (interactive "e")
+		    (xpm-toolbar-select-colour event (, str))))
+		 t
+		 color) xpm-palette))
+    ))
+
+(defun xpm-parse-color ()
+  "Parse xpm color string from current line and set the color"
+  (interactive)
+  (let (end)
+    (save-excursion
+      (end-of-line)
+      (setq end (point))
+      (beginning-of-line)
+      (if (re-search-forward
+	   ;; Generate a regexp on the fly
+	   (concat "\"\\(" (make-string xpm-chars-per-pixel ?.) "\\)" ; chars
+		   "\\s-+\\([c]\\)"	; there are more classes than 'c'
+		   "\\s-+\\([^\"]+\\)\"")
+	   end t)
+	  (progn 
+	    (xpm-store-color (match-string 1) (match-string 3))
+	    (list (match-string 1) (match-string 3)))
+	(error "Unable to parse color")))))
+
+(defun xpm-add-color (str color)
+  "add a color to an xpm's list of color defs"
+  (interactive "sPixel character: 
+sPixel color (any valid X color string):")
+  (save-excursion
+    (goto-char (point-min))
+    (while (not (looking-at "\\s-*\""))
+      (next-line 1))
+    (next-line 1)
+    (while (not (looking-at "\\s-*\""))
+      (next-line 1))
+    (let ((co 0))
+      (while (< co (xpm-num-colors))
+	(next-line 1)
+	(setq co (1+ co))))
+    (insert (format "\"%s\tc %s\",\n" str color))
+    (previous-line 1)
+    (xpm-parse-color)
+
+    (goto-char (point-min))
+    (while (not (looking-at "\\s-*\""))
+      (next-line 1))
+    (let ((entry 0))
+      (while (or (= (char-after (point)) ? ) (= (char-after (point)) ?\"))
+	(forward-char 1))
+      (while (< entry 2)
+	(progn
+	  (if (eq (char-after (point)) ? )
+	      (progn
+		(setq entry (1+ entry))
+		(while (eq (char-after (point)) ? )
+		  (forward-char 1)))
+	    (forward-char 1))))
+      (let ((old-colors (xpm-num-colors)))
+	(while (and (>= (char-after (point)) ?0) (<= (char-after (point)) ?9))
+	  (delete-char 1))
+      (insert (int-to-string (1+ old-colors)))))))
+
+
+(defun xpm-goto-color-def (def)
+  "move to color DEF in the xpm header"
+  (interactive "nColor number:")
+  (goto-char (point-min))
+  (while (not (looking-at "\\s-*\""))
+    (next-line 1))
+  (next-line 1)
+  (while (not (looking-at "\\s-*\""))
+    (next-line 1))
+  (next-line def))
+
+(defun xpm-goto-body-line (line)
+  "move to LINE lines down from the start of the body of an xpm"
+  (interactive "nBody line:")
+  (goto-char (point-min))
+  (xpm-goto-color-def (xpm-num-colors))
+  (next-line line))
+
+(defun xpm-show-image ()
+  "Display the xpm in the current buffer at the end of the topmost line"
+  (interactive)
+  (save-excursion
+    (if (annotationp xpm-anno)
+	(delete-annotation xpm-anno))
+    (setq xpm-glyph (make-glyph 
+		     (vector 'xpm :data 
+			     (buffer-substring (point-min) (point-max)))))
+    (goto-char (point-min))
+    (end-of-line)
+    (setq xpm-anno (make-annotation xpm-glyph (point) 'text))))
+
+(defun xpm-hide-image ()
+  "Remove the image of the xpm from the buffer"
+  (interactive)
+  (if (annotationp xpm-anno)
+      (delete-annotation xpm-anno)))
+
+(defun xpm-in-body ()
+  (let ((p (point)))
+    (save-excursion
+      (xpm-goto-body-line 0)
+      (> p (point)))))
+
+(defvar xpm-mode nil)
+(make-variable-buffer-local 'xpm-mode)
+(add-minor-mode 'xpm-mode " XPM" nil)
+(defvar xpm-mode-map (make-keymap))
+
+(defun xpm-toolbar-select-colour (event chars)
+  "Toolbar button"
+  (let* ((button (event-toolbar-button event))
+	 (help (toolbar-button-help-string button)))
+    (message "Toolbar selected %s (%s)"  help chars)
+    (setq xpm-palette
+	  (mapcar #'(lambda (but)
+		      (aset but 2 (not (eq help (aref but 3))))
+		      but)
+		  xpm-palette)
+	  xpm-paint-string chars)
+    (set-specifier left-toolbar (cons (current-buffer) xpm-palette))))
+
+(defun xpm-mouse-paint (event)
+  (interactive "e")
+  (mouse-set-point event)
+  (if (xpm-in-body)
+      ;; in body, overwrite the paint string where the mouse is clicked
+      (progn
+	(insert xpm-paint-string)
+	(delete-char (length xpm-paint-string)))
+    ;; otherwise, select the color defined by the line where the mouse
+    ;; was clicked
+    (save-excursion
+      (beginning-of-line)
+      (forward-char 1)
+      (setq xpm-paint-string (buffer-substring (point) (1+ (point)))))))
+
+(defun xpm-mouse-down (event n)
+;  (interactive "ep")
+  (mouse-set-point event)
+  (if (xpm-in-body)
+      ;; in body, overwrite the paint string where the mouse is clicked
+      (progn
+	(insert xpm-paint-string)
+	(delete-char (length xpm-paint-string))
+	(if xpm-always-update-image
+	    (xpm-show-image))
+	(let ((ext (make-extent (1- (point))
+				(+ (1- (point)) xpm-chars-per-pixel)))
+	      (pixel-color (assoc xpm-paint-string xpm-pixel-values)))
+	  (if pixel-color
+	      (set-extent-face ext (cdr pixel-color))
+	    (set-extent-face ext 'default))))
+    ;; otherwise, select the color defined by the line where the mouse
+    ;; was clicked
+    (save-excursion
+      (beginning-of-line)
+      (forward-char 1)
+      (setq xpm-paint-string (buffer-substring (point) (1+ (point)))))))
+
+(defun xpm-mouse-drag (event n timeout)
+  (or timeout
+      (progn
+	(mouse-set-point event)
+	(if (xpm-in-body)
+	    ;; Much improved by not using font-lock-mode
+	    (or (string= xpm-paint-string
+			 (buffer-substring (point)
+					   (+ (length xpm-paint-string)
+					      (point))))
+		(progn
+		  (insert-char (string-to-char xpm-paint-string) 1)
+					;	  (insert xpm-paint-string)
+		  (delete-char (length xpm-paint-string))
+		  (if xpm-always-update-image
+		      (xpm-show-image))
+		  (let ((ext (make-extent
+			      (1- (point))
+			      (+ (1- (point)) xpm-chars-per-pixel)))
+			(pixel-color
+			 (assoc xpm-paint-string xpm-pixel-values)))
+		    (if pixel-color
+			(set-extent-face ext (cdr pixel-color))
+		      (set-extent-face ext 'default)))))))))
+
+(defun xpm-mouse-up (event n)
+  (xpm-show-image))
+
+;;;###autoload
+(defun xpm-mode (&optional arg)
+  "Treat the current buffer as an xpm file and colorize it.
+
+  Shift-button-1 lets you paint by dragging the mouse.  Shift-button-1 on a
+color definition line will change the current painting color to that line's
+value.
+
+  Characters inserted from the keyboard will NOT be colored properly yet.
+Use the mouse, or do xpm-init (\\[xpm-init]) after making changes.
+
+\\[xpm-add-color] Add a new color, prompting for character and value
+\\[xpm-show-image] show the current image at the top of the buffer
+\\[xpm-parse-color] parse the current line's color definition and add
+   it to the color table.  Provided as a means of changing colors.
+XPM minor mode bindings:
+\\{xpm-mode-map}"
+
+  (interactive "P")
+  (setq xpm-mode
+	(if (null arg) (not xpm-mode)
+	  (> (prefix-numeric-value arg) 0)))
+  (if xpm-mode
+      (progn
+	(xpm-init)
+	(make-local-variable 'mouse-track-down-hook)
+	(make-local-variable 'mouse-track-drag-hook)
+	(make-local-variable 'mouse-track-up-hook)
+	(make-local-variable 'mouse-track-drag-up-hook)
+	(make-local-variable 'mouse-track-click-hook)
+	(setq mouse-track-down-hook 'xpm-mouse-down)
+	(setq mouse-track-drag-hook 'xpm-mouse-drag)
+	(setq mouse-track-up-hook 'xpm-mouse-up)
+	(setq mouse-track-drag-up-hook 'xpm-mouse-up)
+	(setq mouse-track-click-hook nil)
+	(or (assq 'xpm-mode minor-mode-map-alist)
+	    (progn
+	      (define-key xpm-mode-map [(control c) r] 'xpm-show-image)
+	      (define-key xpm-mode-map [(shift button1)] 'mouse-track)
+	      (define-key xpm-mode-map [button1] 'mouse-track-default)
+	      (define-key xpm-mode-map [(control c) c] 'xpm-add-color)
+	      (define-key xpm-mode-map [(control c) p] 'xpm-parse-color)
+	      (setq minor-mode-map-alist (cons (cons 'xpm-mode xpm-mode-map)
+					       minor-mode-map-alist)))))))
+
+(provide 'xpm-mode)
+;;; xpm-mode.el ends here