Un objeto propio

Este objeto, MyItem, deriva de GnomeCanvasShape. Sólo implementa el método update, y deja todos los demás a GnomeCanvasShape.

Además, en el ejemplo se usa un widget GtkScale para poder cambiar el zoom del canvas.

Ejemplo 13. MyItem


#include <gnome.h>

/*
 * Definición de MyItem
 */

typedef struct _MyItem MyItem;
typedef struct _MyItemClass MyItemClass;

struct _MyItem {
        GnomeCanvasShape shape;

        gdouble x0;
        gdouble y0;

        gdouble x1;
        gdouble y1;
};


struct _MyItemClass {
        GnomeCanvasShapeClass parent_class;
};

#define MY_ITEM_TYPE (my_item_get_type())
#define MY_ITEM(obj) (GTK_CHECK_CAST ((obj), MY_ITEM_TYPE, MyItem))


/*
 * "update" para calcular la ruta que se enviará a GnomeCanvasShape
 */
void my_item_update (GnomeCanvasItem *item, gdouble *affine,
                ArtSVP *clip_path, gint flags);

/*
 * Guardar la dirección de la clase padre de MyItem para llamar a su update
 */
static GnomeCanvasItemClass *my_item_parent_class;

/*
 * Inicializar el objeto. &Eacute;sta es llamada cada vez que se crea un objeto
 */
void
my_item_init(MyItem* item)
{
        item->x0 = 100.0;
        item->y0 = 100.0;
        item->x1 = 200.0;
        item->y1 = 200.0;
}

/*
 * Inicializar la clase. &Eacute;sta es llamada sólo una vez, cuando se crea el primer
 * objeto y se crea la clase
 */
void
my_item_class_init(MyItemClass* class)
{
        GnomeCanvasItemClass *item_class;

        item_class = (GnomeCanvasItemClass *) class;
        item_class->update = my_item_update;

        my_item_parent_class = g_type_class_peek_parent (class);
}

/*
 * Usada para crear la clase y poder enviar un GType a gnome_canvas_item_new
 */
GType
my_item_get_type ()
{
        static GType item_type = 0;

        if(item_type == 0)
        {
                static const GTypeInfo object_info = {
                        sizeof (MyItemClass),
                        (GBaseInitFunc) NULL,
                        (GBaseFinalizeFunc) NULL,
                        (GClassInitFunc) my_item_class_init,
                        (GClassFinalizeFunc) NULL,
                        NULL,
                        sizeof (MyItem),
                        0,
                        (GInstanceInitFunc) my_item_init,
                        NULL
                };

                item_type = g_type_register_static (GNOME_TYPE_CANVAS_SHAPE,
                                "MyItem", &object_info, 0);
        }

        return item_type;
}

void my_item_update (GnomeCanvasItem *item, gdouble *affine,
                ArtSVP *clip_path, gint flags)
{
        GnomeCanvasPathDef* path_def;
        MyItem* my_item;
        gdouble cx, cy;

        /*
         * crear una nueva ruta
         */

        path_def = gnome_canvas_path_def_new();
        my_item = MY_ITEM(item);

        cx = (my_item->x1 - my_item->x0) * 0.25;
        cy = (my_item->y1 - my_item->y0) * 0.25;

        gnome_canvas_path_def_moveto(path_def, my_item->x0, my_item->y0);
        gnome_canvas_path_def_lineto(path_def, my_item->x0 + cx, my_item->y0 - cy );
        gnome_canvas_path_def_lineto(path_def, my_item->x1, my_item->y1);
        gnome_canvas_path_def_lineto(path_def, my_item->x1 - cx, my_item->y1 + cy);
        gnome_canvas_path_def_lineto(path_def, my_item->x0, my_item->y0);

        /* mandar la ruta a GnomeCanvasShape */
        gnome_canvas_path_def_closepath(path_def);
        gnome_canvas_shape_set_path_def (GNOME_CANVAS_SHAPE (item), path_def);
        gnome_canvas_path_def_unref(path_def);

        if (my_item_parent_class->update)
                (* my_item_parent_class->update)(item, affine, clip_path, flags);
}


gboolean
button_press_event(GtkWidget* canvas, GdkEventButton* event, MyItem* item)
{
        gdouble x, y;
        gnome_canvas_window_to_world(GNOME_CANVAS_ITEM(item)->canvas, event->x, event->y, &x, &y);

        switch(event->button)
        {
                case 1: /* botón izquierdo */
                        item->x0 = x;
                        item->y0 = y;
                        gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(item));
                        break;

                case 3: /* botón derecho */
                        item->x1 = x;
                        item->y1 = y;
                        gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(item));
                        break;

                default:
                        break;
        }

        /*
         * Devolver TRUE finaliza la propagación del evento.
         * FALSE lo sigue propagando
         */
        return FALSE;
}

void
on_zoom_changed(GtkAdjustment* adjs, GnomeCanvas* canvas)
{
        gnome_canvas_set_pixels_per_unit(canvas, adjs->value);
}

int
main(int argc, char** argv)
{
        GtkWidget *window;
        GtkWidget *canvas;
        GtkWidget *scroll_window;
        GtkWidget *vbox;
        GtkWidget *scrool_zoom;
        GnomeCanvasGroup *root;
        GnomeCanvasItem* item;
        GtkAdjustment *adjustment;

        /* inicializamos las librerías */
        gnome_init("my_item", "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);

        vbox = gtk_vbox_new(FALSE, 1);
        gtk_container_add(GTK_CONTAINER(window), vbox);

        /* Usar una barra de desplazamiento para el zoom */
        adjustment = GTK_ADJUSTMENT(gtk_adjustment_new(1.0, 0.25, 4.0, 0.1, 0.5, 1.0));
        scrool_zoom = gtk_hscale_new(adjustment);
        gtk_scale_set_draw_value(GTK_SCALE(scrool_zoom), TRUE);
        gtk_box_pack_start(GTK_BOX(vbox), scrool_zoom, FALSE, TRUE, 1);

        /* Usar GtkScrolledWindow para disponer de barras de desplazamiento
         * en el canvas */
        scroll_window = gtk_scrolled_window_new(NULL, NULL);
        gtk_box_pack_end(GTK_BOX(vbox), scroll_window, TRUE, TRUE, 1);

        /* canvas en modo RGB */
        canvas = gnome_canvas_new_aa();
        gtk_container_add(GTK_CONTAINER(scroll_window), canvas);
        gnome_canvas_set_scroll_region(GNOME_CANVAS(canvas), 0, 0, 1000, 1000);

        root = gnome_canvas_root(GNOME_CANVAS(canvas));

        /* poner un círculo, con fondo verde y borde azul */
        item = gnome_canvas_item_new(root,
                        my_item_get_type(),
                        "fill_color", "blue",
                        "outline_color", "black",
                        "width_units", 2.0,
                        NULL);

        /* recibir los eventos de pulsación con el ratón para cambiar el item y la señal
         * de cambio de valor para el zoom*/
        g_signal_connect(G_OBJECT(window), "button-press-event", G_CALLBACK(button_press_event), item);
        g_signal_connect(G_OBJECT(adjustment), "value_changed", G_CALLBACK(on_zoom_changed), canvas);

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

        return 0;
}