Mercurial > emacs
changeset 8743:03445a867bed
Initial revision
author | Richard M. Stallman <rms@gnu.org> |
---|---|
date | Wed, 14 Sep 1994 19:32:18 +0000 |
parents | a82055863414 |
children | e1b824af0849 |
files | lisp/facemenu.el |
diffstat | 1 files changed, 288 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/facemenu.el Wed Sep 14 19:32:18 1994 +0000 @@ -0,0 +1,288 @@ +;;; facemenu.el -- Create a face menu for interactively adding fonts to text +;; Copyright (c) 1994 Free Software Foundation, Inc. + +;; Author: Boris Goldowsky <boris@cs.rochester.edu> +;; Keywords: faces + +;; This file is part of GNU Emacs. + +;; 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, or (at your option) +;; any later version. + +;; 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 GNU Emacs; see the file COPYING. If not, write to +;; 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. + +;;; Installation: +;; Put this file somewhere on emacs's load-path, and put +;; (require 'facemenu) +;; 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. +;; +;; Faces can be selected from the keyboard as well. +;; The standard keybindings are M-s (or ESC s) + letter: +;; M-s i = "set italic", M-s b = "set bold", etc. + +;;; Customization: +;; An alternative set of keybindings that may be easier to type can be set up +;; using "Hyper" keys. This requires that you set up a hyper-key on your +;; keyboard. On my system, putting the following command in my .xinitrc: +;; xmodmap -e "keysym Alt_L = Hyper_L" -e "add Mod2 = Hyper_L" +;; makes the key labelled "Alt" act as a hyper key, but check with local +;; X-perts for how to do it on your system. If you do this, then put the +;; following in your .emacs before the (require 'facemenu): +;; (setq facemenu-keybindings +;; '((default . [?\H-d]) +;; (bold . [?\H-b]) +;; (italic . [?\H-i]) +;; (bold-italic . [?\H-o]) +;; (underline . [?\H-u]))) +;; (setq facemenu-keymap global-map) +;; (setq facemenu-key nil) +;; +;; In general, the order of the faces that appear in the menu and their +;; keybindings can be controlled by setting the variable +;; `facemenu-keybindings'. Faces that you never want to add to your +;; 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. +;; +;; `list-faces-display' shows the faces in a different order than +;; this menu, which could be confusing. I do /not/ sort the list +;; alphabetically, because I like the default order: it puts the most +;; basic, common fonts first. +;; +;; Please send me any other problems, comments or ideas. + +;;; Code: + +(provide 'facemenu) + +(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") + (italic . "i") + (bold-italic . "o") ; O for "Oblique" or "bOld"... + (underline . "u")) + "Alist of interesting faces and keybindings. +Each element is itself a list: the car is the name of the face, +the next element is the key to use as a keyboard equivalent of the menu item; +the binding is made in 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, +but get no keyboard equivalents. + +If you change this variable after loading facemenu.el, you will need to call +`facemenu-update' to make it take effect.") + +(defvar facemenu-unlisted-faces + '(modeline region secondary-selection highlight scratch-face) + "Faces that are not included in the Face menu. +Set this before loading facemenu.el, or call `facemenu-update' after +changing it.") + +(defvar facemenu-next nil) ; set when we are going to set a face on next char. +(defvar facemenu-loc nil) + +(defun facemenu-update () + "Add or update the \"Face\" menu in the menu bar." + (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 + (define-key facemenu-menu [update] '("Update Menu" . facemenu-update)) + (define-key facemenu-menu [display] '("Display" . list-faces-display)) + (define-key facemenu-menu [sep1] '("-------------")) + (define-key facemenu-menu [remove] '("Remove Properties" . + facemenu-remove-all)) + (define-key facemenu-menu [read-only] '("Read-Only". facemenu-set-read-only)) + (define-key facemenu-menu [invisible] '("Invisible" + . facemenu-set-invisible)) + (define-key facemenu-menu [sep2] '("---Special---")) + (define-key facemenu-menu [other] '("Other..." . facemenu-set-face)) + + ;; 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)) + (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) + +;;;###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 +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)))) + +(defun facemenu-set-face-from-menu (face start end) + "Set the face of the region or next character typed. +This function is designed to be called from a menu; the face to use +is the menu item's name. +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 (let ((keys (this-command-keys))) + (list (elt keys (1- (length keys))) + (if mark-active (region-beginning)) + (if mark-active (region-end))))) + (if start + (put-text-property start end 'face face) + (setq facemenu-next face facemenu-loc (point)))) + +(defun facemenu-set-invisible (start end) + "Make the region invisible. +This sets the `invisible' text property; it can be undone with +`facemenu-remove-all'." + (interactive "r") + (put-text-property start end 'invisible t)) + +(defun facemenu-set-intangible (start end) + "Make the region intangible: disallow moving into it. +This sets the `intangible' text property; it can be undone with +`facemenu-remove-all'." + (interactive "r") + (put-text-property start end 'intangible t)) + +(defun facemenu-set-read-only (start end) + "Make the region unmodifiable. +This sets the `read-only' text property; it can be undone with +`facemenu-remove-all'." + (interactive "r") + (put-text-property start end 'read-only t)) + +(defun facemenu-remove-all (start end) + "Remove all text properties that facemenu added to region." + (interactive "*r") ; error if buffer is read-only despite the next line. + (let ((inhibit-read-only t)) + (remove-text-properties + start end '(face nil invisible nil intangible nil + read-only nil category 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 + (put-text-property begin end 'face facemenu-next)) + (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)) + +(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." + (while (and iterate-list (not (funcall func (car iterate-list)))) + (setq iterate-list (cdr iterate-list))) + (car iterate-list)) + +(facemenu-update) +(add-hook 'menu-bar-final-items 'Face) +(add-hook 'after-change-functions 'facemenu-after-change) + +;;; facemenu.el ends here +