[Vala] Drag and Drop
- From: Frederik <scumm_fredo gmx net>
- To: vala-list gnome org
- Subject: [Vala] Drag and Drop
- Date: Thu, 30 Oct 2008 16:37:18 +0100
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;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]