Drag and Drop

Introducción

GTK+ proporciona una serie de funciones para facilitar el intercomunicaicón entre procesos usando drag-and-drop. Los protocolos que reconoce GTK+ son Xdnd y el de Motif.

Para usar DND primero hay que definir qué widgets se usaran como fuentes y/o como destino. Estos widgets tienen que tener asociada una ventana real, lo cual se puede comprobar con la macro GTK_WIDGET_NO_WINDOW(widget))

El envío y la recepción de datos se hace a través de señales. El widget fuente debe controlar la señal drag_data_get, que será usada para pedir los datos a la fuente. Mientras, el widget destino usará drag_data_received para saber cuándo se le están enviado datos. Hay otras señales, que permiten saber cuándo el DND comienza y cuándo acaba.

Definiendo el widget destino

Para que un widget pueda recibir eventos DND basta con invocar a la función gtk_drag_dest_set

void gtk_drag_dest_set(widget,  
 flags,  
 targets,  
 n_targets,  
 actions); 
GtkWidget*  widget;
GtkDestDefaults  flags;
const GtkTargetEntry*  targets;
gint  n_targets;
GdkDragAction  actions;

Con esta función, el widget podrá recibir la señal drag_data_received, que le indicará que se han soltado elementos sobre él.

void "drag_data_received"(widget,  
 dc,  
 x,  
 y,  
 selection_data,  
 info,  
 t,  
 data); 
GtkWidget*  widget;
GdkDragContext*  dc;
gint  x;
gint  y;
GtkSelectionData*  selection_data;
guint  info;
guint  t;
gpointer  data;

En gtk_drag_dest_set, el argumento widget se refiere al widget que recogerá las señales de DND.

flags indica las acciones a tomar cuando un elemento se suelte sobre el widget. Los valores que puede tomar son

Tabla 1. Acciones que se pueden tomar

GTK_DEST_DEFAULT_MOTIONEsto hará que cuando el objeto pase sobre el widget, GTK+ comprobará si se puede soltar sobre él. Si es posible, se llamará a gdk_drag_status.
GTK_DEST_DEFAULT_HIGHLIGHTCon esto, GTK+ cambiará el estado del widget a highlight cuando un objeto esté sobre él y este objeto pueda soltarse sobre el widget.
GTK_DEST_DEFAULT_DROPÉste hará que cuando un objeto se suelte (es decir, se libere el botón del ratón) se compruebe si el objeto puede ser recibido por el widget. Si es así, GTK+ llamará a gtk_drag_get_data para obtener los datos del widget fuente.
GTK_DEST_DEFAULT_ALL>Éste es una combinación de los tres anteriores.

targets y n_targets se usan para especificar qué objetivos (targets) puede recibir. targets es una array de GtkTargetEntry, y n_targets define el número de entradas que tiene ese array. GtkTargetEntry se define como

 struct GtkTargetEntry {
  gchar *target;
  guint  flags;
  guint  info;
 };

El miembro target define el nombre del objetivo. Se pueden usar nombres estándares, como text/uri-list o text/plain, y también usar nombre propios específicos para la aplicación, como GIMP_IMAGE o GIMP_LAYER de gimp.

El miembro flags puede tomar cualquier de estos tres valores.

Tabla 2. Posibles valores para flags

0Se puede recibir objetos de cualquier lugar
GTK_TARGET_SAME_APPSólo se puede recibir objetos desde otros widgets de la propia aplicación
GTK_TARGET_SAME_WIDGETSólo se reciben objetos del mismo widget.

El último miembro, actions, define qué acciones reconoce esta fuente. Las acciones disponibles son

Tabla 3. Acciones cuando se suelta

GDK_ACTION_DEFAULTAcción por defecto
GDK_ACTION_COPYCopiar los datos
GDK_ACTION_MOVEMoverlos. Es decir, primero copiarlos de la fuente y después borrarlos de ella.
GDK_ACTION_LINKHacer un enlace. Esto es útil sólo si la fuente y el destino entienden lo mismo por enlazar.
GDK_ACTION_PRIVATEUn acción especial que la fuente no conocerá.
GDK_ACTION_ASKPreguntar al usuario qué quiere hacer

Definiendo el widget fuente

Para definir un widget y usarlo como fuente usamos la función gtk_drag_source_set

void gtk_drag_source_set(widget,  
 start_button_mask,  
 targets,  
 n_targets,  
 actions); 
GtkWidget*  widget;
GdkModifierType  start_button_mask;
const GtkTargetEntry*  targets;
gint  n_targets;
GdkDragAction  actions;

Los argumentos widget, targets, n_targets y actions son los mismo que los usados en gtk_drag_dest_set.

El argumento start_button_mask define qué botones será válidos para comenzar a arrastrar objetos. Algunos de los valores típicos para este argumento son GDK_BUTTON1_MASK, GDK_BUTTON2_MASK, etc.

Señales

Existen seis señales relacionadas con el DND para el widget fuente, aunque sólo es necesario implementar drag_data_get para poder enviar datos.

drag_data_get

Ésta es la señal principal, a través de la cual se envían datos hacia el cliente.

void "drag_data_get"(widget,  
 dc,  
 selection_data,  
 info,  
 t,  
 data); 
GtkWidget*  widget;
GdkDragContext*  dc;
GtkSelectionData*  selection_data;
guint  info;
guint  t;
gpointer  data;

En esta señal, widget es el wiget del que se quieren extraer datos. dc contiene información sobre el proceso de DND. selection_data se usará para guardar la información que se enviará al widget destino. info es el tipo de objetivo que hemos definido en el miembro info de GtkTargetEntry.

dc es una estructura GdkDragContext, que se define como

 struct GdkDragContext {

  GObject parent_instance;


  GdkDragProtocol protocol;

  gboolean is_source;

  GdkWindow *source_window;
  GdkWindow *dest_window;

  GList *targets;
  GdkDragAction actions;
  GdkDragAction suggested_action;
  GdkDragAction action;

  guint32 start_time;

 };

Y selection_data una estructura GtkSelectionData. Vea el capítulo de selecciones para saber más sobre ellas.

drag_data_delete

Cuando un elemento se suelta sobre el widget destino y la acción realizada es GDK_ACTION_MOVE (por ejemplo), esta señal se enviará para notificar a la fuente que elimine el objeto que se ha arrastrado.

void* "drag_data_delete"(widget,  
 dc,  
 data); 
GtkWidget*  widget;
GdkDragContext*  dc;
gpointer  data;
drag_begin

Esta señal se envía cuando se inicia el arrastre

void "drag_begin"(widget,  
 dc,  
 data); 
GtkWidget*  widget;
GdkDragContext*  dc;
gpointer  data;
drag_motion

Esta otra se envía cada vez que el cursor se mueve arrastrando un objeto

gboolean "drag_motion"(widget,  
 dc,  
 x,  
 y,  
 t,  
 data); 
GtkWidget*  widget;
GdkDragContext*  dc;
gint  x;
gint  y;
guint  t;
gpointer  data;
drag_drop

Esta se enviará cuando el objeto se suelte sobre el widget destino

gboolean "drag_drop"(widget,  
 dc,  
 x,  
 y,  
 t,  
 data); 
GtkWidget*  widget;
GdkDragContext*  dc;
gint  x;
gint  y;
guint  t;
gpointer  data;
drag_end

Y esta última se enviará cuando el proceso del DND haya acabado.

void "drag_end"(widget,  
 dc,  
 data); 
GtkWidget*  widget;
GdkDragContext*  dc;
gpointer  data;