LinuxQuestions.org
Visit Jeremy's Blog.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 08-12-2021, 12:25 PM   #1
CliveMcCarthy
LQ Newbie
 
Registered: Aug 2021
Posts: 13

Rep: Reputation: Disabled
Gtk drag and drop


I use Kernel 5.4 with Linux Mint 20.2 and the Mate (Gnome) 1.24 desktop.

I'm writing a simple Gtk application -- it's not my first -- it's basically a film strip image selector.
The program needs to drag and drop images. My drop code works fine . I can successfully drop images from Caja (the File Manager for the Mate desktop) into my program.

However, I can't drag and drop images within my own program . Since the drop code works I must have something wrong with my drag code. My drag code looks like this:
Code:
void set_drag_image
(
	GtkWidget          *widget,
	__attribute__((unused))GdkDragContext     *context,
	GtkSelectionData   *selection_data,
	__attribute__((unused))guint               info,
	__attribute__((unused))guint               time,
	__attribute__((unused))gpointer            data
)
{
	char widget_name[10];
	int strip_location;
	guchar *dropped_text;

	strcpy(widget_name, gtk_widget_get_name(widget));
	strip_location = atoi(widget_name);
	dropped_text = (guchar *)film_strip_data[strip_location].dropped_text;

	printf
	(
		"source_drag_image called %s\n",
		dropped_text
	);
	
	gtk_selection_data_set_text
	(
		selection_data, (gchar *)dropped_text, sizeof(dropped_text)
	);

	gtk_selection_data_set
	(
		selection_data,
		gtk_selection_data_get_target(selection_data),
		8,	// 8 bits
		dropped_text, sizeof(dropped_text)
	);
}

...
	for(int k = 0; k < STRIP_COUNT; k++) // iterate through the film strip's widgets
	{
		g_signal_connect
		(
			image_strip[k], "drag_data_get",
			G_CALLBACK(set_drag_image),
			NULL
		);

		gtk_drag_source_set
		(
			GTK_WIDGET(image_strip[k]),
			GDK_BUTTON1_MASK,
			drag_and_drop_type_table,
			G_N_ELEMENTS(drag_and_drop_type_table),
			GDK_ACTION_COPY
		);
	}
The printf() shows nothing so the drop receiver isn't getting anything. The Gtk documentation is not at all helpful and I haven't found a good on-line code example.

I have been banging away for a few days and am getting nowhere. Thoughts please ?
 
Old 08-12-2021, 05:50 PM   #2
EdGr
Member
 
Registered: Dec 2010
Location: California, USA
Distribution: I run my own OS
Posts: 693

Rep: Reputation: 329Reputation: 329Reputation: 329Reputation: 329
The signal is "drag-data-get" (with hyphens).

For drag to work, the widget needs to have its own window so that it receives X events. GtkDrawingArea, GtkButton, and GtkWindow have their own windows, but most widgets (including GtkImage) use their parent's window.

You can connect your handler to the top-level GtkWindow to verify that it gets called.
Ed
 
1 members found this post helpful.
Old 08-12-2021, 06:22 PM   #3
CliveMcCarthy
LQ Newbie
 
Registered: Aug 2021
Posts: 13

Original Poster
Rep: Reputation: Disabled
It's unclear what you mean ?

g_signal_connect
(
image_strip[k], "drag_data_get",
G_CALLBACK(set_drag_image),
NULL
);

Did I misspell it ?

My code isn't dragging and dropping images around, as such, merely references to them. When I drag from Caja I get a string such as this: "file:///mnt/art/C/Steenbeck/DSC_9589.JPG" My code then strips the "file://" prefix, and stores the image file name in a list. It then displays a scaled, cropped and and modified version of the image in the widget. So far so good.

Now I want to drag from a loaded widget to another widget (this is a montage making tool). The problem I have is that the drag isn't happening at all -- I see no data transfer of any kind.

I'm now using https://wiki.gnome.org/action/show/N...gNDropTutorial

But that seems to exhibit the same behavior...
 
Old 08-12-2021, 06:32 PM   #4
astrogeek
Moderator
 
Registered: Oct 2008
Distribution: Slackware [64]-X.{0|1|2|37|-current} ::12<=X<=14, FreeBSD_12{.0|.1}
Posts: 5,717
Blog Entries: 11

