/* vim:set sts=2 sw=2: gcc -Wall -O2 -g `pkg-config --cflags --libs gtk+-2.0` -o popuptest popuptest.c */ #include static void synthesize_button_release (GtkWidget *widget) { GdkEvent *event; event = gdk_event_new (GDK_BUTTON_RELEASE); event->button.time = GDK_CURRENT_TIME; event->button.button = 1; gtk_widget_event (widget, event); gdk_event_free (event); } static void do_popup_menu (GtkWidget *my_widget, GdkEventButton *event) { GtkWidget *menu; GtkWidget *item; int button, event_time; g_print ("[%p] do_popup\n", my_widget); menu = gtk_menu_new (); g_signal_connect_swapped (menu, "deactivate", G_CALLBACK (synthesize_button_release), my_widget); /* ... add menu items ... */ item = gtk_menu_item_new_with_label ("Properties..."); gtk_widget_show (item); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); if (event) { button = event->button; event_time = event->time; } else { button = 0; event_time = gtk_get_current_event_time (); } gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, event_time); } static gboolean on_popup_menu (GtkWidget *widget) { do_popup_menu (widget, NULL); return TRUE; } static void on_button_clicked (GtkButton *button) { g_print ("[%p] *click*\n", button); } static gboolean on_timeout (gpointer user_data) { GtkWidget *widget = user_data; /* synthesizing the button release event here generates an extra 'clicked' * event for buttons * * synthesize_button_release (widget); */ do_popup_menu (widget, NULL); return FALSE; } static gboolean on_button_press_event (GtkWidget *widget, GdkEventButton *event) { g_print ("[%p] button-press (%d)\n", widget, event->button); if (event->button == 1) { guint tid; tid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), "timeout-id")); if (tid) g_source_remove (tid); tid = g_timeout_add (1200, on_timeout, widget); g_object_set_data (G_OBJECT (widget), "timeout-id", GUINT_TO_POINTER (tid)); } return FALSE; } static gboolean on_button_release_event (GtkWidget *widget, GdkEventButton *event) { g_print ("[%p] button-release (%d)\n", widget, event->button); if (event->button == 1) { guint tid; tid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), "timeout-id")); if (tid) g_source_remove (tid); g_object_set_data (G_OBJECT (widget), "timeout-id", GUINT_TO_POINTER (0)); } return FALSE; } static void setup_context_menu (GtkWidget *widget) { g_signal_connect (widget, "popup-menu", G_CALLBACK (on_popup_menu), 0); g_signal_connect (widget, "button-press-event", G_CALLBACK (on_button_press_event), 0); g_signal_connect (widget, "button-release-event", G_CALLBACK (on_button_release_event), 0); } int main (int argc, char **argv) { GtkWidget *window; GtkWidget *box; GtkWidget *widget; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), 0); box = gtk_hbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (window), box); /* Entry */ widget = gtk_entry_new (); gtk_entry_set_text (GTK_ENTRY (widget), "Extra Drag"); setup_context_menu (widget); gtk_box_pack_start (GTK_BOX (box), widget, TRUE, TRUE, 0); /* Extra Click */ widget = gtk_button_new_with_label ("Extra Click"); setup_context_menu (widget); g_signal_connect (widget, "clicked", G_CALLBACK (on_button_clicked), 0); gtk_box_pack_start (GTK_BOX (box), widget, FALSE, TRUE, 0); gtk_widget_show_all (window); gtk_main (); return 0; }