From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: "Drew Adams" Newsgroups: gmane.emacs.devel Subject: customizing key definitions with Customize Date: Sun, 11 May 2008 12:40:29 -0700 Message-ID: <000301c8b39e$ded16a50$0200a8c0@us.oracle.com> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_NextPart_000_0004_01C8B364.32729250" X-Trace: ger.gmane.org 1210534921 29879 80.91.229.12 (11 May 2008 19:42:01 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Sun, 11 May 2008 19:42:01 +0000 (UTC) To: Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Sun May 11 21:42:39 2008 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by lo.gmane.org with esmtp (Exim 4.50) id 1JvHRG-0004b9-NZ for ged-emacs-devel@m.gmane.org; Sun, 11 May 2008 21:42:31 +0200 Original-Received: from localhost ([127.0.0.1]:34597 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1JvHQY-0008SJ-1g for ged-emacs-devel@m.gmane.org; Sun, 11 May 2008 15:41:46 -0400 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1JvHQS-0008S4-TM for emacs-devel@gnu.org; Sun, 11 May 2008 15:41:40 -0400 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1JvHQP-0008Rr-5Q for emacs-devel@gnu.org; Sun, 11 May 2008 15:41:40 -0400 Original-Received: from [199.232.76.173] (port=50993 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1JvHQP-0008Ro-2G for emacs-devel@gnu.org; Sun, 11 May 2008 15:41:37 -0400 Original-Received: from rgminet01.oracle.com ([148.87.113.118]:19937) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1JvHQO-00063l-Oa for emacs-devel@gnu.org; Sun, 11 May 2008 15:41:37 -0400 Original-Received: from agmgw1.us.oracle.com (agmgw1.us.oracle.com [152.68.180.212]) by rgminet01.oracle.com (Switch-3.2.4/Switch-3.1.6) with ESMTP id m4BJefrx005879 for ; Sun, 11 May 2008 13:40:41 -0600 Original-Received: from acsmt350.oracle.com (acsmt350.oracle.com [141.146.40.150]) by agmgw1.us.oracle.com (Switch-3.2.0/Switch-3.2.0) with ESMTP id m4BJYaWp002696 for ; Sun, 11 May 2008 13:40:40 -0600 Original-Received: from inet-141-146-46-1.oracle.com by acsmt350.oracle.com with ESMTP id 3669433471210534831; Sun, 11 May 2008 12:40:31 -0700 Original-Received: from dradamslap1 (/24.5.171.3) by bhmail.oracle.com (Oracle Beehive Gateway v4.0) with ESMTP ; Sun, 11 May 2008 12:40:30 -0700 X-Mailer: Microsoft Office Outlook 11 X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.3198 Thread-Index: Aciznt5TpAjfyflIR4mz1/9Cde/3lw== X-Brightmail-Tracker: AAAAAQAAAAI= X-Brightmail-Tracker: AAAAAQAAAAI= X-Whitelist: TRUE X-Whitelist: TRUE X-detected-kernel: by monty-python.gnu.org: Linux 2.4-2.6 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:96980 Archived-At: This is a multi-part message in MIME format. ------=_NextPart_000_0004_01C8B364.32729250 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Users can customize key definitions by using commands such as `global-set-key' or by using functions such as `define-key' in their init files. And `kbd' helps them by letting them use external key descriptions. It can sometimes be useful to let users use Customize to update key-binding definitions. The `key-sequence' widget added in Emacs 22 is helpful here, but it only takes care of the key-sequence part of a binding, not the map part or the target command part. Also, defining keys can sometimes mean remapping existing commands instead of providing a key sequence to bind, and the `key-sequence' widget doesn't apply in that case. I've been using a key definition widget as a custom type, to let users of a library use only Customize to customize key bindings, if they want. The widget uses a `choice' to let you either specify a key sequence to bind or a command to be remapped. Dunno if this is of general interest, but here is what I use. (define-widget 'key-definition 'lazy "Key definition. A list of three components: KEYMAP, KEY, COMMAND, that represents a binding of command COMMAND in keymap KEYMAP according to KEY. KEY is either a key sequence (string or vector) or a command. If KEY is a command, then the binding represented is its remapping to command COMMAND." :tag "Key Definition" :indent 1 :offset 0 :type '(list (restricted-sexp :tag "Keymap" :match-alternatives ((lambda (x) (keymapp (eval x)))) :value global-map) (choice (key-sequence :tag "Key" :value [ignore]) (restricted-sexp :tag "Command to remap" :match-alternatives (commandp) :value ignore)) (restricted-sexp :tag "Command" :match-alternatives (commandp) :value ignore))) You can then create a user option for a set of key definitions (or, less likely, for a single key binding), where a key definition includes the keymap, the key (or command to remap), and the target command. For example, here is a `defcustom' that uses a `repeat' of the widget to define a set of bindings: (defcustom some-bindings `((test-map ,(kbd "C-a") forward-char) (test-map end-of-line backward-char) (test-map-2 ,(kbd "C-a") forward-word) (test-map-2 end-of-line backward-word)) "" :type '(repeat key-definition) :set #'(lambda (sym defs) (custom-set-default sym defs) (dolist (key-def defs) (define-key-from-key-def key-def))) :initialize #'custom-initialize-set) The :set function uses this helper: (defun define-key-from-key-def (key-def) "Define a key using a key-definition data structure Argument KEY-DEF is a list of type `key-definition' or `conditional-key-definition'." (let ((map (eval (car key-def))) (key (cadr key-def)) (command (caddr key-def)) (condition (cadddr key-def))) (when (or (not condition) (eval condition)) (if (symbolp key) (define-key map (vector 'remap key) command) (define-key map key command))))) The condition part is not present, and so is not used, in `key-definition', but it is present and used in the similar widget `conditional-key-definition' mentioned in the doc string. That has an additional list item that is eval'd to determine under what conditions to make the binding. The definition is the same as for `key-definition', except that the `list' has this entry, in addition: (sexp :tag "Condition"). I use conditional key definitions to, for example, deal with different Emacs versions. Attached is a file you can play with to try this out. I'm no widget wizard, so there is perhaps a better way to define the widgets. In particular, what is common between them could perhaps be factored out somehow. Suggestions welcome. If there is interest in something like this, we could add it to wid-edit.el. Key bindings are something that most users want to change, and there currently is no ready way for them to do that entirely within Customize. This approach lets libraries define a default set of bindings and lets users use Customize to modify them. ------=_NextPart_000_0004_01C8B364.32729250 Content-Type: application/octet-stream; name="test-key-def-widgets.el" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="test-key-def-widgets.el" (define-widget 'conditional-key-definition 'lazy=0A= "Conditional key definition.=0A= A list of four components: KEYMAP, KEY, COMMAND, CONDITION, that=0A= represents a binding of command COMMAND in keymap KEYMAP according to=0A= KEY, if CONDITION evaluates to non-nil.=0A= =0A= KEY is either a key sequence (string or vector) or a command.=0A= COMMAND is a command.=0A= CONDITION is a sexp.=0A= =0A= If KEY is a command, then the binding represented is its remapping to=0A= COMMAND."=0A= :tag "Key Definition" :indent 1 :offset 0=0A= :type=0A= '(list=0A= (restricted-sexp :tag "Keymap"=0A= :match-alternatives ((lambda (x) (keymapp (eval x))))=0A= :value global-map)=0A= (choice=0A= (key-sequence :tag "Key" :value [ignore])=0A= (restricted-sexp :tag "Command to remap"=0A= :match-alternatives (commandp) :value ignore))=0A= (restricted-sexp :tag "Command"=0A= :match-alternatives (commandp) :value ignore)=0A= (sexp :tag "Condition")))=0A= =0A= (define-widget 'key-definition 'lazy=0A= "Key definition.=0A= A list of three components: KEYMAP, KEY, COMMAND, that represents a=0A= binding of command COMMAND in keymap KEYMAP according to KEY.=0A= KEY is either a key sequence (string or vector) or a command.=0A= If KEY is a command, then the binding represented is its remapping to=0A= command COMMAND."=0A= :tag "Key Definition" :indent 1 :offset 0=0A= :type=0A= '(list=0A= (restricted-sexp :tag "Keymap"=0A= :match-alternatives ((lambda (x) (keymapp (eval x))))=0A= :value global-map)=0A= (choice=0A= (key-sequence :tag "Key" :value [ignore])=0A= (restricted-sexp :tag "Command to remap"=0A= :match-alternatives (commandp) :value ignore))=0A= (restricted-sexp :tag "Command"=0A= :match-alternatives (commandp) :value ignore)))=0A= =0A= (defun define-key-from-key-def (key-def)=0A= "Define a key using a key-definition data structure=0A= Argument KEY-DEF is a list of type `key-definition' or=0A= `conditional-key-definition'."=0A= (let ((map (eval (car key-def)))=0A= (key (cadr key-def))=0A= (command (caddr key-def))=0A= (condition (cadddr key-def)))=0A= (when (or (not condition) (eval condition))=0A= (if (symbolp key)=0A= (define-key map (vector 'remap key) command)=0A= (define-key map key command)))))=0A= =0A= (defun key-def-set (var key-def)=0A= "Set function for types `key-definition', = `conditional-key-definition'."=0A= (custom-set-default var key-def)=0A= (define-key-from-key-def key-def))=0A= =0A= (setq test-map (make-sparse-keymap))=0A= (setq test-map-2 (make-sparse-keymap))=0A= (setq foobar t)=0A= =0A= (defcustom foo `(test-map ,(kbd "C-a") forward-char) ""=0A= :type 'key-definition=0A= :set #'key-def-set=0A= :initialize #'custom-initialize-set)=0A= =0A= (defcustom bar `(test-map end-of-line forward-char) ""=0A= :type 'key-definition=0A= :set #'key-def-set=0A= :initialize #'custom-initialize-set)=0A= =0A= (defcustom some-bindings=0A= `((test-map ,(kbd "C-a") forward-char)=0A= (test-map end-of-line backward-char)=0A= (test-map-2 ,(kbd "C-a") forward-word)=0A= (test-map-2 end-of-line backward-word)) ""=0A= :type '(repeat key-definition)=0A= :set #'(lambda (sym defs)=0A= (custom-set-default sym defs)=0A= (dolist (key-def defs) (define-key-from-key-def key-def)))=0A= :initialize #'custom-initialize-set)=0A= =0A= (defcustom cond-foo `(test-map ,(kbd "C-a") forward-char t) ""=0A= :type 'conditional-key-definition=0A= :set #'key-def-set=0A= :initialize #'custom-initialize-set)=0A= =0A= (defcustom cond-bar `(test-map end-of-line forward-char foobar) ""=0A= :type 'conditional-key-definition=0A= :set #'key-def-set=0A= :initialize #'custom-initialize-set)=0A= =0A= (defcustom some-conditional-bindings=0A= `((test-map ,(kbd "C-a") forward-char t)=0A= (test-map end-of-line backward-char foobar)=0A= (test-map-2 ,(kbd "C-a") forward-word t)=0A= (test-map-2 end-of-line backward-word foobar)) ""=0A= :type '(repeat conditional-key-definition)=0A= :set #'(lambda (sym defs)=0A= (custom-set-default sym defs)=0A= (dolist (key-def defs)=0A= (define-key-from-key-def key-def)))=0A= :initialize #'custom-initialize-set)=0A= =0A= =0A= ------=_NextPart_000_0004_01C8B364.32729250--