📄 gtkimcontextxim.c
字号:
if (!context_xim->ic) { const char *name1 = NULL; XVaNestedList list1 = NULL; const char *name2 = NULL; XVaNestedList list2 = NULL; XIMStyle im_style = 0; XIC xic = NULL; if (context_xim->use_preedit && (context_xim->im_info->style & PREEDIT_MASK) == XIMPreeditCallbacks) { im_style |= XIMPreeditCallbacks; name1 = XNPreeditAttributes; list1 = set_preedit_callback (context_xim); } else if ((context_xim->im_info->style & PREEDIT_MASK) == XIMPreeditNone) im_style |= XIMPreeditNone; else im_style |= XIMPreeditNothing; if ((context_xim->im_info->style & STATUS_MASK) == XIMStatusCallbacks) { im_style |= XIMStatusCallbacks; if (name1 == NULL) { name1 = XNStatusAttributes; list1 = set_status_callback (context_xim); } else { name2 = XNStatusAttributes; list2 = set_status_callback (context_xim); } } else if ((context_xim->im_info->style & STATUS_MASK) == XIMStatusNone) im_style |= XIMStatusNone; else im_style |= XIMStatusNothing; xic = XCreateIC (context_xim->im_info->im, XNInputStyle, im_style, XNClientWindow, GDK_DRAWABLE_XID (context_xim->client_window), name1, list1, name2, list2, NULL); if (list1) XFree (list1); if (list2) XFree (list2); if (xic) { /* Don't filter key released events with XFilterEvents unless * input methods ask for. This is a workaround for Solaris input * method bug in C and European locales. It doubles each key * stroke if both key pressed and released events are filtered. * (bugzilla #81759) */ gulong mask = 0xaaaaaaaa; XGetICValues (xic, XNFilterEvents, &mask, NULL); context_xim->filter_key_release = (mask & KeyReleaseMask) != 0; set_string_conversion_callback (context_xim, xic); } context_xim->ic = xic; update_status_window (context_xim); if (xic && context_xim->has_focus) XSetICFocus (xic); } return context_xim->ic;}/***************************************************************** * Status Window handling * * A status window is a small window attached to the toplevel * that is used to display information to the user about the * current input operation. * * We claim the toplevel's status window for an input context if: * * A) The input context has a toplevel * B) The input context has the focus * C) The input context has an XIC associated with it * * Tracking A) and C) is pretty reliable since we * compute A) and create the XIC for C) ourselves. * For B) we basically have to depend on our callers * calling ::focus-in and ::focus-out at the right time. * * The toplevel is computed by walking up the GdkWindow * hierarchy from context->client_window until we find a * window that is owned by some widget, and then calling * gtk_widget_get_toplevel() on that widget. This should * handle both cases where we might have GdkWindows without widgets, * and cases where GtkWidgets have strange window hierarchies * (like a torn off GtkHandleBox.) * * The status window is visible if and only if there is text * for it; whenever a new GtkIMContextXIM claims the status * window, we blank out any existing text. We actually only * create a GtkWindow for the status window the first time * it is shown; this is an important optimization when we are * using XIM with something like a simple compose-key input * method that never needs a status window. *****************************************************************//* Called when we no longer need a status window*/static voiddisclaim_status_window (GtkIMContextXIM *context_xim){ if (context_xim->status_window) { g_assert (context_xim->status_window->context == context_xim); status_window_set_text (context_xim->status_window, ""); context_xim->status_window->context = NULL; context_xim->status_window = NULL; }}/* Called when we need a status window */static voidclaim_status_window (GtkIMContextXIM *context_xim){ if (!context_xim->status_window && context_xim->client_widget) { GtkWidget *toplevel = gtk_widget_get_toplevel (context_xim->client_widget); if (toplevel && GTK_WIDGET_TOPLEVEL (toplevel)) { StatusWindow *status_window = status_window_get (toplevel); if (status_window->context) disclaim_status_window (status_window->context); status_window->context = context_xim; context_xim->status_window = status_window; } }}/* Basic call made whenever something changed that might cause * us to need, or not to need a status window. */static voidupdate_status_window (GtkIMContextXIM *context_xim){ if (context_xim->ic && context_xim->in_toplevel && context_xim->has_focus) claim_status_window (context_xim); else disclaim_status_window (context_xim);}/* Updates the in_toplevel flag for @context_xim */static voidupdate_in_toplevel (GtkIMContextXIM *context_xim){ if (context_xim->client_widget) { GtkWidget *toplevel = gtk_widget_get_toplevel (context_xim->client_widget); context_xim->in_toplevel = (toplevel && GTK_WIDGET_TOPLEVEL (toplevel)); } else context_xim->in_toplevel = FALSE; /* Some paranoia, in case we don't get a focus out */ if (!context_xim->in_toplevel) context_xim->has_focus = FALSE; update_status_window (context_xim);}/* Callback when @widget's toplevel changes. It will always * change from NULL to a window, or a window to NULL; * we use that intermediate NULL state to make sure * that we disclaim the toplevel status window for the old * window. */static voidon_client_widget_hierarchy_changed (GtkWidget *widget, GtkWidget *old_toplevel, GtkIMContextXIM *context_xim){ update_in_toplevel (context_xim);}/* Finds the GtkWidget that owns the window, or if none, the * widget owning the nearest parent that has a widget. */static GtkWidget *widget_for_window (GdkWindow *window){ while (window) { gpointer user_data; gdk_window_get_user_data (window, &user_data); if (user_data) return user_data; window = gdk_window_get_parent (window); } return NULL;}/* Called when context_xim->client_window changes; takes care of * removing and/or setting up our watches for the toplevel */static voidupdate_client_widget (GtkIMContextXIM *context_xim){ GtkWidget *new_client_widget = widget_for_window (context_xim->client_window); if (new_client_widget != context_xim->client_widget) { if (context_xim->client_widget) { g_signal_handlers_disconnect_by_func (context_xim->client_widget, G_CALLBACK (on_client_widget_hierarchy_changed), context_xim); } context_xim->client_widget = new_client_widget; if (context_xim->client_widget) { g_signal_connect (context_xim->client_widget, "hierarchy-changed", G_CALLBACK (on_client_widget_hierarchy_changed), context_xim); } update_in_toplevel (context_xim); }}/* Called when the toplevel is destroyed; frees the status window */static voidon_status_toplevel_destroy (GtkWidget *toplevel, StatusWindow *status_window){ status_window_free (status_window);}/* Called when the screen for the toplevel changes; updates the * screen for the status window to match. */static voidon_status_toplevel_notify_screen (GtkWindow *toplevel, GParamSpec *pspec, StatusWindow *status_window){ if (status_window->window) gtk_window_set_screen (GTK_WINDOW (status_window->window), gtk_widget_get_screen (GTK_WIDGET (toplevel)));}/* Called when the toplevel window is moved; updates the position of * the status window to follow it. */static gbooleanon_status_toplevel_configure (GtkWidget *toplevel, GdkEventConfigure *event, StatusWindow *status_window){ GdkRectangle rect; GtkRequisition requisition; gint y; gint height; if (status_window->window) { height = gdk_screen_get_height (gtk_widget_get_screen (toplevel)); gdk_window_get_frame_extents (toplevel->window, &rect); gtk_widget_size_request (status_window->window, &requisition); if (rect.y + rect.height + requisition.height < height) y = rect.y + rect.height; else y = height - requisition.height; gtk_window_move (GTK_WINDOW (status_window->window), rect.x, y); } return FALSE;}/* Frees a status window and removes its link from the status_windows list */static voidstatus_window_free (StatusWindow *status_window){ status_windows = g_slist_remove (status_windows, status_window); if (status_window->context) status_window->context->status_window = NULL; g_signal_handlers_disconnect_by_func (status_window->toplevel, G_CALLBACK (on_status_toplevel_destroy), status_window); g_signal_handlers_disconnect_by_func (status_window->toplevel, G_CALLBACK (on_status_toplevel_notify_screen), status_window); g_signal_handlers_disconnect_by_func (status_window->toplevel, G_CALLBACK (on_status_toplevel_configure), status_window); if (status_window->window) gtk_widget_destroy (status_window->window); g_object_set_data (G_OBJECT (status_window->toplevel), "gtk-im-xim-status-window", NULL); g_free (status_window);}/* Finds the status window object for a toplevel, creating it if necessary. */static StatusWindow *status_window_get (GtkWidget *toplevel){ StatusWindow *status_window; status_window = g_object_get_data (G_OBJECT (toplevel), "gtk-im-xim-status-window"); if (status_window) return status_window; status_window = g_new0 (StatusWindow, 1); status_window->toplevel = toplevel; status_windows = g_slist_prepend (status_windows, status_window); g_signal_connect (toplevel, "destroy", G_CALLBACK (on_status_toplevel_destroy), status_window); g_signal_connect (toplevel, "configure_event", G_CALLBACK (on_status_toplevel_configure), status_window); g_signal_connect (toplevel, "notify::screen", G_CALLBACK (on_status_toplevel_notify_screen), status_window); g_object_set_data (G_OBJECT (toplevel), "gtk-im-xim-status-window", status_window); return status_window;}/* Draw the background (normally white) and border for the status window */static gbooleanon_status_window_expose_event (GtkWidget *widget, GdkEventExpose *event){ gdk_draw_rectangle (widget->window, widget->style->base_gc [GTK_STATE_NORMAL], TRUE, 0, 0, widget->allocation.width, widget->allocation.height); gdk_draw_rectangle (widget->window, widget->style->text_gc [GTK_STATE_NORMAL], FALSE, 0, 0, widget->allocation.width - 1, widget->allocation.height - 1); return FALSE;}/* We watch the ::style-set signal for our label widget * and use that to change it's foreground color to match * the 'text' color of the toplevel window. The text/base * pair of colors might be reversed from the fg/bg pair * that are normally used for labels. */static voidon_status_window_style_set (GtkWidget *toplevel, GtkStyle *previous_style, GtkWidget *label){ gint i; for (i = 0; i < 5; i++) gtk_widget_modify_fg (label, i, &toplevel->style->text[i]);}/* Creates the widgets for the status window; called when we * first need to show text for the status window. */static voidstatus_window_make_window (StatusWindow *status_window){ GtkWidget *window; GtkWidget *status_label; status_window->window = gtk_window_new (GTK_WINDOW_POPUP); window = status_window->window; gtk_window_set_resizable (GTK_WINDOW (window), FALSE); gtk_widget_set_app_paintable (window, TRUE); status_label = gtk_label_new (""); gtk_misc_set_padding (GTK_MISC (status_label), 1, 1); gtk_widget_show (status_label); g_signal_connect (window, "style_set", G_CALLBACK (on_status_window_style_set), status_label); gtk_container_add (GTK_CONTAINER (window), status_label); g_signal_connect (window, "expose_event", G_CALLBACK (on_status_window_expose_event), NULL); gtk_window_set_screen (GTK_WINDOW (status_window->window), gtk_widget_get_screen (status_window->toplevel)); on_status_toplevel_configure (status_window->toplevel, NULL, status_window);}/* Updates the text in the status window, hiding or * showing the window as necessary. */static voidstatus_window_set_text (StatusWindow *status_window, const gchar *text){ if (text[0]) { GtkWidget *label; if (!status_window->window) status_window_make_window (status_window); label = GTK_BIN (status_window->window)->child; gtk_label_set_text (GTK_LABEL (label), text); gtk_widget_show (status_window->window); } else { if (status_window->window) gtk_widget_hide (status_window->window); }}/** * gtk_im_context_xim_shutdown: * * Destroys all the status windows that are kept by the XIM contexts. This * function should only be called by the XIM module exit routine. **/voidgtk_im_context_xim_shutdown (void){ while (status_windows) status_window_free (status_windows->data);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -