]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkimmulticontext.c
GtkTextView: don't popdown a bubble if we don't have one
[~andy/gtk] / gtk / gtkimmulticontext.c
index f48fc84665d9f7943798723c1cdde48f98a012d2..712d5ca8736d83396e7295ca70b4f49796efd579 100644 (file)
@@ -12,9 +12,7 @@
  * 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.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "config.h"
 #include <locale.h>
 
 #include "gtkimmulticontext.h"
-#include "gtkimmodule.h"
+#include "gtkimmoduleprivate.h"
+#include "gtkintl.h"
+#include "gtklabel.h"
 #include "gtkmain.h"
+#include "gtkprivate.h"
 #include "gtkradiomenuitem.h"
-#include "gtkintl.h"
-#include "gtkprivate.h" /* To get redefinition of GTK_LOCALE_DIR on Win32 */
-#include "gtkalias.h"
+#include "gtkseparatormenuitem.h"
+#include "gtksettings.h"
+
+
+/**
+ * SECTION:gtkimmulticontext
+ * @Short_description: An input method context supporting multiple, loadable input methods
+ * @Title: GtkIMMulticontext
+ */
+
+
+#define NONE_ID "gtk-im-context-none"
 
 struct _GtkIMMulticontextPrivate
 {
-  GdkWindow *client_window;
-  GdkRectangle cursor_location;
-  gchar *context_id;
+  GtkIMContext          *slave;
+
+  GdkWindow             *client_window;
+  GdkRectangle           cursor_location;
 
-  guint use_preedit : 1;
-  guint have_cursor_location : 1;
-  guint focus_in : 1;
+  gchar                 *context_id;
+  gchar                 *context_id_aux;
+
+  guint                  use_preedit          : 1;
+  guint                  have_cursor_location : 1;
+  guint                  focus_in             : 1;
 };
 
