;;; quail.el --- Provides simple input method for multilingual text;; Copyright (C) 1995 Electrotechnical Laboratory, JAPAN.;; Licensed to the Free Software Foundation.;; Copyright (C) 1997 MORIOKA Tomohiko;; Author: Kenichi HANDA <handa@etl.go.jp>;; Naoto TAKAHASHI <ntakahas@etl.go.jp>;; modified by MORIOKA Tomohiko <morioka@jaist.ac.jp> for XEmacs;; Keywords: mule, multilingual, input method;; 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, Inc., 59 Temple Place - Suite 330,;; Boston, MA 02111-1307, USA.;;; Commentary:;; In Quail minor mode, you can input multilingual text easily. By;; defining a translation table (named Quail map) which maps ASCII key;; string to multilingual character or string, you can input any text;; from ASCII keyboard.;;;; We use words "translation" and "conversion" differently. The;; former is done by Quail package itself, the latter is the further;; process of converting a translated text to some more desirable;; text. For instance, Quail package for Japanese (`quail-jp');; translates Roman text (transliteration of Japanese in Latin;; alphabets) to Hiragana text, which is then converted to;; Kanji-and-Kana mixed text or Katakana text by commands specified in;; CONVERSION-KEYS argument of the Quail package.;;; Code:(eval-when-compile(if(string-match"XEmacs"emacs-version)(require'overlay)))(cond((featurep'xemacs)(require'overlay))(t(require'faces)));; Buffer local variables(defvarquail-current-packagenil"The current Quail package, which depends on the current input method.See the documentation of `quail-package-alist' for the format.")(make-variable-buffer-local'quail-current-package)(put'quail-current-package'permanent-localt);; Quail uses the following two buffers to assist users.;; A buffer to show available key sequence or translation list.(defvarquail-guidance-bufnil);; A buffer to show completion list of the current key sequence.(defvarquail-completion-bufnil);; Each buffer in which Quail is activated should use different;; guidance buffers.(make-variable-buffer-local'quail-guidance-buf)(put'quail-guidance-buf'permanent-localt);; A main window showing Quail guidance buffer.(defvarquail-guidance-winnil)(make-variable-buffer-local'quail-guidance-win)(defvarquail-modenil"Non-nil if in Quail minor mode.")(make-variable-buffer-local'quail-mode)(put'quail-mode'permanent-localt)(defvarquail-overlaynil"Overlay which covers the current translation region of Quail.")(make-variable-buffer-local'quail-overlay)(defvarquail-conv-overlaynil"Overlay which covers the text to be converted in Quail mode.")(make-variable-buffer-local'quail-conv-overlay)(defvarquail-current-keynil"Current key for translation in Quail mode.")(defvarquail-current-strnil"Currently selected translation of the current key.")(defvarquail-current-translationsnil"Cons of indices and vector of possible translations of the current key.Indices is a list of (CURRENT START END BLOCK BLOCKS), whereCURRENT is an index of the current translation,START and END are indices of the start and end of the current block,BLOCK is the current block index,BLOCKS is a number of blocks of translation.")(defvarquail-current-datanil"Any Lisp object holding information of current translation status.When a key sequence is mapped to TRANS and TRANS is a consof actual translation and some Lisp object to be referedfor translating the longer key sequence, this variable is setto that Lisp object.")(make-variable-buffer-local'quail-current-data);; A flag to control conversion region. Normally nil, but if set to;; t, it means we must start the new conversion region if new key to;; be translated is input.(defvarquail-reset-conversion-regionnil);; Quail package handlers.(defvarquail-package-alistnil"List of Quail packages.A Quail package is a list of these elements: NAME, TITLE, QUAIL-MAP, GUIDANCE, DOCSTRING, TRANSLATION-KEYS, FORGET-LAST-SELECTION, DETERMINISTIC, KBD-TRANSLATE, SHOW-LAYOUT, DECODE-MAP, MAXIMUM-SHORTEST, OVERLAY-PLIST, UPDATE-TRANSLATION-FUNCTION, CONVERSION-KEYS, SIMPLE.QUAIL-MAP is a data structure to map key strings to translations. Forthe format, see the documentation of `quail-map-p'.DECODE-MAP is an alist of translations and corresponding keys.See the documentation of `quail-define-package' for the other elements.");; Return various slots in the current quail-package.(defsubstquail-name()"Return the name of the current Quail package."(nth0quail-current-package))(defsubstquail-title()"Return the title of the current Quail package."(nth1quail-current-package))(defsubstquail-map()"Return the translation map of the current Quail package."(nth2quail-current-package))(defsubstquail-guidance()"Return an object used for `guidance' feature of the current Quail package.See also the documentation of `quail-define-package'."(nth3quail-current-package))(defsubstquail-docstring()"Return the documentation string of the current Quail package."(nth4quail-current-package))(defsubstquail-translation-keymap()"Return translation keymap in the current Quail package.Translation keymap is a keymap used while translation region is active."(nth5quail-current-package))(defsubstquail-forget-last-selection()"Return `forget-last-selection' flag of the current Quail package.See also the documentation of `quail-define-package'."(nth6quail-current-package))(defsubstquail-deterministic()"Return `deterministic' flag of the current Quail package.See also the documentation of `quail-define-package'."(nth7quail-current-package))(defsubstquail-kbd-translate()"Return `kbd-translate' flag of the current Quail package.See also the documentation of `quail-define-package'."(nth8quail-current-package))(defsubstquail-show-layout()"Return `show-layout' flag of the current Quail package.See also the documentation of `quail-define-package'."(nth9quail-current-package))(defsubstquail-decode-map()"Return decode map of the current Quail package.It is an alist of translations and corresponding keys."(nth10quail-current-package))(defsubstquail-maximum-shortest()"Return `maximum-shortest' flag of the current Quail package.See also the documentation of `quail-define-package'."(nth11quail-current-package))(defsubstquail-overlay-plist()"Return property list of an overly used in the current Quail package."(nth12quail-current-package))(defsubstquail-update-translation-function()"Return a function for updating translation in the current Quail package."(nth13quail-current-package))(defsubstquail-conversion-keymap()"Return conversion keymap in the current Quail package.Conversion keymap is a keymap used while conversion region is active but translation region is not active."(nth14quail-current-package))(defsubstquail-simple()"Return t if the current Quail package is simple."(nth15quail-current-package))(defsubstquail-package(name)"Return Quail package named NAME."(assocnamequail-package-alist))(defunquail-add-package(package)"Add Quail package PACKAGE to `quail-package-alist'."(let((pac(quail-package(carpackage))))(ifpac(setcdrpac(cdrpackage))(setqquail-package-alist(conspackagequail-package-alist)))))(defunquail-select-package(name)"Select Quail package named NAME as the current Quail package."(let((package(quail-packagename)))(if(nullpackage)(error"No Quail package `%s'"name))(setqquail-current-packagepackage)(setq-defaultquail-current-packagepackage)name));;;###autoload(defunquail-use-package(package-name&restlibraries)"Start using Quail package PACKAGE-NAME.The remaining arguments are libraries to be loaded before using the package."(let((package(quail-packagepackage-name)))(if(nullpackage);; Perhaps we have not yet loaded necessary libraries.(whilelibraries(if(not(load(carlibraries)t))(progn(with-output-to-temp-buffer"*Help*"(princ"Quail package \"")(princpackage-name)(princ"\" can't be activated\n because library \"")(princ(carlibraries))(princ"\" is not in `load-path'.The most common case is that you have not yet installed appropriatelibraries in LEIM (Libraries of Emacs Input Method) which isdistributed separately from Emacs.LEIM is available from the same ftp directory as Emacs."))(error"Can't use the Quail package `%s'"package-name))(setqlibraries(cdrlibraries))))))(quail-select-packagepackage-name)(setqcurrent-input-method-title(quail-title))(quail-mode1))(defunquail-inactivate()"Turn off Quail input method."(interactive)(setqoverriding-terminal-local-mapnil)(quail-mode-1))(or(assq'quail-modeminor-mode-alist)(setqminor-mode-alist(cons'(quail-mode" Quail")minor-mode-alist)))(defvarquail-mode-map(let((map(make-keymap))(i(+?\ 0)))(while(<i127)(define-keymap(vector(list(int-chari)))'quail-start-translation)(setqi(1+i)))(setqi128)(while(<i256)(define-keymap(vector(list(int-chari)))'quail-start-translation)(setqi(1+i)))map)"Keymap for Quail mode.")(or(assq'quail-modeminor-mode-map-alist)(setqminor-mode-map-alist(cons(cons'quail-modequail-mode-map)minor-mode-map-alist)));; Since some Emacs Lisp programs (e.g. viper.el) make;; minor-mode-map-alist buffer-local, we must be sure to register;; quail-mode-map in default-value of minor-mode-map-alist.(if(local-variable-p'minor-mode-map-alistnil)(let((map(default-value'minor-mode-map-alist)))(or(assq'quail-modemap);; (set-default 'minor-mode-map-alist (cons 'quail-mode map)))))(set-default'minor-mode-map-alist(cons(cons'quail-modequail-mode-map)map)))))(defvarquail-translation-keymap(let((map(make-keymap))(i0))(while(<i?\ )(define-keymap(char-to-stringi)'quail-execute-non-quail-command)(setqi(1+i)))(while(<i127)(define-keymap(char-to-stringi)'quail-self-insert-command)(setqi(1+i)))(define-keymap"\177"'quail-delete-last-char);; (define-key map "\C-\\" 'quail-inactivate)(define-keymap"\C-f"'quail-next-translation)(define-keymap"\C-b"'quail-prev-translation)(define-keymap"\C-n"'quail-next-translation-block)(define-keymap"\C-p"'quail-prev-translation-block)(define-keymap"\C-i"'quail-completion)(define-keymap"\C-@"'quail-select-current);; (define-key map "\C-c" 'quail-abort-translation)(define-keymap"\C-h"'quail-translation-help);;; This interferes with handling of escape sequences on non-X terminals.;;; (define-key map "\e" '(keymap (t . quail-execute-non-quail-command)))(define-keymap[(controlspace)]'quail-select-current)(define-keymap[tab]'quail-completion)(define-keymap[delete]'quail-backward-or-forward-delete-char)(define-keymap[backspace]'quail-delete-backward-char)(let((meta-map(make-sparse-keymap)))(when(characterpmeta-prefix-char)(define-keymap(char-to-stringmeta-prefix-char)meta-map))(define-keymap[escape]meta-map))(when(characterpmeta-prefix-char)(define-keymap(stringmeta-prefix-char?t)'quail-execute-non-quail-command));; At last, define default key binding.(set-keymap-default-bindingmap'quail-execute-non-quail-command)map)"Keymap used processing translation in complex Quail modes.Only a few especially complex input methods use this map;most use `quail-simple-translation-keymap' instead.This map is activated while translation region is active.")(defvarquail-simple-translation-keymap(let((map(make-keymap))(i0))(while(<i?\ )(define-keymap(char-to-stringi)'quail-execute-non-quail-command)(setqi(1+i)))(while(<i127)(define-keymap(char-to-stringi)'quail-self-insert-command)(setqi(1+i)))(define-keymap[delete]'quail-backward-or-forward-delete-char)(define-keymap[backspace]'quail-delete-backward-char);;; This interferes with handling of escape sequences on non-X terminals.;;; (define-key map "\e" '(keymap (t . quail-execute-non-quail-command)))(let((meta-map(make-sparse-keymap)))(when(characterpmeta-prefix-char)(define-keymap(char-to-stringmeta-prefix-char)meta-map))(define-keymap[escape]meta-map))(when(characterpmeta-prefix-char)(define-keymap(stringmeta-prefix-char?t)'quail-execute-non-quail-command));; At last, define default key binding.(set-keymap-default-bindingmap'quail-execute-non-quail-command)map)"Keymap used while processing translation in simple Quail modes.A few especially complex input methods use `quail--translation-keymap' instead.This map is activated while translation region is active.")(defvarquail-conversion-keymap(let((map(make-keymap))(i0))(while(<i?\ )(define-keymap(char-to-stringi)'quail-execute-non-quail-command)(setqi(1+i)))(while(<i127)(define-keymap(char-to-stringi)'quail-start-translation-in-conversion-mode)(setqi(1+i)))(define-keymap"\C-b"'quail-conversion-backward-char)(define-keymap"\C-f"'quail-conversion-forward-char)(define-keymap"\C-a"'quail-conversion-beginning-of-region)(define-keymap"\C-e"'quail-conversion-end-of-region)(define-keymap"\C-d"'quail-conversion-delete-char)(define-keymap"\C-h"'quail-conversion-help);; (define-key map "\C-\\" 'quail-inactivate);;; This interferes with handling of escape sequences on non-X terminals.;;; (define-key map "\e" '(keymap (t . quail-execute-non-quail-command)))(define-keymap"\177"'quail-conversion-backward-delete-char)(define-keymap[delete]'quail-conversion-backward-delete-char)(define-keymap[backspace]'quail-conversion-backward-delete-char)(let((meta-map(make-sparse-keymap)))(when(characterpmeta-prefix-char)(define-keymap(char-to-stringmeta-prefix-char)meta-map))(define-keymap[escape]meta-map))(when(characterpmeta-prefix-char)(define-keymap(vectormeta-prefix-chart)'quail-execute-non-quail-command));; At last, define default key binding.(set-keymap-default-bindingmap'quail-execute-non-quail-command)map)"Keymap used for processing conversion in Quail mode.This map is activated while convesion region is active but translationregion is not active.");;;###autoload(defunquail-define-package(namelanguagetitle&optionalguidancedocstringtranslation-keysforget-last-selectiondeterministickbd-translateshow-layoutcreate-decode-mapmaximum-shortestoverlay-plistupdate-translation-functionconversion-keyssimple)"Define NAME as a new Quail package for input LANGUAGE.TITLE is a string to be displayed at mode-line to indicate this package.Optional arguments are GUIDANCE, DOCSTRING, TRANSLATION-KEYS, FORGET-LAST-SELECTION, DETERMINISTIC, KBD-TRANSLATE, SHOW-LAYOUT, CREATE-DECODE-MAP, MAXIMUM-SHORTEST, OVERLAY-PLIST, UPDATE-TRANSLATION-FUNCTION, CONVERSION-KEYS and SIMPLE.GUIDANCE specifies how a guidance string is shown in echo area.If it is t, list of all possible translations for the current key is shown with the currently selected translation being highlighted.If it is an alist, the element has the form (CHAR . STRING). Each character in the current key is searched in the list and the corresponding string is shown.If it is nil, the current key is shown.DOCSTRING is the documentation string of this package.TRANSLATION-KEYS specifies additional key bindings used while translationregion is active. It is an alist of single key character vs. correspondingcommand to be called.FORGET-LAST-SELECTION non-nil means a selected translation is not keptfor the future to translate the same key. If this flag is nil, atranslation selected for a key is remembered so that it can be thefirst candidate when the same key is entered later.DETERMINISTIC non-nil means the first candidate of translation isselected automatically without allowing users to select anothertranslation for a key. In this case, unselected translations are ofno use for an interactive use of Quail but can be used by some otherprograms. If this flag is non-nil, FORGET-LAST-SELECTION is also setto t.KBD-TRANSLATE non-nil means input characters are translated from auser's keyboard layout to the standard keyboard layout. See thedocumentation of `quail-keyboard-layout' and`quail-keyboard-layout-standard' for more detail.SHOW-LAYOUT non-nil means the `quail-help' command should showthe user's keyboard layout visually with translated characters.If KBD-TRANSLATE is set, it is desirable to set also this flag unlessthis package defines no translations for single character keys.CREATE-DECODE-MAP non-nil means decode map is also created. A decodemap is an alist of translations and corresponding original keys.Although this map is not used by Quail itself, it can be used by someother programs. For instance, Vietnamese supporting needs this map toconvert Vietnamese text to VIQR format which uses only ASCIIcharacters to represent Vietnamese characters.MAXIMUM-SHORTEST non-nil means break key sequence to get maximumlength of the shortest sequence. When we don't have a translation ofkey \"..ABCD\" but have translations of \"..AB\" and \"CD..\", breakthe key at \"..AB\" and start translation of \"CD..\". Hangulpackages, for instance, use this facility. If this flag is nil, webreak the key just at \"..ABC\" and start translation of \"D..\".OVERLAY-PLIST if non-nil is a property list put on an overlay whichcovers Quail translation region.UPDATE-TRANSLATION-FUNCTION if non-nil is a function to call to updatethe current translation region accoding to a new translation data. Bydefault, a translated text or a user's key sequence (if no translationfor it) is inserted.CONVERSION-KEYS specifies additional key bindings used whileconversion region is active. It is an alist of single key charactervs. corresponding command to be called.If SIMPLE is non-nil, then we do not alter the meanings ofcommands such as C-f, C-b, C-n, C-p and TAB; they are treated asnon-Quail commands."(let(translation-keymapconversion-keymap)(ifdeterministic(setqforget-last-selectiont))(iftranslation-keys(progn(setqtranslation-keymap(copy-keymap(ifsimplequail-simple-translation-keymapquail-translation-keymap)))(whiletranslation-keys(define-keytranslation-keymap(car(cartranslation-keys))(cdr(cartranslation-keys)))(setqtranslation-keys(cdrtranslation-keys))))(setqtranslation-keymap(ifsimplequail-simple-translation-keymapquail-translation-keymap)))(whenconversion-keys(setqconversion-keymap(copy-keymapquail-conversion-keymap))(whileconversion-keys(define-keyconversion-keymap(car(carconversion-keys))(cdr(carconversion-keys)))(setqconversion-keys(cdrconversion-keys))))(quail-add-package(listnametitle(listnil)guidance(ordocstring"")translation-keymapforget-last-selectiondeterministickbd-translateshow-layout(ifcreate-decode-map(list'decode-map)nil)maximum-shortestoverlay-plistupdate-translation-functionconversion-keymapsimple));; Update input-method-alist.(let((slot(assocnameinput-method-alist))(val(listlanguage'quail-use-packagetitledocstring)))(ifslot(setcdrslotval)(setqinput-method-alist(cons(consnameval)input-method-alist)))))(quail-select-packagename));; Quail minor mode handlers.;; Setup overlays used in Quail mode.(defunquail-setup-overlays(conversion-mode)(let((pos(point)))(if(overlaypquail-overlay)(move-overlayquail-overlaypospos)(setqquail-overlay(make-overlayposposnilnilt))(ifinput-method-highlight-flag(overlay-putquail-overlay'face'underline))(let((l(quail-overlay-plist)))(whilel(overlay-putquail-overlay(carl)(car(cdrl)))(setql(cdr(cdrl))))))(ifconversion-mode(if(overlaypquail-conv-overlay)(if(not(overlay-startquail-conv-overlay))(move-overlayquail-conv-overlaypospos))(setqquail-conv-overlay(make-overlayposposnilnilt))(ifinput-method-highlight-flag(overlay-putquail-conv-overlay'face'underline))))));; Delete overlays used in Quail mode.(defunquail-delete-overlays()(if(overlaypquail-overlay)(delete-overlayquail-overlay))(if(overlaypquail-conv-overlay)(delete-overlayquail-conv-overlay)));; Kill Quail guidance buffer. Set in kill-buffer-hook.(defunquail-kill-guidance-buf()(if(buffer-live-pquail-guidance-buf)(kill-bufferquail-guidance-buf)))(defunquail-mode(&optionalarg)"Toggle Quail minor mode.With arg, turn Quail mode on if and only if arg is positive.You should not turn on and off Quail mode manually, instead usethe commands `toggle-input-method' or `select-input-methods' (whichsee). They automatically turn on or off this mode.Try \\[describe-bindings] in Quail mode to see the available key bindings.The command \\[describe-input-method] describes the current Quail package."(setqquail-mode(if(nullarg)(nullquail-mode)(>(prefix-numeric-valuearg)0)))(if(nullquail-mode);; Let's turn off Quail mode.(progn(quail-hide-guidance-buf)(quail-delete-overlays)(setqdescribe-current-input-method-functionnil)(run-hooks'quail-mode-exit-hook));; Let's turn on Quail mode.;; At first, be sure that quail-mode is at the first element of;; minor-mode-map-alist.;; The following code removed by slb because it corrupts the XEmacs;; minor-mode-map-alist; (or (eq (car minor-mode-map-alist) 'quail-mode); (let ((l minor-mode-map-alist)); (while (cdr l); (if (eq (car (cdr l)) 'quail-mode); (progn; (setcdr l (cdr (cdr l))); (setq l nil)); (setq l (cdr l)))); (setq minor-mode-map-alist (cons 'quail-mode minor-mode-map-alist))));; End bogus code removal.(if(nullquail-current-package);; Quail package is not yet selected. Select one now.(let(name)(ifquail-package-alist(setqname(car(carquail-package-alist)))(setqquail-modenil)(error"No Quail package loaded"))(quail-select-packagename)))(setqinactivate-current-input-method-function'quail-inactivate)(setqdescribe-current-input-method-function'quail-help)(quail-delete-overlays)(quail-show-guidance-buf);; If we are in minibuffer, turn off the current input method;; before exiting.(if(eq(selected-window)(minibuffer-window))(add-hook'minibuffer-exit-hook'quail-exit-from-minibuffer))(make-local-hook'post-command-hook)(make-local-hook'kill-buffer-hook)(add-hook'kill-buffer-hook'quail-kill-guidance-bufnilt)(run-hooks'quail-mode-hook))(force-mode-line-update))(defunquail-exit-from-minibuffer()(inactivate-input-method)(if(<=(minibuffer-depth)1)(remove-hook'minibuffer-exit-hook'quail-exit-from-minibuffer)))(defvarquail-saved-current-mapnil)(defvarquail-saved-current-buffernil);; Toggle Quail mode. This function is added to `post-command-hook';; in Quail mode, to turn Quail mode temporarily off, or back on after;; one non-Quail command.(defunquail-toggle-mode-temporarily()(ifquail-mode;; We are going to handle following events out of Quail mode.(setqquail-saved-current-buffer(current-buffer)quail-saved-current-mapoverriding-terminal-local-mapquail-modeniloverriding-terminal-local-mapnil);; We have just executed one non-Quail command. We don't need;; this hook any more.(remove-hook'post-command-hook'quail-toggle-mode-temporarilyt)(if(eq(current-buffer)quail-saved-current-buffer);; We should go back to Quail mode only when the current input;; method was not turned off by the last command.(whencurrent-input-method(setqquail-modetoverriding-terminal-local-mapquail-saved-current-map)(ifinput-method-exit-on-invalid-key(inactivate-input-method)));; The last command changed the current buffer, we should not go;; back to Quail mode in this new buffer, but should turn on;; Quail mode in the original buffer.(save-excursion(set-bufferquail-saved-current-buffer)(setqquail-modet)(quail-delete-overlays)))))(defunquail-execute-non-quail-command()"Execute one non-Quail command out of Quail mode.The current translation and conversion are terminated."(interactive)(let*((key(this-command-keys));; dverna - Apr. 98;; This code clones the listify-key-sequence function that was;; removed since 20.5-b33. Quail was the only package to use it;; at that time. + rest in piece +(aux(logior128(read"?\\M-\\^@")))(keylist(if(vectorpkey)(appendkeynil)(mapcar(function(lambda(c)(if(>c127)(logxorcaux)c)))(appendkeynil)))))(setqunread-command-events(appendkeylistunread-command-events)))(reset-this-command-lengths)(quail-terminate-translation)(quail-delete-overlays)(setqoverriding-terminal-local-mapnil)(if(buffer-live-pquail-guidance-buf)(save-excursion(set-bufferquail-guidance-buf)(erase-buffer)))(add-hook'post-command-hook'quail-toggle-mode-temporarilynilt));; Keyboard layout translation handlers.;; Some Quail packages provide localized keyboard simulation which;; requires a particular keyboard layout. In this case, what we need;; is locations of keys the user entered, not character codes;; generated by those keys. However, for the moment, there's no;; common way to get such information. So, we ask a user to give;; information of his own keyboard layout, then translate it to the;; standard layout which we defined so that all Quail packages depend;; just on it.(defconstquail-keyboard-layout-standard"\ \ 1!2@3#4$5%6^7&8*9(0)-_=+`~ \ qQwWeErRtTyYuUiIoOpP[{]} \ aAsSdDfFgGhHjJkKlL;:'\"\\| \ zZxXcCvVbBnNmM,<.>/? \ ""Standard keyboard layout of printable characters Quail assumes.See the documentation of `quail-keyboard-layout' for this format.This layout is almost the same as that of VT100, but the location of key \\ (backslash) is just right of key ' (single-quote), not right of RETURN key.")(defvarquail-keyboard-layoutquail-keyboard-layout-standard"A string which represents physical key layout of a particular keyboard.We assume there are six rows and each row has 15 keys (columns), the first row is above the `1' - `0' row, the first column of the second row is left of key `1', the first column of the third row is left of key `q', the first column of the fourth row is left of key `a', the first column of the fifth row is left of key `z', the sixth row is below the `z' - `/' row.Nth (N is even) and (N+1)th characters in the string are non-shifted and shifted characters respectively at the same location.The location of Nth character is row (N / 30) and column ((N mod 30) / 2).The command `quail-set-keyboard-layout' usually sets this variable.")(defconstquail-keyboard-layout-len180);; Here we provide several examples of famous keyboard layouts.(defvarquail-keyboard-layout-alist(list'("sun-type3"."\ \ 1!2@3#4$5%6^7&8*9(0)-_=+\\|`~\ qQwWeErRtTyYuUiIoOpP[{]} \ aAsSdDfFgGhHjJkKlL;:'\" \ zZxXcCvVbBnNmM,<.>/? \ ")'("atari-german"."\ \ 1!2\"3\2474$5%6&7/8(9)0=\337?'`#^ \ qQwWeErRtTzZuUiIoOpP\374\334+* \ aAsSdDfFgGhHjJkKlL\366\326\344\304~| \<>yYxXcCvVbBnNmM,;.:-_ \ ")(cons"standard"quail-keyboard-layout-standard))"Alist of keyboard names and corresponding layout strings.See the documentation of `quail-keyboard-layout' for the format of the layout string.");;;###autoload(defunquail-set-keyboard-layout(kbd-type)"Set the current keyboard layout to the same as keyboard KBD-TYPE.Since some Quail packages depends on a physical layout of keys (notcharacters generated by them), those are created by assuming thestandard layout defined in `quail-keyboard-layout-standard'. Thisfunction tells Quail system the layout of your keyboard so that whatyou type is correctly handled."(interactive(let*((completion-ignore-caset)(type(completing-read"Keyboard type: "quail-keyboard-layout-alist)))(listtype)))(let((layout(assockbd-typequail-keyboard-layout-alist)))(if(nulllayout);; Here, we had better ask a user to define his own keyboard;; layout interactively.(error"Unknown keyboard type `%s'"kbd-type))(setqquail-keyboard-layout(cdrlayout))))(defunquail-keyboard-translate(ch)"Translate CHAR according to `quail-keyboard-layout' and return the result."(if(eqquail-keyboard-layoutquail-keyboard-layout-standard);; All Quail packages are designed based on;; `quail-keyboard-layout-standard'.ch(let((i0))(while(and(<iquail-keyboard-layout-len)(/=ch(arefquail-keyboard-layouti)))(setqi(1+i)))(if(=iquail-keyboard-layout-len);; CH is not in quail-keyboard-layout, which means that a;; user typed a key which generated a character code to be;; handled out of Quail. Just return CH and make;; quail-execute-non-quail-command handle it correctly.ch(let((char(arefquail-keyboard-layout-standardi)))(if(=char?\ );; A user typed a key at the location not convered by;; quail-keyboard-layout-standard. Just return CH as;; well as above.chchar))))));; Quail map(defsubstquail-map-p(object)"Return t if OBJECT is a Quail map.A Quail map holds information how a particular key should be translated.Its format is (TRANSLATION . ALIST).TRANSLATION is either a character, or a cons (INDEX . VECTOR).In the latter case, each element of VECTOR is a candidate for the translation,and INDEX points the currently selected translation.ALIST is normally a list of elements that look like (CHAR . DEFN),where DEFN is another Quail map for a longer key (CHAR added to thecurrent key). It may also be a symbol of a function which returns analist of the above format.Just after a Quail package is read, TRANSLATION may be a string or avector. Then each element of the string or vector is a candidate forthe translation. These objects are transformed to cons cells in theformat \(INDEX . VECTOR), as described above."(and(conspobject)(let((translation(carobject)))(or(characterptranslation)(nulltranslation)(vectorptranslation)(stringptranslation)(symbolptranslation)(and(consptranslation)(not(vectorp(cdrtranslation))))))(let((alist(cdrobject)))(or(and(listpalist)(consp(caralist)))(symbolpalist)))));;;###autoload(defmacroquail-define-rules(&restrules)"Define translation rules of the current Quail package.Each argument is a list of KEY and TRANSLATION.KEY is a string meaning a sequence of keystrokes to be translated.TRANSLATION is a character, a string, a vector, a Quail map, or a function.If it is a character, it is the sole translation of KEY.If it is a string, each character is a candidate for the translation.If it is a vector, each element (string or character) is a candidate for the translation.In these cases, a key specific Quail map is generated and assigned to KEY.(A Quail map is a cons that satisfies `quail-map-p', which see.)If TRANSLATION is a Quail map it is used to handle KEY. If TRANSLATION is a function symbol, that function should take twoarguments, the input string and its length, and it should return a Quail mapto be used to handle KEY. For each KEY that translates to the function,that function will be called just once, and the result cached by Quail--thismeans that some of the context-dependent processing you may be tempted todo, like manipulating unread-command-events, is not possible. "`(quail-install-map',(let((lrules)(map(listnil)))(whilel(quail-defrule-internal(car(carl))(car(cdr(carl)))map)(setql(cdrl)))map)));;;###autoload(defunquail-install-map(map)"Install the Quail map MAP in the current Quail package.The installed map can be referred by the function `quail-map'."(if(nullquail-current-package)(error"No current Quail package"))(if(null(quail-map-pmap))(error"Invalid Quail map `%s'"map))(setcar(cdr(cdrquail-current-package))map));;;###autoload(defunquail-defrule(keytranslation&optionalname)"Add one translation rule, KEY to TRANSLATION, in the current Quail package.KEY is a string meaning a sequence of keystrokes to be translated.TRANSLATION is a character, a string, a vector, a Quail map, a function, or a cons.It it is a character, it is the sole translation of KEY.If it is a string, each character is a candidate for the translation.If it is a vector, each element (string or character) is a candidate for the translation.If it is a cons, the car is one of the above and the cdr is a function to call when translating KEY (the return value is assigned to the variable `quail-current-data'). If the cdr part is not a function, the value itself is assigned to `quail-current-data'.In these cases, a key specific Quail map is generated and assigned to KEY.(A Quail map is a cons that satisfies `quail-map-p', which see.)If TRANSLATION is a Quail map it is used to handle KEY. If TRANSLATION is a function symbol, that function should take twoarguments, the input string and its length. It should return a Quail map tobe used to handle KEY. For each KEY that translates to the function, thatfunction will be called just once, and the result cached by Quail--thismeans that some of the context-dependent processing you may be tempted todo, like manipulating `unread-command-events', is not possible. Optional argument NAME, if specified, says which Quail packageto define this translation rule in. The default is to define it in thecurrent Quail package."(ifname(let((package(quail-packagename)))(if(nullpackage)(error"No Quail package `%s'"name))(setqquail-current-packagepackage)))(quail-defrule-internalkeytranslation(quail-map)));;;###autoload(defunquail-defrule-internal(keytransmap)"Define KEY as TRANS in a Quail map MAP."(if(null(stringpkey))"Invalid Quail key `%s'"key);; 1997/5/26 by MORIOKA Tomohiko;; modified for XEmacs(if(not(or(characterptrans)(stringptrans)(vectorptrans)(consptrans)(symbolptrans)(quail-map-ptrans)))(error"Invalid Quail translation `%s'"trans))(if(null(quail-map-pmap))(error"Invalid Quail map `%s'"map))(let((len(lengthkey))(idx0)chentry);; Make a map for registering TRANS if necessary.(while(<idxlen)(if(null(conspmap));; We come here, for example, when we try to define a rule;; for "ABC" but a rule for "AB" is already defined as a;; symbol.(error"Quail key %s is too long"key))(setqch(arefkeyidx)entry(assqch(cdrmap)))(if(nullentry)(progn(setqentry(consch(listnil)))(setcdrmap(consentry(cdrmap)))))(setqmap(cdrentry))(setqidx(1+idx)))(if(symbolptrans)(if(cdrmap);; We come here, for example, when we try to define a rule;; for "AB" as a symbol but a rule for "ABC" is already;; defined.(error"Quail key %s is too short"key)(setcdrentrytrans))(if(quail-map-ptrans)(if(not(listp(cdrmap)));; We come here, for example, when we try to define a rule;; for "AB" as a symbol but a rule for "ABC" is already;; defined.(error"Quail key %s is too short"key)(if(not(listp(cdrtrans)))(if(cdrmap);; We come here, for example, when we try to;; define a rule for "AB" as a symbol but a rule;; for "ABC" is already defined.(error"Quail key %s is too short"key)(setcdrentrytrans))(setcdrentry(appendtrans(cdrmap)))))(setcarmaptrans)))))(defunquail-get-translation(defkeylen)"Return the translation specified as DEF for KEY of length LEN.The translation is either a character or a cons of the form (INDEX . VECTOR),where VECTOR is a vector of candidates (character or string) forthe translation, and INDEX points into VECTOR to specify the currentlyselected translation."(if(anddef(symbolpdef));; DEF is a symbol of a function which returns valid translation.(setqdef(funcalldefkeylen)))(if(and(conspdef)(not(vectorp(cdrdef))))(setqdef(cardef)))(cond((or(characterpdef)(conspdef))def)((nulldef);; No translation.nil)((stringpdef);; Each character in DEF is a candidate of translation. Reform;; it as (INDICES . VECTOR).(setqdef(string-to-vectordef));; But if the length is 1, we don't need vector but a single;; candidate as the translation.(if(=(lengthdef)1)(arefdef0)(cons(list0000nil)def)))((vectorpdef);; Each element (string or character) in DEF is a candidate of;; translation. Reform it as (INDICES . VECTOR).(cons(list0000nil)def))(t(error"Invalid object in Quail map: %s"def))))(defunquail-lookup-key(key&optionallennot-reset-indices)"Lookup KEY of length LEN in the current Quail map and return the definition.The returned value is a Quail map specific to KEY."(orlen(setqlen(lengthkey)))(let((idx0)(map(quail-map))(kbd-translate(quail-kbd-translate))slotchtranslationdef)(while(andmap(<idxlen))(setqch(ifkbd-translate(quail-keyboard-translate(arefkeyidx))(arefkeyidx)))(setqidx(1+idx))(if(and(cdrmap)(symbolp(cdrmap)))(setcdrmap(funcall(cdrmap)keyidx)))(setqslot(assqch(cdrmap)))(if(and(cdrslot)(symbolp(cdrslot)))(setcdrslot(funcall(cdrslot)keyidx)))(setqmap(cdrslot)))(setqdef(carmap))(setqquail-current-translationsnil)(if(andmap(setqtranslation(quail-get-translationdefkeylen)))(progn(if(and(conspdef)(not(vectorp(cdrdef))))(progn(if(not(equal(cardef)translation));; We must reflect TRANSLATION to car part of DEF.(setcardeftranslation))(setqquail-current-data(if(functionp(cdrdef))(funcall(cdrdef))(cdrdef))))(if(not(equaldeftranslation));; We must reflect TRANSLATION to car part of MAP.(setcarmaptranslation)))(if(and(consptranslation)(vectorp(cdrtranslation)))(progn(setqquail-current-translationstranslation)(if(and(notnot-reset-indices)(quail-forget-last-selection))(setcar(carquail-current-translations)0))));; We may have to reform cdr part of MAP.(if(and(cdrmap)(symbolp(cdrmap)))(progn(setcdrmap(funcall(cdrmap)keylen))))))map));; If set to non-nil, exit conversion mode before starting new translation.(defvarquail-exit-conversion-modenil)(defvarquail-prefix-argnil)(defunquail-start-translation(arg)"Start translating the typed character in Quail mode."(interactive"*_P")(setqprefix-argarg)(setqquail-prefix-argarg)(setqunread-command-events(conslast-command-eventunread-command-events));; Check the possibility of translating the last key.(if(and(characterp(event-to-characterlast-command-event))(assq(if(quail-kbd-translate)(quail-keyboard-translate(event-to-characterlast-command-event))(event-to-characterlast-command-event))(cdr(quail-map))));; Ok, we can start translation.(if(quail-conversion-keymap);; We must start translation in conversion mode.(setqquail-exit-conversion-modeniloverriding-terminal-local-map(quail-conversion-keymap))(quail-setup-overlaysnil)(setqquail-current-key"")(setqoverriding-terminal-local-map(quail-translation-keymap)));; Since the last event doesn't start any translation, handle it;; out of Quail mode. We come back to Quail mode later by setting;; function `quail-toggle-mode-temporarily' in;; `post-command-hook'.(add-hook'post-command-hook'quail-toggle-mode-temporarilynilt)))(defsubstquail-point-in-conversion-region()"Return non-nil value if the point is in conversion region of Quail mode."(let(startpos)(and(setqstart(overlay-startquail-conv-overlay))(>=(setqpos(point))start)(<=pos(overlay-endquail-conv-overlay)))))(defunquail-start-translation-in-conversion-mode()"Start translating the typed character in conversion mode of Quail mode."(interactive"*")(setqunread-command-events(conslast-command-eventunread-command-events));; Check the possibility of translating the last key.(if(and(characterp(event-to-characterlast-command-event))(assq(if(quail-kbd-translate)(quail-keyboard-translate(event-to-characterlast-command-event))(event-to-characterlast-command-event))(cdr(quail-map))));; Ok, we can start translation.(progn(quail-setup-overlayst)(setqquail-current-key"")(setqoverriding-terminal-local-map(quail-translation-keymap))(move-overlayquail-overlay(point)(point)));; Since the last event doesn't start any translation, handle it;; out of Quail mode. We come back to Quail mode later by setting;; function `quail-toggle-mode-temporarily' in;; `post-command-hook'.(add-hook'post-command-hook'quail-toggle-mode-temporarilynilt)))(defsubstquail-delete-region()"Delete the text in the current translation region of Quail."(if(overlay-startquail-overlay)(delete-region(overlay-startquail-overlay)(overlay-endquail-overlay))))(defunquail-terminate-translation()"Terminate the translation of the current key."(when(overlaypquail-overlay)(let((start(overlay-startquail-overlay)))(if(andstart(<start(overlay-endquail-overlay)));; Here we simulate self-insert-command.(let((seq(string-to-sequence(buffer-substring(overlay-startquail-overlay)(overlay-endquail-overlay))'list))last-command-char)(goto-charstart)(quail-delete-region)(setqlast-command-char(carseq))(self-insert-command(orquail-prefix-arg1))(setqquail-prefix-argnil)(setqseq(cdrseq))(whileseq(setqlast-command-char(carseq))(self-insert-command1)(setqseq(cdrseq))))))(delete-overlayquail-overlay))(if(buffer-live-pquail-guidance-buf)(save-excursion(set-bufferquail-guidance-buf)(erase-buffer)))(setqoverriding-terminal-local-map(quail-conversion-keymap));; Run this hook only when the current input method doesn't require;; conversion. When conversion is required, the conversion function;; should run this hook at a proper timing.(unless(quail-conversion-keymap)(run-hooks'input-method-after-insert-chunk-hook)))(defunquail-select-current()"Select the current text shown in Quail translation region."(interactive)(quail-terminate-translation));; Update the current translation status according to CONTROL-FLAG.;; If CONTROL-FLAG is integer value, it is the number of keys in the;; head quail-current-key which can be translated. The remaining keys;; are put back to unread-command-events to be handled again.;; If CONTROL-FLAG is t, terminate the translation for the whole keys;; in quail-current-key.;; If CONTROL-FLAG is nil, proceed the translation with more keys.(defunquail-update-translation(control-flag);; 1997/9/26 Comment outed by MORIOKA Tomohiko;; `overlay' emulation of XEmacs can not represent 0 length region;;(quail-delete-region)(let((func(quail-update-translation-function)))(iffunc(funcallfunccontrol-flag)(let((start(overlay-startquail-overlay))(end(overlay-endquail-overlay)))(if(numberpcontrol-flag)(let((len(lengthquail-current-key)))(while(>lencontrol-flag)(setqlen(1-len))(setqunread-command-events;; 1997/5/26 by MORIOKA Tomohiko;; modified for XEmacs(cons(character-to-event(arefquail-current-keylen))unread-command-events)))(insert(orquail-current-str(substringquail-current-key0len))))(insert(orquail-current-strquail-current-key)))(if(andstartend)(delete-regionstartend)))))(quail-update-guidance)(ifcontrol-flag(quail-terminate-translation)))(defunquail-self-insert-command()"Add the typed character to the key for translation."(interactive"*");; 1997/5/26 by MORIOKA Tomohiko <morioka@jaist.ac.jp>;; modified for XEmacs(setqquail-current-key(concatquail-current-key(char-to-string(event-to-characterlast-command-event))))(unless(catch'quail-tag(quail-update-translation(quail-translate-key))t);; If someone throws for `quail-tag' by value nil, we exit from;; translation mode.(setqoverriding-terminal-local-mapnil)));; Return the actual definition part of Quail map MAP.(defunquail-map-definition(map)(let((def(carmap)))(if(and(conspdef)(not(vectorp(cdrdef))))(setqdef(cardef)))def));; Return a string to be shown as the current translation of key;; sequence of length LEN. DEF is a definition part of Quail map for;; the sequence.(defunquail-get-current-str(lendef)(or(and(conspdef)(aref(cdrdef)(car(cardef))))def(and(>len1)(let((str(quail-get-current-str(1-len)(quail-map-definition(quail-lookup-keyquail-current-key(1-len))))))(ifstr(concat(if(stringpstr)str(char-to-stringstr))(substringquail-current-key(1-len)len)))))))(defvarquail-guidance-translations-starting-column20);; Update `quail-current-translations' to make RELATIVE-INDEX the;; current translation.(defunquail-update-current-translations(&optionalrelative-index)(let*((indices(carquail-current-translations))(cur(carindices))(start(nth1indices))(end(nth2indices)));; Validate the index number of current translation.(if(<cur0)(setcarindices(setqcur0))(if(>=cur(length(cdrquail-current-translations)))(setcarindices(setqcur(1-(length(cdrquail-current-translations)))))))(if(or(nullend); We have not yet calculated END.(<curstart); We moved to the previous block.(>=curend)); We moved to the next block.(let((len(length(cdrquail-current-translations)))(maxcol(-(window-widthquail-guidance-win)quail-guidance-translations-starting-column))(block(nth3indices))colidxwidthtransnum-items)(if(<curstart);; We must calculate from the head.(setqstart0block0)(ifend; i.e. (>= cur end)(setqstartend)))(setqidxstartcol0endstartnum-items0);; Loop until we hit the tail, or reach the block of CUR.(while(and(<idxlen)(>=curend))(if(=num-items0)(setqstartidxcol0block(1+block)))(setqtrans(aref(cdrquail-current-translations)idx))(setqwidth(if(characterptrans)(char-widthtrans)(string-widthtrans)))(setqcol(+colwidth3)num-items(1+num-items))(if(and(>num-items0)(or(>=colmaxcol)(>num-items10)))(setqendidxnum-items0)(setqidx(1+idx))))(setcar(nthcdr3indices)block)(if(>=idxlen)(progn;; We hit the tail before reaching MAXCOL.(setqendidx)(setcar(nthcdr4indices)block)))(setcar(cdrindices)start)(setcar(nthcdr2indices)end)))(ifrelative-index(if(>=(+startrelative-index)end)(setcarindicesend)(setcarindices(+startrelative-index))))(setqquail-current-str(aref(cdrquail-current-translations)(carindices)))))(defunquail-translate-key()"Translate the current key sequence according to the current Quail map.Return t if we can terminate the translation.Return nil if the current key sequence may be followed by more keys.Return number if we can't find any translation for the current keysequence. The number is the count of valid keys in the currentsequence counting from the head."(let*((len(lengthquail-current-key))(map(quail-lookup-keyquail-current-keylen))defch)(ifmap(let((def(quail-map-definitionmap)))(setqquail-current-str(quail-get-current-strlendef));; Return t only if we can terminate the current translation.(and;; No alternative translations.(or(null(conspdef))(=(length(cdrdef))1));; No translation for the longer key.(null(cdrmap));; No shorter breaking point.(or(null(quail-maximum-shortest))(<len3)(null(quail-lookup-keyquail-current-key(1-len)))(null(quail-lookup-key(substringquail-current-key-2-1)1)))));; There's no translation for the current key sequence. Before;; giving up, we must check two possibilities.(cond((and(quail-maximum-shortest)(>=len4)(setqdef(quail-map-definition(quail-lookup-keyquail-current-key(-len2))))(quail-lookup-key(substringquail-current-key-2)2));; Now the sequence is "...ABCD", which can be split into;; "...AB" and "CD..." to get valid translation.;; At first, get translation of "...AB".(setqquail-current-str(quail-get-current-str(-len2)def));; Then, return the length of "...AB".(-len2))((and(>len0)(quail-lookup-key(substringquail-current-key0-1))quail-current-translations(not(quail-deterministic))(setqch(arefquail-current-key(1-len)))(>=ch?0)(<=ch?9));; A numeric key is entered to select a desirable translation.(setqquail-current-key(substringquail-current-key0-1));; We treat key 1,2..,9,0 as specifying 0,1,..8,9.(setqch(if(=ch?0)9(-ch?1)))(quail-update-current-translationsch);; And, we can terminate the current translation.t)(t;; No way to handle the last character in this context.(1-len))))))(defunquail-next-translation()"Select next translation in the current batch of candidates."(interactive)(ifquail-current-translations(let((indices(carquail-current-translations)))(if(=(1+(carindices))(length(cdrquail-current-translations)));; We are already at the tail.(beep)(setcarindices(1+(carindices)))(quail-update-current-translations)(quail-update-translationnil)))(quail-execute-non-quail-command)))(defunquail-prev-translation()"Select previous translation in the current batch of candidates."(interactive)(ifquail-current-translations(let((indices(carquail-current-translations)))(if(=(carindices)0);; We are already at the head.(beep)(setcarindices(1-(carindices)))(quail-update-current-translations)(quail-update-translationnil)))(quail-execute-non-quail-command)))(defunquail-next-translation-block()"Select from the next block of translations."(interactive)(ifquail-current-translations(let*((indices(carquail-current-translations))(offset(-(carindices)(nth1indices))))(if(>=(nth2indices)(length(cdrquail-current-translations)));; We are already at the last block.(beep)(setcarindices(+(nth2indices)offset))(quail-update-current-translations)(quail-update-translationnil)))(quail-execute-non-quail-command)))(defunquail-prev-translation-block()"Select the previous batch of 10 translation candidates."(interactive)(ifquail-current-translations(let*((indices(carquail-current-translations))(offset(-(carindices)(nth1indices))))(if(=(nth1indices)0);; We are already at the first block.(beep)(setcarindices(1-(nth1indices)))(quail-update-current-translations)(if(<(+(nth1indices)offset)(nth2indices))(progn(setcarindices(+(nth1indices)offset))(quail-update-current-translations)))(quail-update-translationnil)))(quail-execute-non-quail-command)))(defunquail-abort-translation()"Abort translation and delete the current Quail key sequence."(interactive)(quail-delete-region)(quail-terminate-translation))(defunquail-delete-backward-char()"Delete the last input character from the current Quail key sequence."(interactive)(if(=(lengthquail-current-key)1)(quail-abort-translation)(setqquail-current-key(substringquail-current-key0-1))(quail-update-translation(quail-translate-key))))(define-compatible-function-alias'quail-delete-last-char'quail-delete-backward-char)(defunquail-backward-or-forward-delete-char(arg)"Delete either one input character backwards or one character forwards.Which direction depends on the variable `delete-key-deletes-forward' andwhether the BackSpace keysym exists on your keyboard. If there is noBackSpace keysym, this function always deletes one character backwards.With ARG (interactively, a prefix argument; see `universal-argument'),delete that number of characters, only if deleting forwards."(interactive"*p")(if(delete-forward-p)(delete-chararg)(quail-delete-backward-char)));; For conversion mode.(defunquail-conversion-backward-char()(interactive)(if(<=(point)(overlay-startquail-conv-overlay))(error"Beginning of conversion region"))(forward-char-1))(defunquail-conversion-forward-char()(interactive)(if(>=(point)(overlay-endquail-conv-overlay))(error"End of conversion region"))(forward-char1))(defunquail-conversion-beginning-of-region()(interactive)(goto-char(overlay-startquail-conv-overlay)))(defunquail-conversion-end-of-region()(interactive)(goto-char(overlay-endquail-conv-overlay)))(defunquail-conversion-delete-char()(interactive)(if(>=(point)(overlay-endquail-conv-overlay))(error"End of conversion region"))(delete-char1)(when(=(overlay-startquail-conv-overlay)(overlay-endquail-conv-overlay))(quail-delete-overlays)(setqoverriding-terminal-local-mapnil)))(defunquail-conversion-backward-delete-char()(interactive)(if(<=(point)(overlay-startquail-conv-overlay))(error"Beginning of conversion region"))(delete-char-1)(when(=(overlay-startquail-conv-overlay)(overlay-endquail-conv-overlay))(quail-delete-overlays)(setqoverriding-terminal-local-mapnil)))(defunquail-do-conversion(func&restargs)"Call FUNC to convert text in the current conversion region of Quail.Remaining args are for FUNC."(delete-overlayquail-overlay)(applyfuncargs))(defunquail-no-conversion()"Do no conversion of the current conversion region of Quail."(interactive)(quail-delete-overlays)(setqoverriding-terminal-local-mapnil)(run-hooks'input-method-after-insert-chunk-hook));; Guidance, Completion, and Help buffer handlers.(defun-when-voidframe-char-height(&optionalframe)"Return the height in pixels of a line of text on FRAME.If FRAME is on a TTY console, return the integer 1."(face-height'default(orframe(selected-frame))));; Make a new one-line frame for Quail guidance buffer.(defunquail-make-guidance-frame(buf)(let*((fparam(frame-parameters))(top(cdr(assq'topfparam)))(border(cdr(assq'border-widthfparam)))(internal-border(cdr(assq'internal-border-widthfparam)))(newtop(-top(frame-char-height)(*internal-border2)(*border2))))(if(<newtop0)(setqnewtop(+top(frame-pixel-height))))(let*((frame(make-frame(append'((user-position.t)(height.1)(minibuffer)(menu-bar-lines.0))(cons(cons'topnewtop)fparam))))(win(frame-first-windowframe)))(set-window-bufferwinbuf);;(set-window-dedicated-p win t))));; Setup Quail completion buffer.(defunquail-setup-completion-buf()(unless(buffer-live-pquail-completion-buf)(setqquail-completion-buf(get-buffer-create"*Quail Completions*"))(save-excursion(set-bufferquail-completion-buf)(setqquail-overlay(make-overlay11))(overlay-putquail-overlay'face'highlight))));; Return t iff the current Quail package requires showing guidance;; buffer.(defunquail-require-guidance-buf()(andinput-method-verbose-flag(not(and(eq(selected-window)(minibuffer-window))(quail-simple)))))(defunquail-show-guidance-buf()"Display a guidance buffer for Quail input method in some window.Create the buffer if it does not exist yet.The buffer is normally displayed at the echo area,but if the current buffer is a minibuffer, it is shown inthe bottom-most ordinary window of the same frame,or in a newly created frame (if the selected frame has no other windows)."(when(quail-require-guidance-buf);; At first, setup a guidance buffer.(or(buffer-live-pquail-guidance-buf)(setqquail-guidance-buf(generate-new-buffer" *Quail-guidance*")))(let((title(quail-title)))(save-excursion(set-bufferquail-guidance-buf);; To show the title of Quail package.(setqcurrent-input-methodtcurrent-input-method-titletitle)(erase-buffer)(or(overlaypquail-overlay)(progn(setqquail-overlay(make-overlay11))(overlay-putquail-overlay'face'highlight)))(delete-overlayquail-overlay)(set-buffer-modified-pnil)))(bury-bufferquail-guidance-buf);; Then, display it in an appropriate window.(let((win(minibuffer-window)))(if(eq(selected-window)win);; Since we are in minibuffer, we can't use it for guidance.(if(eqwin(frame-root-window));; Create a frame. It is sure that we are using some;; window system.(quail-make-guidance-framequail-guidance-buf);; Find the bottom window and split it if necessary.(let(height)(setqwin(frame-lowest-window))(setqheight(window-heightwin));; If WIN is tall enough, split it vertically and use;; the lower one.(if(>=height4)(let((window-min-height2));; Here, `split-window' returns a lower window;; which is what we wanted.(setqwin(split-windowwin(-height2)))))(set-window-bufferwinquail-guidance-buf);;(set-window-dedicated-p win t)))(set-window-bufferwinquail-guidance-buf))(setqquail-guidance-winwin)));; And, create a buffer for completion.(quail-setup-completion-buf)(bury-bufferquail-completion-buf))(defunquail-hide-guidance-buf()"Hide the Quail guidance buffer."(if(buffer-live-pquail-guidance-buf)(let((win-list(get-buffer-window-listquail-guidance-buftt))win)(whilewin-list(setqwin(carwin-list)win-list(cdrwin-list))(if(window-minibuffer-pwin);; We are using echo area for the guidance buffer.;; Vacate it to the deepest minibuffer.(set-window-bufferwin(format" *Minibuf-%d*"(minibuffer-depth)))(if(eqwin(frame-root-window(window-framewin)))(progn;; We are using a separate frame for guidance buffer.;;(set-window-dedicated-p win nil)(delete-frame(window-framewin)));;(set-window-dedicated-p win nil)(delete-windowwin)))))))(defunquail-update-guidance()"Update the Quail guidance buffer and completion buffer (if displayed now).";; Update guidance buffer.(if(quail-require-guidance-buf)(let((guidance(quail-guidance)))(cond((or(eqguidancet)(listpguidance));; Show the current possible translations.(quail-show-translations))((nullguidance);; Show the current input keys.(let((keyquail-current-key))(save-excursion(set-bufferquail-guidance-buf)(erase-buffer)(insertkey)))))));; Update completion buffer if displayed now. We highlight the;; selected candidate string in *Completion* buffer if any.(let((win(get-buffer-windowquail-completion-buf))keystrpos)(ifwin(save-excursion(setqstr(if(stringpquail-current-str)quail-current-str(if(characterpquail-current-str)(char-to-stringquail-current-str)))keyquail-current-key)(set-bufferquail-completion-buf)(goto-char(point-min))(if(null(search-forward(concat" "key":")nilt))(delete-overlayquail-overlay)(setqpos(point))(if(andstr(search-forward(concat"."str)nilt))(move-overlayquail-overlay(1+(match-beginning0))(point))(move-overlayquail-overlay(match-beginning0)(point)));; Now POS points end of KEY and (point) points end of STR.(if(pos-visible-in-window-p(point)win);; STR is already visible.nil;; We want to make both KEY and STR visible, but if the;; window is too short, make at least STR visible.(setqpos(progn(point)(goto-charpos)))(beginning-of-line)(set-window-startwin(point))(if(not(pos-visible-in-window-pposwin))(set-window-startwinpos))))))))(defunquail-show-translations()"Show the current possible translations."(let*((keyquail-current-key)(map(quail-lookup-keyquail-current-key)))(ifquail-current-translations(quail-update-current-translations))(save-excursion(set-bufferquail-guidance-buf)(erase-buffer);; Show the current key.(let((guidance(quail-guidance)))(if(listpguidance);; We must show the specified PROMPTKEY instead of the;; actual typed keys.(let((i0)(len(lengthkey))prompt-key)(while(<ilen)(setqprompt-key(cdr(assoc(arefkeyi)guidance)))(insert(orprompt-key(arefkeyi)))(setqi(1+i))))(insertkey)));; Show followable keys.(if(cdrmap)(let((l(cdrmap)))(insert"[")(whilel(insert(car(carl)))(setql(cdrl)))(insert"]")));; Show list of translations.(ifquail-current-translations(let*((indices(carquail-current-translations))(cur(carindices))(start(nth1indices))(end(nth2indices))(idxstart))(indent-to(-quail-guidance-translations-starting-column7))(insert(format"(%02d/"(nth3indices))(if(nth4indices)(format"%02d)"(nth4indices))"??)"))(while(<idxend)(insert(format" %d."(if(=(-idxstart)9)0(1+(-idxstart)))))(let((pos(point)))(insert(aref(cdrquail-current-translations)idx))(if(and(overlaypquail-overlay)(=idxcur))(move-overlayquail-overlaypos(point))))(setqidx(1+idx))))))))(defunquail-completion()"List all completions for the current key.All possible translations of the current key and whole possible longer keys are shown."(interactive)(quail-setup-completion-buf)(let((keyquail-current-key)(map(quail-lookup-keyquail-current-key)))(save-excursion(set-bufferquail-completion-buf)(erase-buffer)(insert"Possible completion and corresponding translations are:\n")(quail-completion-1keymap1)(goto-char(point-min))(display-buffer(current-buffer)))(quail-update-guidance)));; List all completions of KEY in MAP with indentation INDENT.(defunquail-completion-1(keymapindent)(let((len(lengthkey)))(indent-toindent)(insertkey":")(if(and(symbolpmap)(fboundpmap))(setqmap(funcallmapkeylen)))(if(carmap)(quail-completion-list-translationsmapkey(+indentlen1))(insert" -\n"))(setqindent(+indent2))(if(cdrmap)(let((l(cdrmap))(newkey(make-string(1+len)0))(i0));; Set KEY in the first LEN characters of NEWKEY.(while(<ilen)(asetnewkeyi(arefkeyi))(setqi(1+i)))(whilel; L = ((CHAR . DEFN) ....) ;(asetnewkeylen(car(carl)))(quail-completion-1newkey(cdr(carl))indent)(setql(cdrl)))))));; List all possible translations of KEY in Quail map MAP with;; indentation INDENT.(defunquail-completion-list-translations(mapkeyindent)(let((translations(quail-get-translation(carmap)key(lengthkey))))(if(characterptranslations)(insert"(1/1) 1."translations"\n");; We need only vector part.(setqtranslations(cdrtranslations));; Insert every 10 elements with indices in a line.(let((len(lengthtranslations))(i0))(while(<ilen)(when(zerop(%i10))(when(>=i10)(newline)(indent-toindent))(insert(format"(%d/%d)"(1+(/i10))(1+(/len10)))));; We show the last digit of FROM while converting;; 0,1,..,9 to 1,2,..,0.(insert(format" %d."(%(1+i)10)))(insert(areftranslationsi))(setqi(1+i)))(newline)))))(defunquail-help()"Show brief description of the current Quail package."(interactive)(let((packagequail-current-package))(with-output-to-temp-buffer"*Quail-Help*"(save-excursion(set-bufferstandard-output)(let((quail-current-packagepackage))(insert"Quail input method (name:"(quail-name)", mode line indicator:["(quail-title)"])\n---- Documentation ----\n"(quail-docstring))(newline)(if(quail-show-layout)(quail-show-kbd-layout))(quail-help-insert-keymap-descriptionquail-mode-map"---- Key bindings (before starting translation) ----key binding--- -------\n")(quail-help-insert-keymap-description(quail-translation-keymap)"--- Key bindings (while translating) ---key binding--- -------\n")(if(quail-conversion-keymap)(quail-help-insert-keymap-description(quail-conversion-keymap)"--- Key bindings (while converting) ---key binding--- -------\n"))(help-mode))))))(defunquail-help-insert-keymap-description(keymap&optionalheader)(let(fromto)(ifheader(insertheader))(save-excursion(save-window-excursion(let((overriding-terminal-local-mapkeymap))(describe-bindings))(set-buffer"*Help*")(goto-char(point-min))(forward-line4)(setqfrom(point))(search-forward"Global Bindings:"nil'move)(beginning-of-line)(setqto(point))))(insert-buffer-substring"*Help*"fromto)))(defunquail-show-kbd-layout()"Show keyboard layout with key tops of multilingual characters."(insert"--- Keyboard layout ---\n")(let*((i0)ch)(while(<iquail-keyboard-layout-len)(if(=(%i30)0)(progn(newline)(indent-to(/i30)))(if(=(%i2)0)(insert" ")))(setqch(arefquail-keyboard-layouti))(when(and(quail-kbd-translate)(/=ch?\ ));; This is the case that the current input method simulates;; some keyboard layout (which means it requires keyboard;; translation) and a key at location `i' exists on users;; keyboard. We must translate that key by;; `quail-keyboard-layout-standard'. But if if there's no;; corresponding key in that standard layout, we must simulate;; what is inserted if that key is pressed by setting CH a;; minus value.(setqch(arefquail-keyboard-layout-standardi))(if(=ch?\ )(setqch(-(arefquail-keyboard-layouti)))))(if(<ch0)(let((last-command-event(character-to-event(-ch))))(self-insert-command1))(if(=ch?\ )(insertch)(let*((map(cdr(assqch(cdr(quail-map)))))(translation(andmap(quail-get-translation(carmap)(char-to-stringch)1))))(if(characterptranslation)(inserttranslation)(if(consptranslation)(insert(aref(cdrtranslation)(cartranslation)))(let((last-command-event(character-to-eventch)))(self-insert-command1)))))))(setqi(1+i))))(newline))(defunquail-translation-help()"Show help message while translating in Quail mode."(interactive)(let((packagequail-current-package)(current-keyquail-current-key))(with-output-to-temp-buffer"*Quail-Help*"(save-excursion(set-bufferstandard-output)(let((quail-current-packagepackage))(princ"You are translating the key sequence ")(prin1current-key)(princ" in Quail mode.\n")(quail-help-insert-keymap-description(quail-translation-keymap)"-----------------------key binding--- -------\n"))(help-mode)))))(defunquail-conversion-help()"Show help message while converting in Quail mode."(interactive)(let((packagequail-current-package)(str(buffer-substring(overlay-startquail-conv-overlay)(overlay-endquail-conv-overlay))))(with-output-to-temp-buffer"*Quail-Help*"(save-excursion(set-bufferstandard-output)(let((quail-current-packagepackage))(princ"You are converting the string ")(prin1str)(princ" in Quail mode.\n")(quail-help-insert-keymap-description(quail-conversion-keymap)"-----------------------key binding--- -------\n"))(help-mode)))));; Quail map generator from state transition table.(defunquail-map-from-table(table)"Make quail map from state transition table TABLE.TABLE is an alist, the form is: ((STATE-0 TRANSITION-0-1 TRANSITION-0-2 ...) (STATE-1 ...) ...)STATE-n are symbols to denote state. STATE-0 is the initial state.TRANSITION-n-m are transition rules from STATE-n, and have the form\(RULES . STATE-x) or RULES, where STATE-x is one of STATE-n above,RULES is a symbol whose value is an alist of keys \(string) vs thecorreponding characters or strings. The format of the symbol value ofRULES is the same as arguments to `quail-define-rules'.If TRANSITION-n-m has the form (RULES . STATE-x), it means thatSTATE-n transits to STATE-x when keys in RULES are input. Recursivetransition is allowed, i.e. STATE-x may be STATE-n.If TRANSITION-n-m has the form RULES, the transition terminateswhen keys in RULES are input.The generated map can be set for the current Quail package by thefunction `quail-install-map' (which see)."(let((state-alist(mapcar(lambda(x)(list(carx)))table))tailelt);; STATE-ALIST is an alist of states vs the correponding sub Quail;; map. It is now initialized to ((STATE-0) (STATE-1) ...).;; Set key sequence mapping rules in cdr part of each element.(whiletable(quail-map-from-table-1state-alist(cartable))(setqtable(cdrtable)));; Now STATE-ALIST has the form ((STATE-0 MAPPING-RULES) ...).;; Elements of MAPPING-RULES may have the form (STATE-x). Replace;; them with MAPPING-RULES of STATE-x to make elements of;; STATE-ALIST valid Quail maps.(setqtailstate-alist)(whiletail(setqelt(cartail)tail(cdrtail))(quail-map-from-table-2state-alistelt));; Return the Quail map for the initial state.(carstate-alist)));; STATE-INFO has the form (STATE TRANSITION ...). Set key sequence;; mapping rules in the element of STATE-ALIST that corresponds to;; STATE according to TRANSITION ...(defunquail-map-from-table-1(state-aliststate-info)(let*((state(carstate-info))(map(assqstatestate-alist))(transitions(cdrstate-info))elt)(whiletransitions(setqelt(cartransitions)transitions(cdrtransitions))(let(rulesdst-statekeytrans);; ELT has the form (RULES-SYMBOL . STATE-x) or RULES-SYMBOL.;; STATE-x is one of car parts of STATE-ALIST's elements.(if(conspelt)(setqrules(symbol-value(carelt));; Set (STATE-x) as branches for all keys in RULES.;; It is replaced with actual branches for STATE-x;; later in `quail-map-from-table-2'.dst-state(list(cdrelt)))(setqrules(symbol-valueelt)))(whilerules(setqkey(car(carrules))trans(cdr(carrules))rules(cdrrules))(if(stringptrans)(if(=(lengthtrans)1)(setqtrans(areftrans0))(setqtrans(string-to-vectortrans))))(set-nested-alistkeytransmapnildst-state))))));; ELEMENT is one element of STATE-ALIST. ELEMENT is a nested alist;;; the form is:;; (STATE (CHAR NESTED-ALIST) ...);; NESTED-ALIST is a nested alist; the form is:;; (TRANS (CHAR NESTED-ALIST) ...);; or;; (TRANS (CHAR NESTED-ALIST) ... . (STATE-x));; Here, the task is to replace all occurrences of (STATE-x) with:;; (cdr (assq STATE-x STATE-ALIST))(defunquail-map-from-table-2(state-alistelement)(let((prevelement)(tail(cdrelement))elt)(while(cdrtail)(setqelt(cartail)prevtailtail(cdrtail))(quail-map-from-table-2state-alist(cdrelt)))(setqelt(cartail))(if(conspelt)(quail-map-from-table-2state-alist(cdrelt))(setcdrprev(cdr(assqeltstate-alist))))));; Concatenate translations for all heading substrings of KEY in the;; current Quail map. Here, `heading substring' means (substring KEY;; 0 LEN), where LEN is 1, 2, ... (length KEY).(defunquail-lookup-map-and-concat(key)(let*((len(lengthkey))(translation-listnil)map)(while(>len0)(setqmap(quail-lookup-keykeylent)len(1-len))(ifmap(let*((def(quail-map-definitionmap))(trans(if(conspdef)(aref(cdrdef)(car(cardef)))def)))(if(integerptrans)(setqtrans(char-to-stringtrans)))(setqtranslation-list(constranstranslation-list)))))(apply'concattranslation-list)))(defvarquail-directory-name"quail""Name of Quail directory which contains Quail packages.This is a sub-directory of LEIM directory.");;;###autoload(defunquail-update-leim-list-file(dirname&restdirnames)"Update entries for Quail packages in `LEIM' list file in directory DIRNAME.DIRNAME is a directory containing Emacs input methods;normally, it should specify the `leim' subdirectoryof the Emacs source tree.It searches for Quail packages under `quail' subdirectory of DIRNAME,and update the file \"leim-list.el\" in DIRNAME.When called from a program, the remaining arguments are additionaldirectory names to search for Quail packages under `quail' subdirectoryof each directory."(interactive"FDirectory of LEIM: ")(setqdirname(expand-file-namedirname))(let((leim-list(expand-file-nameleim-list-file-namedirname))quail-dirslist-bufpkg-listpos)(if(not(file-writable-pleim-list))(error"Can't write to file \"%s\""leim-list))(message"Updating %s ..."leim-list)(setqlist-buf(find-file-noselectleim-list));; At first, clean up the file.(save-excursion(set-bufferlist-buf)(goto-char1);; Insert the correct header.(if(looking-at(regexp-quoteleim-list-header))(goto-char(match-end0))(insertleim-list-header))(setqpos(point))(if(not(re-search-forwardleim-list-entry-regexpnilt))nil;; Remove garbages after the header.(goto-char(match-beginning0))(if(<pos(point))(delete-regionpos(point)));; Remove all entries for Quail.(while(re-search-forwardleim-list-entry-regexpnil'move)(goto-char(match-beginning0))(setqpos(point))(condition-casenil(let((form(readlist-buf)))(when(equal(nth3form)''quail-use-package)(if(eolp)(forward-line1))(delete-regionpos(point))))(error;; Delete the remaining contents because it seems that;; this file is broken.(message"Garbages in %s deleted"leim-list)(delete-regionpos(point-max)))))));; Search for `quail' subdirector under each DIRNAMES.(setqdirnames(consdirnamedirnames))(let((ldirnames))(whilel(setcarl(expand-file-name(carl)))(setqdirname(expand-file-namequail-directory-name(carl)))(if(file-readable-pdirname)(setqquail-dirs(consdirnamequail-dirs))(message"%s doesn't has `%s' subdirectory, just ignored"(carl)quail-directory-name)(setqquail-dirs(consnilquail-dirs)))(setql(cdrl)))(setqquail-dirs(nreversequail-dirs)));; Insert input method registering forms.(whilequail-dirs(setqdirname(carquail-dirs))(whendirname(setqpkg-list(directory-filesdirname'full"\\.el$"'nosort))(whilepkg-list(message"Checking %s ..."(carpkg-list))(with-temp-buffer(insert-file-contents(carpkg-list))(goto-char(point-min))(while(search-forward"(quail-define-package"nilt)(goto-char(match-beginning0))(condition-casenil(let((form(read(current-buffer))))(save-excursion(set-bufferlist-buf)(insert(format"(register-input-method %S %S '%s %S %S %S)\n"(nth1form); PACKAGE-NAME(nth2form); LANGUAGE'quail-use-package; ACTIVATE-FUNC(nth3form); PACKAGE-TITLE(progn; PACKAGE-DESCRIPTION (one line)(string-match".*"(nth5form))(match-string0(nth5form)))(file-relative-name; PACKAGE-FILENAME(file-name-sans-extension(carpkg-list))(cardirnames))))))(error;; Ignore the remaining contents of this file.(goto-char(point-max))(message"Some part of \"%s\" is broken"dirname)))))(setqpkg-list(cdrpkg-list)))(setqquail-dirs(cdrquail-dirs)dirnames(cdrdirnames))));; At last, write out LEIM list file.(save-excursion(set-bufferlist-buf)(setqbuffer-file-coding-system'iso-2022-7bit)(save-buffer0))(kill-bufferlist-buf)(message"Updating %s ... done"leim-list)));;(provide'quail);;; quail.el ends here