Fix focus-out autocommit using an idle handler
authorDavyd Madeley <davyd@madeley.id.au>
Fri, 10 Apr 2009 16:54:49 +0000 (16:54 +0000)
committerXavier Claessens <xclaesse@src.gnome.org>
Fri, 10 Apr 2009 16:54:49 +0000 (16:54 +0000)
From: Davyd Madeley <davyd@madeley.id.au>

svn path=/trunk/; revision=2796

libempathy-gtk/empathy-presence-chooser.c

index 66ff887d6747b9e0931d4ae1f95d46e4018cdd1c..9dea442f2c147e74a4abaa1180e8c4fb0b436643 100644 (file)
@@ -59,6 +59,7 @@ typedef struct {
        gboolean     editing_status;
        int          block_set_editing;
        int          block_changed;
+       guint        focus_out_idle_source;
 
        McPresence   state;
        int          previous_type;
@@ -225,11 +226,20 @@ presence_chooser_popup_shown_cb (GObject *self,
                                  GParamSpec *pspec,
                                 gpointer user_data)
 {
+       EmpathyPresenceChooserPriv *priv = GET_PRIV (self);
        gboolean shown;
+
        g_object_get (self, "popup-shown", &shown, NULL);
 
        if (!shown) return;
 
+       /* see presence_chooser_entry_focus_out_cb() for what this does */
+       if (priv->focus_out_idle_source != 0)
+       {
+               g_source_remove (priv->focus_out_idle_source);
+               priv->focus_out_idle_source = 0;
+       }
+
        GtkTreeModel *model = create_model ();
 
        gtk_combo_box_set_model (GTK_COMBO_BOX (self), GTK_TREE_MODEL (model));
@@ -249,6 +259,7 @@ presence_chooser_set_status_editing (EmpathyPresenceChooser *self,
        if (editing)
        {
                priv->editing_status = TRUE;
+
                gtk_entry_set_icon_from_stock (GTK_ENTRY (entry),
                                GTK_ENTRY_ICON_SECONDARY,
                                GTK_STOCK_OK);
@@ -278,6 +289,15 @@ presence_chooser_set_status_editing (EmpathyPresenceChooser *self,
                        /* unset the focus */
                        gtk_window_set_focus (GTK_WINDOW (window), NULL);
                }
+
+               /* see presence_chooser_entry_focus_out_cb()
+                * for what this does */
+               if (priv->focus_out_idle_source != 0)
+               {
+                       g_source_remove (priv->focus_out_idle_source);
+                       priv->focus_out_idle_source = 0;
+               }
+
                gtk_editable_set_position (GTK_EDITABLE (entry), 0);
 
                priv->editing_status = FALSE;
@@ -392,7 +412,7 @@ presence_chooser_entry_button_press_event_cb (EmpathyPresenceChooser *self,
 
 static void
 presence_chooser_entry_changed_cb (EmpathyPresenceChooser *self,
-                                  gpointer user_data)
+                                  GtkEntry               *entry)
 {
        EmpathyPresenceChooserPriv *priv = GET_PRIV (self);
 
@@ -462,12 +482,23 @@ presence_chooser_changed_cb (GtkComboBox *self, gpointer user_data)
                                icon_name);
 
                /* preseed the status */
-               if (priv->previous_type == ENTRY_TYPE_BUILTIN)
+               if (priv->editing_status)
+               {
+                       /* if the user is already in editing mode and changes
+                        * the status type, preseed the text they've already
+                        * entered */
+                       /* FIXME: make this work */
+               }
+               else if (priv->previous_type == ENTRY_TYPE_BUILTIN)
                {
+                       /* if their previous entry was a builtin, don't
+                        * preseed */
                        gtk_entry_set_text (GTK_ENTRY (entry), "");
                }
                else
                {
+                       /* else preseed the text of their currently entered
+                        * status message */
                        const char *status = empathy_idle_get_status (priv->idle);
                        gtk_entry_set_text (GTK_ENTRY (entry), status);
                }
@@ -513,6 +544,19 @@ combo_row_separator_func (GtkTreeModel     *model,
        return (type == ENTRY_TYPE_SEPARATOR);
 }
 
+static gboolean
+presence_chooser_entry_focus_out_idle_cb (gpointer user_data)
+{
+       DEBUG ("Autocommiting status message\n");
+
+       EmpathyPresenceChooser *chooser = EMPATHY_PRESENCE_CHOOSER (user_data);
+       GtkWidget *entry = gtk_bin_get_child (GTK_BIN (chooser));
+
+       presence_chooser_entry_activate_cb (chooser, GTK_ENTRY (entry));
+
+       return FALSE;
+}
+
 static gboolean
 presence_chooser_entry_focus_out_cb (EmpathyPresenceChooser *chooser,
                                      GdkEventFocus *event,
@@ -522,9 +566,25 @@ presence_chooser_entry_focus_out_cb (EmpathyPresenceChooser *chooser,
 
        if (priv->editing_status)
        {
-               // entry_activate_cb (chooser, entry);
+               /* this seems a bit evil and maybe it will be fragile,
+                * someone should think of a better way to do it.
+                *
+                * The entry has focused out, but we don't know where the focus
+                * has gone. If it goes to the combo box, we don't want to
+                * do anything. If it's gone anywhere else, we want to commit
+                * the result.
+                *
+                * Thus we install this idle handler and store its source.
+                * If the source is scheduled when the popup handler runs,
+                * it will remove it, else the callback will commit the result.
+                */
+               priv->focus_out_idle_source = g_idle_add (
+                               presence_chooser_entry_focus_out_idle_cb,
+                               chooser);
        }
 
+       gtk_editable_set_position (GTK_EDITABLE (entry), 0);
+
        return FALSE;
 }
 
@@ -617,6 +677,10 @@ presence_chooser_finalize (GObject *object)
                g_source_remove (priv->flash_timeout_id);
        }
 
+       if (priv->focus_out_idle_source) {
+               g_source_remove (priv->focus_out_idle_source);
+       }
+
        g_signal_handlers_disconnect_by_func (priv->idle,
                                              presence_chooser_presence_changed_cb,
                                              object);