Popup menus for GtkTreeView



I want a context-sensitive menu in a GtkTreeView.

So I do

	g_signal_connect (tree_view, "button-press_event",
			  G_CALLBACK (tree_button_press_event_cb), NULL);

	static gboolean
	tree_button_press_event_cb (GtkWidget *widget, GdkEventButton *event, gpointer data)
	{
		GtkTreeView *tree_view;

		tree_view = GTK_TREE_VIEW (widget);

		if (event->button != 3)
			return FALSE; /* let the default handler run */

		my_popup_menu (tree_view, event);
		return TRUE;
	}

But the tree's selection doesn't get updated when I press the button.
So, if I press button 1 on row A and press button 3 on row B, the
context menu thinks that it refers to row A while I clearly clicked on
row B.

So let's try again

	/* connect_after() so that the tree will handle the button press
	 * before I do and thus update the selection
         */
	g_signal_connect_after (tree_view, "button_press_event",
				...);

But that doesn't work at all, since gtk_tree_view_button_press_event()
returns TRUE in almost all cases, and so my handler doesn't get run.

So I dig up my m4d GTK+ sk1llz and do

	g_signal_connect (tree_view, "button-press_event",
			  G_CALLBACK (tree_button_press_event_cb), NULL);

	static gboolean
	tree_button_press_event_cb (GtkWidget *widget, GdkEventButton *event, gpointer data)
	{
		GtkTreeView *tree_view;
		GtkTreePath *path;
		GtkTreeSelection *selection;

		if (event->button != 3)
			return FALSE; /* let the default handler run */

		tree_view = GTK_TREE_VIEW (widget);

		/* Event outside the interesting window? */
		if (event->window != gtk_tree_view_get_bin_window (tree_view))
			return FALSE;

		selection = gtk_tree_view_get_selection (tree_view);

		if (gtk_tree_view_get_path_at_pos (tree_view,
						   event->x, event->y,
						   &path,
						   NULL, NULL, NULL, NULL)) {
			/* We are on a row.  Select it */
			gtk_tree_selection_select_path (selection, path);
			gtk_tree_path_free (path);
		} else {
			/* We are on the blank area below all the rows.
			 * Clear the selection.
			 */
			gtk_tree_selection_unselect_all (selection);
		}

		my_popup_menu (tree_view, event);
	}

But that doesn't work either!  If I do Shift+button_3 or Ctrl+button_3
in multiple selection mode, the modifiers don't have the usual
semantics.  Now GTK+ is just abusing its spike-studded leather flogger
on me, and it's ignoring my safe word.

So I steal the leather flogger from its hand and tell it to assume the
position.

	g_signal_connect (tree_view, "button-press_event",
			  G_CALLBACK (tree_button_press_event_cb), NULL);

	static gboolean
	tree_button_press_event_cb (GtkWidget *widget, GdkEventButton *event, gpointer data)
	{
		static gboolean in_press = FALSE;
		GtkTreeView *tree_view;
		gboolean handled;

		tree_view = GTK_TREE_VIEW (widget);

		if (in_press)
			return FALSE; /* we re-entered */

		if (event->button != 3)
			return FALSE; /* let the normal handler run */

		in_press = TRUE;
		handled = gtk_widget_event (widget, (GdkEvent *) event);
		in_press = FALSE;

		if (!handled)
			return FALSE;

		/* The selection is fully updated by now */
		my_popup_menu (tree_view, event);
		return TRUE;
	}

Okay, that works, but it is gross.  It may even be missing a check for
the bin_window.

Then I press Shift-F10 and since I'm a good citizen and implemented
the ::popup_menu signal, I get a menu.  But the motherfucker is far away
from the selected row!  Now GTK+ is really abusing me in ways to which
we didn't consent.

We need a signal in GtkTreeView like this:

gboolean (* tree_popup_menu) (GtkTreeView       *tree_view,
			      int                button,    /* 0 if keyboard */
			      guint32	         timestamp,
			      GtkTreePath       *path,      /* or an iter if you wish */
			      GtkTreeViewColumn *column);

That signal should get emitted automatically when one presses button 3
on the treeview, and it should already have the right rows selected.
The button argument should be 0 when the signal didn't happen through a
mouse button, but through the keyboard's "popup-menu" binding.

It may also need a way to discriminate which particular cell renderer on
which the button press landed, or even the coordinates within that
renderer, for the true perverts who would find that stuff useful.

Thoughts?

  Federico




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]