;;; egg-cnv.el --- Conversion Backend in Egg Input Method Architecture ;; Copyright (C) 1997, 1998 Mule Project, ;; Powered by Electrotechnical Laboratory, JAPAN. ;; Project Leader: Satoru Tomura ;; Author: NIIBE Yutaka ;; KATAYAMA Yoshio ;; Maintainer: NIIBE Yutaka ;; Keywords: mule, multilingual, input method ;; This file is part of EGG. ;; EGG is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; EGG is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;;; Code: (defvar egg-current-language) (make-variable-buffer-local 'egg-current-language) (put 'egg-current-language 'permanent-local t) (defsubst egg-bunsetsu-info () 'intangible) (defun egg-get-bunsetsu-info (p) (let ((bunsetsu-info (get-text-property p (egg-bunsetsu-info)))) (if bunsetsu-info (setq egg-conversion-backend (get-text-property p 'egg-backend) egg-current-language (get-text-property p 'egg-lang))) bunsetsu-info)) ;; (defconst egg-conversion-backend-other-languages [ egg-init-other-languages egg-start-conversion-other-languages egg-get-bunsetsu-converted-other-languages egg-get-bunsetsu-source-other-languages egg-list-candidates-other-languages egg-get-number-of-candidates-other-languages egg-get-current-candidate-number-other-languages egg-get-all-candidates-other-languages egg-decide-candidate-other-languages egg-change-bunsetsu-length-other-languages egg-end-conversion-other-languages nil egg-fini-other-languages ]) (defun egg-init-other-languages () ) (defun egg-start-conversion-other-languages (yomi-string language) (list yomi-string)) (defun egg-get-bunsetsu-converted-other-languages (bunsetsu-info) bunsetsu-info) (defun egg-get-bunsetsu-source-other-languages (bunsetsu-info) bunsetsu-info) (defun egg-list-candidates-other-languages (bunsetsu-info prev-bunsetsu-info) 1) (defun egg-get-number-of-candidates-other-languages (bunsetsu-info) 1) (defun egg-get-current-candidate-number-other-languages (bunsetsu-info) 0) (defun egg-get-all-candidates-other-languages (bunsetsu-info) (list bunsetsu-info)) (defun egg-decide-candidate-other-languages (bunsetsu-info candidate-pos) bunsetsu-info) (defun egg-change-bunsetsu-length-other-languages (b0 b1 b2 len) (let ((s (concat b1 b2))) (set-text-properties 0 (length s) nil s) (if (= len (length s)) (list s) (list (substring s 0 len) (substring s len))))) (defun egg-end-conversion-other-languages (bunsetsu-info-list abort) nil) (defun egg-fini-other-languages (language) nil) (defvar egg-conversion-backend-alist nil) (make-variable-buffer-local 'egg-conversion-backend-alist) (defvar egg-conversion-backend nil) (make-variable-buffer-local 'egg-conversion-backend) (defvar egg-finalize-backend-alist nil) (defun egg-set-current-backend (language) (setq egg-conversion-backend (cdr (assoc language egg-conversion-backend-alist))) (if (null egg-conversion-backend) (setq egg-conversion-backend egg-conversion-backend-other-languages))) (defun egg-initialize-backend (language) (egg-set-current-backend language) (funcall (aref egg-conversion-backend 0))) (defun egg-start-conversion (yomi-string language) (egg-set-current-backend language) (funcall (aref egg-conversion-backend 1) yomi-string language)) (defun egg-get-bunsetsu-converted (bunsetsu-info) (funcall (aref egg-conversion-backend 2) bunsetsu-info)) (defun egg-get-bunsetsu-source (bunsetsu-info) (funcall (aref egg-conversion-backend 3) bunsetsu-info)) (defun egg-list-candidates (bunsetsu-info prev-bunsetsu-info) (funcall (aref egg-conversion-backend 4) bunsetsu-info prev-bunsetsu-info)) (defun egg-get-number-of-candidates (bunsetsu-info) (funcall (aref egg-conversion-backend 5) bunsetsu-info)) (defun egg-get-current-candidate-number (bunsetsu-info) (funcall (aref egg-conversion-backend 6) bunsetsu-info)) (defun egg-get-all-candidates (bunsetsu-info) (funcall (aref egg-conversion-backend 7) bunsetsu-info)) (defun egg-decide-candidate (bunsetsu-info candidate-pos) (funcall (aref egg-conversion-backend 8) bunsetsu-info candidate-pos)) (defun egg-change-bunsetsu-length (b0 b1 b2 len) (funcall (aref egg-conversion-backend 9) b0 b1 b2 len)) (defun egg-end-conversion (bunsetsu-info-list abort) (funcall (aref egg-conversion-backend 10) bunsetsu-info-list abort)) (defun egg-start-reverse-conversion (yomi-string language) (egg-set-current-backend language) (if (aref egg-conversion-backend 11) (funcall (aref egg-conversion-backend 11) yomi-string language) (beep))) (defun egg-finalize-backend () (let ((alist egg-finalize-backend-alist)) (while alist (funcall (car (car (car alist))) (cdr (car (car alist)))) (setq alist (cdr alist))))) (defun egg-set-conversion-backend (backend langs &optional force) (let (pair) (if backend (setq egg-conversion-backend backend) (setq backend egg-conversion-backend)) (while langs (setq pair (assoc (car langs) egg-conversion-backend-alist)) (cond ((null pair) (setq egg-conversion-backend-alist (cons (cons (car langs) backend) egg-conversion-backend-alist))) (force (setcdr pair backend))) (setq pair (cons (aref backend (1- (length backend))) (car langs))) (if (null (assoc pair egg-finalize-backend-alist)) (setq egg-finalize-backend-alist (cons (list pair) egg-finalize-backend-alist))) (setq langs (cdr langs))))) (defvar egg-conversion-open "|" "*フェンスの始点を示す文字列 (1 文字以上)") (defvar egg-conversion-close "|" "*フェンスの終点を示す文字列 (1 文字以上)") (defvar egg-conversion-face nil "*フェンス表示に用いる face または nil") (defvar egg-conversion-separator " ") (defun egg-get-conversion-face () (let ((face (and (listp egg-conversion-face) (or (assoc egg-current-language egg-conversion-face) (assoc t egg-conversion-face))))) (if face (cdr face) egg-conversion-face))) ;; (defun egg-convert-region (start end) (interactive "r") (if (>= start end) ;; nothing to do nil (remove-text-properties start end '(read-only nil intangible nil)) (goto-char start) (insert egg-conversion-open) (let ((inhibit-read-only t) (max (make-marker)) bunsetsu-info-list contin p s e result) (setq p (+ (point) (- end start))) (set-text-properties start (point) (list 'read-only t 'egg-start t 'egg-source (buffer-substring (point) p))) (if egg-conversion-face (put-text-property start (point) 'invisible t)) (setq start (point)) (goto-char p) (insert egg-conversion-close) (set-text-properties p (point) '(read-only t rear-nonsticky t egg-end t)) (if egg-conversion-face (put-text-property p (point) 'invisible t)) (set-marker max p) (egg-separate-languages start max) (goto-char start) (while (< (point) max) (setq egg-current-language (get-text-property (point) 'egg-lang) s (point) e (point)) (while (and (< e max) (equal egg-current-language (get-text-property e 'egg-lang))) (setq e (next-single-property-change e 'egg-lang nil max))) (condition-case result (setq bunsetsu-info-list (egg-start-conversion (buffer-substring-no-properties s e) egg-current-language)) (error ; XXX: catching all error is BADBADBAD (setq egg-conversion-backend egg-conversion-backend-other-languages bunsetsu-info-list (egg-start-conversion-other-languages (buffer-substring-no-properties s e) egg-current-language)) (message "egg %s backend: %s" egg-current-language (cadr result)))) (setq contin (< e max)) (delete-region s e) (egg-insert-bunsetsu-list bunsetsu-info-list (if (< (point) max) 'contine t))) (set-marker max nil) (goto-char start)))) (defun egg-separate-languages (start end &optional use-context) (let (lang last-lang last-chinese p pe l c cset) ;; 1st pass -- mark undefined Chinese part (goto-char start) (and use-context (setq last-lang (get-text-property (1- (point)) 'egg-lang)) (or (equal last-lang "Chinese-GB") (equal last-lang "Chinese-CNS")) (setq last-chinese last-lang)) (while (< (point) end) (setq p (point) pe (next-single-property-change (point) 'egg-lang nil end)) (cond ((get-text-property (point) 'egg-lang) (goto-char pe) (setq lang nil)) ((setq l (egg-chinese-syllable (buffer-substring p pe))) (goto-char (+ p l)) (setq lang "Chinese")) ((progn (setq c (following-char) cset (char-charset c)) (eq cset 'chinese-sisheng)) (forward-char) (setq lang "Chinese")) ((eq cset 'ascii) (skip-chars-forward "\0-\177" pe) (if (eq (char-charset (following-char)) 'chinese-sisheng) (goto-char (max (1+ pp) (- (point) 6)))) (setq lang nil)) ((eq cset 'composition) (forward-char) (setq lang (egg-charset-to-language (char-charset (car (decompose-composite-char c 'list)))))) (t (skip-chars-forward (concat (vector (make-char cset 33 33)) "-" (vector (make-char cset 127 127))) pe) (setq lang (egg-charset-to-language cset)))) (if lang (put-text-property p (point) 'egg-lang lang))) ;; 2nd pass -- set language property (goto-char start) (while (< (point) end) (setq lang (get-text-property (point) 'egg-lang)) (cond ((null lang) (setq lang (or last-lang (egg-next-part-lang end)))) ((equal lang "Chinese") (setq lang (or last-chinese (egg-next-chinese-lang end))))) (setq last-lang lang) (if (or (equal lang "Chinese-GB") (equal lang "Chinese-CNS")) (setq last-chinese lang)) (setq p (point)) (goto-char (next-single-property-change (point) 'egg-lang nil end)) (set-text-properties p (point) (list 'egg-lang lang))))) (defun egg-charset-to-language (charset) (let ((list language-info-alist)) (while (and list (null (memq charset (assq 'charset (car list))))) (setq list (cdr list))) (car (car list)))) (defun egg-next-part-lang (end) (let* ((p (next-single-property-change (point) 'egg-lang nil end)) (lang (get-text-property p 'egg-lang))) (if (equal lang "Chinese") (egg-next-chinese-lang end) (or lang its-current-language egg-default-language)))) (defun egg-next-chinese-lang (end) (let (p lang) (setq p (point)) (while (and (< p end) (null lang)) (setq p (next-single-property-change p 'egg-lang nil end)) (setq lang (get-text-property p 'egg-lang)) (if (null (or (equal lang "Chinese-GB") (equal lang "Chinese-CNS"))) (setq lang nil))) (cond (lang lang) ((or (equal its-current-language "Chinese-GB") (equal its-current-language "Chinese-CNS")) its-current-language) ((or (equal egg-default-language "Chinese-GB") (equal egg-default-language "Chinese-CNS")) egg-default-language) (t "Chinese-GB")))) (require 'its-keydef) (defvar egg-conversion-map (let ((map (make-sparse-keymap)) (i 33)) (while (< i 127) (define-key map (vector i) 'egg-exit-conversion-unread-char) (setq i (1+ i))) (define-key map "\C-@" 'egg-decide-first-char) (define-key map [?\C-\ ] 'egg-decide-first-char) (define-key map "\C-a" 'egg-beginning-of-conversion-buffer) (define-key map "\C-b" 'egg-backward-bunsetsu) (define-key map "\C-c" 'egg-abort-conversion) (define-key map "\C-e" 'egg-end-of-conversion-buffer) (define-key map "\C-f" 'egg-forward-bunsetsu) (define-key map "\C-h" 'egg-help-command) (define-key map "\C-i" 'egg-shrink-bunsetsu) (define-key map "\C-k" 'egg-decide-before-point) ;; (define-key map "\C-l" 'egg-exit-conversion) ; Don't override C-L (define-key map "\C-m" 'egg-exit-conversion) (define-key map "\C-n" 'egg-next-candidate) (define-key map "\C-o" 'egg-enlarge-bunsetsu) (define-key map "\C-p" 'egg-previous-candidate) (define-key map "\C-r" 'egg-reverse-convert-bunsetu) (define-key map "\M-r" 'egg-reconvert-bunsetsu) (define-key map "\M-s" 'egg-select-candidate) (define-key map [return] 'egg-exit-conversion) (define-key map [right] 'egg-forward-bunsetsu) (define-key map [left] 'egg-backward-bunsetsu) (define-key map " " 'egg-next-candidate) (its-define-select-keys map) map) "Keymap for EGG Conversion mode.") (defun egg-exit-conversion-unread-char () (interactive) (setq unread-command-events (list last-command-event)) (egg-exit-conversion)) (defun egg-insert-bunsetsu (bunsetsu-info last) (let ((bunsetsu (egg-get-bunsetsu-converted bunsetsu-info)) (p (point)) p1) (insert bunsetsu) (setq p1 (point)) (if (null (eq last t)) (insert egg-conversion-separator)) (set-text-properties p (point) (list 'read-only t (egg-bunsetsu-info) bunsetsu-info 'egg-backend egg-conversion-backend 'egg-lang egg-current-language 'egg-bunsetsu-last last 'local-map egg-conversion-map)) (if egg-conversion-face (egg-set-face p p1 (egg-get-conversion-face))))) (defun egg-insert-bunsetsu-list (bunsetsu-info-list &optional last) (let ((l bunsetsu-info-list) bunsetsu-info) (while l (setq bunsetsu-info (car l) l (cdr l)) (egg-insert-bunsetsu bunsetsu-info (and (null l) last))))) (defun egg-beginning-of-conversion-buffer (n) (interactive "p") (cond ((<= n 0) (egg-end-of-conversion-buffer 1)) ((null (get-text-property (1- (point)) 'egg-start)) (goto-char (previous-single-property-change (1- (point)) 'egg-start))))) (defun egg-end-of-conversion-buffer(n) (interactive "p") (cond ((<= n 0) (egg-beginning-of-conversion-buffer 1)) (t (goto-char (next-single-property-change (point) 'egg-end)) (backward-char)))) (defun egg-backward-bunsetsu (n) (interactive "p") (let (start) (while (and (null start) (> n 0)) (backward-char) (if (setq start (get-text-property (point) 'egg-start)) (forward-char) (setq n (1- n)))) (if (> n 0) (signal 'beginning-of-buffer nil)))) (defun egg-forward-bunsetsu (n) (interactive "p") (let (end) (while (and (null end) (> n 0)) (forward-char) (if (setq end (get-text-property (point) 'egg-end)) (backward-char) (setq n (1- n)))) (if (> n 0) (signal 'end-of-buffer nil)))) (defun egg-get-previous-bunsetsu (p) (and (null (get-text-property (1- p) 'egg-start)) (null (get-text-property (1- p) 'egg-bunsetsu-last)) (egg-get-bunsetsu-info (- p 2)))) (defun egg-separate-characters (str) (let* ((v (string-to-vector str)) (len (length v)) (i 0) (j 0) m n (nchar 0)) (while (< i len) (if (setq n (egg-chinese-syllable str j)) (setq m (chars-in-string (substring str j (+ j n)))) (setq m 1 n (char-bytes (aref v i)))) (put-text-property j (+ j n) 'egg-char-size n str) (setq nchar (1+ nchar) i (+ i m) j (+ j n))) nchar)) (defun egg-shrink-bunsetsu (n) (interactive "p") (egg-enlarge-bunsetsu (- n))) (defun egg-enlarge-bunsetsu (n) (interactive "p") (let* ((inhibit-read-only t) (b0 (egg-get-previous-bunsetsu (point))) (b1 (egg-get-bunsetsu-info (point))) (s1 (egg-get-bunsetsu-source b1)) (s1len (egg-separate-characters s1)) (s2len 0) (chrs (length s1)) (last (get-text-property (point) 'egg-bunsetsu-last)) b2 s2 source bunsetsu-info-list beep) (if (not last) (let ((p2 (save-excursion (forward-char) (point)))) (setq b2 (egg-get-bunsetsu-info p2) s2 (egg-get-bunsetsu-source b2) s2len (egg-separate-characters s2) last (get-text-property p2 'egg-bunsetsu-last)))) (setq source (concat s1 s2)) (cond ((<= n (- s1len)) (setq beep t chrs (get-text-property 0 'egg-char-size source))) ((> n s2len) (setq beep t chrs (length source))) ((< n 0) (while (< n 0) (setq chrs (- chrs (get-text-property (1- chrs) 'egg-char-size source)) n (1+ n)))) (t (while (> n 0) (setq chrs (+ chrs (get-text-property chrs 'egg-char-size source)) n (1- n))))) (setq bunsetsu-info-list (egg-change-bunsetsu-length b0 b1 b2 chrs)) (delete-region (point) (progn (forward-char) (if b2 (forward-char)) (point))) (let ((p (point))) (egg-insert-bunsetsu-list bunsetsu-info-list last) (goto-char p)) (if beep (ding)))) (defun egg-next-candidate (n) (interactive "p") (let ((inhibit-read-only t) (last (get-text-property (point) 'egg-bunsetsu-last)) (b (egg-get-bunsetsu-info (point))) new i max+ p beep) (setq max+ (egg-get-number-of-candidates b)) (if (null max+) (let ((prev-b (egg-get-previous-bunsetsu (point)))) (setq i (egg-list-candidates b prev-b)) ; there is a case I=/=0 (if (or (> n 1) (< n 0)) ; with N=/=1, start with I (setq i (+ n i)) ; or else (N==1), (setq i (if (= i 0) 1 0))) ; I:=1 when I was 0, or else I:=0 (setq max+ (egg-get-number-of-candidates b))) (setq i (egg-get-current-candidate-number b)) (setq i (+ n i))) (if (null max+) (setq beep t) (if (< i 0) ; go backward as if it is ring (while (< i 0) (setq i (+ i max+)))) (if (>= i max+) ; don't go forward (setq i (1- max+) beep t)) (setq new (egg-decide-candidate b i)) (setq p (point)) (delete-region p (progn (forward-char) (point))) (egg-insert-bunsetsu new last) (goto-char p)) (if beep (ding)))) (defun egg-previous-candidate (n) (interactive "p") (egg-next-candidate (- n))) (defun egg-reconvert-bunsetsu-internal (n func) (let ((inhibit-read-only t) (p (point)) source last bunsetsu-list) (if (<= n 0) (beep) (while (and (null last) (> n 0)) (setq source (concat source (egg-get-bunsetsu-converted (egg-get-bunsetsu-info (point)))) last (get-text-property (point) 'egg-bunsetsu-last) n (1- n)) (forward-char)) (cond ((> n 0) (beep)) ((setq bunsetsu-list (funcall func source egg-current-language)) (delete-region p (point)) (egg-insert-bunsetsu-list bunsetsu-list (if (eq last t) t 'contine)) (goto-char p) (if (egg-get-previous-bunsetsu p) (progn (backward-char) (put-text-property (point) p 'egg-bunsetsu-last 'contine) (forward-char)))))))) (defun egg-reverse-convert-bunsetu (n) (interactive "p") (egg-reconvert-bunsetsu-internal n 'egg-start-reverse-conversion)) (defun egg-reconvert-bunsetsu (n) (interactive "p") (egg-reconvert-bunsetsu-internal n 'egg-start-conversion)) (defun egg-decide-before-point () (interactive) (let ((inhibit-read-only t) (len (length egg-conversion-open)) bunsetsu-list bl (p (point)) source lang s) (save-restriction (if (null (get-text-property (1- (point)) 'egg-start)) (goto-char (previous-single-property-change (point) 'egg-start))) (narrow-to-region (- (point) len) p) (setq bunsetsu-list (setq bl (list nil))) (while (< (point) (point-max)) ;; delete sparator/open marker (delete-region (- (point) len) (point)) (setq len 1 bl (setcdr bl (list (egg-get-bunsetsu-info (point))))) (if (get-text-property (point) 'egg-bunsetsu-last) (progn (egg-end-conversion (cdr bunsetsu-list) nil) (setq bunsetsu-list (setq bl (list nil))))) (setq p (point)) (forward-char) (set-text-properties p (point) nil))) (if (cdr bunsetsu-list) (egg-end-conversion (cdr bunsetsu-list) nil)) (if (get-text-property (point) 'egg-end) (progn ;; delete close marker (delete-region (point) (+ (point) (length egg-conversion-close))) (egg-do-auto-fill) (run-hooks 'input-method-after-insert-chunk-hook)) ;; delete from last speparater (delete-region (1- (point)) (point)) (setq source "") (while (null (get-text-property (point) 'egg-end)) (setq s (egg-get-bunsetsu-source (egg-get-bunsetsu-info (point)))) (put-text-property 0 (length s) 'egg-lang egg-current-language s) (setq source (concat source s)) (setq p (point)) (forward-char) (delete-region p (point))) ;; delete close marker (delete-region (point) (+ (point) (length egg-conversion-close))) (its-restart source t)))) (defun egg-exit-conversion () (interactive) (goto-char (next-single-property-change (point) 'egg-end)) (egg-decide-before-point)) (defun egg-abort-conversion () (interactive) (let ((inhibit-read-only t) start bunsetsu-list source) (if (get-text-property (1- (point)) 'egg-start) (setq start (1- (point))) (setq start (1- (previous-single-property-change (point) 'egg-start)))) (goto-char start) (setq source (get-text-property (point) 'egg-source)) ;; Delete open marker (delete-region start (+ start (length egg-conversion-open))) (setq bunsetsu-list (egg-decide-bunsetsu)) ;; Delete close marker (delete-region (point) (+ (point) (length egg-conversion-close))) (egg-end-conversion bunsetsu-list t) (delete-region start (point)) (its-restart source))) (defun egg-select-candidate () (interactive) (let ((inhibit-read-only t) (last (get-text-property (point) 'egg-bunsetsu-last)) (b (egg-get-bunsetsu-info (point))) (in-loop t) new i max+ p) (setq max+ (egg-get-number-of-candidates b)) (if (null max+) (let ((prev-b (egg-get-previous-bunsetsu (point)))) (setq i (egg-list-candidates b prev-b)) (setq max+ (egg-get-number-of-candidates b))) (setq i (egg-get-current-candidate-number b))) (let (candidate-list candidate l) (if (null max+) ;; fake 1 candidate (menudiag-select (list 'menu "候補:" (list (egg-get-bunsetsu-converted b)) (list (egg-get-bunsetsu-converted b)))) (setq candidate-list (egg-get-all-candidates b) l candidate-list candidate (menudiag-select (list 'menu "候補:" l) (list (nth i l)))) (setq i 0) (while in-loop (if (eq candidate (car l)) (setq in-loop nil) (setq l (cdr l) i (1+ i)))) (setq new (egg-decide-candidate b i)) (setq p (point)) (delete-region p (progn (forward-char) (point))) (egg-insert-bunsetsu new last) (goto-char p))))) (provide 'egg-cnv) ;;; egg-cnv.el ends here.