Rep: Reputation: 3748Reputation: 3748Reputation: 3748Reputation: 3748Reputation: 3748Reputation: 3748Reputation: 3748Reputation: 3748Reputation: 3748Reputation: 3748Reputation: 3748
With respect to the signal name I think EdGr meant that it should be this, as used in the link you referenced:

Code:
"drag-data-get"
...as opposed to this...

Code:
"drag_data_get"
See the difference?
 
Old 08-12-2021, 06:38 PM   #5
EdGr
Member
 
Registered: Dec 2010
Location: California, USA
Distribution: I run my own OS
Posts: 693

Rep: Reputation: 329Reputation: 329Reputation: 329Reputation: 329
First fix the spelling.

If the code still does not work, try connecting the drag handler to the top-level GtkWidget which will have its own window.
Ed
 
Old 08-12-2021, 07:14 PM   #6
CliveMcCarthy
LQ Newbie
 
Registered: Aug 2021
Posts: 13

Original Poster
Rep: Reputation: Disabled
Damn. Back in 1968 I spent several days with a spelling error in my Algol code on a KDF9. Seems I haven't moved on.

I think I still have trouble.

and I note:

Because GTK+ implements drag and drop on top of the native platform's windowing system, widgets must have a GdkWindow to be used as a drag source.

Which is what you are pointing out, I think.
 
Old 08-12-2021, 08:29 PM   #7
CliveMcCarthy
LQ Newbie
 
Registered: Aug 2021
Posts: 13

Original Poster
Rep: Reputation: Disabled
I have made all of my picture widgets top-level windows:
Code:
	for(k = 0; k < STRIP_COUNT; k++)
	{
		image_strip[k] = gtk_window_new(GTK_WINDOW_TOPLEVEL);
		sprintf(widget_name, "%2d", k + 1);
		gtk_widget_set_name(image_strip[k], widget_name);

		image_strip[k] = gtk_image_new_from_pixbuf
		(
			initial_image(image_display_height)
		);
		gtk_widget_set_size_request(image_strip[k], FRAME_WIDTH, FRAME_HEIGHT);

		mode_entry[k] = gtk_entry_new();
		gtk_widget_set_name(mode_entry[k], widget_name);

		// input text is right justified
		gtk_entry_set_alignment(GTK_ENTRY(mode_entry[k]), 1);
		gtk_editable_set_position(GTK_EDITABLE(mode_entry[k]), -1);
		gtk_entry_set_width_chars(GTK_ENTRY(mode_entry[k]), 7);
	}
but I'm still not getting any drag call backs...

Thank you for spotting my typing mistakes.
 
Old 08-12-2021, 08:52 PM   #8
EdGr
Member
 
Registered: Dec 2010
Location: California, USA
Distribution: I run my own OS
Posts: 693

Rep: Reputation: 329Reputation: 329Reputation: 329Reputation: 329
The code needs to pack the image into the window.

I didn't intend to create more windows. I asked you to connect the drag handler to the existing top-level GtkWindow to confirm that the handler gets called.

To make image selection work, you need to pick a suitable widget for use as a drag source. GtkButton would require the fewest changes to your code. The best solution is to use GtkDrawingArea. That gives the application program complete control over drawing and response to input events.

ETA: I realized the ambiguity in my previous posts was due to the word "window". A few GtkWidgets (GtkWindow, GtkButton, GtkDrawingArea) have their own GdkWindows, but most do not. Your application needs to use the former for the drag source.
Ed

Last edited by EdGr; 08-12-2021 at 10:46 PM.
 
Old 08-13-2021, 01:11 AM   #9
CliveMcCarthy
LQ Newbie
 
Registered: Aug 2021
Posts: 13

Original Poster
Rep: Reputation: Disabled
I went back to the example https://wiki.gnome.org/action/show/N...gNDropTutorial
and cross coupled the two windows. A can be dragged to B and vice versa. I'm sneaking up on the problem.

