;; 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.
+;; Faces can be selected from the keyboard as well.
;; The standard keybindings are M-g (or ESC g) + letter:
;; M-g i = "set italic", M-g b = "set bold", etc.
;;; Code:
-(provide 'facemenu)
-
-(eval-when-compile
+(eval-when-compile
(require 'help)
(require 'button))
-(require 'wid-edit)
;;; Provide some binding for startup:
;;;###autoload (define-key global-map "\M-g" 'facemenu-keymap)
;;;###autoload (autoload 'facemenu-keymap "facemenu" "Keymap for face-changing commands." t 'keymap)
-
+
;; Global bindings:
(define-key global-map [C-down-mouse-2] 'facemenu-menu)
(define-key global-map "\M-g" 'facemenu-keymap)
The faces specifically mentioned in this list are put at the top of
the menu, in the order specified. All other faces which are defined,
-except for those in `facemenu-unlisted-faces', are listed after them,
+except for those in `facemenu-unlisted-faces', are listed after them,
but get no keyboard equivalents.
If you change this variable after loading facemenu.el, you will need to call
(defalias 'facemenu-face-menu facemenu-face-menu)
;;;###autoload
-(defvar facemenu-foreground-menu
+(defvar facemenu-foreground-menu
(let ((map (make-sparse-keymap "Foreground Color")))
(define-key map "o" (cons "Other..." 'facemenu-set-foreground))
map)
(defalias 'facemenu-background-menu facemenu-background-menu)
;;;###autoload
-(defvar facemenu-special-menu
+(defvar facemenu-special-menu
(let ((map (make-sparse-keymap "Special")))
(define-key map [?s] (cons (purecopy "Remove Special")
'facemenu-remove-special))
;;;###autoload
(defvar facemenu-indentation-menu
(let ((map (make-sparse-keymap "Indentation")))
- (define-key map [decrease-right-margin]
+ (define-key map [decrease-right-margin]
(cons (purecopy "Indent Right Less") 'decrease-right-margin))
(define-key map [increase-right-margin]
(cons (purecopy "Indent Right More") 'increase-right-margin))
(let ((map facemenu-menu))
(define-key map [dc] (cons (purecopy "Display Colors") 'list-colors-display))
(define-key map [df] (cons (purecopy "Display Faces") 'list-faces-display))
- (define-key map [dp] (cons (purecopy "Describe Text")
- 'describe-text-at))
+ (define-key map [dp] (cons (purecopy "Describe Properties")
+ 'describe-text-properties))
(define-key map [ra] (cons (purecopy "Remove Text Properties")
'facemenu-remove-all))
(define-key map [rm] (cons (purecopy "Remove Face Properties")
(define-key map [s1] (list (purecopy "--"))))
;;;###autoload
(let ((map facemenu-menu))
- (define-key map [in] (cons (purecopy "Indentation")
+ (define-key map [in] (cons (purecopy "Indentation")
'facemenu-indentation-menu))
(define-key map [ju] (cons (purecopy "Justification")
'facemenu-justification-menu))
(define-key map [s2] (list (purecopy "--")))
- (define-key map [sp] (cons (purecopy "Special Properties")
+ (define-key map [sp] (cons (purecopy "Special Properties")
'facemenu-special-menu))
- (define-key map [bg] (cons (purecopy "Background Color")
+ (define-key map [bg] (cons (purecopy "Background Color")
'facemenu-background-menu))
- (define-key map [fg] (cons (purecopy "Foreground Color")
+ (define-key map [fg] (cons (purecopy "Foreground Color")
'facemenu-foreground-menu))
- (define-key map [fc] (cons (purecopy "Face")
+ (define-key map [fc] (cons (purecopy "Face")
'facemenu-face-menu)))
;;;###autoload
(defalias 'facemenu-menu facemenu-menu)
-(defvar facemenu-keymap
+(defvar facemenu-keymap
(let ((map (make-sparse-keymap "Set face")))
(define-key map "o" (cons (purecopy "Other...") 'facemenu-set-face))
map)
Otherwise, this command specifies the face for the next character
inserted. Moving point or switching buffers before
-typing a character to insert cancels the specification."
+typing a character to insert cancels the specification."
(interactive (list (progn
(barf-if-buffer-read-only)
(read-face-name "Use face"))
Otherwise, this command specifies the face for the next character
inserted. Moving point or switching buffers before
-typing a character to insert cancels the specification."
+typing a character to insert cancels the specification."
(interactive (list (progn
(barf-if-buffer-read-only)
(facemenu-read-color "Foreground color: "))
(region-end))))
(unless (color-defined-p color)
(message "Color `%s' undefined" color))
- (facemenu-add-new-face color 'facemenu-foreground-menu)
+ (facemenu-add-new-color color 'facemenu-foreground-menu)
(facemenu-add-face (list (list :foreground color)) start end))
;;;###autoload
Otherwise, this command specifies the face for the next character
inserted. Moving point or switching buffers before
-typing a character to insert cancels the specification."
+typing a character to insert cancels the specification."
(interactive (list (progn
(barf-if-buffer-read-only)
(facemenu-read-color "Background color: "))
(region-end))))
(unless (color-defined-p color)
(message "Color `%s' undefined" color))
- (facemenu-add-new-face color 'facemenu-background-menu)
+ (facemenu-add-new-color color 'facemenu-background-menu)
(facemenu-add-face (list (list :background color)) start end))
;;;###autoload
Otherwise, this command specifies the face for the next character
inserted. Moving point or switching buffers before
-typing a character to insert cancels the specification."
+typing a character to insert cancels the specification."
(interactive (list last-command-event
(if (and mark-active (not current-prefix-arg))
(region-beginning))
(region-end))))
(barf-if-buffer-read-only)
(facemenu-get-face face)
- (if start
+ (if start
(facemenu-add-face face start end)
(facemenu-add-face face)))
"Remove `face' and `mouse-face' text properties."
(interactive "*r") ; error if buffer is read-only despite the next line.
(let ((inhibit-read-only t))
- (remove-text-properties
+ (remove-text-properties
start end '(face nil mouse-face nil))))
;;;###autoload
These special properties include `invisible', `intangible' and `read-only'."
(interactive "*r") ; error if buffer is read-only despite the next line.
(let ((inhibit-read-only t))
- (remove-text-properties
+ (remove-text-properties
start end '(invisible nil intangible nil read-only nil))))
-
-;;; Describe-Text Mode.
-
-(defun describe-text-done ()
- "Delete the current window or bury the current buffer."
- (interactive)
- (if (> (count-windows) 1)
- (delete-window)
- (bury-buffer)))
-
-(defvar describe-text-mode-map
- (let ((map (make-sparse-keymap)))
- (set-keymap-parent map widget-keymap)
- map)
- "Keymap for `describe-text-mode'.")
-
-(defcustom describe-text-mode-hook nil
- "List of hook functions ran by `describe-text-mode'."
- :type 'hook)
-
-(defun describe-text-mode ()
- "Major mode for buffers created by `describe-text-at'.
-
-\\{describe-text-mode-map}
-Entry to this mode calls the value of `describe-text-mode-hook'
-if that value is non-nil."
- (kill-all-local-variables)
- (setq major-mode 'describe-text-mode
- mode-name "Describe-Text")
- (use-local-map describe-text-mode-map)
- (widget-setup)
- (run-hooks 'describe-text-mode-hook))
-
-;;; Describe-Text Utilities.
-
-(defun describe-text-widget (widget)
- "Insert text to describe WIDGET in the current buffer."
- (widget-create 'link
- :notify `(lambda (&rest ignore)
- (widget-browse ',widget))
- (format "%S" (if (symbolp widget)
- widget
- (car widget))))
- (widget-insert " ")
- (widget-create 'info-link :tag "widget" "(widget)Top"))
-
-(defun describe-text-sexp (sexp)
- "Insert a short description of SEXP in the current buffer."
- (let ((pp (condition-case signal
- (pp-to-string sexp)
- (error (prin1-to-string signal)))))
- (when (string-match "\n\\'" pp)
- (setq pp (substring pp 0 (1- (length pp)))))
- (if (cond ((string-match "\n" pp)
- nil)
- ((> (length pp) (- (window-width) (current-column)))
- nil)
- (t t))
- (widget-insert pp)
- (widget-create 'push-button
- :tag "show"
- :action (lambda (widget &optional event)
- (with-output-to-temp-buffer
- "*Pp Eval Output*"
- (princ (widget-get widget :value))))
- pp))))
-
-
-(defun describe-text-properties (properties)
- "Insert a description of PROPERTIES in the current buffer.
-PROPERTIES should be a list of overlay or text properties.
-The `category' property is made into a widget button that call
-`describe-text-category' when pushed."
- (while properties
- (widget-insert (format " %-20s " (car properties)))
- (let ((key (nth 0 properties))
- (value (nth 1 properties)))
- (cond ((eq key 'category)
- (widget-create 'link
- :notify `(lambda (&rest ignore)
- (describe-text-category ',value))
- (format "%S" value)))
- ((widgetp value)
- (describe-text-widget value))
- (t
- (describe-text-sexp value))))
- (widget-insert "\n")
- (setq properties (cdr (cdr properties)))))
-
-;;; Describe-Text Commands.
-
-(defun describe-text-category (category)
- "Describe a text property category."
- (interactive "S")
- (when (get-buffer "*Text Category*")
- (kill-buffer "*Text Category*"))
- (save-excursion
- (with-output-to-temp-buffer "*Text Category*"
- (set-buffer "*Text Category*")
- (widget-insert "Category " (format "%S" category) ":\n\n")
- (describe-text-properties (symbol-plist category))
- (describe-text-mode)
- (goto-char (point-min)))))
-
-;;;###autoload
-(defun describe-text-at (pos)
- "Describe widgets, buttons, overlays and text properties at POS."
- (interactive "d")
- (when (eq (current-buffer) (get-buffer "*Text Description*"))
- (error "Can't do self inspection"))
- (let* ((properties (text-properties-at pos))
- (overlays (overlays-at pos))
- overlay
- (wid-field (get-char-property pos 'field))
- (wid-button (get-char-property pos 'button))
- (wid-doc (get-char-property pos 'widget-doc))
- ;; If button.el is not loaded, we have no buttons in the text.
- (button (and (fboundp 'button-at) (button-at pos)))
- (button-type (and button (button-type button)))
- (button-label (and button (button-label button)))
- (widget (or wid-field wid-button wid-doc)))
- (if (not (or properties overlays))
- (message "This is plain text.")
- (when (get-buffer "*Text Description*")
- (kill-buffer "*Text Description*"))
- (save-excursion
- (with-output-to-temp-buffer "*Text Description*"
- (set-buffer "*Text Description*")
- (widget-insert "Text content at position " (format "%d" pos) ":\n\n")
- ;; Widgets
- (when (widgetp widget)
- (widget-insert (cond (wid-field "This is an editable text area")
- (wid-button "This is an active area")
- (wid-doc "This is documentation text")))
- (widget-insert " of a ")
- (describe-text-widget widget)
- (widget-insert ".\n\n"))
- ;; Buttons
- (when (and button (not (widgetp wid-button)))
- (widget-insert "Here is a " (format "%S" button-type)
- " button labeled `" button-label "'.\n\n"))
- ;; Overlays
- (when overlays
- (if (eq (length overlays) 1)
- (widget-insert "There is an overlay here:\n")
- (widget-insert "There are " (format "%d" (length overlays))
- " overlays here:\n"))
- (dolist (overlay overlays)
- (widget-insert " From " (format "%d" (overlay-start overlay))
- " to " (format "%d" (overlay-end overlay)) "\n")
- (describe-text-properties (overlay-properties overlay)))
- (widget-insert "\n"))
- ;; Text properties
- (when properties
- (widget-insert "There are text properties here:\n")
- (describe-text-properties properties))
- (describe-text-mode)
- (goto-char (point-min)))))))
-
-;;; List Text Properties
-
-;;;###autoload
-(defun list-text-properties-at (p)
- "Pop up a buffer listing text-properties at LOCATION."
- (interactive "d")
- (let ((props (text-properties-at p))
- category
- str)
- (if (null props)
- (message "None")
- (if (and (not (cdr (cdr props)))
- (not (eq (car props) 'category))
- (< (length (setq str (format "Text property at %d: %s %S"
- p (car props) (car (cdr props)))))
- (frame-width)))
- (message "%s" str)
- (with-output-to-temp-buffer "*Text Properties*"
- (princ (format "Text properties at %d:\n\n" p))
- (setq help-xref-stack nil)
- (while props
- (if (eq (car props) 'category)
- (setq category (car (cdr props))))
- (princ (format "%-20s %S\n"
- (car props) (car (cdr props))))
- (setq props (cdr (cdr props))))
- (if category
- (progn
- (setq props (symbol-plist category))
- (princ (format "\nCategory %s:\n\n" category))
- (while props
- (princ (format "%-20s %S\n"
- (car props) (car (cdr props))))
- (if (eq (car props) 'category)
- (setq category (car (cdr props))))
- (setq props (cdr (cdr props)))))))))))
-
+\f
;;;###autoload
(defun facemenu-read-color (&optional prompt)
"Read a color using the minibuffer."
- (let ((col (completing-read (or prompt "Color: ")
+ (let ((col (completing-read (or prompt "Color: ")
(or facemenu-color-alist
- (mapcar 'list (defined-colors)))
+ (defined-colors))
nil t)))
(if (equal "" col)
nil
col)))
;;;###autoload
-(defun list-colors-display (&optional list)
+(defun list-colors-display (&optional list buffer-name)
"Display names of defined colors, and show what they look like.
If the optional argument LIST is non-nil, it should be a list of
-colors to display. Otherwise, this command computes a list
-of colors that the current display can handle."
+colors to display. Otherwise, this command computes a list of
+colors that the current display can handle. If the optional
+argument BUFFER-NAME is nil, it defaults to *Colors*."
(interactive)
(when (and (null list) (> (display-color-cells) 0))
- (setq list (defined-colors))
- ;; Delete duplicate colors.
- (let ((l list))
- (while (cdr l)
- (if (facemenu-color-equal (car l) (car (cdr l)))
- (setcdr l (cdr (cdr l)))
- (setq l (cdr l)))))
- ;; Don't show more than what the display can handle.
- (let ((lc (nthcdr (1- (display-color-cells)) list)))
- (if lc
- (setcdr lc nil))))
- (with-output-to-temp-buffer "*Colors*"
+ (setq list (list-colors-duplicates (defined-colors)))
+ (when (memq (display-visual-class) '(gray-scale pseudo-color direct-color))
+ ;; Don't show more than what the display can handle.
+ (let ((lc (nthcdr (1- (display-color-cells)) list)))
+ (if lc
+ (setcdr lc nil)))))
+ (with-output-to-temp-buffer (or buffer-name "*Colors*")
(save-excursion
(set-buffer standard-output)
- (let (s)
- (while list
- (setq s (point))
- (insert (car list))
- (indent-to 20)
- (put-text-property s (point) 'face
- (cons 'background-color (car list)))
- (setq s (point))
- (insert " " (car list) "\n")
- (put-text-property s (point) 'face
- (cons 'foreground-color (car list)))
- (setq list (cdr list)))))))
+ (setq truncate-lines t)
+ (if temp-buffer-show-function
+ (list-colors-print list)
+ ;; Call list-colors-print from temp-buffer-show-hook
+ ;; to get the right value of window-width in list-colors-print
+ ;; after the buffer is displayed.
+ (add-hook 'temp-buffer-show-hook
+ (lambda () (list-colors-print list)) nil t)))))
+
+(defun list-colors-print (list)
+ (dolist (color list)
+ (if (consp color)
+ (if (cdr color)
+ (setq color (sort color (lambda (a b)
+ (string< (downcase a)
+ (downcase b))))))
+ (setq color (list color)))
+ (put-text-property
+ (prog1 (point)
+ (insert (car color))
+ (indent-to 22))
+ (point)
+ 'face (cons 'background-color (car color)))
+ (put-text-property
+ (prog1 (point)
+ (insert " " (if (cdr color)
+ (mapconcat 'identity (cdr color) ", ")
+ (car color)))
+ (indent-to (max (- (window-width) 8) 44))
+ (insert (apply 'format " #%02x%02x%02x"
+ (mapcar (lambda (c) (lsh c -8))
+ (color-values (car color)))))
+
+ (insert "\n"))
+ (point)
+ 'face (cons 'foreground-color (car color))))
+ (goto-char (point-min)))
+
+(defun list-colors-duplicates (&optional list)
+ "Return a list of colors with grouped duplicate colors.
+If a color has no duplicates, then the element of the returned list
+has the form '(COLOR-NAME). The element of the returned list with
+duplicate colors has the form '(COLOR-NAME DUPLICATE-COLOR-NAME ...).
+This function uses the predicate `facemenu-color-equal' to compare
+color names. If the optional argument LIST is non-nil, it should
+be a list of colors to display. Otherwise, this function uses
+a list of colors that the current display can handle."
+ (let* ((list (mapcar 'list (or list (defined-colors))))
+ (l list))
+ (while (cdr l)
+ (if (and (facemenu-color-equal (car (car l)) (car (car (cdr l))))
+ (not (and (boundp 'w32-default-color-map)
+ (not (assoc (car (car l)) w32-default-color-map)))))
+ (progn
+ (setcdr (car l) (cons (car (car (cdr l))) (cdr (car l))))
+ (setcdr l (cdr (cdr l))))
+ (setq l (cdr l))))
+ list))
(defun facemenu-color-equal (a b)
"Return t if colors A and B are the same color.
(check-face (car face-list)))))
(i mask-len)
(useful nil))
- (while (> (setq i (1- i)) 1)
+ (while (>= (setq i (1- i)) 0)
(and (not (memq (aref face-atts i) '(nil unspecified)))
(memq (aref mask-atts i) '(nil unspecified))
(aset mask-atts i (setq useful t))))
(t (make-face symbol))))
symbol)
-(defun facemenu-add-new-face (face-or-color &optional menu)
- "Add FACE-OR-COLOR (a face or a color) to the appropriate Face menu.
-If MENU is nil, then FACE-OR-COLOR is a face to be added
-to `facemenu-face-menu'. If MENU is `facemenu-foreground-menu'
-or `facemenu-background-menu', FACE-OR-COLOR is a color
-to be added to the specified menu.
+(defun facemenu-add-new-face (face)
+ "Add FACE (a face) to the Face menu.
This is called whenever you create a new face."
(let* (name
symbol
- docstring
- (key (cdr (assoc face-or-color facemenu-keybindings)))
+ menu docstring
+ (key (cdr (assoc face facemenu-keybindings)))
function menu-val)
- (if (symbolp face-or-color)
- (setq name (symbol-name face-or-color)
- symbol face-or-color)
- (setq name face-or-color
+ (if (symbolp face)
+ (setq name (symbol-name face)
+ symbol face)
+ (setq name face
symbol (intern name)))
- (cond ((eq menu 'facemenu-foreground-menu)
- (setq docstring
- (format "Select foreground color %s for subsequent insertion."
- name)))
- ((eq menu 'facemenu-background-menu)
- (setq docstring
- (format "Select background color %s for subsequent insertion."
- name)))
- (t
- (setq menu 'facemenu-face-menu)
- (setq docstring
- (format "Select face `%s' for subsequent insertion."
- name))))
+ (setq menu 'facemenu-face-menu)
+ (setq docstring
+ (format "Select face `%s' for subsequent insertion."
+ name))
(cond ((eq t facemenu-unlisted-faces))
((memq symbol facemenu-unlisted-faces))
;; test against regexps in facemenu-unlisted-faces
(define-key 'facemenu-keymap key (cons name function))
(define-key menu key (cons name function)))
((facemenu-iterate ; check if equivalent face is already in the menu
- (lambda (m) (and (listp m)
+ (lambda (m) (and (listp m)
(symbolp (car m))
(face-equal (car m) symbol)))
(cdr (symbol-function menu))))
(define-key menu key (cons name function))))))
nil) ; Return nil for facemenu-iterate
+(defun facemenu-add-new-color (color &optional menu)
+ "Add COLOR (a color name string) to the appropriate Face menu.
+MENU should be `facemenu-foreground-menu' or
+`facemenu-background-menu'.
+
+This is called whenever you use a new color."
+ (let* (name
+ symbol
+ docstring
+ function menu-val key
+ (color-p (memq menu '(facemenu-foreground-menu
+ facemenu-background-menu))))
+ (unless (stringp color)
+ (error "%s is not a color" color))
+ (setq name color
+ symbol (intern name))
+
+ (cond ((eq menu 'facemenu-foreground-menu)
+ (setq docstring
+ (format "Select foreground color %s for subsequent insertion."
+ name)))
+ ((eq menu 'facemenu-background-menu)
+ (setq docstring
+ (format "Select background color %s for subsequent insertion."
+ name))))
+ (cond ((facemenu-iterate ; check if equivalent face is already in the menu
+ (lambda (m) (and (listp m)
+ (symbolp (car m))
+ (stringp (cadr m))
+ (string-equal (cadr m) color)))
+ (cdr (symbol-function menu))))
+ (t ; No keyboard equivalent. Figure out where to put it:
+ (setq key (vector symbol)
+ function 'facemenu-set-face-from-menu
+ menu-val (symbol-function menu))
+ (if (and facemenu-new-faces-at-end
+ (> (length menu-val) 3))
+ (define-key-after menu-val key (cons name function)
+ (car (nth (- (length menu-val) 3) menu-val)))
+ (define-key menu key (cons name function))))))
+ nil) ; Return nil for facemenu-iterate
+
(defun facemenu-complete-face-list (&optional oldlist)
"Return list of all faces that look different.
-Starts with given ALIST of faces, and adds elements only if they display
+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
+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)
+ (facemenu-iterate
+ (lambda (new-face)
(if (not (memq new-face list))
(setq list (cons new-face list)))
nil)
(facemenu-update)
+(provide 'facemenu)
+
+;;; arch-tag: 85f6d02b-9085-420e-b651-0678f0e9c7eb
;;; facemenu.el ends here