/* dia-handle-layer.c * Copyright (C) 2004 Arjan Molenaar * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "dia-handle-layer.h" #include "dia-canvas-groupable.h" #include "dia-handle.h" #include #include #include #include #include "dia-canvas-i18n.h" //#define DEBUG #ifdef DEBUG # define D(msg) G_STMT_START { g_print (G_STRLOC": "); g_print msg; g_print ("\n"); } G_STMT_END #else # define D(msg) #endif #define DIA_HANDLE_EVENTS (GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK) #define MAX_HANDLE_GLUE_DIST 10 enum { ARG_LAST }; /* Stuff for handles and drawing of handles: */ /* We have the following criteria: * - Moveable on itself * - Connectable to other objects * - Is connected to another object */ enum { DIA_HANDLE_LAYER_MOVABLE = 1, DIA_HANDLE_LAYER_CONNECTABLE = 2, DIA_HANDLE_LAYER_IS_CONNECTED = 3, DIA_HANDLE_LAYER_FOCUS = 4 }; static guint32 _dia_handle_color[8] = { /* Just selected objects: */ 0x8080A0FF, /* 0 dark blue -> not moveable */ 0x80F080FF, /* 1 green -> moveable */ 0x80F080FF, /* 2 green w/ cross -> connectable */ 0xF08080FF, /* 3 red w/ cross -> connected */ /* If object id focused: */ 0x0000D0FF, /* 4 */ 0x00D000FF, /* 5 */ 0x00D000FF, /* 6 */ 0xD00000FF /* 7 */ }; static gchar *handle_image[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; #define PIXEL(p, c) \ G_STMT_START { \ *(((char*) &p) + 0) = DIA_COLOR_RED (c); \ *(((char*) &p) + 1) = DIA_COLOR_GREEN (c); \ *(((char*) &p) + 2) = DIA_COLOR_BLUE (c); \ *(((char*) &p) + 3) = DIA_COLOR_ALPHA (c); \ } G_STMT_END static void dia_handle_layer_init (DiaHandleLayer *handle_layer); static void dia_handle_layer_class_init (DiaHandleLayerClass *klass); /* GnomeCanvasItem signals */ static void dia_handle_layer_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags); static void dia_handle_layer_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width, int height); static void dia_handle_layer_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf); static GnomeCanvasItemClass *parent_class = NULL; /* * Some global initialization: */ /** * create_handle: * @handle_type: type of handle [0..7]. * * Create a handle of size handle_size * handle_size. * The handles are created in RGBA format (4 bytes per pixel). * * Return value: A new handle **/ static gchar* create_handle (int handle_type) { guint32 *handle; gint i = 0; /* index */ gint bufsize; gint handle_size; guint32 border; guint32 fill; guint32 cross; handle_size = dia_handle_size (); bufsize = handle_size * handle_size; //PIXEL (border, 0x00000090); PIXEL (border, _dia_handle_color[handle_type] & 0x888888FF); PIXEL (fill, _dia_handle_color[handle_type] & 0xFFFFFF90); PIXEL (cross, _dia_handle_color[handle_type] & 0x555555FF); handle = g_new (guint32, bufsize); g_assert (handle != NULL); /* Top and bottom row: */ for ( ; i < handle_size; i++) { handle[i] = border; handle[bufsize - 1 - i] = border; } /* second row: */ handle[i++] = border; for ( ; i < handle_size + handle_size - 1; i++) handle[i] = fill; handle[i++] = border; /* third row .. n-1 */ for (; i < bufsize - handle_size; i += handle_size) memcpy (&handle[i], &handle[handle_size], handle_size * sizeof (guint32)); /* If the handle can connect, draw a cross: */ if (handle_type & DIA_HANDLE_LAYER_CONNECTABLE) { for (i = 2; i < handle_size - 2; i++) { /* \ */ handle[i * handle_size + i] = cross; /* / */ handle[i * handle_size + handle_size - i - 1] = cross; } } return (gchar*) handle; } /** * dia_handle_layer_create_images: * * Create images for the handles. This function should be called once from * dia_init(). **/ static void dia_handle_layer_create_images (void) { int i; for (i = 0; i < 8; i++) { handle_image[i] = create_handle (i); } } /** * dia_handle_layer_free_images: * * This function is called via g_atexit(). **/ static void dia_handle_layer_free_images (void) { int i; for (i = 0; i < 8; i++) g_free (handle_image[i]); } GtkType dia_handle_layer_get_type (void) { static GtkType handle_layer_type = 0; if (!handle_layer_type) { static const GtkTypeInfo handle_layer_info = { "DiaHandleLayer", sizeof (DiaHandleLayer), sizeof (DiaHandleLayerClass), (GtkClassInitFunc) dia_handle_layer_class_init, (GtkObjectInitFunc) dia_handle_layer_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; handle_layer_type = gtk_type_unique (gnome_canvas_item_get_type (), &handle_layer_info); } return handle_layer_type; } static void dia_handle_layer_class_init (DiaHandleLayerClass *klass) { GObjectClass *object_class; GnomeCanvasItemClass *item_class; /* Create an internal list of handles. */ dia_handle_layer_create_images (); g_atexit (dia_handle_layer_free_images); object_class = (GObjectClass*) klass; item_class = (GnomeCanvasItemClass*) klass; parent_class = g_type_class_peek_parent (klass); item_class->update = dia_handle_layer_update; item_class->draw = dia_handle_layer_draw; item_class->render = dia_handle_layer_render; } static void dia_handle_layer_init (DiaHandleLayer *hl) { } /* GnomeCanvasItem signals */ static void dia_handle_layer_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags) { D((" ")); if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update) GNOME_CANVAS_ITEM_CLASS (parent_class)->update (item, affine, clip_path, flags); //DIA_HANDLE_LAYER (item)->glue_distance = MAX_HANDLE_GLUE_DIST / affine[0]; gnome_canvas_request_redraw (item->canvas, G_MININT, G_MININT, G_MAXINT, G_MAXINT); item->x1 = item->y1 = G_MININT; item->x2 = item->y2 = G_MAXINT; } static inline gint find_handle_color (DiaHandle *handle, DiaCanvasViewItem *item) { gint color; /* Figure out the handle color. */ if (handle->connectable) { if (handle->connected_to) color = DIA_HANDLE_LAYER_IS_CONNECTED; else color = DIA_HANDLE_LAYER_CONNECTABLE; } else if (handle->movable) { color = DIA_HANDLE_LAYER_MOVABLE; } else color = 0; /* FixMe: Find out if the item has a child object that has the * focus. The item should highlight if the item has DIA_COMPOSITE. */ /* Use highlight color if the object is focused: */ if (DIA_CANVAS_VIEW_ITEM_FOCUS (item) && GTK_WIDGET_HAS_FOCUS(GNOME_CANVAS_ITEM (item)->canvas)) { color |= DIA_HANDLE_LAYER_FOCUS; } return color; } typedef struct { GdkDrawable *drawable; int x, y, width, height; } DrawHelper; static gint real_draw (DiaCanvasViewItem *item, gpointer data) { DrawHelper *buf = data; DiaCanvasItem *diaitem = item->item; gdouble wx, wy, cx, cy; GList *l; //gdouble handle_r; gint handle_size; gint color; //D((G_GNUC_PRETTY_FUNCTION)); /* Groups should draw their children... but invisible items and * unselected leafs should not be drawn. */ if (!DIA_CANVAS_VIEW_ITEM_SELECT (item) || !DIA_CANVAS_VIEW_ITEM_VISIBLE (item)) return TRUE; handle_size = dia_handle_size (); /* First draw all handles: */ for (l = diaitem->handles; l != NULL; l = l->next) { DiaHandle *handle = l->data; color = find_handle_color (handle, item); dia_handle_get_pos_w (handle, &wx, &wy); gnome_canvas_w2c_d (GNOME_CANVAS_ITEM (item)->canvas, wx, wy, &cx, &cy); gdk_draw_rgb_32_image (buf->drawable, item->gc, cx - buf->x + 1, cy - buf->y + 1, handle_size, handle_size, GDK_RGB_DITHER_NORMAL, handle_image[color], handle_size * sizeof (guint32)); } return TRUE; } static void dia_handle_layer_draw (GnomeCanvasItem *hl, GdkDrawable *drawable, int x, int y, int width, int height) { DiaCanvasViewItem *root; DrawHelper helper; gint handle_r = (dia_handle_size() + 1) / 2; D((G_GNUC_PRETTY_FUNCTION)); root = DIA_CANVAS_VIEW (hl->canvas)->root_item; if (!root) return; helper.drawable = drawable; helper.x = x + handle_r; helper.y = y + handle_r; helper.width = width; helper.height = height; dia_canvas_view_item_foreach (root, real_draw, &helper); } typedef struct { GnomeCanvasBuf *buf; gdouble *affine; } RenderHelper; static gint real_render (DiaCanvasViewItem *item, gpointer data) { RenderHelper *helper = data; GnomeCanvasBuf *buf = helper->buf; gdouble affine[6]; DiaCanvasItem *diaitem = item->item; gint i; GList *l; gdouble handle_r; gint handle_size; gint color; /* Groups should draw their children... but invisible items and * unselected leafs should not be drawn. */ if (!DIA_CANVAS_VIEW_ITEM_SELECT (item) || !DIA_CANVAS_VIEW_ITEM_VISIBLE (item)) return TRUE; handle_size = dia_handle_size (); handle_r = (handle_size) / 2; /* First draw all handles: */ for (l = diaitem->handles, i = 0; (i < item->n_handle_pos * 2) && (l != NULL); l = l->next, i += 2) { DiaHandle *handle = l->data; // do not display non-visible handles if (!handle->visible) continue; color = find_handle_color (handle, item); art_affine_translate (affine, item->handle_pos[i] - handle_r, item->handle_pos[i + 1] - handle_r); art_affine_multiply (affine, affine, helper->affine); art_rgb_rgba_affine (buf->buf, buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1, buf->buf_rowstride, handle_image[color], handle_size, handle_size, handle_size * sizeof (guint32), affine, ART_FILTER_NEAREST, NULL); } return TRUE; } /** * dia_handle_layer_render: * @item: * @buf: * * **/ static void dia_handle_layer_render (GnomeCanvasItem *hl, GnomeCanvasBuf *buf) { RenderHelper helper; DiaCanvasViewItem *root; gdouble affine[6]; D((G_GNUC_PRETTY_FUNCTION)); root = DIA_CANVAS_VIEW (hl->canvas)->root_item; if (!root) return; gnome_canvas_buf_ensure_buf (buf); gnome_canvas_item_i2w_affine (hl, affine); helper.buf = buf; helper.affine = affine; dia_canvas_view_item_foreach (root, real_render, &helper); } /** * dia_handle_layer_update_handles: * @layer: * @item: * * Redraw handles if nessesary. The old handle positions are stored * in the #DiaCanvasViewItem to ensure proper redrawing. **/ void dia_handle_layer_update_handles (DiaHandleLayer *layer, DiaCanvasViewItem *item) { DiaCanvasItem *diaitem = item->item; int i; GList *l; /* Create a completely new list of handle positions on the * DiaCanvasViewItem if the lengths of the lists differ. */ if (item->n_handle_pos != g_list_length (diaitem->handles)) { /* Request redraw for the old handle positions: */ for (i = 0; i < item->n_handle_pos * 2; i += 2) { dia_handle_layer_request_redraw (layer, item->handle_pos[i], item->handle_pos[i+1]); } /* Now reconstruct the new (old) handle_coordinates: */ item->n_handle_pos = g_list_length (diaitem->handles); item->handle_pos = g_realloc (item->handle_pos, item->n_handle_pos * sizeof (gdouble) * 2); for (i = 0, l = diaitem->handles; l != NULL; i += 2, l = l->next) { dia_handle_layer_get_pos_c (layer, DIA_HANDLE (l->data), &item->handle_pos[i], &item->handle_pos[i+1]); dia_handle_layer_request_redraw (layer, item->handle_pos[i], item->handle_pos[i+1]); } } else { /* Update the position matrix on the canvasviewitem */ for (i = 0, l = diaitem->handles; l != NULL; i += 2, l = l->next) { dia_handle_layer_request_redraw (layer, item->handle_pos[i], item->handle_pos[i+1]); dia_handle_layer_get_pos_c (layer, DIA_HANDLE (l->data), &item->handle_pos[i], &item->handle_pos[i+1]); dia_handle_layer_request_redraw (layer, item->handle_pos[i], item->handle_pos[i+1]); } } } /** * dia_handle_layer_get_pos_c: * @layer: * @handle: * @x: * @y: * * Given a handle, return the position of that handle in canvas coordinates. **/ void dia_handle_layer_get_pos_c (DiaHandleLayer *layer, DiaHandle *handle, gint *x, gint *y) { GnomeCanvasItem *gitem = GNOME_CANVAS_ITEM (layer)->canvas->root; gdouble wx, wy; dia_handle_get_pos_w (handle, &wx, &wy); gnome_canvas_w2c (gitem->canvas, wx, wy, x, y); } /** * dia_handle_layer_request_redraw: * @layer: * @x: Position of the handle, in canvas coordinates * @y: * * Ask the canvas to do a redraw of the a handle. Only the handles @x and @y * position need to be given (in canvas coordinates). **/ void dia_handle_layer_request_redraw (DiaHandleLayer *layer, gint x, gint y) { ArtDRect r1, r2; ArtIRect ir; gdouble affine[6]; gdouble r = (((gdouble) dia_handle_size ()) / 2.0) + 0.5; g_return_if_fail (DIA_IS_HANDLE_LAYER (layer)); gnome_canvas_item_i2w_affine ((GnomeCanvasItem*) layer, affine); r1.x0 = x - r; r1.y0 = y - r; r1.x1 = x + r; r1.y1 = y + r; /* x1 = (gint) (x - r); y1 = (gint) (y - r); x2 = (gint) (x + r); y2 = (gint) (y + r); */ art_drect_affine_transform (&r2, &r1, affine); art_drect_to_irect (&ir, &r2); gnome_canvas_request_redraw (GNOME_CANVAS_ITEM (layer)->canvas, ir.x0, ir.y0, ir.x1, ir.y1); //x1, y1, x2, y2); } /** * dia_handle_layer_request_redraw_handle: * @layer: * @handle: * * Do a redraw for @handle. **/ void dia_handle_layer_request_redraw_handle (DiaHandleLayer *layer, DiaHandle *handle) { gint x, y; /* TODO: try doing a lookup in the handle's handle_pos list if possible */ dia_handle_layer_get_pos_c (layer, handle, &x, &y); dia_handle_layer_request_redraw (layer, x, y); } /** * dia_handle_layer_grab_handle: * @layer: * @handle: * * DEPRICATED * * Grab a handle. The handle should be grabbed suring a button-press event, * since the handle-layer will grab the widgets events. **/ void dia_handle_layer_grab_handle (DiaHandleLayer *layer, DiaHandle *handle) { g_warning(G_STRLOC": The function dia_handle_layer_grab_handle() is depricated"); }