+static void     gtk_im_multicontext_notify             (GObject                 *object,
+                                                        GParamSpec              *pspec);
 static void     gtk_im_multicontext_finalize           (GObject                 *object);
 
 static void     gtk_im_multicontext_set_slave          (GtkIMMulticontext       *multicontext,
@@ -86,6 +102,8 @@ static gboolean gtk_im_multicontext_delete_surrounding_cb   (GtkIMContext      *
                                                             gint               n_chars,
                                                             GtkIMMulticontext *multicontext);
 
+static void propagate_purpose (GtkIMMulticontext *context);
+
 static const gchar *global_context_id = NULL;
 
 G_DEFINE_TYPE (GtkIMMulticontext, gtk_im_multicontext, GTK_TYPE_IM_CONTEXT)
@@ -95,7 +113,9 @@ gtk_im_multicontext_class_init (GtkIMMulticontextClass *class)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
   GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
-  
+
+  gobject_class->notify = gtk_im_multicontext_notify;
+
   im_context_class->set_client_window = gtk_im_multicontext_set_client_window;
   im_context_class->get_preedit_string = gtk_im_multicontext_get_preedit_string;
   im_context_class->filter_keypress = gtk_im_multicontext_filter_keypress;
@@ -115,12 +135,15 @@ gtk_im_multicontext_class_init (GtkIMMulticontextClass *class)
 static void
 gtk_im_multicontext_init (GtkIMMulticontext *multicontext)
 {
-  multicontext->slave = NULL;
+  GtkIMMulticontextPrivate *priv;
   
   multicontext->priv = G_TYPE_INSTANCE_GET_PRIVATE (multicontext, GTK_TYPE_IM_MULTICONTEXT, GtkIMMulticontextPrivate);
-  multicontext->priv->use_preedit = TRUE;
-  multicontext->priv->have_cursor_location = FALSE;
-  multicontext->priv->focus_in = FALSE;
+  priv = multicontext->priv;
+
+  priv->slave = NULL;
+  priv->use_preedit = TRUE;
+  priv->have_cursor_location = FALSE;
+  priv->focus_in = FALSE;
 }
 
 /**
@@ -140,10 +163,11 @@ static void
 gtk_im_multicontext_finalize (GObject *object)
 {
   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (object);
-  
+  GtkIMMulticontextPrivate *priv = multicontext->priv;
+
   gtk_im_multicontext_set_slave (multicontext, NULL, TRUE);
-  g_free (multicontext->context_id);
-  g_free (multicontext->priv->context_id);
+  g_free (priv->context_id);
+  g_free (priv->context_id_aux);
 
   G_OBJECT_CLASS (gtk_im_multicontext_parent_class)->finalize (object);
 }
@@ -156,56 +180,58 @@ gtk_im_multicontext_set_slave (GtkIMMulticontext *multicontext,
   GtkIMMulticontextPrivate *priv = multicontext->priv;
   gboolean need_preedit_changed = FALSE;
   
-  if (multicontext->slave)
+  if (priv->slave)
     {
       if (!finalizing)
-       gtk_im_context_reset (multicontext->slave);
+       gtk_im_context_reset (priv->slave);
       
-      g_signal_handlers_disconnect_by_func (multicontext->slave,
+      g_signal_handlers_disconnect_by_func (priv->slave,
                                            gtk_im_multicontext_preedit_start_cb,
                                            multicontext);
-      g_signal_handlers_disconnect_by_func (multicontext->slave,
+      g_signal_handlers_disconnect_by_func (priv->slave,
                                            gtk_im_multicontext_preedit_end_cb,
                                            multicontext);
-      g_signal_handlers_disconnect_by_func (multicontext->slave,
+      g_signal_handlers_disconnect_by_func (priv->slave,
                                            gtk_im_multicontext_preedit_changed_cb,
                                            multicontext);
-      g_signal_handlers_disconnect_by_func (multicontext->slave,
+      g_signal_handlers_disconnect_by_func (priv->slave,
                                            gtk_im_multicontext_commit_cb,
                                            multicontext);
 
-      g_object_unref (multicontext->slave);
-      multicontext->slave = NULL;
+      g_object_unref (priv->slave);
+      priv->slave = NULL;
 
       if (!finalizing)
        need_preedit_changed = TRUE;
     }
-  
-  multicontext->slave = slave;
 
-  if (multicontext->slave)
+  priv->slave = slave;
+
+  if (priv->slave)
     {
-      g_object_ref (multicontext->slave);
+      g_object_ref (priv->slave);
+
+      propagate_purpose (multicontext);
 
-      g_signal_connect (multicontext->slave, "preedit-start",
+      g_signal_connect (priv->slave, "preedit-start",
                        G_CALLBACK (gtk_im_multicontext_preedit_start_cb),
                        multicontext);
-      g_signal_connect (multicontext->slave, "preedit-end",
+      g_signal_connect (priv->slave, "preedit-end",
                        G_CALLBACK (gtk_im_multicontext_preedit_end_cb),
                        multicontext);
-      g_signal_connect (multicontext->slave, "preedit-changed",
+      g_signal_connect (priv->slave, "preedit-changed",
                        G_CALLBACK (gtk_im_multicontext_preedit_changed_cb),
                        multicontext);
-      g_signal_connect (multicontext->slave, "commit",
+      g_signal_connect (priv->slave, "commit",
                        G_CALLBACK (gtk_im_multicontext_commit_cb),
                        multicontext);
-      g_signal_connect (multicontext->slave, "retrieve-surrounding",
+      g_signal_connect (priv->slave, "retrieve-surrounding",
                        G_CALLBACK (gtk_im_multicontext_retrieve_surrounding_cb),
                        multicontext);
-      g_signal_connect (multicontext->slave, "delete-surrounding",
+      g_signal_connect (priv->slave, "delete-surrounding",
                        G_CALLBACK (gtk_im_multicontext_delete_surrounding_cb),
                        multicontext);
-      
+
       if (!priv->use_preedit)  /* Default is TRUE */
        gtk_im_context_set_use_preedit (slave, FALSE);
       if (priv->client_window)
@@ -220,29 +246,45 @@ gtk_im_multicontext_set_slave (GtkIMMulticontext *multicontext,
     g_signal_emit_by_name (multicontext, "preedit-changed");
 }
 
+static const gchar *
+get_effective_context_id (GtkIMMulticontext *multicontext)
+{
+  GtkIMMulticontextPrivate *priv = multicontext->priv;
+
+  if (priv->context_id_aux)
+    return priv->context_id_aux;
+
+  if (!global_context_id)
+    global_context_id = _gtk_im_module_get_default_context_id (priv->client_window);
+
+  return global_context_id;
+}
+
 static GtkIMContext *
 gtk_im_multicontext_get_slave (GtkIMMulticontext *multicontext)
 {
-  if (!multicontext->slave)
+  GtkIMMulticontextPrivate *priv = multicontext->priv;
+
+  if (g_strcmp0 (priv->context_id, get_effective_context_id (multicontext)) != 0)
+    gtk_im_multicontext_set_slave (multicontext, NULL, FALSE);
+
+  if (!priv->slave)
     {
       GtkIMContext *slave;
 
-      g_free (multicontext->context_id);
-       
-      if (multicontext->priv->context_id)
-        multicontext->context_id = g_strdup (multicontext->priv->context_id);
-      else 
-        {
-          if (!global_context_id)
-            global_context_id = _gtk_im_module_get_default_context_id (multicontext->priv->client_window);
-          multicontext->context_id = g_strdup (global_context_id);
-        }
-      slave = _gtk_im_module_create (multicontext->context_id);
+      g_free (priv->context_id);
+
+      priv->context_id = g_strdup (get_effective_context_id (multicontext));
+
+      if (g_strcmp0 (priv->context_id, NONE_ID) == 0)
+        return NULL;
+
+      slave = _gtk_im_module_create (priv->context_id);
       gtk_im_multicontext_set_slave (multicontext, slave, FALSE);
       g_object_unref (slave);
     }
 
-  return multicontext->slave;
+  return priv->slave;
 }
 
 static void
@@ -258,38 +300,35 @@ gtk_im_multicontext_set_client_window (GtkIMContext *context,
                                       GdkWindow    *window)
 {
   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
-  GtkIMContext *slave; 
-  GdkScreen *screen; 
+  GtkIMMulticontextPrivate *priv = multicontext->priv;
+  GtkIMContext *slave;
+  GdkScreen *screen;
   GtkSettings *settings;
   gboolean connected;
 
-  multicontext->priv->client_window = window;
-
-  slave = gtk_im_multicontext_get_slave (multicontext);
+  priv->client_window = window;
 
-  if (slave)
-    gtk_im_context_set_client_window (slave, window);
-
-  if (window == NULL) 
-    return;
-   
-  screen = gdk_drawable_get_screen (GDK_DRAWABLE (window));
-  if (screen)
-    settings = gtk_settings_get_for_screen (screen);
-  else
-    settings = gtk_settings_get_default ();
-
-  connected = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (settings),
-                                                  "gtk-im-module-connected"));
-  if (!connected) 
+  if (window)
     {
-      g_signal_connect (settings, "notify::gtk-im-module",
-                        G_CALLBACK (im_module_setting_changed), NULL);
-      g_object_set_data (G_OBJECT (settings), "gtk-im-module-connected",
-                         GINT_TO_POINTER (TRUE));
+      screen = gdk_window_get_screen (window);
+      settings = gtk_settings_get_for_screen (screen);
+
+      connected = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (settings),
+                                                      "gtk-im-module-connected"));
+      if (!connected)
+        {
+          g_signal_connect (settings, "notify::gtk-im-module",
+                            G_CALLBACK (im_module_setting_changed), NULL);
+          g_object_set_data (G_OBJECT (settings), "gtk-im-module-connected",
+                             GINT_TO_POINTER (TRUE));
 
-      global_context_id = NULL;  
+          global_context_id = NULL;
+        }
     }