Code:
int main(int argc, char **argv)
{
	GtkWidget	   *window;
	GtkWidget	   *hbox;
	GtkWidget	   *coin;
	GtkWidget	   *well;
	GtkWidget	   *directions_label;
	guint		   win_xsize		= 200;
	guint		   win_ysize		= 200;
	guint		   spacing			= 50;

	gtk_init(&argc, &argv);

	/* Create the widgets */
	window  = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	hbox	= gtk_box_new(GTK_ORIENTATION_HORIZONTAL, spacing);

	coin	= gtk_window_new(GTK_WINDOW_TOPLEVEL);
	well	= gtk_window_new(GTK_WINDOW_TOPLEVEL);

	gtk_widget_set_name(coin,	"coin");
	gtk_widget_set_name(well,	"well");

	directions_label = gtk_label_new("drag a coin and drop it in the well");

	/* Pack the widgets */
	gtk_container_add(GTK_CONTAINER(window), hbox);
	gtk_container_add(GTK_CONTAINER(hbox), directions_label);
	gtk_container_add(GTK_CONTAINER(hbox), coin);
	gtk_container_add(GTK_CONTAINER(hbox), well);

	/* Make the window big enough for some DnD action */
	gtk_window_set_default_size(GTK_WINDOW(window), win_xsize, win_ysize);
	gtk_window_set_default_size(GTK_WINDOW(coin), win_xsize, win_ysize);
	gtk_window_set_default_size(GTK_WINDOW(well), win_xsize, win_ysize);

	/* Make the "well label" a DnD destination. */
	gtk_drag_dest_set
	(
			well,					// widget that will accept a drop
		GTK_DEST_DEFAULT_MOTION	// default actions for dest on DnD
		| GTK_DEST_DEFAULT_HIGHLIGHT,
		target_list,			// lists of target to support
		n_targets,				// size of list
		GDK_ACTION_COPY			// what to do with data after dropped
	);

	gtk_drag_dest_set
	(
		coin,				// widget will be drag-able
		GTK_DEST_DEFAULT_MOTION	// default actions for dest on DnD
		| GTK_DEST_DEFAULT_HIGHLIGHT,
		target_list,			// lists of target to support
		n_targets,				// size of list
		GDK_ACTION_COPY			// what to do with data after dropped
	);

	/* Make the "coin button" a DnD source. */
	/* Why doesn't GtkLabel work here?
	 * See Caveat Window above
	 */
	gtk_drag_source_set
	(
		coin,				// widget will be drag-able
		GDK_BUTTON1_MASK,	// modifier that will start a drag
		target_list,		// lists of target to support
		n_targets,			// size of list
		GDK_ACTION_COPY		// what to do with data after dropped
	);

	gtk_drag_source_set
	(
		well,				// widget will be drag-able
		GDK_BUTTON1_MASK,	// modifier that will start a drag
		target_list,		// lists of target to support
		n_targets,			// size of list
		GDK_ACTION_COPY		// what to do with data after dropped
	);

	/* Connect the signals */
	g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
	g_signal_connect(well, "destroy", G_CALLBACK(gtk_main_quit), NULL);
	g_signal_connect(coin, "destroy", G_CALLBACK(gtk_main_quit), NULL);

	/* All possible signals */

	g_signal_connect(well, "drag-begin",
			G_CALLBACK(drag_begin_handler), NULL);

	g_signal_connect(well, "drag-drop",
			G_CALLBACK(drag_drop_handler), NULL);

	g_signal_connect(well, "drag-data-received",
			G_CALLBACK(drag_data_received_handler), NULL);

	g_signal_connect(well, "drag-leave",
			G_CALLBACK(drag_leave_handler), NULL);

	g_signal_connect(well, "drag-data-get",
			G_CALLBACK(drag_data_get_handler), NULL);

	g_signal_connect(well, "drag-end",
			G_CALLBACK(drag_end_handler), NULL);


	g_signal_connect(coin, "drag-begin",
			G_CALLBACK(drag_begin_handler), NULL);

	g_signal_connect(coin, "drag-drop",
			G_CALLBACK(drag_drop_handler), NULL);

	g_signal_connect(coin, "drag-data-received",
			G_CALLBACK(drag_data_received_handler), NULL);

	g_signal_connect(coin, "drag-leave",
			G_CALLBACK(drag_leave_handler), NULL);

	g_signal_connect(coin, "drag-data-get",
			G_CALLBACK(drag_data_get_handler), NULL);

	g_signal_connect(coin, "drag-end",
			G_CALLBACK(drag_end_handler), NULL);

	/* Show the widgets */
	gtk_widget_show_all(window);
	gtk_widget_show_all(coin);
	gtk_widget_show_all(well);

	gtk_main();

	return 0;
}
 
Old 08-13-2021, 04:31 PM   #10
CliveMcCarthy
LQ Newbie
 
Registered: Aug 2021
Posts: 13

