]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkcombo.c
Add back the monitoring of the selection when the combo isn't popped down;
[~andy/gtk] / gtk / gtkcombo.c
index 11424632ae7d116f3c18b2e043e861984c1a7c54..35734d70f1affa99d6019319d35b7d2213a23f4d 100644 (file)
@@ -28,6 +28,8 @@
  * The code here relies on all sorts of internal details of GTK+
  */
 
+#undef GTK_DISABLE_DEPRECATED
+
 #include <string.h>
 
 #include "gtkarrow.h"
@@ -76,6 +78,8 @@ static void         gtk_combo_get_pos            (GtkCombo         *combo,
                                                  gint             *height,
                                                  gint             *width);
 static void         gtk_combo_popup_list         (GtkCombo         *combo);
+static void         gtk_combo_popdown_list       (GtkCombo         *combo);
+
 static void         gtk_combo_activate           (GtkWidget        *widget,
                                                  GtkCombo         *combo);
 static gboolean     gtk_combo_popup_button_press (GtkWidget        *button,
@@ -84,8 +88,7 @@ static gboolean     gtk_combo_popup_button_press (GtkWidget        *button,
 static gboolean     gtk_combo_popup_button_leave (GtkWidget        *button,
                                                  GdkEventCrossing *event,
                                                  GtkCombo         *combo);
-static void         gtk_combo_update_entry       (GtkList          *list,
-                                                 GtkCombo         *combo);
+static void         gtk_combo_update_entry       (GtkCombo         *combo);
 static void         gtk_combo_update_list        (GtkEntry         *entry,
                                                  GtkCombo         *combo);
 static gint         gtk_combo_button_press       (GtkWidget        *widget,
@@ -145,7 +148,7 @@ gtk_combo_class_init (GtkComboClass * klass)
                                    PROP_ENABLE_ARROWS_ALWAYS,
                                    g_param_spec_boolean ("enable_arrows_always",
                                                          _("Always enable arrows"),
-                                                         _("Whether the arrow keys work, even if the entry contents are not in the list"),
+                                                         _("Obsolete property, ignored"),
                                                          TRUE,
                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
   g_object_class_install_property (gobject_class,
@@ -199,11 +202,11 @@ static int
 gtk_combo_entry_key_press (GtkEntry * entry, GdkEventKey * event, GtkCombo * combo)
 {
   GList *li;
+  guint state = event->state & gtk_accelerator_get_default_mod_mask ();
 
   /* completion */
-  if ((event->keyval == GDK_Tab ||
-       event->keyval == GDK_KP_Tab) &&
-      (event->state & GDK_MOD1_MASK)) 
+  if ((event->keyval == GDK_Tab ||  event->keyval == GDK_KP_Tab) &&
+      state == GDK_MOD1_MASK)
     {
       GtkEditable *editable = GTK_EDITABLE (entry);
       GCompletion * cmpl;
@@ -214,8 +217,6 @@ gtk_combo_entry_key_press (GtkEntry * entry, GdkEventKey * event, GtkCombo * com
       if ( !GTK_LIST (combo->list)->children )
        return FALSE;
     
-      g_signal_stop_emission_by_name (entry, "key_press_event");
-
       cmpl = g_completion_new ((GCompletionFunc)gtk_combo_func);
       g_completion_add_items (cmpl, GTK_LIST (combo->list)->children);
 
@@ -239,44 +240,49 @@ gtk_combo_entry_key_press (GtkEntry * entry, GdkEventKey * event, GtkCombo * com
       return TRUE;
     }
 
+  if ((event->keyval == GDK_Down || event->keyval == GDK_KP_Down) &&
+      state == GDK_MOD1_MASK)
+    {
+      gtk_combo_activate (NULL, combo);
+      return TRUE;
+    }
+
   if (!combo->use_arrows || !GTK_LIST (combo->list)->children)
     return FALSE;
 
+  gtk_combo_update_list (GTK_ENTRY (combo->entry), combo);
   li = g_list_find (GTK_LIST (combo->list)->children, gtk_combo_find (combo));
 
-  if ((event->keyval == GDK_Up)
-      || (event->keyval == GDK_KP_Up)
-      || ((event->state & GDK_MOD1_MASK) && ((event->keyval == 'p') || (event->keyval == 'P'))))
+  if (((event->keyval == GDK_Up || event->keyval == GDK_KP_Up) && state == 0) ||
+      ((event->keyval == 'p' || event->keyval == 'P') && state == GDK_MOD1_MASK))
     {
-      if (li)
+      if (!li)
+       li = g_list_last (GTK_LIST (combo->list)->children);
+      else
        li = li->prev;
-      if (!li && combo->use_arrows_always)
-       {
-         li = g_list_last (GTK_LIST (combo->list)->children);
-       }
+
       if (li)
        {
          gtk_list_select_child (GTK_LIST (combo->list), GTK_WIDGET (li->data));
-         g_signal_stop_emission_by_name (entry, "key_press_event");
-         return TRUE;
+         gtk_combo_update_entry (combo);
        }
+      
+      return TRUE;
     }
-  else if ((event->keyval == GDK_Down)
-          || (event->keyval == GDK_KP_Down)
-          || ((event->state & GDK_MOD1_MASK) && ((event->keyval == 'n') || (event->keyval == 'N'))))
+  if (((event->keyval == GDK_Down || event->keyval == GDK_KP_Down) && state == 0) ||
+      ((event->keyval == 'n' || event->keyval == 'N') && state == GDK_MOD1_MASK))
     {
-      if (li)
+      if (!li)
+       li = GTK_LIST (combo->list)->children;
+      else if (li)
        li = li->next;
-      if (!li && combo->use_arrows_always)
-       {
-         li = GTK_LIST (combo->list)->children;
-       }
       if (li)
        {
          gtk_list_select_child (GTK_LIST (combo->list), GTK_WIDGET (li->data));
-         g_signal_stop_emission_by_name (entry, "key_press_event");
-         return TRUE;
+         gtk_combo_update_entry (combo);
        }
+      
+      return TRUE;
     }
   return FALSE;
 }
@@ -286,24 +292,28 @@ gtk_combo_window_key_press (GtkWidget   *window,
                            GdkEventKey *event,
                            GtkCombo    *combo)
 {
-  if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter)
+  guint state = event->state & gtk_accelerator_get_default_mod_mask ();
+
+  if ((event->keyval == GDK_Return || event->keyval == GDK_KP_Enter) &&
+      state == 0)
     {
-      if (GTK_WIDGET_VISIBLE (combo->popwin))
-       {
-         gtk_widget_hide (combo->popwin);
-         
-         if (GTK_WIDGET_HAS_GRAB (combo->popwin))
-           {
-             gtk_grab_remove (combo->popwin);
-             gdk_display_pointer_ungrab (gtk_widget_get_display (window),
-                                         event->time);
-           }
-       }
+      gtk_combo_popdown_list (combo);
+      gtk_combo_update_entry (combo);
 
-      g_signal_stop_emission_by_name (window, "key_press_event");
+      return TRUE;
+    }
+  else if ((event->keyval == GDK_Up || event->keyval == GDK_KP_Up) &&
+          state == GDK_MOD1_MASK)
+    {
+      gtk_combo_popdown_list (combo);
 
       return TRUE;
     }
+  else if ((event->keyval == GDK_space || event->keyval == GDK_KP_Space) &&
+          state == 0)
+    {
+      gtk_combo_update_entry (combo);
+    }
 
   return FALSE;
 }
@@ -536,6 +546,7 @@ gtk_combo_popup_list (GtkCombo * combo)
     {
       GTK_WIDGET_SET_FLAGS (list, GTK_CAN_FOCUS);
       gtk_widget_grab_focus (combo->list);
+      GTK_LIST (combo->list)->last_focus_child = NULL;
       GTK_WIDGET_UNSET_FLAGS (list, GTK_CAN_FOCUS);
     }
   
@@ -561,27 +572,56 @@ gtk_combo_popdown_list (GtkCombo *combo)
     {
       gtk_grab_remove (combo->popwin);
       gdk_display_pointer_ungrab (gtk_widget_get_display (GTK_WIDGET (combo)),
-                                 GDK_CURRENT_TIME);
+                                 gtk_get_current_event_time ());
+      gdk_display_keyboard_ungrab (gtk_widget_get_display (GTK_WIDGET (combo)),
+                                  gtk_get_current_event_time ());
     }
   
   gtk_widget_hide (combo->popwin);
 }
 
+static gboolean
+popup_grab_on_window (GdkWindow *window,
+                     guint32    activate_time)
+{
+  if ((gdk_pointer_grab (window, TRUE,
+                        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                        GDK_POINTER_MOTION_MASK,
+                        NULL, NULL, activate_time) == 0))
+    {
+      if (gdk_keyboard_grab (window, TRUE,
+                            activate_time) == 0)
+       return TRUE;
+      else
+       {
+         gdk_display_pointer_ungrab (gdk_drawable_get_display (window),
+                                     activate_time);
+         return FALSE;
+       }
+    }
+
+  return FALSE;
+}
+
 static void        
 gtk_combo_activate (GtkWidget        *widget,
                    GtkCombo         *combo)
 {
+  if (!combo->button->window ||
+      !popup_grab_on_window (combo->button->window,
+                            gtk_get_current_event_time ()))
+    return;
+
   gtk_combo_popup_list (combo);
+  
+  /* This must succeed since we already have the grab */
+  popup_grab_on_window (combo->popwin->window,
+                       gtk_get_current_event_time ());
 
   if (!GTK_WIDGET_HAS_FOCUS (combo->entry))
     gtk_widget_grab_focus (combo->entry);
 
   gtk_grab_add (combo->popwin);
-  gdk_pointer_grab (combo->popwin->window, TRUE,
-                   GDK_BUTTON_PRESS_MASK | 
-                   GDK_BUTTON_RELEASE_MASK |
-                   GDK_POINTER_MOTION_MASK, 
-                   NULL, NULL, GDK_CURRENT_TIME);
 }
 
 static gboolean
@@ -595,20 +635,21 @@ gtk_combo_popup_button_press (GtkWidget        *button,
   if (event->button != 1)
     return FALSE;
 
+  if (!popup_grab_on_window (combo->button->window,
+                            gtk_get_current_event_time ()))
+    return FALSE;
+
   combo->current_button = event->button;
 
   gtk_combo_popup_list (combo);
+
+  /* This must succeed since we already have the grab */
+  popup_grab_on_window (combo->popwin->window,
+                       gtk_get_current_event_time ());
+
   gtk_button_pressed (GTK_BUTTON (button));
 
   gtk_grab_add (combo->popwin);
-  gdk_pointer_grab (combo->popwin->window, TRUE,
-                   GDK_BUTTON_PRESS_MASK | 
-                   GDK_BUTTON_RELEASE_MASK |
-                   GDK_POINTER_MOTION_MASK, 
-                   NULL, NULL, GDK_CURRENT_TIME);
-
-  GTK_LIST (combo->list)->drag_selection = TRUE;
-  gtk_grab_add (combo->list);
 
   return TRUE;
 }
@@ -625,11 +666,11 @@ gtk_combo_popup_button_leave (GtkWidget        *button,
 }
 
 static void
-gtk_combo_update_entry (GtkList * list, GtkCombo * combo)
+gtk_combo_update_entry (GtkCombo * combo)
 {
+  GtkList *list = GTK_LIST (combo->list);
   char *text;
 
-  gtk_grab_remove (GTK_WIDGET (combo));
   g_signal_handler_block (list, combo->list_change_id);
   if (list->selection)
     {
@@ -641,6 +682,14 @@ gtk_combo_update_entry (GtkList * list, GtkCombo * combo)
   g_signal_handler_unblock (list, combo->list_change_id);
 }
 
+static void
+gtk_combo_selection_changed (GtkList  *list,
+                            GtkCombo *combo)
+{
+  if (!GTK_WIDGET_VISIBLE (combo->popwin))
+    gtk_combo_update_entry (combo);
+}
+
 static void
 gtk_combo_update_list (GtkEntry * entry, GtkCombo * combo)
 {
@@ -688,6 +737,13 @@ gtk_combo_button_press (GtkWidget * widget, GdkEvent * event, GtkCombo * combo)
   return TRUE;
 }
 
+static gboolean
+is_within (GtkWidget *widget,
+          GtkWidget *ancestor)
+{
+  return widget == ancestor || gtk_widget_is_ancestor (widget, ancestor);
+}
+
 static void
 gtk_combo_button_event_after (GtkWidget *widget,
                              GdkEvent  *event,
@@ -698,6 +754,8 @@ gtk_combo_button_event_after (GtkWidget *widget,
   if (event->type != GDK_BUTTON_RELEASE)
     return;
   
+  child = gtk_get_event_widget ((GdkEvent*) event);
+
   if ((combo->current_button != 0) && (event->button.button == 1))
     {
       /* This was the initial button press */
@@ -705,12 +763,7 @@ gtk_combo_button_event_after (GtkWidget *widget,
       combo->current_button = 0;
 
       /* Check to see if we released inside the button */
-      child = gtk_get_event_widget ((GdkEvent*) event);
-
-      while (child && child != (combo->button))
-       child = child->parent;
-
-      if (child == combo->button)
+      if (child && is_within (child, combo->button))
        {
          gtk_grab_add (combo->popwin);
          gdk_pointer_grab (combo->popwin->window, TRUE,
@@ -722,7 +775,34 @@ gtk_combo_button_event_after (GtkWidget *widget,
        }
     }
 
+  if (is_within (child, combo->list))
+    gtk_combo_update_entry (combo);
+    
   gtk_combo_popdown_list (combo);
+
+}
+
+static void
+find_child_foreach (GtkWidget *widget,
+                   gpointer   data)
+{
+  GdkEventButton *event = data;
+
+  if (!event->window)
+    {
+      if (event->x >= widget->allocation.x &&
+         event->x < widget->allocation.x + widget->allocation.width &&
+         event->y >= widget->allocation.y &&
+         event->y < widget->allocation.y + widget->allocation.height)
+       event->window = g_object_ref (widget->window);
+    }
+}
+
+static void
+find_child_window (GtkContainer   *container,
+                  GdkEventButton *event)
+{
+  gtk_container_foreach (container, find_child_foreach, event);
 }
 
 static gint         
@@ -733,7 +813,7 @@ gtk_combo_list_enter (GtkWidget        *widget,
   GtkWidget *event_widget;
 
   event_widget = gtk_get_event_widget ((GdkEvent*) event);
-  
+
   if ((event_widget == combo->list) &&
       (combo->current_button != 0) && 
       (!GTK_WIDGET_HAS_GRAB (combo->list)))
@@ -749,7 +829,6 @@ gtk_combo_list_enter (GtkWidget        *widget,
        */
       gdk_window_get_pointer (combo->list->window, &x, &y, &mask);
 
-      tmp_event->button.window = g_object_ref (combo->list->window);
       tmp_event->button.send_event = TRUE;
       tmp_event->button.time = GDK_CURRENT_TIME; /* bad */
       tmp_event->button.x = x;
@@ -760,6 +839,19 @@ gtk_combo_list_enter (GtkWidget        *widget,
       tmp_event->button.button = combo->current_button;
       tmp_event->button.state = mask;
 
+      find_child_window (GTK_CONTAINER (combo->list), &tmp_event->button);
+      if (!tmp_event->button.window)
+       {
+         GtkWidget *child;
+         
+         if (GTK_LIST (combo->list)->children)
+           child = GTK_LIST (combo->list)->children->data;
+         else
+           child = combo->list;
+
+         tmp_event->button.window = g_object_ref (child->window);
+       }
+
       gtk_widget_event (combo->list, tmp_event);
       gdk_event_free (tmp_event);
     }
@@ -770,7 +862,9 @@ gtk_combo_list_enter (GtkWidget        *widget,
 static int
 gtk_combo_list_key_press (GtkWidget * widget, GdkEventKey * event, GtkCombo * combo)
 {
-  if (event->keyval == GDK_Escape)
+  guint state = event->state & gtk_accelerator_get_default_mod_mask ();
+
+  if (event->keyval == GDK_Escape && state == 0)
     {
       if (GTK_WIDGET_HAS_GRAB (combo->list))
        gtk_list_end_drag_selection (GTK_LIST (combo->list));
@@ -872,23 +966,20 @@ gtk_combo_init (GtkCombo * combo)
                                       gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo->popup)));
   gtk_widget_show (combo->list);
 
-  combo->list_change_id = g_signal_connect (combo->list,
-                                           "selection_changed",
-                                           G_CALLBACK (gtk_combo_update_entry),
-                                           combo);
+  combo->list_change_id = g_signal_connect (combo->list, "selection_changed",
+                                           G_CALLBACK (gtk_combo_selection_changed), combo);
+  
   g_signal_connect (combo->popwin, "key_press_event",
                    G_CALLBACK (gtk_combo_list_key_press), combo);
   g_signal_connect (combo->popwin, "button_press_event",
                    G_CALLBACK (gtk_combo_button_press), combo);
 
+  g_signal_connect (combo->popwin, "event_after",
+                   G_CALLBACK (gtk_combo_button_event_after), combo);
   g_signal_connect (combo->list, "event_after",
                    G_CALLBACK (gtk_combo_button_event_after), combo);
 
-  /* We connect here on the button, because we'll have a grab on it
-   * when the event occurs. But we are actually interested in enters
-   * for the combo->list.
-   */
-  g_signal_connect (combo->button, "enter_notify_event",
+  g_signal_connect (combo->list, "enter_notify_event",
                    G_CALLBACK (gtk_combo_list_enter), combo);
 }