+
+  slave = gtk_im_multicontext_get_slave (multicontext);
+  if (slave)
+    gtk_im_context_set_client_window (slave, window);
 }
 
 static void
@@ -320,33 +359,53 @@ gtk_im_multicontext_filter_keypress (GtkIMContext *context,
   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
 
   if (slave)
-    return gtk_im_context_filter_keypress (slave, event);
+    {
+      return gtk_im_context_filter_keypress (slave, event);
+    }
   else
-    return FALSE;
+    {
+      GdkDisplay *display;
+      GdkModifierType no_text_input_mask;
+
+      display = gdk_window_get_display (event->window);
+
+      no_text_input_mask =
+        gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
+                                      GDK_MODIFIER_INTENT_NO_TEXT_INPUT);
+
+      if (event->type == GDK_KEY_PRESS &&
+          (event->state & no_text_input_mask) == 0)
+        {
+          gunichar ch;
+
+          ch = gdk_keyval_to_unicode (event->keyval);
+          if (ch != 0 && !g_unichar_iscntrl (ch))
+            {
+              gint len;
+              gchar buf[10];
+
+              len = g_unichar_to_utf8 (ch, buf);
+              buf[len] = '\0';
+
+              g_signal_emit_by_name (multicontext, "commit", buf);
+
+              return TRUE;
+            }
+        }
+    }
+
+  return FALSE;
 }
 
 static void
 gtk_im_multicontext_focus_in (GtkIMContext   *context)
 {
   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
-  GtkIMContext *slave;
+  GtkIMMulticontextPrivate *priv = multicontext->priv;
+  GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
 
-  /* If the global context type is different from the context we were
-   * using before, get rid of the old slave and create a new one
-   * for the new global context type.
-   */
-  if (multicontext->context_id == NULL || 
-      (multicontext->priv->context_id != NULL &&
-       strcmp (multicontext->priv->context_id, multicontext->context_id) != 0) ||
-      (multicontext->priv->context_id == NULL &&
-       (global_context_id == NULL ||
-        strcmp (global_context_id, multicontext->context_id) != 0)))
-    gtk_im_multicontext_set_slave (multicontext, NULL, FALSE);
+  priv->focus_in = TRUE;
 
-  slave = gtk_im_multicontext_get_slave (multicontext);
-  
-  multicontext->priv->focus_in = TRUE;
-  
   if (slave)
     gtk_im_context_focus_in (slave);
 }
