Trabajar con los objetos

GnomeCanvas ofrece diversas funciones para manipular la posición de los objetos en el canvas.

Las tranformaciones geométricas necesarias para los objetos se hacen usando matrices affine. Estas matrices definen valores de escala, rotación y shearing. Las matrices se pueden manipular fácilmente usando las funciones de libart_lgpl. En la documentación de libart_lgpl se encuentra una descripción detallada sobre estas matrices.

Manejando objetos

Mostrar y ocultar

Para mostrar y ocultar disponemos de gnome_canvas_item_show y gnome_canvas_item_hide.

void gnome_canvas_item_show(item); 
GnomeCanvasItem*  item;
void gnome_canvas_item_hide(item); 
GnomeCanvasItem*  item;

Los grupos formados por GnomeCanvasGroup también pueden ocultarse o mostrarse usando estas funciones, ya que GnomeCanvasGroup deriva de GnomeCanvasItem. Cuando un grupo se oculta, todos los objetos que hay dentro de él también son ocultados.

Mover

Para poder mover objetos sin cambiar sus parámetros se puede usar gnome_canvas_item_move

void gnome_canvas_item_move(item,  
 dx,  
 dy); 
GnomeCanvasItem*  item;
double  dx;
double  dy;

Los valores de dx y dy indican la cantidad de espacio que tiene que moverse desde la posición en la que estén en el momento de llamar a la función. Esta función es especialmente útil cuando no es conocido el tipo de objeto que se quiere mover, ya que no todos usan los mismos parámetros para ubicarse en el canvas.

Cuando esta función se aplica sobre un grupo, todos los objetos de ese grupo son movidos por igual.

Transformar

Para hacer transformaciones sobre los objetos (como rotar o escalar) es necesario crear una matriz affine y aplicarla sobre el objeto.

Cuando la matriz esté lista, se deberá llamar a gnome_canvas_item_affine_relative o a gnome_canvas_item_affine_absolute para aplicarla.

void gnome_canvas_item_affine_relative(affine); 
const double  affine[6];
void gnome_canvas_item_affine_absolute(affine); 
const double  affine[6];

La diferencia entre estas dos funciones es que gnome_canvas_item_affine_absolute elimina cualquier otra transformación que se haya aplicado anteriormente sobre ese mismo objeto, mientras que gnome_canvas_item_affine_relative combina la matriz enviada como argumento con la que ya exista.

Reagrupar

Cambiar el grupo al que pertenece un elemento es posible con gnome_canvas_item_reparent. Crear grupos facilita trabajar con varios objetos a la vez como si fueran uno solo, lo que permite ocultarlos, moverlos o incluso destruirlos con una sola llamada.

void gnome_canvas_item_reparent(item,  
 new_group); 
GnomeCanvasItem*  item;
GnomeCanvasGroup*  new_group;

Recibiendo eventos

Los objetos que se encuentren en el canvas reciben eventos del mismo modo que lo hace un widget, es decir, a través de señales. Cualquier objeto o grupo puede recibir eventos del teclado o del ratón.

La propagación de eventos se hace a través de grupos. Primero el evento se envía al grupo principal (obtenido con gnome_canvas_root). Éste lo propaga por los objetos que le pertenezcan. Si el evento llega a otro grupo, se vuelve a propagar entre los objetos de ese nuevo grupo.

Es posible conectar una señal a un grupo, de modo que la función conectada (pasada como tercer argumento de g_signal_connect) será llamada por cada evento que ocurra en alguno de los objetos pertenecientes a ese grupo.

Señal event

Todos los eventos que pueden recibir los objetos se mandan por la señal event

int item_event(item,  
 event,  
 data); 
GnomeCanvasItem*  item;
GdkEvent*  event;
gpointer  data;

La estructura event se rellena con la misma información que recibe el canvas, con las coordenadas ya transformadas en unidades del canvas, teniendo en cuenta el zoom y el desplazamiento de las barras (si las hubiera).

Capturando eventos

Un objeto sólo recibe eventos del cursor cuando éste está sobre el objeto. Para cambiar este comportamiento y hacer que el objeto reciba los eventos del cursor aunque no esté sobre el objeto, se puede usar gnome_canvas_item_grab.

int gnome_canvas_item_grab(item,  
 event_mask,  
 cursor,  
 etime); 
GnomeCanvasItem*  item;
unsigned int  event_mask;
GdkCursor*  cursor;
guint32  etime;

Los argumentos event_mask y etime son los mismos que recibe gdk_pointer_grab. El argumento cursor es el cursor que se motrará mientras el objeto tenga el control sobre los eventos del cursor. El valor devuelto es el mismo que gdk_pointer_grab, que será GDK_GRAB_SUCCESS en caso de éxito.

