From 95109387d74c66fe02402bddb3fb5009b0550919 Mon Sep 17 00:00:00 2001 From: Kenichi Handa Date: Thu, 7 Sep 2000 02:38:46 +0000 Subject: [PATCH] Don't require face. (quail): New group. (quail-other-command): Dummy command to make quail-help works better. (quail-keyboard-layout-alist): Add Keyboard type "jp106". (quail-keyboard-layout-substitution): New variable. (quail-update-keyboard-layout): New function. (quail-keyboard-layout-type): New customizable variable. (quail-set-keyboard-layout): Call quail-update-keyboard-layout. (quail-keyboard-translate): Pay attention to quail-keyboard-layout-substitution. (quail-insert-kbd-layout): New function. (quail-show-keyboard-layout): New function. (quail-get-translation): If the definition is a vector of length 1, and the element is a string of lenght 1, return the character in that string. (quail-update-current-translations): Fix the case of relative-index out of range. (quail-build-decode-map, quail-insert-decode-map): New Functions. (quail-help): Show keyboard layout by quail-insert-kbd-layout. Show key sequences for all avairable characters. (quail-help-insert-keymap-description): Don't show such verbose key bindings as quail-self-insert-command. --- lisp/international/quail.el | 525 +++++++++++++++++++++++++++++------- 1 file changed, 430 insertions(+), 95 deletions(-) diff --git a/lisp/international/quail.el b/lisp/international/quail.el index 710f8c87d54..22e24f3fa37 100644 --- a/lisp/international/quail.el +++ b/lisp/international/quail.el @@ -43,7 +43,9 @@ ;;; Code: -(require 'faces) +(defgroup quail nil + "Quail: multilingual input method." + :group 'leim) ;; Buffer local variables @@ -270,6 +272,15 @@ 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.") +;; Hide some verbose commands to make the output of quail-help +;; concise. +(let ((l '(quail-other-command + quail-self-insert-command + quail-delete-last-char))) + (while l + (put (car l) 'quail-help-hide t) + (setq l (cdr l)))) + (defvar quail-simple-translation-keymap (let ((map (make-keymap)) (i 0)) @@ -319,6 +330,11 @@ This map is activated while translation region is active.") This map is activated while conversion region is active but translation region is not active.") +;; Just a dummy definition. +(defun quail-other-command () + (interactive) + ) + ;;;###autoload (defun quail-define-package (name language title &optional guidance docstring translation-keys @@ -562,6 +578,7 @@ The command `quail-set-keyboard-layout' usually sets this variable.") (defvar quail-keyboard-layout-alist (list + (cons "standard" quail-keyboard-layout-standard) '("sun-type3" . "\ \ 1!2@3#4$5%6^7&8*9(0)-_=+\\|`~\ @@ -585,10 +602,61 @@ The command `quail-set-keyboard-layout' usually sets this variable.") <>yYxXcCvVbBnNmM,;.:-_ \ ") - (cons "standard" quail-keyboard-layout-standard)) + '("jp106" . "\ + \ + 1!2\"3#4$5%6&7'8(9)0~-=^~\\| \ + qQwWeErRtTyYuUiIoOpP@`[{ \ + aAsSdDfFgGhHjJkKlL;+:*]} \ + zZxXcCvVbBnNmM,<.>/?\\_ \ + ") + ) "Alist of keyboard names and corresponding layout strings. See the documentation of `quail-keyboard-layout' for the format of - the layout string.") +the layout string.") + +;; A non-standard keyboard layout may miss some key locations of the +;; standard layout while having additional key locations not in the +;; standard layout. This alist maps those additional key locations to +;; the missing locations. The value is updated automatically by +;; quail-set-keyboard-layout. +(defvar quail-keyboard-layout-substitution nil) + +(defun quail-update-keyboard-layout (kbd-type) + (let ((layout (assoc kbd-type quail-keyboard-layout-alist))) + (if (null layout) + ;; Here, we had better ask a user to define his own keyboard + ;; layout interactively. + (error "Unknown keyboard type `%s'" kbd-type)) + (setq quail-keyboard-layout (cdr layout)) + (let ((i quail-keyboard-layout-len) + subst-list missing-list) + ;; Sum up additional key locations not in the standard layout in + ;; subst-list, and missing key locations in missing-list. + (while (> i 0) + (setq i (1- i)) + (if (= (aref quail-keyboard-layout i) ? ) + (if (/= (aref quail-keyboard-layout-standard i) ? ) + (setq missing-list (cons i missing-list))) + (if (= (aref quail-keyboard-layout-standard i) ? ) + (setq subst-list (cons (cons i nil) subst-list))))) + (setq quail-keyboard-layout-substitution subst-list) + ;; If there are additional key locations, map them to missing + ;; key locations. + (while missing-list + (while (and subst-list (cdr (car subst-list))) + (setq subst-list (cdr subst-list))) + (if subst-list + (setcdr (car subst-list) (car missing-list))) + (setq missing-list (cdr missing-list)))))) + +(defcustom quail-keyboard-layout-type "standard" + "Type of keyboard layout used in Quail base input method. +Available types are listed in the variable `quail-keyboard-layout-alist'." + :group 'quail + :type 'string + :set #'(lambda (symbol value) + (quail-update-keyboard-layout value) + (set symbol value))) ;;;###autoload (defun quail-set-keyboard-layout (kbd-type) @@ -604,36 +672,166 @@ you type is correctly handled." (type (completing-read "Keyboard type: " quail-keyboard-layout-alist))) (list type))) - (let ((layout (assoc kbd-type quail-keyboard-layout-alist))) - (if (null layout) - ;; Here, we had better ask a user to define his own keyboard - ;; layout interactively. - (error "Unknown keyboard type `%s'" kbd-type)) - (setq quail-keyboard-layout (cdr layout)))) + (quail-update-keyboard-layout kbd-type) + (setq quail-keyboard-layout-type kbd-type)) -(defun quail-keyboard-translate (ch) - "Translate CHAR according to `quail-keyboard-layout' and return the result." +(defun quail-keyboard-translate (char) + "Translate CHAR to the one in the standard keyboard layout." (if (eq quail-keyboard-layout quail-keyboard-layout-standard) ;; All Quail packages are designed based on ;; `quail-keyboard-layout-standard'. - ch + char (let ((i 0)) + ;; Find the key location on the current keyboard layout. (while (and (< i quail-keyboard-layout-len) - (/= ch (aref quail-keyboard-layout i))) + (/= char (aref quail-keyboard-layout i))) (setq i (1+ i))) (if (= i quail-keyboard-layout-len) - ;; CH is not in quail-keyboard-layout, which means that a + ;; CHAR 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 + ;; handled out of Quail. Just return CHAR and make ;; quail-execute-non-quail-command handle it correctly. - ch - (let ((char (aref quail-keyboard-layout-standard i))) - (if (= char ?\ ) - ;; A user typed a key at the location not converted by - ;; quail-keyboard-layout-standard. Just return CH as - ;; well as above. - ch - char)))))) + char + (let ((ch (aref quail-keyboard-layout-standard i))) + (if (= ch ?\ ) + ;; This location not available in the standard keyboard + ;; layout. Check if the location is used to substitute + ;; for the other location of the standard layout. + (if (setq i (cdr (assq i quail-keyboard-layout-substitution))) + (aref quail-keyboard-layout-standard i) + ;; Just return CHAR as well as above. + char) + ch)))))) + +;; Insert the visual keyboard layout table according to KBD-LAYOUT. +;; The format of KBD-LAYOUT is the same as `quail-keyboard-layout'. +(defun quail-insert-kbd-layout (kbd-layout) + (let (done-list layout i ch) + ;; At first, convert KBD-LAYOUT to the same size vector that + ;; contains translated character or string. + (setq layout (string-to-vector kbd-layout) + i 0) + (while (< i quail-keyboard-layout-len) + (setq ch (aref kbd-layout i)) + (if (quail-kbd-translate) + (setq ch (quail-keyboard-translate ch))) + (let* ((map (cdr (assq ch (cdr (quail-map))))) + (translation (and map (quail-get-translation + (car map) (char-to-string ch) 1)))) + (if translation + (progn + (if (consp translation) + (setq translation (aref (cdr translation) 0))) + (setq done-list (cons translation done-list))) + (setq translation ch)) + (aset layout i translation)) + (setq i (1+ i))) + + (let ((pos (point)) + (bar "|") + lower upper row) + ;; Make table without horizontal lines. Each column for a key + ;; has the form "| LU |" where L is for lower key and and U is + ;; for a upper key. If width of L (U) is greater than 1, + ;; preceding (following) space is not inserted. + (put-text-property 0 1 'face 'bold bar) + (setq i 0) + (while (< i quail-keyboard-layout-len) + (when (= (% i 30) 0) + (setq row (/ i 30)) + (if (> row 1) + (insert-char 32 (+ row (/ (- row 2) 2))))) + (setq lower (aref layout i) + upper (aref layout (1+ i))) + (if (and (integerp lower) (>= lower 128) (< lower 256)) + (setq lower (unibyte-char-to-multibyte lower))) + (if (and (integerp upper) (>= upper 128) (< upper 256)) + (setq upper (unibyte-char-to-multibyte upper))) + (insert bar) + (if (= (if (stringp lower) (string-width lower) (char-width lower)) 1) + (insert " ")) + (insert lower upper) + (if (= (if (stringp upper) (string-width upper) (char-width upper)) 1) + (insert " ")) + (setq i (+ i 2)) + (if (= (% i 30) 0) + (insert bar "\n"))) + ;; Insert horizontal lines while deleting blank key columns at the + ;; beginning and end of each line. + (save-restriction + (narrow-to-region pos (point)) + (goto-char pos) + ;;(while (looking-at "[| ]*$") + ;;(forward-line 1) + ;;(delete-region pos (point))) + (let ((from1 100) (to1 0) from2 to2) + (while (not (eobp)) + (if (looking-at "[| ]*$") + ;; The entire row is blank. + (delete-region (point) (match-end 0)) + ;; Delete blank key columns at the head. + (if (looking-at " *\\(| \\)+") + (subst-char-in-region (point) (match-end 0) ?| ? )) + ;; Delete blank key columns at the tail. + (if (re-search-forward "\\( |\\)+$" (line-end-position) t) + (delete-region (match-beginning 0) (point))) + (beginning-of-line)) + ;; Calculate the start and end columns of a horizontal line. + (if (eolp) + (setq from2 from1 to2 to1) + (skip-chars-forward " ") + (setq from2 (current-column)) + (end-of-line) + (setq to2 (current-column)) + (if (< from2 from1) + (setq from1 from2)) + (if (> to2 to1) + (setq to1 to2)) + (beginning-of-line)) + ;; If the previous or the current line has at least one key + ;; column, insert a horizontal line. + (when (> to1 0) + (insert-char 32 from1) + (setq pos (point)) + (insert "+") + (insert-char ?- (- (- to1 from1) 2)) + (insert "+") + (put-text-property pos (point) 'face 'bold) + (insert "\n")) + (setq from1 from2 to1 to2) + (forward-line 1))) + ;; Insert "space bar" box. + (forward-line -1) + (setq pos (point)) + (insert +" +-----------------------------+ + | space bar | + +-----------------------------+ +") + (put-text-property pos (point) 'face 'bold) + (insert ?\n))) + + done-list)) + +;;;###autoload +(defun quail-show-keyboard-layout (&optional keyboard-type) + "Show keyboard layout." + (interactive + (list (completing-read "Keyboard type (default, current choise): " + quail-keyboard-layout-alist + nil t))) + (or (and keyboard-type (> (length keyboard-type) 0)) + (setq keyboard-type quail-keyboard-layout-type)) + (let ((layout (assoc keyboard-type quail-keyboard-layout-alist))) + (or layout + (error "Unknown keyboard type: %s" keyboard-type)) + (with-output-to-temp-buffer "*Help*" + (save-excursion + (set-buffer standard-output) + (insert "Keyboard layout (keyboard type: " + keyboard-type + ")\n") + (quail-insert-kbd-layout (cdr layout)))))) ;; Quail map @@ -921,19 +1119,23 @@ selected translation." nil) ((stringp def) - ;; Each character in DEF is a candidate of translation. Reform - ;; it as (INDICES . VECTOR). - (setq def (string-to-vector def)) - ;; But if the length is 1, we don't need vector but a single - ;; candidate as the translation. + ;; If the length is 1, we don't need vector but a single candidate + ;; as the translation. (if (= (length def) 1) (aref def 0) - (cons (list 0 0 0 0 nil) def))) + ;; Each character in DEF is a candidate of translation. Reform + ;; it as (INDICES . VECTOR). + (cons (list 0 0 0 0 nil) (string-to-vector def)))) ((vectorp def) - ;; Each element (string or character) in DEF is a candidate of - ;; translation. Reform it as (INDICES . VECTOR). - (cons (list 0 0 0 0 nil) def)) + ;; If the length is 1, and the length of element string is 1, we + ;; don't need vector but a single candidate as the translation. + (if (and (= (length def) 1) + (= (length (aref def 0)) 1)) + (aref (aref def 0) 0) + ;; Each element (string or character) in DEF is a candidate of + ;; translation. Reform it as (INDICES . VECTOR). + (cons (list 0 0 0 0 nil) def))) (t (error "Invalid object in Quail map: %s" def)))) @@ -1346,7 +1548,7 @@ The returned value is a Quail map specific to KEY." (setcar (nthcdr 2 indices) end))) (if relative-index (if (>= (+ start relative-index) end) - (setcar indices end) + (setcar indices (1- end)) (setcar indices (+ start relative-index)))) (setq quail-current-str (aref (cdr quail-current-translations) (car indices))) @@ -1992,92 +2194,225 @@ are shown (at most to the depth specified `quail-completion-max-depth')." (select-window (active-minibuffer-window)) (exit-minibuffer)))))) +(defun quail-build-decode-map (map key decode-map num &optional maxnum ignores) + (let ((translation (quail-get-translation (car map) key (length key))) + elt) + (cond ((integerp translation) + (when (and (> translation 255) (not (memq translation ignores))) + (setcdr decode-map + (cons (cons key translation) (cdr decode-map))) + (setq num (1+ num)))) + ((consp translation) + (setq translation (cdr translation)) + (let ((multibyte nil)) + (mapc (function (lambda (x) + (if (and (if (integerp x) (> x 255) + (> (string-bytes x) (length x))) + (not (member x ignores))) + (setq multibyte t)))) + translation) + (when multibyte + (setcdr decode-map + (cons (cons key translation) (cdr decode-map))) + (setq num (+ num (length translation))))))) + (if (and maxnum (> num maxnum)) + (- num) + (setq map (cdr map)) + (while (and map (>= num 0)) + (setq elt (car map) map (cdr map)) + (when (and (integerp (car elt)) (consp (cdr elt))) + (setq num (quail-build-decode-map (cdr elt) + (format "%s%c" key (car elt)) + decode-map num maxnum ignores)))) + num))) + +(defun quail-insert-decode-map (decode-map) + (setq decode-map + (sort (cdr decode-map) + (function (lambda (x y) + (setq x (car x) y (car y)) + (or (> (length x) (length y)) + (and (= (length x) (length y)) + (not (string< x y)))))))) + (let ((frame-width (frame-width)) + (short-key-width 3) + (short-trans-width 4) + (long-key-width 3) + (short-list nil) + (long-list nil) + elt trans width pos cols rows col row str col-width) + ;; Divide the decoding map into shorter one and longer one. + (while decode-map + (setq elt (car decode-map) decode-map (cdr decode-map) + trans (cdr elt)) + (if (and (vectorp trans) (= (length trans) 1)) + (setq trans (aref trans 0))) + (if (vectorp trans) + (setq long-list (cons elt long-list)) + (setq short-list (cons (cons (car elt) trans) short-list) + width (if (stringp trans) (string-width trans) + (char-width trans))) + (if (> width short-trans-width) + (setq short-trans-width width))) + (setq width (length (car elt))) + (if (> width short-key-width) + (setq short-key-width width)) + (if (> width long-key-width) + (setq long-key-width width))) + (when short-list + (setq col-width (+ short-key-width 1 short-trans-width 1) + cols (/ frame-width col-width) + rows (/ (length short-list) cols)) + (if (> (% (length short-list) cols) 0) + (setq rows (1+ rows))) + (insert "key") + (indent-to (1+ short-key-width)) + (insert "char") + (indent-to (1+ col-width)) + (insert "[type a key sequence to insert the corresponding character]\n") + (setq pos (point)) + (insert-char ?\n (+ rows 2)) + (goto-char pos) + (setq col (- col-width) row 0) + (while short-list + (setq elt (car short-list) short-list (cdr short-list)) + (when (= (% row rows) 0) + (goto-char pos) + (setq col (+ col col-width)) + (move-to-column col t) + (insert-char ?- short-key-width) + (insert ? ) + (insert-char ?- short-trans-width) + (forward-line 1)) + (move-to-column col t) + (insert (car elt)) + (indent-to (+ col short-key-width 1)) + (insert (cdr elt)) + (forward-line 1) + (setq row (1+ row))) + (goto-char (point-max))) + + (when long-list + (insert "key") + (indent-to (1+ long-key-width)) + (insert "character(s) [type a key (sequence) and select one from the list]\n") + (insert-char ?- long-key-width) + (insert " ------------\n") + (while long-list + (setq elt (car long-list) long-list (cdr long-list)) + (insert (car elt)) + (indent-to long-key-width) + (if (vectorp (cdr elt)) + (mapc (function + (lambda (x) + (let ((width (if (integerp x) (char-width x) + (string-width x)))) + (when (> (+ (current-column) 1 width) frame-width) + (insert "\n") + (indent-to long-key-width)) + (insert " " x)))) + (cdr elt)) + (insert " " (cdr elt))) + (insert ?\n)) + (insert ?\n)))) + (defun quail-help (&optional package) "Show brief description of the current Quail package. Optional 2nd arg PACKAGE specifies the alternative Quail package to describe." (interactive) - (or package - (setq package quail-current-package)) + (if package + (setq package (assoc package quail-package-alist)) + (setq package quail-current-package)) (let ((help-xref-mule-regexp help-xref-mule-regexp-template)) (with-output-to-temp-buffer "*Help*" (save-excursion (set-buffer standard-output) (setq quail-current-package package) - (insert "Quail input method (name:" - (quail-name) - ", mode line indicator:[" + (insert "Input method: " (quail-name) + " (mode line indicator:" (quail-title) - "])\n\n---- Documentation ----\n" + ")\n\n" (quail-docstring)) - (newline) - (if (quail-show-layout) (quail-show-kbd-layout)) + (or (bolp) + (insert "\n")) + (insert "\n") + + (let ((done-list nil)) + ;; Show keyboard layout if the current package requests it.. + (when (quail-show-layout) + (insert +"Physical key layout for this input method is as below. +You can input a character in the table by typing a key +at the same location on your keyboard.\n") + (setq done-list + (quail-insert-kbd-layout quail-keyboard-layout)) + (insert "It is assumed that your keyboard type is `") + (help-insert-xref-button + quail-keyboard-layout-type + #'quail-show-keyboard-layout quail-keyboard-layout-type + "mouse-2, RET: show this layout") + (insert "'. +If the layout is different from your keyboard, or you see the +different characters when you type keys according to this layout, +adjust the variable `quail-keyboard-layout-type' ") + (help-insert-xref-button + "[customize it]" + #'customize-variable 'quail-keyboard-layout-type + "mouse-2, RET: set keyboard layout type") + (insert ".\n")) + + ;; Show key sequences. + (let ((decode-map (list 'decode-map)) + elt pos num) + (setq num (quail-build-decode-map (quail-map) "" decode-map + 0 512 done-list)) + (when (> num 0) + (insert ?\n) + (if (quail-show-layout) + (insert "You can also input more characters") + (insert "You can input characters")) + (insert " by the following key sequences:\n") + (quail-insert-decode-map decode-map)))) + (quail-help-insert-keymap-description (quail-translation-keymap) - (format "--- Key bindings%s ---\n" - (if (quail-conversion-keymap) - " (while translating)" - ""))) + "--- key bindings for selecting a character ---\n") + (insert ?\n) (if (quail-conversion-keymap) (quail-help-insert-keymap-description (quail-conversion-keymap) - "\n--- Key bindings (while converting) ---\n")) + "--- Key bindings for converting a character (sequence) ---\n")) (setq quail-current-package nil) (help-setup-xref (list #'quail-help package) (interactive-p)))))) (defun quail-help-insert-keymap-description (keymap &optional header) - (let (pos) + (let (pos1 pos2 eol) + (setq pos1 (point)) (if header (insert header)) - (setq pos (point)) (insert (substitute-command-keys "\\{keymap}")) - (goto-char pos) - (while (search-forward "quail-other-command" nil 'move) - (delete-region (line-beginning-position) (1+ (line-end-position)))))) - -(defun quail-show-kbd-layout () - "Show keyboard layout with key tops of multilingual characters." - (insert "--- Keyboard layout ---\n") - (let ((blink-matching-paren nil) - (i 0) - ch) - (while (< i quail-keyboard-layout-len) - (if (= (% i 30) 0) - (progn - (newline) - (indent-to (/ i 30))) - (if (= (% i 2) 0) - (insert " "))) - (setq ch (aref quail-keyboard-layout i)) - (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. - (setq ch (aref quail-keyboard-layout-standard i)) - (if (= ch ?\ ) - (setq ch (- (aref quail-keyboard-layout i))))) - (if (< ch 0) - (let ((last-command-event (- ch))) - (self-insert-command 1)) - (if (= ch ?\ ) - (insert ch) - (let* ((map (cdr (assq ch (cdr (quail-map))))) - (translation (and map (quail-get-translation - (car map) (char-to-string ch) 1)))) - (if (integerp translation) - (insert translation) - (if (consp translation) - (insert (aref (cdr translation) (car (car translation)))) - (let ((last-command-event ch)) - (self-insert-command 1))))))) - (setq i (1+ i)))) - (newline)) + (goto-char pos1) + ;; Skip headers "--- key bindings ---", etc. + (forward-line 3) + (setq pos2 (point)) + (with-syntax-table emacs-lisp-mode-syntax-table + (while (re-search-forward "\\sw\\(\\sw\\|\\s_\\)+" nil t) + (let ((sym (intern-soft (buffer-substring (match-beginning 0) + (point))))) + (if (and sym (fboundp sym) + (get sym 'quail-help-hide)) + (delete-region (line-beginning-position) + (1+ (line-end-position))))))) + (goto-char pos2) + (while (not (eobp)) + (if (looking-at "[ \t]*$") + (delete-region (point) (1+ (line-end-position))) + (forward-line 1))) + (goto-char pos2) + (if (eobp) + (delete-region pos1 (point))) + (goto-char (point-max)))) (defun quail-translation-help () "Show help message while translating in Quail input method." -- 2.39.2