summaryrefslogtreecommitdiff
path: root/lisp/facemenu.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/facemenu.el')
-rw-r--r--lisp/facemenu.el343
1 files changed, 172 insertions, 171 deletions
diff --git a/lisp/facemenu.el b/lisp/facemenu.el
index f520ed43490..e5e2ba81001 100644
--- a/lisp/facemenu.el
+++ b/lisp/facemenu.el
@@ -21,9 +21,14 @@
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
;;; Commentary:
-;; This file defines a menu of faces (bold, italic, etc) which
-;; allows you to set the face used for a region of the buffer.
-;; Some faces also have keybindings, which are shown in the menu.
+;; This file defines a menu of faces (bold, italic, etc) which allows you to
+;; set the face used for a region of the buffer. Some faces also have
+;; keybindings, which are shown in the menu. Faces with names beginning with
+;; "fg:" or "bg:", as in "fg:red", are treated specially. It is assumed that
+;; Such faces are assumed to consist only of a foreground (if "fg:") or
+;; background (if "bg:") color. They are thus put into the color submenus
+;; rather than the general Face submenu. Such faces can also be created on
+;; demand from the "Other..." menu items.
;;; Installation:
;; Put this file somewhere on emacs's load-path, and put
@@ -31,12 +36,11 @@
;; in your .emacs file.
;;; Usage:
-;; Selecting a face from the menu or typing the keyboard equivalent
-;; will change the region to use that face.
-;; If you use transient-mark-mode and the region is not active, the
-;; face will be remembered and used for the next insertion. It will
-;; be forgotten if you move point or make other modifications before
-;; inserting or typing anything.
+;; Selecting a face from the menu or typing the keyboard equivalent will
+;; change the region to use that face. If you use transient-mark-mode and the
+;; region is not active, the face will be remembered and used for the next
+;; insertion. It will be forgotten if you move point or make other
+;; modifications before inserting or typing anything.
;;
;; Faces can be selected from the keyboard as well.
;; The standard keybindings are M-s (or ESC s) + letter:
@@ -82,12 +86,6 @@
(defvar facemenu-key "\M-s"
"Prefix to use for facemenu commands.")
-(defvar facemenu-keymap nil
- "Map for keybindings of face commands.
-If nil, `facemenu-update' will create one.
-`Facemenu-update' also fills in the keymap according to the bindings
-requested in facemenu-keybindings.")
-
(defvar facemenu-keybindings
'((default . "d")
(bold . "b")
@@ -113,94 +111,71 @@ If you change this variable after loading facemenu.el, you will need to call
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-face-menu
+ (let ((map (make-sparse-keymap "Face")))
+ (define-key map [other] (cons "Other..." 'facemenu-set-face))
+ map)
+ "Menu keymap for faces.")
+
+(defvar facemenu-foreground-menu
+ (let ((map (make-sparse-keymap "Foreground Color")))
+ (define-key map "o" (cons "Other" 'facemenu-set-foreground))
+ map)
+ "Menu keymap for foreground colors.")
+
+(defvar facemenu-background-menu
+ (let ((map (make-sparse-keymap "Background Color")))
+ (define-key map "o" (cons "Other" 'facemenu-set-background))
+ map)
+ "Menu keymap for background colors")
+
+(defvar facemenu-special-menu
+ (let ((map (make-sparse-keymap "Special")))
+ (define-key map [read-only] (cons "Read-Only" 'facemenu-set-read-only))
+ (define-key map [invisible] (cons "Invisible" 'facemenu-set-invisible))
+ map)
+ "Menu keymap for non-face text-properties.")
+
+(defvar facemenu-menu
+ (let ((map (make-sparse-keymap "Face")))
+ (define-key map [display] (cons "Display Faces" 'list-faces-display))
+ (define-key map [remove] (cons "Remove Props" 'facemenu-remove-all))
+ (define-key map [sep1] (list "-----------------"))
+ (define-key map [special] (cons "Special Props" facemenu-special-menu))
+ (define-key map [bg] (cons "Background Color" facemenu-background-menu))
+ (define-key map [fg] (cons "Foreground Color" facemenu-foreground-menu))
+ (define-key map [face] (cons "Face" facemenu-face-menu))
+ map)
+ "Facemenu top-level menu keymap")
+
+(defvar facemenu-keymap (make-sparse-keymap "Set face")
+ "Map for keyboard face-changing commands.
+`Facemenu-update' fills in the keymap according to the bindings
+requested in facemenu-keybindings.")
+
+;;; Internal Variables
+
+(defvar facemenu-color-alist nil
+ ;; Don't initialize here; that doesn't work if preloaded.
+ "Alist of colors, used for completion.
+If null, `facemenu-read-color' will set it.")
(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."
+ "Add or update the \"Face\" menu in the menu bar.
+You can call this to update things if you change any of the menu configuration
+variables."
(interactive)
- ;; Set up keymaps
- (fset 'facemenu-menu (setq facemenu-menu (make-sparse-keymap "Face")))
- (if (null facemenu-keymap)
- (fset 'facemenu-keymap
- (setq facemenu-keymap (make-sparse-keymap "Set face"))))
- (if facemenu-key
- (define-key global-map facemenu-key facemenu-keymap))
-
- ;; Define basic keys
- ;; 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 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))
- (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
- (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)
- (cons "Face" facemenu-menu)))
-
-; We'd really like to name the menu items as follows,
-; but we can't since menu entries don't display text properties (yet?)
-; (let ((s (copy-sequence (symbol-name face))))
-; (put-text-property 0 (1- (length s))
-; 'face face s)
-; s)
+ ;; Global bindings:
+ (define-key global-map [C-down-mouse-3] facemenu-menu)
+ (if facemenu-key (define-key global-map facemenu-key facemenu-keymap))
-;;;###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)))
+ ;; Add each defined face to the menu.
+ (facemenu-iterate 'facemenu-add-new-face
+ (facemenu-complete-face-list facemenu-keybindings)))
;;;###autoload
(defun facemenu-set-face (face &optional start end)
@@ -222,6 +197,7 @@ typing a character cancels the request."
(setq facemenu-next face
facemenu-loc (point))))
+;;;###autoload
(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).
@@ -236,6 +212,7 @@ typing a character cancels the request."
(error "Unknown color: %s" color))
(facemenu-set-face face start end)))
+;;;###autoload
(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).
@@ -296,87 +273,41 @@ This sets the `read-only' text property; it can be undone with
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
-`facemenu-set-face[-from-menu]' was called with point at the
-beginning of the insertion."
- (if (null facemenu-next) ; exit immediately if no work
- nil
- (if (and (= 0 old-length) ; insertion
- (= facemenu-loc begin)) ; point wasn't moved in between
- (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
-differently from any face already on the list.
-The original LIST will end up at the end of the returned list, in reverse
-order. The elements added will have null cdrs."
- (let ((list nil))
- (facemenu-iterate
- (function
- (lambda (item)
- (if (internal-find-face (car item))
- (setq list (cons item list)))
- nil))
- oldlist)
- (facemenu-iterate
- (function
- (lambda (new-face)
- (if (not (facemenu-iterate
- (function
- (lambda (item) (face-equal (car item) new-face t)))
- list))
- (setq list (cons (cons new-face nil) list)))
- nil))
- (nreverse (face-list)))
- list))
+;;;###autoload
+(defun facemenu-read-color (prompt)
+ "Read a color using the minibuffer."
+ (let ((col (completing-read (or "Color: ")
+ (or facemenu-color-alist
+ (if (eq 'x window-system)
+ (mapcar 'list (x-defined-colors))))
+ nil t)))
+ (if (equal "" col)
+ nil
+ col)))
(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."
+removed from the list.
+
+As a special case, if FACE is `default', then the region is left with NO face
+text property. Otherwise, selecting the default face would not have any
+effect."
(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))))
+ (if (eq face 'default)
+ (remove-text-properties start end '(face default))
+ (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.
@@ -401,6 +332,77 @@ earlier face."
(facemenu-discard-redundant-faces (cdr face-list) mask)))
(t (facemenu-discard-redundant-faces (cdr face-list) mask)))))
+(defun facemenu-get-face (symbol)
+ "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."
+ (or (internal-find-face symbol)
+ (let* ((face (make-face symbol))
+ (name (symbol-name symbol))
+ (color (substring name 3)))
+ (cond ((string-match "^fg:" name)
+ (set-face-foreground face color)
+ (and (eq 'x window-system) (x-color-defined-p color)))
+ ((string-match "^bg:" name)
+ (set-face-background face color)
+ (and (eq 'x window-system) (x-color-defined-p color)))
+ (t)))))
+
+(defun facemenu-add-new-face (face)
+ "Add a FACE to the appropriate Face menu.
+Automatically called when a new face is created."
+ (let* ((name (symbol-name face))
+ (menu (cond ((string-match "^fg:" name)
+ (setq name (substring name 3))
+ facemenu-foreground-menu)
+ ((string-match "^bg:" name)
+ (setq name (substring name 3))
+ facemenu-background-menu)
+ (t facemenu-face-menu)))
+ key)
+ (cond ((memq face facemenu-unlisted-faces)
+ nil)
+ ((setq key (cdr (assoc face facemenu-keybindings)))
+ (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))))
+ (t (define-key menu (vector face)
+ (cons name 'facemenu-set-face-from-menu)))))
+ ;; Return nil for facemenu-iterate's benefit:
+ nil)
+
+(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
+`facemenu-set-face[-from-menu]' was called with point at the
+beginning of the insertion."
+ (if (null facemenu-next) ; exit immediately if no work
+ nil
+ (if (and (= 0 old-length) ; insertion
+ (= facemenu-loc begin)) ; point wasn't moved in between
+ (facemenu-add-face facemenu-next begin end))
+ (setq facemenu-next nil)))
+
+(defun facemenu-complete-face-list (&optional oldlist)
+ "Return list of all faces that are look different.
+Starts with given ALIST of faces, and adds elements only if they display
+differently from any face already on the list.
+The faces on ALIST will end up at the end of the returned list, in reverse
+order."
+ (let ((list (nreverse (mapcar 'car oldlist))))
+ (facemenu-iterate
+ (lambda (new-face)
+ (if (not (memq new-face list))
+ (setq list (cons new-face list)))
+ nil)
+ (nreverse (face-list)))
+ list))
+
(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."
@@ -409,7 +411,6 @@ Returns the non-nil value it found, or nil if all were nil."
(car iterate-list))
(facemenu-update)
-(add-hook 'menu-bar-final-items 'Face)
(add-hook 'after-change-functions 'facemenu-after-change)
;;; facemenu.el ends here