313 lines
13 KiB
EmacsLisp
313 lines
13 KiB
EmacsLisp
|
;;; new-key-seq-widget.el --- New key-sequence widget for Emacs
|
||
|
;;
|
||
|
;; Author: Lennart Borgman (lennart O borgman A gmail O com)
|
||
|
;; Created: Tue Dec 25 23:00:43 2007
|
||
|
;; Version:
|
||
|
;; Last-Updated:
|
||
|
;; URL:
|
||
|
;; Keywords:
|
||
|
;; Compatibility:
|
||
|
;;
|
||
|
;; Features that might be required by this library:
|
||
|
;;
|
||
|
;; None
|
||
|
;;
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;;
|
||
|
;;; Commentary:
|
||
|
;;
|
||
|
;; New version of Kim's Emacs key-sequence widget. For inclusion in
|
||
|
;; Emacs I hope.
|
||
|
;;
|
||
|
;; Fix-me: check what was included.
|
||
|
;;
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;;
|
||
|
;;; Change log:
|
||
|
;;
|
||
|
;; I do not know how much I have changed, but I keep it together here
|
||
|
;; for simplicity.
|
||
|
;;
|
||
|
;; Note: I have named made `widget-key-sequence-map' a constant for
|
||
|
;; the moment.
|
||
|
;;
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;;
|
||
|
;; This program 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.
|
||
|
;;
|
||
|
;; This program 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 this program; see the file COPYING. If not, write to
|
||
|
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
||
|
;; Floor, Boston, MA 02110-1301, USA.
|
||
|
;;
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;;
|
||
|
;;; Code:
|
||
|
|
||
|
(require 'wid-edit)
|
||
|
(require 'edmacro)
|
||
|
|
||
|
;;; I'm not sure about what this is good for? KFS.
|
||
|
;;
|
||
|
;;; This should probably be for customize-set-value etc, but it is not
|
||
|
;;; used. Or for the widget editing, but it is not used there
|
||
|
;;; either. /Lennart
|
||
|
(defvar widget-key-sequence-prompt-value-history nil
|
||
|
"History of input to `widget-key-sequence-prompt-value'.")
|
||
|
|
||
|
(defvar widget-key-sequence-default-value [ignore]
|
||
|
"Default value for an empty key sequence.")
|
||
|
|
||
|
(defconst widget-key-sequence-map
|
||
|
(let ((map (make-sparse-keymap)))
|
||
|
(set-keymap-parent map widget-field-keymap)
|
||
|
(define-key map [(control ?q)] 'widget-key-sequence-read-event)
|
||
|
(define-key map [(control ?t)] 'widget-key-sequence-toggle-input-format)
|
||
|
map))
|
||
|
|
||
|
(defvar widget-key-sequence-input-formats '(key-description vector))
|
||
|
|
||
|
(defcustom widget-key-sequence-default-input-format 'key-description
|
||
|
"Format used to edit key sequences.
|
||
|
This is the format shown and edited in a key-sequence widget."
|
||
|
:type '(choice (const :tag "Key description" 'key-description)
|
||
|
(const :tag "Vector" 'vector))
|
||
|
:group 'widgets)
|
||
|
|
||
|
(define-widget 'key-sequence 'restricted-sexp
|
||
|
"A key sequence."
|
||
|
:prompt-value 'widget-field-prompt-value
|
||
|
:prompt-internal 'widget-symbol-prompt-internal
|
||
|
; :prompt-match 'fboundp ;; What was this good for? KFS
|
||
|
:prompt-history 'widget-key-sequence-prompt-value-history
|
||
|
:action 'widget-field-action
|
||
|
:match-alternatives '(stringp vectorp)
|
||
|
:format "%{%t%}: %v"
|
||
|
:validate 'widget-key-sequence-validate
|
||
|
:value-to-internal 'widget-key-sequence-value-to-internal
|
||
|
:value-to-external 'widget-key-sequence-value-to-external
|
||
|
:value widget-key-sequence-default-value
|
||
|
:keymap widget-key-sequence-map
|
||
|
:help-echo "C-q: insert KEY, EVENT, or CODE; C-t: toggle format"
|
||
|
:tag "Key sequence")
|
||
|
|
||
|
|
||
|
;;; Leave these here for testing:
|
||
|
;; (edmacro-parse-keys "C-x h" t) => [24 104]
|
||
|
;; (key-description-to-vector "C-x h" ) => [(control 120) 104]
|
||
|
;; (key-description (key-description-to-vector "C-x h")) => "C-x h"
|
||
|
;; (key-description (edmacro-parse-keys "C-x h")) => "C-x h"
|
||
|
;; (key-description [M-mouse-1]) => <M-mouse-1>
|
||
|
;; (edmacro-parse-keys "<M-mouse-1>") => [M-mouse-1]
|
||
|
|
||
|
;; (event-modifiers 'mouse-1) => (click mouse-1 mouse-1 mouse-1 mouse-1 mouse-1)
|
||
|
;; (event-modifiers 'M-mouse-1) =>
|
||
|
;; (event-modifiers '(mouse-1)) => (click mouse-1 mouse-1 mouse-1 mouse-1 mouse-1)
|
||
|
;; (event-modifiers '(down-mouse-1)) => (click mouse-1 mouse-1 mouse-1 mouse-1 mouse-1)
|
||
|
;; (event-modifiers '(S-down-mouse-1)) => (shift down)
|
||
|
;; (event-modifiers 'S-down-mouse-1) => (shift down)
|
||
|
;; (event-modifiers 'down-mouse-1) => (click mouse-1 mouse-1 mouse-1 mouse-1 mouse-1)
|
||
|
;; (event-modifiers '(down-mouse-1)) => (click mouse-1 mouse-1 mouse-1 mouse-1 mouse-1)
|
||
|
;; (let ((m (make-sparse-keymap))) (define-key m [(down mouse-1)] 'hej))
|
||
|
(defun key-description-to-vector (kd)
|
||
|
"Convert human readable key description KD to vector format.
|
||
|
KD should be in the format returned by `key-description'."
|
||
|
(let ((v
|
||
|
(vconcat
|
||
|
(mapcar (lambda (k)
|
||
|
;; Fix-me: temporarily clean the event here:
|
||
|
(when (symbolp k)
|
||
|
(let ((esem (get k 'event-symbol-element-mask))) (when esem (lwarn t :warning "kd=%s, k=%s, esem=%s" kd k esem)))
|
||
|
(put k 'event-symbol-element-mask nil))
|
||
|
(let ((m (event-modifiers k))
|
||
|
(b (event-basic-type k)))
|
||
|
(setq m (delq 'click m))
|
||
|
(if m
|
||
|
(nconc m (list b))
|
||
|
b)))
|
||
|
;; fix-me: does not always work for menu and tool
|
||
|
;; bar event because they may contains spaces.
|
||
|
(edmacro-parse-keys kd t))))
|
||
|
(m (make-sparse-keymap))
|
||
|
)
|
||
|
;; Test before returning it:
|
||
|
(define-key m v 'test)
|
||
|
v))
|
||
|
|
||
|
(defun widget-key-sequence-current-input-format ()
|
||
|
(let ((fmt (or (widget-get (widget-at (point)) :key-sequence-format)
|
||
|
widget-key-sequence-default-input-format)))
|
||
|
fmt))
|
||
|
|
||
|
(defun widget-key-sequence-toggle-input-format ()
|
||
|
"Toggle key sequence input format."
|
||
|
(interactive)
|
||
|
(let* ((widget (widget-at (point)))
|
||
|
(value (widget-apply widget :value-get))
|
||
|
(first (string-to-char value))
|
||
|
(old-fmt
|
||
|
(let ((fmt (or (widget-get widget :key-sequence-format)
|
||
|
widget-key-sequence-default-input-format)))
|
||
|
fmt))
|
||
|
(new-fmt
|
||
|
(let ((m (cdr (memq old-fmt widget-key-sequence-input-formats))))
|
||
|
(if m (car m) (car widget-key-sequence-input-formats))))
|
||
|
(new-value
|
||
|
(cond
|
||
|
((eq new-fmt 'key-description)
|
||
|
(setq value (replace-regexp-in-string "\\` *\\(.*?\\) *\\'" "\\1" value))
|
||
|
(if (string= value "")
|
||
|
""
|
||
|
(key-description (read value))))
|
||
|
((eq new-fmt 'vector)
|
||
|
(format "%S" (key-description-to-vector value)))
|
||
|
(t
|
||
|
(error "Bad key seq format spec: %s" new-fmt))))
|
||
|
(state (widget-get (widget-get widget :parent) :custom-state))
|
||
|
)
|
||
|
(widget-put widget :key-sequence-format new-fmt)
|
||
|
(setq new-value (propertize new-value 'face 'highlight))
|
||
|
(widget-apply widget :value-set new-value)
|
||
|
(widget-setup)
|
||
|
(widget-put (widget-get widget :parent) :custom-state state)
|
||
|
(cond
|
||
|
((eq new-fmt 'key-description)
|
||
|
(message "Switched to human readable format"))
|
||
|
((eq new-fmt 'vector)
|
||
|
(message "Switched to vector format"))
|
||
|
(t
|
||
|
(error "Uh? format=%s" new-fmt)))))
|
||
|
|
||
|
|
||
|
(defun widget-key-sequence-read-event (ev)
|
||
|
"Read event or char code and put description in widget.
|
||
|
The events may come from keyboard, mouse, menu or tool bar.
|
||
|
|
||
|
If the event is a mouse event then multiple entries will be
|
||
|
entered. It is not possible to know which one is wanted. Please
|
||
|
remove those not wanted!
|
||
|
|
||
|
If 0-7 is pressed then code for an event is prompted for."
|
||
|
(interactive (list
|
||
|
(let ((inhibit-quit t) quit-flag)
|
||
|
(unless (eq 'key-description
|
||
|
(widget-key-sequence-current-input-format))
|
||
|
(error "Wrong input format, please do C-t first"))
|
||
|
(read-event "Insert KEY, EVENT, or CODE: "))))
|
||
|
(lwarn t :warning "=====> ev=%s" ev)
|
||
|
(let ((tr (and (keymapp function-key-map)
|
||
|
(lookup-key function-key-map (vector ev)))))
|
||
|
(insert (if (= (char-before) ?\s) "" " "))
|
||
|
;; Fix-me: change to check for ? instead of 0-7 to allow char
|
||
|
;; literal input format
|
||
|
(when (and (integerp ev)
|
||
|
(or (and (<= ?0 ev) (< ev (+ ?0 (min 10 read-quoted-char-radix))))
|
||
|
(and (<= ?a (downcase ev))
|
||
|
(< (downcase ev) (+ ?a -10 (min 36 read-quoted-char-radix))))))
|
||
|
(setq unread-command-events (cons ev unread-command-events)
|
||
|
ev (read-quoted-char (format "Enter code (radix %d)" read-quoted-char-radix))
|
||
|
tr nil)
|
||
|
(if (and (integerp ev) (not (characterp ev)))
|
||
|
(insert (char-to-string ev)))) ;; throw invalid char error
|
||
|
(setq ev (key-description (list ev)))
|
||
|
(when (arrayp tr)
|
||
|
(setq tr (key-description (list (aref tr 0))))
|
||
|
(when (y-or-n-p (format "Key %s is translated to %s -- use %s? " ev tr tr))
|
||
|
(setq ev tr)
|
||
|
;;(setq ev2 nil)
|
||
|
))
|
||
|
(insert ev " ")
|
||
|
(when (or (string-match "mouse-" ev)
|
||
|
(string-match "menu-bar" ev)
|
||
|
(string-match "tool-bar" ev))
|
||
|
(let ((ev2 (read-event nil nil (* 0.001 double-click-time))))
|
||
|
(while ev2
|
||
|
(lwarn t :warning "(stringp ev2)=%s, (sequencp ev2)=%s, (symbolp ev2)=%s, ev2=%S" (stringp ev2) (sequencep ev2) (symbolp ev2) ev2)
|
||
|
(if nil ;(memq 32 (append (symbol-name ev2) nil)) ;; Fix-me: contains space
|
||
|
(insert ?\" (symbol-name ev2) ?\")
|
||
|
(insert (key-description (list ev2))))
|
||
|
(insert " ")
|
||
|
(setq ev2 (read-event nil nil (* 0.001 double-click-time))))))))
|
||
|
|
||
|
(defun widget-key-sequence-validate (widget)
|
||
|
"Validate the internal value of the widget.
|
||
|
Actually there is nothing to validate here. The internal value
|
||
|
is always valid, but it is however maybe not what the user
|
||
|
expects. Because of this the internal format is rewritten when
|
||
|
the user gives the value in a way that is not the normal
|
||
|
representation of it. A warning is also shown then."
|
||
|
(condition-case err
|
||
|
(let* ((int-val (widget-apply widget :value-get))
|
||
|
(def-desc (key-description (edmacro-parse-keys int-val)))
|
||
|
(fmt (or (widget-get widget :key-sequence-format)
|
||
|
widget-key-sequence-default-input-format)))
|
||
|
;; Normalize and compare with default description
|
||
|
(setq int-val
|
||
|
(replace-regexp-in-string " *" " " int-val t))
|
||
|
(setq int-val
|
||
|
(replace-regexp-in-string "\\` *\\(.*?\\) *\\'" "\\1" int-val t))
|
||
|
(unless (or
|
||
|
(eq fmt 'vector)
|
||
|
(string= int-val def-desc))
|
||
|
;; Replace with the default description if it is different
|
||
|
;; so the user sees what the value actually means:
|
||
|
(widget-apply widget :value-set def-desc)
|
||
|
(lwarn t :warning
|
||
|
(concat "Key description %s means the same as %s\n"
|
||
|
"\tTip: You can type C-q to insert a key or event")
|
||
|
int-val def-desc)
|
||
|
)
|
||
|
;; Return nil if there a no problem validating
|
||
|
nil)
|
||
|
(error (widget-put widget :error (error-message-string err))
|
||
|
(lwarn t :warning "invalid %S: %s" widget (error-message-string err))
|
||
|
;; Return widget if there was an error
|
||
|
widget)))
|
||
|
|
||
|
(defun widget-key-sequence-value-to-internal (widget value)
|
||
|
(if (widget-apply widget :match value)
|
||
|
(if (equal value widget-key-sequence-default-value)
|
||
|
""
|
||
|
(let ((fmt (or (widget-get widget :key-sequence-format)
|
||
|
widget-key-sequence-default-input-format)))
|
||
|
(if (eq fmt 'vector)
|
||
|
(format "%S" value)
|
||
|
(key-description value))))
|
||
|
value))
|
||
|
|
||
|
(defun widget-key-sequence-value-to-external (widget value)
|
||
|
(if (stringp value)
|
||
|
(if (string-match "\\`[[:space:]]*\\'" value)
|
||
|
widget-key-sequence-default-value
|
||
|
;; Give a better error message and a trace back on debug:
|
||
|
(condition-case err
|
||
|
(let* ((fmt (or (widget-get widget :key-sequence-format)
|
||
|
widget-key-sequence-default-input-format))
|
||
|
(first (string-to-char value)))
|
||
|
(cond
|
||
|
((eq fmt 'vector)
|
||
|
(read value)
|
||
|
)
|
||
|
(t
|
||
|
(key-description-to-vector value))))
|
||
|
(error (error "Bad value: %s" (error-message-string err)))))
|
||
|
value))
|
||
|
|
||
|
;; (customize-option 'new-key-seq-widget-test)
|
||
|
(defcustom new-key-seq-widget-test []
|
||
|
"Testing only!"
|
||
|
:type 'key-sequence
|
||
|
:group 'widgets)
|
||
|
|
||
|
(provide 'new-key-seq-widget)
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;;; new-key-seq-widget.el ends here
|