Cuando se quiera liberar el cursor se debe llamar a gnome_canvas_item_ungrab, que recibe como argumentos el objeto que tiene el control de los eventos del cursor y el valor pasado en el argumento etime del gnome_canvas_item_grab.

void gnome_canvas_item_ungrab(item,  
 etime); 
GnomeCanvasItem*  item;
guint32  etime;

El canvas también ofrece la posibilidad de enviar los eventos que reciba del teclado a un determinado objeto. Para ello es necesario llamar a gnome_canvas_item_grab_focus. Al contrario que gnome_canvas_item_grab, ésta sólo redirige los eventos que le lleguen al widget, no todos los del sistema.

void gnome_canvas_item_grab_focus(item); 
GnomeCanvasItem*  item;

Ejemplo con señales

El siguiente ejemplo muestra cómo interpretar los eventos del ratón de modo que se puedan mover los objetos que hay en el canvas, arrastrándolos manteniendo pulsado cualquier botón del ratón.

Ejemplo 6. Capturando eventos


/*
 * Este ejemplo muestra cómo conectar una función para que reciba los eventos
 * que llegan de los objetos del canvas.
 *
 * La función mueve el objeto cuando se pulsa con el botón izquierdo y se
 * mueve el cursor.
 */

#include <gnome.h>

/*
 * Esta función hace que el objeto se arrastre si se pulsa el botón izquierdo
 * del ratón y se mueve. Es decir, se arrastra el objeto :-)
 */
int
item_event(GnomeCanvasItem* item, GdkEvent* event, gpointer data)
{
        static gboolean dragging = FALSE;
        static gdouble x;
        static gdouble y;
        gdouble new_x, new_y;
        GdkCursor *cursor;

        new_x = event->button.x;
        new_y = event->button.y;

        switch(event->type)
        {
                case GDK_BUTTON_PRESS:
                        dragging = TRUE;
                        x = new_x;
                        y = new_y;

                        cursor = gdk_cursor_new(GDK_FLEUR);
                        gnome_canvas_item_grab(item,
                                        GDK_POINTER_MOTION_MASK |
                                        GDK_BUTTON_RELEASE_MASK |
                                        GDK_BUTTON_PRESS_MASK,
                                        cursor, GDK_CURRENT_TIME);
                        gdk_cursor_destroy(cursor);
                        break;

                case GDK_MOTION_NOTIFY:
                        if(dragging)
                        {
                                gnome_canvas_item_move(item, new_x - x, new_y - y);
                                x = new_x;
                                y = new_y;
                        }
                        break;

                case GDK_BUTTON_RELEASE:
                        if(dragging)
                        {
                                dragging = FALSE;
                                gnome_canvas_item_ungrab(item, GDK_CURRENT_TIME);
                        }
                        break;

                default:
                        break;
        }
}

/*
 * Para cada objeto en el canvas, se conectará la señal "event" a la función item_event
 * para que se mueva junto con el cursor cuando se pulse con el ratón
 */
void
set_signals(GnomeCanvasItem* item)
{
        g_signal_connect(G_OBJECT(item), "event", G_CALLBACK(item_event), 0);
}

int
main(int argc, char** argv)
{
        GtkWidget *window;
        GtkWidget *canvas;
        GnomeCanvasGroup *root;

        /* inicializamos las librerías */
        gnome_init("events-canvas", "0.1", argc, argv);

        /* crear una ventana que contenga al canvas */
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
        g_signal_connect(G_OBJECT(window), "destroy", gtk_main_quit, 0);

        /* canvas en modo RGB */
        canvas = gnome_canvas_new_aa();
        gtk_container_add(GTK_CONTAINER(window), canvas);

        root = gnome_canvas_root(GNOME_CANVAS(canvas));

        /* poner un rectángulo */
        set_signals(gnome_canvas_item_new(root,
                        gnome_canvas_rect_get_type(),
                        "x1", 0.0,
                        "y1", 0.0,
                        "x2", 100.0,
                        "y2", 100.0,
                        "fill_color_rgba", 0xffff0077,
                        "outline_color_rgba", 0x0000ffff,
                        "width_units", 1.0,
                        NULL));

        /* poner un círculo */
        set_signals(gnome_canvas_item_new(root,
                        gnome_canvas_ellipse_get_type(),
                        "x1", 20.0,
                        "y1", 25.0,
                        "x2", 80.0,
                        "y2", 75.0,
                        "fill_color_rgba", 0x00ff0077,
                        "outline_color_rgba", 0xff00ffff,
                        "width_units", 1.5,
                        NULL));

        /* mostrar los widgets creados y entrar en el bucle gtk_main */
        gtk_widget_show_all(window);
        gtk_main();
}