From 2ce2f8c27698e6122c6a2687f710a18bb4ca602b Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Fri, 30 Nov 2012 10:39:03 -0500 Subject: Avoid gdk_keyboard_grab/ungrab() and gdk_pointer_grab/ungrab(). Use gdk_device_grab() and gdk_device_ungrab() instead. In some cases this requires stashing the grabbed device so it can be ungrabbed outside of an GdkEvent handler. --- calendar/gui/e-calendar-view.c | 71 ++++++++++++- calendar/gui/e-day-view-time-item.c | 29 +++++- calendar/gui/e-day-view.c | 44 +++++++- calendar/gui/e-day-view.h | 3 + widgets/misc/e-dateedit.c | 199 +++++++++++++++++++++++++++--------- widgets/misc/e-map.c | 2 +- widgets/table/e-cell-combo.c | 167 +++++++++++++++++++++++------- widgets/table/e-cell-combo.h | 3 + 8 files changed, 417 insertions(+), 101 deletions(-) diff --git a/calendar/gui/e-calendar-view.c b/calendar/gui/e-calendar-view.c index bbf890af59..9c21a18739 100644 --- a/calendar/gui/e-calendar-view.c +++ b/calendar/gui/e-calendar-view.c @@ -76,6 +76,10 @@ struct _ECalendarViewPrivate { GtkTargetList *copy_target_list; GtkTargetList *paste_target_list; + + /* All keyboard devices are grabbed + * while a tooltip window is shown. */ + GQueue grabbed_keyboards; }; enum { @@ -353,6 +357,13 @@ calendar_view_dispose (GObject *object) priv->selected_cut_list = NULL; } + while (!g_queue_is_empty (&priv->grabbed_keyboards)) { + GdkDevice *keyboard; + keyboard = g_queue_pop_head (&priv->grabbed_keyboards); + gdk_device_ungrab (keyboard, GDK_CURRENT_TIME); + g_object_unref (keyboard); + } + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_calendar_view_parent_class)->dispose (object); } @@ -1833,15 +1844,25 @@ e_calendar_view_modify_and_send (ECalendarView *cal_view, static gboolean tooltip_grab (GtkWidget *tooltip, - GdkEventKey *event, + GdkEvent *key_event, ECalendarView *view) { - GtkWidget *widget = (GtkWidget *) g_object_get_data (G_OBJECT (view), "tooltip-window"); + GtkWidget *widget; + GdkDevice *keyboard; + guint32 event_time; - if (!widget) + widget = g_object_get_data (G_OBJECT (view), "tooltip-window"); + if (widget == NULL) return TRUE; - gdk_keyboard_ungrab (GDK_CURRENT_TIME); + event_time = gdk_event_get_time (key_event); + + while (!g_queue_is_empty (&view->priv->grabbed_keyboards)) { + keyboard = g_queue_pop_head (&view->priv->grabbed_keyboards); + gdk_device_ungrab (keyboard, event_time); + g_object_unref (keyboard); + } + gtk_widget_destroy (widget); g_object_set_data (G_OBJECT (view), "tooltip-window", NULL); @@ -1927,10 +1948,14 @@ e_calendar_view_get_tooltips (const ECalendarViewEventData *data) GtkStyle *style = gtk_widget_get_default_style (); GtkWidget *widget; GdkWindow *window; + GdkDisplay *display; + GdkDeviceManager *device_manager; + GQueue *grabbed_keyboards; ECalComponent *newcomp = e_cal_component_new (); icaltimezone *zone, *default_zone; ECalModel *model; ECalClient *client = NULL; + GList *list, *link; gboolean free_text = FALSE; /* This function is a timeout callback. */ @@ -2099,8 +2124,44 @@ e_calendar_view_get_tooltips (const ECalendarViewEventData *data) e_calendar_view_move_tip (pevent->tooltip, pevent->x +16, pevent->y + 16); + /* Grab all keyboard devices. A key press from + * any of them will dismiss the tooltip window. */ + window = gtk_widget_get_window (pevent->tooltip); - gdk_keyboard_grab (window, FALSE, GDK_CURRENT_TIME); + display = gdk_window_get_display (window); + device_manager = gdk_display_get_device_manager (display); + + grabbed_keyboards = &data->cal_view->priv->grabbed_keyboards; + g_warn_if_fail (g_queue_is_empty (grabbed_keyboards)); + + list = gdk_device_manager_list_devices ( + device_manager, GDK_DEVICE_TYPE_MASTER); + + for (link = list; link != NULL; link = g_list_next (link)) { + GdkDevice *device = GDK_DEVICE (link->data); + GdkGrabStatus grab_status; + + if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD) + continue; + + grab_status = gdk_device_grab ( + device, + window, + GDK_OWNERSHIP_NONE, + FALSE, + GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK, + NULL, + GDK_CURRENT_TIME); + + if (grab_status == GDK_GRAB_SUCCESS) + g_queue_push_tail ( + grabbed_keyboards, + g_object_ref (device)); + } + + g_list_free (list); + g_signal_connect ( pevent->tooltip, "key-press-event", G_CALLBACK (tooltip_grab), data->cal_view); diff --git a/calendar/gui/e-day-view-time-item.c b/calendar/gui/e-day-view-time-item.c index 3a8284be03..f337aa06e1 100644 --- a/calendar/gui/e-day-view-time-item.c +++ b/calendar/gui/e-day-view-time-item.c @@ -908,6 +908,9 @@ e_day_view_time_item_on_button_press (EDayViewTimeItem *time_item, GdkWindow *window; EDayView *day_view; GnomeCanvas *canvas; + GdkGrabStatus grab_status; + GdkDevice *event_device; + guint32 event_time; gint row; day_view = e_day_view_time_item_get_day_view (time_item); @@ -927,10 +930,20 @@ e_day_view_time_item_on_button_press (EDayViewTimeItem *time_item, window = gtk_layout_get_bin_window (GTK_LAYOUT (canvas)); - if (gdk_pointer_grab (window, FALSE, - GDK_POINTER_MOTION_MASK - | GDK_BUTTON_RELEASE_MASK, - NULL, NULL, event->button.time) == 0) { + event_device = gdk_event_get_device (event); + event_time = gdk_event_get_time (event); + + grab_status = gdk_device_grab ( + event_device, + window, + GDK_OWNERSHIP_NONE, + FALSE, + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK, + NULL, + event_time); + + if (grab_status == GDK_GRAB_SUCCESS) { e_day_view_start_selection (day_view, -1, row); time_item->priv->dragging_selection = TRUE; } @@ -946,7 +959,13 @@ e_day_view_time_item_on_button_release (EDayViewTimeItem *time_item, g_return_if_fail (day_view != NULL); if (time_item->priv->dragging_selection) { - gdk_pointer_ungrab (event->button.time); + GdkDevice *event_device; + guint32 event_time; + + event_device = gdk_event_get_device (event); + event_time = gdk_event_get_time (event); + gdk_device_ungrab (event_device, event_time); + e_day_view_finish_selection (day_view); e_day_view_stop_auto_scroll (day_view); } diff --git a/calendar/gui/e-day-view.c b/calendar/gui/e-day-view.c index 0f0448d3df..db4557cbff 100644 --- a/calendar/gui/e-day-view.c +++ b/calendar/gui/e-day-view.c @@ -1394,6 +1394,14 @@ e_day_view_dispose (GObject *object) } } + if (day_view->grabbed_pointer != NULL) { + gdk_device_ungrab ( + day_view->grabbed_pointer, + GDK_CURRENT_TIME); + g_object_unref (day_view->grabbed_pointer); + day_view->grabbed_pointer = NULL; + } + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_day_view_parent_class)->dispose (object); } @@ -3184,6 +3192,9 @@ e_day_view_on_top_canvas_button_press (GtkWidget *widget, event_time); if (grab_status == GDK_GRAB_SUCCESS) { + g_warn_if_fail (day_view->grabbed_pointer == NULL); + day_view->grabbed_pointer = g_object_ref (event_device); + if (event_time - day_view->bc_event_time > 250) e_day_view_get_selected_time_range ( E_CALENDAR_VIEW (day_view), @@ -3345,6 +3356,9 @@ e_day_view_on_main_canvas_button_press (GtkWidget *widget, event_time); if (grab_status == GDK_GRAB_SUCCESS) { + g_warn_if_fail (day_view->grabbed_pointer == NULL); + day_view->grabbed_pointer = g_object_ref (event_device); + if (event_time - day_view->bc_event_time > 250) e_day_view_get_selected_time_range ( E_CALENDAR_VIEW (day_view), @@ -3618,6 +3632,9 @@ e_day_view_on_long_event_click (EDayView *day_view, event_time); if (grab_status == GDK_GRAB_SUCCESS) { + g_warn_if_fail (day_view->grabbed_pointer == NULL); + day_view->grabbed_pointer = g_object_ref (event_device); + day_view->resize_event_day = E_DAY_VIEW_LONG_EVENT; day_view->resize_event_num = event_num; day_view->resize_drag_pos = pos; @@ -3717,6 +3734,9 @@ e_day_view_on_event_click (EDayView *day_view, event_time); if (grab_status == GDK_GRAB_SUCCESS) { + g_warn_if_fail (day_view->grabbed_pointer == NULL); + day_view->grabbed_pointer = g_object_ref (event_device); + day_view->resize_event_day = day; day_view->resize_event_num = event_num; day_view->resize_drag_pos = pos; @@ -3907,11 +3927,15 @@ e_day_view_on_top_canvas_button_release (GtkWidget *widget, event_device = gdk_event_get_device (button_event); event_time = gdk_event_get_time (button_event); + if (day_view->grabbed_pointer == event_device) { + gdk_device_ungrab (day_view->grabbed_pointer, event_time); + g_object_unref (day_view->grabbed_pointer); + day_view->grabbed_pointer = NULL; + } + if (day_view->selection_is_being_dragged) { - gdk_device_ungrab (event_device, event_time); e_day_view_finish_selection (day_view); } else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) { - gdk_device_ungrab (event_device, event_time); e_day_view_finish_long_event_resize (day_view); } else if (day_view->pressed_event_day != -1) { e_day_view_start_editing_event ( @@ -3937,12 +3961,16 @@ e_day_view_on_main_canvas_button_release (GtkWidget *widget, event_device = gdk_event_get_device (button_event); event_time = gdk_event_get_time (button_event); + if (day_view->grabbed_pointer == event_device) { + gdk_device_ungrab (day_view->grabbed_pointer, event_time); + g_object_unref (day_view->grabbed_pointer); + day_view->grabbed_pointer = NULL; + } + if (day_view->selection_is_being_dragged) { - gdk_device_ungrab (event_device, event_time); e_day_view_finish_selection (day_view); e_day_view_stop_auto_scroll (day_view); } else if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) { - gdk_device_ungrab (event_device, event_time); e_day_view_finish_resize (day_view); e_day_view_stop_auto_scroll (day_view); } else if (day_view->pressed_event_day != -1) { @@ -5403,7 +5431,13 @@ e_day_view_do_key_press (GtkWidget *widget, /* The Escape key aborts a resize operation. */ if (day_view->resize_drag_pos != E_CALENDAR_VIEW_POS_NONE) { if (keyval == GDK_KEY_Escape) { - gdk_pointer_ungrab (event->time); + if (day_view->grabbed_pointer != NULL) { + gdk_device_ungrab ( + day_view->grabbed_pointer, + event->time); + g_object_unref (day_view->grabbed_pointer); + day_view->grabbed_pointer = NULL; + } e_day_view_abort_resize (day_view); } return FALSE; diff --git a/calendar/gui/e-day-view.h b/calendar/gui/e-day-view.h index 9f43a1ad78..5bf8974b69 100644 --- a/calendar/gui/e-day-view.h +++ b/calendar/gui/e-day-view.h @@ -459,6 +459,9 @@ struct _EDayView { GnomeCanvasItem *drag_bar_item; GnomeCanvasItem *drag_item; + /* Grabbed pointer device while dragging. */ + GdkDevice *grabbed_pointer; + /* "am" and "pm" in the current locale, and their widths. */ gchar *am_string; gchar *pm_string; diff --git a/widgets/misc/e-dateedit.c b/widgets/misc/e-dateedit.c index e24d4137cb..dce571b57a 100644 --- a/widgets/misc/e-dateedit.c +++ b/widgets/misc/e-dateedit.c @@ -62,6 +62,9 @@ struct _EDateEditPrivate { GtkWidget *none_button; /* This will only be visible if a * 'None' date/time is permitted. */ + GdkDevice *grabbed_keyboard; + GdkDevice *grabbed_pointer; + gboolean show_date; gboolean show_time; gboolean use_24_hour_format; @@ -140,14 +143,15 @@ static gboolean e_date_edit_mnemonic_activate (GtkWidget *widget, static void e_date_edit_grab_focus (GtkWidget *widget); static gint on_date_entry_key_press (GtkWidget *widget, - GdkEventKey *event, + GdkEvent *key_event, EDateEdit *dedit); static gint on_date_entry_key_release (GtkWidget *widget, - GdkEventKey *event, + GdkEvent *key_event, EDateEdit *dedit); static void on_date_button_clicked (GtkWidget *widget, EDateEdit *dedit); -static void e_date_edit_show_date_popup (EDateEdit *dedit); +static void e_date_edit_show_date_popup (EDateEdit *dedit, + GdkEvent *event); static void position_date_popup (EDateEdit *dedit); static void on_date_popup_none_button_clicked (GtkWidget *button, EDateEdit *dedit); @@ -177,10 +181,10 @@ static gboolean e_date_edit_parse_time (EDateEdit *dedit, static void on_date_edit_time_selected (GtkComboBox *combo, EDateEdit *dedit); static gint on_time_entry_key_press (GtkWidget *widget, - GdkEventKey *event, + GdkEvent *key_event, EDateEdit *dedit); static gint on_time_entry_key_release (GtkWidget *widget, - GdkEventKey *event, + GdkEvent *key_event, EDateEdit *dedit); static gint on_date_entry_focus_out (GtkEntry *entry, GdkEventFocus *event, @@ -339,6 +343,22 @@ date_edit_dispose (GObject *object) dedit->priv->cal_popup = NULL; } + if (dedit->priv->grabbed_keyboard != NULL) { + gdk_device_ungrab ( + dedit->priv->grabbed_keyboard, + GDK_CURRENT_TIME); + g_object_unref (dedit->priv->grabbed_keyboard); + dedit->priv->grabbed_keyboard = NULL; + } + + if (dedit->priv->grabbed_pointer != NULL) { + gdk_device_ungrab ( + dedit->priv->grabbed_pointer, + GDK_CURRENT_TIME); + g_object_unref (dedit->priv->grabbed_pointer); + dedit->priv->grabbed_pointer = NULL; + } + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_date_edit_parent_class)->dispose (object); } @@ -1354,19 +1374,31 @@ static void on_date_button_clicked (GtkWidget *widget, EDateEdit *dedit) { - e_date_edit_show_date_popup (dedit); + GdkEvent *event; + + /* Obtain the GdkEvent that triggered + * the date button's "clicked" signal. */ + event = gtk_get_current_event (); + e_date_edit_show_date_popup (dedit, event); } static void -e_date_edit_show_date_popup (EDateEdit *dedit) +e_date_edit_show_date_popup (EDateEdit *dedit, + GdkEvent *event) { EDateEditPrivate *priv; ECalendar *calendar; + GdkDevice *event_device; + GdkDevice *assoc_device; + GdkDevice *keyboard_device; + GdkDevice *pointer_device; GdkWindow *window; + GdkGrabStatus grab_status; struct tm mtm; const gchar *date_text; GDate selected_day; gboolean clear_selection = FALSE; + guint event_time; priv = dedit->priv; calendar = E_CALENDAR (priv->calendar); @@ -1398,14 +1430,63 @@ e_date_edit_show_date_popup (EDateEdit *dedit) gtk_grab_add (priv->cal_popup); window = gtk_widget_get_window (priv->cal_popup); - gdk_pointer_grab ( - window, TRUE, - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK, - NULL, NULL, GDK_CURRENT_TIME); - gdk_keyboard_grab (window, TRUE, GDK_CURRENT_TIME); - gdk_window_focus (window, GDK_CURRENT_TIME); + + g_return_if_fail (priv->grabbed_keyboard == NULL); + g_return_if_fail (priv->grabbed_pointer == NULL); + + event_device = gdk_event_get_device (event); + assoc_device = gdk_device_get_associated_device (event_device); + + event_time = gdk_event_get_time (event); + + if (gdk_device_get_source (event_device) == GDK_SOURCE_KEYBOARD) { + keyboard_device = event_device; + pointer_device = assoc_device; + } else { + keyboard_device = assoc_device; + pointer_device = event_device; + } + + if (keyboard_device != NULL) { + grab_status = gdk_device_grab ( + keyboard_device, + window, + GDK_OWNERSHIP_WINDOW, + TRUE, + GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK, + NULL, + event_time); + if (grab_status == GDK_GRAB_SUCCESS) { + priv->grabbed_keyboard = + g_object_ref (keyboard_device); + } + } + + if (pointer_device != NULL) { + grab_status = gdk_device_grab ( + pointer_device, + window, + GDK_OWNERSHIP_WINDOW, + TRUE, + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK, + NULL, + event_time); + if (grab_status == GDK_GRAB_SUCCESS) { + priv->grabbed_pointer = + g_object_ref (pointer_device); + } else if (priv->grabbed_keyboard != NULL) { + gdk_device_ungrab ( + priv->grabbed_keyboard, + event_time); + g_object_unref (priv->grabbed_keyboard); + priv->grabbed_keyboard = NULL; + } + } + + gdk_window_focus (window, event_time); } /* This positions the date popup below and to the left of the arrow button, @@ -1516,19 +1597,13 @@ on_date_popup_key_press (GtkWidget *widget, GdkEventKey *event, EDateEdit *dedit) { - GdkWindow *window; - - window = gtk_widget_get_window (dedit->priv->cal_popup); - - if (event->keyval != GDK_KEY_Escape) { - gdk_keyboard_grab (window, TRUE, GDK_CURRENT_TIME); - return FALSE; + if (event->keyval == GDK_KEY_Escape) { + g_signal_stop_emission_by_name (widget, "key_press_event"); + hide_date_popup (dedit); + return TRUE; } - g_signal_stop_emission_by_name (widget, "key_press_event"); - hide_date_popup (dedit); - - return TRUE; + return FALSE; } /* A mouse button has been pressed while the date popup is showing. @@ -1583,8 +1658,22 @@ hide_date_popup (EDateEdit *dedit) { gtk_widget_hide (dedit->priv->cal_popup); gtk_grab_remove (dedit->priv->cal_popup); - gdk_pointer_ungrab (GDK_CURRENT_TIME); - gdk_keyboard_ungrab (GDK_CURRENT_TIME); + + if (dedit->priv->grabbed_keyboard != NULL) { + gdk_device_ungrab ( + dedit->priv->grabbed_keyboard, + GDK_CURRENT_TIME); + g_object_unref (dedit->priv->grabbed_keyboard); + dedit->priv->grabbed_keyboard = NULL; + } + + if (dedit->priv->grabbed_pointer != NULL) { + gdk_device_ungrab ( + dedit->priv->grabbed_pointer, + GDK_CURRENT_TIME); + g_object_unref (dedit->priv->grabbed_pointer); + dedit->priv->grabbed_pointer = NULL; + } } /* Clears the time popup and rebuilds it using the lower_hour, upper_hour @@ -1737,21 +1826,26 @@ on_date_edit_time_selected (GtkComboBox *combo, static gint on_date_entry_key_press (GtkWidget *widget, - GdkEventKey *event, + GdkEvent *key_event, EDateEdit *dedit) { - if (event->state & GDK_MOD1_MASK - && (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_Down - || event->keyval == GDK_KEY_Return)) { - g_signal_stop_emission_by_name ( - widget, "key_press_event"); - e_date_edit_show_date_popup (dedit); + GdkModifierType event_state = 0; + guint event_keyval = 0; + + gdk_event_get_keyval (key_event, &event_keyval); + gdk_event_get_state (key_event, &event_state); + + if (event_state & GDK_MOD1_MASK + && (event_keyval == GDK_KEY_Up || event_keyval == GDK_KEY_Down + || event_keyval == GDK_KEY_Return)) { + g_signal_stop_emission_by_name (widget, "key_press_event"); + e_date_edit_show_date_popup (dedit, key_event); return TRUE; } /* If the user hits the return key emit a "date_changed" signal if * needed. But let the signal carry on. */ - if (event->keyval == GDK_KEY_Return) { + if (event_keyval == GDK_KEY_Return) { e_date_edit_check_date_changed (dedit); return FALSE; } @@ -1761,20 +1855,25 @@ on_date_entry_key_press (GtkWidget *widget, static gint on_time_entry_key_press (GtkWidget *widget, - GdkEventKey *event, + GdkEvent *key_event, EDateEdit *dedit) { GtkWidget *child; + GdkModifierType event_state = 0; + guint event_keyval = 0; + + gdk_event_get_keyval (key_event, &event_keyval); + gdk_event_get_state (key_event, &event_state); child = gtk_bin_get_child (GTK_BIN (dedit->priv->time_combo)); /* I'd like to use Alt+Up/Down for popping up the list, like Win32, * but the combo steals any Up/Down keys, so we use Alt + Return. */ #if 0 - if (event->state & GDK_MOD1_MASK - && (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_Down)) { + if (event_state & GDK_MOD1_MASK + && (event_keyval == GDK_KEY_Up || event_keyval == GDK_KEY_Down)) { #else - if (event->state & GDK_MOD1_MASK && event->keyval == GDK_KEY_Return) { + if (event_state & GDK_MOD1_MASK && event_keyval == GDK_KEY_Return) { #endif g_signal_stop_emission_by_name (widget, "key_press_event"); g_signal_emit_by_name (child, "activate", 0); @@ -1783,10 +1882,8 @@ on_time_entry_key_press (GtkWidget *widget, /* Stop the return key from emitting the activate signal, and check * if we need to emit a "time_changed" signal. */ - if (event->keyval == GDK_KEY_Return) { - g_signal_stop_emission_by_name ( - widget, - "key_press_event"); + if (event_keyval == GDK_KEY_Return) { + g_signal_stop_emission_by_name (widget, "key_press_event"); e_date_edit_check_time_changed (dedit); return TRUE; } @@ -1796,7 +1893,7 @@ on_time_entry_key_press (GtkWidget *widget, static gint on_date_entry_key_release (GtkWidget *widget, - GdkEventKey *event, + GdkEvent *key_event, EDateEdit *dedit) { e_date_edit_check_date_changed (dedit); @@ -1805,13 +1902,15 @@ on_date_entry_key_release (GtkWidget *widget, static gint on_time_entry_key_release (GtkWidget *widget, - GdkEventKey *event, + GdkEvent *key_event, EDateEdit *dedit) { - if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_Down) { - g_signal_stop_emission_by_name ( - widget, - "key_release_event"); + guint event_keyval = 0; + + gdk_event_get_keyval (key_event, &event_keyval); + + if (event_keyval == GDK_KEY_Up || event_keyval == GDK_KEY_Down) { + g_signal_stop_emission_by_name (widget, "key_release_event"); e_date_edit_check_time_changed (dedit); return TRUE; } diff --git a/widgets/misc/e-map.c b/widgets/misc/e-map.c index ebccf385d7..b90c4f2e7b 100644 --- a/widgets/misc/e-map.c +++ b/widgets/misc/e-map.c @@ -735,7 +735,7 @@ e_map_button_release (GtkWidget *widget, if (event->button != 1) return FALSE; - gdk_pointer_ungrab (event->time); + gdk_device_ungrab (event->device, event->time); return TRUE; } diff --git a/widgets/table/e-cell-combo.c b/widgets/table/e-cell-combo.c index cc72e90a20..b3e1c524cf 100644 --- a/widgets/table/e-cell-combo.c +++ b/widgets/table/e-cell-combo.c @@ -101,7 +101,7 @@ static gint e_cell_combo_button_release (GtkWidget *popup_window, GdkEvent *button_event, ECellCombo *ecc); static gint e_cell_combo_key_press (GtkWidget *popup_window, - GdkEventKey *event, + GdkEvent *key_event, ECellCombo *ecc); static void e_cell_combo_update_cell (ECellCombo *ecc); static void e_cell_combo_restart_edit (ECellCombo *ecc); @@ -221,9 +221,22 @@ e_cell_combo_dispose (GObject *object) { ECellCombo *ecc = E_CELL_COMBO (object); - if (ecc->popup_window) + if (ecc->popup_window != NULL) { gtk_widget_destroy (ecc->popup_window); - ecc->popup_window = NULL; + ecc->popup_window = NULL; + } + + if (ecc->grabbed_keyboard != NULL) { + gdk_device_ungrab (ecc->grabbed_keyboard, GDK_CURRENT_TIME); + g_object_unref (ecc->grabbed_keyboard); + ecc->grabbed_keyboard = NULL; + } + + if (ecc->grabbed_pointer != NULL) { + gdk_device_ungrab (ecc->grabbed_pointer, GDK_CURRENT_TIME); + g_object_unref (ecc->grabbed_pointer); + ecc->grabbed_pointer = NULL; + } G_OBJECT_CLASS (e_cell_combo_parent_class)->dispose (object); } @@ -260,9 +273,15 @@ e_cell_combo_do_popup (ECellPopup *ecp, { ECellCombo *ecc = E_CELL_COMBO (ecp); GtkTreeSelection *selection; + GdkGrabStatus grab_status; GdkWindow *window; - guint32 time; - gint error_code; + GdkDevice *keyboard; + GdkDevice *pointer; + GdkDevice *event_device; + guint32 event_time; + + g_return_val_if_fail (ecc->grabbed_keyboard == NULL, FALSE); + g_return_val_if_fail (ecc->grabbed_pointer == NULL, FALSE); selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW (ecc->popup_tree_view)); @@ -276,27 +295,65 @@ e_cell_combo_do_popup (ECellPopup *ecp, g_signal_handlers_unblock_by_func ( selection, e_cell_combo_selection_changed, ecc); - if (event->type == GDK_BUTTON_PRESS) - time = event->button.time; - else - time = event->key.time; - window = gtk_widget_get_window (ecc->popup_tree_view); - error_code = gdk_pointer_grab ( - window, TRUE, - GDK_ENTER_NOTIFY_MASK | - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_HINT_MASK | - GDK_BUTTON1_MOTION_MASK, - NULL, NULL, time); + event_device = gdk_event_get_device (event); + event_time = gdk_event_get_time (event); - if (error_code != 0) - g_warning ("Failed to get pointer grab (%i)", error_code); + if (gdk_device_get_source (event_device) == GDK_SOURCE_KEYBOARD) { + keyboard = event_device; + pointer = gdk_device_get_associated_device (event_device); + } else { + keyboard = gdk_device_get_associated_device (event_device); + pointer = event_device; + } + + if (pointer != NULL) { + grab_status = gdk_device_grab ( + pointer, + window, + GDK_OWNERSHIP_NONE, + TRUE, + GDK_ENTER_NOTIFY_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_HINT_MASK | + GDK_BUTTON1_MOTION_MASK, + NULL, + event_time); + + if (grab_status != GDK_GRAB_SUCCESS) + return FALSE; + + ecc->grabbed_pointer = g_object_ref (pointer); + } gtk_grab_add (ecc->popup_window); - gdk_keyboard_grab (window, TRUE, time); + + if (keyboard != NULL) { + grab_status = gdk_device_grab ( + keyboard, + window, + GDK_OWNERSHIP_NONE, + TRUE, + GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK, + NULL, + event_time); + + if (grab_status != GDK_GRAB_SUCCESS) { + if (ecc->grabbed_pointer != NULL) { + gdk_device_ungrab ( + ecc->grabbed_pointer, + event_time); + g_object_unref (ecc->grabbed_pointer); + ecc->grabbed_pointer = NULL; + } + return FALSE; + } + + ecc->grabbed_keyboard = g_object_ref (keyboard); + } return TRUE; } @@ -597,8 +654,19 @@ e_cell_combo_button_press (GtkWidget *popup_window, } gtk_grab_remove (ecc->popup_window); - gdk_pointer_ungrab (event_time); - gdk_keyboard_ungrab (event_time); + + if (ecc->grabbed_keyboard != NULL) { + gdk_device_ungrab (ecc->grabbed_keyboard, event_time); + g_object_unref (ecc->grabbed_keyboard); + ecc->grabbed_keyboard = NULL; + } + + if (ecc->grabbed_pointer != NULL) { + gdk_device_ungrab (ecc->grabbed_pointer, event_time); + g_object_unref (ecc->grabbed_pointer); + ecc->grabbed_pointer = NULL; + } + gtk_widget_hide (ecc->popup_window); e_cell_popup_set_shown (E_CELL_POPUP (ecc), FALSE); @@ -639,9 +707,21 @@ e_cell_combo_button_release (GtkWidget *popup_window, /* The button was released inside the list, so we hide the popup and * update the cell to reflect the new selection. */ + gtk_grab_remove (ecc->popup_window); - gdk_pointer_ungrab (event_time); - gdk_keyboard_ungrab (event_time); + + if (ecc->grabbed_keyboard != NULL) { + gdk_device_ungrab (ecc->grabbed_keyboard, event_time); + g_object_unref (ecc->grabbed_keyboard); + ecc->grabbed_keyboard = NULL; + } + + if (ecc->grabbed_pointer != NULL) { + gdk_device_ungrab (ecc->grabbed_pointer, event_time); + g_object_unref (ecc->grabbed_pointer); + ecc->grabbed_pointer = NULL; + } + gtk_widget_hide (ecc->popup_window); e_cell_popup_set_shown (E_CELL_POPUP (ecc), FALSE); @@ -657,30 +737,47 @@ e_cell_combo_button_release (GtkWidget *popup_window, * pressed we hide the popup, and do not change the cell contents. */ static gint e_cell_combo_key_press (GtkWidget *popup_window, - GdkEventKey *event, + GdkEvent *key_event, ECellCombo *ecc) { + guint event_keyval = 0; + guint32 event_time; + + gdk_event_get_keyval (key_event, &event_keyval); + event_time = gdk_event_get_time (key_event); + /* If the Escape key is pressed we hide the popup. */ - if (event->keyval != GDK_KEY_Escape - && event->keyval != GDK_KEY_Return - && event->keyval != GDK_KEY_KP_Enter - && event->keyval != GDK_KEY_ISO_Enter - && event->keyval != GDK_KEY_3270_Enter) + if (event_keyval != GDK_KEY_Escape + && event_keyval != GDK_KEY_Return + && event_keyval != GDK_KEY_KP_Enter + && event_keyval != GDK_KEY_ISO_Enter + && event_keyval != GDK_KEY_3270_Enter) return FALSE; - if (event->keyval == GDK_KEY_Escape && + if (event_keyval == GDK_KEY_Escape && (!ecc->popup_window || !gtk_widget_get_visible (ecc->popup_window))) return FALSE; gtk_grab_remove (ecc->popup_window); - gdk_pointer_ungrab (event->time); - gdk_keyboard_ungrab (event->time); + + if (ecc->grabbed_keyboard != NULL) { + gdk_device_ungrab (ecc->grabbed_keyboard, event_time); + g_object_unref (ecc->grabbed_keyboard); + ecc->grabbed_keyboard = NULL; + } + + if (ecc->grabbed_pointer != NULL) { + gdk_device_ungrab (ecc->grabbed_pointer, event_time); + g_object_unref (ecc->grabbed_pointer); + ecc->grabbed_pointer = NULL; + } + gtk_widget_hide (ecc->popup_window); e_cell_popup_set_shown (E_CELL_POPUP (ecc), FALSE); d (g_print ("%s: popup_shown = FALSE\n", __FUNCTION__)); - if (event->keyval != GDK_KEY_Escape) + if (event_keyval != GDK_KEY_Escape) e_cell_combo_update_cell (ecc); e_cell_combo_restart_edit (ecc); diff --git a/widgets/table/e-cell-combo.h b/widgets/table/e-cell-combo.h index ec77dcd6f0..3d2fb339de 100644 --- a/widgets/table/e-cell-combo.h +++ b/widgets/table/e-cell-combo.h @@ -63,6 +63,9 @@ struct _ECellCombo { GtkWidget *popup_window; GtkWidget *popup_scrolled_window; GtkWidget *popup_tree_view; + + GdkDevice *grabbed_keyboard; + GdkDevice *grabbed_pointer; }; struct _ECellComboClass { -- cgit v1.2.3