1 ;;; egg-cnv.el --- Conversion Backend in Egg Input Method Architecture
3 ;; Copyright (C) 1997, 1998 Mule Project,
4 ;; Powered by Electrotechnical Laboratory, JAPAN.
5 ;; Project Leader: Satoru Tomura <tomura@etl.go.jp>
7 ;; Author: NIIBE Yutaka <gniibe@mri.co.jp>
8 ;; KATAYAMA Yoshio <kate@pfu.co.jp>
9 ;; Maintainer: NIIBE Yutaka <gniibe@mri.co.jp>
10 ;; Keywords: mule, multilingual, input method
12 ;; This file will be part of GNU Emacs (in future).
14 ;; EGG is free software; you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation; either version 2, or (at your option)
19 ;; EGG is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 ;; GNU General Public License for more details.
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs; see the file COPYING. If not, write to the
26 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
27 ;; Boston, MA 02111-1307, USA.
34 (defsubst egg-bunsetsu-info () 'intangible)
36 (defun egg-get-bunsetsu-info (p)
37 (let ((info (get-text-property p (egg-bunsetsu-info))))
40 (setq egg-conversion-backend (car info))
44 (defvar egg-conversion-backend-alist nil)
45 (make-variable-buffer-local 'egg-conversion-backend-alist)
46 (defvar egg-conversion-backend nil)
47 (make-variable-buffer-local 'egg-conversion-backend)
49 (defvar egg-finalize-backend-alist nil)
51 (defun egg-set-current-backend (language)
52 (let ((backend (assoc lang egg-conversion-backend-alist)))
54 (error "%S is not supported" lang)
55 (setq egg-conversion-backend (cdr backend)))))
57 (defun egg-initialize-backend (language)
58 (egg-set-current-backend language)
59 (funcall (aref egg-conversion-backend 0)))
61 (defun egg-start-conversion (yomi-string language)
62 (egg-set-current-backend language)
63 (funcall (aref egg-conversion-backend 1) yomi-string language))
64 (defun egg-get-bunsetsu-converted (bunsetsu-info)
65 (funcall (aref egg-conversion-backend 2) bunsetsu-info))
66 (defun egg-get-bunsetsu-source (bunsetsu-info)
67 (funcall (aref egg-conversion-backend 3) bunsetsu-info))
68 (defun egg-list-candidates (bunsetsu-info prev-bunsetsu-info)
69 (funcall (aref egg-conversion-backend 4) bunsetsu-info prev-bunsetsu-info))
70 (defun egg-get-number-of-candidates (bunsetsu-info)
71 (funcall (aref egg-conversion-backend 5) bunsetsu-info))
72 (defun egg-get-current-candidate-number (bunsetsu-info)
73 (funcall (aref egg-conversion-backend 6) bunsetsu-info))
74 (defun egg-get-all-candidates (bunsetsu-info)
75 (funcall (aref egg-conversion-backend 7) bunsetsu-info))
76 (defun egg-decide-candidate (bunsetsu-info candidate-pos)
77 (funcall (aref egg-conversion-backend 8) bunsetsu-info candidate-pos))
78 (defun egg-change-bunsetsu-length (b0 b1 b2 len)
79 (funcall (aref egg-conversion-backend 9) b0 b1 b2 len))
80 (defun egg-end-conversion (bunsetsu-info-list)
81 (funcall (aref egg-conversion-backend 10) bunsetsu-info-list))
83 (defun egg-finalize-backend ()
84 (let ((alist egg-finalize-backend-alist))
86 (funcall (car (car (car alist))) (cdr (car (car alist))))
87 (setq alist (cdr alist)))))
89 (defmacro egg-set-conversion-backend-internal (backend langs &optional force)
90 `(let ((l ,langs) pair)
92 (setq pair (assoc (car l) egg-conversion-backend-alist))
94 (setq egg-conversion-backend-alist
95 (cons (cons (car l) ,backend)
96 egg-conversion-backend-alist))
97 ,(if force `(setcdr pair ,backend)))
98 (setq pair (cons (aref ,backend 11) (car l)))
99 (if (null (assoc pair egg-finalize-backend-alist))
100 (setq egg-finalize-backend-alist
101 (cons (list pair) egg-finalize-backend-alist)))
104 (defun egg-set-conversion-backend (backend curent-langs other-langs)
105 (egg-set-conversion-backend-internal backend curent-langs t)
106 (egg-set-conversion-backend-internal backend other-langs))
108 (defvar egg-conversion-open "|")
109 (defvar egg-conversion-close "|")
110 (defvar egg-conversion-separator " ")
113 (defun egg-convert-region (start end)
115 (let (bunsetsu-info-list lang contin p s e)
117 (narrow-to-region start end)
119 (insert egg-conversion-open)
120 (add-text-properties start (point)
123 'egg-source (buffer-substring (point)
125 (if egg-conversion-face
126 (put-text-property start (point) 'invisible t))
128 (egg-separate-languages start (point-max))
130 (while (< (point) (point-max))
131 (setq lang (get-text-property (point) 'egg-lang))
133 e (next-single-property-change s 'egg-lang nil (point-max)))
134 (setq bunsetsu-info-list
135 (egg-start-conversion (buffer-substring s e) lang))
136 (setq contin (< e (point-max)))
138 (egg-insert-bunsetsu-list bunsetsu-info-list
139 (if (< (point) (point-max)) 'contine t))))
141 (insert egg-conversion-close)
142 (put-text-property p (point) 'egg-end t)
143 (if egg-conversion-face
144 (put-text-property p (point) 'invisible t))
147 (defun egg-separate-languages (start end)
148 (let (lang last-lang last-chinese p l c cset)
150 (while (< (point) end)
151 (setq p (next-single-property-change (point) 'its-lang nil end))
153 ((get-text-property (point) 'its-lang)
155 ((setq l (egg-chinese-syllable (buffer-substring (point) p)))
157 (goto-char (+ (point) l))
158 (put-text-property p (point) 'its-lang "Chinese"))
160 (setq c (following-char)
161 cset (char-charset c))
162 (eq cset 'chinese-sisheng))
165 (put-text-property p (point) 'its-lang "Chinese"))
171 (put-text-property p (point) 'its-lang (egg-char-to-language c)))))
173 (while (< (point) end)
174 (setq lang (get-text-property (point) 'its-lang))
177 (setq lang (or last-lang
178 (egg-next-part-lang end))))
179 ((equal lang "Chinese")
180 (setq lang (or last-chinese
181 (egg-next-chinese-lang end)))))
182 (setq last-lang lang)
183 (if (or (equal lang "Chinese-GB") (equal lang "Chinese-CNS"))
184 (setq last-chinese lang))
186 (goto-char (next-single-property-change (point) 'its-lang nil end))
187 (set-text-properties p (point) (list 'egg-lang lang)))))
189 (defun egg-char-to-language (c)
190 (let ((charset (char-charset c))
191 (list language-info-alist))
193 (null (memq charset (assq 'charset (car list)))))
194 (setq list (cdr list)))
197 (defun egg-next-part-lang (end)
198 (let* ((p (next-single-property-change (point) 'its-lang nil end))
199 (lang (get-text-property p 'its-lang)))
200 (if (equal lang "Chinese")
201 (egg-next-chinese-lang end)
204 egg-default-language))))
206 (defun egg-next-chinese-lang (end)
209 (while (and (< p end) (null lang))
210 (setq p (next-single-property-change p 'its-lang nil end))
211 (setq lang (get-text-property p 'its-lang))
212 (if (null (or (equal lang "Chinese-GB")
213 (equal lang "Chinese-CNS")))
217 ((or (equal its-current-language "Chinese-GB")
218 (equal its-current-language "Chinese-CNS"))
219 its-current-language)
220 ((or (equal egg-default-language "Chinese-GB")
221 (equal egg-default-language "Chinese-CNS"))
222 egg-default-language)
225 (defvar egg-conversion-face nil)
226 (defvar egg-conversion-map
227 (let ((map (make-sparse-keymap))
230 (define-key map (vector i) 'egg-exit-conversion-unread-char)
232 (define-key map "\C-@" 'egg-decide-first-char)
233 (define-key map [?\C-\ ] 'egg-decide-first-char)
234 (define-key map "\C-a" 'egg-beginning-of-conversion-buffer)
235 (define-key map "\C-b" 'egg-backward-bunsetsu)
236 (define-key map "\C-c" 'egg-abort-conversion)
237 (define-key map "\C-e" 'egg-end-of-conversion-buffer)
238 (define-key map "\C-f" 'egg-forward-bunsetsu)
239 (define-key map "\C-h" 'egg-help-command)
240 (define-key map "\C-i" 'egg-shrink-bunsetsu)
241 (define-key map "\C-k" 'egg-decide-before-point)
242 ;; (define-key map "\C-l" 'egg-exit-conversion) ; Don't override C-L
243 (define-key map "\C-m" 'egg-exit-conversion)
244 (define-key map "\C-n" 'egg-next-candidate)
245 (define-key map "\C-o" 'egg-enlarge-bunsetsu)
246 (define-key map "\C-p" 'egg-previous-candidate)
247 (define-key map "\M-s" 'egg-select-candidate)
248 (define-key map [return] 'egg-exit-conversion)
249 ;; (define-key map "\C-\\" 'egg-exit-mode-no-egg)
250 (define-key map [right] 'egg-forward-bunsetsu)
251 (define-key map [left] 'egg-backward-bunsetsu)
252 (define-key map " " 'egg-next-candidate)
253 (define-key map "/" 'egg-exit-conversion)
255 "Keymap for EGG Conversion mode.")
257 (defun egg-exit-conversion-unread-char ()
259 (setq unread-command-events (list last-command-event))
260 (egg-exit-conversion))
262 (defun egg-insert-bunsetsu (bunsetsu-info last)
263 (let ((bunsetsu (egg-get-bunsetsu-converted bunsetsu-info))
266 (if (null (eq last t))
267 (insert egg-conversion-separator))
268 (add-text-properties p (point)
269 (list 'face egg-conversion-face
270 'local-map egg-conversion-map
271 (egg-bunsetsu-info) (cons egg-conversion-backend
273 'egg-bunsetsu-last last))))
275 (defun egg-insert-bunsetsu-list (bunsetsu-info-list &optional last)
276 (let ((l bunsetsu-info-list)
277 bunsetsu-info bunsetsu p)
279 (setq bunsetsu-info (car l)
282 (egg-insert-bunsetsu bunsetsu-info (and (null l) last)))))
284 (defun egg-backward-bunsetsu (n)
287 (while (and (null start) (> n 0))
289 (if (setq start (get-text-property (point) 'egg-start))
293 (signal 'beginning-of-buffer nil))))
295 (defun egg-forward-bunsetsu (n)
298 (while (and (null end) (> n 0))
300 (if (setq end (get-text-property (point) 'egg-end))
304 (signal 'end-of-buffer nil))))
306 (defun egg-get-previous-bunsetsu (p)
307 (and (null (get-text-property (1- p) 'egg-start))
308 (null (get-text-property (1- p) 'egg-bunsetsu-last))
309 (egg-get-bunsetsu-info (- p 2))))
311 (defun egg-separate-characters (str)
312 (let* ((v (string-to-vector str))
314 (i 0) (j 0) m n (nchar 0))
316 (if (setq n (egg-chinese-syllable str j))
317 (setq m (chars-in-string (substring str j (+ j n))))
318 (setq m 1 n (char-bytes (aref v i))))
319 (put-text-property j (+ j n) 'egg-char-size n str)
320 (setq nchar (1+ nchar) i (+ i m) j (+ j n)))
323 (defun egg-shrink-bunsetsu (n)
325 (egg-enlarge-bunsetsu (- n)))
327 (defun egg-enlarge-bunsetsu (n)
329 (let* ((b0 (egg-get-previous-bunsetsu (point)))
330 (b1 (egg-get-bunsetsu-info (point)))
331 (s1 (egg-get-bunsetsu-source b1))
332 (s1len (egg-separate-characters s1))
335 (last (get-text-property (point) 'egg-bunsetsu-last))
336 b2 s2 source bunsetsu-info-list beep)
338 (let ((p2 (save-excursion (forward-char) (point))))
339 (setq b2 (egg-get-bunsetsu-info p2)
340 s2 (egg-get-bunsetsu-source b2)
341 s2len (egg-separate-characters s2)
342 last (get-text-property p2 'egg-bunsetsu-last))))
343 (setq source (concat s1 s2))
346 (setq beep t chrs (get-text-property 0 'egg-char-size source)))
348 (setq beep t chrs (length source)))
351 (setq chrs (- chrs (get-text-property (1- chrs) 'egg-char-size source))
355 (setq chrs (+ chrs (get-text-property chrs 'egg-char-size source))
357 (setq bunsetsu-info-list (egg-change-bunsetsu-length b0 b1 b2 chrs))
358 (delete-region (point)
359 (progn (forward-char) (if b2 (forward-char)) (point)))
361 (egg-insert-bunsetsu-list bunsetsu-info-list last)
366 (defun egg-next-candidate (n)
368 (let ((last (get-text-property (point) 'egg-bunsetsu-last))
369 (b (egg-get-bunsetsu-info (point)))
371 (setq max+ (egg-get-number-of-candidates b))
373 (let ((prev-b (egg-get-previous-bunsetsu (point))))
374 (setq i (egg-list-candidates b prev-b)) ; there is a case I=/=0
375 (if (or (> n 1) (< n 0)) ; with N=/=1, start with I
376 (setq i (+ n i)) ; or else (N==1),
377 (setq i (if (= i 0) 1 0))) ; I:=1 when I was 0, or else I:=0
378 (setq max+ (egg-get-number-of-candidates b)))
379 (setq i (egg-get-current-candidate-number b))
381 (if (< i 0) ; go backward as if it is ring
383 (setq i (+ i max+))))
384 (if (>= i max+) ; don't go forward
387 (setq new (egg-decide-candidate b i))
389 (delete-region p (progn (forward-char) (point)))
390 (egg-insert-bunsetsu new last)
395 (defun egg-previous-candidate (n)
397 (egg-next-candidate (- n)))
399 ;; Bogus function 980220
400 (defun egg-decide-bunsetsu (&optional end-marker)
402 p bunsetsu-info-list bl)
405 (let ((bl1 (cons (egg-get-bunsetsu-info p) nil)))
407 (setq bl (setcdr bl bl1))
408 (setq bunsetsu-info-list (setq bl bl1))))
410 (remove-text-properties p (point) '(face nil
413 egg-bunsetsu-last nil))
415 (if (or (and end-marker (= p end-marker))
416 (get-text-property p 'egg-end))
419 (delete-region p (1+ p)))) ; Delete bunsetsu separator
422 (defun egg-decide-before-point ()
424 (let (bunsetsu-list bl (p (point)) source (dlen 0) l s)
426 (if (null (get-text-property (1- (point)) 'egg-start))
427 (goto-char (previous-single-property-change (point) 'egg-start)))
428 (narrow-to-region (1- (point)) p)
429 (setq source (get-text-property (1- (point)) 'egg-source))
430 (setq bunsetsu-list (setq bl (list nil)))
431 (while (< (point) (point-max))
432 ;; delete sparator/open marker
433 (delete-region (1- (point)) (point))
434 (setq bl (setcdr bl (list (egg-get-bunsetsu-info (point)))))
435 (setq dlen (+ dlen (length (egg-get-bunsetsu-source (car bl)))))
436 (if (get-text-property (point) 'egg-bunsetsu-last)
438 (egg-end-conversion (cdr bunsetsu-list))
439 (setq bunsetsu-list (setq bl (list nil)))))
442 (remove-text-properties p (point) '(face nil
445 egg-bunsetsu-last nil))))
446 (if (get-text-property (point) 'egg-end)
448 ;; delete close marker
449 (delete-region (point) (1+ (point)))
451 (run-hooks 'input-method-after-insert-chunk-hook))
452 ;; delete last from speparater to close marker
453 (delete-region (1- (point))
454 (1+ (next-single-property-change (point) 'egg-end)))
455 ;; rebuild fence mode string
458 (setq s (car (get-text-property p 'its-syl source))
462 (put-text-property dlen p
463 'its-syl (list (substring s (- dlen p)))
465 (its-restart (substring source dlen)))))
467 (defun egg-exit-conversion ()
469 (goto-char (next-single-property-change (point) 'egg-end))
470 (egg-decide-before-point))
472 (defun egg-abort-conversion ()
474 (if (null (get-text-property (1- (point)) 'egg-start))
475 (goto-char (previous-single-property-change (point) 'egg-start)))
476 (egg-decide-before-point))
478 (defun egg-select-candidate ()
480 (let ((last (get-text-property (point) 'egg-bunsetsu-last))
481 (b (egg-get-bunsetsu-info (point)))
484 (setq max+ (egg-get-number-of-candidates b))
486 (let ((prev-b (egg-get-previous-bunsetsu (point))))
487 (setq i (egg-list-candidates b prev-b))
488 (setq max+ (egg-get-number-of-candidates b)))
489 (setq i (egg-get-current-candidate-number b)))
490 (let* ((candidate-list (egg-get-all-candidates b))
492 (candidate (menudiag-select (list 'menu "
\e$B8uJd
\e(B:" l) (list (nth i l)))))
495 (if (eq candidate (car l))
499 (setq new (egg-decide-candidate b i))
501 (delete-region p (progn (forward-char) (point)))
502 (egg-insert-bunsetsu new last)
506 ;;; egg-cnv.el ends here.