--- gtk+-2.6.4/gtk/gtkwidget.c 2005-02-24 18:44:02.000000000 +0200 +++ gtk+-2.6.4/gtk/gtkwidget.c 2005-04-06 16:19:38.386702696 +0300 @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include "gtkalias.h" #include "gtkcontainer.h" #include "gtkaccelmap.h" @@ -44,6 +46,11 @@ #include "gtkwindow.h" #include "gtkbindings.h" #include "gtkprivate.h" +#include "gtktreeview.h" +#include "gtkentry.h" +#include "gtktextview.h" +#include "gtkimcontext.h" +#include "gtkmenu.h" #include "gdk/gdk.h" #include "gdk/gdkprivate.h" /* Used in gtk_reset_shapes_recurse to avoid copy */ #include @@ -53,11 +60,30 @@ #include "gtkaccessible.h" #include "gtktooltips.h" #include "gtkinvisible.h" +#include "gtkscrollbar.h" /* Following are needed for special focus changes */ +#include "gtktoolbar.h" +#include "gtkmenu.h" +#include "gtkmenuitem.h" +#include "gtktogglebutton.h" +#include "gtkcomboboxentry.h" +#include "gtktogglebutton.h" +#include "gtkcomboboxentry.h" #define WIDGET_CLASS(w) GTK_WIDGET_GET_CLASS (w) #define INIT_PATH_SIZE (512) +#define GTK_TAP_THRESHOLD 30 +#define GTK_TAP_MENU_THRESHOLD 20 +#define GTK_TAP_AND_HOLD_TIMER_COUNTER 11 +#define GTK_TAP_AND_HOLD_TIMER_INTERVAL 100 +typedef struct _GtkWidgetPrivate GtkWidgetPrivate; + +#define GTK_WIDGET_GET_PRIVATE(obj) ( G_TYPE_INSTANCE_GET_PRIVATE ((obj),\ + GTK_TYPE_WIDGET, GtkWidgetPrivate) ) + +#define TAP_AND_HOLD_ANIMATION 1 + enum { SHOW, HIDE, @@ -120,6 +146,9 @@ ACCEL_CLOSURES_CHANGED, SCREEN_CHANGED, CAN_ACTIVATE_ACCEL, + INSENSITIVE_PRESS, + TAP_AND_HOLD, + TAP_AND_HOLD_SETUP, LAST_SIGNAL }; @@ -142,7 +171,8 @@ PROP_STYLE, PROP_EVENTS, PROP_EXTENSION_EVENTS, - PROP_NO_SHOW_ALL + PROP_NO_SHOW_ALL, + PROP_TAP_AND_HOLD }; typedef struct _GtkStateData GtkStateData; @@ -155,7 +185,50 @@ guint use_forall : 1; }; +struct _GtkWidgetPrivate +{ + GtkWidget *menu; + guint timer_id; + + GtkStateType stype; + GtkStateType type_on_press; + GdkEvent *fake_event; + GtkMenuPositionFunc func; + gint x, y; + gint timer_counter; + gint run_press : 1; + gint button_pressed : 1; + gint signals_connected : 1; + GtkWidgetTapAndHoldFlags flags; + gboolean state_set; + guint interval; + +#ifdef TAP_AND_HOLD_ANIMATION + GdkPixbufAnimation *anim; + GdkPixbufAnimationIter *iter; + guint width, height; +#endif +}; + +/* --- Tap And Hold --- */ +static gboolean gtk_widget_tap_and_hold_button_press_with_events( GtkWidget *widget, + GdkEvent *event, GtkWidgetPrivate *priv ); +static gboolean gtk_widget_tap_and_hold_button_release_with_events( GtkWidget *widget, + GdkEvent *event, GtkWidgetPrivate *priv ); +static gboolean gtk_widget_tap_and_hold_leave_notify_with_events( GtkWidget *widget, + GdkEvent *event, GtkWidgetPrivate *priv ); +static gboolean gtk_widget_tap_and_hold_timeout_with_events( GtkWidget *widget ); +static gboolean gtk_widget_tap_and_hold_timeout( GtkWidget *widget ); +static gboolean gtk_widget_tap_and_hold_button_press( GtkWidget *widget, + GdkEvent *event, GtkWidgetPrivate *priv ); +static gboolean gtk_widget_tap_and_hold_button_release( GtkWidget *widget, + GdkEvent *event, GtkWidgetPrivate *priv ); +static gboolean gtk_widget_tap_and_hold_leave_notify( GtkWidget *widget, + GdkEvent *event, GtkWidgetPrivate *priv ); +static void gtk_widget_tap_and_hold_setup_real( GtkWidget *widget, + GtkWidget *menu, GtkCallback func, GtkWidgetTapAndHoldFlags flags ); +static void gtk_widget_real_tap_and_hold(GtkWidget *widget); /* --- prototypes --- */ static void gtk_widget_class_init (GtkWidgetClass *klass); static void gtk_widget_init (GtkWidget *widget); @@ -228,6 +301,13 @@ gint width, gint height); +/*Hildon focus handling*/ +static void gtk_widget_set_focus_handling( GtkWidget *widget, gboolean state ); + +static gboolean gtk_widget_enter_notify_event( GtkWidget *widget, GdkEventCrossing *event ); +static gboolean gtk_widget_leave_notify_event( GtkWidget *widget, GdkEventCrossing *event ); +static gint gtk_widget_button_release_event( GtkWidget *widget, GdkEventButton *event ); +static gint gtk_widget_button_press_event( GtkWidget *widget, GdkEventButton *event ); /* --- variables --- */ static gpointer parent_class = NULL; @@ -239,6 +319,9 @@ static GtkTextDirection gtk_default_direction = GTK_TEXT_DIR_LTR; static GParamSpecPool *style_property_spec_pool = NULL; +static gboolean on_same_widget = FALSE; /*Hildon focus handling*/ +static gboolean mouse_pressed = FALSE; /*Hildon focus handling*/ + static GQuark quark_property_parser = 0; static GQuark quark_aux_info = 0; static GQuark quark_accel_path = 0; @@ -396,6 +479,9 @@ klass->drag_data_received = NULL; klass->screen_changed = NULL; klass->can_activate_accel = gtk_widget_real_can_activate_accel; + klass->tap_and_hold_setup = gtk_widget_tap_and_hold_setup_real; + klass->insensitive_press = NULL; + klass->tap_and_hold = gtk_widget_real_tap_and_hold; klass->show_help = gtk_widget_real_show_help; @@ -404,6 +490,18 @@ klass->no_expose_event = NULL; + g_type_class_add_private( klass, sizeof(GtkWidgetPrivate) ); + + g_object_class_install_property (gobject_class, + PROP_TAP_AND_HOLD, + g_param_spec_int ("tap_and_hold_state", + P_("Tap and hold State type"), + P_("Sets the state to be used to the tap and hold functionality. The default is GTK_STATE_NORMAL"), + 0, + 4, /*4 == Last state in GTK+-2.0*/ + GTK_STATE_NORMAL, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_NAME, g_param_spec_string ("name", @@ -1389,6 +1487,31 @@ _gtk_marshal_BOOLEAN__UINT, G_TYPE_BOOLEAN, 1, G_TYPE_UINT); + widget_signals[INSENSITIVE_PRESS] = + g_signal_new ("insensitive_press", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkWidgetClass, insensitive_press), + NULL, NULL, + _gtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + widget_signals[TAP_AND_HOLD] = + g_signal_new("tap-and-hold", G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GtkWidgetClass, tap_and_hold), + NULL, NULL, + _gtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + widget_signals[TAP_AND_HOLD_SETUP] = + g_signal_new("tap-and-hold-setup", G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GtkWidgetClass, tap_and_hold_setup), + NULL, NULL, /*FIXME -- OBJECT_POINTER_FLAGS*/ + _gtk_marshal_VOID__OBJECT_UINT_FLAGS, + G_TYPE_NONE, 3, G_TYPE_OBJECT, G_TYPE_POINTER, G_TYPE_UINT); + binding_set = gtk_binding_set_by_class (klass); gtk_binding_entry_add_signal (binding_set, GDK_F10, GDK_SHIFT_MASK, "popup_menu", 0); @@ -1418,7 +1541,12 @@ P_("Whether to draw the focus indicator inside widgets"), TRUE, G_PARAM_READABLE)); - + gtk_widget_class_install_style_property (klass, + g_param_spec_boolean ("hildon-focus-handling", + P_("Hildon focus handling"), + P_("Whether the widget is using the hildon like focus handling or not"), + FALSE, + G_PARAM_READABLE)); gtk_widget_class_install_style_property (klass, g_param_spec_int ("focus-line-width", P_("Focus linewidth"), @@ -1543,6 +1671,8 @@ case PROP_NO_SHOW_ALL: gtk_widget_set_no_show_all (widget, g_value_get_boolean (value)); break; + case PROP_TAP_AND_HOLD: + GTK_WIDGET_GET_PRIVATE(widget)->type_on_press = g_value_get_int(value); default: break; } @@ -1637,16 +1767,45 @@ case PROP_NO_SHOW_ALL: g_value_set_boolean (value, gtk_widget_get_no_show_all (widget)); break; + case PROP_TAP_AND_HOLD: + g_value_set_int (value, + (int)GTK_WIDGET_GET_PRIVATE(widget)->type_on_press); default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } +static void gtk_widget_set_focus_handling( GtkWidget *widget, gboolean state ) +{ + GtkWidgetPrivate *priv; + priv = GTK_WIDGET_GET_PRIVATE (widget); + + if( state && GTK_WIDGET_CAN_FOCUS(widget) ) + { + if (!priv->state_set) + { + g_signal_connect( G_OBJECT(widget), "button-press-event", + G_CALLBACK(gtk_widget_button_press_event), NULL ); + g_signal_connect( G_OBJECT(widget), "button-release-event", + G_CALLBACK(gtk_widget_button_release_event), NULL ); + g_signal_connect( G_OBJECT(widget), "enter-notify-event", + G_CALLBACK(gtk_widget_enter_notify_event), NULL ); + g_signal_connect( G_OBJECT(widget), "leave-notify-event", + G_CALLBACK(gtk_widget_leave_notify_event), NULL ); + priv->state_set = TRUE; + } + } +} + static void gtk_widget_init (GtkWidget *widget) { + GtkWidgetPrivate *priv; GTK_PRIVATE_FLAGS (widget) = PRIVATE_GTK_CHILD_VISIBLE; + + priv = GTK_WIDGET_GET_PRIVATE(widget); + widget->state = GTK_STATE_NORMAL; widget->saved_state = GTK_STATE_NORMAL; widget->name = NULL; @@ -1659,6 +1818,18 @@ widget->window = NULL; widget->parent = NULL; + priv->fake_event = NULL; + priv->timer_id = 0; + priv->menu = NULL; + priv->run_press = TRUE; + priv->signals_connected = FALSE; + priv->x = priv->y = 0; + priv->func = NULL; + priv->timer_counter = 0; + priv->flags = 0x0; + priv->state_set = FALSE; + priv->interval = GTK_TAP_AND_HOLD_TIMER_INTERVAL; + GTK_WIDGET_SET_FLAGS (widget, GTK_SENSITIVE | GTK_PARENT_SENSITIVE | @@ -1670,6 +1841,7 @@ GTK_PRIVATE_SET_FLAG (widget, GTK_ALLOC_NEEDED); widget->style = gtk_widget_get_default_style (); + g_object_ref (widget->style); } @@ -2153,6 +2325,7 @@ if ((GTK_WIDGET_FLAGS (widget) & GTK_NO_SHOW_ALL) != 0) return; + class = GTK_WIDGET_GET_CLASS (widget); @@ -3400,6 +3573,127 @@ return FALSE; } +/** + * gtk_widget_button_press_event + * @widget: a #GtkWidget + * @event: a #GtkEventKey + * +**/ +static gboolean gtk_widget_button_press_event(GtkWidget *widget, GdkEventButton *event ) +{ + if( !mouse_pressed /*&& !GTK_IS_TREE_VIEW(widget) && !GTK_IS_ENTRY(widget)*/ ) + { + GtkWidget *toplevel; + toplevel = gtk_widget_get_toplevel (widget); + if (GTK_IS_WINDOW (toplevel)) + { + mouse_pressed = TRUE; + + if( /*!gtk_window_get_prev_focus_widget(GTK_WINDOW(toplevel)) &&*/ + GTK_IS_WIDGET(GTK_WINDOW(toplevel)->focus_widget) ) + gtk_window_set_prev_focus_widget( GTK_WINDOW(toplevel), + GTK_WINDOW(toplevel)->focus_widget ); + } + } + return FALSE; +} + +/** + * gtk_widget_button_release_event + * @widget: a #GtkWidget + * @event: a #GtkEventKey + * +**/ +static gboolean gtk_widget_button_release_event(GtkWidget *widget, GdkEventButton *event ) +{ + if( mouse_pressed /*&& !GTK_IS_ENTRY(widget)*/ ) + { + GtkWidget *toplevel; + GtkWidget *event_widget; + event_widget = gtk_get_event_widget( (GdkEvent*) event ); + toplevel = gtk_widget_get_toplevel (widget); + + mouse_pressed = FALSE; + on_same_widget = TRUE; + + if (GTK_IS_WINDOW (toplevel)) + { + if( !on_same_widget && GTK_IS_WIDGET(GTK_WINDOW(toplevel)->focus_widget) ) + gtk_window_set_prev_focus_widget( GTK_WINDOW(toplevel), GTK_WINDOW(toplevel)->focus_widget ); + else + gtk_window_set_prev_focus_widget( GTK_WINDOW(toplevel), event_widget ); + } + } + return FALSE; +} + +/** + * gtk_widget_enter_notify_event + * @widget: a #GtkWidget + * @event: a #GtkEventCrossing + * +**/ +static gboolean gtk_widget_enter_notify_event( GtkWidget *widget, GdkEventCrossing *event ) +{ + GtkWidget *toplevel; + GtkWidget *event_widget; + /*if( GTK_IS_ENTRY(widget) ) + return FALSE;*/ + + toplevel = gtk_widget_get_toplevel (widget); + event_widget = gtk_get_event_widget ( (GdkEvent*) event ); + + if(mouse_pressed && !on_same_widget && gtk_window_get_prev_focus_widget( GTK_WINDOW(toplevel) ) == event_widget) + { +/* GtkWidget *temp;*/ + on_same_widget = TRUE; + +/* temp = gtk_window_get_prev_focus_widget( GTK_WINDOW(toplevel) );*/ + if( GTK_IS_WIDGET(GTK_WINDOW(toplevel)->focus_widget) ) + { + gtk_window_set_prev_focus_widget( GTK_WINDOW(toplevel), GTK_WINDOW(toplevel)->focus_widget ); + if( GTK_WIDGET_CAN_FOCUS(event_widget) ) + gtk_widget_grab_focus( event_widget ); + else + gtk_widget_activate( event_widget ); + } + } + return FALSE; +} + + +/** + * gtk_widget_leave_notify_event + * @widget: a #GtkWidget + * @event: a #GtkEventCrossing + * +**/ +static gboolean gtk_widget_leave_notify_event( GtkWidget *widget, GdkEventCrossing *event ) +{ + if( mouse_pressed && on_same_widget /*&& !GTK_IS_ENTRY(widget)*/ ) + { + GtkWidget *event_widget; + GtkWidget *toplevel; + GtkWidget *temp; + toplevel = gtk_widget_get_toplevel( widget ); + event_widget = gtk_get_event_widget( (GdkEvent*) event ); + on_same_widget = FALSE; + + temp = gtk_window_get_prev_focus_widget( GTK_WINDOW(toplevel) ); + if( GTK_IS_WIDGET(temp) && + gtk_window_has_toplevel_focus(GTK_WINDOW(toplevel)) ) + { + gtk_window_set_prev_focus_widget( GTK_WINDOW(toplevel), event_widget ); + if( GTK_WIDGET_CAN_FOCUS(temp) ) + gtk_widget_grab_focus( temp ); + else + gtk_widget_activate( temp ); + } + } + return FALSE; +} + + #define WIDGET_REALIZED_FOR_EVENT(widget, event) \ (event->type == GDK_FOCUS_CHANGE || GTK_WIDGET_REALIZED(widget)) @@ -3947,11 +4241,14 @@ static void gtk_widget_real_grab_focus (GtkWidget *focus_widget) { - if (GTK_WIDGET_CAN_FOCUS (focus_widget)) + if (GTK_WIDGET_CAN_FOCUS (focus_widget) && + GTK_WIDGET_VISIBLE (focus_widget)) { + static GtkIMContext *last_context = NULL; GtkWidget *toplevel; GtkWidget *widget; - + GtkIMContext *context; + /* clear the current focus setting, break if the current widget * is the focus widget's parent, since containers above that will * be set by the next loop. @@ -3972,6 +4269,53 @@ return; } + + /* Focus change stuff (previously in modified im context) */ + if (GTK_IS_ENTRY (widget)) + context = GTK_ENTRY (widget)->im_context; + else if (GTK_IS_TEXT_VIEW (widget)) + context = GTK_TEXT_VIEW (widget)->im_context; + else + context = NULL; + + if (context || last_context) + { + gboolean is_combo, is_inside_toolbar; + GtkWidget *parent; + + parent = gtk_widget_get_parent (focus_widget); + is_combo = GTK_IS_TOGGLE_BUTTON (focus_widget) && + (GTK_IS_COMBO_BOX_ENTRY (parent) || + GTK_IS_COMBO_BOX (parent)); + is_inside_toolbar = + gtk_widget_get_ancestor (focus_widget, + GTK_TYPE_TOOLBAR) != NULL; + + if (focus_widget == NULL || + !GTK_IS_ENTRY (focus_widget) && + !GTK_IS_TEXT_VIEW (focus_widget) && + !GTK_IS_SCROLLBAR (focus_widget) && + !GTK_IS_MENU_ITEM (focus_widget) && + !GTK_IS_MENU (focus_widget) && + !is_inside_toolbar && + !is_combo) + { + /* we can't hide IM without IM context. it's possible to move + * focus to widget which doesn't have IM context, but which + * doesn't want IM to be hidden either. So, we have this + * static last_context variable which is used... */ + gtk_im_context_hide (context != NULL + ? context : last_context); + } + + if (context) + { + if (last_context != NULL) + g_object_unref (last_context); + last_context = context; + g_object_ref (last_context); + } + } if (widget) { @@ -4462,9 +4806,13 @@ { g_return_if_fail (GTK_IS_WIDGET (widget)); - if (!GTK_WIDGET_USER_STYLE (widget) && - !GTK_WIDGET_RC_STYLE (widget)) + if (!GTK_WIDGET_USER_STYLE (widget) && !GTK_WIDGET_RC_STYLE (widget)) + { + gboolean hfh = FALSE; gtk_widget_reset_rc_style (widget); + gtk_widget_style_get( widget, "hildon-focus-handling", &hfh, NULL ); + gtk_widget_set_focus_handling( widget, hfh ); + } } /* Look up the RC style for this widget, unsetting any user style that @@ -6396,7 +6744,7 @@ if (!GTK_WIDGET_DIRECTION_SET (widget)) gtk_widget_emit_direction_changed (widget, old_dir); - + if (GTK_IS_CONTAINER (widget)) gtk_container_forall (GTK_CONTAINER (widget), gtk_widget_set_default_direction_recurse, @@ -6405,6 +6753,13 @@ g_object_unref (widget); } +/* Non static */ +void gtk_widget_set_direction_recursive(GtkWidget * widget, GtkTextDirection dir ) +{ + gtk_widget_set_default_direction_recurse( widget, GUINT_TO_POINTER(dir) ); +} + + /** * gtk_widget_set_default_direction: * @dir: the new default direction. This cannot be @@ -6422,7 +6777,7 @@ { GList *toplevels, *tmp_list; GtkTextDirection old_dir = gtk_default_direction; - + gtk_default_direction = dir; tmp_list = toplevels = gtk_window_list_toplevels (); @@ -6497,6 +6852,7 @@ gtk_widget_finalize (GObject *object) { GtkWidget *widget = GTK_WIDGET (object); + GtkWidgetPrivate *priv = GTK_WIDGET_GET_PRIVATE(object); GtkWidgetAuxInfo *aux_info; gint *events; GdkExtensionMode *mode; @@ -6507,6 +6863,12 @@ g_object_unref (widget->style); widget->style = NULL; + if (priv->timer_id) + { + g_source_remove (priv->timer_id); + priv->timer_id = 0; + } + if (widget->name) g_free (widget->name); @@ -6526,6 +6888,12 @@ if (accessible) g_object_unref (accessible); + if (GTK_IS_MENU(priv->menu)) + gtk_widget_destroy (priv->menu); + + if (priv->fake_event) + gdk_event_free (priv->fake_event); + G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -7577,3 +7945,450 @@ g_object_notify (G_OBJECT (widget), "no_show_all"); } + +void gtk_widget_insensitive_press ( GtkWidget *widget ) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + g_signal_emit(widget, widget_signals[INSENSITIVE_PRESS], 0); +} + +/*Tap And Hold*/ + +#ifdef TAP_AND_HOLD_ANIMATION +static void +init_tap_and_hold_animation( GtkWidgetPrivate *priv ) +{ + GTimeVal time; + if( priv->anim ) + { + g_get_current_time( &time ); + priv->iter = gdk_pixbuf_animation_get_iter( priv->anim, &time ); + priv->interval = gdk_pixbuf_animation_iter_get_delay_time( priv->iter ); + } +} + +static void +timeout_tap_and_hold_animation( GtkWidgetPrivate *priv ) +{ + GdkScreen *screen; + GdkPixbuf *pic; + GdkCursor *cursor; + GTimeVal time; + + if( priv->anim ) + { + screen = gdk_screen_get_default(); + g_get_current_time( &time ); + + pic = gdk_pixbuf_animation_iter_get_pixbuf( priv->iter ); + cursor = gdk_cursor_new_from_pixbuf( gdk_display_get_default(), pic, + priv->width, priv->height ); + + gdk_window_set_cursor( priv->fake_event->button.window, cursor ); + + gdk_pixbuf_animation_iter_advance( priv->iter, &time ); + } +} + +static void +stop_tap_and_hold_animation( GtkWidgetPrivate *priv ) +{ + if( priv->anim ) + { + gdk_window_set_cursor( priv->fake_event->button.window, NULL ); + } +} + + +#endif + +void tap_and_hold_remove_timer( GtkWidgetPrivate *priv ) +{ + if (priv->timer_id) + { + g_source_remove (priv->timer_id); + priv->timer_id = 0; + #ifdef TAP_AND_HOLD_ANIMATION + stop_tap_and_hold_animation( priv ); + #endif + } +} + +/** + * gtk_widget_tap_and_hold_setup: + * + * @widget : A @GtkWidget + * @menu : A @GtkWidget + * @func : A @GtkCallback + * @flags : A @GtkWidgetTapAndHoldFlags + * + * Setups the tap and hold functionality to the @widget. + * The @menu is shown when the functionality is activated. + * If the @menu is wanted to be positioned in a different way than the + * gtk+ default, the menuposition @func can be passed as a third parameter. + * Fourth parameter, @flags are explaned with detail in the documentation. + */ +void gtk_widget_tap_and_hold_setup (GtkWidget *widget, GtkWidget *menu, + GtkCallback func, GtkWidgetTapAndHoldFlags flags) +{ + /*GtkWidgetClass *klass = GTK_WIDGET_GET_CLASS(widget);*/ + g_return_if_fail( GTK_IS_WIDGET(widget)); + g_return_if_fail(menu == NULL || GTK_IS_MENU(menu)); + g_signal_emit( widget, widget_signals[TAP_AND_HOLD_SETUP], 0, menu, func, + flags ); +} + +static void gtk_widget_tap_and_hold_setup_real (GtkWidget *widget, + GtkWidget *menu, GtkCallback func, GtkWidgetTapAndHoldFlags flags) +{ + #ifdef TAP_AND_HOLD_ANIMATION + GtkStyle *style = NULL; + GError *error = NULL; + #endif + GtkWidgetPrivate *priv; + g_return_if_fail (GTK_IS_WIDGET(widget)); + g_return_if_fail (menu == NULL || GTK_IS_MENU(menu)); + priv = GTK_WIDGET_GET_PRIVATE (widget); + + if (priv->signals_connected) + return; + + _gtk_menu_enable_context_menu_behavior (menu); + + priv->menu = menu; + priv->func = (GtkMenuPositionFunc)func; + priv->signals_connected = TRUE; + priv->timer_counter = 0; + priv->flags = flags; + + if (flags & GTK_TAP_AND_HOLD_PASS_PRESS) + { + g_signal_connect( widget, "button-press-event", + G_CALLBACK(gtk_widget_tap_and_hold_button_press_with_events), priv ); + g_signal_connect( widget, "button-release-event", + G_CALLBACK(gtk_widget_tap_and_hold_button_release_with_events), priv ); + g_signal_connect( widget, "leave-notify-event", + G_CALLBACK(gtk_widget_tap_and_hold_leave_notify_with_events), priv ); + } + else + { + g_signal_connect( widget, "button-press-event", + G_CALLBACK(gtk_widget_tap_and_hold_button_press), priv ); + g_signal_connect( widget, "button-release-event", + G_CALLBACK(gtk_widget_tap_and_hold_button_release), priv ); + g_signal_connect( widget, "leave-notify-event", + G_CALLBACK(gtk_widget_tap_and_hold_leave_notify), priv ); + } + +#ifdef TAP_AND_HOLD_ANIMATION + priv->anim = NULL; + style = gtk_rc_get_style_by_paths(gtk_settings_get_default(), + "gtk-tap-and-hold-animation", + NULL, G_TYPE_NONE); + + if( style ) + { + + priv->anim = gdk_pixbuf_animation_new_from_file( + (gchar*)style->rc_style->bg_pixmap_name[0], &error ); + + priv->width = gdk_pixbuf_animation_get_width( priv->anim )/2; + priv->height = gdk_pixbuf_animation_get_height( priv->anim )/2; + } +#endif +} + +static void gtk_widget_real_tap_and_hold(GtkWidget *widget) +{ + GtkWidgetPrivate *priv = GTK_WIDGET_GET_PRIVATE (widget); + + if (GTK_IS_MENU(priv->menu)) + gtk_menu_popup( GTK_MENU(priv->menu), NULL, NULL, + (GtkMenuPositionFunc)priv->func, + widget, 1, gdk_x11_get_server_time(widget->window) ); +} + +static gboolean gtk_widget_tap_and_hold_timeout (GtkWidget *widget) +{ + GtkWidgetPrivate *priv= GTK_WIDGET_GET_PRIVATE(widget); + gboolean return_value; + gint x = 0, y = 0; + + #ifdef TAP_AND_HOLD_ANIMATION + timeout_tap_and_hold_animation( priv ); + #endif + + if( priv->timer_counter ) + priv->timer_counter--; + else + priv->timer_id = 0; + + gdk_display_get_pointer( gdk_x11_lookup_xdisplay( + GDK_WINDOW_XDISPLAY(priv->fake_event->button.window) ), + NULL, &x, &y, NULL ); + + if ((abs(x - priv->x) > GTK_TAP_THRESHOLD) || + (abs(y - priv->y) > GTK_TAP_THRESHOLD)) + { + if (priv->stype != priv->type_on_press) + gtk_widget_set_state( widget, priv->stype ); + priv->timer_counter = 0; + priv->timer_id = 0; + priv->x = priv->y = 0; + priv->run_press = FALSE; + g_signal_emit_by_name (G_OBJECT(widget), "button-press-event", + priv->fake_event, &return_value); + return FALSE; + } + if (!priv->timer_id) + { + if (priv->stype != priv->type_on_press) + gtk_widget_set_state( widget, priv->stype ); + #ifdef TAP_AND_HOLD_ANIMATION + stop_tap_and_hold_animation( priv ); + #endif + g_signal_emit(widget, widget_signals[TAP_AND_HOLD], 0); + priv->x = x; + priv->y = y; + return FALSE; + } + return TRUE; +} + +static gboolean gtk_widget_tap_and_hold_button_press (GtkWidget *widget, + GdkEvent *event, GtkWidgetPrivate *priv) +{ + if (!priv->run_press || event->button.button != 1) + { + priv->run_press = TRUE; + return FALSE; + } + + if (event->button.type == GDK_2BUTTON_PRESS) + return FALSE; + + if (priv->fake_event) + gdk_event_free (priv->fake_event); + priv->fake_event = gdk_event_copy(event); + + if (!priv->timer_id) + { + priv->stype = GTK_WIDGET_STATE(widget); + if (priv->stype != priv->type_on_press) + gtk_widget_set_state( widget, priv->type_on_press ); + gdk_display_get_pointer( + gdk_x11_lookup_xdisplay( GDK_WINDOW_XDISPLAY(event->button.window) ), + NULL, &priv->x, &priv->y, NULL ); + priv->timer_counter = GTK_TAP_AND_HOLD_TIMER_COUNTER; + + #ifdef TAP_AND_HOLD_ANIMATION + init_tap_and_hold_animation( priv ); + #endif + priv->timer_id = g_timeout_add( priv->interval, + (GSourceFunc)gtk_widget_tap_and_hold_timeout, widget ); + } +return TRUE; +} + +static gboolean gtk_widget_tap_and_hold_button_release (GtkWidget *widget, + GdkEvent *event, GtkWidgetPrivate *priv) +{ + gboolean return_value; + + if (!priv->run_press || event->button.button != 1 || !priv->timer_id || + event->button.type == GDK_2BUTTON_PRESS) + return FALSE; + + g_source_remove (priv->timer_id); + priv->timer_id = 0; + priv->x = priv->y = priv->timer_counter = 0; + if (priv->stype != priv->type_on_press) + gtk_widget_set_state (widget, priv->stype); + + #ifdef TAP_AND_HOLD_ANIMATION + stop_tap_and_hold_animation( priv ); + #endif + + if (priv->flags & GTK_TAP_AND_HOLD_NO_SIGNALS) + return FALSE; + + priv->run_press = FALSE; + + g_signal_emit_by_name (G_OBJECT(widget), "button-press-event", + priv->fake_event, &return_value); + +return FALSE; +} + +static gboolean gtk_widget_tap_and_hold_leave_notify (GtkWidget *widget, + GdkEvent *event, GtkWidgetPrivate *priv) +{ + gboolean return_value; + if (!priv->timer_id) + return FALSE; + + g_source_remove (priv->timer_id); + priv->timer_id = 0; + priv->x = priv->y = priv->timer_counter = 0; + if (priv->stype != priv->type_on_press) + gtk_widget_set_state (widget, priv->stype); + + #ifdef TAP_AND_HOLD_ANIMATION + stop_tap_and_hold_animation( priv ); + #endif + priv->run_press = FALSE; + g_signal_emit_by_name (G_OBJECT(widget), "button-press-event", + priv->fake_event, &return_value); + +return FALSE; +} + +static gboolean +gtk_widget_tap_and_hold_timeout_with_events (GtkWidget *widget) +{ + gint x, y; + GtkWidgetPrivate *priv= GTK_WIDGET_GET_PRIVATE(widget); + + g_return_val_if_fail (priv->fake_event, FALSE); + + #ifdef TAP_AND_HOLD_ANIMATION + timeout_tap_and_hold_animation( priv ); + #endif + + gdk_display_get_pointer( gdk_x11_lookup_xdisplay( + GDK_WINDOW_XDISPLAY(priv->fake_event->button.window) ), + NULL, &x, &y, NULL ); + + if( priv->timer_counter ) + { + priv->timer_counter--; + if ((abs(x - priv->x) > GTK_TAP_THRESHOLD) || + (abs(y - priv->y) > GTK_TAP_THRESHOLD)) + { + #ifdef TAP_AND_HOLD_ANIMATION + stop_tap_and_hold_animation( priv ); + #endif + tap_and_hold_remove_timer( priv ); + } + return TRUE; + } + + if (!((abs(x - priv->x) > GTK_TAP_THRESHOLD) || + (abs(y - priv->y) > GTK_TAP_THRESHOLD))) + { + gboolean return_value; + priv->fake_event->button.type = GDK_BUTTON_RELEASE; + priv->fake_event->button.x = x; + priv->fake_event->button.y = y; + g_signal_emit_by_name (G_OBJECT(widget), "button-release-event", + priv->fake_event, &return_value); + #ifdef TAP_AND_HOLD_ANIMATION + stop_tap_and_hold_animation( priv ); + #endif + g_signal_emit(widget, widget_signals[TAP_AND_HOLD], 0); + priv->timer_id = 0; + priv->x = x; + priv->y = y; + gdk_event_free(priv->fake_event); + priv->fake_event = NULL; + } + + + if (priv->timer_id) + { + g_source_remove (priv->timer_id); + priv->timer_id = 0; + } + + return FALSE; +} + +static gboolean gtk_widget_tap_and_hold_button_press_with_events( + GtkWidget *widget, GdkEvent *event, GtkWidgetPrivate *priv) +{ + if( priv->timer_id || event->button.type == GDK_2BUTTON_PRESS) + return FALSE; + + if (priv->fake_event) + gdk_event_free (priv->fake_event); + priv->fake_event = gdk_event_copy (event); + + gdk_display_get_pointer( + gdk_x11_lookup_xdisplay(GDK_WINDOW_XDISPLAY(event->button.window)), + NULL, &priv->x, &priv->y, NULL); + #ifdef TAP_AND_HOLD_ANIMATION + init_tap_and_hold_animation( priv ); + #endif + priv->timer_counter = GTK_TAP_AND_HOLD_TIMER_COUNTER; + priv->timer_id = g_timeout_add(priv->interval, + (GSourceFunc)gtk_widget_tap_and_hold_timeout_with_events, + widget); + return FALSE; +} + +static gboolean gtk_widget_tap_and_hold_button_release_with_events( + GtkWidget *widget, GdkEvent *event, GtkWidgetPrivate *priv) +{ + tap_and_hold_remove_timer( priv ); + return FALSE; +} + +static gboolean gtk_widget_tap_and_hold_leave_notify_with_events( + GtkWidget *widget, GdkEvent *event, GtkWidgetPrivate *priv) +{ + tap_and_hold_remove_timer( priv ); + return FALSE; +} + +/** + * gtk_widget_tap_and_hold_menu_position_top: + * @menu: a #GtkMenu + * @x: x cordinate to be returned + * @y: y cordinate to be returned + * @push_in: If going off screen, push it pack on the screen + * @widget: a #GtkWidget + * + * Pre-made menu positioning function. + * It positiones the @menu over the @widget. + * + **/ +void gtk_widget_tap_and_hold_menu_position_top( GtkWidget *menu, + gint *x, gint *y, gboolean *push_in, GtkWidget *widget ) +{ + /* + * This function positiones the menu above widgets. + * This is a modified version of the position function + * gtk_combo_box_position_over. + */ + GtkWidget *topw; + GtkRequisition requisition; + gint screen_width = 0; + gint menu_xpos = 0; + gint menu_ypos = 0; + gint w_xpos = 0, w_ypos = 0; + + gtk_widget_size_request( menu, &requisition ); + + topw = gtk_widget_get_toplevel(widget); + gdk_window_get_origin( topw->window, &w_xpos, &w_ypos ); + + menu_xpos += widget->allocation.x + w_xpos; + menu_ypos += widget->allocation.y + w_ypos - requisition.height; + + if( gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL ) + menu_xpos = menu_xpos + widget->allocation.width - requisition.width; + + screen_width = gdk_screen_get_width( gtk_widget_get_screen(widget) ); + + if( menu_xpos < w_xpos ) + menu_xpos = w_xpos; + else if( (menu_xpos + requisition.width) > screen_width ) + menu_xpos -= ( (menu_xpos + requisition.width) - screen_width ); + if( menu_ypos < w_ypos ) + menu_ypos = w_ypos; + + *x = menu_xpos; + *y = menu_ypos; + *push_in = TRUE; +}