@@ -355,10 +414,11 @@ static void
 gtk_im_multicontext_focus_out (GtkIMContext   *context)
 {
   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
+  GtkIMMulticontextPrivate *priv = multicontext->priv;
   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
 
-  multicontext->priv->focus_in = FALSE;
-  
+  priv->focus_in = FALSE;
+
   if (slave)
     gtk_im_context_focus_out (slave);
 }
@@ -378,10 +438,11 @@ gtk_im_multicontext_set_cursor_location (GtkIMContext   *context,
                                         GdkRectangle   *area)
 {
   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
+  GtkIMMulticontextPrivate *priv = multicontext->priv;
   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
 
-  multicontext->priv->have_cursor_location = TRUE;
-  multicontext->priv->cursor_location = *area;
+  priv->have_cursor_location = TRUE;
+  priv->cursor_location = *area;
 
   if (slave)
     gtk_im_context_set_cursor_location (slave, area);
@@ -392,11 +453,12 @@ gtk_im_multicontext_set_use_preedit (GtkIMContext   *context,
                                     gboolean       use_preedit)
 {
   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
+  GtkIMMulticontextPrivate *priv = multicontext->priv;
   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
 
   use_preedit = use_preedit != FALSE;
 
-  multicontext->priv->use_preedit = use_preedit;
+  priv->use_preedit = use_preedit;
 
   if (slave)
     gtk_im_context_set_use_preedit (slave, use_preedit);
@@ -462,7 +524,7 @@ gtk_im_multicontext_commit_cb (GtkIMContext      *slave,
                               const gchar       *str,
                               GtkIMMulticontext *multicontext)
 {
-  g_signal_emit_by_name (multicontext, "commit", str);;
+  g_signal_emit_by_name (multicontext, "commit", str);
 }
 
 static gboolean
@@ -494,7 +556,7 @@ static void
 activate_cb (GtkWidget         *menuitem,
             GtkIMMulticontext *context)
 {
-  if (GTK_CHECK_MENU_ITEM (menuitem)->active)
+  if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem)))
     {
       const gchar *id = g_object_get_data (G_OBJECT (menuitem), "gtk-context-id");
 
@@ -535,15 +597,16 @@ void
 gtk_im_multicontext_append_menuitems (GtkIMMulticontext *context,
                                      GtkMenuShell      *menushell)
 {
+  GtkIMMulticontextPrivate *priv = context->priv;
   const GtkIMContextInfo **contexts;
   guint n_contexts, i;
   GSList *group = NULL;
   GtkWidget *menuitem, *system_menuitem;
   const char *system_context_id; 
-  
-  system_context_id = _gtk_im_module_get_default_context_id (context->priv->client_window);
+
+  system_context_id = _gtk_im_module_get_default_context_id (priv->client_window);
   system_menuitem = menuitem = gtk_radio_menu_item_new_with_label (group, C_("input method menu", "System"));
-  if (!context->priv->context_id)
+  if (!priv->context_id_aux)
     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), TRUE);
   group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (menuitem));
   g_object_set_data (G_OBJECT (menuitem), I_("gtk-context-id"), NULL);
