/* * Copyright (C) 2000 Red Hat Software * Copyright (C) 2003 Motonobu Ichimura * Copyright 2003 Sun Microsystems Inc. * * This is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Authors: Hidetoshi Tajima */ #include #include #include #include #include "iiimcf.h" #include "gtkimcontextiiim.h" #include "imswitcher.h" #include #include #include /* #define DEBUG */ #ifdef DEBUG #define DEBUG_DO(x) (x) #else #define DEBUG_DO(x) #endif struct _SwitcherInfo { GdkWindow *switcher; GdkAtom selection_atom; GdkAtom set_current_input_language_atom; GdkAtom set_current_client_atom; GdkAtom set_status_text_atom; GdkAtom set_input_language_list_atom; GdkAtom set_language_engine_list_atom; GdkAtom set_conversion_mode_atom; /* When switcher is NULL while switcher_x_window isn't and works, it is due to gdk_selection_owner_get()'s bug. See bugzilla #126375. */ Window switcher_x_window; }; /* A listener window for input method switcher */ struct _SwitcherContext { GtkWidget *invisible; gulong destroy_handler_id; gulong property_handler_id; }; static GdkFilterReturn switcher_owner_filter (GdkXEvent *xev, GdkEvent *event, gpointer data); static gboolean filter_destroy_event (Display *d, Window w, XEvent *ev, gpointer data); gboolean im_info_switcher_new (GtkIIIMInfo *info) { GdkAtom selection = GDK_NONE; SwitcherInfo *sw_info; if (info == NULL) return FALSE; selection = gdk_atom_intern ("_IIIM_SWITCHER", FALSE); sw_info = im_info_get_switcher_info (info); if (sw_info == NULL) { sw_info = g_new0 (SwitcherInfo, 1); im_info_set_switcher_info (info, sw_info); } if (selection != GDK_NONE) sw_info->switcher = gdk_selection_owner_get (selection); sw_info->selection_atom = selection; if (sw_info->switcher) gdk_window_add_filter (sw_info->switcher, switcher_owner_filter, info); else { /* this could be due to bugzilla 126375, hence try to do Xlib directly. */ GdkScreen *screen; GdkDisplay *display; Atom x_atom; Window xwindow; screen = im_info_get_screen (info); if (screen == None) return FALSE; display = gdk_screen_get_display (screen); x_atom = gdk_x11_atom_to_xatom_for_display (display, selection); xwindow = XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display), x_atom); if (xwindow == None) { DEBUG_DO (g_message ("Unable to find input method switcher")); return FALSE; } sw_info->switcher_x_window = xwindow; _XRegisterFilterByType (GDK_DISPLAY_XDISPLAY (display), xwindow, DestroyNotify, DestroyNotify, filter_destroy_event, info); XSelectInput (GDK_DISPLAY_XDISPLAY (display), xwindow, StructureNotifyMask); } sw_info->set_current_input_language_atom = gdk_atom_intern ("_IIIM_SWITCHER_CURRENT_INPUT_LANGUAGE", FALSE); sw_info->set_current_client_atom = gdk_atom_intern ("_IIIM_SWITCHER_CURRENT_CLIENT", FALSE); sw_info->set_status_text_atom = gdk_atom_intern ("_IIIM_SWITCHER_STATUS_TEXT", FALSE); sw_info->set_input_language_list_atom = gdk_atom_intern ("_IIIM_SWITCHER_INPUT_LANGUAGE_LIST", FALSE); sw_info->set_language_engine_list_atom = gdk_atom_intern ("_IIIM_SWITCHER_LANGUAGE_ENGINE_LIST", FALSE); sw_info->set_conversion_mode_atom = gdk_atom_intern ("_IIIM_SWITCHER_SET_CONVERSION_MODE", FALSE); return TRUE; } static void destroy_switcher_window (GtkWidget *widget, GtkIMContextIIIM *context_iiim) { SwitcherContext *w = context_iiim->switcher_context; if (!w) return; gtk_widget_destroy (w->invisible); g_free (w); context_iiim->switcher_context = NULL; return; } static void property_notify_switcher_window (GtkWidget *widget, GdkEventProperty *ev, GtkIMContextIIIM *context_iiim) { GdkAtom type; guchar *data = NULL; gint format; gint length; SwitcherInfo *sw_info = im_info_get_switcher_info (context_iiim->iiim_info); if (context_iiim->context == NULL) return; if (ev->atom == sw_info->set_current_input_language_atom) { gdk_property_get (widget->window, ev->atom, ev->atom, 0, INT_MAX, FALSE, &type, &format, &length, &data); im_context_initialize_with_input_language (context_iiim, data); g_free (data); } if (ev->atom == sw_info->set_conversion_mode_atom) { gdk_property_get (widget->window, ev->atom, ev->atom, 0, INT_MAX, FALSE, &type, &format, &length, &data); im_context_change_conversion_mode (context_iiim, data); g_free (data); } return; } void im_context_switcher_set_status_text (GtkIMContextIIIM *context_iiim, gchar *utf8) { GtkIIIMInfo *info = context_iiim->iiim_info; SwitcherInfo *sw_info = im_info_get_switcher_info (info); if (sw_info == NULL) return; if (sw_info->switcher) { gdk_property_change (sw_info->switcher, sw_info->set_status_text_atom, sw_info->set_status_text_atom, 8, GDK_PROP_MODE_REPLACE, (unsigned char*)utf8, strlen (utf8)); } else if (sw_info->switcher_x_window) { GdkScreen *screen = im_info_get_screen (info); GdkDisplay *display = gdk_screen_get_display (screen); Atom x_atom = gdk_x11_atom_to_xatom_for_display (display, sw_info->set_status_text_atom); XChangeProperty (GDK_DISPLAY_XDISPLAY (display), sw_info->switcher_x_window, x_atom, x_atom, 8, PropModeReplace, (guchar *)utf8, strlen (utf8)); } } void im_context_switcher_set_conversion_mode (GtkIMContextIIIM *context_iiim) { GtkIIIMInfo *info = context_iiim->iiim_info; SwitcherInfo *sw_info = im_info_get_switcher_info (info); IIIMF_status st; gint conversion_mode = FALSE; if (sw_info == NULL) return; st = iiimcf_get_current_conversion_mode (context_iiim->context, &conversion_mode); if (sw_info->switcher) { gdk_property_change (sw_info->switcher, sw_info->set_conversion_mode_atom, sw_info->set_conversion_mode_atom, 32, GDK_PROP_MODE_REPLACE, (unsigned char*)&conversion_mode, 1); } else if (sw_info->switcher_x_window) { GdkScreen *screen = im_info_get_screen (info); GdkDisplay *display = gdk_screen_get_display (screen); Atom x_atom = gdk_x11_atom_to_xatom_for_display (display, sw_info->set_conversion_mode_atom); XChangeProperty (GDK_DISPLAY_XDISPLAY (display), sw_info->switcher_x_window, x_atom, x_atom, 32, PropModeReplace, (guchar *)&conversion_mode, 1); } } void im_context_switcher_set_input_language (GtkIMContextIIIM *context_iiim, gchar *input_lang) { GtkIIIMInfo *info = context_iiim->iiim_info; SwitcherInfo *sw_info = im_info_get_switcher_info (info); if (sw_info == NULL) return; if (input_lang == NULL) input_lang = context_iiim->current_language; if (sw_info->switcher && input_lang) { gdk_property_change (sw_info->switcher, sw_info->set_current_input_language_atom, sw_info->set_current_input_language_atom, 8, GDK_PROP_MODE_REPLACE, (unsigned char*)input_lang, strlen (input_lang)); } else if (sw_info->switcher_x_window && input_lang) { GdkScreen *screen = im_info_get_screen (info); GdkDisplay *display = gdk_screen_get_display (screen); Atom x_atom = gdk_x11_atom_to_xatom_for_display (display, sw_info->set_current_input_language_atom); XChangeProperty (GDK_DISPLAY_XDISPLAY (display), sw_info->switcher_x_window, x_atom, x_atom, 8, PropModeReplace, (guchar *)input_lang, strlen (input_lang)); } } void im_context_switcher_set_language_engine_list (GtkIMContextIIIM *context_iiim, gchar *le_list) { GtkIIIMInfo *info = context_iiim->iiim_info; SwitcherInfo *sw_info = im_info_get_switcher_info (info); gsize len; if (sw_info == NULL) return; len = strlen (le_list); if (len == 0) return; if (sw_info->switcher) gdk_property_change (sw_info->switcher, sw_info->set_language_engine_list_atom, sw_info->set_language_engine_list_atom, 8, GDK_PROP_MODE_REPLACE, (guchar*)le_list, len); else if (sw_info->switcher_x_window) { GdkScreen *screen = im_info_get_screen (info); GdkDisplay *display = gdk_screen_get_display (screen); Atom x_atom = gdk_x11_atom_to_xatom_for_display (display, sw_info->set_language_engine_list_atom); XChangeProperty (GDK_DISPLAY_XDISPLAY (display), sw_info->switcher_x_window, x_atom, x_atom, 8, PropModeReplace, (guchar *)le_list, len); } } void im_context_switcher_set_language_list (GtkIMContextIIIM *context_iiim, IIIMCF_language *lang_list, int n_lang) { GtkIIIMInfo *info = context_iiim->iiim_info; SwitcherInfo *sw_info = im_info_get_switcher_info (info); gchar *languages; gchar *ptr; IIIMF_status st; char *langid; gint i; gsize len; const char *separator = ";"; gsize separator_len; if (sw_info == NULL) return; if (lang_list == NULL || n_lang == 0) return; /* First part, getting length */ st = iiimcf_get_language_id (lang_list[0], (const char **) &langid); if (st != IIIMF_STATUS_SUCCESS) return; separator_len = strlen (separator); len = strlen (langid); for (i = 1; i < n_lang; i++) { st = iiimcf_get_language_id (lang_list[i], (const char **) &langid); if (st != IIIMF_STATUS_SUCCESS) continue; len += strlen (langid); } len += separator_len * (i - 1); /* Second part, building string */ languages = g_new (gchar, len + 1); st = iiimcf_get_language_id (lang_list[0], (const char **) &langid); ptr = g_stpcpy (languages, langid); for (i = 1; i < n_lang; i++) { ptr = g_stpcpy (ptr, separator); st = iiimcf_get_language_id (lang_list[i], (const char **) &langid); if (st != IIIMF_STATUS_SUCCESS) continue; ptr = g_stpcpy (ptr, langid); } if (sw_info->switcher) gdk_property_change (sw_info->switcher, sw_info->set_input_language_list_atom, sw_info->set_input_language_list_atom, 8, GDK_PROP_MODE_REPLACE, (guchar*)languages, len); else if (sw_info->switcher_x_window) { GdkScreen *screen = im_info_get_screen (info); GdkDisplay *display = gdk_screen_get_display (screen); Atom x_atom = gdk_x11_atom_to_xatom_for_display (display, sw_info->set_input_language_list_atom); XChangeProperty (GDK_DISPLAY_XDISPLAY (display), sw_info->switcher_x_window, x_atom, x_atom, 8, PropModeReplace, (guchar *)languages, len); } g_free (languages); } void im_context_switcher_new (GtkIMContextIIIM *context_iiim) { SwitcherContext *w = g_new0 (SwitcherContext, 1); g_return_if_fail (context_iiim != NULL); g_return_if_fail (context_iiim->iiim_info != NULL); w->invisible = gtk_invisible_new (); gtk_widget_realize (w->invisible); gtk_widget_add_events (w->invisible, GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK); w->destroy_handler_id = g_signal_connect (G_OBJECT (w->invisible), "destroy", G_CALLBACK (destroy_switcher_window), context_iiim); w->property_handler_id = g_signal_connect (G_OBJECT (w->invisible), "property-notify-event", G_CALLBACK (property_notify_switcher_window), context_iiim); context_iiim->switcher_context = w; } gboolean im_info_switcher_active (GtkIIIMInfo *info) { SwitcherInfo *sw_info = im_info_get_switcher_info (info); return (sw_info && (sw_info->switcher || sw_info->switcher_x_window)); } void im_context_switcher_set_focus (GtkIMContextIIIM *context_iiim) { SwitcherContext *w; GtkIIIMInfo *info = context_iiim->iiim_info; SwitcherInfo *sw_info; if (!im_info_switcher_active (info)) { im_info_switcher_new (info); if (!im_info_switcher_active (info)) return; } if (context_iiim->switcher_context == NULL) im_context_switcher_new (context_iiim); w = context_iiim->switcher_context; sw_info = im_info_get_switcher_info (info); if (w && w->invisible) gdk_selection_convert (w->invisible->window, sw_info->selection_atom, sw_info->set_current_client_atom, gtk_get_current_event_time ()); } /* input method switcher */ static GdkFilterReturn switcher_owner_filter (GdkXEvent *xev, GdkEvent *event, gpointer data) { XEvent *xevent = (GdkXEvent *)xev; GtkIIIMInfo *info = data; SwitcherInfo *sw_info = im_info_get_switcher_info (info); if (sw_info != NULL) { switch (event->type) { case SelectionClear: sw_info->switcher = NULL; sw_info->switcher_x_window = None; g_free (sw_info); im_info_set_switcher_info (info, NULL); break; default: break; } } return GDK_FILTER_CONTINUE; } static gboolean filter_destroy_event (Display *d, Window w, XEvent *ev, gpointer data) { GtkIIIMInfo *info = data; SwitcherInfo *sw_info = im_info_get_switcher_info (info); if (sw_info && sw_info->switcher_x_window == w) { sw_info->switcher = NULL; sw_info->switcher_x_window = None; g_free (sw_info); im_info_set_switcher_info (info, NULL); return TRUE; } return FALSE; } void im_info_switcher_shutdown (GtkIIIMInfo *info) { SwitcherInfo *sw_info = im_info_get_switcher_info (info); if (sw_info) { if (sw_info->switcher) gdk_window_remove_filter (sw_info->switcher, switcher_owner_filter, info); else if (sw_info->switcher_x_window) { GdkScreen *screen = im_info_get_screen (info); GdkDisplay *display = gdk_screen_get_display (screen); _XUnregisterFilter (GDK_DISPLAY_XDISPLAY (display), sw_info->switcher_x_window, filter_destroy_event, info); } } } void im_context_switcher_finalize (GtkIMContextIIIM *context_iiim) { SwitcherContext *w = context_iiim->switcher_context; if (w == NULL) return; g_signal_handler_disconnect (G_OBJECT (w->invisible), w->destroy_handler_id); g_signal_handler_disconnect (G_OBJECT (w->invisible), w->property_handler_id); gtk_widget_destroy (w->invisible); g_free (w); context_iiim->switcher_context = NULL; return; }