changeset 9494:9a4ed505445e

(facemenu-read-color, facemenu-colors): New fn, var. (facemenu-set-face, facemenu-set-face-from-menu, facemenu-after-change): Face property can take a list value; add to it rather than completely replacing the property. (facemenu-add-face, facemenu-discard-redundant-faces): New functions. (facemenu-set-foreground, facemenu-set-background) (facemenu-get-face, facemenu-foreground, facemenu-background): New functions and variables. Faces with names of the form fg:color and bg:color are now treated specially. (facemenu-update): Updated for above.
author Richard M. Stallman <rms@gnu.org>
date Wed, 12 Oct 1994 23:23:23 +0000
parents 0160fca3dee1
children 5825378d775b
files lisp/facemenu.el
diffstat 1 files changed, 159 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/facemenu.el	Wed Oct 12 23:12:25 1994 +0000
+++ b/lisp/facemenu.el	Wed Oct 12 23:23:23 1994 +0000
@@ -65,8 +65,6 @@
 ;; document (e.g., `region') are listed in `facemenu-unlisted-faces'.
 
 ;;; Known Problems:
-;; Only works with Emacs 19.23 and later.
-;;
 ;; There is at present no way to display what the faces look like in
 ;; the menu itself.
 ;;
@@ -115,9 +113,17 @@
 Set this before loading facemenu.el, or call `facemenu-update' after
 changing it.")
 
+(defvar facemenu-colors
+  (if (eq 'x window-system)
+      (mapcar 'list (x-defined-colors)))
+  "Alist of colors, used for completion.")
+
 (defvar facemenu-next nil) ; set when we are going to set a face on next char.
 (defvar facemenu-loc nil)
 
+(defalias 'facemenu-foreground (make-sparse-keymap "Foreground"))
+(defalias 'facemenu-background (make-sparse-keymap "Background"))
+
 (defun facemenu-update ()
   "Add or update the \"Face\" menu in the menu bar."
   (interactive)
@@ -134,35 +140,48 @@
   ;; We construct this list structure explicitly because a quoted constant
   ;; would be pure.
   (define-key facemenu-menu [update]    (cons "Update Menu" 'facemenu-update))
-  (define-key facemenu-menu [display]   (cons "Display" 'list-faces-display))
+  (define-key facemenu-menu [display]   (cons "Display Faces" 
+					      'list-faces-display))
   (define-key facemenu-menu [sep1]      (list "-------------"))
   (define-key facemenu-menu [remove]    (cons "Remove Properties"
 					      'facemenu-remove-all))
   (define-key facemenu-menu [read-only] (cons "Read-Only"
 					      'facemenu-set-read-only))
   (define-key facemenu-menu [invisible] (cons "Invisible"
-					  'facemenu-set-invisible))
+					      'facemenu-set-invisible))
   (define-key facemenu-menu [sep2]      (list "-------------"))
+  (define-key facemenu-menu [bg]        (cons "Background Color"
+					      'facemenu-background))
+  (define-key facemenu-menu [fg]        (cons "Foreground Color"
+					      'facemenu-foreground))
+  (define-key facemenu-menu [sep3]      (list "-------------"))
   (define-key facemenu-menu [other]     (cons "Other..." 'facemenu-set-face))
 
+  (define-key 'facemenu-foreground "o" (cons "Other" 'facemenu-set-foreground))
+  (define-key 'facemenu-background "o" (cons "Other" 'facemenu-set-background))
+
   ;; Define commands for face-changing
   (facemenu-iterate
-   (function 
-    (lambda (f)
-      (let ((face (car f))
-	    (name (symbol-name (car f)))
-	    (key  (cdr f)))
-	(cond ((memq face facemenu-unlisted-faces)
-	       nil)
-	      ((null key) (define-key facemenu-menu (vector face) 
-			     (cons name 'facemenu-set-face-from-menu)))
-	      (t (let ((function (intern (concat "facemenu-set-" name))))
-		   (fset function
-			 (` (lambda () (interactive)
-			      (facemenu-set-face (quote (, face))))))
-		   (define-key facemenu-keymap key (cons name function))
-		   (define-key facemenu-menu key (cons name function))))))
-      nil))
+   (lambda (f)
+     (let* ((face (car f))
+	    (name (symbol-name face))
+	    (key  (cdr f))
+	    (menu (cond ((string-match "^fg:" name) 'facemenu-foreground)
+			((string-match "^bg:" name) 'facemenu-background)
+			(t facemenu-menu))))
+       (if (memq menu '(facemenu-foreground facemenu-background))
+	   (setq name (substring name 3)))
+       (cond ((memq face facemenu-unlisted-faces)
+	      nil)
+	     ((null key) (define-key menu (vector face) 
+			   (cons name 'facemenu-set-face-from-menu)))
+	     (t (let ((function (intern (concat "facemenu-set-" name))))
+		  (fset function
+			(` (lambda () (interactive)
+			     (facemenu-set-face (quote (, face))))))
+		  (define-key facemenu-keymap key (cons name function))
+		  (define-key menu key (cons name function))))))
+     nil)
    (facemenu-complete-face-list facemenu-keybindings))
 
   (define-key global-map (vector 'menu-bar 'Face) 
@@ -176,20 +195,60 @@
 ;   s)
 
 ;;;###autoload
+(defun facemenu-read-color (prompt)
+  "Read a color using the minibuffer."
+  (let ((col (completing-read (or  "Color: ") facemenu-colors nil t)))
+    (if (equal "" col)
+	nil
+      col)))
+
+;;;###autoload
 (defun facemenu-set-face (face &optional start end)
-  "Set the face of the region or next character typed.
-The face to be used is prompted for.  
-If the region is active, it will be set to the requested face.  If
+  "Add FACE to the region or next character typed.
+It will be added to the top of the face list; any faces lower on the list that
+will not show through at all will be removed.
+
+Interactively, the face to be used is prompted for.
+If the region is active, it will be set to the requested face.  If 
 it is inactive \(even if mark-even-if-inactive is set) the next
 character that is typed \(via `self-insert-command') will be set to
 the the selected face.  Moving point or switching buffers before
 typing a character cancels the request." 
   (interactive (list (read-face-name "Use face: ")))
   (if mark-active
-      (put-text-property (or start (region-beginning))
-			 (or end (region-end))
-			 'face face)
-    (setq facemenu-next face facemenu-loc (point))))
+      (let ((start (or start (region-beginning)))
+	    (end (or end (region-end))))
+	(facemenu-add-face face start end))
+    (setq facemenu-next face
+	  facemenu-loc (point))))
+
+(defun facemenu-set-foreground (color &optional start end)
+  "Set the foreground color of the region or next character typed.
+The color is prompted for.  A face named `fg:color' is used \(or created).
+If the region is active, it will be set to the requested face.  If
+it is inactive \(even if mark-even-if-inactive is set) the next
+character that is typed \(via `self-insert-command') will be set to
+the the selected face.  Moving point or switching buffers before
+typing a character cancels the request." 
+  (interactive (list (facemenu-read-color "Foreground color: ")))
+  (let ((face (intern (concat "fg:" color))))
+    (or (facemenu-get-face face)
+	(error "Unknown color: %s" color))
+    (facemenu-set-face face start end)))
+
+(defun facemenu-set-background (color &optional start end)
+  "Set the background color of the region or next character typed.
+The color is prompted for.  A face named `bg:color' is used \(or created).
+If the region is active, it will be set to the requested face.  If
+it is inactive \(even if mark-even-if-inactive is set) the next
+character that is typed \(via `self-insert-command') will be set to
+the the selected face.  Moving point or switching buffers before
+typing a character cancels the request." 
+  (interactive (list (facemenu-read-color "Background color: ")))
+  (let ((face (intern (concat "bg:" color))))
+    (or (facemenu-get-face face)
+	(error "Unknown color: %s" color))
+    (facemenu-set-face face start end)))
 
 (defun facemenu-set-face-from-menu (face start end)
   "Set the face of the region or next character typed.
@@ -200,12 +259,12 @@
 character that is typed \(via `self-insert-command') will be set to
 the the selected face.  Moving point or switching buffers before
 typing a character cancels the request." 
-  (interactive (let ((keys (this-command-keys)))
-		 (list (elt keys (1- (length keys)))
-		       (if mark-active (region-beginning))
-		       (if mark-active (region-end)))))
+  (interactive (list last-command-event
+		     (if mark-active (region-beginning))
+		     (if mark-active (region-end))))
+  (facemenu-get-face face)
   (if start 
-      (put-text-property start end 'face face)
+      (facemenu-add-face face start end)
     (setq facemenu-next face facemenu-loc (point))))
 
 (defun facemenu-set-invisible (start end)
@@ -237,6 +296,32 @@
      start end '(face nil invisible nil intangible nil 
 		      read-only nil category nil))))
 
+(defun facemenu-get-face (face)
+  "Make sure FACE exists.
+If not, it is created.  If it is created and is of the form `fg:color', then
+set the foreground to that color. If of the form `bg:color', set the
+background.  In any case, add it to the appropriate menu.  Returns nil if
+given a bad color."
+  (if (internal-find-face face)
+      t
+    (make-face face)
+    (let* ((name (symbol-name face))
+	   (color (substring name 3)))
+      (cond ((string-match "^fg:" name)
+	     (set-face-foreground face color)
+	     (define-key 'facemenu-foreground (vector face) 
+	       (cons color 'facemenu-set-face-from-menu))
+	     (x-color-defined-p color))
+	    ((string-match "^bg:" name)
+	     (set-face-background face color)
+	     (define-key 'facemenu-background (vector face) 
+	       (cons color 'facemenu-set-face-from-menu))
+	     (x-color-defined-p color))
+	    (t
+	     (define-key facemenu-menu (vector face)
+	       (cons name 'facemenu-set-face-from-menu))
+	     t)))))
+
 (defun facemenu-after-change (begin end old-length)
   "May set the face of just-inserted text to user's request.
 This only happens if the change is an insertion, and
@@ -246,10 +331,9 @@
       nil
     (if (and (= 0 old-length)		; insertion
 	     (= facemenu-loc begin))	; point wasn't moved in between
-	(put-text-property begin end 'face facemenu-next))
+	(facemenu-add-face facemenu-next begin end))
     (setq facemenu-next nil)))
 
-
 (defun facemenu-complete-face-list (&optional oldlist)
   "Return alist of all faces that are look different.
 Starts with given LIST of faces, and adds elements only if they display 
@@ -276,6 +360,47 @@
      (nreverse (face-list)))
     list))
 
+(defun facemenu-add-face (face start end)
+  "Add FACE to text between START and END.
+For each section of that region that has a different face property, FACE will
+be consed onto it, and other faces that are completely hidden by that will be
+removed from the list."
+  (interactive "*xFace:\nr")
+  (let ((part-start start) part-end)
+    (while (not (= part-start end))
+      (setq part-end (next-single-property-change part-start 'face nil end))
+      (let ((prev (get-text-property part-start 'face)))
+	(put-text-property part-start part-end 'face
+			   (if (null prev)
+			       face
+			     (facemenu-discard-redundant-faces
+			      (cons face
+				    (if (listp prev) prev (list prev)))))))
+      (setq part-start part-end))))
+
+(defun facemenu-discard-redundant-faces (face-list &optional mask)
+  "Remove from FACE-LIST any faces that won't show at all.
+This means they have no non-nil elements that aren't also non-nil in an
+earlier face."
+  (let ((useful nil))
+    (cond ((null face-list) nil)
+	  ((null mask)
+	   (cons (car face-list)
+		 (facemenu-discard-redundant-faces
+		  (cdr face-list) 
+		  (copy-sequence (internal-get-face (car face-list))))))
+	  ((let ((i (length mask))
+		 (face (internal-get-face (car face-list))))
+	     (while (>= (setq i (1- i)) 0)
+	       (if (and (aref face i)
+			(not (aref mask i)))
+		   (progn (setq useful t)
+			  (aset mask i t))))
+	     useful)
+	   (cons (car face-list)
+		 (facemenu-discard-redundant-faces (cdr face-list) mask)))
+	  (t (facemenu-discard-redundant-faces (cdr face-list) mask)))))
+
 (defun facemenu-iterate (func iterate-list)
   "Apply FUNC to each element of LIST until one returns non-nil.
 Returns the non-nil value it found, or nil if all were nil."
@@ -288,4 +413,3 @@
 (add-hook 'after-change-functions 'facemenu-after-change)
 
 ;;; facemenu.el ends here
-