Original Poster
Rep: Reputation: Disabled
I now know that it is necessary for a window to be at the top level to get X11 inputs but then the 'drag_data_get_handler' complains that the drop is on a root window.

I want a pair of windows each to be a source and a destination. It also seems weird not to have a strict hierarchy of windows with one top and many children. How does one get a child window to receive X11 events ?

Code:
/*------------------------------------------------------------------------------
	dnd_window is a source and a destination and all signals are hooked up
------------------------------------------------------------------------------*/
#define TEST
void make_window_drag_and_drop
(
	GtkWidget *dnd_window,
	GtkTargetEntry *target_list,
	int n_targets
)
{
	gtk_drag_dest_set
	(
		dnd_window,				// widget that will accept a drop
		GTK_DEST_DEFAULT_MOTION	// default actions for dest on DnD
		| GTK_DEST_DEFAULT_HIGHLIGHT,
		target_list,			// lists of target to support
		n_targets,				// size of list
		GDK_ACTION_COPY			// what to do with data after dropped
	);

	gtk_drag_source_set
	(
		dnd_window,				/* widget will be drag-able */
		GDK_BUTTON1_MASK,       /* modifier that will start a drag */
		target_list,            /* lists of target to support */
		n_targets,				// size of list
		GDK_ACTION_COPY         /* what to do with data after dropped */
	);

	// connect all possible signals

	g_signal_connect
	(
		dnd_window, "drag-data-received",
		G_CALLBACK
		(
#ifdef TEST
			drag_data_received_handler	// change this as needed
#else
			rx_dragged_image
#endif
		),
		NULL
	);

	g_signal_connect
	(
		dnd_window, "drag-data-get",
		G_CALLBACK
		(
#ifdef TEST
			drag_data_get_handler	// change this as needed
#else
			tx_dragged_image
#endif
		),
		NULL
	);

	// these do nothing except report their call
	g_signal_connect(dnd_window, "drag-begin",
			G_CALLBACK(drag_begin_handler), NULL);

	g_signal_connect(dnd_window, "drag-drop",
			G_CALLBACK(drag_drop_handler), NULL);

	g_signal_connect(dnd_window, "drag-leave",
			G_CALLBACK(drag_leave_handler), NULL);

	g_signal_connect(dnd_window, "drag-end",
			G_CALLBACK(drag_end_handler), NULL);
}



int main(int argc, char **argv)
{
	GtkWidget	   *window;
	GtkWidget	   *hbox;
	GtkWidget	   *dnd_window_1;
	GtkWidget	   *dnd_window_2;
	GtkWidget	   *directions_label;
	guint		   width	= 200;
	guint		   height	= 200;
	guint		   spacing	= 50;

	gtk_init(&argc, &argv);

	// Create the widgets
	window  = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	hbox	= gtk_box_new(GTK_ORIENTATION_HORIZONTAL, spacing);

	directions_label = gtk_label_new
	(
		"dnd_window_1 <-drag and drop-> dnd_window_2"
	);

	// Pack the widgets
	gtk_container_add(GTK_CONTAINER(window), hbox);
	gtk_container_add(GTK_CONTAINER(hbox), directions_label);

	dnd_window_1	= gtk_button_new_with_label("[dnd_window_1]");
	dnd_window_2	= gtk_button_new_with_label("[dnd_window_2]");

	gtk_container_add(GTK_CONTAINER(hbox), dnd_window_1);
	gtk_container_add(GTK_CONTAINER(hbox), dnd_window_2);

	gtk_widget_set_name(dnd_window_1, "dnd_window_1");
	gtk_widget_set_name(dnd_window_2, "dnd_window_2");

	// Make the windows big enough for some DnD action
	gtk_window_set_default_size(GTK_WINDOW(window),			width, height);
	gtk_window_set_default_size(GTK_WINDOW(dnd_window_1),	width, height);
	gtk_window_set_default_size(GTK_WINDOW(dnd_window_2),	width, height);

	// Make dnd_window_1 and dnd_window_2 a DnD source and destination.
	make_window_drag_and_drop
	(
		dnd_window_1, target_list, G_N_ELEMENTS(target_list)
	);
	make_window_drag_and_drop
	(
		dnd_window_2, target_list, G_N_ELEMENTS(target_list)
	);

	g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
	gtk_widget_show_all(window);
	gtk_main();

	return 0;
}

Last edited by CliveMcCarthy; 08-13-2021 at 05:19 PM.
 
Old 08-13-2021, 06:59 PM   #11
EdGr
Member
 
