--- /dev/null +++ lisp/international/w32-ime.el @@ -0,0 +1,201 @@ +;;;;; w32-ime.el ---- Meadow features for NTEmacs. +;; +;; Author H.Miyashita +;; +;;;;; + +(defgroup W32-IME nil + "w32-ime" + :group 'emacs) + +(defvar w32-last-selection nil + "It is stored the last data from Emacs.") + +;---------- + +(defvar w32-ime-on-hook nil + "Functions to eval when IME is turned on at least. +Even if IME state is not changed, these functiona are maybe called.") +(defvar w32-ime-off-hook nil + "Functions to eval when IME is turned off at least. +Even if IME state is not changed, these functiona are maybe called.") +(defvar w32-ime-buffer-switch-p t + "If this variable is nil, IME control when buffer is switched is disabled.") +(defvar w32-ime-show-mode-line t + "When t, mode line indicates IME state.") +(defvar w32-ime-mode-line-state-indicator "[O]" + "This is shown at the mode line. It is regarded as state of ime.") +(make-variable-buffer-local 'w32-ime-mode-line-state-indicator) +(put 'w32-ime-mode-line-state-indicator 'permanent-local t) +(defvar w32-ime-mode-line-state-indicator-list '("-" "[|]" "[O]") + "List of IME state indicator string.") +(defvar w32-ime-mode-line-format-original nil + "Original mode line format.") + +;; +;; Section: IME +;; + +;; ;; This is temporal solution. In the future, we will prepare +;; ;; dynamic configuration. +;; (defvar w32-ime-coding-system-language-environment-alist +;; '(("Japanese" . japanese-shift-jis) +;; ("Chinese-GB" . chinese-iso-8bit) +;; ("Chinese-BIG5" . chinese-big5) +;; ("Korean" . korean-iso-8bit))) + +;; +;; IME state indicator +;; +(global-set-key [kanji] 'ignore) +(global-set-key [compend] 'ignore) + +(defun wrap-function-to-control-ime + (function interactive-p interactive-arg &optional suffix) + "Wrap FUNCTION, and IME control is enabled when FUNCTION is called. +An original function is saved to FUNCTION-SUFFIX when suffix is string. +If SUFFIX is nil, \"-original\" is added. " + (let ((original-function + (intern (concat (symbol-name function) + (if suffix suffix "-original"))))) + (cond + ((not (fboundp original-function)) + (fset original-function + (symbol-function function)) + (fset function + (list + 'lambda '(&rest arguments) + (when interactive-p + (list 'interactive interactive-arg)) + `(cond + ((and (ime-get-mode) + (equal current-input-method "W32-IME")) + (ime-force-off) + (unwind-protect + (apply ',original-function arguments) + (when (and (not (ime-get-mode)) + (equal current-input-method "W32-IME")) + (ime-force-on)))) + (t + (apply ',original-function arguments))))))))) + +(defvar w32-ime-toroku-region-yomigana nil + "* if this variable is string, toroku-region regard this value as yomigana.") + +(defun w32-ime-toroku-region (begin end) + (interactive "r") + (let ((string (buffer-substring begin end)) + (w32-ime-buffer-switch-p nil) + (reading w32-ime-toroku-region-yomigana)) + (unless (stringp reading) + (w32-set-ime-mode 'hiragana) + (setq reading + (read-multilingual-string + (format "Input reading of \"%s\": " string) nil "W32-IME"))) + (w32-ime-register-word-dialog reading string))) + +;; for IME management system. + +(defun w32-ime-sync-state (window) + (when w32-ime-buffer-switch-p + (with-current-buffer (window-buffer window) + (let* ((frame (window-frame window)) + (ime-state (ime-get-mode))) + (cond + ((and (not ime-state) + (equal current-input-method "W32-IME")) + (ime-force-on nil) + (run-hooks 'w32-ime-on-hook)) + ((and ime-state + (not (equal current-input-method "W32-IME"))) +;;; (when (= (w32-ime-undetermined-string-length) 0) + (ime-force-off nil) + (run-hooks 'w32-ime-off-hook))))))) + +(defun w32-ime-set-selected-window-buffer-hook (oldbuf newwin newbuf) + (w32-ime-sync-state newwin)) + +(defun w32-ime-select-window-hook (old new) + (w32-ime-sync-state new)) + +(defun w32-ime-mode-line-update () + (cond + (w32-ime-show-mode-line + (unless (window-minibuffer-p (selected-window)) + (setq w32-ime-mode-line-state-indicator + (nth (if (ime-get-mode) 1 2) + w32-ime-mode-line-state-indicator-list)))) + (t + (setq w32-ime-mode-line-state-indicator + (nth 0 w32-ime-mode-line-state-indicator-list)))) + (force-mode-line-update)) + +(defun w32-ime-init-mode-line-display () + (unless (member 'w32-ime-mode-line-state-indicator mode-line-format) + (setq w32-ime-mode-line-format-original + (default-value 'mode-line-format)) + (if (and (stringp (car mode-line-format)) + (string= (car mode-line-format) "-")) + (setq-default mode-line-format + (cons "" + (cons 'w32-ime-mode-line-state-indicator + (cdr mode-line-format)))) + (setq-default mode-line-format + (cons "" + (cons 'w32-ime-mode-line-state-indicator + mode-line-format)))) + (force-mode-line-update t))) + +(defun w32-ime-initialize () + (when (and (or (eq system-type 'windows-nt) (eq system-type 'cygwin)) + (eq window-system 'w32) + (featurep 'w32-ime)) + (w32-ime-init-mode-line-display) + (w32-ime-mode-line-update) + (add-hook 'select-window-functions + 'w32-ime-select-window-hook) + (add-hook 'set-selected-window-buffer-functions + 'w32-ime-set-selected-window-buffer-hook) + (define-key global-map [kanji] 'toggle-input-method))) +;; (set-keyboard-coding-system 'utf-8))) + +(defun w32-ime-uninitialize () + (when (and (or (eq system-type 'windows-nt) (eq system-type 'cygwin)) + (eq window-system 'w32) + (featurep 'w32-ime)) + (setq-default mode-line-format + w32-ime-mode-line-format-original) + (force-mode-line-update t) + (remove-hook 'select-window-functions + 'w32-ime-select-window-hook) + (remove-hook 'set-selected-window-buffer-functions + 'w32-ime-set-selected-window-buffer-hook) + (define-key global-map [kanji] 'ignore))) + +(defun w32-ime-exit-from-minibuffer () + (deactivate-input-method) + (when (<= (minibuffer-depth) 1) + (remove-hook 'minibuffer-exit-hook 'w32-ime-exit-from-minibuffer))) + +(defun w32-ime-state-switch (&optional arg) + (if arg + (progn + (setq deactivate-current-input-method-function + 'w32-ime-state-switch) + (run-hooks 'input-method-activate-hook) + (run-hooks 'w32-ime-on-hook) + (setq describe-current-input-method-function nil) + (when (eq (selected-window) (minibuffer-window)) + (add-hook 'minibuffer-exit-hook 'w32-ime-exit-from-minibuffer)) + (ime-force-on)) + (setq current-input-method nil) + (run-hooks 'input-method-deactivate-hook) + (run-hooks 'w32-ime-off-hook) + (setq describe-current-input-method-function nil) + (ime-force-off)) + (w32-ime-mode-line-update)) + +(register-input-method "W32-IME" "Japanese" 'w32-ime-state-switch "" + "W32 System IME") + +(provide 'w32-ime) diff --git lisp/loadup.el lisp/loadup.el index 5ecfae170f..ec55f4863c 100644 --- lisp/loadup.el +++ lisp/loadup.el @@ -283,6 +283,7 @@ (load "w32-vars") (load "term/w32-win") (load "disp-table") + (load "international/w32-ime") (when (eq system-type 'windows-nt) (load "w32-fns") (load "ls-lisp") diff --git lisp/startup.el lisp/startup.el index 9d16b59def..ee26947b49 100644 --- lisp/startup.el +++ lisp/startup.el @@ -596,6 +596,8 @@ normal-top-level ;; `user-full-name' is now known; reset its standard-value here. (put 'user-full-name 'standard-value (list (default-value 'user-full-name))) + ;; Set language environment for using "Japanese". + (set-language-environment "Japanese") ;; If the PWD environment variable isn't accurate, delete it. (let ((pwd (getenv "PWD"))) (and (stringp pwd) diff --git src/w32fns.c src/w32fns.c index e50b7d5c3c..a47a3b435a 100644 --- src/w32fns.c +++ src/w32fns.c @@ -83,6 +83,12 @@ extern const char *map_w32_filename (const char *, const char **); #define IDC_HAND MAKEINTRESOURCE(32649) #endif +static int IME_event_off_count; +const char * const ImmGetOpenStatus_Name = "ImmGetOpenStatus"; +const char * const ImmSetOpenStatus_Name = "ImmSetOpenStatus"; +const char * const ImmGetContext_Name = "ImmGetContext"; +const char * const ImmReleaseContext_Name = "ImmReleaseContext"; + /* Prefix for system colors. */ #define SYSTEM_COLOR_PREFIX "System" #define SYSTEM_COLOR_PREFIX_LEN (sizeof (SYSTEM_COLOR_PREFIX) - 1) @@ -793,6 +799,9 @@ w32_color_map_lookup (const char *colorname) return ret; } +static void w32_set_ime_status (HWND, int); +static int w32_get_ime_status (HWND); +static LRESULT CALLBACK conversion_agent_wndproc (HWND, UINT, WPARAM, LPARAM); static void add_system_logical_colors_to_map (Lisp_Object *system_colors) @@ -4544,6 +4553,7 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) set_ime_composition_window_fn (context, &form); release_ime_context_fn (hwnd, context); + goto dflt; } /* We should "goto dflt" here to pass WM_IME_STARTCOMPOSITION to DefWindowProc, so that the composition window will actually @@ -5375,7 +5385,21 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) my_post_msg (&wmsg, hwnd, msg, wParam, lParam); return 1; + case WM_IME_NOTIFY: + if (wParam == IMN_SETOPENSTATUS) + { + if (!IME_event_off_count) + my_post_msg (&wmsg, hwnd, WM_MULE_IME_STATUS, 0, 0); + else + IME_event_off_count--; + } + goto dflt; + default: + + if (MESSAGE_IMM_COM_P(msg)) + return conversion_agent_wndproc (hwnd, msg, wParam, lParam); + /* Check for messages registered at runtime. */ if (msg == msh_mousewheel) { @@ -8150,6 +8174,201 @@ DEFUN ("system-move-file-to-trash", Fsystem_move_file_to_trash, #endif /* WINDOWSNT */ +/*********************************************************************** + Input Method Editor + ***********************************************************************/ +BOOL fIME = FALSE; + +typedef BOOL (WINAPI *IMMGETOPENSTATUSPROC)(HIMC); +IMMGETOPENSTATUSPROC ImmGetOpenStatusProc; + +typedef BOOL (WINAPI *IMMSETOPENSTATUSPROC)(HIMC, BOOL); +IMMSETOPENSTATUSPROC ImmSetOpenStatusProc; + +typedef HIMC (WINAPI *IMMGETCONTEXTPROC)(HWND); +IMMGETCONTEXTPROC ImmGetContextProc; + +typedef BOOL (WINAPI *IMMRELEASECONTEXTPROC)(HWND, HIMC); +IMMRELEASECONTEXTPROC ImmReleaseContextProc; + +static void +w32_set_ime_status (HWND hwnd, int openp) +{ + HIMC himc; + + himc = (ImmGetContextProc) (hwnd); + (ImmSetOpenStatusProc) (himc, openp); + (ImmReleaseContextProc) (hwnd, himc); +} + +static int +w32_get_ime_status (HWND hwnd) +{ + HIMC himc; + int ret; + + himc = (ImmGetContextProc) (hwnd); + ret = (ImmGetOpenStatusProc) (himc); + (ImmReleaseContextProc) (hwnd, himc); + + return ret; +} + +void +w32_ime_control_init (void) +{ + HMODULE hImm32; + + hImm32 = GetModuleHandle ("IMM32.DLL"); + if (!hImm32) + hImm32 = LoadLibrary ("IMM32.DLL"); + + fIME = FALSE; + Vime_control = Qnil; + IME_event_off_count = 0; + + if (hImm32) + { + ImmGetOpenStatusProc = + (IMMGETOPENSTATUSPROC) + GetProcAddress (hImm32, + ImmGetOpenStatus_Name); + ImmSetOpenStatusProc = + (IMMSETOPENSTATUSPROC) + GetProcAddress (hImm32, + ImmSetOpenStatus_Name); + ImmGetContextProc = + (IMMGETCONTEXTPROC) + GetProcAddress (hImm32, + ImmGetContext_Name); + ImmReleaseContextProc = + (IMMRELEASECONTEXTPROC) + GetProcAddress (hImm32, + ImmReleaseContext_Name); + + if (ImmGetOpenStatusProc && + ImmSetOpenStatusProc && + ImmGetContextProc && + ImmReleaseContextProc) + { + fIME = TRUE; + Vime_control = Qt; + } + } +} + +static LRESULT CALLBACK +conversion_agent_wndproc (HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) +{ + switch (message) + { + case WM_MULE_IMM_SET_STATUS: + if (InSendMessage()) + ReplyMessage(1); + w32_set_ime_status (hwnd, (int) wparam); + break; + + case WM_MULE_IMM_GET_STATUS: + return w32_get_ime_status (hwnd); + + default: + return DefWindowProc (hwnd, message, wparam, lparam); + } + return 0; +} + + +/* + Emacs Lisp function entries +*/ + +DEFUN ("ime-force-on", Fime_force_on, Sime_force_on, 0, 1, 0, + doc: /* Force status of IME open. */) + (Lisp_Object eventp) +{ + if (fIME && !NILP (Vime_control)) + { + HWND hwnd; + + if (!NILP (Fime_get_mode ())) + return Qnil; +#ifdef HAVE_NTGUI + if (NILP (eventp)) + IME_event_off_count++; + hwnd = FRAME_W32_WINDOW (SELECTED_FRAME ()); +#else + hwnd = hwndConsole; +#endif + SendMessage (hwnd, WM_MULE_IMM_SET_STATUS, 1, 0); + } + return Qnil; +} + +DEFUN ("ime-force-off", Fime_force_off, Sime_force_off, 0, 1, 0, + doc: /* Force status of IME close. */) + (Lisp_Object eventp) +{ + if (fIME && !NILP (Vime_control)) + { + HWND hwnd; + + if (NILP (Fime_get_mode ())) + return Qnil; +#ifdef HAVE_NTGUI + if (NILP (eventp)) + IME_event_off_count++; + hwnd = FRAME_W32_WINDOW (SELECTED_FRAME ()); +#else + hwnd = hwndConsole; +#endif + SendMessage (hwnd, WM_MULE_IMM_SET_STATUS, 0, 0); + } + return Qnil; +} + +DEFUN ("ime-get-mode", Fime_get_mode, Sime_get_mode, 0, 0, "", + doc: /* Get IME status. +t means status of IME is open. nil means it is close. */) + (void) +{ + if (fIME && !NILP (Vime_control)) + { + HWND hwnd; + int result; + +#ifdef HAVE_NTGUI + hwnd = FRAME_W32_WINDOW (SELECTED_FRAME ()); +#else + hwnd = hwndConsole; +#endif + result = SendMessage (hwnd, WM_MULE_IMM_GET_STATUS, 0, 0); + + return result ? Qt : Qnil; + } + else + return Qnil; +} + +DEFUN ("w32-set-ime-mode", + Fw32_set_ime_mode, + Sw32_set_ime_mode, 1, 2, 0, + doc: /* Set IME mode to MODE. If FRAME is omitted, the selected frame is used. */) + (Lisp_Object mode, Lisp_Object frame) +{ + /* do nothing */ + return Qnil; +} + +DEFUN ("w32-ime-register-word-dialog", + Fw32_ime_register_word_dialog, + Sw32_ime_register_word_dialog, 2, 2, 0, + doc: /* Open IME regist word dialog. */) + (Lisp_Object reading, Lisp_Object word) +{ + /* do nothing */ + return Qnil; +} + /*********************************************************************** w32 specialized functions ***********************************************************************/ @@ -10381,6 +10600,7 @@ syms_of_w32fns (void) w32_visible_system_caret_hwnd = NULL; DEFSYM (Qundefined_color, "undefined-color"); + DEFSYM (Qw32_ime_buffer_switch_p, "w32-ime-buffer-switch-p"); DEFSYM (Qcancel_timer, "cancel-timer"); DEFSYM (Qhyper, "hyper"); DEFSYM (Qsuper, "super"); @@ -10727,6 +10947,9 @@ tip frame. */); doc: /* Non-nil means don't display the abort dialog when aborting. */); w32_disable_abort_dialog = 0; + DEFVAR_LISP ("ime-control", Vime_control, doc: /* IME control flag. */); + Vime_control = Qnil; + #if 0 /* TODO: Port to W32 */ defsubr (&Sx_change_window_property); defsubr (&Sx_delete_window_property); @@ -10781,6 +11004,12 @@ tip frame. */); defsubr (&Sw32_notification_close); #endif + defsubr (&Sw32_set_ime_mode); + defsubr (&Sw32_ime_register_word_dialog); + defsubr (&Sime_force_on); + defsubr (&Sime_force_off); + defsubr (&Sime_get_mode); + #ifdef WINDOWSNT defsubr (&Sfile_system_info); defsubr (&Sdefault_printer_name); diff --git src/w32term.c src/w32term.c index 611b7c66e7..bd3d61f012 100644 --- src/w32term.c +++ src/w32term.c @@ -5499,6 +5499,19 @@ w32_read_socket (struct terminal *terminal, break; #endif + case WM_MULE_IME_STATUS: + f = x_window_to_frame (dpyinfo, msg.msg.hwnd); + + if (f && !f->iconified && f->visible) + { + inev.kind = NON_ASCII_KEYSTROKE_EVENT; + inev.code = VK_KANJI; + inev.modifiers = 0; + XSETFRAME (inev.frame_or_window, f); + inev.timestamp = msg.msg.time; + } + break; + default: /* Check for messages registered at runtime. */ if (msg.msg.message == msh_mousewheel) @@ -7315,6 +7328,7 @@ w32_initialize (void) horizontal_scroll_bar_left_border = horizontal_scroll_bar_right_border = GetSystemMetrics (SM_CYHSCROLL); } + w32_ime_control_init(); } void diff --git src/w32term.h src/w32term.h index e500b730ea..819f54e486 100644 --- src/w32term.h +++ src/w32term.h @@ -657,6 +657,20 @@ do { \ #define WM_EMACS_FILENOTIFY (WM_EMACS_START + 25) #define WM_EMACS_END (WM_EMACS_START + 26) +/* For internal communications + from window procedure to event loop. */ +#define WM_MULE_IME_STATUS (WM_USER+2201) + +/* For internal communications + from main thread to window procedure. */ +#define WM_MULE_IMM_MESSAGE_START (WM_USER+2300) +#define WM_MULE_IMM_SET_STATUS (WM_USER+2300) +#define WM_MULE_IMM_GET_STATUS (WM_USER+2301) +#define WM_MULE_IMM_MESSAGE_END (WM_USER+2399) +#define MESSAGE_IMM_COM_P(message) \ + (((message) >= WM_MULE_IMM_MESSAGE_START) && \ + ((message) <= WM_MULE_IMM_MESSAGE_END)) + #define WND_FONTWIDTH_INDEX (0) #define WND_LINEHEIGHT_INDEX (4) #define WND_BORDER_INDEX (8) @@ -870,3 +884,5 @@ extern void w32_init_main_thread (void); #ifdef CYGWIN extern int w32_message_fd; #endif /* CYGWIN */ + +extern void w32_ime_control_init (void);