diff options
Diffstat (limited to 'eel/eel-canvas-rect-ellipse.c')
-rw-r--r-- | eel/eel-canvas-rect-ellipse.c | 1550 |
1 files changed, 1550 insertions, 0 deletions
diff --git a/eel/eel-canvas-rect-ellipse.c b/eel/eel-canvas-rect-ellipse.c new file mode 100644 index 00000000..9481a7fd --- /dev/null +++ b/eel/eel-canvas-rect-ellipse.c @@ -0,0 +1,1550 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation + * All rights reserved. + * + * This file is part of the Mate Library. + * + * The Mate 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. + * + * The Mate 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 the Mate Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + @NOTATION@ + */ +/* Rectangle and ellipse item types for EelCanvas widget + * + * EelCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is + * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties. + * + * + * Author: Federico Mena <[email protected]> + */ + +#include <config.h> +#include <math.h> +#include "eel-canvas-rect-ellipse.h" +#include "eel-canvas-util.h" +#include <string.h> + +#ifdef HAVE_RENDER +#include <gdk/gdkx.h> +#include <X11/extensions/Xrender.h> +#endif + +/* Base class for rectangle and ellipse item types */ + +#define noVERBOSE + +enum +{ + PROP_0, + PROP_X1, + PROP_Y1, + PROP_X2, + PROP_Y2, + PROP_FILL_COLOR, + PROP_FILL_COLOR_GDK, + PROP_FILL_COLOR_RGBA, + PROP_OUTLINE_COLOR, + PROP_OUTLINE_COLOR_GDK, + PROP_OUTLINE_COLOR_RGBA, + PROP_FILL_STIPPLE, + PROP_OUTLINE_STIPPLE, + PROP_WIDTH_PIXELS, + PROP_WIDTH_UNITS +}; + + +static void eel_canvas_re_class_init (EelCanvasREClass *klass); +static void eel_canvas_re_init (EelCanvasRE *re); +static void eel_canvas_re_destroy (GtkObject *object); +static void eel_canvas_re_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void eel_canvas_re_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); + +static void eel_canvas_re_update_shared (EelCanvasItem *item, + double i2w_dx, double i2w_dy, int flags); +static void eel_canvas_re_realize (EelCanvasItem *item); +static void eel_canvas_re_unrealize (EelCanvasItem *item); +static void eel_canvas_re_bounds (EelCanvasItem *item, double *x1, double *y1, double *x2, double *y2); +static void eel_canvas_re_translate (EelCanvasItem *item, double dx, double dy); +static void eel_canvas_rect_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags); +static void eel_canvas_ellipse_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags); + +typedef struct +{ + /*< public >*/ + int x0, y0, x1, y1; +} Rect; + +static Rect make_rect (int x0, int y0, int x1, int y1); +static void diff_rects (Rect r1, Rect r2, int *count, Rect result[4]); + +static EelCanvasItemClass *re_parent_class; +static EelCanvasREClass *rect_parent_class; + + +GType +eel_canvas_re_get_type (void) +{ + static GType re_type = 0; + + if (!re_type) + { + GTypeInfo re_info = + { + sizeof (EelCanvasREClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) eel_canvas_re_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (EelCanvasRE), + 0, /* n_preallocs */ + (GInstanceInitFunc) eel_canvas_re_init + }; + + re_type = g_type_register_static (eel_canvas_item_get_type (), + "EelCanvasRE", + &re_info, + 0); + } + + return re_type; +} + +static void +eel_canvas_re_class_init (EelCanvasREClass *klass) +{ + GObjectClass *gobject_class; + GtkObjectClass *object_class; + EelCanvasItemClass *item_class; + + gobject_class = (GObjectClass *) klass; + object_class = (GtkObjectClass *) klass; + item_class = (EelCanvasItemClass *) klass; + + re_parent_class = g_type_class_peek_parent (klass); + + gobject_class->set_property = eel_canvas_re_set_property; + gobject_class->get_property = eel_canvas_re_get_property; + + g_object_class_install_property + (gobject_class, + PROP_X1, + g_param_spec_double ("x1", NULL, NULL, + -G_MAXDOUBLE, G_MAXDOUBLE, 0, + G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_Y1, + g_param_spec_double ("y1", NULL, NULL, + -G_MAXDOUBLE, G_MAXDOUBLE, 0, + G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_X2, + g_param_spec_double ("x2", NULL, NULL, + -G_MAXDOUBLE, G_MAXDOUBLE, 0, + G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_Y2, + g_param_spec_double ("y2", NULL, NULL, + -G_MAXDOUBLE, G_MAXDOUBLE, 0, + G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_FILL_COLOR, + g_param_spec_string ("fill-color", NULL, NULL, + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_FILL_COLOR_GDK, + g_param_spec_boxed ("fill-color-gdk", NULL, NULL, + GDK_TYPE_COLOR, + G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_FILL_COLOR_RGBA, + g_param_spec_uint ("fill-color-rgba", NULL, NULL, + 0, G_MAXUINT, 0, + G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_FILL_STIPPLE, + g_param_spec_object ("fill-stipple", NULL, NULL, + GDK_TYPE_DRAWABLE, + G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_OUTLINE_COLOR, + g_param_spec_string ("outline-color", NULL, NULL, + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_OUTLINE_COLOR_GDK, + g_param_spec_boxed ("outline-color-gdk", NULL, NULL, + GDK_TYPE_COLOR, + G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_OUTLINE_COLOR_RGBA, + g_param_spec_uint ("outline-color-rgba", NULL, NULL, + 0, G_MAXUINT, 0, + G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_OUTLINE_STIPPLE, + g_param_spec_object ("outline-stipple", NULL, NULL, + GDK_TYPE_DRAWABLE, + G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_WIDTH_PIXELS, + g_param_spec_uint ("width-pixels", NULL, NULL, + 0, G_MAXUINT, 0, + G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_WIDTH_UNITS, + g_param_spec_double ("width-units", NULL, NULL, + 0.0, G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + object_class->destroy = eel_canvas_re_destroy; + + item_class->realize = eel_canvas_re_realize; + item_class->unrealize = eel_canvas_re_unrealize; + item_class->translate = eel_canvas_re_translate; + item_class->bounds = eel_canvas_re_bounds; +} + +static void +eel_canvas_re_init (EelCanvasRE *re) +{ + re->x1 = 0.0; + re->y1 = 0.0; + re->x2 = 0.0; + re->y2 = 0.0; + re->width = 0.0; +} + +static void +eel_canvas_re_destroy (GtkObject *object) +{ + EelCanvasRE *re; + + g_return_if_fail (object != NULL); + g_return_if_fail (EEL_IS_CANVAS_RE (object)); + + re = EEL_CANVAS_RE (object); + + /* remember, destroy can be run multiple times! */ + + if (re->fill_stipple) + g_object_unref (re->fill_stipple); + re->fill_stipple = NULL; + + if (re->outline_stipple) + g_object_unref (re->outline_stipple); + re->outline_stipple = NULL; + + if (GTK_OBJECT_CLASS (re_parent_class)->destroy) + (* GTK_OBJECT_CLASS (re_parent_class)->destroy) (object); +} + +static void get_bounds (EelCanvasRE *re, double *px1, double *py1, double *px2, double *py2) +{ + EelCanvasItem *item; + double x1, y1, x2, y2; + int cx1, cy1, cx2, cy2; + double hwidth; + +#ifdef VERBOSE + g_print ("re get_bounds\n"); +#endif + item = EEL_CANVAS_ITEM (re); + + if (re->width_pixels) + hwidth = (re->width / item->canvas->pixels_per_unit) / 2.0; + else + hwidth = re->width / 2.0; + + x1 = re->x1; + y1 = re->y1; + x2 = re->x2; + y2 = re->y2; + + eel_canvas_item_i2w (item, &x1, &y1); + eel_canvas_item_i2w (item, &x2, &y2); + eel_canvas_w2c (item->canvas, x1 - hwidth, y1 - hwidth, &cx1, &cy1); + eel_canvas_w2c (item->canvas, x2 + hwidth, y2 + hwidth, &cx2, &cy2); + *px1 = cx1; + *py1 = cy1; + *px2 = cx2; + *py2 = cy2; + + /* Some safety fudging */ + + *px1 -= 2; + *py1 -= 2; + *px2 += 2; + *py2 += 2; +} + +/* Convenience function to set a GC's foreground color to the specified pixel value */ +static void +set_gc_foreground (GdkGC *gc, gulong pixel) +{ + GdkColor c; + + if (!gc) + return; + + c.pixel = pixel; + gdk_gc_set_foreground (gc, &c); +} + +/* Sets the stipple pattern for the specified gc */ +static void +set_stipple (GdkGC *gc, GdkBitmap **internal_stipple, GdkBitmap *stipple, int reconfigure) +{ + if (*internal_stipple && !reconfigure) + g_object_unref (*internal_stipple); + + *internal_stipple = stipple; + if (stipple && !reconfigure) + g_object_ref (stipple); + + if (gc) + { + if (stipple) + { + gdk_gc_set_stipple (gc, stipple); + gdk_gc_set_fill (gc, GDK_STIPPLED); + } + else + gdk_gc_set_fill (gc, GDK_SOLID); + } +} + +/* Recalculate the outline width of the rectangle/ellipse and set it in its GC */ +static void +set_outline_gc_width (EelCanvasRE *re) +{ + int width; + + if (!re->outline_gc) + return; + + if (re->width_pixels) + width = (int) re->width; + else + width = (int) (re->width * re->item.canvas->pixels_per_unit + 0.5); + + gdk_gc_set_line_attributes (re->outline_gc, width, + GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER); +} + +static void +eel_canvas_re_set_fill (EelCanvasRE *re, gboolean fill_set) +{ + if (re->fill_set != fill_set) + { + re->fill_set = fill_set; + eel_canvas_item_request_update (EEL_CANVAS_ITEM (re)); + } +} + +static void +eel_canvas_re_set_outline (EelCanvasRE *re, gboolean outline_set) +{ + if (re->outline_set != outline_set) + { + re->outline_set = outline_set; + eel_canvas_item_request_update (EEL_CANVAS_ITEM (re)); + } +} + +static void +eel_canvas_re_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + EelCanvasItem *item; + EelCanvasRE *re; + GdkColor color = { 0, 0, 0, 0, }; + GdkColor *pcolor; + int have_pixel; + + g_return_if_fail (object != NULL); + g_return_if_fail (EEL_IS_CANVAS_RE (object)); + + item = EEL_CANVAS_ITEM (object); + re = EEL_CANVAS_RE (object); + have_pixel = FALSE; + + switch (param_id) + { + case PROP_X1: + re->x1 = g_value_get_double (value); + + eel_canvas_item_request_update (item); + break; + + case PROP_Y1: + re->y1 = g_value_get_double (value); + + eel_canvas_item_request_update (item); + break; + + case PROP_X2: + re->x2 = g_value_get_double (value); + + eel_canvas_item_request_update (item); + break; + + case PROP_Y2: + re->y2 = g_value_get_double (value); + + eel_canvas_item_request_update (item); + break; + + case PROP_FILL_COLOR: + case PROP_FILL_COLOR_GDK: + case PROP_FILL_COLOR_RGBA: + switch (param_id) + { + case PROP_FILL_COLOR: + if (g_value_get_string (value) && + gdk_color_parse (g_value_get_string (value), &color)) + eel_canvas_re_set_fill (re, TRUE); + else + eel_canvas_re_set_fill (re, FALSE); + + re->fill_color = ((color.red & 0xff00) << 16 | + (color.green & 0xff00) << 8 | + (color.blue & 0xff00) | + 0xff); + break; + + case PROP_FILL_COLOR_GDK: + pcolor = g_value_get_boxed (value); + eel_canvas_re_set_fill (re, pcolor != NULL); + + if (pcolor) + { + GdkColormap *colormap; + + color = *pcolor; + colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas)); + gdk_rgb_find_color (colormap, &color); + have_pixel = TRUE; + } + + re->fill_color = ((color.red & 0xff00) << 16 | + (color.green & 0xff00) << 8 | + (color.blue & 0xff00) | + 0xff); + break; + + case PROP_FILL_COLOR_RGBA: + eel_canvas_re_set_fill (re, TRUE); + re->fill_color = g_value_get_uint (value); + break; + } +#ifdef VERBOSE + g_print ("re fill color = %08x\n", re->fill_color); +#endif + if (have_pixel) + re->fill_pixel = color.pixel; + else + re->fill_pixel = eel_canvas_get_color_pixel (item->canvas, re->fill_color); + + set_gc_foreground (re->fill_gc, re->fill_pixel); + + eel_canvas_item_request_redraw (item); + break; + + case PROP_OUTLINE_COLOR: + case PROP_OUTLINE_COLOR_GDK: + case PROP_OUTLINE_COLOR_RGBA: + switch (param_id) + { + case PROP_OUTLINE_COLOR: + if (g_value_get_string (value) && + gdk_color_parse (g_value_get_string (value), &color)) + eel_canvas_re_set_outline (re, TRUE); + else + eel_canvas_re_set_outline (re, FALSE); + + re->outline_color = ((color.red & 0xff00) << 16 | + (color.green & 0xff00) << 8 | + (color.blue & 0xff00) | + 0xff); + break; + + case PROP_OUTLINE_COLOR_GDK: + pcolor = g_value_get_boxed (value); + eel_canvas_re_set_outline (re, pcolor != NULL); + + if (pcolor) + { + GdkColormap *colormap; + + color = *pcolor; + colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas)); + gdk_rgb_find_color (colormap, &color); + + have_pixel = TRUE; + } + + re->outline_color = ((color.red & 0xff00) << 16 | + (color.green & 0xff00) << 8 | + (color.blue & 0xff00) | + 0xff); + break; + + case PROP_OUTLINE_COLOR_RGBA: + eel_canvas_re_set_outline (re, TRUE); + re->outline_color = g_value_get_uint (value); + break; + } +#ifdef VERBOSE + g_print ("re outline color %x %x %x\n", color.red, color.green, color.blue); +#endif + if (have_pixel) + re->outline_pixel = color.pixel; + else + re->outline_pixel = eel_canvas_get_color_pixel (item->canvas, + re->outline_color); + + set_gc_foreground (re->outline_gc, re->outline_pixel); + + eel_canvas_item_request_redraw (item); + break; + + case PROP_FILL_STIPPLE: + set_stipple (re->fill_gc, &re->fill_stipple, (GdkBitmap *) g_value_get_object (value), FALSE); + + break; + + case PROP_OUTLINE_STIPPLE: + set_stipple (re->outline_gc, &re->outline_stipple, (GdkBitmap *) g_value_get_object (value), FALSE); + break; + + case PROP_WIDTH_PIXELS: + re->width = g_value_get_uint (value); + re->width_pixels = TRUE; + set_outline_gc_width (re); + + eel_canvas_item_request_update (item); + break; + + case PROP_WIDTH_UNITS: + re->width = fabs (g_value_get_double (value)); + re->width_pixels = FALSE; + set_outline_gc_width (re); + + eel_canvas_item_request_update (item); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +/* Allocates a GdkColor structure filled with the specified pixel, and puts it into the specified + * value for returning it in the get_property method. + */ +static void +get_color_value (EelCanvasRE *re, gulong pixel, GValue *value) +{ + GdkColor color; + EelCanvasItem *item = (EelCanvasItem *) re; + GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas)); + + gdk_colormap_query_color (colormap, pixel, &color); + g_value_set_boxed (value, &color); +} + +static void +eel_canvas_re_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + EelCanvasRE *re; + + g_return_if_fail (object != NULL); + g_return_if_fail (EEL_IS_CANVAS_RE (object)); + + re = EEL_CANVAS_RE (object); + + switch (param_id) + { + case PROP_X1: + g_value_set_double (value, re->x1); + break; + + case PROP_Y1: + g_value_set_double (value, re->y1); + break; + + case PROP_X2: + g_value_set_double (value, re->x2); + break; + + case PROP_Y2: + g_value_set_double (value, re->y2); + break; + + case PROP_FILL_COLOR_GDK: + get_color_value (re, re->fill_pixel, value); + break; + + case PROP_OUTLINE_COLOR_GDK: + get_color_value (re, re->outline_pixel, value); + break; + + case PROP_FILL_COLOR_RGBA: + g_value_set_uint (value, re->fill_color); + break; + + case PROP_OUTLINE_COLOR_RGBA: + g_value_set_uint (value, re->outline_color); + break; + + case PROP_FILL_STIPPLE: + g_value_set_object (value, (GObject *) re->fill_stipple); + break; + + case PROP_OUTLINE_STIPPLE: + g_value_set_object (value, (GObject *) re->outline_stipple); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +set_colors_and_stipples (EelCanvasRE *re) +{ + set_gc_foreground (re->fill_gc, re->fill_pixel); + set_gc_foreground (re->outline_gc, re->outline_pixel); + set_stipple (re->fill_gc, &re->fill_stipple, re->fill_stipple, TRUE); + set_stipple (re->outline_gc, &re->outline_stipple, re->outline_stipple, TRUE); + set_outline_gc_width (re); +} + +static void +eel_canvas_re_update_shared (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags) +{ + EelCanvasRE *re; + +#ifdef VERBOSE + g_print ("eel_canvas_re_update_shared\n"); +#endif + re = EEL_CANVAS_RE (item); + + if (re_parent_class->update) + (* re_parent_class->update) (item, i2w_dx, i2w_dy, flags); + + set_colors_and_stipples (re); + +#ifdef OLD_XFORM + recalc_bounds (re); +#endif +} + +static void +eel_canvas_re_realize (EelCanvasItem *item) +{ + EelCanvasRE *re; + +#ifdef VERBOSE + g_print ("eel_canvas_re_realize\n"); +#endif + re = EEL_CANVAS_RE (item); + + if (re_parent_class->realize) + (* re_parent_class->realize) (item); + + re->fill_gc = gdk_gc_new (gtk_layout_get_bin_window (&item->canvas->layout)); + re->fill_pixel = eel_canvas_get_color_pixel (item->canvas, re->fill_color); + re->outline_gc = gdk_gc_new (gtk_layout_get_bin_window (&item->canvas->layout)); + re->outline_pixel = eel_canvas_get_color_pixel (item->canvas, re->outline_color); + set_colors_and_stipples (re); + +#ifdef OLD_XFORM + (* EEL_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0); +#endif +} + +static void +eel_canvas_re_unrealize (EelCanvasItem *item) +{ + EelCanvasRE *re; + + re = EEL_CANVAS_RE (item); + + g_object_unref (re->fill_gc); + re->fill_gc = NULL; + g_object_unref (re->outline_gc); + re->outline_gc = NULL; + + if (re_parent_class->unrealize) + (* re_parent_class->unrealize) (item); +} + +static void +eel_canvas_re_translate (EelCanvasItem *item, double dx, double dy) +{ + EelCanvasRE *re; + +#ifdef VERBOSE + g_print ("eel_canvas_re_translate\n"); +#endif + re = EEL_CANVAS_RE (item); + + re->x1 += dx; + re->y1 += dy; + re->x2 += dx; + re->y2 += dy; +} + + +static void +eel_canvas_re_bounds (EelCanvasItem *item, double *x1, double *y1, double *x2, double *y2) +{ + EelCanvasRE *re; + double hwidth; + +#ifdef VERBOSE + g_print ("eel_canvas_re_bounds\n"); +#endif + re = EEL_CANVAS_RE (item); + + if (re->width_pixels) + hwidth = (re->width / item->canvas->pixels_per_unit) / 2.0; + else + hwidth = re->width / 2.0; + + *x1 = re->x1 - hwidth; + *y1 = re->y1 - hwidth; + *x2 = re->x2 + hwidth; + *y2 = re->y2 + hwidth; +} + +/* Rectangle item */ + + +static void eel_canvas_rect_class_init (EelCanvasRectClass *klass); +static void eel_canvas_rect_init (EelCanvasRect *rect); +static void eel_canvas_rect_finalize (GObject *object); +static void eel_canvas_rect_realize (EelCanvasItem *item); + +static void eel_canvas_rect_draw (EelCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose); +static double eel_canvas_rect_point (EelCanvasItem *item, double x, double y, int cx, int cy, + EelCanvasItem **actual_item); + +struct _EelCanvasRectPrivate +{ + Rect last_update_rect; + Rect last_outline_update_rect; + int last_outline_update_width; + +#ifdef HAVE_RENDER + gboolean use_render; + XRenderPictFormat *format; +#endif +}; + +GType +eel_canvas_rect_get_type (void) +{ + static GType rect_type = 0; + + if (!rect_type) + { + GTypeInfo rect_info = + { + sizeof (EelCanvasRectClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) eel_canvas_rect_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (EelCanvasRect), + 0, /* n_preallocs */ + (GInstanceInitFunc) eel_canvas_rect_init + }; + + rect_type = g_type_register_static (eel_canvas_re_get_type (), + "EelCanvasRect", + &rect_info, + 0); + } + + return rect_type; +} + +static void +eel_canvas_rect_class_init (EelCanvasRectClass *klass) +{ + EelCanvasItemClass *item_class; + + rect_parent_class = g_type_class_peek_parent (klass); + + item_class = (EelCanvasItemClass *) klass; + + item_class->draw = eel_canvas_rect_draw; + item_class->point = eel_canvas_rect_point; + item_class->update = eel_canvas_rect_update; + item_class->realize = eel_canvas_rect_realize; + + G_OBJECT_CLASS (klass)->finalize = eel_canvas_rect_finalize; + +} + +static void +eel_canvas_rect_init (EelCanvasRect *rect) +{ + rect->priv = g_new0 (EelCanvasRectPrivate, 1); +} + +static void +eel_canvas_rect_finalize (GObject *object) +{ + EelCanvasRect *rect = EEL_CANVAS_RECT (object); + + if (rect->priv) + { + g_free (rect->priv); + } + + G_OBJECT_CLASS (rect_parent_class)->finalize (object); +} + +static void +eel_canvas_rect_realize (EelCanvasItem *item) +{ +#ifdef HAVE_RENDER + EelCanvasRectPrivate *priv; + int event_base, error_base; + Display *dpy; + + priv = EEL_CANVAS_RECT (item)->priv; + + dpy = gdk_x11_drawable_get_xdisplay (gtk_widget_get_window (GTK_WIDGET (item->canvas))); + priv->use_render = XRenderQueryExtension (dpy, &event_base, &error_base); + + if (priv->use_render) + { + GdkVisual *gdk_visual; + Visual *visual; + + gdk_visual = gtk_widget_get_visual (GTK_WIDGET (item->canvas)); + visual = gdk_x11_visual_get_xvisual (gdk_visual); + + priv->format = XRenderFindVisualFormat (dpy, visual); + } +#endif + + if (EEL_CANVAS_ITEM_CLASS (rect_parent_class)->realize) + { + (* EEL_CANVAS_ITEM_CLASS (rect_parent_class)->realize) (item); + } +} + + +static void +render_rect_alpha (EelCanvasRect *rect, + GdkDrawable *drawable, + int x, int y, + int width, int height, + guint32 rgba) +{ + GdkPixbuf *pixbuf; + guchar *data; + int rowstride, i; + guchar r, g, b, a; + EelCanvasRectPrivate *priv; + + if (width <= 0 || height <= 0 ) + { + return; + } + + priv = rect->priv; + + r = (rgba >> 24) & 0xff; + g = (rgba >> 16) & 0xff; + b = (rgba >> 8) & 0xff; + a = (rgba >> 0) & 0xff; + +#ifdef HAVE_RENDER + /* Every visual is not guaranteed to have a matching + * XRenderPictFormat. So make sure that format is not null before + * trying to render using Xrender calls. + */ + if (priv->use_render && (priv->format != NULL)) + { + GdkDrawable *real_drawable; + int x_offset, y_offset; + + Display *dpy; + Picture pict; + XRenderPictureAttributes attributes; + XRenderColor color; + + gdk_window_get_internal_paint_info (drawable, &real_drawable, + &x_offset, &y_offset); + + dpy = gdk_x11_drawable_get_xdisplay (real_drawable); + + pict = XRenderCreatePicture (dpy, + gdk_x11_drawable_get_xid (real_drawable), + priv->format, + 0, + &attributes); + + + /* Convert to premultiplied alpha: */ + r = r * a / 255; + g = g * a / 255; + b = b * a / 255; + + color.red = (r << 8) + r; + color.green = (g << 8) + g; + color.blue = (b << 8) + b; + color.alpha = (a << 8) + a; + + XRenderFillRectangle (dpy, + PictOpOver, + pict, + &color, + x - x_offset, y - y_offset, + width, height); + + XRenderFreePicture (dpy, pict); + + return; + } +#endif + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height); + data = gdk_pixbuf_get_pixels (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + + r = (rgba >> 24) & 0xff; + g = (rgba >> 16) & 0xff; + b = (rgba >> 8) & 0xff; + a = (rgba >> 0) & 0xff; + + for (i = 0; i < width*4; ) + { + data[i++] = r; + data[i++] = g; + data[i++] = b; + data[i++] = a; + } + + for (i = 1; i < height; i++) + { + memcpy (data + i*rowstride, data, width*4); + } + + gdk_draw_pixbuf (drawable, NULL, pixbuf, + 0, 0, x, y, width, height, + GDK_RGB_DITHER_NONE, 0, 0); + g_object_unref (pixbuf); +} + + +static void +eel_canvas_rect_draw (EelCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose) +{ + EelCanvasRE *re; + double x1, y1, x2, y2; + int cx1, cy1, cx2, cy2; + double i2w_dx, i2w_dy; + + re = EEL_CANVAS_RE (item); + + /* Get canvas pixel coordinates */ + i2w_dx = 0.0; + i2w_dy = 0.0; + eel_canvas_item_i2w (item, &i2w_dx, &i2w_dy); + + x1 = re->x1 + i2w_dx; + y1 = re->y1 + i2w_dy; + x2 = re->x2 + i2w_dx; + y2 = re->y2 + i2w_dy; + + eel_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1); + eel_canvas_w2c (item->canvas, x2, y2, &cx2, &cy2); + + if (re->fill_set) + { + if ((re->fill_color & 0xff) != 255) + { + GdkRectangle *rectangles; + gint i, n_rectangles; + GdkRectangle draw_rect; + GdkRectangle part; + + draw_rect.x = cx1; + draw_rect.y = cy1; + draw_rect.width = cx2 - cx1 + 1; + draw_rect.height = cy2 - cy1 + 1; + + /* For alpha mode, only render the parts of the region + that are actually exposed */ + gdk_region_get_rectangles (expose->region, + &rectangles, + &n_rectangles); + + for (i = 0; i < n_rectangles; i++) + { + if (gdk_rectangle_intersect (&rectangles[i], + &draw_rect, + &part)) + { + render_rect_alpha (EEL_CANVAS_RECT (item), + drawable, + part.x, part.y, + part.width, part.height, + re->fill_color); + } + } + + g_free (rectangles); + } + else + { + if (re->fill_stipple) + eel_canvas_set_stipple_origin (item->canvas, re->fill_gc); + + gdk_draw_rectangle (drawable, + re->fill_gc, + TRUE, + cx1, cy1, + cx2 - cx1 + 1, + cy2 - cy1 + 1); + } + } + + if (re->outline_set) + { + if (re->outline_stipple) + eel_canvas_set_stipple_origin (item->canvas, re->outline_gc); + + gdk_draw_rectangle (drawable, + re->outline_gc, + FALSE, + cx1, + cy1, + cx2 - cx1, + cy2 - cy1); + } +} + +static double +eel_canvas_rect_point (EelCanvasItem *item, double x, double y, int cx, int cy, EelCanvasItem **actual_item) +{ + EelCanvasRE *re; + double x1, y1, x2, y2; + double hwidth; + double dx, dy; + double tmp; + +#ifdef VERBOSE + g_print ("eel_canvas_rect_point\n"); +#endif + re = EEL_CANVAS_RE (item); + + *actual_item = item; + + /* Find the bounds for the rectangle plus its outline width */ + + x1 = re->x1; + y1 = re->y1; + x2 = re->x2; + y2 = re->y2; + + if (re->outline_set) + { + if (re->width_pixels) + hwidth = (re->width / item->canvas->pixels_per_unit) / 2.0; + else + hwidth = re->width / 2.0; + + x1 -= hwidth; + y1 -= hwidth; + x2 += hwidth; + y2 += hwidth; + } + else + hwidth = 0.0; + + /* Is point inside rectangle (which can be hollow if it has no fill set)? */ + + if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) + { + if (re->fill_set || !re->outline_set) + return 0.0; + + dx = x - x1; + tmp = x2 - x; + if (tmp < dx) + dx = tmp; + + dy = y - y1; + tmp = y2 - y; + if (tmp < dy) + dy = tmp; + + if (dy < dx) + dx = dy; + + dx -= 2.0 * hwidth; + + if (dx < 0.0) + return 0.0; + else + return dx; + } + + /* Point is outside rectangle */ + + if (x < x1) + dx = x1 - x; + else if (x > x2) + dx = x - x2; + else + dx = 0.0; + + if (y < y1) + dy = y1 - y; + else if (y > y2) + dy = y - y2; + else + dy = 0.0; + + return sqrt (dx * dx + dy * dy); +} + +static void +request_redraw_borders (EelCanvas *canvas, + Rect *update_rect, + int width) +{ + eel_canvas_request_redraw (canvas, + update_rect->x0, update_rect->y0, + update_rect->x1, update_rect->y0 + width); + eel_canvas_request_redraw (canvas, + update_rect->x0, update_rect->y1-width, + update_rect->x1, update_rect->y1); + eel_canvas_request_redraw (canvas, + update_rect->x0, update_rect->y0, + update_rect->x0+width, update_rect->y1); + eel_canvas_request_redraw (canvas, + update_rect->x1-width, update_rect->y0, + update_rect->x1, update_rect->y1); +} + + +static void +eel_canvas_rect_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, gint flags) +{ + EelCanvasRE *re; + double x1, y1, x2, y2; + int cx1, cy1, cx2, cy2; + int repaint_rects_count, i; + int width_pixels; + int width_lt, width_rb; + Rect update_rect, repaint_rects[4]; + EelCanvasRectPrivate *priv; + + eel_canvas_re_update_shared (item, i2w_dx, i2w_dy, flags); + + re = EEL_CANVAS_RE (item); + priv = EEL_CANVAS_RECT (item)->priv; + + x1 = re->x1 + i2w_dx; + y1 = re->y1 + i2w_dy; + x2 = re->x2 + i2w_dx; + y2 = re->y2 + i2w_dy; + + eel_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1); + eel_canvas_w2c (item->canvas, x2, y2, &cx2, &cy2); + + update_rect = make_rect (cx1, cy1, cx2+1, cy2+1); +#if 0 + eel_canvas_request_redraw (item->canvas, + update_rect.x0, update_rect.y0, + update_rect.x1, update_rect.y1); + eel_canvas_request_redraw (item->canvas, + priv->last_update_rect.x0, priv->last_update_rect.y0, + priv->last_update_rect.x1, priv->last_update_rect.y1); +#else + diff_rects (update_rect, priv->last_update_rect, + &repaint_rects_count, repaint_rects); + for (i = 0; i < repaint_rects_count; i++) + { + eel_canvas_request_redraw (item->canvas, + repaint_rects[i].x0, repaint_rects[i].y0, + repaint_rects[i].x1, repaint_rects[i].y1); + } +#endif + priv->last_update_rect = update_rect; + + if (re->outline_set) + { + /* Outline and bounding box */ + if (re->width_pixels) + width_pixels = (int) re->width; + else + width_pixels = (int) floor (re->width * re->item.canvas->pixels_per_unit + 0.5); + + width_lt = width_pixels / 2; + width_rb = (width_pixels + 1) / 2; + + cx1 -= width_lt; + cy1 -= width_lt; + cx2 += width_rb; + cy2 += width_rb; + + update_rect = make_rect (cx1, cy1, cx2, cy2); + request_redraw_borders (item->canvas, &update_rect, + (width_lt + width_rb)); + request_redraw_borders (item->canvas, &priv->last_outline_update_rect, + priv->last_outline_update_width); + priv->last_outline_update_rect = update_rect; + priv->last_outline_update_width = width_lt + width_rb; + + item->x1 = cx1; + item->y1 = cy1; + item->x2 = cx2+1; + item->y2 = cy2+1; + } + else + { + item->x1 = cx1; + item->y1 = cy1; + item->x2 = cx2+1; + item->y2 = cy2+1; + } +} + +/* Ellipse item */ + + +static void eel_canvas_ellipse_class_init (EelCanvasEllipseClass *klass); + +static void eel_canvas_ellipse_draw (EelCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose); +static double eel_canvas_ellipse_point (EelCanvasItem *item, double x, double y, int cx, int cy, + EelCanvasItem **actual_item); + + +GType +eel_canvas_ellipse_get_type (void) +{ + static GType ellipse_type = 0; + + if (!ellipse_type) + { + GTypeInfo ellipse_info = + { + sizeof (EelCanvasEllipseClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) eel_canvas_ellipse_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (EelCanvasEllipse), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL + + }; + + ellipse_type = g_type_register_static (eel_canvas_re_get_type (), + "EelCanvasEllipse", + &ellipse_info, + 0); + } + + return ellipse_type; +} + +static void +eel_canvas_ellipse_class_init (EelCanvasEllipseClass *klass) +{ + EelCanvasItemClass *item_class; + + item_class = (EelCanvasItemClass *) klass; + + item_class->draw = eel_canvas_ellipse_draw; + item_class->point = eel_canvas_ellipse_point; + item_class->update = eel_canvas_ellipse_update; +} + +static void +eel_canvas_ellipse_draw (EelCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose) +{ + EelCanvasRE *re; + int x1, y1, x2, y2; + double i2w_dx, i2w_dy; + + re = EEL_CANVAS_RE (item); + + /* Get canvas pixel coordinates */ + + i2w_dx = 0.0; + i2w_dy = 0.0; + eel_canvas_item_i2w (item, &i2w_dx, &i2w_dy); + + eel_canvas_w2c (item->canvas, + re->x1 + i2w_dx, + re->y1 + i2w_dy, + &x1, &y1); + eel_canvas_w2c (item->canvas, + re->x2 + i2w_dx, + re->y2 + i2w_dy, + &x2, &y2); + + if (re->fill_set) + { + if (re->fill_stipple) + eel_canvas_set_stipple_origin (item->canvas, re->fill_gc); + + gdk_draw_arc (drawable, + re->fill_gc, + TRUE, + x1, + y1, + x2 - x1, + y2 - y1, + 0 * 64, + 360 * 64); + } + + if (re->outline_set) + { + if (re->outline_stipple) + eel_canvas_set_stipple_origin (item->canvas, re->outline_gc); + + gdk_draw_arc (drawable, + re->outline_gc, + FALSE, + x1, + y1, + x2 - x1, + y2 - y1, + 0 * 64, + 360 * 64); + } +} + +static double +eel_canvas_ellipse_point (EelCanvasItem *item, double x, double y, int cx, int cy, EelCanvasItem **actual_item) +{ + EelCanvasRE *re; + double dx, dy; + double scaled_dist; + double outline_dist; + double center_dist; + double width; + double a, b; + double diamx, diamy; + + re = EEL_CANVAS_RE (item); + + *actual_item = item; + + if (re->outline_set) + { + if (re->width_pixels) + width = re->width / item->canvas->pixels_per_unit; + else + width = re->width; + } + else + width = 0.0; + + /* Compute the distance between the center of the ellipse and the point, with the ellipse + * considered as being scaled to a circle. + */ + + dx = x - (re->x1 + re->x2) / 2.0; + dy = y - (re->y1 + re->y2) / 2.0; + center_dist = sqrt (dx * dx + dy * dy); + + a = dx / ((re->x2 + width - re->x1) / 2.0); + b = dy / ((re->y2 + width - re->y1) / 2.0); + scaled_dist = sqrt (a * a + b * b); + + /* If the scaled distance is greater than 1, then we are outside. Compute the distance from + * the point to the edge of the circle, then scale back to the original un-scaled coordinate + * system. + */ + + if (scaled_dist > 1.0) + return (center_dist / scaled_dist) * (scaled_dist - 1.0); + + /* We are inside the outer edge of the ellipse. If it is filled, then we are "inside". + * Otherwise, do the same computation as above, but also check whether we are inside the + * outline. + */ + + if (re->fill_set) + return 0.0; + + if (scaled_dist > EEL_CANVAS_EPSILON) + outline_dist = (center_dist / scaled_dist) * (1.0 - scaled_dist) - width; + else + { + /* Handle very small distance */ + + diamx = re->x2 - re->x1; + diamy = re->y2 - re->y1; + + if (diamx < diamy) + outline_dist = (diamx - width) / 2.0; + else + outline_dist = (diamy - width) / 2.0; + } + + if (outline_dist < 0.0) + return 0.0; + + return outline_dist; +} + +static void +eel_canvas_ellipse_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, gint flags) +{ + EelCanvasRE *re; + double x0, y0, x1, y1; + +#ifdef VERBOSE + g_print ("eel_canvas_sllipse_update item %x\n", item); +#endif + + eel_canvas_re_update_shared (item, i2w_dx, i2w_dy, flags); + re = EEL_CANVAS_RE (item); + + get_bounds (re, &x0, &y0, &x1, &y1); + eel_canvas_update_bbox (item, x0, y0, x1, y1); +} + +static int +rect_empty (const Rect *src) +{ + return (src->x1 <= src->x0 || src->y1 <= src->y0); +} + +static Rect +make_rect (int x0, int y0, int x1, int y1) +{ + Rect r; + + r.x0 = x0; + r.y0 = y0; + r.x1 = x1; + r.y1 = y1; + return r; +} + +static gboolean +rects_intersect (Rect r1, Rect r2) +{ + if (r1.x0 >= r2.x1) + { + return FALSE; + } + if (r2.x0 >= r1.x1) + { + return FALSE; + } + if (r1.y0 >= r2.y1) + { + return FALSE; + } + if (r2.y0 >= r1.y1) + { + return FALSE; + } + return TRUE; +} + +static void +diff_rects_guts (Rect ra, Rect rb, int *count, Rect result[4]) +{ + if (ra.x0 < rb.x0) + { + result[(*count)++] = make_rect (ra.x0, ra.y0, rb.x0, ra.y1); + } + if (ra.y0 < rb.y0) + { + result[(*count)++] = make_rect (ra.x0, ra.y0, ra.x1, rb.y0); + } + if (ra.x1 < rb.x1) + { + result[(*count)++] = make_rect (ra.x1, rb.y0, rb.x1, rb.y1); + } + if (ra.y1 < rb.y1) + { + result[(*count)++] = make_rect (rb.x0, ra.y1, rb.x1, rb.y1); + } +} + +static void +diff_rects (Rect r1, Rect r2, int *count, Rect result[4]) +{ + g_assert (count != NULL); + g_assert (result != NULL); + + *count = 0; + + if (rects_intersect (r1, r2)) + { + diff_rects_guts (r1, r2, count, result); + diff_rects_guts (r2, r1, count, result); + } + else + { + if (!rect_empty (&r1)) + { + result[(*count)++] = r1; + } + if (!rect_empty (&r2)) + { + result[(*count)++] = r2; + } + } +} |