Commit | Line | Data |
---|---|---|
64fd2bb1 | 1 | ;;; kkc.el --- Kana Kanji converter -*- coding: iso-2022-7bit; -*- |
4ed46869 | 2 | |
4ed46869 | 3 | ;; Copyright (C) 1995 Electrotechnical Laboratory, JAPAN. |
fa526c4a | 4 | ;; Licensed to the Free Software Foundation. |
4ed46869 | 5 | |
1b333492 | 6 | ;; Keywords: mule, multilingual, Japanese |
4ed46869 KH |
7 | |
8 | ;; This file is part of GNU Emacs. | |
9 | ||
10 | ;; GNU Emacs is free software; you can redistribute it and/or modify | |
11 | ;; it under the terms of the GNU General Public License as published by | |
12 | ;; the Free Software Foundation; either version 2, or (at your option) | |
13 | ;; any later version. | |
14 | ||
15 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | ;; GNU General Public License for more details. | |
19 | ||
20 | ;; You should have received a copy of the GNU General Public License | |
369314dc KH |
21 | ;; along with GNU Emacs; see the file COPYING. If not, write to the |
22 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
23 | ;; Boston, MA 02111-1307, USA. | |
4ed46869 KH |
24 | |
25 | ;;; Commentary: | |
26 | ||
27 | ;; These routines provide a simple and easy-to-use converter from | |
28 | ;; Kana-string to Kana-Kanji-mixed-string. This converter (here after | |
29 | ;; KKC) uses a SKK dictionary to get information how to convert | |
30 | ;; Kana-string. Since KKC can't be fully automated, we need an | |
31 | ;; interaction with a user to decide the correct conversion. For | |
32 | ;; that, we provide KKC major mode. | |
33 | ||
34 | ;;; Code: | |
35 | ||
1b333492 | 36 | (require 'ja-dic-utl) |
4ed46869 KH |
37 | |
38 | (defvar kkc-input-method-title "\e$B4A\e(B" | |
39 | "String denoting KKC input method. | |
40 | This string is shown at mode line when users are in KKC mode.") | |
41 | ||
42 | (defvar kkc-init-file-name "~/.kkcrc" | |
43 | "Name of a file which contains user's initial setup code for KKC.") | |
44 | ||
45 | ;; A flag to control a file specified by `kkc-init-file-name'. | |
46 | ;; The value nil means the file is not yet consulted. | |
47 | ;; The value t means the file has already been consulted but there's | |
48 | ;; no need of updating it yet. | |
49 | ;; Any other value means that we must update the file before exiting Emacs. | |
50 | (defvar kkc-init-file-flag nil) | |
51 | ||
52 | ;; Cash data for `kkc-lookup-key'. This may be initialized by loading | |
53 | ;; a file specified by `kkc-init-file-name'. If any elements are | |
54 | ;; modified, the data is written out to the file when exiting Emacs. | |
f9628a49 KH |
55 | (defvar kkc-lookup-cache nil) |
56 | ||
57 | ;; Tag symbol of `kkc-lookup-cache'. | |
58 | (defconst kkc-lookup-cache-tag 'kkc-lookup-cache-2) | |
4ed46869 KH |
59 | |
60 | (defun kkc-save-init-file () | |
61 | "Save initial setup code for KKC to a file specified by `kkc-init-file-name'" | |
62 | (if (and kkc-init-file-flag | |
63 | (not (eq kkc-init-file-flag t))) | |
74e7d166 KH |
64 | (let ((coding-system-for-write 'iso-2022-7bit) |
65 | (print-length nil)) | |
4ed46869 KH |
66 | (write-region (format "(setq kkc-lookup-cache '%S)\n" kkc-lookup-cache) |
67 | nil | |
68 | kkc-init-file-name)))) | |
69 | ||
70 | ;; Sequence of characters to be used for indexes for shown list. The | |
71 | ;; Nth character is for the Nth conversion in the list currently shown. | |
72 | (defvar kkc-show-conversion-list-index-chars | |
9045dc7e | 73 | "1234567890") |
4ed46869 | 74 | |
15f7f59e KH |
75 | (defun kkc-help () |
76 | "Show key bindings available while converting by KKC." | |
77 | (interactive) | |
78 | (with-output-to-temp-buffer "*Help*" | |
79 | (princ (substitute-command-keys "\\{kkc-keymap}")))) | |
80 | ||
9045dc7e | 81 | (defvar kkc-keymap |
15f7f59e | 82 | (let ((map (make-sparse-keymap)) |
9045dc7e | 83 | (len (length kkc-show-conversion-list-index-chars)) |
4ed46869 | 84 | (i 0)) |
9045dc7e KH |
85 | (while (< i len) |
86 | (define-key map | |
87 | (char-to-string (aref kkc-show-conversion-list-index-chars i)) | |
88 | 'kkc-select-from-list) | |
4ed46869 | 89 | (setq i (1+ i))) |
4ed46869 | 90 | (define-key map " " 'kkc-next) |
4ed46869 KH |
91 | (define-key map "\r" 'kkc-terminate) |
92 | (define-key map "\C-@" 'kkc-first-char-only) | |
93 | (define-key map "\C-n" 'kkc-next) | |
94 | (define-key map "\C-p" 'kkc-prev) | |
95 | (define-key map "\C-i" 'kkc-shorter) | |
96 | (define-key map "\C-o" 'kkc-longer) | |
f9628a49 KH |
97 | (define-key map "I" 'kkc-shorter-conversion) |
98 | (define-key map "O" 'kkc-longer-phrase) | |
4ed46869 KH |
99 | (define-key map "\C-c" 'kkc-cancel) |
100 | (define-key map "\C-?" 'kkc-cancel) | |
101 | (define-key map "\C-f" 'kkc-next-phrase) | |
102 | (define-key map "K" 'kkc-katakana) | |
103 | (define-key map "H" 'kkc-hiragana) | |
104 | (define-key map "l" 'kkc-show-conversion-list-or-next-group) | |
105 | (define-key map "L" 'kkc-show-conversion-list-or-prev-group) | |
e68e61b5 | 106 | (define-key map [?\C- ] 'kkc-first-char-only) |
4ed46869 KH |
107 | (define-key map [delete] 'kkc-cancel) |
108 | (define-key map [return] 'kkc-terminate) | |
15f7f59e | 109 | (define-key map "\C-h" 'kkc-help) |
5d3cb559 | 110 | map) |
9045dc7e | 111 | "Keymap for KKC (Kana Kanji Converter).") |
4ed46869 KH |
112 | |
113 | ;;; Internal variables used in KKC. | |
114 | ||
115 | ;; The current Kana string to be converted. | |
116 | (defvar kkc-original-kana nil) | |
117 | ||
118 | ;; The current key sequence (vector of Kana characters) generated from | |
119 | ;; `kkc-original-kana'. | |
120 | (defvar kkc-current-key nil) | |
121 | ||
122 | ;; List of the current conversions for `kkc-current-key'. | |
123 | (defvar kkc-current-conversions nil) | |
124 | ||
125 | ;; Vector of the same length as `kkc-current-conversion'. The first | |
126 | ;; element is a vector of: | |
127 | ;; o index number of the first conversion shown previously, | |
128 | ;; o index number of a conversion next of the last one shown previously, | |
129 | ;; o the shown string itself. | |
130 | ;; The remaining elements are widths (including columns for index | |
131 | ;; numbers) of conversions stored in the same order as in | |
132 | ;; `kkc-current-conversion'. | |
133 | (defvar kkc-current-conversions-width nil) | |
134 | ||
a9fbabda KH |
135 | (defcustom kkc-show-conversion-list-count 4 |
136 | "*Count of successive `kkc-next' or `kkc-prev' to show conversion list. | |
137 | When you type SPC or C-p successively this count while using the input | |
138 | method `japanese', the conversion candidates are shown in the echo | |
139 | area while indicating the current selection by `<N>'." | |
140 | :group 'mule | |
141 | :type 'integer) | |
142 | ||
143 | ;; Count of successive invocations of `kkc-next'. | |
144 | (defvar kkc-next-count nil) | |
145 | ||
146 | ;; Count of successive invocations of `kkc-prev'. | |
147 | (defvar kkc-prev-count nil) | |
4ed46869 KH |
148 | |
149 | ;; Provided that `kkc-current-key' is [A B C D E F G H I], the current | |
9045dc7e | 150 | ;; conversion target is [A B C D E F], and the sequence of which |
4ed46869 KH |
151 | ;; conversion is found is [A B C D]: |
152 | ;; | |
153 | ;; A B C D E F G H I | |
154 | ;; kkc-overlay-head (black): |<--------->| | |
155 | ;; kkc-overlay-tail (underline): |<------->| | |
156 | ;; kkc-length-head: |<--------->| | |
157 | ;; kkc-length-converted: |<----->| | |
158 | ;; | |
159 | (defvar kkc-overlay-head nil) | |
160 | (defvar kkc-overlay-tail nil) | |
161 | (defvar kkc-length-head nil) | |
162 | (defvar kkc-length-converted nil) | |
163 | ||
164 | ;; Cursor type (`box' or `bar') of the current frame. | |
165 | (defvar kkc-cursor-type nil) | |
166 | ||
1b333492 | 167 | ;; Lookup Japanese dictionary to set list of conversions in |
4ed46869 KH |
168 | ;; kkc-current-conversions for key sequence kkc-current-key of length |
169 | ;; LEN. If no conversion is found in the dictionary, don't change | |
170 | ;; kkc-current-conversions and return nil. | |
a1506d29 | 171 | ;; Postfixes are handled only if POSTFIX is non-nil. |
93303c99 | 172 | (defun kkc-lookup-key (len &optional postfix prefer-noun) |
4ed46869 | 173 | ;; At first, prepare cache data if any. |
f9628a49 KH |
174 | (unless kkc-init-file-flag |
175 | (setq kkc-init-file-flag t | |
176 | kkc-lookup-cache nil) | |
177 | (add-hook 'kill-emacs-hook 'kkc-save-init-file) | |
178 | (if (file-readable-p kkc-init-file-name) | |
179 | (condition-case nil | |
180 | (load-file kkc-init-file-name) | |
181 | (kkc-error "Invalid data in %s" kkc-init-file-name)))) | |
182 | (or (and (nested-alist-p kkc-lookup-cache) | |
183 | (eq (car kkc-lookup-cache) kkc-lookup-cache-tag)) | |
184 | (setq kkc-lookup-cache (list kkc-lookup-cache-tag) | |
185 | kkc-init-file-flag 'kkc-lookup-cache)) | |
4ed46869 KH |
186 | (let ((entry (lookup-nested-alist kkc-current-key kkc-lookup-cache len 0 t))) |
187 | (if (consp (car entry)) | |
188 | (setq kkc-length-converted len | |
189 | kkc-current-conversions-width nil | |
190 | kkc-current-conversions (car entry)) | |
93303c99 | 191 | (setq entry (skkdic-lookup-key kkc-current-key len postfix prefer-noun)) |
4ed46869 KH |
192 | (if entry |
193 | (progn | |
194 | (setq kkc-length-converted len | |
195 | kkc-current-conversions-width nil | |
196 | kkc-current-conversions (cons 1 entry)) | |
197 | (if postfix | |
198 | ;; Store this conversions in the cache. | |
199 | (progn | |
200 | (set-nested-alist kkc-current-key kkc-current-conversions | |
201 | kkc-lookup-cache kkc-length-converted) | |
202 | (setq kkc-init-file-flag 'kkc-lookup-cache))) | |
203 | t) | |
204 | (if (= len 1) | |
205 | (setq kkc-length-converted 1 | |
206 | kkc-current-conversions-width nil | |
207 | kkc-current-conversions (cons 0 nil))))))) | |
208 | ||
f9628a49 KH |
209 | (put 'kkc-error 'error-conditions '(kkc-error error)) |
210 | (defun kkc-error (&rest args) | |
211 | (signal 'kkc-error (apply 'format args))) | |
212 | ||
9045dc7e KH |
213 | (defvar kkc-converting nil) |
214 | ||
44bec171 KH |
215 | ;;;###autoload |
216 | (defvar kkc-after-update-conversion-functions nil | |
217 | "Functions to run after a conversion is selected in `japanese' input method. | |
218 | With this input method, a user can select a proper conversion from | |
219 | candidate list. Each time he changes the selection, functions in this | |
220 | list are called with two arguments; starting and ending buffer | |
221 | positions that contains the current selection.") | |
222 | ||
4ed46869 | 223 | ;;;###autoload |
9045dc7e | 224 | (defun kkc-region (from to) |
4ed46869 | 225 | "Convert Kana string in the current region to Kanji-Kana mixed string. |
9045dc7e KH |
226 | Users can select a desirable conversion interactively. |
227 | When called from a program, expects two arguments, | |
228 | positions FROM and TO (integers or markers) specifying the target region. | |
229 | When it returns, the point is at the tail of the selected conversion, | |
230 | and the return value is the length of the conversion." | |
4ed46869 KH |
231 | (interactive "r") |
232 | (setq kkc-original-kana (buffer-substring from to)) | |
233 | (goto-char from) | |
234 | ||
235 | ;; Setup overlays. | |
236 | (if (overlayp kkc-overlay-head) | |
237 | (move-overlay kkc-overlay-head from to) | |
238 | (setq kkc-overlay-head (make-overlay from to nil nil t)) | |
239 | (overlay-put kkc-overlay-head 'face 'highlight)) | |
240 | (if (overlayp kkc-overlay-tail) | |
241 | (move-overlay kkc-overlay-tail to to) | |
242 | (setq kkc-overlay-tail (make-overlay to to nil nil t)) | |
243 | (overlay-put kkc-overlay-tail 'face 'underline)) | |
244 | ||
9045dc7e KH |
245 | (setq kkc-current-key (string-to-vector kkc-original-kana)) |
246 | (setq kkc-length-head (length kkc-current-key)) | |
247 | (setq kkc-length-converted 0) | |
248 | ||
7efe5dbc | 249 | (unwind-protect |
f9628a49 | 250 | ;; At first convert the region to the first candidate. |
7efe5dbc | 251 | (let ((current-input-method-title kkc-input-method-title) |
f9628a49 | 252 | (input-method-function nil) |
e0d77f0d | 253 | (modified-p (buffer-modified-p)) |
f9628a49 KH |
254 | (first t)) |
255 | (while (not (kkc-lookup-key kkc-length-head nil first)) | |
256 | (setq kkc-length-head (1- kkc-length-head) | |
257 | first nil)) | |
258 | (goto-char to) | |
259 | (kkc-update-conversion 'all) | |
a9fbabda KH |
260 | (setq kkc-next-count 1 kkc-prev-count 0) |
261 | (if (and (>= kkc-next-count kkc-show-conversion-list-count) | |
262 | (>= (length kkc-current-conversions) 3)) | |
263 | (kkc-show-conversion-list-or-next-group)) | |
f9628a49 | 264 | |
e8dd0160 | 265 | ;; Then, ask users to select a desirable conversion. |
7efe5dbc KH |
266 | (force-mode-line-update) |
267 | (setq kkc-converting t) | |
9dfb0fa2 KH |
268 | ;; Hide "... loaded" message. |
269 | (message nil) | |
7efe5dbc | 270 | (while kkc-converting |
e0d77f0d | 271 | (set-buffer-modified-p modified-p) |
f12d44e5 | 272 | (let* ((overriding-terminal-local-map kkc-keymap) |
15f7f59e | 273 | (help-char nil) |
7efe5dbc KH |
274 | (keyseq (read-key-sequence nil)) |
275 | (cmd (lookup-key kkc-keymap keyseq))) | |
276 | (if (commandp cmd) | |
277 | (condition-case err | |
a9fbabda KH |
278 | (progn |
279 | (cond ((eq cmd 'kkc-next) | |
280 | (setq kkc-next-count (1+ kkc-next-count) | |
281 | kkc-prev-count 0)) | |
282 | ((eq cmd 'kkc-prev) | |
283 | (setq kkc-prev-count (1+ kkc-prev-count) | |
284 | kkc-next-count 0)) | |
285 | (t | |
286 | (setq kkc-next-count 0 kkc-prev-count 0))) | |
287 | (call-interactively cmd)) | |
7efe5dbc KH |
288 | (kkc-error (message "%s" (cdr err)) (beep))) |
289 | ;; KEYSEQ is not defined in KKC keymap. | |
290 | ;; Let's put the event back. | |
291 | (setq unread-input-method-events | |
292 | (append (string-to-list keyseq) | |
293 | unread-input-method-events)) | |
294 | (kkc-terminate)))) | |
295 | ||
296 | (force-mode-line-update) | |
297 | (goto-char (overlay-end kkc-overlay-tail)) | |
298 | (- (overlay-start kkc-overlay-head) from)) | |
9045dc7e KH |
299 | (delete-overlay kkc-overlay-head) |
300 | (delete-overlay kkc-overlay-tail))) | |
4ed46869 KH |
301 | |
302 | (defun kkc-terminate () | |
303 | "Exit from KKC mode by fixing the current conversion." | |
304 | (interactive) | |
f370eb4c KH |
305 | (goto-char (overlay-end kkc-overlay-tail)) |
306 | (move-overlay kkc-overlay-head (point) (point)) | |
9045dc7e | 307 | (setq kkc-converting nil)) |
4ed46869 KH |
308 | |
309 | (defun kkc-cancel () | |
310 | "Exit from KKC mode by canceling any conversions." | |
311 | (interactive) | |
9045dc7e | 312 | (goto-char (overlay-start kkc-overlay-head)) |
4ed46869 KH |
313 | (delete-region (overlay-start kkc-overlay-head) |
314 | (overlay-end kkc-overlay-tail)) | |
315 | (insert kkc-original-kana) | |
f370eb4c | 316 | (setq kkc-converting nil)) |
4ed46869 KH |
317 | |
318 | (defun kkc-first-char-only () | |
319 | "Select only the first character currently converted." | |
320 | (interactive) | |
321 | (goto-char (overlay-start kkc-overlay-head)) | |
322 | (forward-char 1) | |
323 | (delete-region (point) (overlay-end kkc-overlay-tail)) | |
324 | (kkc-terminate)) | |
325 | ||
4ed46869 KH |
326 | (defun kkc-next () |
327 | "Select the next candidate of conversion." | |
328 | (interactive) | |
4ed46869 KH |
329 | (let ((idx (1+ (car kkc-current-conversions)))) |
330 | (if (< idx 0) | |
331 | (setq idx 1)) | |
332 | (if (>= idx (length kkc-current-conversions)) | |
333 | (setq idx 0)) | |
334 | (setcar kkc-current-conversions idx) | |
335 | (if (> idx 1) | |
336 | (progn | |
337 | (set-nested-alist kkc-current-key kkc-current-conversions | |
338 | kkc-lookup-cache kkc-length-converted) | |
339 | (setq kkc-init-file-flag 'kkc-lookup-cache))) | |
340 | (if (or kkc-current-conversions-width | |
341 | (>= kkc-next-count kkc-show-conversion-list-count)) | |
342 | (kkc-show-conversion-list-update)) | |
343 | (kkc-update-conversion))) | |
344 | ||
4ed46869 KH |
345 | (defun kkc-prev () |
346 | "Select the previous candidate of conversion." | |
347 | (interactive) | |
4ed46869 KH |
348 | (let ((idx (1- (car kkc-current-conversions)))) |
349 | (if (< idx 0) | |
350 | (setq idx (1- (length kkc-current-conversions)))) | |
351 | (setcar kkc-current-conversions idx) | |
352 | (if (> idx 1) | |
353 | (progn | |
354 | (set-nested-alist kkc-current-key kkc-current-conversions | |
355 | kkc-lookup-cache kkc-length-converted) | |
356 | (setq kkc-init-file-flag 'kkc-lookup-cache))) | |
357 | (if (or kkc-current-conversions-width | |
358 | (>= kkc-prev-count kkc-show-conversion-list-count)) | |
359 | (kkc-show-conversion-list-update)) | |
360 | (kkc-update-conversion))) | |
361 | ||
362 | (defun kkc-select-from-list () | |
363 | "Select one candidate from the list currently shown in echo area." | |
364 | (interactive) | |
365 | (let (idx) | |
366 | (if kkc-current-conversions-width | |
367 | (let ((len (length kkc-show-conversion-list-index-chars)) | |
368 | (maxlen (- (aref (aref kkc-current-conversions-width 0) 1) | |
369 | (aref (aref kkc-current-conversions-width 0) 0))) | |
370 | (i 0)) | |
371 | (if (> len maxlen) | |
372 | (setq len maxlen)) | |
373 | (while (< i len) | |
374 | (if (= (aref kkc-show-conversion-list-index-chars i) | |
9045dc7e | 375 | last-input-event) |
4ed46869 KH |
376 | (setq idx i i len) |
377 | (setq i (1+ i)))))) | |
378 | (if idx | |
379 | (progn | |
380 | (setcar kkc-current-conversions | |
381 | (+ (aref (aref kkc-current-conversions-width 0) 0) idx)) | |
382 | (kkc-show-conversion-list-update) | |
383 | (kkc-update-conversion)) | |
9045dc7e KH |
384 | (setq unread-input-method-events |
385 | (cons last-input-event unread-input-method-events)) | |
4ed46869 KH |
386 | (kkc-terminate)))) |
387 | ||
388 | (defun kkc-katakana () | |
389 | "Convert to Katakana." | |
390 | (interactive) | |
391 | (setcar kkc-current-conversions -1) | |
392 | (kkc-update-conversion 'all)) | |
393 | ||
394 | (defun kkc-hiragana () | |
395 | "Convert to hiragana." | |
396 | (interactive) | |
397 | (setcar kkc-current-conversions 0) | |
398 | (kkc-update-conversion)) | |
399 | ||
400 | (defun kkc-shorter () | |
401 | "Make the Kana string to be converted shorter." | |
402 | (interactive) | |
403 | (if (<= kkc-length-head 1) | |
f9628a49 KH |
404 | (kkc-error "Can't be shorter")) |
405 | (setq kkc-length-head (1- kkc-length-head)) | |
406 | (if (> kkc-length-converted kkc-length-head) | |
407 | (let ((len kkc-length-head)) | |
408 | (setq kkc-length-converted 0) | |
409 | (while (not (kkc-lookup-key len)) | |
410 | (setq len (1- len))))) | |
411 | (kkc-update-conversion 'all)) | |
4ed46869 KH |
412 | |
413 | (defun kkc-longer () | |
414 | "Make the Kana string to be converted longer." | |
415 | (interactive) | |
416 | (if (>= kkc-length-head (length kkc-current-key)) | |
f9628a49 KH |
417 | (kkc-error "Can't be longer")) |
418 | (setq kkc-length-head (1+ kkc-length-head)) | |
419 | ;; This time, try also entries with postfixes. | |
420 | (kkc-lookup-key kkc-length-head 'postfix) | |
421 | (kkc-update-conversion 'all)) | |
422 | ||
423 | (defun kkc-shorter-conversion () | |
424 | "Make the Kana string to be converted shorter." | |
425 | (interactive) | |
426 | (if (<= kkc-length-converted 1) | |
427 | (kkc-error "Can't be shorter")) | |
428 | (let ((len (1- kkc-length-converted))) | |
429 | (setq kkc-length-converted 0) | |
430 | (while (not (kkc-lookup-key len)) | |
431 | (setq len (1- len)))) | |
432 | (kkc-update-conversion 'all)) | |
433 | ||
434 | (defun kkc-longer-phrase () | |
435 | "Make the current phrase (BUNSETSU) longer without looking up dictionary." | |
436 | (interactive) | |
437 | (if (>= kkc-length-head (length kkc-current-key)) | |
438 | (kkc-error "Can't be longer")) | |
439 | (setq kkc-length-head (1+ kkc-length-head)) | |
440 | (kkc-update-conversion 'all)) | |
4ed46869 KH |
441 | |
442 | (defun kkc-next-phrase () | |
443 | "Fix the currently converted string and try to convert the remaining string." | |
444 | (interactive) | |
445 | (if (>= kkc-length-head (length kkc-current-key)) | |
446 | (kkc-terminate) | |
447 | (setq kkc-length-head (- (length kkc-current-key) kkc-length-head)) | |
448 | (goto-char (overlay-end kkc-overlay-head)) | |
449 | (while (and (< (point) (overlay-end kkc-overlay-tail)) | |
450 | (looking-at "\\CH")) | |
451 | (goto-char (match-end 0)) | |
452 | (setq kkc-length-head (1- kkc-length-head))) | |
453 | (if (= kkc-length-head 0) | |
454 | (kkc-terminate) | |
455 | (let ((newkey (make-vector kkc-length-head 0)) | |
456 | (idx (- (length kkc-current-key) kkc-length-head)) | |
93303c99 | 457 | (len kkc-length-head) |
4ed46869 KH |
458 | (i 0)) |
459 | ;; For the moment, (setq kkc-original-kana (concat newkey)) | |
460 | ;; doesn't work. | |
461 | (setq kkc-original-kana "") | |
462 | (while (< i kkc-length-head) | |
463 | (aset newkey i (aref kkc-current-key (+ idx i))) | |
464 | (setq kkc-original-kana | |
465 | (concat kkc-original-kana (char-to-string (aref newkey i)))) | |
466 | (setq i (1+ i))) | |
467 | (setq kkc-current-key newkey) | |
468 | (setq kkc-length-converted 0) | |
93303c99 KH |
469 | (while (and (not (kkc-lookup-key kkc-length-head nil |
470 | (< kkc-length-head len))) | |
4ed46869 KH |
471 | (> kkc-length-head 1)) |
472 | (setq kkc-length-head (1- kkc-length-head))) | |
473 | (let ((pos (point)) | |
474 | (tail (overlay-end kkc-overlay-tail))) | |
475 | (move-overlay kkc-overlay-head pos tail) | |
476 | (move-overlay kkc-overlay-tail tail tail)) | |
477 | (kkc-update-conversion 'all))))) | |
478 | ||
479 | ;; We'll show users a list of available conversions in echo area with | |
480 | ;; index numbers so that users can select one conversion with the | |
481 | ;; number. | |
482 | ||
483 | ;; Set `kkc-current-conversions-width'. | |
484 | (defun kkc-setup-current-conversions-width () | |
485 | (let ((convs (cdr kkc-current-conversions)) | |
486 | (len (length kkc-current-conversions)) | |
487 | (idx 1)) | |
488 | (setq kkc-current-conversions-width (make-vector len nil)) | |
489 | ;; To tell `kkc-show-conversion-list-update' to generate | |
490 | ;; message from scratch. | |
491 | (aset kkc-current-conversions-width 0 (vector len -2 nil)) | |
492 | ;; Fill the remaining slots. | |
493 | (while convs | |
494 | (aset kkc-current-conversions-width idx | |
495 | (+ (string-width (car convs)) 4)) | |
496 | (setq convs (cdr convs) | |
497 | idx (1+ idx))))) | |
498 | ||
499 | (defun kkc-show-conversion-list-or-next-group () | |
500 | "Show list of available conversions in echo area with index numbers. | |
501 | If the list is already shown, show the next group of conversions, | |
502 | and change the current conversion to the first one in the group." | |
503 | (interactive) | |
504 | (if (< (length kkc-current-conversions) 3) | |
f9628a49 | 505 | (kkc-error "No alternative")) |
4ed46869 KH |
506 | (if kkc-current-conversions-width |
507 | (let ((next-idx (aref (aref kkc-current-conversions-width 0) 1))) | |
508 | (if (< next-idx (length kkc-current-conversions-width)) | |
509 | (setcar kkc-current-conversions next-idx) | |
510 | (setcar kkc-current-conversions 1)) | |
511 | (kkc-show-conversion-list-update) | |
512 | (kkc-update-conversion)) | |
513 | (kkc-setup-current-conversions-width) | |
514 | (kkc-show-conversion-list-update))) | |
515 | ||
516 | (defun kkc-show-conversion-list-or-prev-group () | |
517 | "Show list of available conversions in echo area with index numbers. | |
518 | If the list is already shown, show the previous group of conversions, | |
519 | and change the current conversion to the last one in the group." | |
520 | (interactive) | |
521 | (if (< (length kkc-current-conversions) 3) | |
f9628a49 | 522 | (kkc-error "No alternative")) |
4ed46869 KH |
523 | (if kkc-current-conversions-width |
524 | (let ((this-idx (aref (aref kkc-current-conversions-width 0) 0))) | |
525 | (if (> this-idx 1) | |
526 | (setcar kkc-current-conversions (1- this-idx)) | |
527 | (setcar kkc-current-conversions | |
528 | (1- (length kkc-current-conversions-width)))) | |
529 | (kkc-show-conversion-list-update) | |
530 | (kkc-update-conversion)) | |
531 | (kkc-setup-current-conversions-width) | |
532 | (kkc-show-conversion-list-update))) | |
533 | ||
534 | ;; Update the conversion list shown in echo area. | |
535 | (defun kkc-show-conversion-list-update () | |
536 | (or kkc-current-conversions-width | |
537 | (kkc-setup-current-conversions-width)) | |
538 | (let* ((current-idx (car kkc-current-conversions)) | |
539 | (first-slot (aref kkc-current-conversions-width 0)) | |
540 | (this-idx (aref first-slot 0)) | |
541 | (next-idx (aref first-slot 1)) | |
542 | (msg (aref first-slot 2))) | |
543 | (if (< current-idx this-idx) | |
544 | ;; The currently selected conversion is before the list shown | |
545 | ;; previously. We must start calculation of message width | |
546 | ;; from the start again. | |
547 | (setq this-idx 1 msg nil) | |
548 | (if (>= current-idx next-idx) | |
549 | ;; The currently selected conversion is after the list shown | |
550 | ;; previously. We start calculation of message width from | |
551 | ;; the conversion next of TO. | |
ee6916fd | 552 | (setq this-idx next-idx msg nil))) |
4ed46869 KH |
553 | (if (not msg) |
554 | (let ((len (length kkc-current-conversions)) | |
555 | (max-width (window-width (minibuffer-window))) | |
556 | (width-table kkc-current-conversions-width) | |
557 | (width 0) | |
558 | (idx this-idx) | |
7efe5dbc | 559 | (max-items (length kkc-show-conversion-list-index-chars)) |
4ed46869 | 560 | l) |
a9fbabda KH |
561 | ;; Set THIS-IDX to the first index of conversion to be shown |
562 | ;; in MSG, and reflect it in kkc-current-conversions-width. | |
563 | (while (<= idx current-idx) | |
7efe5dbc KH |
564 | (if (and (<= (+ width (aref width-table idx)) max-width) |
565 | (< (- idx this-idx) max-items)) | |
4ed46869 KH |
566 | (setq width (+ width (aref width-table idx))) |
567 | (setq this-idx idx width (aref width-table idx))) | |
568 | (setq idx (1+ idx) | |
569 | l (cdr l))) | |
570 | (aset first-slot 0 this-idx) | |
a9fbabda KH |
571 | ;; Set NEXT-IDX to the next index of the last conversion |
572 | ;; shown in MSG, and reflect it in | |
573 | ;; kkc-current-conversions-width. | |
4ed46869 | 574 | (while (and (< idx len) |
7efe5dbc KH |
575 | (<= (+ width (aref width-table idx)) max-width) |
576 | (< (- idx this-idx) max-items)) | |
4ed46869 KH |
577 | (setq width (+ width (aref width-table idx)) |
578 | idx (1+ idx) | |
579 | l (cdr l))) | |
580 | (aset first-slot 1 (setq next-idx idx)) | |
581 | (setq l (nthcdr this-idx kkc-current-conversions)) | |
a9fbabda KH |
582 | (setq msg (format " %c %s" |
583 | (aref kkc-show-conversion-list-index-chars 0) | |
ee6916fd KH |
584 | (propertize (car l) |
585 | 'kkc-conversion-index this-idx)) | |
a9fbabda KH |
586 | idx (1+ this-idx) |
587 | l (cdr l)) | |
4ed46869 | 588 | (while (< idx next-idx) |
a9fbabda | 589 | (setq msg (format "%s %c %s" |
4ed46869 KH |
590 | msg |
591 | (aref kkc-show-conversion-list-index-chars | |
592 | (- idx this-idx)) | |
ee6916fd KH |
593 | (propertize (car l) |
594 | 'kkc-conversion-index idx)) | |
595 | idx (1+ idx) | |
4ed46869 KH |
596 | l (cdr l))) |
597 | (aset first-slot 2 msg))) | |
ee6916fd KH |
598 | |
599 | ;; Highlight the current conversion. | |
4ed46869 | 600 | (if (> current-idx 0) |
ee6916fd KH |
601 | (let ((pos 3) |
602 | (limit (length msg))) | |
603 | (remove-text-properties 0 (length msg) '(face nil) msg) | |
604 | (while (not (eq (get-text-property pos 'kkc-conversion-index msg) | |
605 | current-idx)) | |
606 | (setq pos (next-single-property-change pos 'kkc-conversion-index | |
607 | msg limit))) | |
608 | (put-text-property pos (next-single-property-change | |
609 | pos 'kkc-conversion-index msg limit) | |
610 | 'face 'highlight msg))) | |
611 | (let ((message-log-max nil)) | |
612 | (message "%s" msg)))) | |
4ed46869 KH |
613 | |
614 | ;; Update the conversion area with the latest conversion selected. | |
615 | ;; ALL if non nil means to update the whole area, else update only | |
616 | ;; inside quail-overlay-head. | |
617 | ||
618 | (defun kkc-update-conversion (&optional all) | |
619 | (goto-char (overlay-start kkc-overlay-head)) | |
620 | (cond ((= (car kkc-current-conversions) 0) ; Hiragana | |
621 | (let ((i 0)) | |
622 | (while (< i kkc-length-converted) | |
623 | (insert (aref kkc-current-key i)) | |
624 | (setq i (1+ i))))) | |
625 | ((= (car kkc-current-conversions) -1) ; Katakana | |
626 | (let ((i 0)) | |
627 | (while (< i kkc-length-converted) | |
628 | (insert (japanese-katakana (aref kkc-current-key i))) | |
629 | (setq i (1+ i))))) | |
630 | (t | |
631 | (insert (nth (car kkc-current-conversions) kkc-current-conversions)))) | |
632 | (delete-region (point) (overlay-start kkc-overlay-tail)) | |
633 | (if all | |
634 | (let ((len (length kkc-current-key)) | |
635 | (i kkc-length-converted)) | |
636 | (delete-region (overlay-start kkc-overlay-tail) | |
637 | (overlay-end kkc-overlay-head)) | |
638 | (while (< i kkc-length-head) | |
639 | (if (= (car kkc-current-conversions) -1) | |
640 | (insert (japanese-katakana (aref kkc-current-key i))) | |
641 | (insert (aref kkc-current-key i))) | |
642 | (setq i (1+ i))) | |
643 | (let ((pos (point))) | |
644 | (while (< i len) | |
645 | (insert (aref kkc-current-key i)) | |
646 | (setq i (1+ i))) | |
647 | (move-overlay kkc-overlay-head | |
648 | (overlay-start kkc-overlay-head) pos) | |
649 | (delete-region (point) (overlay-end kkc-overlay-tail))))) | |
44bec171 KH |
650 | (unwind-protect |
651 | (run-hook-with-args 'kkc-after-update-conversion-functions | |
652 | (overlay-start kkc-overlay-head) | |
653 | (overlay-end kkc-overlay-head)) | |
654 | (goto-char (overlay-end kkc-overlay-tail)))) | |
4ed46869 KH |
655 | |
656 | ;; | |
657 | (provide 'kkc) | |
658 | ||
ab5796a9 | 659 | ;;; arch-tag: 3cbfd56e-74e6-4f60-bb46-ba7c2d366fbf |
60370d40 | 660 | ;;; kkc.el ends here |