Registered: Dec 2010
Location: California, USA
Distribution: I run my own OS
Posts: 693

Rep: Reputation: 329Reputation: 329Reputation: 329Reputation: 329
I will explicitly use the terms GdkWindow (= X window) and GtkWindow (= top of the GtkWidget hierarchy) to avoid confusion.

Quote:
I now know that it is necessary for a window to be at the top level to get X11 inputs but then the 'drag_data_get_handler' complains that the drop is on a root window.
Partially right. A GtkWidget needs to have its own GdkWindow to receive X events.

Quote:
I want a pair of windows each to be a source and a destination.
The same GtkWidget can be both a source and destination.

Quote:
It also seems weird not to have a strict hierarchy of windows with one top and many children.
Both GtkWidget and GdkWindow have strict hierarchies, but they are different!

This is done for efficiency: only GtkWidgets that need to receive X events have their own GdkWindows. All other GtkWidgets simply draw on their parent's GdkWindow. This behavior is built-in to GTK. You can't change it.

Quote:
How does one get a child window to receive X11 events ?
Put the GtkWidget inside a GtkEventBox.

IMO, your program is trying to do too much with GTK's built-in widgets. A better approach would be to use a GtkDrawingArea because it is intended for customization. The application can do anything it wants in its drawing and event handlers.
Ed
 
1 members found this post helpful.
Old 08-13-2021, 07:39 PM   #12
CliveMcCarthy
LQ Newbie
 
Registered: Aug 2021
Posts: 13

Original Poster
Rep: Reputation: Disabled
I think what I'm trying to do is close to a trivial application. It's a simple contact-sheet / film-strip image organizer. When I'm done it will be less than 1,000 lines of code.

So please clue me in with little code. How do I declare and then attach a GtkWidget to a GdkWindow ?

I can stash that in my:
Code:
void make_window_drag_and_drop
(
	GtkWidget *dnd_window,
	GtkTargetEntry *target_list,
	int n_targets
)
and hide all the details so when I want an array of them it is clean simple.
 
Old 08-13-2021, 08:03 PM   #13
EdGr
Member
 
Registered: Dec 2010
Location: California, USA
Distribution: I run my own OS
Posts: 693

Rep: Reputation: 329Reputation: 329Reputation: 329Reputation: 329
Add an event box as the parent of the image widget. Then, connect the event box to the drag handler.

Code:
GtkWidget *eventbox;

eventbox=gtk_event_box_new ();

gtk_container_add (GTK_CONTAINER (eventbox), imagewidget);

gtk_drag_source_set (eventbox,          /* widget will be drag-able */
                     GDK_BUTTON1_MASK,  /* modifier that will start a drag */
                     target_list,       /* lists of target to support */
                     n_targets,         /* size of list */
                     GDK_ACTION_COPY);  /* what to do with data after dropped */
Ed
 
Old 08-13-2021, 08:04 PM   #14
CliveMcCarthy
LQ Newbie
 
Registered: Aug 2021
Posts: 13

Original Poster
Rep: Reputation: Disabled
Is this the kind of thing I should do ?

for(k = 0; k < STRIP_COUNT; k++)
{
event_box[k] = gtk_event_box_new();
gtk_container_add(GTK_CONTAINER(window), event_box[k]);
gtk_container_add(GTK_CONTAINER(event_box[k]), image_strip[k]);

And by so doing each of my GtkWidgets gets X11 events ?
 
Old 08-13-2021, 08:06 PM   #15
EdGr
Member
 
Registered: Dec 2010
Location: California, USA
Distribution: I run my own OS
Posts: 693

Rep: Reputation: 329Reputation: 329Reputation: 329Reputation: 329
Our posts crossed. Yes, that should work.
Ed
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
LXer: Drag And Drop Files And Folders In Terminal To Print Their Absolute Path LXer Syndicated Linux News 0 10-11-2020 12:30 PM
LXer: VirtualBox 5.0 Brings Full Drag and Drop Between Host and Guest LXer Syndicated Linux News 0 07-10-2015 08:42 AM
moved to Gnome 3 and can't drag and drop urls anymoe pwalden Linux - Desktop 3 01-05-2012 01:52 PM
Drag and Drop Fail kajensen Linux - General 2 09-26-2003 06:08 PM
Drag and Drop Fail kajensen Linux - General 0 09-23-2003 02:47 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 02:51 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration