Hi, I decided to learn about drag'n'drop with Gtk+ and found this (very good) tutorial:
http://live.gnome.org/GnomeLove/DragNDropTutorial Now I have ported the sample to Vala (see attachment). It would work with the following changes to gtk+-2.0.vapi public struct TargetEntry { public string target; public uint flags; public uint info; } instead of public struct TargetEntry { public uint flags; public uint info; public weak string target; } and public void set (Gdk.Atom type, int format, void* data, ulong size); in class SelectionData instead of public void set (Gdk.Atom type, int format, uchar[] data); I don't know why the order in TargetEntry is wrong and how to fix it via vapi-metadata. And still the code for setting and getting the DnD data for an integer DnD target type is a little bit ugly: selection_data.set ( selection_data.target, // target type DWORD_BITS, // number of bits per 'unit' (void*) (ref integer_data), // pointer to data to be sent sizeof (long) // length of data in units ); // ... long* data = (long*) selection_data.data; print ("integer: %ld", (*data)); Has anyone else experience with Drag'n'Drop in Vala or proposals how to do it better? Regards, Frederik
/* TestDnD - dnd.vala : Simple tutorial for GTK+ Drag-N-Drop * Copyright (C) 2005 Ryan McDougall. * Vala port 2008 by Frederik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ using Gtk; using Gdk; const int BYTE_BITS = 8; const int WORD_BITS = 16; const int DWORD_BITS = 32; /** * Define a list of data types called "targets" that a destination widget will * accept. The string type is arbitrary, and negotiated between DnD widgets by * the developer. An enum or Quark can serve as the integer target id. */ enum Target { INT32, STRING, ROOTWIN } /* datatype (string), restrictions on DnD (Gtk.TargetFlags), datatype (int) */ const TargetEntry[] target_list = { { "INTEGER", 0, Target.INT32 }, { "STRING", 0, Target.STRING }, { "text/plain", 0, Target.STRING }, { "application/x-rootwindow-drop", 0, Target.ROOTWIN } }; public class Well : Label { public Well () { set_text ("[a well]"); // Make this widget a DnD destination. Gtk.drag_dest_set ( this, // widget that will accept a drop DestDefaults.MOTION // default actions for dest on DnD | DestDefaults.HIGHLIGHT, target_list, // lists of target to support DragAction.COPY // what to do with data after dropped ); // All possible destination signals this.drag_motion += on_drag_motion; this.drag_leave += on_drag_leave; this.drag_drop += on_drag_drop; this.drag_data_received += on_drag_data_received; } /** Emitted when a drag is over the destination */ private bool on_drag_motion (Well widget, DragContext context, int x, int y, uint time) { // Fancy stuff here. This signal spams the console something horrible. // print ("%s: on_drag_motion\n", widget.name); return false; } /** Emitted when a drag leaves the destination */ private void on_drag_leave (Well widget, DragContext context, uint time) { print ("%s: on_drag_leave\n", widget.name); } /** * Emitted when the user releases (drops) the selection. It should check * that the drop is over a valid part of the widget (if its a complex * widget), and itself to return true if the operation should continue. Next * choose the target type it wishes to ask the source for. Finally call * Gtk.drag_get_data which will emit "drag_data_get" on the source. */ private bool on_drag_drop (Well widget, DragContext context, int x, int y, uint time) { print ("%s: on_drag_drop\n", widget.name); // Check to see if (x, y) is a valid drop site within widget bool is_valid_drop_site = true; // If the source offers a target if (context.targets != null) { // Choose the best target type var target_type = (Atom) context.targets.nth_data (Target.INT32); // Request the data from the source. Gtk.drag_get_data ( widget, // will receive 'drag_data_received' signal context, // represents the current state of the DnD target_type, // the target type we want time // time stamp ); } else { // No target offered by source => error is_valid_drop_site = false; } return is_valid_drop_site; } /** * Emitted when the data has been received from the source. It should check * the SelectionData sent by the source, and do something with it. Finally * it needs to finish the operation by calling Gtk.drag_finish, which will * emit the "data_delete" signal if told to. */ private void on_drag_data_received (Well widget, DragContext context, int x, int y, SelectionData selection_data, uint target_type, uint time) { bool dnd_success = false; bool delete_selection_data = false; print ("%s: on_drag_data_received\n", widget.name); // Deal with what we are given from source if ((selection_data != null) && (selection_data.length >= 0)) { if (context.action == DragAction.ASK) { // Ask the user to move or copy, then set the context action. } if (context.action == DragAction.MOVE) { delete_selection_data = true; } // Check that we got the format we can use print (" Receiving "); switch (target_type) { case Target.INT32: long* data = (long*) selection_data.data; print ("integer: %ld", (*data)); dnd_success = true; break; case Target.STRING: print ("string: %s", (string) selection_data.data); dnd_success = true; break; default: print ("nothing good"); break; } print (".\n"); } if (dnd_success == false) { print ("DnD data transfer failed!\n"); } Gtk.drag_finish (context, dnd_success, delete_selection_data, time); } } public class Coins : Button { public Coins () { set_label ("[coins]"); // Make the this widget a DnD source. // Why doesn't Gtk.Label work here? Gtk.drag_source_set ( this, // widget will be drag-able ModifierType.BUTTON1_MASK, // modifier that will start a drag target_list, // lists of target to support DragAction.COPY // what to do with data after dropped ); // All possible source signals this.drag_begin += on_drag_begin; this.drag_data_get += on_drag_data_get; this.drag_data_delete += on_drag_data_delete; this.drag_end += on_drag_end; } /** * Emitted when DnD begins. This is often used to present custom graphics. */ private void on_drag_begin (Coins widget, DragContext context) { print ("%s: on_drag_begin\n", widget.name); } /** * Emitted when the destination requests data from the source via * Gtk.drag_get_data. It should attempt to provide its data in the form * requested in the target_type passed to it from the destination. If it * cannot, it should default to a "safe" type such as a string or text, even * if only to print an error. Then use Gtk.SelectionData.set to put the * source data into the allocated selection_data object, which will then be * passed to the destination. This will cause "drag_data_received" to be * emitted on the destination. Gtk.SelectionData is based on X's selection * mechanism which, via X properties, is only capable of storing data in * blocks of 8, 16, or 32 bit units. */ private void on_drag_data_get (Coins widget, DragContext context, SelectionData selection_data, uint target_type, uint time) { string string_data = "This is data from the source."; long integer_data = 42; print ("%s: on_drag_data_get\n", widget.name); print (" Sending "); switch (target_type) { // case Target.SOME_OBJECT: // Serialize the object and send as a string of bytes. // Pixbufs, (UTF-8) text, and URIs have their own convenience // setter functions case Target.INT32: print ("integer: %ld", integer_data); selection_data.set ( selection_data.target, // target type DWORD_BITS, // number of bits per 'unit' (void*) (ref integer_data), // pointer to data to be sent sizeof (long) // length of data in units ); break; case Target.STRING: print ("string: %s", string_data); selection_data.set ( selection_data.target, BYTE_BITS, string_data, string_data.size () ); break; case Target.ROOTWIN: print ("Dropped on the root window!\n"); break; default: // Default to some a safe target instead of fail. assert_not_reached (); } print (".\n"); } /** * Emitted after "drag_data_received" is handled, and Gtk.drag_finish is * called with the "delete" parameter set to true (when DnD is * DragAction.MOVE). */ private void on_drag_data_delete (Coins widget, DragContext context) { // We aren't moving or deleting anything here print ("%s: on_drag_data_delete\n", widget.name); } /** Emitted when DnD ends. This is used to clean up any leftover data. */ private void on_drag_end (Coins widget, DragContext context) { print ("%s: on_drag_end\n", widget.name); } } static int main (string[] args) { // Always start GTK+ first! Gtk.init (ref args); // Create the widgets var window = new Gtk.Window (Gtk.WindowType.TOPLEVEL); var hbox = new HBox (false, 5); var coin_source = new Coins (); var well_dest = new Well (); var directions_label = new Label ("drag a coin and drop it in the well"); // Pack the widgets window.add (hbox); hbox.add (coin_source); hbox.add (directions_label); hbox.add (well_dest); // Make the window big enough for some DnD action window.set_default_size (450, 50); // Connect the signals window.destroy += Gtk.main_quit; // Show the widgets window.show_all (); // Start the event loop Gtk.main (); return 0; }
_______________________________________________ Vala-list mailing list Vala-list@gnome.org http://mail.gnome.org/mailman/listinfo/vala-list