@@ -552,6 +615,15 @@ gtk_im_multicontext_append_menuitems (GtkIMMulticontext *context,
   gtk_widget_show (menuitem);
   gtk_menu_shell_append (menushell, menuitem);
 
+  menuitem = gtk_radio_menu_item_new_with_label (group, C_("input method menu", "None"));
+  if (g_strcmp0 (priv->context_id_aux, NONE_ID) == 0)
+    gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), TRUE);
+  g_object_set_data (G_OBJECT (menuitem), I_("gtk-context-id"), NONE_ID);
+  g_signal_connect (menuitem, "activate", G_CALLBACK (activate_cb), context);
+  gtk_widget_show (menuitem);
+  gtk_menu_shell_append (menushell, menuitem);
+  group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (menuitem));
+  
   menuitem = gtk_separator_menu_item_new ();
   gtk_widget_show (menuitem);
   gtk_menu_shell_append (menushell, menuitem);
@@ -568,7 +640,7 @@ gtk_im_multicontext_append_menuitems (GtkIMMulticontext *context,
            {
              /* Same translation domain as GTK+ */
              if (!(contexts[i]->domain_dirname && contexts[i]->domain_dirname[0]) ||
-                 pathnamecmp (contexts[i]->domain_dirname, GTK_LOCALEDIR) == 0)
+                 pathnamecmp (contexts[i]->domain_dirname, _gtk_get_localedir ()) == 0)
                {
                  /* Empty or NULL, domain directory, or same as
                   * GTK+. Input method may have a name in the GTK+
@@ -616,8 +688,8 @@ gtk_im_multicontext_append_menuitems (GtkIMMulticontext *context,
       menuitem = gtk_radio_menu_item_new_with_label (group,
                                                     translated_name);
       
-      if ((context->priv->context_id &&
-           strcmp (contexts[i]->context_id, context->priv->context_id) == 0))
+      if ((priv->context_id_aux &&
+           strcmp (contexts[i]->context_id, priv->context_id_aux) == 0))
         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), TRUE);
 
       if (strcmp (contexts[i]->context_id, system_context_id) == 0)
@@ -658,7 +730,9 @@ gtk_im_multicontext_append_menuitems (GtkIMMulticontext *context,
 const char *
 gtk_im_multicontext_get_context_id (GtkIMMulticontext *context)
 {
-  return context->context_id;
+  g_return_val_if_fail (GTK_IS_IM_MULTICONTEXT (context), NULL);
+
+  return context->priv->context_id;
 }
 
 /**
@@ -677,12 +751,37 @@ void
 gtk_im_multicontext_set_context_id (GtkIMMulticontext *context,
                                     const char        *context_id)
 {
+  GtkIMMulticontextPrivate *priv;
+
+  g_return_if_fail (GTK_IS_IM_MULTICONTEXT (context));
+
+  priv = context->priv;
+
   gtk_im_context_reset (GTK_IM_CONTEXT (context));
-  g_free (context->priv->context_id);
-  context->priv->context_id = g_strdup (context_id);
+  g_free (priv->context_id_aux);
+  priv->context_id_aux = g_strdup (context_id);
   gtk_im_multicontext_set_slave (context, NULL, FALSE);
 }
 
+static void
+propagate_purpose (GtkIMMulticontext *context)
+{
+  GtkInputPurpose purpose;
+  GtkInputHints hints;
+
+  if (context->priv->slave == NULL)
+    return;
 
-#define __GTK_IM_MULTICONTEXT_C__
-#include "gtkaliasdef.c"
+  g_object_get (context, "input-purpose", &purpose, NULL);
+  g_object_set (context->priv->slave, "input-purpose", purpose, NULL);
+
+  g_object_get (context, "input-hints", &hints, NULL);
+  g_object_set (context->priv->slave, "input-hints", hints, NULL);
+}
+
+static void
+gtk_im_multicontext_notify (GObject      *object,
+                            GParamSpec   *pspec)
+{
+  propagate_purpose (GTK_IM_MULTICONTEXT (object));
+}