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