[anjuta/git-shell] Add things not included in the last merge
- From: James Liggett <jrliggett src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [anjuta/git-shell] Add things not included in the last merge
- Date: Tue, 11 May 2010 08:06:55 +0000 (UTC)
commit f61f6b35e3c125fd1f2c75eb690143d3238b4e80
Author: James Liggett <jrliggett cox net>
Date: Tue May 11 01:04:14 2010 -0700
Add things not included in the last merge
libfoocanvas and the new class-inheritance plugin added.
libfoocanvas/Makefile.am | 62 +
libfoocanvas/foo-canvas-i18n.h | 68 +
libfoocanvas/foo-canvas-line.c | 1376 +++++++
libfoocanvas/foo-canvas-line.h | 151 +
libfoocanvas/foo-canvas-marshal.list | 2 +
libfoocanvas/foo-canvas-pixbuf.c | 825 ++++
libfoocanvas/foo-canvas-pixbuf.h | 62 +
libfoocanvas/foo-canvas-polygon.c | 826 ++++
libfoocanvas/foo-canvas-polygon.h | 121 +
libfoocanvas/foo-canvas-rect-ellipse.c | 1489 ++++++++
libfoocanvas/foo-canvas-rect-ellipse.h | 174 +
libfoocanvas/foo-canvas-text.c | 1587 ++++++++
libfoocanvas/foo-canvas-text.h | 168 +
libfoocanvas/foo-canvas-util.c | 399 ++
libfoocanvas/foo-canvas-util.h | 105 +
libfoocanvas/foo-canvas-widget.c | 591 +++
libfoocanvas/foo-canvas-widget.h | 103 +
libfoocanvas/foo-canvas.c | 3999 ++++++++++++++++++++
libfoocanvas/foo-canvas.h | 530 +++
libfoocanvas/libfoocanvas.h | 45 +
libfoocanvas/libfoocanvastypes.c | 43 +
plugins/class-inheritance/Makefile.am | 63 +
.../anjuta-class-inheritance-plugin-48.png | Bin 0 -> 980 bytes
.../anjuta-class-inheritance-plugin.svg | 311 ++
.../anjuta-class-inheritance.plugin | 26 +
.../anjuta-class-inheritance.plugin.in | 5 +
plugins/class-inheritance/class-callbacks.c | 448 +++
plugins/class-inheritance/class-callbacks.h | 47 +
plugins/class-inheritance/class-inherit.c | 1174 ++++++
plugins/class-inheritance/class-inherit.h | 118 +
plugins/class-inheritance/plugin.c | 197 +
plugins/class-inheritance/plugin.h | 87 +
32 files changed, 15202 insertions(+), 0 deletions(-)
---
diff --git a/libfoocanvas/Makefile.am b/libfoocanvas/Makefile.am
new file mode 100644
index 0000000..25f2de1
--- /dev/null
+++ b/libfoocanvas/Makefile.am
@@ -0,0 +1,62 @@
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ $(WARN_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(PANG_CFLAGS) \
+ -DFOOCANVASLIBDIR=\""$(libdir)"\" \
+ -DFOOCANVASDATADIR=\""$(datadir)"\" \
+ -DFOOCANVASPIXMAPDIR=\""$(datadir)/pixmaps"\" \
+ -DFOOCANVASBINDIR=\""$(bindir)"\" \
+ -DFOOCANVASLOCALSTATEDIR=\""$(localstatedir)"\" \
+ -DFOOCANVASLOCALEDIR=\""$(gnomelocaledir)"\" \
+ -DG_LOG_DOMAIN=\"Foocanvas\" \
+ -DVERSION=\"$(VERSION)\"
+
+lib_LTLIBRARIES = libanjuta-foocanvas.la
+
+libanjuta_foocanvas_la_LIBADD = \
+ $(RENDER_LIBS) \
+ $(GTK_LIBS) \
+ $(PANGO_LIBS)
+
+libanjuta_foocanvas_la_SOURCES = \
+ foo-canvas-line.h \
+ foo-canvas-pixbuf.h \
+ foo-canvas-polygon.h \
+ foo-canvas-rect-ellipse.h \
+ foo-canvas-text.h \
+ foo-canvas-util.h \
+ foo-canvas-widget.h \
+ foo-canvas.h \
+ libfoocanvas.h \
+ foo-canvas-marshal.list \
+ foo-canvas-i18n.h \
+ foo-canvas-line.c \
+ foo-canvas-pixbuf.c \
+ foo-canvas-polygon.c \
+ foo-canvas-rect-ellipse.c \
+ foo-canvas-text.c \
+ foo-canvas-util.c \
+ foo-canvas-widget.c \
+ foo-canvas.c \
+ libfoocanvastypes.c
+
+GENMARSHAL_COMMAND = $(GLIB_GENMARSHAL) --prefix=foo_canvas_marshal
+SUFFIXES = .list
+
+.list.h:
+ $(GENMARSHAL_COMMAND) --header $< >$@
+
+.list.c:
+ (echo '/* This file has been automatically generated. Do not edit. */' && \
+ echo '#include "$*.h"' && \
+ $(GENMARSHAL_COMMAND) --body $< ) >$@
+
+# A hint is needed to build the header first:
+BUILT_SOURCES = foo-canvas-marshal.h
+
+# Another hint, see bugs #172211 and #172212:
+non-intermediate: foo-canvas-marshal.c
+
+CLEANFILES = foo-canvas-marshal.h foo-canvas-marshal.c
diff --git a/libfoocanvas/foo-canvas-i18n.h b/libfoocanvas/foo-canvas-i18n.h
new file mode 100644
index 0000000..1675bc4
--- /dev/null
+++ b/libfoocanvas/foo-canvas-i18n.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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@
+ */
+
+/*
+ * Handles all of the internationalization configuration options.
+ * Author: Tom Tromey <tromey creche cygnus com>
+ */
+
+#ifndef __LIBFOO_CANVAS_I18N_H__
+#define __LIBFOO_CANVAS_I18N_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#if !defined(__LIBFOO_CANVAS_I18NP_H__)
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+# ifdef GNOME_EXPLICIT_TRANSLATION_DOMAIN
+# undef _
+# define _(String) dgettext (GNOME_EXPLICIT_TRANSLATION_DOMAIN, String)
+# else
+# define _(String) gettext (String)
+# endif
+# ifdef gettext_noop
+# define N_(String) gettext_noop (String)
+# else
+# define N_(String) (String)
+# endif
+#else
+/* Stubs that do something close enough. */
+# define textdomain(String) (String)
+# define gettext(String) (String)
+# define dgettext(Domain,Message) (Message)
+# define dcgettext(Domain,Message,Type) (Message)
+# define bindtextdomain(Domain,Directory) (Domain)
+# define _(String) (String)
+# define N_(String) (String)
+#endif
+
+#endif
+
+G_END_DECLS
+
+#endif /* __LIBFOO_CANVAS_I18N_H__ */
diff --git a/libfoocanvas/foo-canvas-line.c b/libfoocanvas/foo-canvas-line.c
new file mode 100644
index 0000000..5a1600e
--- /dev/null
+++ b/libfoocanvas/foo-canvas-line.c
@@ -0,0 +1,1376 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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@
+ */
+
+/* Line/curve item type for FooCanvas widget
+ *
+ * FooCanvas 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 <federico nuclecu unam mx>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+#include "libfoocanvas.h"
+
+#define noVERBOSE
+
+#define DEFAULT_SPLINE_STEPS 12 /* this is what Tk uses */
+#define NUM_ARROW_POINTS 6 /* number of points in an arrowhead */
+#define NUM_STATIC_POINTS 256 /* number of static points to use to avoid allocating arrays */
+
+
+#define GROW_BOUNDS(bx1, by1, bx2, by2, x, y) { \
+ if (x < bx1) \
+ bx1 = x; \
+ \
+ if (x > bx2) \
+ bx2 = x; \
+ \
+ if (y < by1) \
+ by1 = y; \
+ \
+ if (y > by2) \
+ by2 = y; \
+}
+
+
+enum {
+ PROP_0,
+ PROP_POINTS,
+ PROP_FILL_COLOR,
+ PROP_FILL_COLOR_GDK,
+ PROP_FILL_COLOR_RGBA,
+ PROP_FILL_STIPPLE,
+ PROP_WIDTH_PIXELS,
+ PROP_WIDTH_UNITS,
+ PROP_CAP_STYLE,
+ PROP_JOIN_STYLE,
+ PROP_LINE_STYLE,
+ PROP_FIRST_ARROWHEAD,
+ PROP_LAST_ARROWHEAD,
+ PROP_SMOOTH,
+ PROP_SPLINE_STEPS,
+ PROP_ARROW_SHAPE_A,
+ PROP_ARROW_SHAPE_B,
+ PROP_ARROW_SHAPE_C
+};
+
+
+static void foo_canvas_line_class_init (FooCanvasLineClass *klass);
+static void foo_canvas_line_init (FooCanvasLine *line);
+static void foo_canvas_line_destroy (GtkObject *object);
+static void foo_canvas_line_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void foo_canvas_line_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void foo_canvas_line_update (FooCanvasItem *item,
+ double i2w_dx, double i2w_dy,
+ int flags);
+static void foo_canvas_line_realize (FooCanvasItem *item);
+static void foo_canvas_line_unrealize (FooCanvasItem *item);
+static void foo_canvas_line_draw (FooCanvasItem *item, GdkDrawable *drawable,
+ GdkEventExpose *event);
+static double foo_canvas_line_point (FooCanvasItem *item, double x, double y,
+ int cx, int cy, FooCanvasItem **actual_item);
+static void foo_canvas_line_translate (FooCanvasItem *item, double dx, double dy);
+static void foo_canvas_line_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+
+static FooCanvasItemClass *parent_class;
+
+G_DEFINE_TYPE (FooCanvasLine, foo_canvas_line, FOO_TYPE_CANVAS_ITEM)
+
+static void
+foo_canvas_line_class_init (FooCanvasLineClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ FooCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) klass;
+ object_class = (GtkObjectClass *) klass;
+ item_class = (FooCanvasItemClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->set_property = foo_canvas_line_set_property;
+ gobject_class->get_property = foo_canvas_line_get_property;
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_POINTS,
+ g_param_spec_boxed ("points", NULL, NULL,
+ FOO_TYPE_CANVAS_POINTS,
+ 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_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));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_CAP_STYLE,
+ g_param_spec_enum ("cap-style", NULL, NULL,
+ GDK_TYPE_CAP_STYLE,
+ GDK_CAP_BUTT,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_JOIN_STYLE,
+ g_param_spec_enum ("join-style", NULL, NULL,
+ GDK_TYPE_JOIN_STYLE,
+ GDK_JOIN_MITER,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_LINE_STYLE,
+ g_param_spec_enum ("line-style", NULL, NULL,
+ GDK_TYPE_LINE_STYLE,
+ GDK_LINE_SOLID,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FIRST_ARROWHEAD,
+ g_param_spec_boolean ("first-arrowhead", NULL, NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_LAST_ARROWHEAD,
+ g_param_spec_boolean ("last-arrowhead", NULL, NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SMOOTH,
+ g_param_spec_boolean ("smooth", NULL, NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SPLINE_STEPS,
+ g_param_spec_uint ("spline-steps", NULL, NULL,
+ 0, G_MAXUINT, DEFAULT_SPLINE_STEPS,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_ARROW_SHAPE_A,
+ g_param_spec_double ("arrow-shape-a", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_ARROW_SHAPE_B,
+ g_param_spec_double ("arrow-shape-b", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_ARROW_SHAPE_C,
+ g_param_spec_double ("arrow-shape-c", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ G_PARAM_READWRITE));
+
+ object_class->destroy = foo_canvas_line_destroy;
+
+ item_class->update = foo_canvas_line_update;
+ item_class->realize = foo_canvas_line_realize;
+ item_class->unrealize = foo_canvas_line_unrealize;
+ item_class->draw = foo_canvas_line_draw;
+ item_class->point = foo_canvas_line_point;
+ item_class->translate = foo_canvas_line_translate;
+ item_class->bounds = foo_canvas_line_bounds;
+}
+
+static void
+foo_canvas_line_init (FooCanvasLine *line)
+{
+ line->width = 0.0;
+ line->cap = GDK_CAP_BUTT;
+ line->join = GDK_JOIN_MITER;
+ line->line_style = GDK_LINE_SOLID;
+ line->shape_a = 0.0;
+ line->shape_b = 0.0;
+ line->shape_c = 0.0;
+ line->spline_steps = DEFAULT_SPLINE_STEPS;
+ line->line_smoothed = FALSE;
+}
+
+static void
+foo_canvas_line_destroy (GtkObject *object)
+{
+ FooCanvasLine *line;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_LINE (object));
+
+ line = FOO_CANVAS_LINE (object);
+
+ /* remember, destroy can be run multiple times! */
+
+ if (line->coords)
+ g_free (line->coords);
+ line->coords = NULL;
+
+ if (line->first_coords)
+ g_free (line->first_coords);
+ line->first_coords = NULL;
+
+ if (line->last_coords)
+ g_free (line->last_coords);
+ line->last_coords = NULL;
+
+ if (line->stipple)
+ g_object_unref (line->stipple);
+ line->stipple = NULL;
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+/* Computes the bounding box of the line, including its arrow points. Assumes that the number of
+ * points in the line is not zero.
+ */
+static void
+get_bounds (FooCanvasLine *line, double *bx1, double *by1, double *bx2, double *by2)
+{
+ double *coords;
+ double x1, y1, x2, y2;
+ double width;
+ int i;
+
+ if (!line->coords) {
+ *bx1 = *by1 = *bx2 = *by2 = 0.0;
+ return;
+ }
+
+ /* Find bounding box of line's points */
+
+ x1 = x2 = line->coords[0];
+ y1 = y2 = line->coords[1];
+
+ for (i = 1, coords = line->coords + 2; i < line->num_points; i++, coords += 2)
+ GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
+
+ /* Add possible over-estimate for wide lines */
+
+ if (line->width_pixels)
+ width = line->width / line->item.canvas->pixels_per_unit;
+ else
+ width = line->width;
+
+ x1 -= width;
+ y1 -= width;
+ x2 += width;
+ y2 += width;
+
+ /* For mitered lines, make a second pass through all the points. Compute the location of
+ * the two miter vertex points and add them to the bounding box.
+ */
+
+ if (line->join == GDK_JOIN_MITER)
+ for (i = line->num_points, coords = line->coords; i >= 3; i--, coords += 2) {
+ double mx1, my1, mx2, my2;
+
+ if (foo_canvas_get_miter_points (coords[0], coords[1],
+ coords[2], coords[3],
+ coords[4], coords[5],
+ width,
+ &mx1, &my1, &mx2, &my2)) {
+ GROW_BOUNDS (x1, y1, x2, y2, mx1, my1);
+ GROW_BOUNDS (x1, y1, x2, y2, mx2, my2);
+ }
+ }
+
+ /* Add the arrow points, if any */
+
+ if (line->first_arrow && line->first_coords)
+ for (i = 0, coords = line->first_coords; i < NUM_ARROW_POINTS; i++, coords += 2)
+ GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
+
+ if (line->last_arrow && line->last_coords)
+ for (i = 0, coords = line->last_coords; i < NUM_ARROW_POINTS; i++, coords += 2)
+ GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
+
+ /* Done */
+
+ *bx1 = x1;
+ *by1 = y1;
+ *bx2 = x2;
+ *by2 = y2;
+}
+
+/* Computes the bounding box of the line, in canvas coordinates. Assumes that the number of points in the polygon is
+ * not zero.
+ */
+static void
+get_bounds_canvas (FooCanvasLine *line,
+ double *bx1, double *by1, double *bx2, double *by2,
+ double i2w_dx, double i2w_dy)
+{
+ FooCanvasItem *item;
+ double bbox_x0, bbox_y0, bbox_x1, bbox_y1;
+
+ item = FOO_CANVAS_ITEM (line);
+
+ get_bounds (line, &bbox_x0, &bbox_y0, &bbox_x1, &bbox_y1);
+
+ bbox_x0 += i2w_dx;
+ bbox_y0 += i2w_dy;
+ bbox_x1 += i2w_dx;
+ bbox_y1 += i2w_dy;
+
+ foo_canvas_w2c_rect_d (item->canvas,
+ &bbox_x0, &bbox_y0, &bbox_x1, &bbox_y1);
+
+ /* include 1 pixel of fudge */
+ *bx1 = bbox_x0 - 1;
+ *by1 = bbox_y0 - 1;
+ *bx2 = bbox_x1 + 1;
+ *by2 = bbox_y1 + 1;
+}
+
+/* Recalculates the arrow polygons for the line */
+static void
+reconfigure_arrows (FooCanvasLine *line)
+{
+ double *poly, *coords;
+ double dx, dy, length;
+ double sin_theta, cos_theta, tmp;
+ double frac_height; /* Line width as fraction of arrowhead width */
+ double backup; /* Distance to backup end points so the line ends in the middle of the arrowhead */
+ double vx, vy; /* Position of arrowhead vertex */
+ double shape_a, shape_b, shape_c;
+ double width;
+ int i;
+
+ if (line->num_points == 0)
+ return;
+
+ /* Set up things */
+
+ if (line->first_arrow) {
+ if (line->first_coords) {
+ line->coords[0] = line->first_coords[0];
+ line->coords[1] = line->first_coords[1];
+ } else
+ line->first_coords = g_new (double, 2 * NUM_ARROW_POINTS);
+ } else if (line->first_coords) {
+ line->coords[0] = line->first_coords[0];
+ line->coords[1] = line->first_coords[1];
+
+ g_free (line->first_coords);
+ line->first_coords = NULL;
+ }
+
+ i = 2 * (line->num_points - 1);
+
+ if (line->last_arrow) {
+ if (line->last_coords) {
+ line->coords[i] = line->last_coords[0];
+ line->coords[i + 1] = line->last_coords[1];
+ } else
+ line->last_coords = g_new (double, 2 * NUM_ARROW_POINTS);
+ } else if (line->last_coords) {
+ line->coords[i] = line->last_coords[0];
+ line->coords[i + 1] = line->last_coords[1];
+
+ g_free (line->last_coords);
+ line->last_coords = NULL;
+ }
+
+ if (!line->first_arrow && !line->last_arrow)
+ return;
+
+ if (line->width_pixels)
+ width = line->width / line->item.canvas->pixels_per_unit;
+ else
+ width = line->width;
+
+ /* Add fudge value for better-looking results */
+
+ shape_a = line->shape_a;
+ shape_b = line->shape_b;
+ shape_c = line->shape_c + width / 2.0;
+
+ if (line->width_pixels) {
+ shape_a /= line->item.canvas->pixels_per_unit;
+ shape_b /= line->item.canvas->pixels_per_unit;
+ shape_c /= line->item.canvas->pixels_per_unit;
+ }
+
+ shape_a += 0.001;
+ shape_b += 0.001;
+ shape_c += 0.001;
+
+ /* Compute the polygon for the first arrowhead and adjust the first point in the line so
+ * that the line does not stick out past the leading edge of the arrowhead.
+ */
+
+ frac_height = (line->width / 2.0) / shape_c;
+ backup = frac_height * shape_b + shape_a * (1.0 - frac_height) / 2.0;
+
+ if (line->first_arrow) {
+ poly = line->first_coords;
+ poly[0] = poly[10] = line->coords[0];
+ poly[1] = poly[11] = line->coords[1];
+
+ dx = poly[0] - line->coords[2];
+ dy = poly[1] - line->coords[3];
+ length = sqrt (dx * dx + dy * dy);
+ if (length < FOO_CANVAS_EPSILON)
+ sin_theta = cos_theta = 0.0;
+ else {
+ sin_theta = dy / length;
+ cos_theta = dx / length;
+ }
+
+ vx = poly[0] - shape_a * cos_theta;
+ vy = poly[1] - shape_a * sin_theta;
+
+ tmp = shape_c * sin_theta;
+
+ poly[2] = poly[0] - shape_b * cos_theta + tmp;
+ poly[8] = poly[2] - 2.0 * tmp;
+
+ tmp = shape_c * cos_theta;
+
+ poly[3] = poly[1] - shape_b * sin_theta - tmp;
+ poly[9] = poly[3] + 2.0 * tmp;
+
+ poly[4] = poly[2] * frac_height + vx * (1.0 - frac_height);
+ poly[5] = poly[3] * frac_height + vy * (1.0 - frac_height);
+ poly[6] = poly[8] * frac_height + vx * (1.0 - frac_height);
+ poly[7] = poly[9] * frac_height + vy * (1.0 - frac_height);
+
+ /* Move the first point towards the second so that the corners at the end of the
+ * line are inside the arrowhead.
+ */
+
+ line->coords[0] = poly[0] - backup * cos_theta;
+ line->coords[1] = poly[1] - backup * sin_theta;
+ }
+
+ /* Same process for last arrowhead */
+
+ if (line->last_arrow) {
+ coords = line->coords + 2 * (line->num_points - 2);
+ poly = line->last_coords;
+ poly[0] = poly[10] = coords[2];
+ poly[1] = poly[11] = coords[3];
+
+ dx = poly[0] - coords[0];
+ dy = poly[1] - coords[1];
+ length = sqrt (dx * dx + dy * dy);
+ if (length < FOO_CANVAS_EPSILON)
+ sin_theta = cos_theta = 0.0;
+ else {
+ sin_theta = dy / length;
+ cos_theta = dx / length;
+ }
+
+ vx = poly[0] - shape_a * cos_theta;
+ vy = poly[1] - shape_a * sin_theta;
+
+ tmp = shape_c * sin_theta;
+
+ poly[2] = poly[0] - shape_b * cos_theta + tmp;
+ poly[8] = poly[2] - 2.0 * tmp;
+
+ tmp = shape_c * cos_theta;
+
+ poly[3] = poly[1] - shape_b * sin_theta - tmp;
+ poly[9] = poly[3] + 2.0 * tmp;
+
+ poly[4] = poly[2] * frac_height + vx * (1.0 - frac_height);
+ poly[5] = poly[3] * frac_height + vy * (1.0 - frac_height);
+ poly[6] = poly[8] * frac_height + vx * (1.0 - frac_height);
+ poly[7] = poly[9] * frac_height + vy * (1.0 - frac_height);
+
+ coords[2] = poly[0] - backup * cos_theta;
+ coords[3] = poly[1] - backup * sin_theta;
+ }
+}
+
+/* Convenience function to set the line's GC's foreground color */
+static void
+set_line_gc_foreground (FooCanvasLine *line)
+{
+ GdkColor c;
+
+ if (!line->gc)
+ return;
+
+ c.pixel = line->fill_pixel;
+ gdk_gc_set_foreground (line->gc, &c);
+}
+
+/* Recalculate the line's width and set it in its GC */
+static void
+set_line_gc_width (FooCanvasLine *line)
+{
+ int width;
+
+ if (!line->gc)
+ return;
+
+ if (line->width_pixels)
+ width = (int) line->width;
+ else
+ width = (int) (line->width * line->item.canvas->pixels_per_unit + 0.5);
+
+ gdk_gc_set_line_attributes (line->gc,
+ width,
+ line->line_style,
+ (line->first_arrow || line->last_arrow) ? GDK_CAP_BUTT : line->cap,
+ line->join);
+}
+
+/* Sets the stipple pattern for the line */
+static void
+set_stipple (FooCanvasLine *line, GdkBitmap *stipple, int reconfigure)
+{
+ if (line->stipple && !reconfigure)
+ g_object_unref (line->stipple);
+
+ line->stipple = stipple;
+ if (stipple && !reconfigure)
+ g_object_ref (stipple);
+
+ if (line->gc) {
+ if (stipple) {
+ gdk_gc_set_stipple (line->gc, stipple);
+ gdk_gc_set_fill (line->gc, GDK_STIPPLED);
+ } else
+ gdk_gc_set_fill (line->gc, GDK_SOLID);
+ }
+}
+
+static void
+foo_canvas_line_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ FooCanvasItem *item;
+ FooCanvasLine *line;
+ FooCanvasPoints *points;
+ GdkColor color = { 0, 0, 0, 0, };
+ GdkColor *pcolor;
+ gboolean color_changed;
+ int have_pixel;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_LINE (object));
+
+ item = FOO_CANVAS_ITEM (object);
+ line = FOO_CANVAS_LINE (object);
+
+ color_changed = FALSE;
+ have_pixel = FALSE;
+
+ switch (param_id) {
+ case PROP_POINTS:
+ points = g_value_get_boxed (value);
+
+ if (line->coords) {
+ g_free (line->coords);
+ line->coords = NULL;
+ }
+
+ if (!points)
+ line->num_points = 0;
+ else {
+ line->num_points = points->num_points;
+ line->coords = g_new (double, 2 * line->num_points);
+ memcpy (line->coords, points->coords, 2 * line->num_points * sizeof (double));
+ }
+
+ /* Drop the arrowhead polygons if they exist -- they will be regenerated */
+
+ if (line->first_coords) {
+ g_free (line->first_coords);
+ line->first_coords = NULL;
+ }
+
+ if (line->last_coords) {
+ g_free (line->last_coords);
+ line->last_coords = NULL;
+ }
+
+ /* Since the line's points have changed, we need to re-generate arrowheads in
+ * addition to recalculating the bounds.
+ */
+ line->line_smoothed = FALSE;
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_FILL_COLOR:
+ if (g_value_get_string (value))
+ gdk_color_parse (g_value_get_string (value), &color);
+ line->fill_rgba = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8 |
+ (color.blue & 0xff00) |
+ 0xff);
+ color_changed = TRUE;
+ break;
+
+ case PROP_FILL_COLOR_GDK:
+ pcolor = g_value_get_boxed (value);
+ if (pcolor) {
+ GdkColormap *colormap;
+ color = *pcolor;
+
+ colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+ gdk_rgb_find_color (colormap, &color);
+
+ have_pixel = TRUE;
+ }
+
+ line->fill_rgba = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8 |
+ (color.blue & 0xff00) |
+ 0xff);
+ color_changed = TRUE;
+ break;
+
+ case PROP_FILL_COLOR_RGBA:
+ line->fill_rgba = g_value_get_uint (value);
+ color_changed = TRUE;
+ break;
+
+ case PROP_FILL_STIPPLE:
+ set_stipple (line, (GdkBitmap *) g_value_get_object (value), FALSE);
+ foo_canvas_item_request_redraw (item);
+ break;
+
+ case PROP_WIDTH_PIXELS:
+ line->width = g_value_get_uint (value);
+ line->width_pixels = TRUE;
+ set_line_gc_width (line);
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIDTH_UNITS:
+ line->width = fabs (g_value_get_double (value));
+ line->width_pixels = FALSE;
+ set_line_gc_width (line);
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_CAP_STYLE:
+ line->cap = g_value_get_enum (value);
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_JOIN_STYLE:
+ line->join = g_value_get_enum (value);
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_LINE_STYLE:
+ line->line_style = g_value_get_enum (value);
+ set_line_gc_width (line);
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_FIRST_ARROWHEAD:
+ line->first_arrow = g_value_get_boolean (value);
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_LAST_ARROWHEAD:
+ line->last_arrow = g_value_get_boolean (value);
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_SMOOTH:
+ line->smooth = g_value_get_boolean (value);
+ line->line_smoothed = FALSE;
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_SPLINE_STEPS:
+ line->spline_steps = g_value_get_int (value);
+ line->line_smoothed = FALSE;
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_ARROW_SHAPE_A:
+ line->shape_a = fabs (g_value_get_double (value));
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_ARROW_SHAPE_B:
+ line->shape_b = fabs (g_value_get_double (value));
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_ARROW_SHAPE_C:
+ line->shape_c = fabs (g_value_get_double (value));
+ foo_canvas_item_request_update (item);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+
+ if (color_changed) {
+ if (have_pixel)
+ line->fill_pixel = color.pixel;
+ else
+ line->fill_pixel = foo_canvas_get_color_pixel (item->canvas,
+ line->fill_rgba);
+
+ set_line_gc_foreground (line);
+
+ foo_canvas_item_request_redraw (item);
+ }
+}
+
+/* Returns a copy of the line's points without the endpoint adjustments for
+ * arrowheads.
+ */
+static FooCanvasPoints *
+get_points (FooCanvasLine *line)
+{
+ FooCanvasPoints *points;
+ int start_ofs, end_ofs;
+
+ if (line->num_points == 0)
+ return NULL;
+
+ start_ofs = end_ofs = 0;
+
+ points = foo_canvas_points_new (line->num_points);
+
+ /* Invariant: if first_coords or last_coords exist, then the line's
+ * endpoints have been adjusted.
+ */
+
+ if (line->first_coords) {
+ start_ofs = 1;
+
+ points->coords[0] = line->first_coords[0];
+ points->coords[1] = line->first_coords[1];
+ }
+
+ if (line->last_coords) {
+ end_ofs = 1;
+
+ points->coords[2 * (line->num_points - 1)] = line->last_coords[0];
+ points->coords[2 * (line->num_points - 1) + 1] = line->last_coords[1];
+ }
+
+ memcpy (points->coords + 2 * start_ofs,
+ line->coords + 2 * start_ofs,
+ 2 * (line->num_points - (start_ofs + end_ofs)) * sizeof (double));
+
+ return points;
+}
+
+static void
+foo_canvas_line_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ FooCanvasLine *line;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_LINE (object));
+
+ line = FOO_CANVAS_LINE (object);
+
+ switch (param_id) {
+ case PROP_POINTS:
+ g_value_set_boxed (value, get_points (line));
+ break;
+
+ case PROP_FILL_COLOR:
+ g_value_take_string (value,
+ g_strdup_printf ("#%02x%02x%02x",
+ line->fill_rgba >> 24,
+ (line->fill_rgba >> 16) & 0xff,
+ (line->fill_rgba >> 8) & 0xff));
+ break;
+
+ case PROP_FILL_COLOR_GDK: {
+ FooCanvas *canvas = FOO_CANVAS_ITEM (line)->canvas;
+ GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+ GdkColor color;
+
+ gdk_colormap_query_color (colormap, line->fill_pixel, &color);
+ g_value_set_boxed (value, &color);
+ break;
+ }
+
+ case PROP_FILL_COLOR_RGBA:
+ g_value_set_uint (value, line->fill_rgba);
+ break;
+
+ case PROP_FILL_STIPPLE:
+ g_value_set_object (value, line->stipple);
+ break;
+
+ case PROP_WIDTH_PIXELS:
+ g_value_set_uint (value, line->width);
+ break;
+
+ case PROP_WIDTH_UNITS:
+ g_value_set_double (value, line->width);
+ break;
+
+ case PROP_CAP_STYLE:
+ g_value_set_enum (value, line->cap);
+ break;
+
+ case PROP_JOIN_STYLE:
+ g_value_set_enum (value, line->join);
+ break;
+
+ case PROP_LINE_STYLE:
+ g_value_set_enum (value, line->line_style);
+ break;
+
+ case PROP_FIRST_ARROWHEAD:
+ g_value_set_boolean (value, line->first_arrow);
+ break;
+
+ case PROP_LAST_ARROWHEAD:
+ g_value_set_boolean (value, line->last_arrow);
+ break;
+
+ case PROP_SMOOTH:
+ g_value_set_boolean (value, line->smooth);
+ break;
+
+ case PROP_SPLINE_STEPS:
+ g_value_set_uint (value, line->spline_steps);
+ break;
+
+ case PROP_ARROW_SHAPE_A:
+ g_value_set_double (value, line->shape_a);
+ break;
+
+ case PROP_ARROW_SHAPE_B:
+ g_value_set_double (value, line->shape_b);
+ break;
+
+ case PROP_ARROW_SHAPE_C:
+ g_value_set_double (value, line->shape_c);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+foo_canvas_line_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
+{
+ FooCanvasLine *line;
+ double x1, y1, x2, y2;
+
+ line = FOO_CANVAS_LINE (item);
+
+ if (parent_class->update)
+ (* parent_class->update) (item, i2w_dx, i2w_dy, flags);
+
+ reconfigure_arrows (line);
+
+ set_line_gc_foreground (line);
+ set_line_gc_width (line);
+ set_stipple (line, line->stipple, TRUE);
+
+ get_bounds_canvas (line, &x1, &y1, &x2, &y2, i2w_dx, i2w_dy);
+ foo_canvas_update_bbox (item, x1, y1, x2, y2);
+}
+
+static void
+foo_canvas_line_realize (FooCanvasItem *item)
+{
+ FooCanvasLine *line;
+
+ line = FOO_CANVAS_LINE (item);
+
+ if (parent_class->realize)
+ (* parent_class->realize) (item);
+
+ line->gc = gdk_gc_new (item->canvas->layout.bin_window);
+/* FIXME FIXME FIXME Need to recalc pixel values, set colours, etc. */
+
+#if 0
+ (* FOO_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0);
+#endif
+}
+
+static void
+foo_canvas_line_unrealize (FooCanvasItem *item)
+{
+ FooCanvasLine *line;
+
+ line = FOO_CANVAS_LINE (item);
+
+ g_object_unref (line->gc);
+ line->gc = NULL;
+
+ if (parent_class->unrealize)
+ (* parent_class->unrealize) (item);
+}
+
+static void
+item_to_canvas (FooCanvas *canvas, double *item_coords, GdkPoint *canvas_coords, int num_points,
+ int *num_drawn_points, double i2w_dx, double i2w_dy)
+{
+ int i;
+ int old_cx, old_cy;
+ int cx, cy;
+
+ /* the first point is always drawn */
+ foo_canvas_w2c (canvas,
+ item_coords[0] + i2w_dx,
+ item_coords[1] + i2w_dy,
+ &canvas_coords->x, &canvas_coords->y);
+ old_cx = canvas_coords->x;
+ old_cy = canvas_coords->y;
+ canvas_coords++;
+ *num_drawn_points = 1;
+
+ for (i = 1; i < num_points; i++) {
+ foo_canvas_w2c (canvas,
+ item_coords[i*2] + i2w_dx,
+ item_coords[i*2+1] + i2w_dy,
+ &cx, &cy);
+ if (old_cx != cx || old_cy != cy) {
+ canvas_coords->x = cx;
+ canvas_coords->y = cy;
+ old_cx = cx;
+ old_cy = cy;
+ canvas_coords++;
+ (*num_drawn_points)++;
+ }
+ }
+}
+
+/**
+ * Implements a cubic B-curve. 4 consecutive points are taken at a time to
+ * build the curve segments. Any remaining points at the end that don't form
+ * a curve are just joined as line segments.
+ *
+ * The process modifies the line points of the canvas item. So after this, the
+ * points are no longer what user originally supplied. The curve is computed
+ * once before a draw and reused, until influencial properties are modified
+ * (points, smooth, spline_steps properties of the canvas item). So, make sure
+ * when these properties are modified, line->line_smoothed is reset to FALSE
+ * so that next draw would recalculate it.
+ *
+ * Algorithm: see http://en.wikipedia.org/wiki/Bezier_curve
+ */
+static void
+foo_canvas_line_smooth (FooCanvasLine *line)
+{
+ gint curr_point = 0;
+ gint num_points = 0;
+ gdouble *line_points;
+
+ if (line->line_smoothed)
+ return;
+
+ if (!line->smooth || line->num_points <= 2)
+ {
+ line->line_smoothed = TRUE;
+ return;
+ }
+
+ line_points = g_new (gdouble, 2 * ((line->num_points - 1) * line->spline_steps));
+ while (curr_point < (line->num_points - 3))
+ {
+ gint step;
+ gdouble p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y;
+ p0_x = line->coords[2 * curr_point];
+ p0_y = line->coords[2 * curr_point + 1];
+ p1_x = line->coords[2 * (curr_point + 1)];
+ p1_y = line->coords[2 * (curr_point + 1) + 1];
+ p2_x = line->coords[2 * (curr_point + 2)];
+ p2_y = line->coords[2 * (curr_point + 2) + 1];
+ p3_x = line->coords[2 * (curr_point + 3)];
+ p3_y = line->coords[2 * (curr_point + 3) + 1];
+ for (step = 0; step < line->spline_steps; step++)
+ {
+ gdouble q0_x, q0_y, q1_x, q1_y, q2_x, q2_y;
+ gdouble r0_x, r0_y, r1_x, r1_y;
+
+ q0_x = p0_x + (p1_x - p0_x) * step / line->spline_steps;
+ q0_y = p0_y + (p1_y - p0_y) * step / line->spline_steps;
+ q1_x = p1_x + (p2_x - p1_x) * step / line->spline_steps;
+ q1_y = p1_y + (p2_y - p1_y) * step / line->spline_steps;
+ q2_x = p2_x + (p3_x - p2_x) * step / line->spline_steps;
+ q2_y = p2_y + (p3_y - p2_y) * step / line->spline_steps;
+
+ r0_x = q0_x + (q1_x - q0_x) * step / line->spline_steps;
+ r0_y = q0_y + (q1_y - q0_y) * step / line->spline_steps;
+ r1_x = q1_x + (q2_x - q1_x) * step / line->spline_steps;
+ r1_y = q1_y + (q2_y - q1_y) * step / line->spline_steps;
+
+ line_points[2 * num_points] = r0_x + (r1_x - r0_x) * step / line->spline_steps;
+ line_points[2 * num_points + 1] = r0_y + (r1_y - r0_y) * step / line->spline_steps;
+ num_points++;
+ }
+ curr_point += 3;
+ }
+ for (; curr_point < line->num_points; curr_point++)
+ {
+ line_points[2 * num_points] = line->coords[2 * curr_point];
+ line_points[2 * num_points + 1] = line->coords[2 * curr_point + 1];
+ num_points++;
+ }
+ line->line_smoothed = TRUE;
+ g_free (line->coords);
+ line->coords = line_points;
+ line->num_points = num_points;
+}
+
+static void
+foo_canvas_line_draw (FooCanvasItem *item, GdkDrawable *drawable,
+ GdkEventExpose *event)
+{
+ FooCanvasLine *line;
+ GdkPoint static_points[NUM_STATIC_POINTS];
+ GdkPoint *points;
+ int actual_num_points_drawn;
+ double i2w_dx, i2w_dy;
+
+ line = FOO_CANVAS_LINE (item);
+
+ if (line->num_points == 0)
+ return;
+
+ /* Smooth the line, if not yet done */
+ foo_canvas_line_smooth (FOO_CANVAS_LINE (line));
+
+ /* Build array of canvas pixel coordinates */
+
+ if (line->num_points <= NUM_STATIC_POINTS)
+ points = static_points;
+ else
+ points = g_new (GdkPoint, line->num_points);
+
+ i2w_dx = 0.0;
+ i2w_dy = 0.0;
+ foo_canvas_item_i2w (item, &i2w_dx, &i2w_dy);
+
+ item_to_canvas (item->canvas, line->coords, points, line->num_points,
+ &actual_num_points_drawn, i2w_dx, i2w_dy);
+
+ if (line->stipple)
+ foo_canvas_set_stipple_origin (item->canvas, line->gc);
+
+ gdk_draw_lines (drawable, line->gc, points, actual_num_points_drawn);
+
+ if (points != static_points)
+ g_free (points);
+
+ /* Draw arrowheads */
+
+ points = static_points;
+
+ if (line->first_arrow) {
+ item_to_canvas (item->canvas, line->first_coords, points, NUM_ARROW_POINTS,
+ &actual_num_points_drawn, i2w_dx, i2w_dy);
+ gdk_draw_polygon (drawable, line->gc, TRUE, points, actual_num_points_drawn );
+ }
+
+ if (line->last_arrow) {
+ item_to_canvas (item->canvas, line->last_coords, points, NUM_ARROW_POINTS,
+ &actual_num_points_drawn, i2w_dx, i2w_dy);
+ gdk_draw_polygon (drawable, line->gc, TRUE, points, actual_num_points_drawn );
+ }
+}
+
+static double
+foo_canvas_line_point (FooCanvasItem *item, double x, double y,
+ int cx, int cy, FooCanvasItem **actual_item)
+{
+ FooCanvasLine *line;
+ double *line_points = NULL, *coords;
+ double static_points[2 * NUM_STATIC_POINTS];
+ double poly[10];
+ double best, dist;
+ double dx, dy;
+ double width;
+ int num_points = 0, i;
+ int changed_miter_to_bevel;
+
+#ifdef VERBOSE
+ g_print ("foo_canvas_line_point x, y = (%g, %g); cx, cy = (%d, %d)\n", x, y, cx, cy);
+#endif
+
+ line = FOO_CANVAS_LINE (item);
+
+ *actual_item = item;
+
+ best = 1.0e36;
+
+ /* Handle smoothed lines by generating an expanded set ot points */
+
+ if (line->smooth && (line->num_points > 2)) {
+ /* FIXME */
+ } else {
+ num_points = line->num_points;
+ line_points = line->coords;
+ }
+
+ /* Compute a polygon for each edge of the line and test the point against it. The effective
+ * width of the line is adjusted so that it will be at least one pixel thick (so that zero
+ * pixel-wide lines can be pickedup as well).
+ */
+
+ if (line->width_pixels)
+ width = line->width / item->canvas->pixels_per_unit;
+ else
+ width = line->width;
+
+ if (width < (1.0 / item->canvas->pixels_per_unit))
+ width = 1.0 / item->canvas->pixels_per_unit;
+
+ changed_miter_to_bevel = 0;
+
+ for (i = num_points, coords = line_points; i >= 2; i--, coords += 2) {
+ /* If rounding is done around the first point, then compute distance between the
+ * point and the first point.
+ */
+
+ if (((line->cap == GDK_CAP_ROUND) && (i == num_points))
+ || ((line->join == GDK_JOIN_ROUND) && (i != num_points))) {
+ dx = coords[0] - x;
+ dy = coords[1] - y;
+ dist = sqrt (dx * dx + dy * dy) - width / 2.0;
+ if (dist < FOO_CANVAS_EPSILON) {
+ best = 0.0;
+ goto done;
+ } else if (dist < best)
+ best = dist;
+ }
+
+ /* Compute the polygonal shape corresponding to this edge, with two points for the
+ * first point of the edge and two points for the last point of the edge.
+ */
+
+ if (i == num_points)
+ foo_canvas_get_butt_points (coords[2], coords[3], coords[0], coords[1],
+ width, (line->cap == GDK_CAP_PROJECTING),
+ poly, poly + 1, poly + 2, poly + 3);
+ else if ((line->join == GDK_JOIN_MITER) && !changed_miter_to_bevel) {
+ poly[0] = poly[6];
+ poly[1] = poly[7];
+ poly[2] = poly[4];
+ poly[3] = poly[5];
+ } else {
+ foo_canvas_get_butt_points (coords[2], coords[3], coords[0], coords[1],
+ width, FALSE,
+ poly, poly + 1, poly + 2, poly + 3);
+
+ /* If this line uses beveled joints, then check the distance to a polygon
+ * comprising the last two points of the previous polygon and the first two
+ * from this polygon; this checks the wedges that fill the mitered point.
+ */
+
+ if ((line->join == GDK_JOIN_BEVEL) || changed_miter_to_bevel) {
+ poly[8] = poly[0];
+ poly[9] = poly[1];
+
+ dist = foo_canvas_polygon_to_point (poly, 5, x, y);
+ if (dist < FOO_CANVAS_EPSILON) {
+ best = 0.0;
+ goto done;
+ } else if (dist < best)
+ best = dist;
+
+ changed_miter_to_bevel = FALSE;
+ }
+ }
+
+ if (i == 2)
+ foo_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
+ width, (line->cap == GDK_CAP_PROJECTING),
+ poly + 4, poly + 5, poly + 6, poly + 7);
+ else if (line->join == GDK_JOIN_MITER) {
+ if (!foo_canvas_get_miter_points (coords[0], coords[1],
+ coords[2], coords[3],
+ coords[4], coords[5],
+ width,
+ poly + 4, poly + 5, poly + 6, poly + 7)) {
+ changed_miter_to_bevel = TRUE;
+ foo_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
+ width, FALSE,
+ poly + 4, poly + 5, poly + 6, poly + 7);
+ }
+ } else
+ foo_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
+ width, FALSE,
+ poly + 4, poly + 5, poly + 6, poly + 7);
+
+ poly[8] = poly[0];
+ poly[9] = poly[1];
+
+ dist = foo_canvas_polygon_to_point (poly, 5, x, y);
+ if (dist < FOO_CANVAS_EPSILON) {
+ best = 0.0;
+ goto done;
+ } else if (dist < best)
+ best = dist;
+ }
+
+ /* If caps are rounded, check the distance to the cap around the final end point of the line */
+
+ if (line->cap == GDK_CAP_ROUND) {
+ dx = coords[0] - x;
+ dy = coords[1] - y;
+ dist = sqrt (dx * dx + dy * dy) - width / 2.0;
+ if (dist < FOO_CANVAS_EPSILON) {
+ best = 0.0;
+ goto done;
+ } else
+ best = dist;
+ }
+
+ /* sometimes the FooCanvasItem::update signal will not have
+ been processed between deleting the arrow points and a call
+ to this routine -- this can cause a segfault here */
+ if ((line->first_arrow && !line->first_coords) ||
+ (line->last_arrow && !line->last_coords))
+ reconfigure_arrows(line);
+
+ /* If there are arrowheads, check the distance to them */
+
+ if (line->first_arrow) {
+ dist = foo_canvas_polygon_to_point (line->first_coords, NUM_ARROW_POINTS, x, y);
+ if (dist < FOO_CANVAS_EPSILON) {
+ best = 0.0;
+ goto done;
+ } else
+ best = dist;
+ }
+
+ if (line->last_arrow) {
+ dist = foo_canvas_polygon_to_point (line->last_coords, NUM_ARROW_POINTS, x, y);
+ if (dist < FOO_CANVAS_EPSILON) {
+ best = 0.0;
+ goto done;
+ } else
+ best = dist;
+ }
+
+done:
+
+ if ((line_points != static_points) && (line_points != line->coords))
+ g_free (line_points);
+
+ return best;
+}
+
+static void
+foo_canvas_line_translate (FooCanvasItem *item, double dx, double dy)
+{
+ FooCanvasLine *line;
+ int i;
+ double *coords;
+
+ line = FOO_CANVAS_LINE (item);
+
+ for (i = 0, coords = line->coords; i < line->num_points; i++, coords += 2) {
+ coords[0] += dx;
+ coords[1] += dy;
+ }
+
+ if (line->first_arrow)
+ for (i = 0, coords = line->first_coords; i < NUM_ARROW_POINTS; i++, coords += 2) {
+ coords[0] += dx;
+ coords[1] += dy;
+ }
+
+ if (line->last_arrow)
+ for (i = 0, coords = line->last_coords; i < NUM_ARROW_POINTS; i++, coords += 2) {
+ coords[0] += dx;
+ coords[1] += dy;
+ }
+}
+
+static void
+foo_canvas_line_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ FooCanvasLine *line;
+
+ line = FOO_CANVAS_LINE (item);
+
+ if (line->num_points == 0) {
+ *x1 = *y1 = *x2 = *y2 = 0.0;
+ return;
+ }
+
+ get_bounds (line, x1, y1, x2, y2);
+}
diff --git a/libfoocanvas/foo-canvas-line.h b/libfoocanvas/foo-canvas-line.h
new file mode 100644
index 0000000..f77c065
--- /dev/null
+++ b/libfoocanvas/foo-canvas-line.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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@
+ */
+
+/* Line/curve item type for FooCanvas widget
+ *
+ * FooCanvas 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 <federico nuclecu unam mx>
+ */
+
+#ifndef FOO_CANVAS_LINE_H
+#define FOO_CANVAS_LINE_H
+
+
+#include <libfoocanvas/foo-canvas.h>
+
+
+G_BEGIN_DECLS
+
+
+/* Line item for the canvas. This is a polyline with configurable width, cap/join styles, and arrowheads.
+ * If arrowheads are enabled, then three values are used to specify their shape:
+ *
+ * arrow_shape_a: Distance from tip of arrowhead to the center point.
+ * arrow_shape_b: Distance from tip of arrowhead to trailing point, measured along the shaft.
+ * arrow_shape_c: Distance of trailing point from outside edge of shaft.
+ *
+ * The following object arguments are available:
+ *
+ * name type read/write description
+ * ------------------------------------------------------------------------------------------
+ * points FooCanvasPoints* RW Pointer to a FooCanvasPoints structure.
+ * This can be created by a call to
+ * foo_canvas_points_new() (in foo-canvas-util.h).
+ * X coordinates are in the even indices of the
+ * points->coords array, Y coordinates are in
+ * the odd indices.
+ * fill_color string W X color specification for line
+ * fill_color_gdk GdkColor* RW Pointer to an allocated GdkColor
+ * fill_stipple GdkBitmap* RW Stipple pattern for the line
+ * width_pixels uint R Width of the line in pixels. The line width
+ * will not be scaled when the canvas zoom factor changes.
+ * width_units double R Width of the line in canvas units. The line width
+ * will be scaled when the canvas zoom factor changes.
+ * cap_style GdkCapStyle RW Cap ("endpoint") style for the line.
+ * join_style GdkJoinStyle RW Join ("vertex") style for the line.
+ * line_style GdkLineStyle RW Line dash style
+ * first_arrowhead boolean RW Specifies whether to draw an arrowhead on the
+ * first point of the line.
+ * last_arrowhead boolean RW Specifies whether to draw an arrowhead on the
+ * last point of the line.
+ * smooth boolean RW Specifies whether to smooth the line using
+ * parabolic splines.
+ * spline_steps uint RW Specifies the number of steps to use when rendering curves.
+ * arrow_shape_a double RW First arrow shape specifier.
+ * arrow_shape_b double RW Second arrow shape specifier.
+ * arrow_shape_c double RW Third arrow shape specifier.
+ */
+
+
+#define FOO_TYPE_CANVAS_LINE (foo_canvas_line_get_type ())
+#define FOO_CANVAS_LINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_LINE, FooCanvasLine))
+#define FOO_CANVAS_LINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_LINE, FooCanvasLineClass))
+#define FOO_IS_CANVAS_LINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_LINE))
+#define FOO_IS_CANVAS_LINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_LINE))
+#define FOO_CANVAS_LINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_LINE, FooCanvasLineClass))
+
+
+typedef struct _FooCanvasLine FooCanvasLine;
+typedef struct _FooCanvasLineClass FooCanvasLineClass;
+
+struct _FooCanvasLine {
+ FooCanvasItem item;
+
+ double *coords; /* Array of coordinates for the line's points. X coords are in the
+ * even indices, Y coords are in the odd indices. If the line has
+ * arrowheads then the first and last points have been adjusted to
+ * refer to the necks of the arrowheads rather than their tips. The
+ * actual endpoints are stored in the first_arrow and last_arrow
+ * arrays, if they exist.
+ */
+
+ double *first_coords; /* Array of points describing polygon for the first arrowhead */
+ double *last_coords; /* Array of points describing polygon for the last arrowhead */
+
+ GdkGC *gc; /* GC for drawing line */
+
+ GdkBitmap *stipple; /* Stipple pattern */
+
+ double width; /* Width of the line */
+
+ double shape_a; /* Distance from tip of arrowhead to center */
+ double shape_b; /* Distance from tip of arrowhead to trailing point, measured along shaft */
+ double shape_c; /* Distance of trailing points from outside edge of shaft */
+
+ GdkCapStyle cap; /* Cap style for line */
+ GdkJoinStyle join; /* Join style for line */
+ GdkLineStyle line_style;/* Style for the line */
+
+ gulong fill_pixel; /* Color for line */
+
+ guint32 fill_rgba; /* RGBA color for outline */ /*AA*/
+
+ int num_points; /* Number of points in the line */
+ guint fill_color; /* Fill color, RGBA */
+
+ int spline_steps; /* Number of steps in each spline segment */
+
+ guint width_pixels : 1; /* Is the width specified in pixels or units? */
+ guint first_arrow : 1; /* Draw first arrowhead? */
+ guint last_arrow : 1; /* Draw last arrowhead? */
+ guint smooth : 1; /* Smooth line (with parabolic splines)? */
+ gboolean line_smoothed : 1; /* If smooth == TRUE, has the line been smoothened */
+};
+
+struct _FooCanvasLineClass {
+ FooCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_line_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libfoocanvas/foo-canvas-marshal.list b/libfoocanvas/foo-canvas-marshal.list
new file mode 100644
index 0000000..86f67df
--- /dev/null
+++ b/libfoocanvas/foo-canvas-marshal.list
@@ -0,0 +1,2 @@
+VOID:INT,INT,INT,INT
+BOOLEAN:BOXED
diff --git a/libfoocanvas/foo-canvas-pixbuf.c b/libfoocanvas/foo-canvas-pixbuf.c
new file mode 100644
index 0000000..0816b07
--- /dev/null
+++ b/libfoocanvas/foo-canvas-pixbuf.c
@@ -0,0 +1,825 @@
+/* GNOME libraries - GdkPixbuf item for the GNOME canvas
+ *
+ * Copyright (C) 1999 The Free Software Foundation
+ *
+ * Author: Federico Mena-Quintero <federico gimp org>
+ *
+ * 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 <config.h>
+#include <math.h>
+#include <libfoocanvas/foo-canvas.h>
+#include <libfoocanvas/foo-canvas-util.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include "foo-canvas-pixbuf.h"
+
+/* Private part of the FooCanvasPixbuf structure */
+typedef struct {
+ /* Our gdk-pixbuf */
+ GdkPixbuf *pixbuf, *pixbuf_scaled;
+
+ /* Width value */
+ double width;
+
+ /* Height value */
+ double height;
+
+ /* X translation */
+ double x;
+
+ /* Y translation */
+ double y;
+
+ /* Whether dimensions are set and whether they are in pixels or units */
+ guint width_set : 1;
+ guint width_in_pixels : 1;
+ guint height_set : 1;
+ guint height_in_pixels : 1;
+ guint x_in_pixels : 1;
+ guint y_in_pixels : 1;
+
+ /* Whether the pixbuf has changed */
+ guint need_pixbuf_update : 1;
+
+ /* Whether the transformation or size have changed */
+ guint need_xform_update : 1;
+
+ /* Should the point method ignore transparent areas */
+ guint point_ignores_alpha : 1;
+
+ /* Anchor */
+ GtkAnchorType anchor;
+
+ /* Approximation method used for transformations */
+ GdkInterpType interp_type;
+
+} PixbufPrivate;
+
+/* Object argument IDs */
+enum {
+ PROP_0,
+ PROP_PIXBUF,
+ PROP_WIDTH,
+ PROP_WIDTH_SET,
+ PROP_WIDTH_IN_PIXELS,
+ PROP_HEIGHT,
+ PROP_HEIGHT_SET,
+ PROP_HEIGHT_IN_PIXELS,
+ PROP_X,
+ PROP_X_IN_PIXELS,
+ PROP_Y,
+ PROP_Y_IN_PIXELS,
+ PROP_ANCHOR,
+ PROP_INTERP_TYPE,
+ PROP_POINT_IGNORES_ALPHA
+};
+
+static void foo_canvas_pixbuf_class_init (FooCanvasPixbufClass *klass);
+static void foo_canvas_pixbuf_init (FooCanvasPixbuf *cpb);
+static void foo_canvas_pixbuf_destroy (GtkObject *object);
+static void foo_canvas_pixbuf_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void foo_canvas_pixbuf_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void foo_canvas_pixbuf_update (FooCanvasItem *item,
+ double i2w_dx, double i2w_dy,
+ int flags);
+static void foo_canvas_pixbuf_draw (FooCanvasItem *item, GdkDrawable *drawable,
+ GdkEventExpose *expose);
+static double foo_canvas_pixbuf_point (FooCanvasItem *item, double x, double y, int cx, int cy,
+ FooCanvasItem **actual_item);
+static void foo_canvas_pixbuf_translate (FooCanvasItem *item, double dx, double dy);
+static void foo_canvas_pixbuf_bounds (FooCanvasItem *item,
+ double *x1, double *y1, double *x2, double *y2);
+
+static FooCanvasItemClass *parent_class;
+
+G_DEFINE_TYPE (FooCanvasPixbuf, foo_canvas_pixbuf, FOO_TYPE_CANVAS_ITEM)
+
+/* Class initialization function for the pixbuf canvas item */
+static void
+foo_canvas_pixbuf_class_init (FooCanvasPixbufClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ FooCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) klass;
+ object_class = (GtkObjectClass *) klass;
+ item_class = (FooCanvasItemClass *) klass;
+
+ parent_class = g_type_class_peek (foo_canvas_item_get_type ());
+
+ gobject_class->set_property = foo_canvas_pixbuf_set_property;
+ gobject_class->get_property = foo_canvas_pixbuf_get_property;
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_PIXBUF,
+ g_param_spec_object ("pixbuf", NULL, NULL,
+ GDK_TYPE_PIXBUF,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDTH,
+ g_param_spec_double ("width", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDTH_SET,
+ g_param_spec_boolean ("width-set", NULL, NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDTH_IN_PIXELS,
+ g_param_spec_boolean ("width-in-pixels", NULL, NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_HEIGHT,
+ g_param_spec_double ("height", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_HEIGHT_SET,
+ g_param_spec_boolean ("height-set", NULL, NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_HEIGHT_IN_PIXELS,
+ g_param_spec_boolean ("height-in-pixels", NULL, NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X,
+ g_param_spec_double ("x", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X_IN_PIXELS,
+ g_param_spec_boolean ("x-in-pixels", NULL, NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y,
+ g_param_spec_double ("y", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y_IN_PIXELS,
+ g_param_spec_boolean ("y-in-pixels", NULL, NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_ANCHOR,
+ g_param_spec_enum ("anchor", NULL, NULL,
+ GTK_TYPE_ANCHOR_TYPE,
+ GTK_ANCHOR_NW,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_INTERP_TYPE,
+ g_param_spec_enum ("interp-type", NULL, NULL,
+ GDK_TYPE_INTERP_TYPE,
+ GDK_INTERP_BILINEAR,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_POINT_IGNORES_ALPHA,
+ g_param_spec_boolean ("point-ignores-alpha", NULL, NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ object_class->destroy = foo_canvas_pixbuf_destroy;
+
+ item_class->update = foo_canvas_pixbuf_update;
+ item_class->draw = foo_canvas_pixbuf_draw;
+ item_class->point = foo_canvas_pixbuf_point;
+ item_class->translate = foo_canvas_pixbuf_translate;
+ item_class->bounds = foo_canvas_pixbuf_bounds;
+}
+
+/* Object initialization function for the pixbuf canvas item */
+static void
+foo_canvas_pixbuf_init (FooCanvasPixbuf *gcp)
+{
+ PixbufPrivate *priv;
+
+ priv = g_new0 (PixbufPrivate, 1);
+ gcp->priv = priv;
+
+ priv->width = 0.0;
+ priv->height = 0.0;
+ priv->x = 0.0;
+ priv->y = 0.0;
+ priv->anchor = GTK_ANCHOR_NW;
+ priv->interp_type = GDK_INTERP_BILINEAR;
+ priv->point_ignores_alpha = FALSE;
+}
+
+/* Destroy handler for the pixbuf canvas item */
+static void
+foo_canvas_pixbuf_destroy (GtkObject *object)
+{
+ FooCanvasItem *item;
+ FooCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_PIXBUF (object));
+
+ item = FOO_CANVAS_ITEM (object);
+ gcp = (FOO_CANVAS_PIXBUF (object));
+ priv = gcp->priv;
+
+ /* remember, destroy can be run multiple times! */
+
+ if (priv) {
+ foo_canvas_item_request_redraw (item);
+
+ if (priv->pixbuf)
+ g_object_unref (priv->pixbuf);
+ if (priv->pixbuf_scaled)
+ g_object_unref (priv->pixbuf_scaled);
+
+ g_free (priv);
+ gcp->priv = NULL;
+ }
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+
+
+/* Set_property handler for the pixbuf canvas item */
+static void
+foo_canvas_pixbuf_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ FooCanvasItem *item;
+ FooCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+ GdkPixbuf *pixbuf;
+ double val;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_PIXBUF (object));
+
+ item = FOO_CANVAS_ITEM (object);
+ gcp = FOO_CANVAS_PIXBUF (object);
+ priv = gcp->priv;
+
+ switch (param_id) {
+ case PROP_PIXBUF:
+ if (g_value_get_object (value))
+ pixbuf = GDK_PIXBUF (g_value_get_object (value));
+ else
+ pixbuf = NULL;
+ if (pixbuf != priv->pixbuf) {
+ if (pixbuf) {
+ g_return_if_fail
+ (gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB);
+ g_return_if_fail
+ (gdk_pixbuf_get_n_channels (pixbuf) == 3
+ || gdk_pixbuf_get_n_channels (pixbuf) == 4);
+ g_return_if_fail
+ (gdk_pixbuf_get_bits_per_sample (pixbuf) == 8);
+
+ g_object_ref (pixbuf);
+ }
+
+ if (priv->pixbuf)
+ g_object_unref (priv->pixbuf);
+ priv->pixbuf = pixbuf;
+
+ if (priv->pixbuf_scaled) {
+ g_object_unref (priv->pixbuf_scaled);
+ priv->pixbuf_scaled = NULL;
+ }
+ }
+
+ priv->need_pixbuf_update = TRUE;
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIDTH:
+ val = g_value_get_double (value);
+ g_return_if_fail (val >= 0.0);
+ priv->width = val;
+ priv->need_xform_update = TRUE;
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIDTH_SET:
+ priv->width_set = g_value_get_boolean (value);
+ priv->need_xform_update = TRUE;
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIDTH_IN_PIXELS:
+ priv->width_in_pixels = g_value_get_boolean (value);
+ priv->need_xform_update = TRUE;
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_HEIGHT:
+ val = g_value_get_double (value);
+ g_return_if_fail (val >= 0.0);
+ priv->height = val;
+ priv->need_xform_update = TRUE;
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_HEIGHT_SET:
+ priv->height_set = g_value_get_boolean (value);
+ priv->need_xform_update = TRUE;
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_HEIGHT_IN_PIXELS:
+ priv->height_in_pixels = g_value_get_boolean (value);
+ priv->need_xform_update = TRUE;
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_X:
+ priv->x = g_value_get_double (value);
+ priv->need_xform_update = TRUE;
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_X_IN_PIXELS:
+ priv->x_in_pixels = g_value_get_boolean (value);
+ priv->need_xform_update = TRUE;
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_Y:
+ priv->y = g_value_get_double (value);
+ priv->need_xform_update = TRUE;
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_Y_IN_PIXELS:
+ priv->y_in_pixels = g_value_get_boolean (value);
+ priv->need_xform_update = TRUE;
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_ANCHOR:
+ priv->anchor = g_value_get_enum (value);
+ priv->need_xform_update = TRUE;
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_INTERP_TYPE:
+ priv->interp_type = g_value_get_enum (value);
+ priv->need_xform_update = TRUE;
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_POINT_IGNORES_ALPHA:
+ priv->point_ignores_alpha = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+/* Get_property handler for the pixbuf canvasi item */
+static void
+foo_canvas_pixbuf_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ FooCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_PIXBUF (object));
+
+ gcp = FOO_CANVAS_PIXBUF (object);
+ priv = gcp->priv;
+
+ switch (param_id) {
+ case PROP_PIXBUF:
+ g_value_set_object (value, G_OBJECT (priv->pixbuf));
+ break;
+
+ case PROP_WIDTH:
+ g_value_set_double (value, priv->width);
+ break;
+
+ case PROP_WIDTH_SET:
+ g_value_set_boolean (value, priv->width_set);
+ break;
+
+ case PROP_WIDTH_IN_PIXELS:
+ g_value_set_boolean (value, priv->width_in_pixels);
+ break;
+
+ case PROP_HEIGHT:
+ g_value_set_double (value, priv->height);
+ break;
+
+ case PROP_HEIGHT_SET:
+ g_value_set_boolean (value, priv->height_set);
+ break;
+
+ case PROP_HEIGHT_IN_PIXELS:
+ g_value_set_boolean (value, priv->height_in_pixels);
+ break;
+
+ case PROP_X:
+ g_value_set_double (value, priv->x);
+ break;
+
+ case PROP_X_IN_PIXELS:
+ g_value_set_boolean (value, priv->x_in_pixels);
+ break;
+
+ case PROP_Y:
+ g_value_set_double (value, priv->y);
+ break;
+
+ case PROP_Y_IN_PIXELS:
+ g_value_set_boolean (value, priv->y_in_pixels);
+ break;
+
+ case PROP_ANCHOR:
+ g_value_set_enum (value, priv->anchor);
+ break;
+
+ case PROP_INTERP_TYPE:
+ g_value_set_enum (value, priv->interp_type);
+ break;
+
+ case PROP_POINT_IGNORES_ALPHA:
+ g_value_set_boolean (value, priv->point_ignores_alpha);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+
+
+/* Bounds and utilities */
+
+
+/* Recomputes the bounding box of a pixbuf canvas item. The horizontal and
+ * vertical dimensions may be specified in units or pixels, separately, so we
+ * have to compute the components individually for each dimension.
+ *
+ * Returns the coordinates with respect to the parent items coordinates.
+ */
+static void
+compute_bounding_box (FooCanvasPixbuf *gcp,
+ double i2w_dx, double i2w_dy,
+ double *bbox_x0, double *bbox_y0,
+ double *bbox_x1, double *bbox_y1)
+{
+ FooCanvasItem *item;
+ PixbufPrivate *priv;
+ double x, y;
+ double width, height;
+
+ item = FOO_CANVAS_ITEM (gcp);
+ priv = gcp->priv;
+
+ if (!priv->pixbuf) {
+ *bbox_x0 = *bbox_y0 = *bbox_x1 = *bbox_y1 = 0.0;
+ return;
+ }
+
+ if (priv->x_in_pixels) {
+ x = i2w_dx + priv->x / item->canvas->pixels_per_unit;
+ } else {
+ x = i2w_dx + priv->x;
+ }
+
+ if (priv->y_in_pixels) {
+ y = i2w_dy + priv->y / item->canvas->pixels_per_unit;
+ } else {
+ y = i2w_dy + priv->y;
+ }
+
+ if (priv->width_set) {
+ width = priv->width;
+ } else {
+ width = gdk_pixbuf_get_width (priv->pixbuf);
+ }
+
+ if (priv->width_in_pixels)
+ width /= item->canvas->pixels_per_unit;
+
+ if (priv->height_set) {
+ height = priv->height;
+ } else {
+ height = gdk_pixbuf_get_height (priv->pixbuf);
+ }
+
+ if (priv->height_in_pixels)
+ height /= item->canvas->pixels_per_unit;
+
+
+ switch (priv->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_SW:
+ break;
+
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_S:
+ x -= width / 2.0;
+ break;
+
+ case GTK_ANCHOR_NE:
+ case GTK_ANCHOR_E:
+ case GTK_ANCHOR_SE:
+ x -= width;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (priv->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_NE:
+ break;
+
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_E:
+ y -= height / 2.0;
+ break;
+
+ case GTK_ANCHOR_SW:
+ case GTK_ANCHOR_S:
+ case GTK_ANCHOR_SE:
+ y -= height;
+ break;
+
+ default:
+ break;
+ }
+
+ *bbox_x0 = x;
+ *bbox_y0 = y;
+ *bbox_x1 = x + width;
+ *bbox_y1 = y + height;
+}
+
+
+
+/* Update sequence */
+
+/* Update handler for the pixbuf canvas item */
+static void
+foo_canvas_pixbuf_update (FooCanvasItem *item,
+ double i2w_dx, double i2w_dy,
+ int flags)
+{
+ FooCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+ double bbox_x0, bbox_y0, bbox_x1, bbox_y1;
+ int w, h;
+
+ gcp = FOO_CANVAS_PIXBUF (item);
+ priv = gcp->priv;
+
+ if (parent_class->update)
+ (* parent_class->update) (item, i2w_dx, i2w_dy, flags);
+
+ /* If we need a pixbuf update, or if the item changed visibility to
+ * shown, recompute the bounding box.
+ */
+ if (priv->need_pixbuf_update || priv->need_xform_update ||
+ (flags & FOO_CANVAS_UPDATE_DEEP)) {
+
+ foo_canvas_item_request_redraw (item);
+
+ compute_bounding_box (gcp, i2w_dx, i2w_dy,
+ &bbox_x0, &bbox_y0,
+ &bbox_x1, &bbox_y1);
+
+ foo_canvas_w2c_d (item->canvas,
+ bbox_x0, bbox_y0,
+ &item->x1, &item->y1);
+
+ foo_canvas_w2c_d (item->canvas,
+ bbox_x1, bbox_y1,
+ &item->x2, &item->y2);
+
+ item->x1 = floor (item->x1 + .5);
+ item->y1 = floor (item->y1 + .5);
+ item->x2 = floor (item->x2 + .5);
+ item->y2 = floor (item->y2 + .5);
+
+#ifdef FOO_CANVAS_PIXBUF_VERBOSE
+ g_print ("BBox is %g %g %g %g\n", item->x1, item->y1, item->x2, item->y2);
+#endif
+
+ if (priv->pixbuf) {
+ w = item->x2 - item->x1;
+ h = item->y2 - item->y1;
+
+ if (priv->pixbuf_scaled)
+ g_object_unref (priv->pixbuf_scaled);
+ if (gdk_pixbuf_get_width (priv->pixbuf) != w ||
+ gdk_pixbuf_get_height (priv->pixbuf) != h)
+ priv->pixbuf_scaled = gdk_pixbuf_scale_simple (
+ priv->pixbuf, w, h, priv->interp_type);
+ else
+ priv->pixbuf_scaled = g_object_ref (priv->pixbuf);
+ }
+
+ foo_canvas_item_request_redraw (item);
+
+ priv->need_pixbuf_update = FALSE;
+ priv->need_xform_update = FALSE;
+ }
+}
+
+
+
+/* Draw handler for the pixbuf canvas item */
+static void
+foo_canvas_pixbuf_draw (FooCanvasItem *item, GdkDrawable *drawable,
+ GdkEventExpose *expose)
+{
+ FooCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+ GdkRectangle display_rect, draw_rect;
+ GdkRegion *draw_region;
+ int w, h;
+
+ gcp = FOO_CANVAS_PIXBUF (item);
+ priv = gcp->priv;
+
+ if (!priv->pixbuf)
+ return;
+
+ /* Compute the area we need to repaint */
+
+ w = item->x2 - item->x1;
+ h = item->y2 - item->y1;
+
+ display_rect.x = item->x1;
+ display_rect.y = item->y1;
+ display_rect.width = w;
+ display_rect.height = h;
+ draw_region = gdk_region_rectangle (&display_rect);
+ gdk_region_intersect (draw_region, expose->region);
+ if (!gdk_region_empty (draw_region)) {
+ gdk_region_get_clipbox (draw_region, &draw_rect);
+ gdk_draw_pixbuf (drawable, NULL, priv->pixbuf_scaled,
+ /* pixbuf 0, 0 is at pix_rect.x, pix_rect.y */
+ draw_rect.x - display_rect.x,
+ draw_rect.y - display_rect.y,
+ draw_rect.x,
+ draw_rect.y,
+ draw_rect.width,
+ draw_rect.height,
+ GDK_RGB_DITHER_NORMAL, 0, 0);
+ }
+ gdk_region_destroy (draw_region);
+}
+
+
+
+
+/* Point handler for the pixbuf canvas item */
+static double
+foo_canvas_pixbuf_point (FooCanvasItem *item, double x, double y, int cx, int cy,
+ FooCanvasItem **actual_item)
+{
+ FooCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+ double x1, y1, x2, y2;
+ int px, py;
+ double no_hit;
+ guchar *src;
+ GdkPixbuf *pixbuf;
+
+ gcp = FOO_CANVAS_PIXBUF (item);
+ priv = gcp->priv;
+ pixbuf = priv->pixbuf;
+
+ *actual_item = item;
+
+ no_hit = item->canvas->pixels_per_unit * 2 + 10;
+
+ if (!priv->pixbuf)
+ return no_hit;
+
+ compute_bounding_box (gcp, 0.0, 0.0,
+ &x1, &y1, &x2, &y2);
+
+
+ if (x < x1 || x >= x2 ||
+ y < y1 || y >= y2)
+ return no_hit;
+
+ if (!gdk_pixbuf_get_has_alpha (pixbuf) || priv->point_ignores_alpha)
+ return 0.0;
+
+ px = (x - x1) * gdk_pixbuf_get_width (pixbuf) / (x2 - x1);
+ py = (y - y1) * gdk_pixbuf_get_height (pixbuf) / (y2 - y1);
+
+ src = gdk_pixbuf_get_pixels (pixbuf) +
+ py * gdk_pixbuf_get_rowstride (pixbuf) +
+ px * gdk_pixbuf_get_n_channels (pixbuf);
+
+ if (src[3] < 128)
+ return no_hit;
+ else
+ return 0.0;
+}
+
+
+
+static void
+foo_canvas_pixbuf_translate (FooCanvasItem *item, double dx, double dy)
+{
+ FooCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+
+ gcp = FOO_CANVAS_PIXBUF (item);
+ priv = gcp->priv;
+
+ if (priv->x_in_pixels) {
+ priv->x += dx * item->canvas->pixels_per_unit;
+ } else {
+ priv->x += dx;
+ }
+
+ if (priv->y_in_pixels) {
+ priv->y += dy * item->canvas->pixels_per_unit;
+ } else {
+ priv->y += dy;
+ }
+
+ priv->need_xform_update = TRUE;
+}
+
+
+
+/* Bounds handler for the pixbuf canvas item */
+static void
+foo_canvas_pixbuf_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ FooCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+
+ gcp = FOO_CANVAS_PIXBUF (item);
+ priv = gcp->priv;
+
+ if (!priv->pixbuf) {
+ *x1 = *y1 = *x2 = *y2 = 0.0;
+ return;
+ }
+
+ compute_bounding_box (gcp, 0.0, 0.0,
+ x1, y1, x2, y2);
+}
diff --git a/libfoocanvas/foo-canvas-pixbuf.h b/libfoocanvas/foo-canvas-pixbuf.h
new file mode 100644
index 0000000..c477445
--- /dev/null
+++ b/libfoocanvas/foo-canvas-pixbuf.h
@@ -0,0 +1,62 @@
+/* GNOME libraries - GdkPixbuf item for the GNOME canvas
+ *
+ * Copyright (C) 1999 The Free Software Foundation
+ *
+ * Author: Federico Mena-Quintero <federico gimp org>
+ *
+ * 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.
+ */
+
+#ifndef FOO_CANVAS_PIXBUF_H
+#define FOO_CANVAS_PIXBUF_H
+
+
+#include <libfoocanvas/foo-canvas.h>
+
+G_BEGIN_DECLS
+
+
+
+#define FOO_TYPE_CANVAS_PIXBUF (foo_canvas_pixbuf_get_type ())
+#define FOO_CANVAS_PIXBUF(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_PIXBUF, FooCanvasPixbuf))
+#define FOO_CANVAS_PIXBUF_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_PIXBUF, FooCanvasPixbufClass))
+#define FOO_IS_CANVAS_PIXBUF(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_PIXBUF))
+#define FOO_IS_CANVAS_PIXBUF_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_PIXBUF))
+#define FOO_CANVAS_PIXBUF_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_PIXBUF, FooCanvasPixbufClass))
+
+
+typedef struct _FooCanvasPixbuf FooCanvasPixbuf;
+typedef struct _FooCanvasPixbufClass FooCanvasPixbufClass;
+
+struct _FooCanvasPixbuf {
+ FooCanvasItem item;
+
+ /* Private data */
+ gpointer priv;
+};
+
+struct _FooCanvasPixbufClass {
+ FooCanvasItemClass parent_class;
+};
+
+
+GType foo_canvas_pixbuf_get_type (void) G_GNUC_CONST;
+
+
+
+G_END_DECLS
+
+#endif
diff --git a/libfoocanvas/foo-canvas-polygon.c b/libfoocanvas/foo-canvas-polygon.c
new file mode 100644
index 0000000..679d7bb
--- /dev/null
+++ b/libfoocanvas/foo-canvas-polygon.c
@@ -0,0 +1,826 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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@
+ */
+/* Polygon item type for FooCanvas widget
+ *
+ * FooCanvas 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 <federico nuclecu unam mx>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+#include "libfoocanvas.h"
+
+
+#define NUM_STATIC_POINTS 256 /* Number of static points to use to avoid allocating arrays */
+
+
+#define GROW_BOUNDS(bx1, by1, bx2, by2, x, y) { \
+ if (x < bx1) \
+ bx1 = x; \
+ \
+ if (x > bx2) \
+ bx2 = x; \
+ \
+ if (y < by1) \
+ by1 = y; \
+ \
+ if (y > by2) \
+ by2 = y; \
+}
+
+
+enum {
+ PROP_0,
+ PROP_POINTS,
+ 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 foo_canvas_polygon_class_init (FooCanvasPolygonClass *klass);
+static void foo_canvas_polygon_init (FooCanvasPolygon *poly);
+static void foo_canvas_polygon_destroy (GtkObject *object);
+static void foo_canvas_polygon_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void foo_canvas_polygon_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void foo_canvas_polygon_update (FooCanvasItem *item,
+ double i2w_dx, double i2w_dy,
+ int flags);
+static void foo_canvas_polygon_realize (FooCanvasItem *item);
+static void foo_canvas_polygon_unrealize (FooCanvasItem *item);
+static void foo_canvas_polygon_draw (FooCanvasItem *item, GdkDrawable *drawable,
+ GdkEventExpose *expose);
+static double foo_canvas_polygon_point (FooCanvasItem *item, double x, double y,
+ int cx, int cy, FooCanvasItem **actual_item);
+static void foo_canvas_polygon_translate (FooCanvasItem *item, double dx, double dy);
+static void foo_canvas_polygon_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+
+static FooCanvasItemClass *parent_class;
+
+G_DEFINE_TYPE (FooCanvasPolygon, foo_canvas_polygon, FOO_TYPE_CANVAS_ITEM)
+
+static void
+foo_canvas_polygon_class_init (FooCanvasPolygonClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ FooCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) klass;
+ object_class = (GtkObjectClass *) klass;
+ item_class = (FooCanvasItemClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->set_property = foo_canvas_polygon_set_property;
+ gobject_class->get_property = foo_canvas_polygon_get_property;
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_POINTS,
+ g_param_spec_boxed ("points", NULL, NULL,
+ FOO_TYPE_CANVAS_POINTS,
+ 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_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_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_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 = foo_canvas_polygon_destroy;
+
+ item_class->update = foo_canvas_polygon_update;
+ item_class->realize = foo_canvas_polygon_realize;
+ item_class->unrealize = foo_canvas_polygon_unrealize;
+ item_class->draw = foo_canvas_polygon_draw;
+ item_class->point = foo_canvas_polygon_point;
+ item_class->translate = foo_canvas_polygon_translate;
+ item_class->bounds = foo_canvas_polygon_bounds;
+}
+
+static void
+foo_canvas_polygon_init (FooCanvasPolygon *poly)
+{
+ poly->width = 0.0;
+}
+
+static void
+foo_canvas_polygon_destroy (GtkObject *object)
+{
+ FooCanvasPolygon *poly;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_POLYGON (object));
+
+ poly = FOO_CANVAS_POLYGON (object);
+
+ /* remember, destroy can be run multiple times! */
+
+ if (poly->coords)
+ g_free (poly->coords);
+ poly->coords = NULL;
+
+ if (poly->fill_stipple)
+ g_object_unref (poly->fill_stipple);
+ poly->fill_stipple = NULL;
+
+ if (poly->outline_stipple)
+ g_object_unref (poly->outline_stipple);
+ poly->outline_stipple = NULL;
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+/* Computes the bounding box of the polygon. Assumes that the number of points in the polygon is
+ * not zero.
+ */
+static gboolean
+get_bounds (FooCanvasPolygon *poly, double *bx1, double *by1, double *bx2, double *by2)
+{
+ double *coords;
+ double x1, y1, x2, y2;
+ double width;
+ int i;
+
+ if (poly->num_points == 0)
+ return FALSE;
+
+ /* Compute bounds of vertices */
+
+ x1 = x2 = poly->coords[0];
+ y1 = y2 = poly->coords[1];
+
+ for (i = 1, coords = poly->coords + 2; i < poly->num_points; i++, coords += 2) {
+ GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
+ }
+
+ /* Add outline width */
+
+ if (poly->width_pixels)
+ width = poly->width / poly->item.canvas->pixels_per_unit;
+ else
+ width = poly->width;
+
+ width /= 2.0;
+
+ x1 -= width;
+ y1 -= width;
+ x2 += width;
+ y2 += width;
+
+ /* Done */
+
+ *bx1 = x1;
+ *by1 = y1;
+ *bx2 = x2;
+ *by2 = y2;
+ return TRUE;
+}
+
+/* Computes the bounding box of the polygon, in canvas coordinates. Assumes that the number of points in the polygon is
+ * not zero.
+ */
+static gboolean
+get_bounds_canvas (FooCanvasPolygon *poly,
+ double *bx1, double *by1, double *bx2, double *by2,
+ double i2w_dx, double i2w_dy)
+{
+ FooCanvasItem *item;
+ double bbox_x0, bbox_y0, bbox_x1, bbox_y1;
+
+ item = FOO_CANVAS_ITEM (poly);
+
+ if (!get_bounds (poly, &bbox_x0, &bbox_y0, &bbox_x1, &bbox_y1))
+ return FALSE;
+
+ bbox_x0 += i2w_dx;
+ bbox_y0 += i2w_dy;
+ bbox_x1 += i2w_dx;
+ bbox_y1 += i2w_dy;
+
+ foo_canvas_w2c_rect_d (item->canvas,
+ &bbox_x0, &bbox_y0, &bbox_x1, &bbox_y1);
+
+ /* include 1 pixel of fudge */
+ *bx1 = bbox_x0 - 1;
+ *by1 = bbox_y0 - 1;
+ *bx2 = bbox_x1 + 1;
+ *by2 = bbox_y1 + 1;
+ return TRUE;
+}
+
+/* Sets the points of the polygon item to the specified ones. If needed, it will add a point to
+ * close the polygon.
+ */
+static void
+set_points (FooCanvasPolygon *poly, FooCanvasPoints *points)
+{
+ int duplicate;
+
+ /* See if we need to duplicate the first point */
+
+ duplicate = ((points->coords[0] != points->coords[2 * points->num_points - 2])
+ || (points->coords[1] != points->coords[2 * points->num_points - 1]));
+
+ if (duplicate)
+ poly->num_points = points->num_points + 1;
+ else
+ poly->num_points = points->num_points;
+
+ poly->coords = g_new (double, 2 * poly->num_points);
+ memcpy (poly->coords, points->coords, 2 * points->num_points * sizeof (double));
+
+ if (duplicate) {
+ poly->coords[2 * poly->num_points - 2] = poly->coords[0];
+ poly->coords[2 * poly->num_points - 1] = poly->coords[1];
+ }
+}
+
+/* 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 polygon and set it in its GC */
+static void
+set_outline_gc_width (FooCanvasPolygon *poly)
+{
+ int width;
+
+ if (!poly->outline_gc)
+ return;
+
+ if (poly->width_pixels)
+ width = (int) poly->width;
+ else
+ width = (int) (poly->width * poly->item.canvas->pixels_per_unit + 0.5);
+
+ gdk_gc_set_line_attributes (poly->outline_gc, width,
+ GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
+}
+
+static void
+foo_canvas_polygon_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ FooCanvasItem *item;
+ FooCanvasPolygon *poly;
+ FooCanvasPoints *points;
+ GdkColor color = { 0, 0, 0, 0, };
+ GdkColor *pcolor;
+ int have_pixel;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_POLYGON (object));
+
+ item = FOO_CANVAS_ITEM (object);
+ poly = FOO_CANVAS_POLYGON (object);
+ have_pixel = FALSE;
+
+ switch (param_id) {
+ case PROP_POINTS:
+ points = g_value_get_boxed (value);
+
+ if (poly->coords) {
+ g_free (poly->coords);
+ poly->coords = NULL;
+ }
+
+ if (!points)
+ poly->num_points = 0;
+ else
+ set_points (poly, points);
+
+ foo_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))
+ poly->fill_set = TRUE;
+ else
+ poly->fill_set = FALSE;
+
+ poly->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);
+ poly->fill_set = 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;
+ }
+
+ poly->fill_color = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8 |
+ (color.blue & 0xff00) |
+ 0xff);
+ break;
+
+ case PROP_FILL_COLOR_RGBA:
+ poly->fill_set = TRUE;
+ poly->fill_color = g_value_get_uint (value);
+ break;
+ }
+#ifdef VERBOSE
+ g_print ("poly fill color = %08x\n", poly->fill_color);
+#endif
+ if (have_pixel)
+ poly->fill_pixel = color.pixel;
+ else
+ poly->fill_pixel = foo_canvas_get_color_pixel (item->canvas,
+ poly->fill_color);
+
+ set_gc_foreground (poly->fill_gc, poly->fill_pixel);
+ foo_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))
+ poly->outline_set = TRUE;
+ else
+ poly->outline_set = FALSE;
+
+ poly->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);
+ poly->outline_set = 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;
+ }
+
+ poly->outline_color = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8 |
+ (color.blue & 0xff00) |
+ 0xff);
+ break;
+
+ case PROP_OUTLINE_COLOR_RGBA:
+ poly->outline_set = TRUE;
+ poly->outline_color = g_value_get_uint (value);
+ break;
+ }
+#ifdef VERBOSE
+ g_print ("poly outline color = %08x\n", poly->outline_color);
+#endif
+ if (have_pixel)
+ poly->outline_pixel = color.pixel;
+ else
+ poly->outline_pixel = foo_canvas_get_color_pixel (item->canvas,
+ poly->outline_color);
+
+ set_gc_foreground (poly->outline_gc, poly->outline_pixel);
+ foo_canvas_item_request_redraw (item);
+ break;
+
+ case PROP_FILL_STIPPLE:
+ set_stipple (poly->fill_gc, &poly->fill_stipple, (GdkBitmap *) g_value_get_object (value), FALSE);
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_OUTLINE_STIPPLE:
+ set_stipple (poly->outline_gc, &poly->outline_stipple, (GdkBitmap *) g_value_get_object (value), FALSE);
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIDTH_PIXELS:
+ poly->width = g_value_get_uint (value);
+ poly->width_pixels = TRUE;
+ set_outline_gc_width (poly);
+#ifdef OLD_XFORM
+ recalc_bounds (poly);
+#else
+ foo_canvas_item_request_update (item);
+#endif
+ break;
+
+ case PROP_WIDTH_UNITS:
+ poly->width = fabs (g_value_get_double (value));
+ poly->width_pixels = FALSE;
+ set_outline_gc_width (poly);
+#ifdef OLD_XFORM
+ recalc_bounds (poly);
+#else
+ foo_canvas_item_request_update (item);
+#endif
+ 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 (FooCanvasPolygon *poly, gulong pixel, GValue *value)
+{
+ GdkColor *color;
+ GdkColormap *colormap;
+
+ color = g_new (GdkColor, 1);
+ color->pixel = pixel;
+
+ colormap = gtk_widget_get_colormap (GTK_WIDGET (poly));
+ gdk_rgb_find_color (colormap, color);
+ g_value_set_boxed (value, color);
+}
+
+static void
+foo_canvas_polygon_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ FooCanvasPolygon *poly;
+ FooCanvasPoints *points;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_POLYGON (object));
+
+ poly = FOO_CANVAS_POLYGON (object);
+
+ switch (param_id) {
+ case PROP_POINTS:
+ if (poly->num_points != 0) {
+ points = foo_canvas_points_new (poly->num_points);
+ memcpy (points->coords, poly->coords, 2 * poly->num_points * sizeof (double));
+ g_value_set_boxed (value, points);
+ } else
+ g_value_set_boxed (value, NULL);
+ break;
+
+ case PROP_FILL_COLOR_GDK:
+ get_color_value (poly, poly->fill_pixel, value);
+ break;
+
+ case PROP_OUTLINE_COLOR_GDK:
+ get_color_value (poly, poly->outline_pixel, value);
+ break;
+
+ case PROP_FILL_COLOR_RGBA:
+ g_value_set_uint (value, poly->fill_color);
+ break;
+
+ case PROP_OUTLINE_COLOR_RGBA:
+ g_value_set_uint (value, poly->outline_color);
+ break;
+
+ case PROP_FILL_STIPPLE:
+ g_value_set_object (value, (GObject *) poly->fill_stipple);
+ break;
+
+ case PROP_OUTLINE_STIPPLE:
+ g_value_set_object (value, (GObject *) poly->outline_stipple);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+foo_canvas_polygon_update (FooCanvasItem *item,
+ double i2w_dx, double i2w_dy,
+ int flags)
+{
+ FooCanvasPolygon *poly;
+ double x1, y1, x2, y2;
+
+ poly = FOO_CANVAS_POLYGON (item);
+
+ if (parent_class->update)
+ (* parent_class->update) (item, i2w_dx, i2w_dy, flags);
+
+ set_outline_gc_width (poly);
+ set_gc_foreground (poly->fill_gc, poly->fill_pixel);
+ set_gc_foreground (poly->outline_gc, poly->outline_pixel);
+ set_stipple (poly->fill_gc, &poly->fill_stipple, poly->fill_stipple, TRUE);
+ set_stipple (poly->outline_gc, &poly->outline_stipple, poly->outline_stipple, TRUE);
+
+ if (get_bounds_canvas (poly, &x1, &y1, &x2, &y2, i2w_dx, i2w_dy))
+ foo_canvas_update_bbox (item, x1, y1, x2, y2);
+}
+
+static void
+foo_canvas_polygon_realize (FooCanvasItem *item)
+{
+ FooCanvasPolygon *poly;
+
+ poly = FOO_CANVAS_POLYGON (item);
+
+ if (parent_class->realize)
+ (* parent_class->realize) (item);
+
+ poly->fill_gc = gdk_gc_new (item->canvas->layout.bin_window);
+ poly->outline_gc = gdk_gc_new (item->canvas->layout.bin_window);
+/* FIXME FIXME FIXME Need to recalc pixel values, set colours, etc. */
+
+#ifdef OLD_XFORM
+ (* FOO_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0);
+#endif
+}
+
+static void
+foo_canvas_polygon_unrealize (FooCanvasItem *item)
+{
+ FooCanvasPolygon *poly;
+
+ poly = FOO_CANVAS_POLYGON (item);
+
+ g_object_unref (poly->fill_gc);
+ poly->fill_gc = NULL;
+ g_object_unref (poly->outline_gc);
+ poly->outline_gc = NULL;
+
+ if (parent_class->unrealize)
+ (* parent_class->unrealize) (item);
+}
+
+/* Converts an array of world coordinates into an array of canvas pixel coordinates. Takes in the
+ * item->world deltas and the drawable deltas.
+ */
+static void
+item_to_canvas (FooCanvas *canvas, double *item_coords, GdkPoint *canvas_coords, int num_points,
+ double i2w_dx, double i2w_dy)
+{
+ int i;
+
+ for (i = 0; i < num_points; i++) {
+ foo_canvas_w2c (canvas,
+ item_coords[i*2] + i2w_dx,
+ item_coords[i*2+1] + i2w_dy,
+ &canvas_coords->x, &canvas_coords->y);
+ canvas_coords++;
+ }
+}
+
+static void
+foo_canvas_polygon_draw (FooCanvasItem *item, GdkDrawable *drawable,
+ GdkEventExpose *expose)
+{
+ FooCanvasPolygon *poly;
+ GdkPoint static_points[NUM_STATIC_POINTS];
+ GdkPoint *points;
+ double i2w_dx, i2w_dy;
+
+ poly = FOO_CANVAS_POLYGON (item);
+
+ if (poly->num_points == 0)
+ return;
+
+ /* Build array of canvas pixel coordinates */
+
+ if (poly->num_points <= NUM_STATIC_POINTS)
+ points = static_points;
+ else
+ points = g_new (GdkPoint, poly->num_points);
+
+ i2w_dx = 0.0;
+ i2w_dy = 0.0;
+ foo_canvas_item_i2w (item, &i2w_dx, &i2w_dy);
+
+ item_to_canvas (item->canvas,
+ poly->coords, points, poly->num_points,
+ i2w_dx, i2w_dy);
+
+ if (poly->fill_set) {
+ if (poly->fill_stipple)
+ foo_canvas_set_stipple_origin (item->canvas, poly->fill_gc);
+
+ gdk_draw_polygon (drawable, poly->fill_gc, TRUE, points, poly->num_points);
+ }
+
+ if (poly->outline_set) {
+ if (poly->outline_stipple)
+ foo_canvas_set_stipple_origin (item->canvas, poly->outline_gc);
+
+ gdk_draw_polygon (drawable, poly->outline_gc, FALSE, points, poly->num_points);
+ }
+
+ /* Done */
+
+ if (points != static_points)
+ g_free (points);
+}
+
+static double
+foo_canvas_polygon_point (FooCanvasItem *item, double x, double y,
+ int cx, int cy, FooCanvasItem **actual_item)
+{
+ FooCanvasPolygon *poly;
+ double dist;
+ double width;
+
+ poly = FOO_CANVAS_POLYGON (item);
+
+ *actual_item = item;
+
+ dist = foo_canvas_polygon_to_point (poly->coords, poly->num_points, x, y);
+
+ if (poly->outline_set) {
+ if (poly->width_pixels)
+ width = poly->width / item->canvas->pixels_per_unit;
+ else
+ width = poly->width;
+
+ dist -= width / 2.0;
+
+ if (dist < 0.0)
+ dist = 0.0;
+ }
+
+ return dist;
+}
+
+static void
+foo_canvas_polygon_translate (FooCanvasItem *item, double dx, double dy)
+{
+ FooCanvasPolygon *poly;
+ int i;
+ double *coords;
+
+ poly = FOO_CANVAS_POLYGON (item);
+
+ for (i = 0, coords = poly->coords; i < poly->num_points; i++, coords += 2) {
+ coords[0] += dx;
+ coords[1] += dy;
+ }
+
+}
+
+static void
+foo_canvas_polygon_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ FooCanvasPolygon *poly;
+
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_POLYGON (item));
+
+ poly = FOO_CANVAS_POLYGON (item);
+
+ if (poly->num_points == 0) {
+ *x1 = *y1 = *x2 = *y2 = 0.0;
+ return;
+ }
+
+ get_bounds (poly, x1, y1, x2, y2);
+}
diff --git a/libfoocanvas/foo-canvas-polygon.h b/libfoocanvas/foo-canvas-polygon.h
new file mode 100644
index 0000000..d56fd45
--- /dev/null
+++ b/libfoocanvas/foo-canvas-polygon.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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@
+ */
+/* Polygon item type for FooCanvas widget
+ *
+ * FooCanvas 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 <federico nuclecu unam mx>
+ */
+
+#ifndef FOO_CANVAS_POLYGON_H
+#define FOO_CANVAS_POLYGON_H
+
+
+#include <libfoocanvas/foo-canvas.h>
+
+G_BEGIN_DECLS
+
+
+/* Polygon item for the canvas. A polygon is a bit different from rectangles and ellipses in that
+ * points inside it will always be considered "inside", even if the fill color is not set. If you
+ * want to have a hollow polygon, use a line item instead.
+ *
+ * The following object arguments are available:
+ *
+ * name type read/write description
+ * ------------------------------------------------------------------------------------------
+ * points FooCanvasPoints* RW Pointer to a FooCanvasPoints structure.
+ * This can be created by a call to
+ * foo_canvas_points_new() (in foo-canvas-util.h).
+ * X coordinates are in the even indices of the
+ * points->coords array, Y coordinates are in
+ * the odd indices.
+ * fill_color string W X color specification for fill color,
+ * or NULL pointer for no color (transparent).
+ * fill_color_gdk GdkColor* RW Allocated GdkColor for fill.
+ * outline_color string W X color specification for outline color,
+ * or NULL pointer for no color (transparent).
+ * outline_color_gdk GdkColor* RW Allocated GdkColor for outline.
+ * fill_stipple GdkBitmap* RW Stipple pattern for fill
+ * outline_stipple GdkBitmap* RW Stipple pattern for outline
+ * width_pixels uint RW Width of the outline in pixels. The outline will
+ * not be scaled when the canvas zoom factor is changed.
+ * width_units double RW Width of the outline in canvas units. The outline
+ * will be scaled when the canvas zoom factor is changed.
+ */
+
+#define FOO_TYPE_CANVAS_POLYGON (foo_canvas_polygon_get_type ())
+#define FOO_CANVAS_POLYGON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_POLYGON, FooCanvasPolygon))
+#define FOO_CANVAS_POLYGON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_POLYGON, FooCanvasPolygonClass))
+#define FOO_IS_CANVAS_POLYGON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_POLYGON))
+#define FOO_IS_CANVAS_POLYGON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_POLYGON))
+#define FOO_CANVAS_POLYGON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_POLYGON, FooCanvasPolygonClass))
+
+
+typedef struct _FooCanvasPolygon FooCanvasPolygon;
+typedef struct _FooCanvasPolygonClass FooCanvasPolygonClass;
+
+struct _FooCanvasPolygon {
+ FooCanvasItem item;
+
+ double *coords; /* Array of coordinates for the polygon's points. X coords
+ * are in the even indices, Y coords are in the odd indices.
+ */
+ GdkBitmap *fill_stipple; /* Stipple for fill */
+ GdkBitmap *outline_stipple; /* Stipple for outline */
+
+ GdkGC *fill_gc; /* GC for filling */
+ GdkGC *outline_gc; /* GC for outline */
+
+ gulong fill_pixel; /* Color for fill */
+ gulong outline_pixel; /* Color for outline */
+ double width; /* Width of polygon's outline */
+
+ int num_points; /* Number of points in the polygon */
+ guint fill_color; /* Fill color, RGBA */
+ guint outline_color; /* Outline color, RGBA */
+
+ guint32 fill_rgba; /* RGBA color for filling */ /*AA*/
+ guint32 outline_rgba; /* RGBA color for outline */ /*AA*/
+
+ guint fill_set : 1; /* Is fill color set? */
+ guint outline_set : 1; /* Is outline color set? */
+ guint width_pixels : 1; /* Is outline width specified in pixels or units? */
+};
+
+struct _FooCanvasPolygonClass {
+ FooCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_polygon_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libfoocanvas/foo-canvas-rect-ellipse.c b/libfoocanvas/foo-canvas-rect-ellipse.c
new file mode 100644
index 0000000..1c18949
--- /dev/null
+++ b/libfoocanvas/foo-canvas-rect-ellipse.c
@@ -0,0 +1,1489 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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 FooCanvas widget
+ *
+ * FooCanvas 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 <federico nuclecu unam mx>
+ */
+
+#include <config.h>
+#include <math.h>
+#include "foo-canvas-rect-ellipse.h"
+#include "foo-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 foo_canvas_re_class_init (FooCanvasREClass *klass);
+static void foo_canvas_re_init (FooCanvasRE *re);
+static void foo_canvas_re_destroy (GtkObject *object);
+static void foo_canvas_re_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void foo_canvas_re_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void foo_canvas_re_update_shared (FooCanvasItem *item,
+ double i2w_dx, double i2w_dy, int flags);
+static void foo_canvas_re_realize (FooCanvasItem *item);
+static void foo_canvas_re_unrealize (FooCanvasItem *item);
+static void foo_canvas_re_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+static void foo_canvas_re_translate (FooCanvasItem *item, double dx, double dy);
+static void foo_canvas_rect_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags);
+static void foo_canvas_ellipse_update (FooCanvasItem *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 FooCanvasItemClass *re_parent_class;
+static FooCanvasREClass *rect_parent_class;
+
+
+GType
+foo_canvas_re_get_type (void)
+{
+ static GType re_type = 0;
+
+ if (!re_type) {
+ GTypeInfo re_info = {
+ sizeof (FooCanvasREClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) foo_canvas_re_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (FooCanvasRE),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) foo_canvas_re_init
+ };
+
+ re_type = g_type_register_static (foo_canvas_item_get_type (),
+ "FooCanvasRE",
+ &re_info,
+ 0);
+ }
+
+ return re_type;
+}
+
+static void
+foo_canvas_re_class_init (FooCanvasREClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ FooCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) klass;
+ object_class = (GtkObjectClass *) klass;
+ item_class = (FooCanvasItemClass *) klass;
+
+ re_parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->set_property = foo_canvas_re_set_property;
+ gobject_class->get_property = foo_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 = foo_canvas_re_destroy;
+
+ item_class->realize = foo_canvas_re_realize;
+ item_class->unrealize = foo_canvas_re_unrealize;
+ item_class->translate = foo_canvas_re_translate;
+ item_class->bounds = foo_canvas_re_bounds;
+}
+
+static void
+foo_canvas_re_init (FooCanvasRE *re)
+{
+ re->x1 = 0.0;
+ re->y1 = 0.0;
+ re->x2 = 0.0;
+ re->y2 = 0.0;
+ re->width = 0.0;
+}
+
+static void
+foo_canvas_re_destroy (GtkObject *object)
+{
+ FooCanvasRE *re;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_RE (object));
+
+ re = FOO_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 (FooCanvasRE *re, double *px1, double *py1, double *px2, double *py2)
+{
+ FooCanvasItem *item;
+ double x1, y1, x2, y2;
+ int cx1, cy1, cx2, cy2;
+ double hwidth;
+
+#ifdef VERBOSE
+ g_print ("re get_bounds\n");
+#endif
+ item = FOO_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;
+
+ foo_canvas_item_i2w (item, &x1, &y1);
+ foo_canvas_item_i2w (item, &x2, &y2);
+ foo_canvas_w2c (item->canvas, x1 - hwidth, y1 - hwidth, &cx1, &cy1);
+ foo_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 (FooCanvasRE *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
+foo_canvas_re_set_fill (FooCanvasRE *re, gboolean fill_set)
+{
+ if (re->fill_set != fill_set) {
+ re->fill_set = fill_set;
+ foo_canvas_item_request_update (FOO_CANVAS_ITEM (re));
+ }
+}
+
+static void
+foo_canvas_re_set_outline (FooCanvasRE *re, gboolean outline_set)
+{
+ if (re->outline_set != outline_set) {
+ re->outline_set = outline_set;
+ foo_canvas_item_request_update (FOO_CANVAS_ITEM (re));
+ }
+}
+
+static void
+foo_canvas_re_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ FooCanvasItem *item;
+ FooCanvasRE *re;
+ GdkColor color = { 0, 0, 0, 0, };
+ GdkColor *pcolor;
+ int have_pixel;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_RE (object));
+
+ item = FOO_CANVAS_ITEM (object);
+ re = FOO_CANVAS_RE (object);
+ have_pixel = FALSE;
+
+ switch (param_id) {
+ case PROP_X1:
+ re->x1 = g_value_get_double (value);
+
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_Y1:
+ re->y1 = g_value_get_double (value);
+
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_X2:
+ re->x2 = g_value_get_double (value);
+
+ foo_canvas_item_request_update (item);
+ break;
+
+ case PROP_Y2:
+ re->y2 = g_value_get_double (value);
+
+ foo_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))
+ foo_canvas_re_set_fill (re, TRUE);
+ else
+ foo_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);
+ foo_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:
+ foo_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 = foo_canvas_get_color_pixel (item->canvas, re->fill_color);
+
+ set_gc_foreground (re->fill_gc, re->fill_pixel);
+
+ foo_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))
+ foo_canvas_re_set_outline (re, TRUE);
+ else
+ foo_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);
+ foo_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:
+ foo_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 = foo_canvas_get_color_pixel (item->canvas,
+ re->outline_color);
+
+ set_gc_foreground (re->outline_gc, re->outline_pixel);
+
+ foo_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);
+
+ foo_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);
+
+ foo_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 (FooCanvasRE *re, gulong pixel, GValue *value)
+{
+ GdkColor color;
+ FooCanvasItem *item = (FooCanvasItem *) 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
+foo_canvas_re_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ FooCanvasRE *re;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_RE (object));
+
+ re = FOO_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 (FooCanvasRE *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
+foo_canvas_re_update_shared (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
+{
+ FooCanvasRE *re;
+
+#ifdef VERBOSE
+ g_print ("foo_canvas_re_update_shared\n");
+#endif
+ re = FOO_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
+foo_canvas_re_realize (FooCanvasItem *item)
+{
+ FooCanvasRE *re;
+
+#ifdef VERBOSE
+ g_print ("foo_canvas_re_realize\n");
+#endif
+ re = FOO_CANVAS_RE (item);
+
+ if (re_parent_class->realize)
+ (* re_parent_class->realize) (item);
+
+ re->fill_gc = gdk_gc_new (item->canvas->layout.bin_window);
+ re->fill_pixel = foo_canvas_get_color_pixel (item->canvas, re->fill_color);
+ re->outline_gc = gdk_gc_new (item->canvas->layout.bin_window);
+ re->outline_pixel = foo_canvas_get_color_pixel (item->canvas, re->outline_color);
+ set_colors_and_stipples (re);
+
+#ifdef OLD_XFORM
+ (* FOO_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0);
+#endif
+}
+
+static void
+foo_canvas_re_unrealize (FooCanvasItem *item)
+{
+ FooCanvasRE *re;
+
+ re = FOO_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
+foo_canvas_re_translate (FooCanvasItem *item, double dx, double dy)
+{
+ FooCanvasRE *re;
+
+#ifdef VERBOSE
+ g_print ("foo_canvas_re_translate\n");
+#endif
+ re = FOO_CANVAS_RE (item);
+
+ re->x1 += dx;
+ re->y1 += dy;
+ re->x2 += dx;
+ re->y2 += dy;
+}
+
+
+static void
+foo_canvas_re_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ FooCanvasRE *re;
+ double hwidth;
+
+#ifdef VERBOSE
+ g_print ("foo_canvas_re_bounds\n");
+#endif
+ re = FOO_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 foo_canvas_rect_class_init (FooCanvasRectClass *klass);
+static void foo_canvas_rect_init (FooCanvasRect *rect);
+static void foo_canvas_rect_finalize (GObject *object);
+static void foo_canvas_rect_realize (FooCanvasItem *item);
+
+static void foo_canvas_rect_draw (FooCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose);
+static double foo_canvas_rect_point (FooCanvasItem *item, double x, double y, int cx, int cy,
+ FooCanvasItem **actual_item);
+
+struct _FooCanvasRectPrivate {
+ Rect last_update_rect;
+ Rect last_outline_update_rect;
+ int last_outline_update_width;
+
+#ifdef HAVE_RENDER
+ gboolean use_render;
+ XRenderPictFormat *format;
+#endif
+};
+
+GType
+foo_canvas_rect_get_type (void)
+{
+ static GType rect_type = 0;
+
+ if (!rect_type) {
+ GTypeInfo rect_info = {
+ sizeof (FooCanvasRectClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) foo_canvas_rect_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (FooCanvasRect),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) foo_canvas_rect_init
+ };
+
+ rect_type = g_type_register_static (foo_canvas_re_get_type (),
+ "FooCanvasRect",
+ &rect_info,
+ 0);
+ }
+
+ return rect_type;
+}
+
+static void
+foo_canvas_rect_class_init (FooCanvasRectClass *klass)
+{
+ FooCanvasItemClass *item_class;
+
+ rect_parent_class = g_type_class_peek_parent (klass);
+
+ item_class = (FooCanvasItemClass *) klass;
+
+ item_class->draw = foo_canvas_rect_draw;
+ item_class->point = foo_canvas_rect_point;
+ item_class->update = foo_canvas_rect_update;
+ item_class->realize = foo_canvas_rect_realize;
+
+ G_OBJECT_CLASS (klass)->finalize = foo_canvas_rect_finalize;
+
+}
+
+static void
+foo_canvas_rect_init (FooCanvasRect *rect)
+{
+ rect->priv = g_new0 (FooCanvasRectPrivate, 1);
+}
+
+static void
+foo_canvas_rect_finalize (GObject *object)
+{
+ FooCanvasRect *rect = FOO_CANVAS_RECT (object);
+
+ if (rect->priv) {
+ g_free (rect->priv);
+ }
+
+ G_OBJECT_CLASS (rect_parent_class)->finalize (object);
+}
+
+static void
+foo_canvas_rect_realize (FooCanvasItem *item)
+{
+#ifdef HAVE_RENDER
+ FooCanvasRectPrivate *priv;
+ int event_base, error_base;
+ Display *dpy;
+
+ priv = FOO_CANVAS_RECT (item)->priv;
+
+ dpy = gdk_x11_drawable_get_xdisplay (GTK_WIDGET (item->canvas)->window);
+ 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 (FOO_CANVAS_ITEM_CLASS (rect_parent_class)->realize) {
+ (* FOO_CANVAS_ITEM_CLASS (rect_parent_class)->realize) (item);
+ }
+}
+
+
+static void
+render_rect_alpha (FooCanvasRect *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;
+ FooCanvasRectPrivate *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
+foo_canvas_rect_draw (FooCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose)
+{
+ FooCanvasRE *re;
+ double x1, y1, x2, y2;
+ int cx1, cy1, cx2, cy2;
+ double i2w_dx, i2w_dy;
+
+ re = FOO_CANVAS_RE (item);
+
+ /* Get canvas pixel coordinates */
+ i2w_dx = 0.0;
+ i2w_dy = 0.0;
+ foo_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;
+
+ foo_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1);
+ foo_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 (FOO_CANVAS_RECT (item),
+ drawable,
+ part.x, part.y,
+ part.width, part.height,
+ re->fill_color);
+ }
+ }
+
+ g_free (rectangles);
+ } else {
+ if (re->fill_stipple)
+ foo_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)
+ foo_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
+foo_canvas_rect_point (FooCanvasItem *item, double x, double y, int cx, int cy, FooCanvasItem **actual_item)
+{
+ FooCanvasRE *re;
+ double x1, y1, x2, y2;
+ double hwidth;
+ double dx, dy;
+ double tmp;
+
+#ifdef VERBOSE
+ g_print ("foo_canvas_rect_point\n");
+#endif
+ re = FOO_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 (FooCanvas *canvas,
+ Rect *update_rect,
+ int width)
+{
+ foo_canvas_request_redraw (canvas,
+ update_rect->x0, update_rect->y0,
+ update_rect->x1, update_rect->y0 + width);
+ foo_canvas_request_redraw (canvas,
+ update_rect->x0, update_rect->y1-width,
+ update_rect->x1, update_rect->y1);
+ foo_canvas_request_redraw (canvas,
+ update_rect->x0, update_rect->y0,
+ update_rect->x0+width, update_rect->y1);
+ foo_canvas_request_redraw (canvas,
+ update_rect->x1-width, update_rect->y0,
+ update_rect->x1, update_rect->y1);
+}
+
+
+static void
+foo_canvas_rect_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, gint flags)
+{
+ FooCanvasRE *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];
+ FooCanvasRectPrivate *priv;
+
+ foo_canvas_re_update_shared (item, i2w_dx, i2w_dy, flags);
+
+ re = FOO_CANVAS_RE (item);
+ priv = FOO_CANVAS_RECT (item)->priv;
+
+ x1 = re->x1 + i2w_dx;
+ y1 = re->y1 + i2w_dy;
+ x2 = re->x2 + i2w_dx;
+ y2 = re->y2 + i2w_dy;
+
+ foo_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1);
+ foo_canvas_w2c (item->canvas, x2, y2, &cx2, &cy2);
+
+ update_rect = make_rect (cx1, cy1, cx2+1, cy2+1);
+#if 0
+ foo_canvas_request_redraw (item->canvas,
+ update_rect.x0, update_rect.y0,
+ update_rect.x1, update_rect.y1);
+ foo_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++) {
+ foo_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 foo_canvas_ellipse_class_init (FooCanvasEllipseClass *klass);
+
+static void foo_canvas_ellipse_draw (FooCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose);
+static double foo_canvas_ellipse_point (FooCanvasItem *item, double x, double y, int cx, int cy,
+ FooCanvasItem **actual_item);
+
+
+GType
+foo_canvas_ellipse_get_type (void)
+{
+ static GType ellipse_type = 0;
+
+ if (!ellipse_type) {
+ GTypeInfo ellipse_info = {
+ sizeof (FooCanvasEllipseClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) foo_canvas_ellipse_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (FooCanvasEllipse),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) NULL
+
+ };
+
+ ellipse_type = g_type_register_static (foo_canvas_re_get_type (),
+ "FooCanvasEllipse",
+ &ellipse_info,
+ 0);
+ }
+
+ return ellipse_type;
+}
+
+static void
+foo_canvas_ellipse_class_init (FooCanvasEllipseClass *klass)
+{
+ FooCanvasItemClass *item_class;
+
+ item_class = (FooCanvasItemClass *) klass;
+
+ item_class->draw = foo_canvas_ellipse_draw;
+ item_class->point = foo_canvas_ellipse_point;
+ item_class->update = foo_canvas_ellipse_update;
+}
+
+static void
+foo_canvas_ellipse_draw (FooCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose)
+{
+ FooCanvasRE *re;
+ int x1, y1, x2, y2;
+ double i2w_dx, i2w_dy;
+
+ re = FOO_CANVAS_RE (item);
+
+ /* Get canvas pixel coordinates */
+
+ i2w_dx = 0.0;
+ i2w_dy = 0.0;
+ foo_canvas_item_i2w (item, &i2w_dx, &i2w_dy);
+
+ foo_canvas_w2c (item->canvas,
+ re->x1 + i2w_dx,
+ re->y1 + i2w_dy,
+ &x1, &y1);
+ foo_canvas_w2c (item->canvas,
+ re->x2 + i2w_dx,
+ re->y2 + i2w_dy,
+ &x2, &y2);
+
+ if (re->fill_set) {
+ if (re->fill_stipple)
+ foo_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)
+ foo_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
+foo_canvas_ellipse_point (FooCanvasItem *item, double x, double y, int cx, int cy, FooCanvasItem **actual_item)
+{
+ FooCanvasRE *re;
+ double dx, dy;
+ double scaled_dist;
+ double outline_dist;
+ double center_dist;
+ double width;
+ double a, b;
+ double diamx, diamy;
+
+ re = FOO_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 > FOO_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
+foo_canvas_ellipse_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, gint flags)
+{
+ FooCanvasRE *re;
+ double x0, y0, x1, y1;
+
+#ifdef VERBOSE
+ g_print ("foo_canvas_sllipse_update item %x\n", item);
+#endif
+
+ foo_canvas_re_update_shared (item, i2w_dx, i2w_dy, flags);
+ re = FOO_CANVAS_RE (item);
+
+ get_bounds (re, &x0, &y0, &x1, &y1);
+ foo_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;
+ }
+ }
+}
diff --git a/libfoocanvas/foo-canvas-rect-ellipse.h b/libfoocanvas/foo-canvas-rect-ellipse.h
new file mode 100644
index 0000000..5ee8a25
--- /dev/null
+++ b/libfoocanvas/foo-canvas-rect-ellipse.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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 FooCanvas widget
+ *
+ * FooCanvas 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 <federico nuclecu unam mx>
+ */
+
+#ifndef FOO_CANVAS_RECT_ELLIPSE_H
+#define FOO_CANVAS_RECT_ELLIPSE_H
+
+
+#include <libfoocanvas/foo-canvas.h>
+
+G_BEGIN_DECLS
+
+
+/* Base class for rectangle and ellipse item types. These are defined by their top-left and
+ * bottom-right corners. Rectangles and ellipses share the following arguments:
+ *
+ * name type read/write description
+ * ------------------------------------------------------------------------------------------
+ * x1 double RW Leftmost coordinate of rectangle or ellipse
+ * y1 double RW Topmost coordinate of rectangle or ellipse
+ * x2 double RW Rightmost coordinate of rectangle or ellipse
+ * y2 double RW Bottommost coordinate of rectangle or ellipse
+ * fill_color string W X color specification for fill color,
+ * or NULL pointer for no color (transparent)
+ * fill_color_gdk GdkColor* RW Allocated GdkColor for fill
+ * outline_color string W X color specification for outline color,
+ * or NULL pointer for no color (transparent)
+ * outline_color_gdk GdkColor* RW Allocated GdkColor for outline
+ * fill_stipple GdkBitmap* RW Stipple pattern for fill
+ * outline_stipple GdkBitmap* RW Stipple pattern for outline
+ * width_pixels uint RW Width of the outline in pixels. The outline will
+ * not be scaled when the canvas zoom factor is changed.
+ * width_units double RW Width of the outline in canvas units. The outline
+ * will be scaled when the canvas zoom factor is changed.
+ */
+
+
+#define FOO_TYPE_CANVAS_RE (foo_canvas_re_get_type ())
+#define FOO_CANVAS_RE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_RE, FooCanvasRE))
+#define FOO_CANVAS_RE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_RE, FooCanvasREClass))
+#define FOO_IS_CANVAS_RE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_RE))
+#define FOO_IS_CANVAS_RE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_RE))
+#define FOO_CANVAS_RE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_RE, FooCanvasREClass))
+
+
+typedef struct _FooCanvasRE FooCanvasRE;
+typedef struct _FooCanvasREClass FooCanvasREClass;
+
+struct _FooCanvasRE {
+ FooCanvasItem item;
+
+ GdkBitmap *fill_stipple; /* Stipple for fill */
+ GdkBitmap *outline_stipple; /* Stipple for outline */
+
+ GdkGC *fill_gc; /* GC for filling */
+ GdkGC *outline_gc; /* GC for outline */
+
+ gulong fill_pixel; /* Fill color */
+ gulong outline_pixel; /* Outline color */
+
+ double x1, y1, x2, y2; /* Corners of item */
+ double width; /* Outline width */
+
+ guint fill_color; /* Fill color, RGBA */
+ guint outline_color; /* Outline color, RGBA */
+
+ /* Configuration flags */
+
+ unsigned int fill_set : 1; /* Is fill color set? */
+ unsigned int outline_set : 1; /* Is outline color set? */
+ unsigned int width_pixels : 1; /* Is outline width specified in pixels or units? */
+};
+
+struct _FooCanvasREClass {
+ FooCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_re_get_type (void) G_GNUC_CONST;
+
+
+/* Rectangle item. No configurable or queryable arguments are available (use those in
+ * FooCanvasRE).
+ */
+
+
+#define FOO_TYPE_CANVAS_RECT (foo_canvas_rect_get_type ())
+#define FOO_CANVAS_RECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_RECT, FooCanvasRect))
+#define FOO_CANVAS_RECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_RECT, FooCanvasRectClass))
+#define FOO_IS_CANVAS_RECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_RECT))
+#define FOO_IS_CANVAS_RECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_RECT))
+#define FOO_CANVAS_RECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_RECT, FooCanvasRectClass))
+
+
+typedef struct _FooCanvasRect FooCanvasRect;
+typedef struct _FooCanvasRectPrivate FooCanvasRectPrivate;
+typedef struct _FooCanvasRectClass FooCanvasRectClass;
+
+struct _FooCanvasRect {
+ FooCanvasRE re;
+ FooCanvasRectPrivate *priv;
+};
+
+struct _FooCanvasRectClass {
+ FooCanvasREClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_rect_get_type (void) G_GNUC_CONST;
+
+
+/* Ellipse item. No configurable or queryable arguments are available (use those in
+ * FooCanvasRE).
+ */
+
+
+#define FOO_TYPE_CANVAS_ELLIPSE (foo_canvas_ellipse_get_type ())
+#define FOO_CANVAS_ELLIPSE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_ELLIPSE, FooCanvasEllipse))
+#define FOO_CANVAS_ELLIPSE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_ELLIPSE, FooCanvasEllipseClass))
+#define FOO_IS_CANVAS_ELLIPSE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_ELLIPSE))
+#define FOO_IS_CANVAS_ELLIPSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_ELLIPSE))
+#define FOO_CANVAS_ELLIPSE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_ELLIPSE, FooCanvasEllipseClass))
+
+
+typedef struct _FooCanvasEllipse FooCanvasEllipse;
+typedef struct _FooCanvasEllipseClass FooCanvasEllipseClass;
+
+struct _FooCanvasEllipse {
+ FooCanvasRE re;
+};
+
+struct _FooCanvasEllipseClass {
+ FooCanvasREClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_ellipse_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libfoocanvas/foo-canvas-text.c b/libfoocanvas/foo-canvas-text.c
new file mode 100644
index 0000000..ca876db
--- /dev/null
+++ b/libfoocanvas/foo-canvas-text.c
@@ -0,0 +1,1587 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * $Id$
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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@
+ */
+/* Text item type for FooCanvas widget
+ *
+ * FooCanvas 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 <federico nuclecu unam mx>
+ * Port to Pango co-done by Gergõ Érdi <cactus cactus rulez org>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+#include "foo-canvas-text.h"
+
+#include "foo-canvas-util.h"
+#include "foo-canvas-i18n.h"
+
+
+
+/* Object argument IDs */
+enum {
+ PROP_0,
+
+ /* Text contents */
+ PROP_TEXT,
+ PROP_MARKUP,
+
+ /* Position */
+ PROP_X,
+ PROP_Y,
+
+ /* Font */
+ PROP_FONT,
+ PROP_FONT_DESC,
+ PROP_FAMILY, PROP_FAMILY_SET,
+
+ /* Style */
+ PROP_ATTRIBUTES,
+ PROP_STYLE, PROP_STYLE_SET,
+ PROP_VARIANT, PROP_VARIANT_SET,
+ PROP_WEIGHT, PROP_WEIGHT_SET,
+ PROP_STRETCH, PROP_STRETCH_SET,
+ PROP_SIZE, PROP_SIZE_SET,
+ PROP_SIZE_POINTS,
+ PROP_STRIKETHROUGH, PROP_STRIKETHROUGH_SET,
+ PROP_UNDERLINE, PROP_UNDERLINE_SET,
+ PROP_RISE, PROP_RISE_SET,
+ PROP_SCALE, PROP_SCALE_SET,
+
+ /* Clipping */
+ PROP_ANCHOR,
+ PROP_JUSTIFICATION,
+ PROP_CLIP_WIDTH,
+ PROP_CLIP_HEIGHT,
+ PROP_CLIP,
+ PROP_WRAP_WIDTH,
+ PROP_X_OFFSET,
+ PROP_Y_OFFSET,
+
+ /* Coloring */
+ PROP_FILL_COLOR,
+ PROP_FILL_COLOR_GDK,
+ PROP_FILL_COLOR_RGBA,
+ PROP_FILL_STIPPLE,
+
+ /* Rendered size accessors */
+ PROP_TEXT_WIDTH,
+ PROP_TEXT_HEIGHT
+};
+
+struct _FooCanvasTextPrivate {
+ gint placeholder;
+};
+
+static void foo_canvas_text_class_init (FooCanvasTextClass *klass);
+static void foo_canvas_text_init (FooCanvasText *text);
+static void foo_canvas_text_destroy (GtkObject *object);
+static void foo_canvas_text_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void foo_canvas_text_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void foo_canvas_text_update (FooCanvasItem *item,
+ double i2w_dx,
+ double i2w_dy,
+ int flags);
+static void foo_canvas_text_realize (FooCanvasItem *item);
+static void foo_canvas_text_unrealize (FooCanvasItem *item);
+static void foo_canvas_text_draw (FooCanvasItem *item,
+ GdkDrawable *drawable,
+ GdkEventExpose *expose);
+static double foo_canvas_text_point (FooCanvasItem *item,
+ double x,
+ double y,
+ int cx,
+ int cy,
+ FooCanvasItem **actual_item);
+static void foo_canvas_text_translate (FooCanvasItem *item,
+ double dx,
+ double dy);
+static void foo_canvas_text_bounds (FooCanvasItem *item,
+ double *x1,
+ double *y1,
+ double *x2,
+ double *y2);
+
+static void foo_canvas_text_set_markup (FooCanvasText *textitem,
+ const gchar *markup);
+
+static void foo_canvas_text_set_font_desc (FooCanvasText *textitem,
+ PangoFontDescription *font_desc);
+
+static void foo_canvas_text_apply_font_desc (FooCanvasText *textitem);
+static void foo_canvas_text_apply_attributes (FooCanvasText *textitem);
+
+static void add_attr (PangoAttrList *attr_list,
+ PangoAttribute *attr);
+
+static FooCanvasItemClass *parent_class;
+
+G_DEFINE_TYPE (FooCanvasText, foo_canvas_text, FOO_TYPE_CANVAS_ITEM)
+
+/* Class initialization function for the text item */
+static void
+foo_canvas_text_class_init (FooCanvasTextClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ FooCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) klass;
+ object_class = (GtkObjectClass *) klass;
+ item_class = (FooCanvasItemClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->set_property = foo_canvas_text_set_property;
+ gobject_class->get_property = foo_canvas_text_get_property;
+
+ /* Text */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_TEXT,
+ g_param_spec_string ("text",
+ _("Text"),
+ _("Text to render"),
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_MARKUP,
+ g_param_spec_string ("markup",
+ _("Markup"),
+ _("Marked up text to render"),
+ NULL,
+ (G_PARAM_WRITABLE)));
+
+ /* Position */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X,
+ g_param_spec_double ("x", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y,
+ g_param_spec_double ("y", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+
+
+ /* Font */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FONT,
+ g_param_spec_string ("font",
+ _("Font"),
+ _("Font description as a string"),
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FONT_DESC,
+ g_param_spec_boxed ("font-desc",
+ _("Font description"),
+ _("Font description as a PangoFontDescription struct"),
+ PANGO_TYPE_FONT_DESCRIPTION,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FAMILY,
+ g_param_spec_string ("family",
+ _("Font family"),
+ _("Name of the font family, e.g. Sans, Helvetica, Times, Monospace"),
+ NULL,
+ G_PARAM_READWRITE));
+
+ /* Style */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_ATTRIBUTES,
+ g_param_spec_boxed ("attributes", NULL, NULL,
+ PANGO_TYPE_ATTR_LIST,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_STYLE,
+ g_param_spec_enum ("style",
+ _("Font style"),
+ _("Font style"),
+ PANGO_TYPE_STYLE,
+ PANGO_STYLE_NORMAL,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_VARIANT,
+ g_param_spec_enum ("variant",
+ _("Font variant"),
+ _("Font variant"),
+ PANGO_TYPE_VARIANT,
+ PANGO_VARIANT_NORMAL,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WEIGHT,
+ g_param_spec_int ("weight",
+ _("Font weight"),
+ _("Font weight"),
+ 0,
+ G_MAXINT,
+ PANGO_WEIGHT_NORMAL,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_STRETCH,
+ g_param_spec_enum ("stretch",
+ _("Font stretch"),
+ _("Font stretch"),
+ PANGO_TYPE_STRETCH,
+ PANGO_STRETCH_NORMAL,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SIZE,
+ g_param_spec_int ("size",
+ _("Font size"),
+ _("Font size"),
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SIZE_POINTS,
+ g_param_spec_double ("size-points",
+ _("Font points"),
+ _("Font size in points"),
+ 0.0,
+ G_MAXDOUBLE,
+ 0.0,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_RISE,
+ g_param_spec_int ("rise",
+ _("Rise"),
+ _("Offset of text above the baseline (below the baseline if rise is negative)"),
+ -G_MAXINT,
+ G_MAXINT,
+ 0,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_STRIKETHROUGH,
+ g_param_spec_boolean ("strikethrough",
+ _("Strikethrough"),
+ _("Whether to strike through the text"),
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_UNDERLINE,
+ g_param_spec_enum ("underline",
+ _("Underline"),
+ _("Style of underline for this text"),
+ PANGO_TYPE_UNDERLINE,
+ PANGO_UNDERLINE_NONE,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SCALE,
+ g_param_spec_double ("scale",
+ _("Scale"),
+ _("Size of font, relative to default size"),
+ 0.0,
+ G_MAXDOUBLE,
+ 1.0,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_ANCHOR,
+ g_param_spec_enum ("anchor", NULL, NULL,
+ GTK_TYPE_ANCHOR_TYPE,
+ GTK_ANCHOR_CENTER,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_JUSTIFICATION,
+ g_param_spec_enum ("justification", NULL, NULL,
+ GTK_TYPE_JUSTIFICATION,
+ GTK_JUSTIFY_LEFT,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_CLIP_WIDTH,
+ g_param_spec_double ("clip-width", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_CLIP_HEIGHT,
+ g_param_spec_double ("clip-height", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_CLIP,
+ g_param_spec_boolean ("clip", NULL, NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WRAP_WIDTH,
+ g_param_spec_double ("wrap-width", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X_OFFSET,
+ g_param_spec_double ("x-offset", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y_OFFSET,
+ g_param_spec_double ("y-offset", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FILL_COLOR,
+ g_param_spec_string ("fill-color",
+ _("Color"),
+ _("Text color, as string"),
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FILL_COLOR_GDK,
+ g_param_spec_boxed ("fill-color-gdk",
+ _("Color"),
+ _("Text color, as a GdkColor"),
+ GDK_TYPE_COLOR,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FILL_COLOR_RGBA,
+ g_param_spec_uint ("fill-color-rgba",
+ _("Color"),
+ _("Text color, as an R/G/B/A combined integer"),
+ 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_TEXT_WIDTH,
+ g_param_spec_double ("text-width",
+ _("Text width"),
+ _("Width of the rendered text"),
+ 0.0, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_TEXT_HEIGHT,
+ g_param_spec_double ("text-height",
+ _("Text height"),
+ _("Height of the rendered text"),
+ 0.0, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+
+ /* Style props are set (explicitly applied) or not */
+#define ADD_SET_PROP(propname, propval, nick, blurb) g_object_class_install_property (gobject_class, propval, g_param_spec_boolean (propname, nick, blurb, FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE))
+
+ ADD_SET_PROP ("family-set", PROP_FAMILY_SET,
+ _("Font family set"),
+ _("Whether this tag affects the font family"));
+
+ ADD_SET_PROP ("style-set", PROP_STYLE_SET,
+ _("Font style set"),
+ _("Whether this tag affects the font style"));
+
+ ADD_SET_PROP ("variant-set", PROP_VARIANT_SET,
+ _("Font variant set"),
+ _("Whether this tag affects the font variant"));
+
+ ADD_SET_PROP ("weight-set", PROP_WEIGHT_SET,
+ _("Font weight set"),
+ _("Whether this tag affects the font weight"));
+
+ ADD_SET_PROP ("stretch-set", PROP_STRETCH_SET,
+ _("Font stretch set"),
+ _("Whether this tag affects the font stretch"));
+
+ ADD_SET_PROP ("size-set", PROP_SIZE_SET,
+ _("Font size set"),
+ _("Whether this tag affects the font size"));
+
+ ADD_SET_PROP ("rise-set", PROP_RISE_SET,
+ _("Rise set"),
+ _("Whether this tag affects the rise"));
+
+ ADD_SET_PROP ("strikethrough-set", PROP_STRIKETHROUGH_SET,
+ _("Strikethrough set"),
+ _("Whether this tag affects strikethrough"));
+
+ ADD_SET_PROP ("underline-set", PROP_UNDERLINE_SET,
+ _("Underline set"),
+ _("Whether this tag affects underlining"));
+
+ ADD_SET_PROP ("scale-set", PROP_SCALE_SET,
+ _("Scale set"),
+ _("Whether this tag affects font scaling"));
+#undef ADD_SET_PROP
+
+ object_class->destroy = foo_canvas_text_destroy;
+
+ item_class->update = foo_canvas_text_update;
+ item_class->realize = foo_canvas_text_realize;
+ item_class->unrealize = foo_canvas_text_unrealize;
+ item_class->draw = foo_canvas_text_draw;
+ item_class->point = foo_canvas_text_point;
+ item_class->translate = foo_canvas_text_translate;
+ item_class->bounds = foo_canvas_text_bounds;
+}
+
+/* Object initialization function for the text item */
+static void
+foo_canvas_text_init (FooCanvasText *text)
+{
+ text->x = 0.0;
+ text->y = 0.0;
+ text->anchor = GTK_ANCHOR_CENTER;
+ text->justification = GTK_JUSTIFY_LEFT;
+ text->clip_width = 0.0;
+ text->clip_height = 0.0;
+ text->xofs = 0.0;
+ text->yofs = 0.0;
+ text->layout = NULL;
+
+ text->font_desc = NULL;
+
+ text->underline = PANGO_UNDERLINE_NONE;
+ text->strikethrough = FALSE;
+ text->rise = 0;
+
+ text->underline_set = FALSE;
+ text->strike_set = FALSE;
+ text->rise_set = FALSE;
+
+ text->priv = g_new (FooCanvasTextPrivate, 1);
+}
+
+/* Destroy handler for the text item */
+static void
+foo_canvas_text_destroy (GtkObject *object)
+{
+ FooCanvasText *text;
+
+ g_return_if_fail (FOO_IS_CANVAS_TEXT (object));
+
+ text = FOO_CANVAS_TEXT (object);
+
+ /* remember, destroy can be run multiple times! */
+
+ g_free (text->text);
+ text->text = NULL;
+
+ if (text->layout)
+ g_object_unref (G_OBJECT (text->layout));
+ text->layout = NULL;
+
+ if (text->font_desc) {
+ pango_font_description_free (text->font_desc);
+ text->font_desc = NULL;
+ }
+
+ if (text->attr_list)
+ pango_attr_list_unref (text->attr_list);
+ text->attr_list = NULL;
+
+ if (text->stipple)
+ g_object_unref (text->stipple);
+ text->stipple = NULL;
+
+ g_free (text->priv);
+ text->priv = NULL;
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+get_bounds (FooCanvasText *text, double *px1, double *py1, double *px2, double *py2)
+{
+ FooCanvasItem *item;
+ double wx, wy;
+
+ item = FOO_CANVAS_ITEM (text);
+
+ /* Get canvas pixel coordinates for text position */
+
+
+ wx = text->x;
+ wy = text->y;
+ foo_canvas_item_i2w (item, &wx, &wy);
+ foo_canvas_w2c (item->canvas, wx + text->xofs, wy + text->yofs, &text->cx, &text->cy);
+
+ /* Get canvas pixel coordinates for clip rectangle position */
+
+ foo_canvas_w2c (item->canvas, wx, wy, &text->clip_cx, &text->clip_cy);
+ text->clip_cwidth = text->clip_width * item->canvas->pixels_per_unit;
+ text->clip_cheight = text->clip_height * item->canvas->pixels_per_unit;
+
+ /* Anchor text */
+
+ switch (text->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_SW:
+ break;
+
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_S:
+ text->cx -= text->max_width / 2;
+ text->clip_cx -= text->clip_cwidth / 2;
+ break;
+
+ case GTK_ANCHOR_NE:
+ case GTK_ANCHOR_E:
+ case GTK_ANCHOR_SE:
+ text->cx -= text->max_width;
+ text->clip_cx -= text->clip_cwidth;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (text->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_NE:
+ break;
+
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_E:
+ text->cy -= text->height / 2;
+ text->clip_cy -= text->clip_cheight / 2;
+ break;
+
+ case GTK_ANCHOR_SW:
+ case GTK_ANCHOR_S:
+ case GTK_ANCHOR_SE:
+ text->cy -= text->height;
+ text->clip_cy -= text->clip_cheight;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Bounds */
+
+ if (text->clip) {
+ *px1 = text->clip_cx;
+ *py1 = text->clip_cy;
+ *px2 = text->clip_cx + text->clip_cwidth;
+ *py2 = text->clip_cy + text->clip_cheight;
+ } else {
+ *px1 = text->cx;
+ *py1 = text->cy;
+ *px2 = text->cx + text->max_width;
+ *py2 = text->cy + text->height;
+ }
+}
+
+/* Convenience function to set the text's GC's foreground color */
+static void
+set_text_gc_foreground (FooCanvasText *text)
+{
+ GdkColor c;
+
+ if (!text->gc)
+ return;
+
+ c.pixel = text->pixel;
+ gdk_gc_set_foreground (text->gc, &c);
+}
+
+/* Sets the stipple pattern for the text */
+static void
+set_stipple (FooCanvasText *text, GdkBitmap *stipple, int reconfigure)
+{
+ if (text->stipple && !reconfigure)
+ g_object_unref (text->stipple);
+
+ text->stipple = stipple;
+ if (stipple && !reconfigure)
+ g_object_ref (stipple);
+
+ if (text->gc) {
+ if (stipple) {
+ gdk_gc_set_stipple (text->gc, stipple);
+ gdk_gc_set_fill (text->gc, GDK_STIPPLED);
+ } else
+ gdk_gc_set_fill (text->gc, GDK_SOLID);
+ }
+}
+
+static PangoFontMask
+get_property_font_set_mask (guint prop_id)
+{
+ switch (prop_id)
+ {
+ case PROP_FAMILY_SET:
+ return PANGO_FONT_MASK_FAMILY;
+ case PROP_STYLE_SET:
+ return PANGO_FONT_MASK_STYLE;
+ case PROP_VARIANT_SET:
+ return PANGO_FONT_MASK_VARIANT;
+ case PROP_WEIGHT_SET:
+ return PANGO_FONT_MASK_WEIGHT;
+ case PROP_STRETCH_SET:
+ return PANGO_FONT_MASK_STRETCH;
+ case PROP_SIZE_SET:
+ return PANGO_FONT_MASK_SIZE;
+ }
+
+ return 0;
+}
+
+static void
+ensure_font (FooCanvasText *text)
+{
+ if (!text->font_desc)
+ text->font_desc = pango_font_description_new ();
+}
+
+/* Set_arg handler for the text item */
+static void
+foo_canvas_text_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ FooCanvasItem *item;
+ FooCanvasText *text;
+ GdkColor color = { 0, 0, 0, 0, };
+ GdkColor *pcolor;
+ gboolean color_changed;
+ int have_pixel;
+ PangoAlignment align;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_TEXT (object));
+
+ item = FOO_CANVAS_ITEM (object);
+ text = FOO_CANVAS_TEXT (object);
+
+ color_changed = FALSE;
+ have_pixel = FALSE;
+
+
+ if (!text->layout) {
+ text->layout = gtk_widget_create_pango_layout (GTK_WIDGET (item->canvas), NULL);
+ }
+
+ switch (param_id) {
+ case PROP_TEXT:
+ if (text->text)
+ g_free (text->text);
+
+ text->text = g_value_dup_string (value);
+ pango_layout_set_text (text->layout, text->text, -1);
+
+ break;
+
+ case PROP_MARKUP:
+ foo_canvas_text_set_markup (text,
+ g_value_get_string (value));
+ break;
+
+ case PROP_X:
+ text->x = g_value_get_double (value);
+ break;
+
+ case PROP_Y:
+ text->y = g_value_get_double (value);
+ break;
+
+ case PROP_FONT: {
+ const char *font_name;
+ PangoFontDescription *font_desc;
+
+ font_name = g_value_get_string (value);
+ if (font_name)
+ font_desc = pango_font_description_from_string (font_name);
+ else
+ font_desc = NULL;
+
+ foo_canvas_text_set_font_desc (text, font_desc);
+ if (font_desc)
+ pango_font_description_free (font_desc);
+
+ break;
+ }
+
+ case PROP_FONT_DESC:
+ foo_canvas_text_set_font_desc (text, g_value_peek_pointer (value));
+ break;
+
+ case PROP_FAMILY:
+ case PROP_STYLE:
+ case PROP_VARIANT:
+ case PROP_WEIGHT:
+ case PROP_STRETCH:
+ case PROP_SIZE:
+ case PROP_SIZE_POINTS:
+ ensure_font (text);
+
+ switch (param_id) {
+ case PROP_FAMILY:
+ pango_font_description_set_family (text->font_desc,
+ g_value_get_string (value));
+ break;
+ case PROP_STYLE:
+ pango_font_description_set_style (text->font_desc,
+ g_value_get_enum (value));
+ break;
+ case PROP_VARIANT:
+ pango_font_description_set_variant (text->font_desc,
+ g_value_get_enum (value));
+ break;
+ case PROP_WEIGHT:
+ pango_font_description_set_weight (text->font_desc,
+ g_value_get_int (value));
+ break;
+ case PROP_STRETCH:
+ pango_font_description_set_stretch (text->font_desc,
+ g_value_get_enum (value));
+ break;
+ case PROP_SIZE:
+ /* FIXME: This is bogus! It should be pixels, not points/PANGO_SCALE! */
+ pango_font_description_set_size (text->font_desc,
+ g_value_get_int (value));
+ break;
+ case PROP_SIZE_POINTS:
+ pango_font_description_set_size (text->font_desc,
+ g_value_get_double (value) * PANGO_SCALE);
+ break;
+ }
+
+ foo_canvas_text_apply_font_desc (text);
+ break;
+
+ case PROP_FAMILY_SET:
+ case PROP_STYLE_SET:
+ case PROP_VARIANT_SET:
+ case PROP_WEIGHT_SET:
+ case PROP_STRETCH_SET:
+ case PROP_SIZE_SET:
+ if (!g_value_get_boolean (value) && text->font_desc)
+ pango_font_description_unset_fields (text->font_desc,
+ get_property_font_set_mask (param_id));
+ break;
+
+ case PROP_SCALE:
+ text->scale = g_value_get_double (value);
+ text->scale_set = TRUE;
+
+ foo_canvas_text_apply_font_desc (text);
+ break;
+
+ case PROP_SCALE_SET:
+ text->scale_set = g_value_get_boolean (value);
+
+ foo_canvas_text_apply_font_desc (text);
+ break;
+
+ case PROP_UNDERLINE:
+ text->underline = g_value_get_enum (value);
+ text->underline_set = TRUE;
+
+ foo_canvas_text_apply_attributes (text);
+ break;
+
+ case PROP_UNDERLINE_SET:
+ text->underline_set = g_value_get_boolean (value);
+
+ foo_canvas_text_apply_attributes (text);
+ break;
+
+ case PROP_STRIKETHROUGH:
+ text->strikethrough = g_value_get_boolean (value);
+ text->strike_set = TRUE;
+
+ foo_canvas_text_apply_attributes (text);
+ break;
+
+ case PROP_STRIKETHROUGH_SET:
+ text->strike_set = g_value_get_boolean (value);
+
+ foo_canvas_text_apply_attributes (text);
+ break;
+
+ case PROP_RISE:
+ text->rise = g_value_get_int (value);
+ text->rise_set = TRUE;
+
+ foo_canvas_text_apply_attributes (text);
+ break;
+
+ case PROP_RISE_SET:
+ text->rise_set = TRUE;
+
+ foo_canvas_text_apply_attributes (text);
+ break;
+
+ case PROP_ATTRIBUTES:
+ if (text->attr_list)
+ pango_attr_list_unref (text->attr_list);
+
+ text->attr_list = g_value_peek_pointer (value);
+ if (text->attr_list)
+ pango_attr_list_ref (text->attr_list);
+
+ foo_canvas_text_apply_attributes (text);
+ break;
+
+ case PROP_ANCHOR:
+ text->anchor = g_value_get_enum (value);
+ break;
+
+ case PROP_JUSTIFICATION:
+ text->justification = g_value_get_enum (value);
+
+ switch (text->justification) {
+ case GTK_JUSTIFY_LEFT:
+ align = PANGO_ALIGN_LEFT;
+ break;
+ case GTK_JUSTIFY_CENTER:
+ align = PANGO_ALIGN_CENTER;
+ break;
+ case GTK_JUSTIFY_RIGHT:
+ align = PANGO_ALIGN_RIGHT;
+ break;
+ default:
+ /* GTK_JUSTIFY_FILL isn't supported yet. */
+ align = PANGO_ALIGN_LEFT;
+ break;
+ }
+ pango_layout_set_alignment (text->layout, align);
+ break;
+
+ case PROP_CLIP_WIDTH:
+ text->clip_width = fabs (g_value_get_double (value));
+ break;
+
+ case PROP_CLIP_HEIGHT:
+ text->clip_height = fabs (g_value_get_double (value));
+ break;
+
+ case PROP_CLIP:
+ text->clip = g_value_get_boolean (value);
+ break;
+
+ case PROP_WRAP_WIDTH: {
+ double w = fabs (g_value_get_double (value));
+ pango_layout_set_width (text->layout,
+ w * text->item.canvas->pixels_per_unit * PANGO_SCALE);
+
+ break;
+ }
+
+ case PROP_X_OFFSET:
+ text->xofs = g_value_get_double (value);
+ break;
+
+ case PROP_Y_OFFSET:
+ text->yofs = g_value_get_double (value);
+ break;
+
+ case PROP_FILL_COLOR: {
+ const char *color_name;
+
+ color_name = g_value_get_string (value);
+ if (color_name) {
+ gdk_color_parse (color_name, &color);
+
+ text->rgba = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8 |
+ (color.blue & 0xff00) |
+ 0xff);
+ color_changed = TRUE;
+ }
+ break;
+ }
+
+ case PROP_FILL_COLOR_GDK:
+ pcolor = g_value_get_boxed (value);
+ if (pcolor) {
+ GdkColormap *colormap;
+
+ color = *pcolor;
+ colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+ gdk_rgb_find_color (colormap, &color);
+ have_pixel = TRUE;
+ }
+
+ text->rgba = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8|
+ (color.blue & 0xff00) |
+ 0xff);
+ color_changed = TRUE;
+ break;
+
+ case PROP_FILL_COLOR_RGBA:
+ text->rgba = g_value_get_uint (value);
+ color_changed = TRUE;
+ break;
+
+ case PROP_FILL_STIPPLE:
+ set_stipple (text, (GdkBitmap *)g_value_get_object (value), FALSE);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+
+ if (color_changed) {
+ if (have_pixel)
+ text->pixel = color.pixel;
+ else
+ text->pixel = foo_canvas_get_color_pixel (item->canvas, text->rgba);
+
+ set_text_gc_foreground (text);
+ }
+
+ /* Calculate text dimensions */
+
+ if (text->layout)
+ pango_layout_get_pixel_size (text->layout,
+ &text->max_width,
+ &text->height);
+ else {
+ text->max_width = 0;
+ text->height = 0;
+ }
+
+ foo_canvas_item_request_update (item);
+}
+
+/* Get_arg handler for the text item */
+static void
+foo_canvas_text_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ FooCanvasText *text;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_TEXT (object));
+
+ text = FOO_CANVAS_TEXT (object);
+
+ switch (param_id) {
+ case PROP_TEXT:
+ g_value_set_string (value, text->text);
+ break;
+
+ case PROP_X:
+ g_value_set_double (value, text->x);
+ break;
+
+ case PROP_Y:
+ g_value_set_double (value, text->y);
+ break;
+
+ case PROP_FONT:
+ case PROP_FONT_DESC:
+ case PROP_FAMILY:
+ case PROP_STYLE:
+ case PROP_VARIANT:
+ case PROP_WEIGHT:
+ case PROP_STRETCH:
+ case PROP_SIZE:
+ case PROP_SIZE_POINTS:
+ ensure_font (text);
+
+ switch (param_id) {
+ case PROP_FONT:
+ {
+ /* FIXME GValue imposes a totally gratuitous string copy
+ * here, we could just hand off string ownership
+ */
+ gchar *str;
+
+ str = pango_font_description_to_string (text->font_desc);
+ g_value_set_string (value, str);
+ g_free (str);
+
+ break;
+ }
+
+ case PROP_FONT_DESC:
+ g_value_set_boxed (value, text->font_desc);
+ break;
+
+ case PROP_FAMILY:
+ g_value_set_string (value, pango_font_description_get_family (text->font_desc));
+ break;
+
+ case PROP_STYLE:
+ g_value_set_enum (value, pango_font_description_get_style (text->font_desc));
+ break;
+
+ case PROP_VARIANT:
+ g_value_set_enum (value, pango_font_description_get_variant (text->font_desc));
+ break;
+
+ case PROP_WEIGHT:
+ g_value_set_int (value, pango_font_description_get_weight (text->font_desc));
+ break;
+
+ case PROP_STRETCH:
+ g_value_set_enum (value, pango_font_description_get_stretch (text->font_desc));
+ break;
+
+ case PROP_SIZE:
+ g_value_set_int (value, pango_font_description_get_size (text->font_desc));
+ break;
+
+ case PROP_SIZE_POINTS:
+ g_value_set_double (value, ((double)pango_font_description_get_size (text->font_desc)) / (double)PANGO_SCALE);
+ break;
+ }
+ break;
+
+ case PROP_FAMILY_SET:
+ case PROP_STYLE_SET:
+ case PROP_VARIANT_SET:
+ case PROP_WEIGHT_SET:
+ case PROP_STRETCH_SET:
+ case PROP_SIZE_SET:
+ {
+ PangoFontMask set_mask = text->font_desc ? pango_font_description_get_set_fields (text->font_desc) : 0;
+ PangoFontMask test_mask = get_property_font_set_mask (param_id);
+ g_value_set_boolean (value, (set_mask & test_mask) != 0);
+
+ break;
+ }
+
+ case PROP_SCALE:
+ g_value_set_double (value, text->scale);
+ break;
+ case PROP_SCALE_SET:
+ g_value_set_boolean (value, text->scale_set);
+ break;
+
+ case PROP_UNDERLINE:
+ g_value_set_enum (value, text->underline);
+ break;
+ case PROP_UNDERLINE_SET:
+ g_value_set_boolean (value, text->underline_set);
+ break;
+
+ case PROP_STRIKETHROUGH:
+ g_value_set_boolean (value, text->strikethrough);
+ break;
+ case PROP_STRIKETHROUGH_SET:
+ g_value_set_boolean (value, text->strike_set);
+ break;
+
+ case PROP_RISE:
+ g_value_set_int (value, text->rise);
+ break;
+ case PROP_RISE_SET:
+ g_value_set_boolean (value, text->rise_set);
+ break;
+
+ case PROP_ATTRIBUTES:
+ g_value_set_boxed (value, text->attr_list);
+ break;
+
+ case PROP_ANCHOR:
+ g_value_set_enum (value, text->anchor);
+ break;
+
+ case PROP_JUSTIFICATION:
+ g_value_set_enum (value, text->justification);
+ break;
+
+ case PROP_CLIP_WIDTH:
+ g_value_set_double (value, text->clip_width);
+ break;
+
+ case PROP_CLIP_HEIGHT:
+ g_value_set_double (value, text->clip_height);
+ break;
+
+ case PROP_CLIP:
+ g_value_set_boolean (value, text->clip);
+ break;
+
+ case PROP_WRAP_WIDTH:
+ g_value_set_double (value,
+ pango_layout_get_width (text->layout) / PANGO_SCALE);
+ break;
+
+ case PROP_X_OFFSET:
+ g_value_set_double (value, text->xofs);
+ break;
+
+ case PROP_Y_OFFSET:
+ g_value_set_double (value, text->yofs);
+ break;
+
+ case PROP_FILL_COLOR:
+ g_value_take_string (value,
+ g_strdup_printf ("#%02x%02x%02x",
+ text->rgba >> 24,
+ (text->rgba >> 16) & 0xff,
+ (text->rgba >> 8) & 0xff));
+ break;
+
+ case PROP_FILL_COLOR_GDK: {
+ FooCanvas *canvas = FOO_CANVAS_ITEM (text)->canvas;
+ GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+ GdkColor color;
+
+ gdk_colormap_query_color (colormap, text->pixel, &color);
+ g_value_set_boxed (value, &color);
+ break;
+ }
+ case PROP_FILL_COLOR_RGBA:
+ g_value_set_uint (value, text->rgba);
+ break;
+
+ case PROP_FILL_STIPPLE:
+ g_value_set_object (value, text->stipple);
+ break;
+
+ case PROP_TEXT_WIDTH:
+ g_value_set_double (value, text->max_width / text->item.canvas->pixels_per_unit);
+ break;
+
+ case PROP_TEXT_HEIGHT:
+ g_value_set_double (value, text->height / text->item.canvas->pixels_per_unit);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+/* */
+static void
+foo_canvas_text_apply_font_desc (FooCanvasText *text)
+{
+ PangoFontDescription *font_desc =
+ pango_font_description_copy (
+ GTK_WIDGET (FOO_CANVAS_ITEM (text)->canvas)->style->font_desc);
+
+ if (text->font_desc)
+ pango_font_description_merge (font_desc, text->font_desc, TRUE);
+
+ pango_layout_set_font_description (text->layout, font_desc);
+ pango_font_description_free (font_desc);
+}
+
+static void
+add_attr (PangoAttrList *attr_list,
+ PangoAttribute *attr)
+{
+ attr->start_index = 0;
+ attr->end_index = G_MAXINT;
+
+ pango_attr_list_insert (attr_list, attr);
+}
+
+/* */
+static void
+foo_canvas_text_apply_attributes (FooCanvasText *text)
+{
+ PangoAttrList *attr_list;
+ double zoom;
+
+ if (text->attr_list)
+ attr_list = pango_attr_list_copy (text->attr_list);
+ else
+ attr_list = pango_attr_list_new ();
+
+ if (text->underline_set)
+ add_attr (attr_list, pango_attr_underline_new (text->underline));
+ if (text->strike_set)
+ add_attr (attr_list, pango_attr_strikethrough_new (text->strikethrough));
+ if (text->rise_set)
+ add_attr (attr_list, pango_attr_rise_new (text->rise));
+
+ zoom = text->item.canvas->pixels_per_unit;
+ if (fabs (zoom - 1.) > 1e-4) {
+ PangoAttribute *attr = pango_attr_scale_new (zoom);
+ attr->start_index = 0;
+ attr->end_index = -1;
+ pango_attr_list_insert_before (attr_list, attr);
+ }
+
+ pango_layout_set_attributes (text->layout, attr_list);
+ pango_attr_list_unref (attr_list);
+}
+
+static void
+foo_canvas_text_set_font_desc (FooCanvasText *text,
+ PangoFontDescription *font_desc)
+{
+ if (text->font_desc)
+ pango_font_description_free (text->font_desc);
+
+ if (font_desc)
+ text->font_desc = pango_font_description_copy (font_desc);
+ else
+ text->font_desc = NULL;
+
+ foo_canvas_text_apply_font_desc (text);
+}
+
+/* Setting the text from a Pango markup string */
+static void
+foo_canvas_text_set_markup (FooCanvasText *textitem,
+ const gchar *markup)
+{
+ PangoAttrList *attr_list = NULL;
+ gchar *text = NULL;
+ GError *error = NULL;
+
+ if (textitem->text)
+ g_free (textitem->text);
+ if (textitem->attr_list)
+ pango_attr_list_unref (textitem->attr_list);
+
+ if (markup && !pango_parse_markup (markup, -1,
+ 0,
+ &attr_list, &text, NULL,
+ &error))
+ {
+ g_warning ("Failed to set cell text from markup due to error parsing markup: %s",
+ error->message);
+ g_error_free (error);
+ return;
+ }
+
+ textitem->text = text;
+ textitem->attr_list = attr_list;
+
+ pango_layout_set_text (textitem->layout, text, -1);
+
+ foo_canvas_text_apply_attributes (textitem);
+}
+
+/* Update handler for the text item */
+static void
+foo_canvas_text_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
+{
+ FooCanvasText *text;
+ double x1, y1, x2, y2;
+
+ text = FOO_CANVAS_TEXT (item);
+
+ if (parent_class->update)
+ (* parent_class->update) (item, i2w_dx, i2w_dy, flags);
+
+ set_text_gc_foreground (text);
+ set_stipple (text, text->stipple, TRUE);
+ get_bounds (text, &x1, &y1, &x2, &y2);
+
+ foo_canvas_update_bbox (item,
+ floor (x1+.5), floor (y1+.5),
+ floor (x2+.5), floor (y2+.5));
+}
+
+/* Realize handler for the text item */
+static void
+foo_canvas_text_realize (FooCanvasItem *item)
+{
+ FooCanvasText *text;
+
+ text = FOO_CANVAS_TEXT (item);
+
+ if (parent_class->realize)
+ (* parent_class->realize) (item);
+
+ text->gc = gdk_gc_new (item->canvas->layout.bin_window);
+}
+
+/* Unrealize handler for the text item */
+static void
+foo_canvas_text_unrealize (FooCanvasItem *item)
+{
+ FooCanvasText *text;
+
+ text = FOO_CANVAS_TEXT (item);
+
+ g_object_unref (text->gc);
+ text->gc = NULL;
+
+ if (parent_class->unrealize)
+ (* parent_class->unrealize) (item);
+}
+
+/* Draw handler for the text item */
+static void
+foo_canvas_text_draw (FooCanvasItem *item, GdkDrawable *drawable,
+ GdkEventExpose *expose)
+{
+ FooCanvasText *text;
+ GdkRectangle rect;
+
+ text = FOO_CANVAS_TEXT (item);
+
+ if (!text->text)
+ return;
+
+ if (text->clip) {
+ rect.x = text->clip_cx;
+ rect.y = text->clip_cy;
+ rect.width = text->clip_cwidth;
+ rect.height = text->clip_cheight;
+
+ gdk_gc_set_clip_rectangle (text->gc, &rect);
+ }
+
+ if (text->stipple)
+ foo_canvas_set_stipple_origin (item->canvas, text->gc);
+
+
+ gdk_draw_layout (drawable, text->gc, text->cx, text->cy, text->layout);
+
+ if (text->clip)
+ gdk_gc_set_clip_rectangle (text->gc, NULL);
+}
+
+/* Point handler for the text item */
+static double
+foo_canvas_text_point (FooCanvasItem *item, double x, double y,
+ int cx, int cy, FooCanvasItem **actual_item)
+{
+ FooCanvasText *text;
+ PangoLayoutIter *iter;
+ int x1, y1, x2, y2;
+ int dx, dy;
+ double dist, best;
+
+ text = FOO_CANVAS_TEXT (item);
+
+ *actual_item = item;
+
+ /* The idea is to build bounding rectangles for each of the lines of
+ * text (clipped by the clipping rectangle, if it is activated) and see
+ * whether the point is inside any of these. If it is, we are done.
+ * Otherwise, calculate the distance to the nearest rectangle.
+ */
+
+ best = 1.0e36;
+
+ iter = pango_layout_get_iter (text->layout);
+ do {
+ PangoRectangle log_rect;
+
+ pango_layout_iter_get_line_extents (iter, NULL, &log_rect);
+
+ if (text->clip) {
+ x1 = PANGO_PIXELS (log_rect.x);
+ y1 = PANGO_PIXELS (log_rect.y);
+ x2 = PANGO_PIXELS (log_rect.x+log_rect.width);
+ y2 = PANGO_PIXELS (log_rect.y+log_rect.height);
+
+
+ if (x1 < text->clip_cx)
+ x1 = text->clip_cx;
+
+ if (y1 < text->clip_cy)
+ y1 = text->clip_cy;
+
+ if (x2 > (text->clip_cx + text->clip_width))
+ x2 = text->clip_cx + text->clip_width;
+
+ if (y2 > (text->clip_cy + text->clip_height))
+ y2 = text->clip_cy + text->clip_height;
+
+ if ((x1 >= x2) || (y1 >= y2))
+ continue;
+ } else {
+ x1 = text->x;
+ y1 = text->y;
+ x2 = log_rect.width;
+ y2 = log_rect.height;
+ }
+
+ /* Calculate distance from point to rectangle */
+
+ if (cx < x1)
+ dx = x1 - cx;
+ else if (cx >= x2)
+ dx = cx - x2 + 1;
+ else
+ dx = 0;
+
+ if (cy < y1)
+ dy = y1 - cy;
+ else if (cy >= y2)
+ dy = cy - y2 + 1;
+ else
+ dy = 0;
+
+ if ((dx == 0) && (dy == 0)) {
+ pango_layout_iter_free(iter);
+ return 0.0;
+ }
+
+ dist = sqrt (dx * dx + dy * dy);
+ if (dist < best)
+ best = dist;
+
+ } while (pango_layout_iter_next_line(iter));
+
+ pango_layout_iter_free(iter);
+
+ return best / item->canvas->pixels_per_unit;
+}
+
+static void
+foo_canvas_text_translate (FooCanvasItem *item, double dx, double dy)
+{
+ FooCanvasText *text;
+
+ text = FOO_CANVAS_TEXT (item);
+
+ text->x += dx;
+ text->y += dy;
+}
+
+/* Bounds handler for the text item */
+static void
+foo_canvas_text_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ FooCanvasText *text;
+ double width, height;
+
+ text = FOO_CANVAS_TEXT (item);
+
+ *x1 = text->x;
+ *y1 = text->y;
+
+ if (text->clip) {
+ width = text->clip_width;
+ height = text->clip_height;
+ } else {
+ width = text->max_width / item->canvas->pixels_per_unit;
+ height = text->height / item->canvas->pixels_per_unit;
+ }
+
+ switch (text->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_SW:
+ break;
+
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_S:
+ *x1 -= width / 2.0;
+ break;
+
+ case GTK_ANCHOR_NE:
+ case GTK_ANCHOR_E:
+ case GTK_ANCHOR_SE:
+ *x1 -= width;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (text->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_NE:
+ break;
+
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_E:
+ *y1 -= height / 2.0;
+ break;
+
+ case GTK_ANCHOR_SW:
+ case GTK_ANCHOR_S:
+ case GTK_ANCHOR_SE:
+ *y1 -= height;
+ break;
+
+ default:
+ break;
+ }
+
+ *x2 = *x1 + width;
+ *y2 = *y1 + height;
+}
diff --git a/libfoocanvas/foo-canvas-text.h b/libfoocanvas/foo-canvas-text.h
new file mode 100644
index 0000000..571c642
--- /dev/null
+++ b/libfoocanvas/foo-canvas-text.h
@@ -0,0 +1,168 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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@
+ */
+/* Text item type for FooCanvas widget
+ *
+ * FooCanvas 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 <federico nuclecu unam mx>
+ * Port to Pango co-done by Gergõ Érdi <cactus cactus rulez org>
+ */
+
+#ifndef FOO_CANVAS_TEXT_H
+#define FOO_CANVAS_TEXT_H
+
+
+#include <libfoocanvas/foo-canvas.h>
+
+
+G_BEGIN_DECLS
+
+
+/* Text item for the canvas. Text items are positioned by an anchor point and an anchor direction.
+ *
+ * A clipping rectangle may be specified for the text. The rectangle is anchored at the text's anchor
+ * point, and is specified by clipping width and height parameters. If the clipping rectangle is
+ * enabled, it will clip the text.
+ *
+ * In addition, x and y offset values may be specified. These specify an offset from the anchor
+ * position. If used in conjunction with the clipping rectangle, these could be used to implement
+ * simple scrolling of the text within the clipping rectangle.
+ *
+ * Properties marked with [*] also have _set properties associated
+ * with them, that determine if the specified value should be used
+ * instead of the default (style-defined) values
+ *
+ * The following object arguments are available:
+ *
+ * name type read/write description
+ * ------------------------------------------------------------------------------------------
+ * text string RW The string of the text label
+ * markup string W A Pango markup string for the text label
+ *
+ * x double RW X coordinate of anchor point
+ * y double RW Y coordinate of anchor point
+ *
+ * font string W A string describing the font
+ * font_desc PangoFontDescription* RW Pointer to a PangoFontDescriptor
+ * attributes PangoAttrList* RW Pointer to a Pango attribute list
+ * style PangoStyle RW Pango style of font to use [*]
+ * variant PangoVariant RW Pango variant of font to use [*]
+ * weight int RW Pango weight of font to use [*]
+ * stretch PangoStretch RW Pango stretch of font to use [*]
+ * size int RW Size (in pixels) of font [*]
+ * size_points double RW Size (in points) of font
+ * scale double RW Ratio to scale font [*]
+ *
+ * anchor GtkAnchorType RW Anchor side for the text
+ * justification GtkJustification RW Justification for multiline text
+ * clip_width double RW Width of clip rectangle
+ * clip_height double RW Height of clip rectangle
+ * clip boolean RW Use clipping rectangle?
+ * x_offset double RW Horizontal offset distance from anchor position
+ * y_offset double RW Vertical offset distance from anchor position
+ *
+ * text_width double R Used to query the width of the rendered text
+ * text_height double R Used to query the rendered height of the text
+ *
+ * fill_color string W X color specification for text
+ * fill_color_gdk GdkColor* RW Pointer to an allocated GdkColor
+ * fill_color_rgba guint RW RGBA value used for AA color.
+ * fill_stipple GdkBitmap* RW Stipple pattern for filling the text
+ */
+
+#define FOO_TYPE_CANVAS_TEXT (foo_canvas_text_get_type ())
+#define FOO_CANVAS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_TEXT, FooCanvasText))
+#define FOO_CANVAS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_TEXT, FooCanvasTextClass))
+#define FOO_IS_CANVAS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_TEXT))
+#define FOO_IS_CANVAS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_TEXT))
+#define FOO_CANVAS_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_TEXT, FooCanvasTextClass))
+
+
+typedef struct _FooCanvasText FooCanvasText;
+typedef struct _FooCanvasTextClass FooCanvasTextClass;
+
+typedef struct _FooCanvasTextPrivate FooCanvasTextPrivate;
+
+struct _FooCanvasText {
+ FooCanvasItem item;
+
+ PangoFontDescription *font_desc; /* Font description for text */
+ PangoAttrList *attr_list; /* Attribute list of the text (caching) */
+ PangoUnderline underline;
+ gboolean strikethrough;
+ int rise;
+ double scale;
+
+ char *text; /* Text to display */
+ GdkBitmap *stipple; /* Stipple for text */
+ GdkGC *gc; /* GC for drawing text */
+ PangoLayout *layout; /* The PangoLayout containing the text */
+
+ gulong pixel; /* Fill color */
+
+ double x, y; /* Position at anchor */
+
+ double clip_width; /* Width of optional clip rectangle */
+ double clip_height; /* Height of optional clip rectangle */
+
+ double xofs, yofs; /* Text offset distance from anchor position */
+
+ GtkAnchorType anchor; /* Anchor side for text */
+ GtkJustification justification; /* Justification for text */
+
+ int cx, cy; /* Top-left canvas coordinates for text */
+ int clip_cx, clip_cy; /* Top-left canvas coordinates for clip rectangle */
+ int clip_cwidth, clip_cheight; /* Size of clip rectangle in pixels */
+ int max_width; /* Maximum width of text lines */
+ int height; /* Rendered text height in pixels */
+
+ guint32 rgba; /* RGBA color for text */ /*AA*/
+
+ guint clip : 1; /* Use clip rectangle? */
+
+ guint underline_set : 1; /* Apply specified underline style? */
+ guint strike_set : 1; /* Apply specified strikethrough style? */
+ guint rise_set : 1; /* Apply specified ascension/descension? */
+
+ guint scale_set : 1; /* Apply specified font scaling ratio? */
+
+ FooCanvasTextPrivate *priv;
+};
+
+struct _FooCanvasTextClass {
+ FooCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_text_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libfoocanvas/foo-canvas-util.c b/libfoocanvas/foo-canvas-util.c
new file mode 100644
index 0000000..879db57
--- /dev/null
+++ b/libfoocanvas/foo-canvas-util.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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@
+ */
+/* Miscellaneous utility functions for the FooCanvas widget
+ *
+ * FooCanvas 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 <federico nuclecu unam mx>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <glib.h>
+#include <math.h>
+#include "foo-canvas.h"
+#include "foo-canvas-util.h"
+
+/*
+ * Ok, so some systems require magic incantations for M_PI to be defined.
+ * It's not important enough to worry about.
+ */
+#ifndef M_PI
+#define M_PI 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117
+#endif
+
+/**
+ * foo_canvas_points_new:
+ * @num_points: The number of points to allocate space for in the array.
+ *
+ * Creates a structure that should be used to pass an array of points to
+ * items.
+ *
+ * Return value: A newly-created array of points. It should be filled in
+ * by the user.
+ **/
+FooCanvasPoints *
+foo_canvas_points_new (int num_points)
+{
+ FooCanvasPoints *points;
+
+ g_return_val_if_fail (num_points > 1, NULL);
+
+ points = g_new (FooCanvasPoints, 1);
+ points->num_points = num_points;
+ points->coords = g_new (double, 2 * num_points);
+ points->ref_count = 1;
+
+ return points;
+}
+
+/**
+ * foo_canvas_points_ref:
+ * @points: A canvas points structure.
+ *
+ * Increases the reference count of the specified points structure.
+ *
+ * Return value: The canvas points structure itself.
+ **/
+FooCanvasPoints *
+foo_canvas_points_ref (FooCanvasPoints *points)
+{
+ g_return_val_if_fail (points != NULL, NULL);
+
+ points->ref_count += 1;
+ return points;
+}
+
+/**
+ * foo_canvas_points_free:
+ * @points: A canvas points structure.
+ *
+ * Decreases the reference count of the specified points structure. If it
+ * reaches zero, then the structure is freed.
+ **/
+void
+foo_canvas_points_free (FooCanvasPoints *points)
+{
+ g_return_if_fail (points != NULL);
+
+ points->ref_count -= 1;
+ if (points->ref_count == 0) {
+ g_free (points->coords);
+ g_free (points);
+ }
+}
+
+/**
+ * foo_canvas_get_miter_points:
+ * @x1: X coordinate of the first point
+ * @y1: Y coordinate of the first point
+ * @x2: X coordinate of the second (angle) point
+ * @y2: Y coordinate of the second (angle) point
+ * @x3: X coordinate of the third point
+ * @y3: Y coordinate of the third point
+ * @width: Width of the line
+ * @mx1: The X coordinate of the first miter point is returned here.
+ * @my1: The Y coordinate of the first miter point is returned here.
+ * @mx2: The X coordinate of the second miter point is returned here.
+ * @my2: The Y coordinate of the second miter point is returned here.
+ *
+ * Given three points forming an angle, computes the coordinates of the inside
+ * and outside points of the mitered corner formed by a line of a given width at
+ * that angle.
+ *
+ * Return value: FALSE if the angle is less than 11 degrees (this is the same
+ * threshold as X uses. If this occurs, the return points are not modified.
+ * Otherwise, returns TRUE.
+ **/
+int
+foo_canvas_get_miter_points (double x1, double y1, double x2, double y2, double x3, double y3,
+ double width,
+ double *mx1, double *my1, double *mx2, double *my2)
+{
+ double theta1; /* angle of segment p2-p1 */
+ double theta2; /* angle of segment p2-p3 */
+ double theta; /* angle between line segments */
+ double theta3; /* angle that bisects theta1 and theta2 and points to p1 */
+ double dist; /* distance of miter points from p2 */
+ double dx, dy; /* x and y offsets corresponding to dist */
+
+ double ELEVEN_DEGREES = 11.0 * M_PI / 180.0;
+
+ /* Degenerate cases. */
+ if ((x1 == x2 && y1 == y2) || (x2 == x3 && y2 == y3))
+ return FALSE;
+
+ theta1 = atan2 (y1 - y2, x1 - x2);
+ theta2 = atan2 (y3 - y2, x3 - x2);
+ theta = theta1 - theta2;
+
+ /* Normalize to (-pi; pi]. */
+ if (theta > M_PI)
+ theta -= 2.0 * M_PI;
+ else if (theta <= -M_PI)
+ theta += 2.0 * M_PI;
+
+ if (fabs (theta) < ELEVEN_DEGREES)
+ return FALSE;
+
+ dist = fabs (0.5 * width / sin (0.5 * theta));
+
+ theta3 = (theta1 + theta2) / 2.0;
+ if (sin (theta3 - theta1) > 0.0)
+ theta3 += M_PI;
+
+ dx = dist * cos (theta3);
+ dy = dist * sin (theta3);
+
+ *mx1 = x2 + dx;
+ *mx2 = x2 - dx;
+ *my1 = y2 + dy;
+ *my2 = y2 - dy;
+
+ return TRUE;
+}
+
+/**
+ * foo_canvas_get_butt_points:
+ * @x1: X coordinate of first point in the line
+ * @y1: Y cooordinate of first point in the line
+ * @x2: X coordinate of second point (endpoint) of the line
+ * @y2: Y coordinate of second point (endpoint) of the line
+ * @width: Width of the line
+ * @project: Whether the butt points should project out by width/2 distance
+ * @bx1: X coordinate of first butt point is returned here
+ * @by1: Y coordinate of first butt point is returned here
+ * @bx2: X coordinate of second butt point is returned here
+ * @by2: Y coordinate of second butt point is returned here
+ *
+ * Computes the butt points of a line segment.
+ **/
+void
+foo_canvas_get_butt_points (double x1, double y1, double x2, double y2,
+ double width, int project,
+ double *bx1, double *by1, double *bx2, double *by2)
+{
+ double length;
+ double dx, dy;
+
+ width *= 0.5;
+ dx = x2 - x1;
+ dy = y2 - y1;
+ length = sqrt (dx * dx + dy * dy);
+
+ if (length < FOO_CANVAS_EPSILON) {
+ *bx1 = *bx2 = x2;
+ *by1 = *by2 = y2;
+ } else {
+ dx = -width * (y2 - y1) / length;
+ dy = width * (x2 - x1) / length;
+
+ *bx1 = x2 + dx;
+ *bx2 = x2 - dx;
+ *by1 = y2 + dy;
+ *by2 = y2 - dy;
+
+ if (project) {
+ *bx1 += dy;
+ *bx2 += dy;
+ *by1 -= dx;
+ *by2 -= dx;
+ }
+ }
+}
+
+/**
+ * foo_canvas_polygon_to_point:
+ * @poly: Vertices of the polygon. X coordinates are in the even indices, and Y
+ * coordinates are in the odd indices
+ * @num_points: Number of points in the polygon
+ * @x: X coordinate of the point
+ * @y: Y coordinate of the point
+ *
+ * Computes the distance between a point and a polygon.
+ *
+ * Return value: The distance from the point to the polygon, or zero if the
+ * point is inside the polygon.
+ **/
+double
+foo_canvas_polygon_to_point (double *poly, int num_points, double x, double y)
+{
+ double best;
+ int intersections;
+ int i;
+ double *p;
+ double dx, dy;
+
+ /* Iterate through all the edges in the polygon, updating best and intersections.
+ *
+ * When computing intersections, include left X coordinate of line within its range, but not
+ * Y coordinate. Otherwise if the point lies exactly below a vertex we'll count it as two
+ * intersections.
+ */
+
+ best = 1.0e36;
+ if (poly == NULL)
+ return best;
+
+ intersections = 0;
+
+ for (i = num_points, p = poly; i > 1; i--, p += 2) {
+ double px, py, dist;
+
+ /* Compute the point on the current edge closest to the point and update the
+ * intersection count. This must be done separately for vertical edges, horizontal
+ * edges, and others.
+ */
+
+ if (p[2] == p[0]) {
+ /* Vertical edge */
+
+ px = p[0];
+
+ if (p[1] >= p[3]) {
+ py = MIN (p[1], y);
+ py = MAX (py, p[3]);
+ } else {
+ py = MIN (p[3], y);
+ py = MAX (py, p[1]);
+ }
+ } else if (p[3] == p[1]) {
+ /* Horizontal edge */
+
+ py = p[1];
+
+ if (p[0] >= p[2]) {
+ px = MIN (p[0], x);
+ px = MAX (px, p[2]);
+
+ if ((y < py) && (x < p[0]) && (x >= p[2]))
+ intersections++;
+ } else {
+ px = MIN (p[2], x);
+ px = MAX (px, p[0]);
+
+ if ((y < py) && (x < p[2]) && (x >= p[0]))
+ intersections++;
+ }
+ } else {
+ double m1, b1, m2, b2;
+ int lower;
+
+ /* Diagonal edge. Convert the edge to a line equation (y = m1*x + b1), then
+ * compute a line perpendicular to this edge but passing through the point,
+ * (y = m2*x + b2).
+ */
+
+ m1 = (p[3] - p[1]) / (p[2] - p[0]);
+ b1 = p[1] - m1 * p[0];
+
+ m2 = -1.0 / m1;
+ b2 = y - m2 * x;
+
+ px = (b2 - b1) / (m1 - m2);
+ py = m1 * px + b1;
+
+ if (p[0] > p[2]) {
+ if (px > p[0]) {
+ px = p[0];
+ py = p[1];
+ } else if (px < p[2]) {
+ px = p[2];
+ py = p[3];
+ }
+ } else {
+ if (px > p[2]) {
+ px = p[2];
+ py = p[3];
+ } else if (px < p[0]) {
+ px = p[0];
+ py = p[1];
+ }
+ }
+
+ lower = (m1 * x + b1) > y;
+
+ if (lower && (x >= MIN (p[0], p[2])) && (x < MAX (p[0], p[2])))
+ intersections++;
+ }
+
+ /* Compute the distance to the closest point, and see if that is the best so far */
+
+ dx = x - px;
+ dy = y - py;
+ dist = sqrt (dx * dx + dy * dy);
+ if (dist < best)
+ best = dist;
+ }
+
+ /* We've processed all the points. If the number of intersections is odd, the point is
+ * inside the polygon.
+ */
+
+ if (intersections & 0x1)
+ return 0.0;
+ else
+ return best;
+}
+
+/**
+ * foo_canvas_item_reset_bounds:
+ * @item: A canvas item
+ *
+ * Resets the bounding box of a canvas item to an empty rectangle.
+ **/
+void
+foo_canvas_item_reset_bounds (FooCanvasItem *item)
+{
+ item->x1 = 0.0;
+ item->y1 = 0.0;
+ item->x2 = 0.0;
+ item->y2 = 0.0;
+}
+
+/**
+ * foo_canvas_update_bbox:
+ * @canvas: the canvas needing update
+ * @x1: Left coordinate of the new bounding box
+ * @y1: Top coordinate of the new bounding box
+ * @x2: Right coordinate of the new bounding box
+ * @y2: Bottom coordinate of the new bounding box
+ *
+ * Sets the bbox to the new value, requesting full repaint.
+ **/
+void
+foo_canvas_update_bbox (FooCanvasItem *item, int x1, int y1, int x2, int y2)
+{
+ foo_canvas_item_request_redraw (item);
+ item->x1 = x1;
+ item->y1 = y1;
+ item->x2 = x2;
+ item->y2 = y2;
+ foo_canvas_item_request_redraw (item);
+}
+
diff --git a/libfoocanvas/foo-canvas-util.h b/libfoocanvas/foo-canvas-util.h
new file mode 100644
index 0000000..63fc3ac
--- /dev/null
+++ b/libfoocanvas/foo-canvas-util.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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@
+ */
+/* Miscellaneous utility functions for the FooCanvas widget
+ *
+ * FooCanvas 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 <federico nuclecu unam mx>
+ */
+
+#ifndef FOO_CANVAS_UTIL_H
+#define FOO_CANVAS_UTIL_H
+
+
+G_BEGIN_DECLS
+
+
+/* This structure defines an array of points. X coordinates are stored in the even-numbered
+ * indices, and Y coordinates are stored in the odd-numbered indices. num_points indicates the
+ * number of points, so the array is 2*num_points elements big.
+ */
+typedef struct {
+ double *coords;
+ int num_points;
+ int ref_count;
+} FooCanvasPoints;
+
+
+/* Allocate a new FooCanvasPoints structure with enough space for the specified number of points */
+FooCanvasPoints *foo_canvas_points_new (int num_points);
+
+/* Increate ref count */
+FooCanvasPoints *foo_canvas_points_ref (FooCanvasPoints *points);
+#define foo_canvas_points_unref foo_canvas_points_free
+
+/* Decrease ref count and free structure if it has reached zero */
+void foo_canvas_points_free (FooCanvasPoints *points);
+
+/* Given three points forming an angle, compute the coordinates of the inside and outside points of
+ * the mitered corner formed by a line of a given width at that angle.
+ *
+ * If the angle is less than 11 degrees, then FALSE is returned and the return points are not
+ * modified. Otherwise, TRUE is returned.
+ */
+int foo_canvas_get_miter_points (double x1, double y1, double x2, double y2, double x3, double y3,
+ double width,
+ double *mx1, double *my1, double *mx2, double *my2);
+
+/* Compute the butt points of a line segment. If project is FALSE, then the results are as follows:
+ *
+ * -------------------* (bx1, by1)
+ * |
+ * (x1, y1) *------------------* (x2, y2)
+ * |
+ * -------------------* (bx2, by2)
+ *
+ * that is, the line is not projected beyond (x2, y2). If project is TRUE, then the results are as
+ * follows:
+ *
+ * -------------------* (bx1, by1)
+ * (x2, y2) |
+ * (x1, y1) *-------------* |
+ * |
+ * -------------------* (bx2, by2)
+ */
+void foo_canvas_get_butt_points (double x1, double y1, double x2, double y2,
+ double width, int project,
+ double *bx1, double *by1, double *bx2, double *by2);
+
+/* Calculate the distance from a polygon to a point. The polygon's X coordinates are in the even
+ * indices of the poly array, and the Y coordinates are in the odd indices.
+ */
+double foo_canvas_polygon_to_point (double *poly, int num_points, double x, double y);
+
+
+void foo_canvas_item_reset_bounds (FooCanvasItem *item);
+
+/* Sets the bbox to the new value, requesting full repaint. */
+void foo_canvas_update_bbox (FooCanvasItem *item, int x1, int y1, int x2, int y2);
+
+G_END_DECLS
+
+#endif
diff --git a/libfoocanvas/foo-canvas-widget.c b/libfoocanvas/foo-canvas-widget.c
new file mode 100644
index 0000000..0ba8aba
--- /dev/null
+++ b/libfoocanvas/foo-canvas-widget.c
@@ -0,0 +1,591 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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@
+ */
+/* Widget item type for FooCanvas widget
+ *
+ * FooCanvas 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 <federico nuclecu unam mx>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <glib-object.h>
+#include "foo-canvas-widget.h"
+
+enum {
+ PROP_0,
+ PROP_WIDGET,
+ PROP_X,
+ PROP_Y,
+ PROP_WIDTH,
+ PROP_HEIGHT,
+ PROP_ANCHOR,
+ PROP_SIZE_PIXELS
+};
+
+
+static void foo_canvas_widget_class_init (FooCanvasWidgetClass *klass);
+static void foo_canvas_widget_init (FooCanvasWidget *witem);
+static void foo_canvas_widget_destroy (GtkObject *object);
+static void foo_canvas_widget_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void foo_canvas_widget_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void foo_canvas_widget_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags);
+static double foo_canvas_widget_point (FooCanvasItem *item, double x, double y,
+ int cx, int cy, FooCanvasItem **actual_item);
+static void foo_canvas_widget_translate (FooCanvasItem *item, double dx, double dy);
+static void foo_canvas_widget_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+static void foo_canvas_widget_draw (FooCanvasItem *item,
+ GdkDrawable *drawable,
+ GdkEventExpose *event);
+static void foo_canvas_widget_map (FooCanvasItem *item);
+static void foo_canvas_widget_unmap (FooCanvasItem *item);
+
+static FooCanvasItemClass *parent_class;
+
+G_DEFINE_TYPE (FooCanvasWidget, foo_canvas_widget, FOO_TYPE_CANVAS_ITEM)
+
+static void
+foo_canvas_widget_class_init (FooCanvasWidgetClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ FooCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) klass;
+ object_class = (GtkObjectClass *) klass;
+ item_class = (FooCanvasItemClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->set_property = foo_canvas_widget_set_property;
+ gobject_class->get_property = foo_canvas_widget_get_property;
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDGET,
+ g_param_spec_object ("widget", NULL, NULL,
+ GTK_TYPE_WIDGET,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X,
+ g_param_spec_double ("x", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y,
+ g_param_spec_double ("y", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDTH,
+ g_param_spec_double ("width", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_HEIGHT,
+ g_param_spec_double ("height", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_ANCHOR,
+ g_param_spec_enum ("anchor", NULL, NULL,
+ GTK_TYPE_ANCHOR_TYPE,
+ GTK_ANCHOR_NW,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SIZE_PIXELS,
+ g_param_spec_boolean ("size-pixels", NULL, NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ object_class->destroy = foo_canvas_widget_destroy;
+
+ item_class->update = foo_canvas_widget_update;
+ item_class->point = foo_canvas_widget_point;
+ item_class->translate = foo_canvas_widget_translate;
+ item_class->bounds = foo_canvas_widget_bounds;
+ item_class->draw = foo_canvas_widget_draw;
+ item_class->map = foo_canvas_widget_map;
+ item_class->unmap = foo_canvas_widget_unmap;
+}
+
+static void
+foo_canvas_widget_init (FooCanvasWidget *witem)
+{
+ witem->x = 0.0;
+ witem->y = 0.0;
+ witem->width = 0.0;
+ witem->height = 0.0;
+ witem->anchor = GTK_ANCHOR_NW;
+ witem->size_pixels = FALSE;
+}
+
+static void
+foo_canvas_widget_destroy (GtkObject *object)
+{
+ FooCanvasWidget *witem;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_WIDGET (object));
+
+ witem = FOO_CANVAS_WIDGET (object);
+
+ if (witem->widget && !witem->in_destroy) {
+ g_signal_handler_disconnect (G_OBJECT (witem->widget), witem->destroy_id);
+ gtk_widget_destroy (witem->widget);
+ witem->widget = NULL;
+ }
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+recalc_bounds (FooCanvasWidget *witem)
+{
+ FooCanvasItem *item;
+ double wx, wy;
+
+ item = FOO_CANVAS_ITEM (witem);
+
+ /* Get world coordinates */
+
+ wx = witem->x;
+ wy = witem->y;
+ foo_canvas_item_i2w (item, &wx, &wy);
+
+ /* Get canvas pixel coordinates */
+
+ foo_canvas_w2c (item->canvas, wx, wy, &witem->cx, &witem->cy);
+
+ /* Anchor widget item */
+
+ switch (witem->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_SW:
+ break;
+
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_S:
+ witem->cx -= witem->cwidth / 2;
+ break;
+
+ case GTK_ANCHOR_NE:
+ case GTK_ANCHOR_E:
+ case GTK_ANCHOR_SE:
+ witem->cx -= witem->cwidth;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (witem->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_NE:
+ break;
+
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_E:
+ witem->cy -= witem->cheight / 2;
+ break;
+
+ case GTK_ANCHOR_SW:
+ case GTK_ANCHOR_S:
+ case GTK_ANCHOR_SE:
+ witem->cy -= witem->cheight;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Bounds */
+
+ item->x1 = witem->cx;
+ item->y1 = witem->cy;
+ item->x2 = witem->cx + witem->cwidth;
+ item->y2 = witem->cy + witem->cheight;
+
+ if (witem->widget)
+ gtk_layout_move (GTK_LAYOUT (item->canvas), witem->widget,
+ witem->cx,
+ witem->cy);
+}
+
+static void
+do_destroy (GtkObject *object, gpointer data)
+{
+ FooCanvasWidget *witem;
+
+ witem = data;
+
+ witem->in_destroy = TRUE;
+
+ gtk_object_destroy (data);
+}
+
+static void
+foo_canvas_widget_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ FooCanvasItem *item;
+ FooCanvasWidget *witem;
+ GObject *obj;
+ int update;
+ int calc_bounds;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_WIDGET (object));
+
+ item = FOO_CANVAS_ITEM (object);
+ witem = FOO_CANVAS_WIDGET (object);
+
+ update = FALSE;
+ calc_bounds = FALSE;
+
+ switch (param_id) {
+ case PROP_WIDGET:
+ if (witem->widget) {
+ g_signal_handler_disconnect (G_OBJECT (witem->widget), witem->destroy_id);
+ gtk_container_remove (GTK_CONTAINER (item->canvas), witem->widget);
+ }
+
+ obj = g_value_get_object (value);
+ if (obj) {
+ witem->widget = GTK_WIDGET (obj);
+ witem->destroy_id = g_signal_connect (G_OBJECT (obj),
+ "destroy", G_CALLBACK (do_destroy), witem);
+ gtk_layout_put (GTK_LAYOUT (item->canvas), witem->widget,
+ witem->cx + item->canvas->zoom_xofs,
+ witem->cy + item->canvas->zoom_yofs);
+ }
+
+ update = TRUE;
+ break;
+
+ case PROP_X:
+ if (witem->x != g_value_get_double (value))
+ {
+ witem->x = g_value_get_double (value);
+ calc_bounds = TRUE;
+ }
+ break;
+
+ case PROP_Y:
+ if (witem->y != g_value_get_double (value))
+ {
+ witem->y = g_value_get_double (value);
+ calc_bounds = TRUE;
+ }
+ break;
+
+ case PROP_WIDTH:
+ if (witem->width != fabs (g_value_get_double (value)))
+ {
+ witem->width = fabs (g_value_get_double (value));
+ update = TRUE;
+ }
+ break;
+
+ case PROP_HEIGHT:
+ if (witem->height != fabs (g_value_get_double (value)))
+ {
+ witem->height = fabs (g_value_get_double (value));
+ update = TRUE;
+ }
+ break;
+
+ case PROP_ANCHOR:
+ if (witem->anchor != (GtkAnchorType)g_value_get_enum (value))
+ {
+ witem->anchor = g_value_get_enum (value);
+ update = TRUE;
+ }
+ break;
+
+ case PROP_SIZE_PIXELS:
+ if (witem->size_pixels != g_value_get_boolean (value))
+ {
+ witem->size_pixels = g_value_get_boolean (value);
+ update = TRUE;
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+
+ if (update)
+ (* FOO_CANVAS_ITEM_GET_CLASS (item)->update) (item, 0, 0, 0);
+
+ if (calc_bounds)
+ recalc_bounds (witem);
+}
+
+static void
+foo_canvas_widget_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ FooCanvasWidget *witem;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_WIDGET (object));
+
+ witem = FOO_CANVAS_WIDGET (object);
+
+ switch (param_id) {
+ case PROP_WIDGET:
+ g_value_set_object (value, (GObject *) witem->widget);
+ break;
+
+ case PROP_X:
+ g_value_set_double (value, witem->x);
+ break;
+
+ case PROP_Y:
+ g_value_set_double (value, witem->y);
+ break;
+
+ case PROP_WIDTH:
+ g_value_set_double (value, witem->width);
+ break;
+
+ case PROP_HEIGHT:
+ g_value_set_double (value, witem->height);
+ break;
+
+ case PROP_ANCHOR:
+ g_value_set_enum (value, witem->anchor);
+ break;
+
+ case PROP_SIZE_PIXELS:
+ g_value_set_boolean (value, witem->size_pixels);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+foo_canvas_widget_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
+{
+ FooCanvasWidget *witem;
+
+ witem = FOO_CANVAS_WIDGET (item);
+
+ if (parent_class->update)
+ (* parent_class->update) (item, i2w_dx, i2w_dy, flags);
+
+ if (witem->widget) {
+ GtkRequisition req;
+ if (witem->size_pixels) {
+ witem->cwidth = (int) (witem->width + 0.5);
+ witem->cheight = (int) (witem->height + 0.5);
+ } else {
+ witem->cwidth = (int) (witem->width * item->canvas->pixels_per_unit + 0.5);
+ witem->cheight = (int) (witem->height * item->canvas->pixels_per_unit + 0.5);
+ }
+ req.width = witem->cwidth;
+ req.height = witem->cheight;
+ gtk_widget_size_request (witem->widget, &req);
+ } else {
+ witem->cwidth = 0.0;
+ witem->cheight = 0.0;
+ }
+
+ recalc_bounds (witem);
+}
+
+static void
+foo_canvas_widget_draw (FooCanvasItem *item,
+ GdkDrawable *drawable,
+ GdkEventExpose *event)
+{
+#if 0
+ FooCanvasWidget *witem;
+
+ witem = FOO_CANVAS_WIDGET (item);
+
+ if (witem->widget)
+ gtk_widget_queue_draw (witem->widget);
+#endif
+}
+static void
+foo_canvas_widget_map (FooCanvasItem *item)
+{
+ FooCanvasWidget *witem = FOO_CANVAS_WIDGET (item);
+ if (parent_class->map)
+ (* parent_class->map) (item);
+ if (witem->widget && gtk_widget_get_visible (witem->widget))
+ gtk_widget_map (witem->widget);
+}
+
+static void
+foo_canvas_widget_unmap (FooCanvasItem *item)
+{
+ FooCanvasWidget *witem = FOO_CANVAS_WIDGET (item);
+ if (parent_class->unmap)
+ (* parent_class->unmap) (item);
+ gtk_widget_unmap (witem->widget);
+}
+
+static double
+foo_canvas_widget_point (FooCanvasItem *item, double x, double y,
+ int cx, int cy, FooCanvasItem **actual_item)
+{
+ FooCanvasWidget *witem;
+ double x1, y1, x2, y2;
+ double dx, dy;
+
+ witem = FOO_CANVAS_WIDGET (item);
+
+ *actual_item = item;
+
+ foo_canvas_c2w (item->canvas, witem->cx, witem->cy, &x1, &y1);
+
+ x2 = x1 + (witem->cwidth - 1) / item->canvas->pixels_per_unit;
+ y2 = y1 + (witem->cheight - 1) / item->canvas->pixels_per_unit;
+
+ /* Is point inside widget bounds? */
+
+ if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2))
+ return 0.0;
+
+ /* Point is outside widget bounds */
+
+ 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
+foo_canvas_widget_translate (FooCanvasItem *item, double dx, double dy)
+{
+ FooCanvasWidget *witem;
+
+ witem = FOO_CANVAS_WIDGET (item);
+
+ witem->x += dx;
+ witem->y += dy;
+}
+
+
+static void
+foo_canvas_widget_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ FooCanvasWidget *witem;
+
+ witem = FOO_CANVAS_WIDGET (item);
+
+ *x1 = witem->x;
+ *y1 = witem->y;
+
+ switch (witem->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_SW:
+ break;
+
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_S:
+ *x1 -= witem->width / 2.0;
+ break;
+
+ case GTK_ANCHOR_NE:
+ case GTK_ANCHOR_E:
+ case GTK_ANCHOR_SE:
+ *x1 -= witem->width;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (witem->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_NE:
+ break;
+
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_E:
+ *y1 -= witem->height / 2.0;
+ break;
+
+ case GTK_ANCHOR_SW:
+ case GTK_ANCHOR_S:
+ case GTK_ANCHOR_SE:
+ *y1 -= witem->height;
+ break;
+
+ default:
+ break;
+ }
+
+ *x2 = *x1 + witem->width;
+ *y2 = *y1 + witem->height;
+}
diff --git a/libfoocanvas/foo-canvas-widget.h b/libfoocanvas/foo-canvas-widget.h
new file mode 100644
index 0000000..09612ee
--- /dev/null
+++ b/libfoocanvas/foo-canvas-widget.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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@
+ */
+/* Widget item type for FooCanvas widget
+ *
+ * FooCanvas 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 <federico nuclecu unam mx>
+ */
+
+#ifndef FOO_CANVAS_WIDGET_H
+#define FOO_CANVAS_WIDGET_H
+
+
+#include <libfoocanvas/foo-canvas.h>
+
+
+G_BEGIN_DECLS
+
+
+/* Widget item for canvas. The widget is positioned with respect to an anchor point.
+ * The following object arguments are available:
+ *
+ * name type read/write description
+ * ------------------------------------------------------------------------------------------
+ * widget GtkWidget* RW Pointer to the widget
+ * x double RW X coordinate of anchor point
+ * y double RW Y coordinate of anchor point
+ * width double RW Width of widget (see below)
+ * height double RW Height of widget (see below)
+ * anchor GtkAnchorType RW Anchor side for widget
+ * size_pixels boolean RW Specifies whether the widget size
+ * is specified in pixels or canvas units.
+ * If it is in pixels, then the widget will not
+ * be scaled when the canvas zoom factor changes.
+ * Otherwise, it will be scaled.
+ */
+
+
+#define FOO_TYPE_CANVAS_WIDGET (foo_canvas_widget_get_type ())
+#define FOO_CANVAS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_WIDGET, FooCanvasWidget))
+#define FOO_CANVAS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_WIDGET, FooCanvasWidgetClass))
+#define FOO_IS_CANVAS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_WIDGET))
+#define FOO_IS_CANVAS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_WIDGET))
+#define FOO_CANVAS_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_WIDGET, FooCanvasWidgetClass))
+
+
+typedef struct _FooCanvasWidget FooCanvasWidget;
+typedef struct _FooCanvasWidgetClass FooCanvasWidgetClass;
+
+struct _FooCanvasWidget {
+ FooCanvasItem item;
+
+ GtkWidget *widget; /* The child widget */
+
+ double x, y; /* Position at anchor */
+ double width, height; /* Dimensions of widget */
+ GtkAnchorType anchor; /* Anchor side for widget */
+
+ int cx, cy; /* Top-left canvas coordinates for widget */
+ int cwidth, cheight; /* Size of widget in pixels */
+
+ guint destroy_id; /* Signal connection id for destruction of child widget */
+
+ guint size_pixels : 1; /* Is size specified in (unchanging) pixels or units (get scaled)? */
+ guint in_destroy : 1; /* Is child widget being destroyed? */
+};
+
+struct _FooCanvasWidgetClass {
+ FooCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_widget_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libfoocanvas/foo-canvas.c b/libfoocanvas/foo-canvas.c
new file mode 100644
index 0000000..0ac3534
--- /dev/null
+++ b/libfoocanvas/foo-canvas.c
@@ -0,0 +1,3999 @@
+/* -*- 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 Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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@
+ */
+/*
+ * FooCanvas widget - Tk-like canvas widget for Gnome
+ *
+ * FooCanvas 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.
+ *
+ *
+ * Authors: Federico Mena <federico nuclecu unam mx>
+ * Raph Levien <raph gimp org>
+ */
+
+/*
+ * TO-DO list for the canvas:
+ *
+ * - Allow to specify whether FooCanvasImage sizes are in units or pixels (scale or don't scale).
+ *
+ * - Implement a flag for foo_canvas_item_reparent() that tells the function to keep the item
+ * visually in the same place, that is, to keep it in the same place with respect to the canvas
+ * origin.
+ *
+ * - GC put functions for items.
+ *
+ * - Widget item (finish it).
+ *
+ * - GList *foo_canvas_gimme_all_items_contained_in_this_area (FooCanvas *canvas, Rectangle area);
+ *
+ * - Retrofit all the primitive items with microtile support.
+ *
+ * - Curve support for line item.
+ *
+ * - Arc item (Havoc has it; to be integrated in FooCanvasEllipse).
+ *
+ * - Sane font handling API.
+ *
+ * - Get_arg methods for items:
+ * - How to fetch the outline width and know whether it is in pixels or units?
+ */
+
+#include <config.h>
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <gdk/gdkprivate.h>
+#include <gtk/gtk.h>
+#include "foo-canvas.h"
+#include "foo-canvas-i18n.h"
+
+#include "foo-canvas-marshal.h"
+
+static void foo_canvas_request_update (FooCanvas *canvas);
+static void group_add (FooCanvasGroup *group,
+ FooCanvasItem *item);
+static void group_remove (FooCanvasGroup *group,
+ FooCanvasItem *item);
+static void redraw_and_repick_if_mapped (FooCanvasItem *item);
+
+/*** FooCanvasItem ***/
+
+/* Some convenience stuff */
+#define GCI_UPDATE_MASK (FOO_CANVAS_UPDATE_REQUESTED | FOO_CANVAS_UPDATE_DEEP)
+#define GCI_EPSILON 1e-18
+
+enum {
+ ITEM_PROP_0,
+ ITEM_PROP_PARENT,
+ ITEM_PROP_VISIBLE
+};
+
+enum {
+ ITEM_EVENT,
+ ITEM_LAST_SIGNAL
+};
+
+static void foo_canvas_item_class_init (FooCanvasItemClass *klass);
+static void foo_canvas_item_init (FooCanvasItem *item);
+static int emit_event (FooCanvas *canvas, GdkEvent *event);
+
+static guint item_signals[ITEM_LAST_SIGNAL];
+
+static GtkObjectClass *item_parent_class;
+
+static gpointer accessible_item_parent_class;
+static gpointer accessible_parent_class;
+
+
+/**
+ * foo_canvas_item_get_type:
+ *
+ * Registers the &FooCanvasItem class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &FooCanvasItem class.
+ **/
+GType
+foo_canvas_item_get_type (void)
+{
+ static GType canvas_item_type = 0;
+
+ if (!canvas_item_type) {
+ static const GTypeInfo canvas_item_info = {
+ sizeof (FooCanvasItemClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) foo_canvas_item_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (FooCanvasItem),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) foo_canvas_item_init
+ };
+
+ canvas_item_type = g_type_register_static (gtk_object_get_type (),
+ "FooCanvasItem",
+ &canvas_item_info,
+ 0);
+ }
+
+ return canvas_item_type;
+}
+
+/* Object initialization function for FooCanvasItem */
+static void
+foo_canvas_item_init (FooCanvasItem *item)
+{
+ item->object.flags |= FOO_CANVAS_ITEM_VISIBLE;
+}
+
+/**
+ * foo_canvas_item_new:
+ * @parent: The parent group for the new item.
+ * @type: The object type of the item.
+ * @first_arg_name: A list of object argument name/value pairs, NULL-terminated,
+ * used to configure the item. For example, "fill_color", "black",
+ * "width_units", 5.0, NULL.
+ * @Varargs:
+ *
+ * Creates a new canvas item with @parent as its parent group. The item is
+ * created at the top of its parent's stack, and starts up as visible. The item
+ * is of the specified @type, for example, it can be
+ * foo_canvas_rect_get_type(). The list of object arguments/value pairs is
+ * used to configure the item.
+ *
+ * Return value: The newly-created item.
+ **/
+FooCanvasItem *
+foo_canvas_item_new (FooCanvasGroup *parent, GType type, const gchar *first_arg_name, ...)
+{
+ FooCanvasItem *item;
+ va_list args;
+
+ g_return_val_if_fail (FOO_IS_CANVAS_GROUP (parent), NULL);
+ g_return_val_if_fail (g_type_is_a (type, foo_canvas_item_get_type ()), NULL);
+
+ item = FOO_CANVAS_ITEM (g_object_new (type, NULL));
+
+ va_start (args, first_arg_name);
+ foo_canvas_item_construct (item, parent, first_arg_name, args);
+ va_end (args);
+
+ return item;
+}
+
+
+/* Performs post-creation operations on a canvas item (adding it to its parent
+ * group, etc.)
+ */
+static void
+item_post_create_setup (FooCanvasItem *item)
+{
+ group_add (FOO_CANVAS_GROUP (item->parent), item);
+
+ redraw_and_repick_if_mapped (item);
+}
+
+/* Set_property handler for canvas items */
+static void
+foo_canvas_item_set_property (GObject *gobject, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ FooCanvasItem *item;
+
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (gobject));
+
+ item = FOO_CANVAS_ITEM (gobject);
+
+ switch (param_id) {
+ case ITEM_PROP_PARENT:
+ if (item->parent != NULL) {
+ g_warning ("Cannot set `parent' argument after item has "
+ "already been constructed.");
+ } else if (g_value_get_object (value)) {
+ item->parent = FOO_CANVAS_ITEM (g_value_get_object (value));
+ item->canvas = item->parent->canvas;
+ item_post_create_setup (item);
+ }
+ break;
+ case ITEM_PROP_VISIBLE:
+ if (g_value_get_boolean (value)) {
+ foo_canvas_item_show (item);
+ } else {
+ foo_canvas_item_hide (item);
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ break;
+ }
+}
+
+/* Get_property handler for canvas items */
+static void
+foo_canvas_item_get_property (GObject *gobject, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ FooCanvasItem *item;
+
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (gobject));
+
+ item = FOO_CANVAS_ITEM (gobject);
+
+ switch (param_id) {
+ case ITEM_PROP_VISIBLE:
+ g_value_set_boolean (value, item->object.flags & FOO_CANVAS_ITEM_VISIBLE);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ break;
+ }
+}
+
+/**
+ * foo_canvas_item_construct:
+ * @item: An unconstructed canvas item.
+ * @parent: The parent group for the item.
+ * @first_arg_name: The name of the first argument for configuring the item.
+ * @args: The list of arguments used to configure the item.
+ *
+ * Constructs a canvas item; meant for use only by item implementations.
+ **/
+void
+foo_canvas_item_construct (FooCanvasItem *item, FooCanvasGroup *parent,
+ const gchar *first_arg_name, va_list args)
+{
+ g_return_if_fail (FOO_IS_CANVAS_GROUP (parent));
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+ item->parent = FOO_CANVAS_ITEM (parent);
+ item->canvas = item->parent->canvas;
+
+ g_object_set_valist (G_OBJECT (item), first_arg_name, args);
+
+ item_post_create_setup (item);
+}
+
+
+static void
+redraw_and_repick_if_mapped (FooCanvasItem *item)
+{
+ if (item->object.flags & FOO_CANVAS_ITEM_MAPPED) {
+ foo_canvas_item_request_redraw (item);
+ item->canvas->need_repick = TRUE;
+ }
+}
+
+/* Dispose handler for canvas items */
+static void
+foo_canvas_item_dispose (GObject *object)
+{
+ FooCanvasItem *item;
+
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (object));
+
+ item = FOO_CANVAS_ITEM (object);
+
+ if (item->canvas) {
+ foo_canvas_item_request_redraw (item);
+
+ /* Make the canvas forget about us */
+
+ if (item == item->canvas->current_item) {
+ item->canvas->current_item = NULL;
+ item->canvas->need_repick = TRUE;
+ }
+
+ if (item == item->canvas->new_current_item) {
+ item->canvas->new_current_item = NULL;
+ item->canvas->need_repick = TRUE;
+ }
+
+ if (item == item->canvas->grabbed_item) {
+ GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (item->canvas));
+ item->canvas->grabbed_item = NULL;
+ gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
+ }
+
+ if (item == item->canvas->focused_item)
+ item->canvas->focused_item = NULL;
+
+ /* Normal destroy stuff */
+
+ if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
+ (* FOO_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+ if (item->object.flags & FOO_CANVAS_ITEM_REALIZED)
+ (* FOO_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
+
+ if (item->parent)
+ group_remove (FOO_CANVAS_GROUP (item->parent), item);
+
+ item->canvas = NULL;
+ }
+
+ G_OBJECT_CLASS (item_parent_class)->dispose (object);
+}
+
+
+/* Realize handler for canvas items */
+static void
+foo_canvas_item_realize (FooCanvasItem *item)
+{
+ if (item->parent && !(item->parent->object.flags & FOO_CANVAS_ITEM_REALIZED))
+ (* FOO_CANVAS_ITEM_GET_CLASS (item->parent)->realize) (item->parent);
+
+ if (item->parent == NULL && !gtk_widget_get_realized (GTK_WIDGET (item->canvas)))
+ gtk_widget_realize (GTK_WIDGET (item->canvas));
+
+ GTK_OBJECT_SET_FLAGS (item, FOO_CANVAS_ITEM_REALIZED);
+
+ foo_canvas_item_request_update (item);
+}
+
+/* Unrealize handler for canvas items */
+static void
+foo_canvas_item_unrealize (FooCanvasItem *item)
+{
+ if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
+ (* FOO_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+ GTK_OBJECT_UNSET_FLAGS (item, FOO_CANVAS_ITEM_REALIZED);
+}
+
+/* Map handler for canvas items */
+static void
+foo_canvas_item_map (FooCanvasItem *item)
+{
+ GTK_OBJECT_SET_FLAGS (item, FOO_CANVAS_ITEM_MAPPED);
+}
+
+/* Unmap handler for canvas items */
+static void
+foo_canvas_item_unmap (FooCanvasItem *item)
+{
+ GTK_OBJECT_UNSET_FLAGS (item, FOO_CANVAS_ITEM_MAPPED);
+}
+
+/* Update handler for canvas items */
+static void
+foo_canvas_item_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
+{
+ GTK_OBJECT_UNSET_FLAGS (item, FOO_CANVAS_ITEM_NEED_UPDATE);
+ GTK_OBJECT_UNSET_FLAGS (item, FOO_CANVAS_ITEM_NEED_DEEP_UPDATE);
+}
+
+/*
+ * This routine invokes the update method of the item
+ * Please notice, that we take parent to canvas pixel matrix as argument
+ * unlike virtual method ::update, whose argument is item 2 canvas pixel
+ * matrix
+ *
+ * I will try to force somewhat meaningful naming for affines (Lauris)
+ * General naming rule is FROM2TO, where FROM and TO are abbreviations
+ * So p2cpx is Parent2CanvasPixel and i2cpx is Item2CanvasPixel
+ * I hope that this helps to keep track of what really happens
+ *
+ */
+
+static void
+foo_canvas_item_invoke_update (FooCanvasItem *item,
+ double i2w_dx,
+ double i2w_dy,
+ int flags)
+{
+ int child_flags;
+
+ child_flags = flags;
+
+ /* apply object flags to child flags */
+ child_flags &= ~FOO_CANVAS_UPDATE_REQUESTED;
+
+ if (item->object.flags & FOO_CANVAS_ITEM_NEED_UPDATE)
+ child_flags |= FOO_CANVAS_UPDATE_REQUESTED;
+
+ if (item->object.flags & FOO_CANVAS_ITEM_NEED_DEEP_UPDATE)
+ child_flags |= FOO_CANVAS_UPDATE_DEEP;
+
+ if (child_flags & GCI_UPDATE_MASK) {
+ if (FOO_CANVAS_ITEM_GET_CLASS (item)->update)
+ FOO_CANVAS_ITEM_GET_CLASS (item)->update (item, i2w_dx, i2w_dy, child_flags);
+ }
+
+ /* If this fail you probably forgot to chain up to
+ * FooCanvasItem::update from a derived class */
+ g_return_if_fail (!(item->object.flags & FOO_CANVAS_ITEM_NEED_UPDATE));
+}
+
+/*
+ * This routine invokes the point method of the item.
+ * The arguments x, y should be in the parent item local coordinates.
+ */
+
+static double
+foo_canvas_item_invoke_point (FooCanvasItem *item, double x, double y, int cx, int cy, FooCanvasItem **actual_item)
+{
+ /* Calculate x & y in item local coordinates */
+
+ if (FOO_CANVAS_ITEM_GET_CLASS (item)->point)
+ return FOO_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy, actual_item);
+
+ return 1e18;
+}
+
+/**
+ * foo_canvas_item_set:
+ * @item: A canvas item.
+ * @first_arg_name: The list of object argument name/value pairs used to configure the item.
+ * @Varargs:
+ *
+ * Configures a canvas item. The arguments in the item are set to the specified
+ * values, and the item is repainted as appropriate.
+ **/
+void
+foo_canvas_item_set (FooCanvasItem *item, const gchar *first_arg_name, ...)
+{
+ va_list args;
+
+ va_start (args, first_arg_name);
+ foo_canvas_item_set_valist (item, first_arg_name, args);
+ va_end (args);
+}
+
+
+/**
+ * foo_canvas_item_set_valist:
+ * @item: A canvas item.
+ * @first_arg_name: The name of the first argument used to configure the item.
+ * @args: The list of object argument name/value pairs used to configure the item.
+ *
+ * Configures a canvas item. The arguments in the item are set to the specified
+ * values, and the item is repainted as appropriate.
+ **/
+void
+foo_canvas_item_set_valist (FooCanvasItem *item, const gchar *first_arg_name, va_list args)
+{
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+ g_object_set_valist (G_OBJECT (item), first_arg_name, args);
+
+#if 0
+ /* I commented this out, because item implementations have to schedule update/redraw */
+ foo_canvas_item_request_redraw (item);
+#endif
+
+ item->canvas->need_repick = TRUE;
+}
+
+
+/**
+ * foo_canvas_item_move:
+ * @item: A canvas item.
+ * @dx: Horizontal offset.
+ * @dy: Vertical offset.
+ *
+ * Moves a canvas item by creating an affine transformation matrix for
+ * translation by using the specified values. This happens in item
+ * local coordinate system, so if you have nontrivial transform, it
+ * most probably does not do, what you want.
+ **/
+void
+foo_canvas_item_move (FooCanvasItem *item, double dx, double dy)
+{
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+ if (!FOO_CANVAS_ITEM_GET_CLASS (item)->translate) {
+ g_warning ("Item type %s does not implement translate method.\n",
+ g_type_name (G_OBJECT_TYPE (item)));
+ return;
+ }
+
+ (* FOO_CANVAS_ITEM_GET_CLASS (item)->translate) (item, dx, dy);
+
+ if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
+ item->canvas->need_repick = TRUE;
+
+ if (!(item->object.flags & FOO_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
+ item->object.flags |= FOO_CANVAS_ITEM_NEED_DEEP_UPDATE;
+ if (item->parent != NULL)
+ foo_canvas_item_request_update (item->parent);
+ else
+ foo_canvas_request_update (item->canvas);
+ }
+
+}
+
+/* Convenience function to reorder items in a group's child list. This puts the
+ * specified link after the "before" link. Returns TRUE if the list was changed.
+ */
+static gboolean
+put_item_after (GList *link, GList *before)
+{
+ FooCanvasGroup *parent;
+
+ if (link == before)
+ return FALSE;
+
+ parent = FOO_CANVAS_GROUP (FOO_CANVAS_ITEM (link->data)->parent);
+
+ if (before == NULL) {
+ if (link == parent->item_list)
+ return FALSE;
+
+ link->prev->next = link->next;
+
+ if (link->next)
+ link->next->prev = link->prev;
+ else
+ parent->item_list_end = link->prev;
+
+ link->prev = before;
+ link->next = parent->item_list;
+ link->next->prev = link;
+ parent->item_list = link;
+ } else {
+ if ((link == parent->item_list_end) && (before == parent->item_list_end->prev))
+ return FALSE;
+
+ if (link->next)
+ link->next->prev = link->prev;
+
+ if (link->prev)
+ link->prev->next = link->next;
+ else {
+ parent->item_list = link->next;
+ parent->item_list->prev = NULL;
+ }
+
+ link->prev = before;
+ link->next = before->next;
+
+ link->prev->next = link;
+
+ if (link->next)
+ link->next->prev = link;
+ else
+ parent->item_list_end = link;
+ }
+ return TRUE;
+}
+
+
+/**
+ * foo_canvas_item_raise:
+ * @item: A canvas item.
+ * @positions: Number of steps to raise the item.
+ *
+ * Raises the item in its parent's stack by the specified number of positions.
+ * If the number of positions is greater than the distance to the top of the
+ * stack, then the item is put at the top.
+ **/
+void
+foo_canvas_item_raise (FooCanvasItem *item, int positions)
+{
+ GList *link, *before;
+ FooCanvasGroup *parent;
+
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+ g_return_if_fail (positions >= 0);
+
+ if (!item->parent || positions == 0)
+ return;
+
+ parent = FOO_CANVAS_GROUP (item->parent);
+ link = g_list_find (parent->item_list, item);
+ g_assert (link != NULL);
+
+ for (before = link; positions && before; positions--)
+ before = before->next;
+
+ if (!before)
+ before = parent->item_list_end;
+
+ if (put_item_after (link, before)) {
+ redraw_and_repick_if_mapped (item);
+ }
+}
+
+
+/**
+ * foo_canvas_item_lower:
+ * @item: A canvas item.
+ * @positions: Number of steps to lower the item.
+ *
+ * Lowers the item in its parent's stack by the specified number of positions.
+ * If the number of positions is greater than the distance to the bottom of the
+ * stack, then the item is put at the bottom.
+ **/
+void
+foo_canvas_item_lower (FooCanvasItem *item, int positions)
+{
+ GList *link, *before;
+ FooCanvasGroup *parent;
+
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+ g_return_if_fail (positions >= 1);
+
+ if (!item->parent || positions == 0)
+ return;
+
+ parent = FOO_CANVAS_GROUP (item->parent);
+ link = g_list_find (parent->item_list, item);
+ g_assert (link != NULL);
+
+ if (link->prev)
+ for (before = link->prev; positions && before; positions--)
+ before = before->prev;
+ else
+ before = NULL;
+
+ if (put_item_after (link, before)) {
+ redraw_and_repick_if_mapped (item);
+ }
+}
+
+
+/**
+ * foo_canvas_item_raise_to_top:
+ * @item: A canvas item.
+ *
+ * Raises an item to the top of its parent's stack.
+ **/
+void
+foo_canvas_item_raise_to_top (FooCanvasItem *item)
+{
+ GList *link;
+ FooCanvasGroup *parent;
+
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+ if (!item->parent)
+ return;
+
+ parent = FOO_CANVAS_GROUP (item->parent);
+ link = g_list_find (parent->item_list, item);
+ g_assert (link != NULL);
+
+ if (put_item_after (link, parent->item_list_end)) {
+ redraw_and_repick_if_mapped (item);
+ }
+}
+
+
+/**
+ * foo_canvas_item_lower_to_bottom:
+ * @item: A canvas item.
+ *
+ * Lowers an item to the bottom of its parent's stack.
+ **/
+void
+foo_canvas_item_lower_to_bottom (FooCanvasItem *item)
+{
+ GList *link;
+ FooCanvasGroup *parent;
+
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+ if (!item->parent)
+ return;
+
+ parent = FOO_CANVAS_GROUP (item->parent);
+ link = g_list_find (parent->item_list, item);
+ g_assert (link != NULL);
+
+ if (put_item_after (link, NULL)) {
+ redraw_and_repick_if_mapped (item);
+ }
+}
+
+/**
+ * foo_canvas_item_send_behind:
+ * @item: A canvas item.
+ * @behind_item: The canvas item to put item behind, or NULL
+ *
+ * Moves item to a in the position in the stacking order so that
+ * it is placed immediately below behind_item, or at the top if
+ * behind_item is NULL.
+ **/
+void
+foo_canvas_item_send_behind (FooCanvasItem *item,
+ FooCanvasItem *behind_item)
+{
+ GList *item_list;
+ int item_position, behind_position;
+
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+ if (behind_item == NULL) {
+ foo_canvas_item_raise_to_top (item);
+ return;
+ }
+
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (behind_item));
+ g_return_if_fail (item->parent == behind_item->parent);
+
+ item_list = FOO_CANVAS_GROUP (item->parent)->item_list;
+
+ item_position = g_list_index (item_list, item);
+ g_assert (item_position != -1);
+ behind_position = g_list_index (item_list, behind_item);
+ g_assert (behind_position != -1);
+ g_assert (item_position != behind_position);
+
+ if (item_position == behind_position - 1) {
+ return;
+ }
+
+ if (item_position < behind_position) {
+ foo_canvas_item_raise (item, (behind_position - 1) - item_position);
+ } else {
+ foo_canvas_item_lower (item, item_position - behind_position);
+ }
+}
+
+/**
+ * foo_canvas_item_show:
+ * @item: A canvas item.
+ *
+ * Shows a canvas item. If the item was already shown, then no action is taken.
+ **/
+void
+foo_canvas_item_show (FooCanvasItem *item)
+{
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+ if (!(item->object.flags & FOO_CANVAS_ITEM_VISIBLE)) {
+ item->object.flags |= FOO_CANVAS_ITEM_VISIBLE;
+
+ if (!(item->object.flags & FOO_CANVAS_ITEM_REALIZED))
+ (* FOO_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
+
+ if (item->parent != NULL) {
+ if (!(item->object.flags & FOO_CANVAS_ITEM_MAPPED) &&
+ item->parent->object.flags & FOO_CANVAS_ITEM_MAPPED)
+ (* FOO_CANVAS_ITEM_GET_CLASS (item)->map) (item);
+ } else {
+ if (!(item->object.flags & FOO_CANVAS_ITEM_MAPPED) &&
+ gtk_widget_get_mapped (GTK_WIDGET (item->canvas)))
+ (* FOO_CANVAS_ITEM_GET_CLASS (item)->map) (item);
+ }
+
+ redraw_and_repick_if_mapped (item);
+ }
+}
+
+
+/**
+ * foo_canvas_item_hide:
+ * @item: A canvas item.
+ *
+ * Hides a canvas item. If the item was already hidden, then no action is
+ * taken.
+ **/
+void
+foo_canvas_item_hide (FooCanvasItem *item)
+{
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+ if (item->object.flags & FOO_CANVAS_ITEM_VISIBLE) {
+ item->object.flags &= ~FOO_CANVAS_ITEM_VISIBLE;
+
+ redraw_and_repick_if_mapped (item);
+
+ if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
+ (* FOO_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+ /* No need to unrealize when we just want to hide */
+ }
+}
+
+
+/**
+ * foo_canvas_item_grab:
+ * @item: A canvas item.
+ * @event_mask: Mask of events that will be sent to this item.
+ * @cursor: If non-NULL, the cursor that will be used while the grab is active.
+ * @etime: The timestamp required for grabbing the mouse, or GDK_CURRENT_TIME.
+ *
+ * Specifies that all events that match the specified event mask should be sent
+ * to the specified item, and also grabs the mouse by calling
+ * gdk_pointer_grab(). The event mask is also used when grabbing the pointer.
+ * If @cursor is not NULL, then that cursor is used while the grab is active.
+ * The @etime parameter is the timestamp required for grabbing the mouse.
+ *
+ * Return value: If an item was already grabbed, it returns %GDK_GRAB_ALREADY_GRABBED. If
+ * the specified item was hidden by calling foo_canvas_item_hide(), then it
+ * returns %GDK_GRAB_NOT_VIEWABLE. Else, it returns the result of calling
+ * gdk_pointer_grab().
+ **/
+int
+foo_canvas_item_grab (FooCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
+{
+ int retval;
+
+ g_return_val_if_fail (FOO_IS_CANVAS_ITEM (item), GDK_GRAB_NOT_VIEWABLE);
+ g_return_val_if_fail (gtk_widget_get_mapped (GTK_WIDGET (item->canvas)), GDK_GRAB_NOT_VIEWABLE);
+
+ if (item->canvas->grabbed_item)
+ return GDK_GRAB_ALREADY_GRABBED;
+
+ if (!(item->object.flags & FOO_CANVAS_ITEM_MAPPED))
+ return GDK_GRAB_NOT_VIEWABLE;
+
+ retval = gdk_pointer_grab (item->canvas->layout.bin_window,
+ FALSE,
+ event_mask,
+ NULL,
+ cursor,
+ etime);
+
+ if (retval != GDK_GRAB_SUCCESS)
+ return retval;
+
+ item->canvas->grabbed_item = item;
+ item->canvas->grabbed_event_mask = event_mask;
+ item->canvas->current_item = item; /* So that events go to the grabbed item */
+
+ return retval;
+}
+
+
+/**
+ * foo_canvas_item_ungrab:
+ * @item: A canvas item that holds a grab.
+ * @etime: The timestamp for ungrabbing the mouse.
+ *
+ * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
+ * mouse.
+ **/
+void
+foo_canvas_item_ungrab (FooCanvasItem *item, guint32 etime)
+{
+ GdkDisplay *display;
+
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+ if (item->canvas->grabbed_item != item)
+ return;
+
+ display = gtk_widget_get_display (GTK_WIDGET (item->canvas));
+ item->canvas->grabbed_item = NULL;
+ gdk_display_pointer_ungrab (display, etime);
+}
+
+
+/**
+ * foo_canvas_item_w2i:
+ * @item: A canvas item.
+ * @x: X coordinate to convert (input/output value).
+ * @y: Y coordinate to convert (input/output value).
+ *
+ * Converts a coordinate pair from world coordinates to item-relative
+ * coordinates.
+ **/
+void
+foo_canvas_item_w2i (FooCanvasItem *item, double *x, double *y)
+{
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+ g_return_if_fail (x != NULL);
+ g_return_if_fail (y != NULL);
+
+ item = item->parent;
+ while (item) {
+ if (FOO_IS_CANVAS_GROUP (item)) {
+ *x -= FOO_CANVAS_GROUP (item)->xpos;
+ *y -= FOO_CANVAS_GROUP (item)->ypos;
+ }
+
+ item = item->parent;
+ }
+}
+
+
+/**
+ * foo_canvas_item_i2w:
+ * @item: A canvas item.
+ * @x: X coordinate to convert (input/output value).
+ * @y: Y coordinate to convert (input/output value).
+ *
+ * Converts a coordinate pair from item-relative coordinates to world
+ * coordinates.
+ **/
+void
+foo_canvas_item_i2w (FooCanvasItem *item, double *x, double *y)
+{
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+ g_return_if_fail (x != NULL);
+ g_return_if_fail (y != NULL);
+
+ item = item->parent;
+ while (item) {
+ if (FOO_IS_CANVAS_GROUP (item)) {
+ *x += FOO_CANVAS_GROUP (item)->xpos;
+ *y += FOO_CANVAS_GROUP (item)->ypos;
+ }
+
+ item = item->parent;
+ }
+}
+
+/* Returns whether the item is an inferior of or is equal to the parent. */
+static int
+is_descendant (FooCanvasItem *item, FooCanvasItem *parent)
+{
+ for (; item; item = item->parent)
+ if (item == parent)
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * foo_canvas_item_reparent:
+ * @item: A canvas item.
+ * @new_group: A canvas group.
+ *
+ * Changes the parent of the specified item to be the new group. The item keeps
+ * its group-relative coordinates as for its old parent, so the item may change
+ * its absolute position within the canvas.
+ **/
+void
+foo_canvas_item_reparent (FooCanvasItem *item, FooCanvasGroup *new_group)
+{
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+ g_return_if_fail (FOO_IS_CANVAS_GROUP (new_group));
+
+ /* Both items need to be in the same canvas */
+ g_return_if_fail (item->canvas == FOO_CANVAS_ITEM (new_group)->canvas);
+
+ /* The group cannot be an inferior of the item or be the item itself --
+ * this also takes care of the case where the item is the root item of
+ * the canvas. */
+ g_return_if_fail (!is_descendant (FOO_CANVAS_ITEM (new_group), item));
+
+ /* Everything is ok, now actually reparent the item */
+
+ g_object_ref (G_OBJECT (item)); /* protect it from the unref in group_remove */
+
+ foo_canvas_item_request_redraw (item);
+
+ group_remove (FOO_CANVAS_GROUP (item->parent), item);
+ item->parent = FOO_CANVAS_ITEM (new_group);
+ /* item->canvas is unchanged. */
+ group_add (new_group, item);
+
+ /* Redraw and repick */
+
+ redraw_and_repick_if_mapped (item);
+
+ g_object_unref (G_OBJECT (item));
+}
+
+/**
+ * foo_canvas_item_grab_focus:
+ * @item: A canvas item.
+ *
+ * Makes the specified item take the keyboard focus, so all keyboard events will
+ * be sent to it. If the canvas widget itself did not have the focus, it grabs
+ * it as well.
+ **/
+void
+foo_canvas_item_grab_focus (FooCanvasItem *item)
+{
+ FooCanvasItem *focused_item;
+ GdkEvent ev;
+
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+ g_return_if_fail (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas)));
+
+ focused_item = item->canvas->focused_item;
+
+ if (focused_item) {
+ ev.focus_change.type = GDK_FOCUS_CHANGE;
+ ev.focus_change.window = GTK_LAYOUT (item->canvas)->bin_window;
+ ev.focus_change.send_event = FALSE;
+ ev.focus_change.in = FALSE;
+
+ emit_event (item->canvas, &ev);
+ }
+
+ item->canvas->focused_item = item;
+ gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
+
+ if (focused_item) {
+ ev.focus_change.type = GDK_FOCUS_CHANGE;
+ ev.focus_change.window = GTK_LAYOUT (item->canvas)->bin_window;
+ ev.focus_change.send_event = FALSE;
+ ev.focus_change.in = TRUE;
+
+ emit_event (item->canvas, &ev);
+ }
+}
+
+
+/**
+ * foo_canvas_item_get_bounds:
+ * @item: A canvas item.
+ * @x1: Leftmost edge of the bounding box (return value).
+ * @y1: Upper edge of the bounding box (return value).
+ * @x2: Rightmost edge of the bounding box (return value).
+ * @y2: Lower edge of the bounding box (return value).
+ *
+ * Queries the bounding box of a canvas item. The bounds are returned in the
+ * coordinate system of the item's parent.
+ **/
+void
+foo_canvas_item_get_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ double tx1, ty1, tx2, ty2;
+
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+ tx1 = ty1 = tx2 = ty2 = 0.0;
+
+ /* Get the item's bounds in its coordinate system */
+
+ if (FOO_CANVAS_ITEM_GET_CLASS (item)->bounds)
+ (* FOO_CANVAS_ITEM_GET_CLASS (item)->bounds) (item, &tx1, &ty1, &tx2, &ty2);
+
+ /* Return the values */
+
+ if (x1)
+ *x1 = tx1;
+
+ if (y1)
+ *y1 = ty1;
+
+ if (x2)
+ *x2 = tx2;
+
+ if (y2)
+ *y2 = ty2;
+}
+
+
+/**
+ * foo_canvas_item_request_update
+ * @item: A canvas item.
+ *
+ * To be used only by item implementations. Requests that the canvas queue an
+ * update for the specified item.
+ **/
+void
+foo_canvas_item_request_update (FooCanvasItem *item)
+{
+ if (NULL == item->canvas)
+ return;
+
+ g_return_if_fail (!item->canvas->doing_update);
+
+ if (item->object.flags & FOO_CANVAS_ITEM_NEED_UPDATE)
+ return;
+
+ item->object.flags |= FOO_CANVAS_ITEM_NEED_UPDATE;
+
+ if (item->parent != NULL) {
+ /* Recurse up the tree */
+ foo_canvas_item_request_update (item->parent);
+ } else {
+ /* Have reached the top of the tree, make sure the update call gets scheduled. */
+ foo_canvas_request_update (item->canvas);
+ }
+}
+
+/**
+ * foo_canvas_item_request_update
+ * @item: A canvas item.
+ *
+ * Convenience function that informs a canvas that the specified item needs
+ * to be repainted. To be used by item implementations
+ **/
+void
+foo_canvas_item_request_redraw (FooCanvasItem *item)
+{
+ if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
+ foo_canvas_request_redraw (item->canvas,
+ item->x1, item->y1,
+ item->x2 + 1, item->y2 + 1);
+}
+
+
+
+/*** FooCanvasGroup ***/
+
+
+enum {
+ GROUP_PROP_0,
+ GROUP_PROP_X,
+ GROUP_PROP_Y
+};
+
+
+static void foo_canvas_group_class_init (FooCanvasGroupClass *klass);
+static void foo_canvas_group_init (FooCanvasGroup *group);
+static void foo_canvas_group_set_property(GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void foo_canvas_group_get_property(GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void foo_canvas_group_destroy (GtkObject *object);
+
+static void foo_canvas_group_update (FooCanvasItem *item,
+ double i2w_dx,
+ double i2w_dy,
+ int flags);
+static void foo_canvas_group_unrealize (FooCanvasItem *item);
+static void foo_canvas_group_map (FooCanvasItem *item);
+static void foo_canvas_group_unmap (FooCanvasItem *item);
+static void foo_canvas_group_draw (FooCanvasItem *item, GdkDrawable *drawable,
+ GdkEventExpose *expose);
+static double foo_canvas_group_point (FooCanvasItem *item, double x, double y,
+ int cx, int cy,
+ FooCanvasItem **actual_item);
+static void foo_canvas_group_translate (FooCanvasItem *item, double dx, double dy);
+static void foo_canvas_group_bounds (FooCanvasItem *item, double *x1, double *y1,
+ double *x2, double *y2);
+
+
+static FooCanvasItemClass *group_parent_class;
+
+
+/**
+ * foo_canvas_group_get_type:
+ *
+ * Registers the &FooCanvasGroup class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &FooCanvasGroup class.
+ **/
+GType
+foo_canvas_group_get_type (void)
+{
+ static GType group_type = 0;
+
+ if (!group_type) {
+ static const GTypeInfo group_info = {
+ sizeof (FooCanvasGroupClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) foo_canvas_group_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (FooCanvasGroup),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) foo_canvas_group_init
+
+
+ };
+
+ group_type = g_type_register_static (foo_canvas_item_get_type (),
+ "FooCanvasGroup",
+ &group_info,
+ 0);
+ }
+
+ return group_type;
+}
+
+/* Class initialization function for FooCanvasGroupClass */
+static void
+foo_canvas_group_class_init (FooCanvasGroupClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ FooCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) klass;
+ object_class = (GtkObjectClass *) klass;
+ item_class = (FooCanvasItemClass *) klass;
+
+ group_parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->set_property = foo_canvas_group_set_property;
+ gobject_class->get_property = foo_canvas_group_get_property;
+
+ g_object_class_install_property
+ (gobject_class, GROUP_PROP_X,
+ g_param_spec_double ("x",
+ _("X"),
+ _("X"),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property
+ (gobject_class, GROUP_PROP_Y,
+ g_param_spec_double ("y",
+ _("Y"),
+ _("Y"),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+
+ object_class->destroy = foo_canvas_group_destroy;
+
+ item_class->update = foo_canvas_group_update;
+ item_class->unrealize = foo_canvas_group_unrealize;
+ item_class->map = foo_canvas_group_map;
+ item_class->unmap = foo_canvas_group_unmap;
+ item_class->draw = foo_canvas_group_draw;
+ item_class->point = foo_canvas_group_point;
+ item_class->translate = foo_canvas_group_translate;
+ item_class->bounds = foo_canvas_group_bounds;
+}
+
+/* Object initialization function for FooCanvasGroup */
+static void
+foo_canvas_group_init (FooCanvasGroup *group)
+{
+ group->xpos = 0.0;
+ group->ypos = 0.0;
+}
+
+/* Set_property handler for canvas groups */
+static void
+foo_canvas_group_set_property (GObject *gobject, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ FooCanvasItem *item;
+ FooCanvasGroup *group;
+ double old;
+ gboolean moved;
+
+ g_return_if_fail (FOO_IS_CANVAS_GROUP (gobject));
+
+ item = FOO_CANVAS_ITEM (gobject);
+ group = FOO_CANVAS_GROUP (gobject);
+
+ moved = FALSE;
+ switch (param_id) {
+ case GROUP_PROP_X:
+ old = group->xpos;
+ group->xpos = g_value_get_double (value);
+ if (old != group->xpos)
+ moved = TRUE;
+ break;
+
+ case GROUP_PROP_Y:
+ old = group->ypos;
+ group->ypos = g_value_get_double (value);
+ if (old != group->ypos)
+ moved = TRUE;
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ break;
+ }
+
+ if (moved) {
+ item->object.flags |= FOO_CANVAS_ITEM_NEED_DEEP_UPDATE;
+ if (item->parent != NULL)
+ foo_canvas_item_request_update (item->parent);
+ else
+ foo_canvas_request_update (item->canvas);
+ }
+}
+
+/* Get_property handler for canvas groups */
+static void
+foo_canvas_group_get_property (GObject *gobject, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ FooCanvasItem *item;
+ FooCanvasGroup *group;
+
+ g_return_if_fail (FOO_IS_CANVAS_GROUP (gobject));
+
+ item = FOO_CANVAS_ITEM (gobject);
+ group = FOO_CANVAS_GROUP (gobject);
+
+ switch (param_id) {
+ case GROUP_PROP_X:
+ g_value_set_double (value, group->xpos);
+ break;
+
+ case GROUP_PROP_Y:
+ g_value_set_double (value, group->ypos);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ break;
+ }
+}
+
+/* Destroy handler for canvas groups */
+static void
+foo_canvas_group_destroy (GtkObject *object)
+{
+ FooCanvasGroup *group;
+ FooCanvasItem *child;
+ GList *list;
+
+ g_return_if_fail (FOO_IS_CANVAS_GROUP (object));
+
+ group = FOO_CANVAS_GROUP (object);
+
+ list = group->item_list;
+ while (list) {
+ child = list->data;
+ list = list->next;
+
+ gtk_object_destroy (GTK_OBJECT (child));
+ }
+
+ if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
+}
+
+/* Update handler for canvas groups */
+static void
+foo_canvas_group_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
+{
+ FooCanvasGroup *group;
+ GList *list;
+ FooCanvasItem *i;
+ double bbox_x0, bbox_y0, bbox_x1, bbox_y1;
+ gboolean first = TRUE;
+
+ group = FOO_CANVAS_GROUP (item);
+
+ (* group_parent_class->update) (item, i2w_dx, i2w_dy, flags);
+
+ bbox_x0 = 0;
+ bbox_y0 = 0;
+ bbox_x1 = 0;
+ bbox_y1 = 0;
+
+ for (list = group->item_list; list; list = list->next) {
+ i = list->data;
+
+ foo_canvas_item_invoke_update (i, i2w_dx + group->xpos, i2w_dy + group->ypos, flags);
+
+ if (first) {
+ first = FALSE;
+ bbox_x0 = i->x1;
+ bbox_y0 = i->y1;
+ bbox_x1 = i->x2;
+ bbox_y1 = i->y2;
+ } else {
+ bbox_x0 = MIN (bbox_x0, i->x1);
+ bbox_y0 = MIN (bbox_y0, i->y1);
+ bbox_x1 = MAX (bbox_x1, i->x2);
+ bbox_y1 = MAX (bbox_y1, i->y2);
+ }
+ }
+ item->x1 = bbox_x0;
+ item->y1 = bbox_y0;
+ item->x2 = bbox_x1;
+ item->y2 = bbox_y1;
+}
+
+/* Unrealize handler for canvas groups */
+static void
+foo_canvas_group_unrealize (FooCanvasItem *item)
+{
+ FooCanvasGroup *group;
+ GList *list;
+ FooCanvasItem *i;
+
+ group = FOO_CANVAS_GROUP (item);
+
+ /* Unmap group before children to avoid flash */
+ if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
+ (* FOO_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+ for (list = group->item_list; list; list = list->next) {
+ i = list->data;
+
+ if (i->object.flags & FOO_CANVAS_ITEM_REALIZED)
+ (* FOO_CANVAS_ITEM_GET_CLASS (i)->unrealize) (i);
+ }
+
+ (* group_parent_class->unrealize) (item);
+}
+
+/* Map handler for canvas groups */
+static void
+foo_canvas_group_map (FooCanvasItem *item)
+{
+ FooCanvasGroup *group;
+ GList *list;
+ FooCanvasItem *i;
+
+ group = FOO_CANVAS_GROUP (item);
+
+ for (list = group->item_list; list; list = list->next) {
+ i = list->data;
+
+ if (i->object.flags & FOO_CANVAS_ITEM_VISIBLE &&
+ !(i->object.flags & FOO_CANVAS_ITEM_MAPPED)) {
+ if (!(i->object.flags & FOO_CANVAS_ITEM_REALIZED))
+ (* FOO_CANVAS_ITEM_GET_CLASS (i)->realize) (i);
+
+ (* FOO_CANVAS_ITEM_GET_CLASS (i)->map) (i);
+ }
+ }
+
+ (* group_parent_class->map) (item);
+}
+
+/* Unmap handler for canvas groups */
+static void
+foo_canvas_group_unmap (FooCanvasItem *item)
+{
+ FooCanvasGroup *group;
+ GList *list;
+ FooCanvasItem *i;
+
+ group = FOO_CANVAS_GROUP (item);
+
+ for (list = group->item_list; list; list = list->next) {
+ i = list->data;
+
+ if (i->object.flags & FOO_CANVAS_ITEM_MAPPED)
+ (* FOO_CANVAS_ITEM_GET_CLASS (i)->unmap) (i);
+ }
+
+ (* group_parent_class->unmap) (item);
+}
+
+/* Draw handler for canvas groups */
+static void
+foo_canvas_group_draw (FooCanvasItem *item, GdkDrawable *drawable,
+ GdkEventExpose *expose)
+{
+ FooCanvasGroup *group;
+ GList *list;
+ FooCanvasItem *child = NULL;
+
+ group = FOO_CANVAS_GROUP (item);
+
+ for (list = group->item_list; list; list = list->next) {
+ child = list->data;
+
+ if ((child->object.flags & FOO_CANVAS_ITEM_MAPPED) &&
+ (FOO_CANVAS_ITEM_GET_CLASS (child)->draw)) {
+ GdkRectangle child_rect;
+
+ child_rect.x = child->x1;
+ child_rect.y = child->y1;
+ child_rect.width = child->x2 - child->x1 + 1;
+ child_rect.height = child->y2 - child->y1 + 1;
+
+ if (gdk_region_rect_in (expose->region, &child_rect) != GDK_OVERLAP_RECTANGLE_OUT)
+ (* FOO_CANVAS_ITEM_GET_CLASS (child)->draw) (child, drawable, expose);
+ }
+ }
+}
+
+/* Point handler for canvas groups */
+static double
+foo_canvas_group_point (FooCanvasItem *item, double x, double y, int cx, int cy,
+ FooCanvasItem **actual_item)
+{
+ FooCanvasGroup *group;
+ GList *list;
+ FooCanvasItem *child, *point_item;
+ int x1, y1, x2, y2;
+ double gx, gy;
+ double dist, best;
+ int has_point;
+
+ group = FOO_CANVAS_GROUP (item);
+
+ x1 = cx - item->canvas->close_enough;
+ y1 = cy - item->canvas->close_enough;
+ x2 = cx + item->canvas->close_enough;
+ y2 = cy + item->canvas->close_enough;
+
+ best = 0.0;
+ *actual_item = NULL;
+
+ gx = x - group->xpos;
+ gy = y - group->ypos;
+
+ dist = 0.0; /* keep gcc happy */
+
+ for (list = group->item_list; list; list = list->next) {
+ child = list->data;
+
+ if ((child->x1 > x2) || (child->y1 > y2) || (child->x2 < x1) || (child->y2 < y1))
+ continue;
+
+ point_item = NULL; /* cater for incomplete item implementations */
+
+ if ((child->object.flags & FOO_CANVAS_ITEM_MAPPED)
+ && FOO_CANVAS_ITEM_GET_CLASS (child)->point) {
+ dist = foo_canvas_item_invoke_point (child, gx, gy, cx, cy, &point_item);
+ has_point = TRUE;
+ } else
+ has_point = FALSE;
+
+ if (has_point
+ && point_item
+ && ((int) (dist * item->canvas->pixels_per_unit + 0.5)
+ <= item->canvas->close_enough)) {
+ best = dist;
+ *actual_item = point_item;
+ }
+ }
+
+ return best;
+}
+
+void
+foo_canvas_group_translate (FooCanvasItem *item, double dx, double dy)
+{
+ FooCanvasGroup *group;
+
+ group = FOO_CANVAS_GROUP (item);
+
+ group->xpos += dx;
+ group->ypos += dy;
+}
+
+/* Bounds handler for canvas groups */
+static void
+foo_canvas_group_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ FooCanvasGroup *group;
+ FooCanvasItem *child;
+ GList *list;
+ double tx1, ty1, tx2, ty2;
+ double minx, miny, maxx, maxy;
+ int set;
+
+ group = FOO_CANVAS_GROUP (item);
+
+ /* Get the bounds of the first visible item */
+
+ child = NULL; /* Unnecessary but eliminates a warning. */
+
+ set = FALSE;
+
+ for (list = group->item_list; list; list = list->next) {
+ child = list->data;
+
+ if (child->object.flags & FOO_CANVAS_ITEM_MAPPED) {
+ set = TRUE;
+ foo_canvas_item_get_bounds (child, &minx, &miny, &maxx, &maxy);
+ break;
+ }
+ }
+
+ /* If there were no visible items, return an empty bounding box */
+
+ if (!set) {
+ *x1 = *y1 = *x2 = *y2 = 0.0;
+ return;
+ }
+
+ /* Now we can grow the bounds using the rest of the items */
+
+ list = list->next;
+
+ for (; list; list = list->next) {
+ child = list->data;
+
+ if (!(child->object.flags & FOO_CANVAS_ITEM_MAPPED))
+ continue;
+
+ foo_canvas_item_get_bounds (child, &tx1, &ty1, &tx2, &ty2);
+
+ if (tx1 < minx)
+ minx = tx1;
+
+ if (ty1 < miny)
+ miny = ty1;
+
+ if (tx2 > maxx)
+ maxx = tx2;
+
+ if (ty2 > maxy)
+ maxy = ty2;
+ }
+
+ /* Make the bounds be relative to our parent's coordinate system */
+
+ if (item->parent) {
+ minx += group->xpos;
+ miny += group->ypos;
+ maxx += group->xpos;
+ maxy += group->ypos;
+ }
+
+ *x1 = minx;
+ *y1 = miny;
+ *x2 = maxx;
+ *y2 = maxy;
+}
+
+/* Adds an item to a group */
+static void
+group_add (FooCanvasGroup *group, FooCanvasItem *item)
+{
+#if GLIB_CHECK_VERSION(2,10,0) && GTK_CHECK_VERSION(2,8,14)
+ g_object_ref_sink (item);
+#else
+ g_object_ref (item);
+ gtk_object_sink (GTK_OBJECT (item));
+#endif
+
+ if (!group->item_list) {
+ group->item_list = g_list_append (group->item_list, item);
+ group->item_list_end = group->item_list;
+ } else
+ group->item_list_end = g_list_append (group->item_list_end, item)->next;
+
+ if (item->object.flags & FOO_CANVAS_ITEM_VISIBLE &&
+ group->item.object.flags & FOO_CANVAS_ITEM_MAPPED) {
+ if (!(item->object.flags & FOO_CANVAS_ITEM_REALIZED))
+ (* FOO_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
+
+ if (!(item->object.flags & FOO_CANVAS_ITEM_MAPPED))
+ (* FOO_CANVAS_ITEM_GET_CLASS (item)->map) (item);
+ }
+}
+
+/* Removes an item from a group */
+static void
+group_remove (FooCanvasGroup *group, FooCanvasItem *item)
+{
+ GList *children;
+
+ g_return_if_fail (FOO_IS_CANVAS_GROUP (group));
+ g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+ for (children = group->item_list; children; children = children->next)
+ if (children->data == item) {
+ if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
+ (* FOO_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+ if (item->object.flags & FOO_CANVAS_ITEM_REALIZED)
+ (* FOO_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
+
+ /* Unparent the child */
+
+ item->parent = NULL;
+ /* item->canvas = NULL; */
+ g_object_unref (G_OBJECT (item));
+
+ /* Remove it from the list */
+
+ if (children == group->item_list_end)
+ group->item_list_end = children->prev;
+
+ group->item_list = g_list_remove_link (group->item_list, children);
+ g_list_free (children);
+ break;
+ }
+}
+
+
+/*** FooCanvas ***/
+
+
+enum {
+ DRAW_BACKGROUND,
+ LAST_SIGNAL
+};
+
+static void foo_canvas_class_init (FooCanvasClass *klass);
+static void foo_canvas_init (FooCanvas *canvas);
+static void foo_canvas_destroy (GtkObject *object);
+static void foo_canvas_map (GtkWidget *widget);
+static void foo_canvas_unmap (GtkWidget *widget);
+static void foo_canvas_realize (GtkWidget *widget);
+static void foo_canvas_unrealize (GtkWidget *widget);
+static void foo_canvas_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static gint foo_canvas_button (GtkWidget *widget,
+ GdkEventButton *event);
+static gint foo_canvas_motion (GtkWidget *widget,
+ GdkEventMotion *event);
+static gint foo_canvas_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static gint foo_canvas_key (GtkWidget *widget,
+ GdkEventKey *event);
+static gint foo_canvas_crossing (GtkWidget *widget,
+ GdkEventCrossing *event);
+static gint foo_canvas_focus_in (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint foo_canvas_focus_out (GtkWidget *widget,
+ GdkEventFocus *event);
+static void foo_canvas_request_update_real (FooCanvas *canvas);
+static void foo_canvas_draw_background (FooCanvas *canvas,
+ int x,
+ int y,
+ int width,
+ int height);
+
+
+static GtkLayoutClass *canvas_parent_class;
+
+static guint canvas_signals[LAST_SIGNAL];
+
+/**
+ * foo_canvas_get_type:
+ *
+ * Registers the &FooCanvas class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &FooCanvas class.
+ **/
+GType
+foo_canvas_get_type (void)
+{
+ static GType canvas_type = 0;
+
+ if (!canvas_type) {
+ static const GTypeInfo canvas_info = {
+ sizeof (FooCanvasClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) foo_canvas_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (FooCanvas),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) foo_canvas_init
+ };
+
+ canvas_type = g_type_register_static (gtk_layout_get_type (),
+ "FooCanvas",
+ &canvas_info,
+ 0);
+ }
+
+ return canvas_type;
+}
+
+static void
+foo_canvas_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+foo_canvas_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+foo_canvas_accessible_adjustment_changed (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ AtkObject *atk_obj;
+
+ /* The scrollbars have changed */
+ atk_obj = ATK_OBJECT (data);
+
+ g_signal_emit_by_name (atk_obj, "visible_data_changed");
+}
+
+static void
+foo_canvas_accessible_initialize (AtkObject *obj,
+ gpointer data)
+{
+ FooCanvas *canvas;
+
+ if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize != NULL)
+ ATK_OBJECT_CLASS (accessible_parent_class)->initialize (obj, data);
+
+ canvas = FOO_CANVAS (data);
+ g_signal_connect (canvas->layout.hadjustment,
+ "value_changed",
+ G_CALLBACK (foo_canvas_accessible_adjustment_changed),
+ obj);
+ g_signal_connect (canvas->layout.vadjustment,
+ "value_changed",
+ G_CALLBACK (foo_canvas_accessible_adjustment_changed),
+ obj);
+
+ obj->role = ATK_ROLE_LAYERED_PANE;
+}
+
+static gint
+foo_canvas_accessible_get_n_children (AtkObject* obj)
+{
+ GtkAccessible *accessible;
+ GtkWidget *widget;
+ FooCanvas *canvas;
+ FooCanvasGroup *root_group;
+
+ accessible = GTK_ACCESSIBLE (obj);
+ widget = accessible->widget;
+ if (widget == NULL) {
+ /* State is defunct */
+ return 0;
+ }
+
+ g_return_val_if_fail (FOO_IS_CANVAS (widget), 0);
+
+ canvas = FOO_CANVAS (widget);
+ root_group = foo_canvas_root (canvas);
+ g_return_val_if_fail (root_group, 0);
+ return 1;
+}
+
+static AtkObject*
+foo_canvas_accessible_ref_child (AtkObject *obj,
+ gint i)
+{
+ GtkAccessible *accessible;
+ GtkWidget *widget;
+ FooCanvas *canvas;
+ FooCanvasGroup *root_group;
+ AtkObject *atk_object;
+
+ /* Canvas only has one child, so return NULL if index is non zero */
+ if (i != 0) {
+ return NULL;
+ }
+
+ accessible = GTK_ACCESSIBLE (obj);
+ widget = accessible->widget;
+ if (widget == NULL) {
+ /* State is defunct */
+ return NULL;
+ }
+
+ canvas = FOO_CANVAS (widget);
+ root_group = foo_canvas_root (canvas);
+ g_return_val_if_fail (root_group, NULL);
+ atk_object = atk_gobject_accessible_for_object (G_OBJECT (root_group));
+ g_object_ref (atk_object);
+
+ g_warning ("Accessible support for FooGroup needs to be implemented");
+
+ return atk_object;
+}
+
+static void
+foo_canvas_accessible_class_init (AtkObjectClass *klass)
+{
+ accessible_parent_class = g_type_class_peek_parent (klass);
+
+ klass->initialize = foo_canvas_accessible_initialize;
+ klass->get_n_children = foo_canvas_accessible_get_n_children;
+ klass->ref_child = foo_canvas_accessible_ref_child;
+}
+
+static GType
+foo_canvas_accessible_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ AtkObjectFactory *factory;
+ GType parent_atk_type;
+ GTypeQuery query;
+ GTypeInfo tinfo = { 0 };
+
+ factory = atk_registry_get_factory (atk_get_default_registry(),
+ GTK_TYPE_WIDGET);
+ if (!factory) {
+ return G_TYPE_INVALID;
+ }
+ parent_atk_type = atk_object_factory_get_accessible_type (factory);
+ if (!parent_atk_type) {
+ return G_TYPE_INVALID;
+ }
+ g_type_query (parent_atk_type, &query);
+ tinfo.class_init = (GClassInitFunc) foo_canvas_accessible_class_init;
+ tinfo.class_size = query.class_size;
+ tinfo.instance_size = query.instance_size;
+ type = g_type_register_static (parent_atk_type,
+ "FooCanvasAccessibility",
+ &tinfo, 0);
+ }
+ return type;
+}
+
+static AtkObject *
+foo_canvas_accessible_create (GObject *for_object)
+{
+ GType type;
+ AtkObject *accessible;
+ FooCanvas *canvas;
+
+ canvas = FOO_CANVAS (for_object);
+ g_return_val_if_fail (canvas != NULL, NULL);
+
+ type = foo_canvas_accessible_get_type ();
+
+ if (type == G_TYPE_INVALID) {
+ return atk_no_op_object_new (for_object);
+ }
+
+ accessible = g_object_new (type, NULL);
+ atk_object_initialize (accessible, for_object);
+ return accessible;
+}
+
+static GType
+foo_canvas_accessible_factory_get_accessible_type (void)
+{
+ return foo_canvas_accessible_get_type ();
+}
+
+static AtkObject*
+foo_canvas_accessible_factory_create_accessible (GObject *obj)
+{
+ AtkObject *accessible;
+
+ g_return_val_if_fail (G_IS_OBJECT (obj), NULL);
+
+ accessible = foo_canvas_accessible_create (obj);
+
+ return accessible;
+}
+
+static void
+foo_canvas_accessible_factory_class_init (AtkObjectFactoryClass *klass)
+{
+ klass->create_accessible = foo_canvas_accessible_factory_create_accessible;
+ klass->get_accessible_type = foo_canvas_accessible_factory_get_accessible_type;
+}
+
+static GType
+foo_canvas_accessible_factory_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ static const GTypeInfo tinfo = {
+ sizeof (AtkObjectFactoryClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) foo_canvas_accessible_factory_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (AtkObjectFactory),
+ 0, /* n_preallocs */
+ NULL
+ };
+ type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY,
+ "FooCanvasAccessibilityFactory",
+ &tinfo, 0);
+ }
+
+ return type;
+}
+
+
+/* Class initialization function for FooCanvasClass */
+static void
+foo_canvas_class_init (FooCanvasClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ gobject_class = (GObjectClass *)klass;
+ object_class = (GtkObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+
+ canvas_parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->set_property = foo_canvas_set_property;
+ gobject_class->get_property = foo_canvas_get_property;
+
+ object_class->destroy = foo_canvas_destroy;
+
+ widget_class->map = foo_canvas_map;
+ widget_class->unmap = foo_canvas_unmap;
+ widget_class->realize = foo_canvas_realize;
+ widget_class->unrealize = foo_canvas_unrealize;
+ widget_class->size_allocate = foo_canvas_size_allocate;
+ widget_class->button_press_event = foo_canvas_button;
+ widget_class->button_release_event = foo_canvas_button;
+ widget_class->motion_notify_event = foo_canvas_motion;
+ widget_class->expose_event = foo_canvas_expose;
+ widget_class->key_press_event = foo_canvas_key;
+ widget_class->key_release_event = foo_canvas_key;
+ widget_class->enter_notify_event = foo_canvas_crossing;
+ widget_class->leave_notify_event = foo_canvas_crossing;
+ widget_class->focus_in_event = foo_canvas_focus_in;
+ widget_class->focus_out_event = foo_canvas_focus_out;
+
+ klass->draw_background = foo_canvas_draw_background;
+ klass->request_update = foo_canvas_request_update_real;
+
+ canvas_signals[DRAW_BACKGROUND] =
+ g_signal_new ("draw_background",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FooCanvasClass, draw_background),
+ NULL, NULL,
+ foo_canvas_marshal_VOID__INT_INT_INT_INT,
+ G_TYPE_NONE, 4,
+ G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
+
+ atk_registry_set_factory_type (atk_get_default_registry (),
+ FOO_TYPE_CANVAS,
+ foo_canvas_accessible_factory_get_type ());
+}
+
+/* Callback used when the root item of a canvas is destroyed. The user should
+ * never ever do this, so we panic if this happens.
+ */
+static void
+panic_root_destroyed (GtkObject *object, gpointer data)
+{
+ g_error ("Eeeek, root item %p of canvas %p was destroyed!", object, data);
+}
+
+/* Object initialization function for FooCanvas */
+static void
+foo_canvas_init (FooCanvas *canvas)
+{
+ GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
+
+ gtk_widget_set_redraw_on_allocate (GTK_WIDGET (canvas), FALSE);
+
+ canvas->scroll_x1 = 0.0;
+ canvas->scroll_y1 = 0.0;
+ canvas->scroll_x2 = canvas->layout.width;
+ canvas->scroll_y2 = canvas->layout.height;
+
+ canvas->pixels_per_unit = 1.0;
+
+ canvas->pick_event.type = GDK_LEAVE_NOTIFY;
+ canvas->pick_event.crossing.x = 0;
+ canvas->pick_event.crossing.y = 0;
+
+ gtk_layout_set_hadjustment (GTK_LAYOUT (canvas), NULL);
+ gtk_layout_set_vadjustment (GTK_LAYOUT (canvas), NULL);
+
+ /* Create the root item as a special case */
+
+ canvas->root = FOO_CANVAS_ITEM (g_object_new (foo_canvas_group_get_type (), NULL));
+ canvas->root->canvas = canvas;
+
+#if GLIB_CHECK_VERSION(2,10,0) && GTK_CHECK_VERSION(2,8,14)
+ g_object_ref_sink (canvas->root);
+#else
+ g_object_ref (canvas->root);
+ gtk_object_sink (GTK_OBJECT (canvas->root));
+#endif
+
+ canvas->root_destroy_id = g_signal_connect (G_OBJECT (canvas->root),
+ "destroy", G_CALLBACK (panic_root_destroyed), canvas);
+
+ canvas->need_repick = TRUE;
+ canvas->doing_update = FALSE;
+}
+
+/* Convenience function to remove the idle handler of a canvas */
+static void
+remove_idle (FooCanvas *canvas)
+{
+ if (canvas->idle_id == 0)
+ return;
+
+ g_source_remove (canvas->idle_id);
+ canvas->idle_id = 0;
+}
+
+/* Removes the transient state of the canvas (idle handler, grabs). */
+static void
+shutdown_transients (FooCanvas *canvas)
+{
+ /* We turn off the need_redraw flag, since if the canvas is mapped again
+ * it will request a redraw anyways. We do not turn off the need_update
+ * flag, though, because updates are not queued when the canvas remaps
+ * itself.
+ */
+ if (canvas->need_redraw) {
+ canvas->need_redraw = FALSE;
+ }
+
+ if (canvas->grabbed_item) {
+ GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (canvas));
+ canvas->grabbed_item = NULL;
+ gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
+ }
+
+ remove_idle (canvas);
+}
+
+/* Destroy handler for FooCanvas */
+static void
+foo_canvas_destroy (GtkObject *object)
+{
+ FooCanvas *canvas;
+
+ g_return_if_fail (FOO_IS_CANVAS (object));
+
+ /* remember, destroy can be run multiple times! */
+
+ canvas = FOO_CANVAS (object);
+
+ if (canvas->root_destroy_id) {
+ g_signal_handler_disconnect (G_OBJECT (canvas->root), canvas->root_destroy_id);
+ canvas->root_destroy_id = 0;
+ }
+ if (canvas->root) {
+ FooCanvasItem *root = canvas->root;
+ canvas->root = NULL;
+ gtk_object_destroy (GTK_OBJECT (root));
+ g_object_unref (root);
+ }
+
+ shutdown_transients (canvas);
+
+ if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
+}
+
+/**
+ * foo_canvas_new:
+ * @void:
+ *
+ * Creates a new empty canvas. If you wish to use the
+ * &FooCanvasImage item inside this canvas, then you must push the gdk_imlib
+ * visual and colormap before calling this function, and they can be popped
+ * afterwards.
+ *
+ * Return value: A newly-created canvas.
+ **/
+GtkWidget *
+foo_canvas_new (void)
+{
+ return GTK_WIDGET (g_object_new (foo_canvas_get_type (), NULL));
+}
+
+/* Map handler for the canvas */
+static void
+foo_canvas_map (GtkWidget *widget)
+{
+ FooCanvas *canvas;
+
+ g_return_if_fail (FOO_IS_CANVAS (widget));
+
+ /* Normal widget mapping stuff */
+
+ if (GTK_WIDGET_CLASS (canvas_parent_class)->map)
+ (* GTK_WIDGET_CLASS (canvas_parent_class)->map) (widget);
+
+ canvas = FOO_CANVAS (widget);
+
+ /* Map items */
+
+ if (canvas->root->object.flags & FOO_CANVAS_ITEM_VISIBLE &&
+ !(canvas->root->object.flags & FOO_CANVAS_ITEM_MAPPED) &&
+ FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->map)
+ (* FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->map) (canvas->root);
+}
+
+/* Unmap handler for the canvas */
+static void
+foo_canvas_unmap (GtkWidget *widget)
+{
+ FooCanvas *canvas;
+
+ g_return_if_fail (FOO_IS_CANVAS (widget));
+
+ canvas = FOO_CANVAS (widget);
+
+ shutdown_transients (canvas);
+
+ /* Unmap items */
+
+ if (FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap)
+ (* FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap) (canvas->root);
+
+ /* Normal widget unmapping stuff */
+
+ if (GTK_WIDGET_CLASS (canvas_parent_class)->unmap)
+ (* GTK_WIDGET_CLASS (canvas_parent_class)->unmap) (widget);
+}
+
+/* Realize handler for the canvas */
+static void
+foo_canvas_realize (GtkWidget *widget)
+{
+ FooCanvas *canvas;
+
+ g_return_if_fail (FOO_IS_CANVAS (widget));
+
+ /* Normal widget realization stuff */
+
+ if (GTK_WIDGET_CLASS (canvas_parent_class)->realize)
+ (* GTK_WIDGET_CLASS (canvas_parent_class)->realize) (widget);
+
+ canvas = FOO_CANVAS (widget);
+
+ gdk_window_set_events (canvas->layout.bin_window,
+ (gdk_window_get_events (canvas->layout.bin_window)
+ | GDK_EXPOSURE_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_KEY_PRESS_MASK
+ | GDK_KEY_RELEASE_MASK
+ | GDK_ENTER_NOTIFY_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_FOCUS_CHANGE_MASK));
+
+ /* Create our own temporary pixmap gc and realize all the items */
+
+ canvas->pixmap_gc = gdk_gc_new (canvas->layout.bin_window);
+
+ (* FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->realize) (canvas->root);
+}
+
+/* Unrealize handler for the canvas */
+static void
+foo_canvas_unrealize (GtkWidget *widget)
+{
+ FooCanvas *canvas;
+
+ g_return_if_fail (FOO_IS_CANVAS (widget));
+
+ canvas = FOO_CANVAS (widget);
+
+ shutdown_transients (canvas);
+
+ /* Unrealize items and parent widget */
+
+ (* FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->unrealize) (canvas->root);
+
+ g_object_unref (canvas->pixmap_gc);
+ canvas->pixmap_gc = NULL;
+
+ if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
+ (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
+}
+
+/* Handles scrolling of the canvas. Adjusts the scrolling and zooming offset to
+ * keep as much as possible of the canvas scrolling region in view.
+ */
+static void
+scroll_to (FooCanvas *canvas, int cx, int cy)
+{
+ int scroll_width, scroll_height;
+ int right_limit, bottom_limit;
+ int old_zoom_xofs, old_zoom_yofs;
+ int changed_x = FALSE, changed_y = FALSE;
+ int canvas_width, canvas_height;
+
+ canvas_width = GTK_WIDGET (canvas)->allocation.width;
+ canvas_height = GTK_WIDGET (canvas)->allocation.height;
+
+ scroll_width = floor ((canvas->scroll_x2 - canvas->scroll_x1) * canvas->pixels_per_unit + 0.5);
+ scroll_height = floor ((canvas->scroll_y2 - canvas->scroll_y1) * canvas->pixels_per_unit + 0.5);
+
+ right_limit = scroll_width - canvas_width;
+ bottom_limit = scroll_height - canvas_height;
+
+ old_zoom_xofs = canvas->zoom_xofs;
+ old_zoom_yofs = canvas->zoom_yofs;
+
+ if (right_limit < 0) {
+ cx = 0;
+ if (canvas->center_scroll_region) {
+ canvas->zoom_xofs = (canvas_width - scroll_width) / 2;
+ scroll_width = canvas_width;
+ } else {
+ canvas->zoom_xofs = 0;
+ }
+ } else if (cx < 0) {
+ cx = 0;
+ canvas->zoom_xofs = 0;
+ } else if (cx > right_limit) {
+ cx = right_limit;
+ canvas->zoom_xofs = 0;
+ } else
+ canvas->zoom_xofs = 0;
+
+ if (bottom_limit < 0) {
+ cy = 0;
+ if (canvas->center_scroll_region) {
+ canvas->zoom_yofs = (canvas_height - scroll_height) / 2;
+ scroll_height = canvas_height;
+ } else {
+ canvas->zoom_yofs = 0;
+ }
+ } else if (cy < 0) {
+ cy = 0;
+ canvas->zoom_yofs = 0;
+ } else if (cy > bottom_limit) {
+ cy = bottom_limit;
+ canvas->zoom_yofs = 0;
+ } else
+ canvas->zoom_yofs = 0;
+
+ if ((canvas->zoom_xofs != old_zoom_xofs) || (canvas->zoom_yofs != old_zoom_yofs)) {
+ /* This can only occur, if either canvas size or widget size changes */
+ /* So I think we can request full redraw here */
+ /* More stuff - we have to mark root as needing fresh affine (Lauris) */
+ if (!(canvas->root->object.flags & FOO_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
+ canvas->root->object.flags |= FOO_CANVAS_ITEM_NEED_DEEP_UPDATE;
+ foo_canvas_request_update (canvas);
+ }
+ gtk_widget_queue_draw (GTK_WIDGET (canvas));
+ }
+
+ if (((int) canvas->layout.hadjustment->value) != cx) {
+ canvas->layout.hadjustment->value = cx;
+ changed_x = TRUE;
+ }
+
+ if (((int) canvas->layout.vadjustment->value) != cy) {
+ canvas->layout.vadjustment->value = cy;
+ changed_y = TRUE;
+ }
+
+ if ((scroll_width != (int) canvas->layout.width) || (scroll_height != (int) canvas->layout.height)) {
+ gtk_layout_set_size (GTK_LAYOUT (canvas), scroll_width, scroll_height);
+ }
+
+ /* Signal GtkLayout that it should do a redraw. */
+ if (changed_x)
+ g_signal_emit_by_name (G_OBJECT (canvas->layout.hadjustment), "value_changed");
+ if (changed_y)
+ g_signal_emit_by_name (G_OBJECT (canvas->layout.vadjustment), "value_changed");
+}
+
+/* Size allocation handler for the canvas */
+static void
+foo_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ FooCanvas *canvas;
+
+ g_return_if_fail (FOO_IS_CANVAS (widget));
+ g_return_if_fail (allocation != NULL);
+
+ if (GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate)
+ (* GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate) (widget, allocation);
+
+ canvas = FOO_CANVAS (widget);
+
+ /* Recenter the view, if appropriate */
+
+ canvas->layout.hadjustment->page_size = allocation->width;
+ canvas->layout.hadjustment->page_increment = allocation->width / 2;
+
+ canvas->layout.vadjustment->page_size = allocation->height;
+ canvas->layout.vadjustment->page_increment = allocation->height / 2;
+
+ scroll_to (canvas,
+ canvas->layout.hadjustment->value,
+ canvas->layout.vadjustment->value);
+
+ g_signal_emit_by_name (G_OBJECT (canvas->layout.hadjustment), "changed");
+ g_signal_emit_by_name (G_OBJECT (canvas->layout.vadjustment), "changed");
+}
+
+/* Emits an event for an item in the canvas, be it the current item, grabbed
+ * item, or focused item, as appropriate.
+ */
+
+static int
+emit_event (FooCanvas *canvas, GdkEvent *event)
+{
+ GdkEvent ev;
+ gint finished;
+ FooCanvasItem *item;
+ FooCanvasItem *parent;
+ guint mask;
+
+ /* Could be an old pick event */
+ if (!gtk_widget_get_realized (GTK_WIDGET (canvas))) {
+ return FALSE;
+ }
+
+ /* Perform checks for grabbed items */
+
+ if (canvas->grabbed_item &&
+ !is_descendant (canvas->current_item, canvas->grabbed_item)) {
+ return FALSE;
+ }
+
+ if (canvas->grabbed_item) {
+ switch (event->type) {
+ case GDK_ENTER_NOTIFY:
+ mask = GDK_ENTER_NOTIFY_MASK;
+ break;
+
+ case GDK_LEAVE_NOTIFY:
+ mask = GDK_LEAVE_NOTIFY_MASK;
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ mask = GDK_POINTER_MOTION_MASK;
+ break;
+
+ case GDK_BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ mask = GDK_BUTTON_PRESS_MASK;
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ mask = GDK_BUTTON_RELEASE_MASK;
+ break;
+
+ case GDK_KEY_PRESS:
+ mask = GDK_KEY_PRESS_MASK;
+ break;
+
+ case GDK_KEY_RELEASE:
+ mask = GDK_KEY_RELEASE_MASK;
+ break;
+
+ default:
+ mask = 0;
+ break;
+ }
+
+ if (!(mask & canvas->grabbed_event_mask))
+ return FALSE;
+ }
+
+ /* Convert to world coordinates -- we have two cases because of diferent
+ * offsets of the fields in the event structures.
+ */
+
+ ev = *event;
+
+ switch (ev.type)
+ {
+ case GDK_ENTER_NOTIFY:
+ case GDK_LEAVE_NOTIFY:
+ foo_canvas_window_to_world (canvas,
+ ev.crossing.x, ev.crossing.y,
+ &ev.crossing.x, &ev.crossing.y);
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ foo_canvas_window_to_world (canvas,
+ ev.motion.x, ev.motion.y,
+ &ev.motion.x, &ev.motion.y);
+ break;
+
+ case GDK_BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ foo_canvas_window_to_world (canvas,
+ ev.motion.x, ev.motion.y,
+ &ev.motion.x, &ev.motion.y);
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ foo_canvas_window_to_world (canvas,
+ ev.motion.x, ev.motion.y,
+ &ev.motion.x, &ev.motion.y);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Choose where we send the event */
+
+ item = canvas->current_item;
+
+ if (canvas->focused_item
+ && ((event->type == GDK_KEY_PRESS) ||
+ (event->type == GDK_KEY_RELEASE) ||
+ (event->type == GDK_FOCUS_CHANGE)))
+ item = canvas->focused_item;
+
+ /* The event is propagated up the hierarchy (for if someone connected to
+ * a group instead of a leaf event), and emission is stopped if a
+ * handler returns TRUE, just like for GtkWidget events.
+ */
+
+ finished = FALSE;
+
+ while (item && !finished) {
+ g_object_ref (GTK_OBJECT (item));
+
+ g_signal_emit (
+ G_OBJECT (item), item_signals[ITEM_EVENT], 0,
+ &ev, &finished);
+
+ parent = item->parent;
+ g_object_unref (GTK_OBJECT (item));
+
+ item = parent;
+ }
+
+ return finished;
+}
+
+/* Re-picks the current item in the canvas, based on the event's coordinates.
+ * Also emits enter/leave events for items as appropriate.
+ */
+static int
+pick_current_item (FooCanvas *canvas, GdkEvent *event)
+{
+ int button_down;
+ double x, y;
+ int cx, cy;
+ int retval;
+
+ retval = FALSE;
+
+ /* If a button is down, we'll perform enter and leave events on the
+ * current item, but not enter on any other item. This is more or less
+ * like X pointer grabbing for canvas items.
+ */
+ button_down = canvas->state & (GDK_BUTTON1_MASK
+ | GDK_BUTTON2_MASK
+ | GDK_BUTTON3_MASK
+ | GDK_BUTTON4_MASK
+ | GDK_BUTTON5_MASK);
+ if (!button_down)
+ canvas->left_grabbed_item = FALSE;
+
+ /* Save the event in the canvas. This is used to synthesize enter and
+ * leave events in case the current item changes. It is also used to
+ * re-pick the current item if the current one gets deleted. Also,
+ * synthesize an enter event.
+ */
+ if (event != &canvas->pick_event) {
+ if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
+ /* these fields have the same offsets in both types of events */
+
+ canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
+ canvas->pick_event.crossing.window = event->motion.window;
+ canvas->pick_event.crossing.send_event = event->motion.send_event;
+ canvas->pick_event.crossing.subwindow = NULL;
+ canvas->pick_event.crossing.x = event->motion.x;
+ canvas->pick_event.crossing.y = event->motion.y;
+ canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
+ canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
+ canvas->pick_event.crossing.focus = FALSE;
+ canvas->pick_event.crossing.state = event->motion.state;
+
+ /* these fields don't have the same offsets in both types of events */
+
+ if (event->type == GDK_MOTION_NOTIFY) {
+ canvas->pick_event.crossing.x_root = event->motion.x_root;
+ canvas->pick_event.crossing.y_root = event->motion.y_root;
+ } else {
+ canvas->pick_event.crossing.x_root = event->button.x_root;
+ canvas->pick_event.crossing.y_root = event->button.y_root;
+ }
+ } else
+ canvas->pick_event = *event;
+ }
+
+ /* Don't do anything else if this is a recursive call */
+
+ if (canvas->in_repick)
+ return retval;
+
+ /* LeaveNotify means that there is no current item, so we don't look for one */
+
+ if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
+ /* these fields don't have the same offsets in both types of events */
+
+ if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
+ x = canvas->pick_event.crossing.x;
+ y = canvas->pick_event.crossing.y;
+ } else {
+ x = canvas->pick_event.motion.x;
+ y = canvas->pick_event.motion.y;
+ }
+
+ /* canvas pixel coords */
+
+ cx = (int) (x + 0.5);
+ cy = (int) (y + 0.5);
+
+ /* world coords */
+ foo_canvas_c2w (canvas, cx, cy, &x, &y);
+
+ /* find the closest item */
+ if (canvas->root->object.flags & FOO_CANVAS_ITEM_MAPPED)
+ foo_canvas_item_invoke_point (canvas->root, x, y, cx, cy,
+ &canvas->new_current_item);
+ else
+ canvas->new_current_item = NULL;
+ } else
+ canvas->new_current_item = NULL;
+
+ if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item)
+ return retval; /* current item did not change */
+
+ /* Synthesize events for old and new current items */
+
+ if ((canvas->new_current_item != canvas->current_item)
+ && (canvas->current_item != NULL)
+ && !canvas->left_grabbed_item) {
+ GdkEvent new_event;
+ FooCanvasItem *item;
+
+ item = canvas->current_item;
+
+ new_event = canvas->pick_event;
+ new_event.type = GDK_LEAVE_NOTIFY;
+
+ new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
+ new_event.crossing.subwindow = NULL;
+ canvas->in_repick = TRUE;
+ retval = emit_event (canvas, &new_event);
+ canvas->in_repick = FALSE;
+ }
+
+ /* new_current_item may have been set to NULL during the call to emit_event() above */
+
+ if ((canvas->new_current_item != canvas->current_item) && button_down) {
+ canvas->left_grabbed_item = TRUE;
+ return retval;
+ }
+
+ /* Handle the rest of cases */
+
+ canvas->left_grabbed_item = FALSE;
+ canvas->current_item = canvas->new_current_item;
+
+ if (canvas->current_item != NULL) {
+ GdkEvent new_event;
+
+ new_event = canvas->pick_event;
+ new_event.type = GDK_ENTER_NOTIFY;
+ new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
+ new_event.crossing.subwindow = NULL;
+ retval = emit_event (canvas, &new_event);
+ }
+
+ return retval;
+}
+
+/* Button event handler for the canvas */
+static gint
+foo_canvas_button (GtkWidget *widget, GdkEventButton *event)
+{
+ FooCanvas *canvas;
+ int mask;
+ int retval;
+
+ g_return_val_if_fail (FOO_IS_CANVAS (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ retval = FALSE;
+
+ canvas = FOO_CANVAS (widget);
+
+ /*
+ * dispatch normally regardless of the event's window if an item has
+ * has a pointer grab in effect
+ */
+ if (!canvas->grabbed_item && event->window != canvas->layout.bin_window)
+ return retval;
+
+ switch (event->button) {
+ case 1:
+ mask = GDK_BUTTON1_MASK;
+ break;
+ case 2:
+ mask = GDK_BUTTON2_MASK;
+ break;
+ case 3:
+ mask = GDK_BUTTON3_MASK;
+ break;
+ case 4:
+ mask = GDK_BUTTON4_MASK;
+ break;
+ case 5:
+ mask = GDK_BUTTON5_MASK;
+ break;
+ default:
+ mask = 0;
+ }
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ /* Pick the current item as if the button were not pressed, and
+ * then process the event.
+ */
+ canvas->state = event->state;
+ pick_current_item (canvas, (GdkEvent *) event);
+ canvas->state ^= mask;
+ retval = emit_event (canvas, (GdkEvent *) event);
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ /* Process the event as if the button were pressed, then repick
+ * after the button has been released
+ */
+ canvas->state = event->state;
+ retval = emit_event (canvas, (GdkEvent *) event);
+ event->state ^= mask;
+ canvas->state = event->state;
+ pick_current_item (canvas, (GdkEvent *) event);
+ event->state ^= mask;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ return retval;
+}
+
+/* Motion event handler for the canvas */
+static gint
+foo_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
+{
+ FooCanvas *canvas;
+
+ g_return_val_if_fail (FOO_IS_CANVAS (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ canvas = FOO_CANVAS (widget);
+
+ if (event->window != canvas->layout.bin_window)
+ return FALSE;
+
+ canvas->state = event->state;
+ pick_current_item (canvas, (GdkEvent *) event);
+ return emit_event (canvas, (GdkEvent *) event);
+}
+
+/* Key event handler for the canvas */
+static gint
+foo_canvas_key (GtkWidget *widget, GdkEventKey *event)
+{
+ FooCanvas *canvas;
+
+ g_return_val_if_fail (FOO_IS_CANVAS (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ canvas = FOO_CANVAS (widget);
+
+ if (emit_event (canvas, (GdkEvent *) event))
+ return TRUE;
+ if (event->type == GDK_KEY_RELEASE)
+ return GTK_WIDGET_CLASS (canvas_parent_class)->key_release_event (widget, event);
+ else
+ return GTK_WIDGET_CLASS (canvas_parent_class)->key_press_event (widget, event);
+}
+
+
+/* Crossing event handler for the canvas */
+static gint
+foo_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
+{
+ FooCanvas *canvas;
+
+ g_return_val_if_fail (FOO_IS_CANVAS (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ canvas = FOO_CANVAS (widget);
+
+ if (event->window != canvas->layout.bin_window)
+ return FALSE;
+
+ canvas->state = event->state;
+ return pick_current_item (canvas, (GdkEvent *) event);
+}
+
+/* Focus in handler for the canvas */
+static gint
+foo_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
+{
+ FooCanvas *canvas;
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
+
+ canvas = FOO_CANVAS (widget);
+
+ if (canvas->focused_item)
+ return emit_event (canvas, (GdkEvent *) event);
+ else
+ return FALSE;
+}
+
+/* Focus out handler for the canvas */
+static gint
+foo_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
+{
+ FooCanvas *canvas;
+
+ GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
+
+ canvas = FOO_CANVAS (widget);
+
+ if (canvas->focused_item)
+ return emit_event (canvas, (GdkEvent *) event);
+ else
+ return FALSE;
+}
+
+/* Expose handler for the canvas */
+static gint
+foo_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+ FooCanvas *canvas;
+
+ canvas = FOO_CANVAS (widget);
+
+ if (!gtk_widget_is_drawable (widget) || (event->window != canvas->layout.bin_window)) return FALSE;
+
+#ifdef VERBOSE
+ g_print ("Expose\n");
+#endif
+ /* If there are any outstanding items that need updating, do them now */
+ if (canvas->idle_id) {
+ g_source_remove (canvas->idle_id);
+ canvas->idle_id = 0;
+ }
+ if (canvas->need_update) {
+ g_return_val_if_fail (!canvas->doing_update, FALSE);
+
+ canvas->doing_update = TRUE;
+ foo_canvas_item_invoke_update (canvas->root, 0, 0, 0);
+
+ g_return_val_if_fail (canvas->doing_update, FALSE);
+
+ canvas->doing_update = FALSE;
+
+ canvas->need_update = FALSE;
+ }
+
+ /* Hmmm. Would like to queue antiexposes if the update marked
+ anything that is gonna get redrawn as invalid */
+
+
+ g_signal_emit (G_OBJECT (canvas), canvas_signals[DRAW_BACKGROUND], 0,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+
+ if (canvas->root->object.flags & FOO_CANVAS_ITEM_MAPPED)
+ (* FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->draw) (canvas->root,
+ canvas->layout.bin_window,
+ event);
+
+
+
+ /* Chain up to get exposes on child widgets */
+ GTK_WIDGET_CLASS (canvas_parent_class)->expose_event (widget, event);
+
+ return FALSE;
+}
+
+static void
+foo_canvas_draw_background (FooCanvas *canvas,
+ int x, int y, int width, int height)
+{
+ /* By default, we use the style background. */
+ gdk_gc_set_foreground (canvas->pixmap_gc,
+ >K_WIDGET (canvas)->style->bg[GTK_STATE_NORMAL]);
+ gdk_draw_rectangle (canvas->layout.bin_window,
+ canvas->pixmap_gc,
+ TRUE,
+ x, y,
+ width, height);
+}
+
+static void
+do_update (FooCanvas *canvas)
+{
+ /* Cause the update if necessary */
+
+update_again:
+ if (canvas->need_update) {
+ g_return_if_fail (!canvas->doing_update);
+
+ canvas->doing_update = TRUE;
+ foo_canvas_item_invoke_update (canvas->root, 0, 0, 0);
+
+ g_return_if_fail (canvas->doing_update);
+
+ canvas->doing_update = FALSE;
+
+ canvas->need_update = FALSE;
+ }
+
+ /* Pick new current item */
+
+ while (canvas->need_repick) {
+ canvas->need_repick = FALSE;
+ pick_current_item (canvas, &canvas->pick_event);
+ }
+
+ /* it is possible that during picking we emitted an event in which
+ the user then called some function which then requested update
+ of something. Without this we'd be left in a state where
+ need_update would have been left TRUE and the canvas would have
+ been left unpainted. */
+ if (canvas->need_update) {
+ goto update_again;
+ }
+}
+
+/* Idle handler for the canvas. It deals with pending updates and redraws. */
+static gint
+idle_handler (gpointer data)
+{
+ FooCanvas *canvas;
+
+ GDK_THREADS_ENTER ();
+
+ canvas = FOO_CANVAS (data);
+ do_update (canvas);
+
+ /* Reset idle id */
+ canvas->idle_id = 0;
+
+ GDK_THREADS_LEAVE ();
+
+ return FALSE;
+}
+
+/* Convenience function to add an idle handler to a canvas */
+static void
+add_idle (FooCanvas *canvas)
+{
+ if (!canvas->idle_id) {
+ /* We let the update idle handler have higher priority
+ * than the redraw idle handler so the canvas state
+ * will be updated during the expose event. canvas in
+ * expose_event.
+ */
+ canvas->idle_id = g_idle_add_full (GDK_PRIORITY_REDRAW - 20,
+ idle_handler, canvas, NULL);
+ }
+}
+
+/**
+ * foo_canvas_root:
+ * @canvas: A canvas.
+ *
+ * Queries the root group of a canvas.
+ *
+ * Return value: The root group of the specified canvas.
+ **/
+FooCanvasGroup *
+foo_canvas_root (FooCanvas *canvas)
+{
+ g_return_val_if_fail (FOO_IS_CANVAS (canvas), NULL);
+
+ return FOO_CANVAS_GROUP (canvas->root);
+}
+
+
+/**
+ * foo_canvas_set_scroll_region:
+ * @canvas: A canvas.
+ * @x1: Leftmost limit of the scrolling region.
+ * @y1: Upper limit of the scrolling region.
+ * @x2: Rightmost limit of the scrolling region.
+ * @y2: Lower limit of the scrolling region.
+ *
+ * Sets the scrolling region of a canvas to the specified rectangle. The canvas
+ * will then be able to scroll only within this region. The view of the canvas
+ * is adjusted as appropriate to display as much of the new region as possible.
+ **/
+void
+foo_canvas_set_scroll_region (FooCanvas *canvas, double x1, double y1, double x2, double y2)
+{
+ double wxofs, wyofs;
+ int xofs, yofs;
+
+ g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+ if ((canvas->scroll_x1 == x1) && (canvas->scroll_y1 == y1) &&
+ (canvas->scroll_x2 == x2) && (canvas->scroll_y2 == y2)) {
+ return;
+ }
+
+ /*
+ * Set the new scrolling region. If possible, do not move the visible contents of the
+ * canvas.
+ */
+
+ foo_canvas_c2w (canvas,
+ GTK_LAYOUT (canvas)->hadjustment->value + canvas->zoom_xofs,
+ GTK_LAYOUT (canvas)->vadjustment->value + canvas->zoom_yofs,
+ /*canvas->zoom_xofs,
+ canvas->zoom_yofs,*/
+ &wxofs, &wyofs);
+
+ canvas->scroll_x1 = x1;
+ canvas->scroll_y1 = y1;
+ canvas->scroll_x2 = x2;
+ canvas->scroll_y2 = y2;
+
+ foo_canvas_w2c (canvas, wxofs, wyofs, &xofs, &yofs);
+
+ scroll_to (canvas, xofs, yofs);
+
+ canvas->need_repick = TRUE;
+
+ if (!(canvas->root->object.flags & FOO_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
+ canvas->root->object.flags |= FOO_CANVAS_ITEM_NEED_DEEP_UPDATE;
+ foo_canvas_request_update (canvas);
+ }
+}
+
+
+/**
+ * foo_canvas_get_scroll_region:
+ * @canvas: A canvas.
+ * @x1: Leftmost limit of the scrolling region (return value).
+ * @y1: Upper limit of the scrolling region (return value).
+ * @x2: Rightmost limit of the scrolling region (return value).
+ * @y2: Lower limit of the scrolling region (return value).
+ *
+ * Queries the scrolling region of a canvas.
+ **/
+void
+foo_canvas_get_scroll_region (FooCanvas *canvas, double *x1, double *y1, double *x2, double *y2)
+{
+ g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+ if (x1)
+ *x1 = canvas->scroll_x1;
+
+ if (y1)
+ *y1 = canvas->scroll_y1;
+
+ if (x2)
+ *x2 = canvas->scroll_x2;
+
+ if (y2)
+ *y2 = canvas->scroll_y2;
+}
+
+void
+foo_canvas_set_center_scroll_region (FooCanvas *canvas,
+ gboolean center_scroll_region)
+{
+ g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+ canvas->center_scroll_region = center_scroll_region != 0;
+
+ scroll_to (canvas,
+ canvas->layout.hadjustment->value,
+ canvas->layout.vadjustment->value);
+}
+
+
+/**
+ * foo_canvas_set_pixels_per_unit:
+ * @canvas: A canvas.
+ * @n: The number of pixels that correspond to one canvas unit.
+ *
+ * Sets the zooming factor of a canvas by specifying the number of pixels that
+ * correspond to one canvas unit.
+ **/
+void
+foo_canvas_set_pixels_per_unit (FooCanvas *canvas, double n)
+{
+ GtkWidget *widget;
+ double cx, cy;
+ int x1, y1;
+ int center_x, center_y;
+ GdkWindow *window;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ g_return_if_fail (FOO_IS_CANVAS (canvas));
+ g_return_if_fail (n > FOO_CANVAS_EPSILON);
+
+ widget = GTK_WIDGET (canvas);
+
+ center_x = widget->allocation.width / 2;
+ center_y = widget->allocation.height / 2;
+
+ /* Find the coordinates of the screen center in units. */
+ cx = (canvas->layout.hadjustment->value + center_x) / canvas->pixels_per_unit + canvas->scroll_x1 + canvas->zoom_xofs;
+ cy = (canvas->layout.vadjustment->value + center_y) / canvas->pixels_per_unit + canvas->scroll_y1 + canvas->zoom_yofs;
+
+ /* Now calculate the new offset of the upper left corner. (round not truncate) */
+ x1 = ((cx - canvas->scroll_x1) * n) - center_x + .5;
+ y1 = ((cy - canvas->scroll_y1) * n) - center_y + .5;
+
+ canvas->pixels_per_unit = n;
+
+ if (!(canvas->root->object.flags & FOO_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
+ canvas->root->object.flags |= FOO_CANVAS_ITEM_NEED_DEEP_UPDATE;
+ foo_canvas_request_update (canvas);
+ }
+
+ /* Map a background None window over the bin_window to avoid
+ * scrolling the window scroll causing exposes.
+ */
+ window = NULL;
+ if (gtk_widget_get_mapped (widget)) {
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+ attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+ window = gdk_window_new (gtk_widget_get_parent_window (widget),
+ &attributes, attributes_mask);
+ gdk_window_set_back_pixmap (window, NULL, FALSE);
+ gdk_window_set_user_data (window, widget);
+
+ gdk_window_show (window);
+ }
+
+ scroll_to (canvas, x1, y1);
+
+ /* If we created a an overlapping background None window, remove it how.
+ *
+ * TODO: We would like to temporarily set the bin_window background to
+ * None to avoid clearing the bin_window to the background, but gdk doesn't
+ * expose enought to let us do this, so we get a flash-effect here. At least
+ * it looks better than scroll + expose.
+ */
+ if (window != NULL) {
+ gdk_window_hide (window);
+ gdk_window_set_user_data (window, NULL);
+ gdk_window_destroy (window);
+ }
+
+ canvas->need_repick = TRUE;
+}
+
+/**
+ * foo_canvas_scroll_to:
+ * @canvas: A canvas.
+ * @cx: Horizontal scrolling offset in canvas pixel units.
+ * @cy: Vertical scrolling offset in canvas pixel units.
+ *
+ * Makes a canvas scroll to the specified offsets, given in canvas pixel units.
+ * The canvas will adjust the view so that it is not outside the scrolling
+ * region. This function is typically not used, as it is better to hook
+ * scrollbars to the canvas layout's scrolling adjusments.
+ **/
+void
+foo_canvas_scroll_to (FooCanvas *canvas, int cx, int cy)
+{
+ g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+ scroll_to (canvas, cx, cy);
+}
+
+/**
+ * foo_canvas_get_scroll_offsets:
+ * @canvas: A canvas.
+ * @cx: Horizontal scrolling offset (return value).
+ * @cy: Vertical scrolling offset (return value).
+ *
+ * Queries the scrolling offsets of a canvas. The values are returned in canvas
+ * pixel units.
+ **/
+void
+foo_canvas_get_scroll_offsets (FooCanvas *canvas, int *cx, int *cy)
+{
+ g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+ if (cx)
+ *cx = canvas->layout.hadjustment->value;
+
+ if (cy)
+ *cy = canvas->layout.vadjustment->value;
+}
+
+/**
+ * foo_canvas_update_now:
+ * @canvas: A canvas.
+ *
+ * Forces an immediate update and redraw of a canvas. If the canvas does not
+ * have any pending update or redraw requests, then no action is taken. This is
+ * typically only used by applications that need explicit control of when the
+ * display is updated, like games. It is not needed by normal applications.
+ */
+void
+foo_canvas_update_now (FooCanvas *canvas)
+{
+ g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+ if (!(canvas->need_update || canvas->need_redraw))
+ return;
+ remove_idle (canvas);
+ do_update (canvas);
+}
+
+/**
+ * foo_canvas_get_item_at:
+ * @canvas: A canvas.
+ * @x: X position in world coordinates.
+ * @y: Y position in world coordinates.
+ *
+ * Looks for the item that is under the specified position, which must be
+ * specified in world coordinates.
+ *
+ * Return value: The sought item, or NULL if no item is at the specified
+ * coordinates.
+ **/
+FooCanvasItem *
+foo_canvas_get_item_at (FooCanvas *canvas, double x, double y)
+{
+ FooCanvasItem *item;
+ double dist;
+ int cx, cy;
+
+ g_return_val_if_fail (FOO_IS_CANVAS (canvas), NULL);
+
+ foo_canvas_w2c (canvas, x, y, &cx, &cy);
+
+ dist = foo_canvas_item_invoke_point (canvas->root, x, y, cx, cy, &item);
+ if ((int) (dist * canvas->pixels_per_unit + 0.5) <= canvas->close_enough)
+ return item;
+ else
+ return NULL;
+}
+
+/* Queues an update of the canvas */
+static void
+foo_canvas_request_update (FooCanvas *canvas)
+{
+ FOO_CANVAS_GET_CLASS (canvas)->request_update (canvas);
+}
+
+static void
+foo_canvas_request_update_real (FooCanvas *canvas)
+{
+ canvas->need_update = TRUE;
+ add_idle (canvas);
+}
+
+/**
+ * foo_canvas_request_redraw:
+ * @canvas: A canvas.
+ * @x1: Leftmost coordinate of the rectangle to be redrawn.
+ * @y1: Upper coordinate of the rectangle to be redrawn.
+ * @x2: Rightmost coordinate of the rectangle to be redrawn, plus 1.
+ * @y2: Lower coordinate of the rectangle to be redrawn, plus 1.
+ *
+ * Convenience function that informs a canvas that the specified rectangle needs
+ * to be repainted. The rectangle includes @x1 and @y1, but not @x2 and @y2.
+ * To be used only by item implementations.
+ **/
+void
+foo_canvas_request_redraw (FooCanvas *canvas, int x1, int y1, int x2, int y2)
+{
+ GdkRectangle bbox;
+
+ g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+ if (!gtk_widget_is_drawable (GTK_WIDGET (canvas)) || (x1 >= x2) || (y1 >= y2)) return;
+
+ bbox.x = x1;
+ bbox.y = y1;
+ bbox.width = x2 - x1;
+ bbox.height = y2 - y1;
+
+ gdk_window_invalidate_rect (canvas->layout.bin_window,
+ &bbox, FALSE);
+}
+
+/**
+ * foo_canvas_w2c:
+ * @canvas: A canvas.
+ * @wx: World X coordinate.
+ * @wy: World Y coordinate.
+ * @cx: X pixel coordinate (return value).
+ * @cy: Y pixel coordinate (return value).
+ *
+ * Converts world coordinates into canvas pixel coordinates.
+ **/
+void
+foo_canvas_w2c (FooCanvas *canvas, double wx, double wy, int *cx, int *cy)
+{
+ double zoom;
+
+ g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+ zoom = canvas->pixels_per_unit;
+
+ if (cx)
+ *cx = floor ((wx - canvas->scroll_x1)*zoom + canvas->zoom_xofs + 0.5);
+ if (cy)
+ *cy = floor ((wy - canvas->scroll_y1)*zoom + canvas->zoom_yofs + 0.5);
+}
+
+/**
+ * foo_canvas_w2c:
+ * @canvas: A canvas.
+ * @world: rectangle in world coordinates.
+ * @canvas: rectangle in canvase coordinates.
+ *
+ * Converts rectangles in world coordinates into canvas pixel coordinates.
+ **/
+void
+foo_canvas_w2c_rect_d (FooCanvas *canvas,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ foo_canvas_w2c_d (canvas,
+ *x1, *y1,
+ x1, y1);
+ foo_canvas_w2c_d (canvas,
+ *x2, *y2,
+ x2, y2);
+}
+
+
+
+/**
+ * foo_canvas_w2c_d:
+ * @canvas: A canvas.
+ * @wx: World X coordinate.
+ * @wy: World Y coordinate.
+ * @cx: X pixel coordinate (return value).
+ * @cy: Y pixel coordinate (return value).
+ *
+ * Converts world coordinates into canvas pixel coordinates. This version
+ * produces coordinates in floating point coordinates, for greater precision.
+ **/
+void
+foo_canvas_w2c_d (FooCanvas *canvas, double wx, double wy, double *cx, double *cy)
+{
+ double zoom;
+
+ g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+ zoom = canvas->pixels_per_unit;
+
+ if (cx)
+ *cx = (wx - canvas->scroll_x1)*zoom + canvas->zoom_xofs;
+ if (cy)
+ *cy = (wy - canvas->scroll_y1)*zoom + canvas->zoom_yofs;
+}
+
+
+/**
+ * foo_canvas_c2w:
+ * @canvas: A canvas.
+ * @cx: Canvas pixel X coordinate.
+ * @cy: Canvas pixel Y coordinate.
+ * @wx: X world coordinate (return value).
+ * @wy: Y world coordinate (return value).
+ *
+ * Converts canvas pixel coordinates to world coordinates.
+ **/
+void
+foo_canvas_c2w (FooCanvas *canvas, int cx, int cy, double *wx, double *wy)
+{
+ double zoom;
+
+ g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+ zoom = canvas->pixels_per_unit;
+
+ if (wx)
+ *wx = (cx - canvas->zoom_xofs)/zoom + canvas->scroll_x1;
+ if (wy)
+ *wy = (cy - canvas->zoom_yofs)/zoom + canvas->scroll_y1;
+}
+
+
+/**
+ * foo_canvas_window_to_world:
+ * @canvas: A canvas.
+ * @winx: Window-relative X coordinate.
+ * @winy: Window-relative Y coordinate.
+ * @worldx: X world coordinate (return value).
+ * @worldy: Y world coordinate (return value).
+ *
+ * Converts window-relative coordinates into world coordinates. You can use
+ * this when you need to convert mouse coordinates into world coordinates, for
+ * example.
+ * Window coordinates are really the same as canvas coordinates now, but this
+ * function is here for backwards compatibility reasons.
+ **/
+void
+foo_canvas_window_to_world (FooCanvas *canvas, double winx, double winy,
+ double *worldx, double *worldy)
+{
+ g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+ if (worldx)
+ *worldx = canvas->scroll_x1 + ((winx - canvas->zoom_xofs)
+ / canvas->pixels_per_unit);
+
+ if (worldy)
+ *worldy = canvas->scroll_y1 + ((winy - canvas->zoom_yofs)
+ / canvas->pixels_per_unit);
+}
+
+
+/**
+ * foo_canvas_world_to_window:
+ * @canvas: A canvas.
+ * @worldx: World X coordinate.
+ * @worldy: World Y coordinate.
+ * @winx: X window-relative coordinate.
+ * @winy: Y window-relative coordinate.
+ *
+ * Converts world coordinates into window-relative coordinates.
+ * Window coordinates are really the same as canvas coordinates now, but this
+ * function is here for backwards compatibility reasons.
+ **/
+void
+foo_canvas_world_to_window (FooCanvas *canvas, double worldx, double worldy,
+ double *winx, double *winy)
+{
+ g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+ if (winx)
+ *winx = (canvas->pixels_per_unit)*(worldx - canvas->scroll_x1) + canvas->zoom_xofs;
+
+ if (winy)
+ *winy = (canvas->pixels_per_unit)*(worldy - canvas->scroll_y1) + canvas->zoom_yofs;
+}
+
+
+
+/**
+ * foo_canvas_get_color:
+ * @canvas: A canvas.
+ * @spec: X color specification, or NULL for "transparent".
+ * @color: Returns the allocated color.
+ *
+ * Allocates a color based on the specified X color specification. As a
+ * convenience to item implementations, it returns TRUE if the color was
+ * allocated, or FALSE if the specification was NULL. A NULL color
+ * specification is considered as "transparent" by the canvas.
+ *
+ * Return value: TRUE if @spec is non-NULL and the color is allocated. If @spec
+ * is NULL, then returns FALSE.
+ **/
+int
+foo_canvas_get_color (FooCanvas *canvas, const char *spec, GdkColor *color)
+{
+ GdkColormap *colormap;
+
+ g_return_val_if_fail (FOO_IS_CANVAS (canvas), FALSE);
+ g_return_val_if_fail (color != NULL, FALSE);
+
+ if (!spec) {
+ color->pixel = 0;
+ color->red = 0;
+ color->green = 0;
+ color->blue = 0;
+ return FALSE;
+ }
+
+ gdk_color_parse (spec, color);
+
+ colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+
+ gdk_rgb_find_color (colormap, color);
+
+ return TRUE;
+}
+
+/**
+ * foo_canvas_get_color_pixel:
+ * @canvas: A canvas.
+ * @rgba: RGBA color specification.
+ *
+ * Allocates a color from the RGBA value passed into this function. The alpha
+ * opacity value is discarded, since normal X colors do not support it.
+ *
+ * Return value: Allocated pixel value corresponding to the specified color.
+ **/
+gulong
+foo_canvas_get_color_pixel (FooCanvas *canvas, guint rgba)
+{
+ GdkColormap *colormap;
+ GdkColor color;
+
+ g_return_val_if_fail (FOO_IS_CANVAS (canvas), 0);
+
+ color.red = ((rgba & 0xff000000) >> 16) + ((rgba & 0xff000000) >> 24);
+ color.green = ((rgba & 0x00ff0000) >> 8) + ((rgba & 0x00ff0000) >> 16);
+ color.blue = (rgba & 0x0000ff00) + ((rgba & 0x0000ff00) >> 8);
+ color.pixel = 0;
+
+ colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+
+ gdk_rgb_find_color (colormap, &color);
+
+ return color.pixel;
+}
+
+
+/* FIXME: This function is not useful anymore */
+/**
+ * foo_canvas_set_stipple_origin:
+ * @canvas: A canvas.
+ * @gc: GC on which to set the stipple origin.
+ *
+ * Sets the stipple origin of the specified GC as is appropriate for the canvas,
+ * so that it will be aligned with other stipple patterns used by canvas items.
+ * This is typically only needed by item implementations.
+ **/
+void
+foo_canvas_set_stipple_origin (FooCanvas *canvas, GdkGC *gc)
+{
+ g_return_if_fail (FOO_IS_CANVAS (canvas));
+ g_return_if_fail (GDK_IS_GC (gc));
+
+ gdk_gc_set_ts_origin (gc, 0, 0);
+}
+
+static gboolean
+boolean_handled_accumulator (GSignalInvocationHint *ihint,
+ GValue *return_accu,
+ const GValue *handler_return,
+ gpointer dummy)
+{
+ gboolean continue_emission;
+ gboolean signal_handled;
+
+ signal_handled = g_value_get_boolean (handler_return);
+ g_value_set_boolean (return_accu, signal_handled);
+ continue_emission = !signal_handled;
+
+ return continue_emission;
+}
+
+static guint
+foo_canvas_item_accessible_add_focus_handler (AtkComponent *component,
+ AtkFocusHandler handler)
+{
+ GSignalMatchType match_type;
+ guint signal_id;
+
+ match_type = G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC;
+ signal_id = g_signal_lookup ("focus-event", ATK_TYPE_OBJECT);
+
+ if (!g_signal_handler_find (component, match_type, signal_id, 0, NULL,
+ (gpointer) handler, NULL)) {
+ return g_signal_connect_closure_by_id (component,
+ signal_id, 0,
+ g_cclosure_new (
+ G_CALLBACK (handler), NULL,
+ (GClosureNotify) NULL),
+ FALSE);
+ }
+ return 0;
+}
+
+static void
+foo_canvas_item_accessible_get_item_extents (FooCanvasItem *item,
+ GdkRectangle *rect)
+{
+ double bx1, bx2, by1, by2;
+ gint scroll_x, scroll_y;
+ gint x1, x2, y1, y2;
+
+ foo_canvas_item_get_bounds (item, &bx1, &by1, &bx2, &by2);
+ foo_canvas_w2c_rect_d (item->canvas, &bx1, &by1, &bx2, &by2);
+ foo_canvas_get_scroll_offsets (item->canvas, &scroll_x, &scroll_y);
+ x1 = floor (bx1 + .5);
+ y1 = floor (by1 + .5);
+ x2 = floor (bx2 + .5);
+ y2 = floor (by2 + .5);
+ rect->x = x1 - scroll_x;
+ rect->y = y1 - scroll_y;
+ rect->width = x2 - x1;
+ rect->height = y2 - y1;
+}
+
+static gboolean
+foo_canvas_item_accessible_is_item_in_window (FooCanvasItem *item,
+ GdkRectangle *rect)
+{
+ GtkWidget *widget;
+ gboolean retval;
+
+ widget = GTK_WIDGET (item->canvas);
+ if (widget->window) {
+ int window_width, window_height;
+
+ gdk_window_get_geometry (widget->window, NULL, NULL,
+ &window_width, &window_height, NULL);
+ /*
+ * Check whether rectangles intersect
+ */
+ if (rect->x + rect->width < 0 ||
+ rect->y + rect->height < 0 ||
+ rect->x > window_width ||
+ rect->y > window_height) {
+ retval = FALSE;
+ } else {
+ retval = TRUE;
+ }
+ } else {
+ retval = FALSE;
+ }
+ return retval;
+}
+
+
+static void
+foo_canvas_item_accessible_get_extents (AtkComponent *component,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height,
+ AtkCoordType coord_type)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GObject *obj;
+ FooCanvasItem *item;
+ gint window_x, window_y;
+ gint toplevel_x, toplevel_y;
+ GdkRectangle rect;
+ GdkWindow *window;
+ GtkWidget *canvas;
+
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
+ obj = atk_gobject_accessible_get_object (atk_gobj);
+
+ if (obj == NULL) {
+ /* item is defunct */
+ return;
+ }
+
+ /* Get the CanvasItem */
+ item = FOO_CANVAS_ITEM (obj);
+
+ /* If this item has no parent canvas, something's broken */
+ g_return_if_fail (GTK_IS_WIDGET (item->canvas));
+
+ foo_canvas_item_accessible_get_item_extents (item, &rect);
+ *width = rect.width;
+ *height = rect.height;
+ if (!foo_canvas_item_accessible_is_item_in_window (item, &rect)) {
+ *x = G_MININT;
+ *y = G_MININT;
+ return;
+ }
+
+ canvas = GTK_WIDGET (item->canvas);
+ window = gtk_widget_get_parent_window (canvas);
+ gdk_window_get_origin (window, &window_x, &window_y);
+ *x = rect.x + window_x;
+ *y = rect.y + window_y;
+ if (coord_type == ATK_XY_WINDOW) {
+ window = gdk_window_get_toplevel (canvas->window);
+ gdk_window_get_origin (window, &toplevel_x, &toplevel_y);
+ *x -= toplevel_x;
+ *y -= toplevel_y;
+ }
+ return;
+}
+
+static gint
+foo_canvas_item_accessible_get_mdi_zorder (AtkComponent *component)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GObject *g_obj;
+ FooCanvasItem *item;
+
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
+ g_obj = atk_gobject_accessible_get_object (atk_gobj);
+ if (g_obj == NULL) {
+ /* Object is defunct */
+ return -1;
+ }
+
+ item = FOO_CANVAS_ITEM (g_obj);
+ if (item->parent) {
+ return g_list_index (FOO_CANVAS_GROUP (item->parent)->item_list, item);
+ } else {
+ g_return_val_if_fail (item->canvas->root == item, -1);
+ return 0;
+ }
+}
+
+static gboolean
+foo_canvas_item_accessible_grab_focus (AtkComponent *component)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GObject *obj;
+ FooCanvasItem *item;
+ GtkWidget *toplevel;
+
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
+ obj = atk_gobject_accessible_get_object (atk_gobj);
+
+ item = FOO_CANVAS_ITEM (obj);
+ if (item == NULL) {
+ /* item is defunct */
+ return FALSE;
+ }
+
+ foo_canvas_item_grab_focus (item);
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (item->canvas));
+ if (gtk_widget_is_toplevel (toplevel)) {
+ gtk_window_present (GTK_WINDOW (toplevel));
+ }
+
+ return TRUE;
+}
+
+static void
+foo_canvas_item_accessible_remove_focus_handler (AtkComponent *component,
+ guint handler_id)
+{
+ g_signal_handler_disconnect (component, handler_id);
+}
+
+static void
+foo_canvas_item_accessible_component_interface_init (AtkComponentIface *iface)
+{
+ g_return_if_fail (iface != NULL);
+
+ iface->add_focus_handler = foo_canvas_item_accessible_add_focus_handler;
+ iface->get_extents = foo_canvas_item_accessible_get_extents;
+ iface->get_mdi_zorder = foo_canvas_item_accessible_get_mdi_zorder;
+ iface->grab_focus = foo_canvas_item_accessible_grab_focus;
+ iface->remove_focus_handler = foo_canvas_item_accessible_remove_focus_handler;
+}
+
+static gboolean
+foo_canvas_item_accessible_is_item_on_screen (FooCanvasItem *item)
+{
+ GdkRectangle rect;
+
+ foo_canvas_item_accessible_get_item_extents (item, &rect);
+ return foo_canvas_item_accessible_is_item_in_window (item, &rect);
+}
+
+static void
+foo_canvas_item_accessible_initialize (AtkObject *obj, gpointer data)
+{
+ if (ATK_OBJECT_CLASS (accessible_item_parent_class)->initialize != NULL)
+ ATK_OBJECT_CLASS (accessible_item_parent_class)->initialize (obj, data);
+ g_object_set_data (G_OBJECT (obj), "atk-component-layer",
+ GINT_TO_POINTER (ATK_LAYER_MDI));
+}
+
+static AtkStateSet*
+foo_canvas_item_accessible_ref_state_set (AtkObject *accessible)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GObject *obj;
+ FooCanvasItem *item;
+ AtkStateSet *state_set;
+
+ state_set = ATK_OBJECT_CLASS (accessible_item_parent_class)->ref_state_set (accessible);
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (accessible);
+ obj = atk_gobject_accessible_get_object (atk_gobj);
+
+ item = FOO_CANVAS_ITEM (obj);
+ if (item == NULL) {
+ atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
+ } else {
+ if (item->object.flags & FOO_CANVAS_ITEM_VISIBLE) {
+ atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
+
+ if (foo_canvas_item_accessible_is_item_on_screen (item)) {
+ atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
+ }
+ }
+ if (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas))) {
+ atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
+
+ if (item->canvas->focused_item == item) {
+ atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
+ }
+ }
+ }
+
+ return state_set;
+}
+
+static void
+foo_canvas_item_accessible_class_init (AtkObjectClass *klass)
+{
+ accessible_item_parent_class = g_type_class_peek_parent (klass);
+
+ klass->initialize = foo_canvas_item_accessible_initialize;
+ klass->ref_state_set = foo_canvas_item_accessible_ref_state_set;
+}
+
+static GType
+foo_canvas_item_accessible_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ static const GInterfaceInfo atk_component_info = {
+ (GInterfaceInitFunc) foo_canvas_item_accessible_component_interface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL
+ };
+ AtkObjectFactory *factory;
+ GType parent_atk_type;
+ GTypeQuery query;
+ GTypeInfo tinfo = { 0 };
+
+ factory = atk_registry_get_factory (atk_get_default_registry(),
+ GTK_TYPE_OBJECT);
+ if (!factory) {
+ return G_TYPE_INVALID;
+ }
+ parent_atk_type = atk_object_factory_get_accessible_type (factory);
+ if (!parent_atk_type) {
+ return G_TYPE_INVALID;
+ }
+ g_type_query (parent_atk_type, &query);
+ tinfo.class_init = (GClassInitFunc) foo_canvas_item_accessible_class_init;
+ tinfo.class_size = query.class_size;
+ tinfo.instance_size = query.instance_size;
+ type = g_type_register_static (parent_atk_type,
+ "FooCanvasItemAccessibility",
+ &tinfo, 0);
+
+ g_type_add_interface_static (type, ATK_TYPE_COMPONENT,
+ &atk_component_info);
+
+ }
+
+ return type;
+}
+
+static AtkObject *
+foo_canvas_item_accessible_create (GObject *for_object)
+{
+ GType type;
+ AtkObject *accessible;
+ FooCanvasItem *item;
+
+ item = FOO_CANVAS_ITEM (for_object);
+ g_return_val_if_fail (item != NULL, NULL);
+
+ type = foo_canvas_item_accessible_get_type ();
+ if (type == G_TYPE_INVALID) {
+ return atk_no_op_object_new (for_object);
+ }
+
+ accessible = g_object_new (type, NULL);
+ atk_object_initialize (accessible, for_object);
+ return accessible;
+}
+
+static GType
+foo_canvas_item_accessible_factory_get_accessible_type (void)
+{
+ return foo_canvas_item_accessible_get_type ();
+}
+
+static AtkObject*
+foo_canvas_item_accessible_factory_create_accessible (GObject *obj)
+{
+ AtkObject *accessible;
+
+ g_return_val_if_fail (G_IS_OBJECT (obj), NULL);
+
+ accessible = foo_canvas_item_accessible_create (obj);
+
+ return accessible;
+}
+
+static void
+foo_canvas_item_accessible_factory_class_init (AtkObjectFactoryClass *klass)
+{
+ klass->create_accessible = foo_canvas_item_accessible_factory_create_accessible;
+ klass->get_accessible_type = foo_canvas_item_accessible_factory_get_accessible_type;
+}
+
+static GType
+foo_canvas_item_accessible_factory_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ static const GTypeInfo tinfo = {
+ sizeof (AtkObjectFactoryClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) foo_canvas_item_accessible_factory_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (AtkObjectFactory),
+ 0, /* n_preallocs */
+ NULL
+ };
+ type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY,
+ "FooCanvasItemAccessibilityFactory",
+ &tinfo, 0);
+ }
+
+ return type;
+}
+
+/* Class initialization function for FooCanvasItemClass */
+static void
+foo_canvas_item_class_init (FooCanvasItemClass *klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ item_parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->set_property = foo_canvas_item_set_property;
+ gobject_class->get_property = foo_canvas_item_get_property;
+ gobject_class->dispose = foo_canvas_item_dispose;
+
+ g_object_class_install_property
+ (gobject_class, ITEM_PROP_PARENT,
+ g_param_spec_object ("parent", NULL, NULL,
+ FOO_TYPE_CANVAS_ITEM,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (gobject_class, ITEM_PROP_VISIBLE,
+ g_param_spec_boolean ("visible", NULL, NULL,
+ TRUE,
+ G_PARAM_READWRITE));
+
+ item_signals[ITEM_EVENT] =
+ g_signal_new ("event",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FooCanvasItemClass, event),
+ boolean_handled_accumulator, NULL,
+ foo_canvas_marshal_BOOLEAN__BOXED,
+ G_TYPE_BOOLEAN, 1,
+ GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+ klass->realize = foo_canvas_item_realize;
+ klass->unrealize = foo_canvas_item_unrealize;
+ klass->map = foo_canvas_item_map;
+ klass->unmap = foo_canvas_item_unmap;
+ klass->update = foo_canvas_item_update;
+
+ atk_registry_set_factory_type (atk_get_default_registry (),
+ FOO_TYPE_CANVAS_ITEM,
+ foo_canvas_item_accessible_factory_get_type ());
+}
diff --git a/libfoocanvas/foo-canvas.h b/libfoocanvas/foo-canvas.h
new file mode 100644
index 0000000..401ba63
--- /dev/null
+++ b/libfoocanvas/foo-canvas.h
@@ -0,0 +1,530 @@
+/* -*- 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 Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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@
+ */
+/* FooCanvas widget - Tk-like canvas widget for Gnome
+ *
+ * FooCanvas 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.
+ *
+ *
+ * Authors: Federico Mena <federico nuclecu unam mx>
+ * Raph Levien <raph gimp org>
+ */
+
+#ifndef FOO_CANVAS_H
+#define FOO_CANVAS_H
+
+#include <gtk/gtklayout.h>
+#include <gdk/gdkevents.h>
+#include <stdarg.h>
+
+G_BEGIN_DECLS
+
+
+/* "Small" value used by canvas stuff */
+#define FOO_CANVAS_EPSILON 1e-10
+
+
+/* Macros for building colors that fit in a 32-bit integer. The values are in
+ * [0, 255].
+ */
+
+#define FOO_CANVAS_COLOR(r, g, b) ((((int) (r) & 0xff) << 24) \
+ | (((int) (g) & 0xff) << 16) \
+ | (((int) (b) & 0xff) << 8) \
+ | 0xff)
+
+#define FOO_CANVAS_COLOR_A(r, g, b, a) ((((int) (r) & 0xff) << 24) \
+ | (((int) (g) & 0xff) << 16) \
+ | (((int) (b) & 0xff) << 8) \
+ | ((int) (a) & 0xff))
+
+
+typedef struct _FooCanvas FooCanvas;
+typedef struct _FooCanvasClass FooCanvasClass;
+typedef struct _FooCanvasItem FooCanvasItem;
+typedef struct _FooCanvasItemClass FooCanvasItemClass;
+typedef struct _FooCanvasGroup FooCanvasGroup;
+typedef struct _FooCanvasGroupClass FooCanvasGroupClass;
+
+
+/* FooCanvasItem - base item class for canvas items
+ *
+ * All canvas items are derived from FooCanvasItem. The only information a
+ * FooCanvasItem contains is its parent canvas, its parent canvas item group,
+ * and its bounding box in world coordinates.
+ *
+ * Items inside a canvas are organized in a tree of FooCanvasItemGroup nodes
+ * and FooCanvasItem leaves. Each canvas has a single root group, which can
+ * be obtained with the foo_canvas_get_root() function.
+ *
+ * The abstract FooCanvasItem class does not have any configurable or
+ * queryable attributes.
+ */
+
+/* Object flags for items */
+enum {
+ FOO_CANVAS_ITEM_REALIZED = 1 << 4,
+ FOO_CANVAS_ITEM_MAPPED = 1 << 5,
+ FOO_CANVAS_ITEM_ALWAYS_REDRAW = 1 << 6,
+ FOO_CANVAS_ITEM_VISIBLE = 1 << 7,
+ FOO_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
+ FOO_CANVAS_ITEM_NEED_DEEP_UPDATE = 1 << 9
+};
+
+/* Update flags for items */
+enum {
+ FOO_CANVAS_UPDATE_REQUESTED = 1 << 0,
+ FOO_CANVAS_UPDATE_DEEP = 1 << 1
+};
+
+#define FOO_TYPE_CANVAS_ITEM (foo_canvas_item_get_type ())
+#define FOO_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_ITEM, FooCanvasItem))
+#define FOO_CANVAS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_ITEM, FooCanvasItemClass))
+#define FOO_IS_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_ITEM))
+#define FOO_IS_CANVAS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_ITEM))
+#define FOO_CANVAS_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_ITEM, FooCanvasItemClass))
+
+
+struct _FooCanvasItem {
+ GtkObject object;
+
+ /* Parent canvas for this item */
+ FooCanvas *canvas;
+
+ /* Parent canvas group for this item (a FooCanvasGroup) */
+ FooCanvasItem *parent;
+
+ /* Bounding box for this item (in canvas coordinates) */
+ double x1, y1, x2, y2;
+};
+
+struct _FooCanvasItemClass {
+ GtkObjectClass parent_class;
+
+ /* Tell the item to update itself. The flags are from the update flags
+ * defined above. The item should update its internal state from its
+ * queued state, and recompute and request its repaint area. The
+ * update method also recomputes the bounding box of the item.
+ */
+ void (* update) (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags);
+
+ /* Realize an item -- create GCs, etc. */
+ void (* realize) (FooCanvasItem *item);
+
+ /* Unrealize an item */
+ void (* unrealize) (FooCanvasItem *item);
+
+ /* Map an item - normally only need by items with their own GdkWindows */
+ void (* map) (FooCanvasItem *item);
+
+ /* Unmap an item */
+ void (* unmap) (FooCanvasItem *item);
+
+ /* Draw an item of this type. (x, y) are the upper-left canvas pixel
+ * coordinates of the drawable, a temporary pixmap, where things get
+ * drawn. (width, height) are the dimensions of the drawable.
+ */
+ void (* draw) (FooCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose);
+
+ /* Calculate the distance from an item to the specified point. It also
+ * returns a canvas item which is the item itself in the case of the
+ * object being an actual leaf item, or a child in case of the object
+ * being a canvas group. (cx, cy) are the canvas pixel coordinates that
+ * correspond to the item-relative coordinates (x, y).
+ */
+ double (* point) (FooCanvasItem *item, double x, double y, int cx, int cy,
+ FooCanvasItem **actual_item);
+
+ void (* translate) (FooCanvasItem *item, double dx, double dy);
+
+ /* Fetch the item's bounding box (need not be exactly tight). This
+ * should be in item-relative coordinates.
+ */
+ void (* bounds) (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+ /* Signal: an event ocurred for an item of this type. The (x, y)
+ * coordinates are in the canvas world coordinate system.
+ */
+ gboolean (* event) (FooCanvasItem *item, GdkEvent *event);
+
+ /* Reserved for future expansion */
+ gpointer spare_vmethods [4];
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_item_get_type (void) G_GNUC_CONST;
+
+/* Create a canvas item using the standard Gtk argument mechanism. The item is
+ * automatically inserted at the top of the specified canvas group. The last
+ * argument must be a NULL pointer.
+ */
+FooCanvasItem *foo_canvas_item_new (FooCanvasGroup *parent, GType type,
+ const gchar *first_arg_name, ...);
+
+/* Constructors for use in derived classes and language wrappers */
+void foo_canvas_item_construct (FooCanvasItem *item, FooCanvasGroup *parent,
+ const gchar *first_arg_name, va_list args);
+
+/* Configure an item using the standard Gtk argument mechanism. The last
+ * argument must be a NULL pointer.
+ */
+void foo_canvas_item_set (FooCanvasItem *item, const gchar *first_arg_name, ...);
+
+/* Used only for language wrappers and the like */
+void foo_canvas_item_set_valist (FooCanvasItem *item,
+ const gchar *first_arg_name, va_list args);
+
+/* Move an item by the specified amount */
+void foo_canvas_item_move (FooCanvasItem *item, double dx, double dy);
+
+/* Raise an item in the z-order of its parent group by the specified number of
+ * positions.
+ */
+void foo_canvas_item_raise (FooCanvasItem *item, int positions);
+
+/* Lower an item in the z-order of its parent group by the specified number of
+ * positions.
+ */
+void foo_canvas_item_lower (FooCanvasItem *item, int positions);
+
+/* Raise an item to the top of its parent group's z-order. */
+void foo_canvas_item_raise_to_top (FooCanvasItem *item);
+
+/* Lower an item to the bottom of its parent group's z-order */
+void foo_canvas_item_lower_to_bottom (FooCanvasItem *item);
+
+/* Send an item behind another item */
+void foo_canvas_item_send_behind (FooCanvasItem *item,
+ FooCanvasItem *behind_item);
+
+
+/* Show an item (make it visible). If the item is already shown, it has no
+ * effect.
+ */
+void foo_canvas_item_show (FooCanvasItem *item);
+
+/* Hide an item (make it invisible). If the item is already invisible, it has
+ * no effect.
+ */
+void foo_canvas_item_hide (FooCanvasItem *item);
+
+/* Grab the mouse for the specified item. Only the events in event_mask will be
+ * reported. If cursor is non-NULL, it will be used during the duration of the
+ * grab. Time is a proper X event time parameter. Returns the same values as
+ * XGrabPointer().
+ */
+int foo_canvas_item_grab (FooCanvasItem *item, unsigned int event_mask,
+ GdkCursor *cursor, guint32 etime);
+
+/* Ungrabs the mouse -- the specified item must be the same that was passed to
+ * foo_canvas_item_grab(). Time is a proper X event time parameter.
+ */
+void foo_canvas_item_ungrab (FooCanvasItem *item, guint32 etime);
+
+/* These functions convert from a coordinate system to another. "w" is world
+ * coordinates and "i" is item coordinates.
+ */
+void foo_canvas_item_w2i (FooCanvasItem *item, double *x, double *y);
+void foo_canvas_item_i2w (FooCanvasItem *item, double *x, double *y);
+
+/* Remove the item from its parent group and make the new group its parent. The
+ * item will be put on top of all the items in the new group. The item's
+ * coordinates relative to its new parent to *not* change -- this means that the
+ * item could potentially move on the screen.
+ *
+ * The item and the group must be in the same canvas. An item cannot be
+ * reparented to a group that is the item itself or that is an inferior of the
+ * item.
+ */
+void foo_canvas_item_reparent (FooCanvasItem *item, FooCanvasGroup *new_group);
+
+/* Used to send all of the keystroke events to a specific item as well as
+ * GDK_FOCUS_CHANGE events.
+ */
+void foo_canvas_item_grab_focus (FooCanvasItem *item);
+
+/* Fetch the bounding box of the item. The bounding box may not be exactly
+ * tight, but the canvas items will do the best they can. The returned bounding
+ * box is in the coordinate system of the item's parent.
+ */
+void foo_canvas_item_get_bounds (FooCanvasItem *item,
+ double *x1, double *y1, double *x2, double *y2);
+
+/* Request that the update method eventually get called. This should be used
+ * only by item implementations.
+ */
+void foo_canvas_item_request_update (FooCanvasItem *item);
+
+/* Request a redraw of the bounding box of the canvas item */
+void foo_canvas_item_request_redraw (FooCanvasItem *item);
+
+/* FooCanvasGroup - a group of canvas items
+ *
+ * A group is a node in the hierarchical tree of groups/items inside a canvas.
+ * Groups serve to give a logical structure to the items.
+ *
+ * Consider a circuit editor application that uses the canvas for its schematic
+ * display. Hierarchically, there would be canvas groups that contain all the
+ * components needed for an "adder", for example -- this includes some logic
+ * gates as well as wires. You can move stuff around in a convenient way by
+ * doing a foo_canvas_item_move() of the hierarchical groups -- to move an
+ * adder, simply move the group that represents the adder.
+ *
+ * The following arguments are available:
+ *
+ * name type read/write description
+ * --------------------------------------------------------------------------------
+ * x double RW X coordinate of group's origin
+ * y double RW Y coordinate of group's origin
+ */
+
+
+#define FOO_TYPE_CANVAS_GROUP (foo_canvas_group_get_type ())
+#define FOO_CANVAS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_GROUP, FooCanvasGroup))
+#define FOO_CANVAS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_GROUP, FooCanvasGroupClass))
+#define FOO_IS_CANVAS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_GROUP))
+#define FOO_IS_CANVAS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_GROUP))
+#define FOO_CANVAS_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_GROUP, FooCanvasGroupClass))
+
+
+struct _FooCanvasGroup {
+ FooCanvasItem item;
+
+ double xpos, ypos;
+
+ /* Children of the group */
+ GList *item_list;
+ GList *item_list_end;
+};
+
+struct _FooCanvasGroupClass {
+ FooCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_group_get_type (void) G_GNUC_CONST;
+
+
+/*** FooCanvas ***/
+
+
+#define FOO_TYPE_CANVAS (foo_canvas_get_type ())
+#define FOO_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS, FooCanvas))
+#define FOO_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS, FooCanvasClass))
+#define FOO_IS_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS))
+#define FOO_IS_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS))
+#define FOO_CANVAS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS, FooCanvasClass))
+
+
+struct _FooCanvas {
+ GtkLayout layout;
+
+ /* Root canvas group */
+ FooCanvasItem *root;
+
+ /* The item containing the mouse pointer, or NULL if none */
+ FooCanvasItem *current_item;
+
+ /* Item that is about to become current (used to track deletions and such) */
+ FooCanvasItem *new_current_item;
+
+ /* Item that holds a pointer grab, or NULL if none */
+ FooCanvasItem *grabbed_item;
+
+ /* If non-NULL, the currently focused item */
+ FooCanvasItem *focused_item;
+
+ /* GC for temporary draw pixmap */
+ GdkGC *pixmap_gc;
+
+ /* Event on which selection of current item is based */
+ GdkEvent pick_event;
+
+ /* Scrolling region */
+ double scroll_x1, scroll_y1;
+ double scroll_x2, scroll_y2;
+
+ /* Scaling factor to be used for display */
+ double pixels_per_unit;
+
+ /* Idle handler ID */
+ guint idle_id;
+
+ /* Signal handler ID for destruction of the root item */
+ guint root_destroy_id;
+
+ /* Internal pixel offsets when zoomed out */
+ int zoom_xofs, zoom_yofs;
+
+ /* Last known modifier state, for deferred repick when a button is down */
+ int state;
+
+ /* Event mask specified when grabbing an item */
+ guint grabbed_event_mask;
+
+ /* Tolerance distance for picking items */
+ int close_enough;
+
+ /* Whether the canvas should center the canvas in the middle of
+ * the window if the scroll region is smaller than the window */
+ unsigned int center_scroll_region : 1;
+
+ /* Whether items need update at next idle loop iteration */
+ unsigned int need_update : 1;
+
+ /* Are we in the midst of an update */
+ unsigned int doing_update : 1;
+
+ /* Whether the canvas needs redrawing at the next idle loop iteration */
+ unsigned int need_redraw : 1;
+
+ /* Whether current item will be repicked at next idle loop iteration */
+ unsigned int need_repick : 1;
+
+ /* For use by internal pick_current_item() function */
+ unsigned int left_grabbed_item : 1;
+
+ /* For use by internal pick_current_item() function */
+ unsigned int in_repick : 1;
+};
+
+struct _FooCanvasClass {
+ GtkLayoutClass parent_class;
+
+ /* Draw the background for the area given.
+ */
+ void (* draw_background) (FooCanvas *canvas,
+ int x, int y, int width, int height);
+
+ /* Private Virtual methods for groping the canvas inside bonobo */
+ void (* request_update) (FooCanvas *canvas);
+
+ /* Reserved for future expansion */
+ gpointer spare_vmethods [4];
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_get_type (void) G_GNUC_CONST;
+
+/* Creates a new canvas. You should check that the canvas is created with the
+ * proper visual and colormap. Any visual will do unless you intend to insert
+ * gdk_imlib images into it, in which case you should use the gdk_imlib visual.
+ *
+ * You should call foo_canvas_set_scroll_region() soon after calling this
+ * function to set the desired scrolling limits for the canvas.
+ */
+GtkWidget *foo_canvas_new (void);
+
+/* Returns the root canvas item group of the canvas */
+FooCanvasGroup *foo_canvas_root (FooCanvas *canvas);
+
+/* Sets the limits of the scrolling region, in world coordinates */
+void foo_canvas_set_scroll_region (FooCanvas *canvas,
+ double x1, double y1, double x2, double y2);
+
+/* Gets the limits of the scrolling region, in world coordinates */
+void foo_canvas_get_scroll_region (FooCanvas *canvas,
+ double *x1, double *y1, double *x2, double *y2);
+
+/* Sets the number of pixels that correspond to one unit in world coordinates */
+void foo_canvas_set_pixels_per_unit (FooCanvas *canvas, double n);
+
+/* Wether the canvas centers the scroll region if it is smaller than the window */
+void foo_canvas_set_center_scroll_region (FooCanvas *canvas, gboolean center_scroll_region);
+
+/* Scrolls the canvas to the specified offsets, given in canvas pixel coordinates */
+void foo_canvas_scroll_to (FooCanvas *canvas, int cx, int cy);
+
+/* Returns the scroll offsets of the canvas in canvas pixel coordinates. You
+ * can specify NULL for any of the values, in which case that value will not be
+ * queried.
+ */
+void foo_canvas_get_scroll_offsets (FooCanvas *canvas, int *cx, int *cy);
+
+/* Requests that the canvas be repainted immediately instead of in the idle
+ * loop.
+ */
+void foo_canvas_update_now (FooCanvas *canvas);
+
+/* Returns the item that is at the specified position in world coordinates, or
+ * NULL if no item is there.
+ */
+FooCanvasItem *foo_canvas_get_item_at (FooCanvas *canvas, double x, double y);
+
+/* For use only by item type implementations. Request that the canvas
+ * eventually redraw the specified region, specified in canvas pixel
+ * coordinates. The region contains (x1, y1) but not (x2, y2).
+ */
+void foo_canvas_request_redraw (FooCanvas *canvas, int x1, int y1, int x2, int y2);
+
+/* These functions convert from a coordinate system to another. "w" is world
+ * coordinates, "c" is canvas pixel coordinates (pixel coordinates that are
+ * (0,0) for the upper-left scrolling limit and something else for the
+ * lower-left scrolling limit).
+ */
+void foo_canvas_w2c_rect_d (FooCanvas *canvas,
+ double *x1, double *y1,
+ double *x2, double *y2);
+void foo_canvas_w2c (FooCanvas *canvas, double wx, double wy, int *cx, int *cy);
+void foo_canvas_w2c_d (FooCanvas *canvas, double wx, double wy, double *cx, double *cy);
+void foo_canvas_c2w (FooCanvas *canvas, int cx, int cy, double *wx, double *wy);
+
+/* This function takes in coordinates relative to the GTK_LAYOUT
+ * (canvas)->bin_window and converts them to world coordinates.
+ * These days canvas coordinates and window coordinates are the same, but
+ * these are left for backwards compat reasons.
+ */
+void foo_canvas_window_to_world (FooCanvas *canvas,
+ double winx, double winy, double *worldx, double *worldy);
+
+/* This is the inverse of foo_canvas_window_to_world() */
+void foo_canvas_world_to_window (FooCanvas *canvas,
+ double worldx, double worldy, double *winx, double *winy);
+
+/* Takes a string specification for a color and allocates it into the specified
+ * GdkColor. If the string is null, then it returns FALSE. Otherwise, it
+ * returns TRUE.
+ */
+int foo_canvas_get_color (FooCanvas *canvas, const char *spec, GdkColor *color);
+
+/* Allocates a color from the RGB value passed into this function. */
+gulong foo_canvas_get_color_pixel (FooCanvas *canvas,
+ guint rgba);
+
+
+/* Sets the stipple origin of the specified gc so that it will be aligned with
+ * all the stipples used in the specified canvas. This is intended for use only
+ * by canvas item implementations.
+ */
+void foo_canvas_set_stipple_origin (FooCanvas *canvas, GdkGC *gc);
+
+G_END_DECLS
+
+#endif
diff --git a/libfoocanvas/libfoocanvas.h b/libfoocanvas/libfoocanvas.h
new file mode 100644
index 0000000..adce512
--- /dev/null
+++ b/libfoocanvas/libfoocanvas.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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@
+ */
+
+#ifndef LIBFOOCANVAS_H
+#define LIBFOOCANVAS_H
+
+#include <libfoocanvas/foo-canvas.h>
+#include <libfoocanvas/foo-canvas-line.h>
+#include <libfoocanvas/foo-canvas-text.h>
+#include <libfoocanvas/foo-canvas-polygon.h>
+#include <libfoocanvas/foo-canvas-pixbuf.h>
+#include <libfoocanvas/foo-canvas-widget.h>
+#include <libfoocanvas/foo-canvas-rect-ellipse.h>
+#include <libfoocanvas/foo-canvas-util.h>
+
+G_BEGIN_DECLS
+
+GType foo_canvas_points_get_type (void);
+#define FOO_TYPE_CANVAS_POINTS foo_canvas_points_get_type()
+
+G_END_DECLS
+
+#endif /* LIBFOOCANVAS_H */
diff --git a/libfoocanvas/libfoocanvastypes.c b/libfoocanvas/libfoocanvastypes.c
new file mode 100644
index 0000000..bdf98dc
--- /dev/null
+++ b/libfoocanvas/libfoocanvastypes.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 1999, 2000 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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@
+ */
+
+#include <config.h>
+#include <glib-object.h>
+
+#include <libfoocanvas/libfoocanvas.h>
+
+GType
+foo_canvas_points_get_type (void)
+{
+ static GType type_canvas_points = 0;
+
+ if (!type_canvas_points)
+ type_canvas_points = g_boxed_type_register_static
+ ("FooCanvasPoints",
+ (GBoxedCopyFunc) foo_canvas_points_ref,
+ (GBoxedFreeFunc) foo_canvas_points_unref);
+
+ return type_canvas_points;
+}
diff --git a/plugins/class-inheritance/Makefile.am b/plugins/class-inheritance/Makefile.am
new file mode 100644
index 0000000..09b8eb1
--- /dev/null
+++ b/plugins/class-inheritance/Makefile.am
@@ -0,0 +1,63 @@
+
+if HAVE_GRAPHVIZ
+
+# Plugin Icon file
+cls_inheritance_pixmapsdir = $(anjuta_image_dir)
+cls_inheritance_pixmaps_DATA = \
+ anjuta-class-inheritance-plugin.svg \
+ anjuta-class-inheritance-plugin-48.png
+
+# Plugin description file
+plugin_in_files = anjuta-class-inheritance.plugin.in
+%.plugin: %.plugin.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+
+cls_inheritance_plugindir = $(anjuta_plugin_dir)
+cls_inheritance_plugin_DATA = $(plugin_in_files:.plugin.in=.plugin)
+
+# NOTE :
+# The naming convention is very intentional
+# We are forced to use the prefix 'lib' by automake and libtool
+# There is probably a way to avoid it but it is not worth to effort
+# to find out.
+# The 'anjuta_' prfix is a safety measure to avoid conflicts where the
+# plugin 'libpython.so' needs to link with the real 'libpython.so'
+
+# Include paths
+AM_CPPFLAGS = \
+ $(LIBFOOCANVAS_CFLAGS) \
+ $(LIBANJUTA_CFLAGS) \
+ $(EXTRA_CFLAGS) \
+ $(GRAPHVIZ_CFLAGS) \
+ -DG_LOG_DOMAIN=\"libanjuta-class-inheritance\"
+
+# Where to install the plugin
+plugindir = $(anjuta_plugin_dir)
+
+# The plugin
+plugin_LTLIBRARIES = libanjuta-class-inheritance.la
+
+libanjuta_class_inheritance_la_LDFLAGS = $(ANJUTA_PLUGIN_LDFLAGS)
+
+# Plugin dependencies
+libanjuta_class_inheritance_la_LIBADD = \
+ $(LIBFOOCANVAS_LIBS) \
+ $(LIBANJUTA_LIBS) \
+ $(GRAPHVIZ_LIBS)
+
+# Plugin sources
+libanjuta_class_inheritance_la_SOURCES = \
+ plugin.c \
+ plugin.h \
+ class-inherit.c \
+ class-inherit.h \
+ class-callbacks.c \
+ class-callbacks.h
+
+endif
+
+EXTRA_DIST = \
+ $(plugin_in_files) \
+ $(cls_inheritance_plugin_DATA) \
+ $(cls_inheritance_ui_DATA) \
+ $(cls_inheritance_glade_DATA) \
+ $(cls_inheritance_pixmaps_DATA)
diff --git a/plugins/class-inheritance/anjuta-class-inheritance-plugin-48.png b/plugins/class-inheritance/anjuta-class-inheritance-plugin-48.png
new file mode 100644
index 0000000..04e3608
Binary files /dev/null and b/plugins/class-inheritance/anjuta-class-inheritance-plugin-48.png differ
diff --git a/plugins/class-inheritance/anjuta-class-inheritance-plugin.svg b/plugins/class-inheritance/anjuta-class-inheritance-plugin.svg
new file mode 100644
index 0000000..1f50cc7
--- /dev/null
+++ b/plugins/class-inheritance/anjuta-class-inheritance-plugin.svg
@@ -0,0 +1,311 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ width="48"
+ height="48"
+ version="1.0"
+ sodipodi:docbase="/home/maker/icons/anjuta-new2/plugins"
+ sodipodi:docname="anjuta-class-inheritance.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:export-filename="/home/maker/icons/anjuta-new2/plugins/anjuta-class-inheritance-plugin-48.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <sodipodi:namedview
+ inkscape:cy="12.527183"
+ inkscape:cx="23.616362"
+ inkscape:zoom="1"
+ inkscape:window-height="923"
+ inkscape:window-width="1044"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#ffffff"
+ id="base"
+ width="48px"
+ height="48px"
+ showgrid="true"
+ inkscape:window-x="0"
+ inkscape:window-y="32"
+ inkscape:current-layer="svg2" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs5">
+ <marker
+ style="overflow:visible"
+ id="TriangleOutS"
+ orient="auto"
+ refY="0.0"
+ inkscape:stockid="TriangleOutS"
+ refX="0.0">
+ <path
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ id="path3274"
+ transform="scale(0.2)"
+ d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z " />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="TriangleOutM"
+ orient="auto"
+ refY="0.0"
+ inkscape:stockid="TriangleOutM"
+ refX="0.0">
+ <path
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ id="path3271"
+ transform="scale(0.4)"
+ d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z " />
+ </marker>
+ <marker
+ style="overflow:visible;"
+ id="Arrow2Send"
+ orient="auto"
+ refY="0.0"
+ inkscape:stockid="Arrow2Send"
+ refX="0.0">
+ <path
+ style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+ id="path3203"
+ transform="scale(0.3) rotate(180) translate(-2.3,0)"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="Arrow2Sstart"
+ orient="auto"
+ refY="0.0"
+ inkscape:stockid="Arrow2Sstart"
+ refX="0.0">
+ <path
+ style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round"
+ id="path3200"
+ transform="scale(0.3) translate(-2.3,0)"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " />
+ </marker>
+ <marker
+ style="overflow:visible;"
+ id="Arrow1Send"
+ orient="auto"
+ refY="0.0"
+ inkscape:stockid="Arrow1Send"
+ refX="0.0">
+ <path
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+ id="path3185"
+ transform="scale(0.2) rotate(180) translate(6,0)"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="Arrow1Sstart"
+ orient="auto"
+ refY="0.0"
+ inkscape:stockid="Arrow1Sstart"
+ refX="0.0">
+ <path
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ id="path3182"
+ transform="scale(0.2) translate(6,0)"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="TriangleInS"
+ orient="auto"
+ refY="0.0"
+ inkscape:stockid="TriangleInS"
+ refX="0.0">
+ <path
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ id="path3265"
+ transform="scale(-0.2)"
+ d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z " />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="TriangleInM"
+ orient="auto"
+ refY="0.0"
+ inkscape:stockid="TriangleInM"
+ refX="0.0">
+ <path
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ id="path3262"
+ transform="scale(-0.4)"
+ d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z " />
+ </marker>
+ <marker
+ style="overflow:visible;"
+ id="Arrow2Mend"
+ orient="auto"
+ refY="0.0"
+ inkscape:stockid="Arrow2Mend"
+ refX="0.0">
+ <path
+ style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+ id="path3197"
+ transform="scale(0.6) rotate(180) translate(0,0)"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " />
+ </marker>
+ <linearGradient
+ id="linearGradient3146"
+ inkscape:collect="always">
+ <stop
+ offset="0"
+ style="stop-color:#204a87;stop-opacity:1;"
+ id="stop3148" />
+ <stop
+ offset="1"
+ style="stop-color:#204a87;stop-opacity:0;"
+ id="stop3150" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3136">
+ <stop
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;"
+ id="stop3138" />
+ <stop
+ offset="1"
+ style="stop-color:#eeeeec;stop-opacity:1;"
+ id="stop3140" />
+ </linearGradient>
+ <radialGradient
+ r="22.499998"
+ id="radialGradient3144"
+ fx="58.651649"
+ fy="14.520809"
+ gradientUnits="userSpaceOnUse"
+ inkscape:collect="always"
+ cy="14.520809"
+ cx="58.651649"
+ xlink:href="#linearGradient3136"
+ gradientTransform="matrix(1,0,0,1.2111365,-109,0.9341241)" />
+ <linearGradient
+ id="linearGradient3152"
+ gradientUnits="userSpaceOnUse"
+ inkscape:collect="always"
+ x1="58.446415"
+ y1="5.2448635"
+ xlink:href="#linearGradient3146"
+ gradientTransform="translate(-109,3.9999963)"
+ x2="58.651649"
+ y2="-12.72976" />
+ <linearGradient
+ id="linearGradient6308"
+ gradientUnits="userSpaceOnUse"
+ inkscape:collect="always"
+ x1="15"
+ y1="8.7244282"
+ xlink:href="#linearGradient3136"
+ x2="15"
+ y2="21.010408" />
+ <linearGradient
+ id="linearGradient6316"
+ gradientUnits="userSpaceOnUse"
+ inkscape:collect="always"
+ x1="34.452351"
+ y1="30.287689"
+ xlink:href="#linearGradient3136"
+ gradientTransform="translate(0,2)"
+ x2="34.398602"
+ y2="37.977474" />
+ <linearGradient
+ id="linearGradient6324"
+ gradientUnits="userSpaceOnUse"
+ inkscape:collect="always"
+ x1="33.83363"
+ y1="11.906407"
+ xlink:href="#linearGradient3136"
+ x2="33.868267"
+ y2="18.005203" />
+ <marker
+ style="overflow:visible"
+ id="TriangleOutSu"
+ orient="auto"
+ refY="0.0"
+ inkscape:stockid="TriangleOutSu"
+ refX="0.0">
+ <path
+ style="marker-start:none;stroke:#ad7fa8;stroke-width:1.0pt;fill:#ad7fa8;fill-rule:evenodd"
+ id="path2223"
+ transform="scale(0.2)"
+ d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z " />
+ </marker>
+ <marker
+ style="overflow:visible"
+ id="TriangleOutSb"
+ orient="auto"
+ refY="0.0"
+ inkscape:stockid="TriangleOutSb"
+ refX="0.0">
+ <path
+ style="marker-start:none;stroke:#ef2929;stroke-width:1.0pt;fill:#ef2929;fill-rule:evenodd"
+ id="path2305"
+ transform="scale(0.2)"
+ d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z " />
+ </marker>
+ </defs>
+ <path
+ style="stroke-linejoin:miter;marker-end:url(#TriangleOutSu);stroke-opacity:1;fill-rule:evenodd;marker-start:none;fill-opacity:0.75;stroke-dashoffset:0;stroke:#ad7fa8;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-width:3;fill:none"
+ d="M 14.977505,23.248699 L 14.933311,36.109121 L 23.818783,36.042858"
+ sodipodi:nodetypes="ccc"
+ id="path3379" />
+ <rect
+ style="opacity:1;fill:url(#linearGradient6308);fill-opacity:1.0;fill-rule:nonzero;stroke:#75507b;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect3154"
+ width="16"
+ rx="0.057870258"
+ ry="0.057870261"
+ y="7"
+ x="7"
+ height="16" />
+ <path
+ style="stroke-linejoin:miter;marker-end:url(#TriangleOutSb);stroke-opacity:1;fill-rule:evenodd;marker-start:none;fill-opacity:0.75;stroke-dashoffset:0;stroke:#ef2929;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-width:3;fill:none"
+ id="path3165"
+ sodipodi:nodetypes="cc"
+ d="M 34.949104,19.05688 L 34.949104,25.846793" />
+ <rect
+ style="opacity:1;fill:url(#linearGradient6324);fill-opacity:1.0;fill-rule:nonzero;stroke:#cc0000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect3156"
+ width="12"
+ rx="0.057870254"
+ ry="0.057870261"
+ y="11"
+ x="28.999998"
+ height="8" />
+ <rect
+ style="opacity:1;fill:url(#linearGradient6316);fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:1.99999988;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect3163"
+ width="12"
+ rx="0.057870258"
+ ry="0.057870265"
+ y="31"
+ x="29"
+ height="10" />
+</svg>
diff --git a/plugins/class-inheritance/anjuta-class-inheritance.plugin b/plugins/class-inheritance/anjuta-class-inheritance.plugin
new file mode 100644
index 0000000..30e5fdf
--- /dev/null
+++ b/plugins/class-inheritance/anjuta-class-inheritance.plugin
@@ -0,0 +1,26 @@
+[Anjuta Plugin]
+Location=anjuta-class-inheritance:AnjutaClassInheritance
+Icon=anjuta-class-inheritance-plugin-48.png
+Name=Class Inheritance
+Name[ar]=Ù?راثة اÙ?Ù?ئات
+Name[dz]=��ྱེ�རི�ས��ྱི��ར�ྱུ���ེ��ོ���ི�
+Name[eu]=Klase-herentzia
+Name[gu]=વર�� વારસ�
+Name[hi]=वर�� ��ह�र����स
+Name[ja]=������
+Name[ko]=í?´ë??ì?¤ ì??ì??
+Name[mk]=Ð?аÑ?ледÑ?ваÑ?е на клаÑ?и
+Name[mr]=वर�� �न�व�शि�ता
+Name[ne]=वर�� �त�तराधि�ार�:
+Name[nl]=Klasse-overerving:
+Name[vi]=Kế thừa hạng
+Description=A graph painter for the inheritance of the classes.
+Description[ar]=راسÙ? بÙ?اÙ?Ù? Ù?Ù?راثة اÙ?Ù?ئات.
+Description[dz]=��ྱེ�རི�ས��ུ��ི��ར�ྱུ���ེ��ོ���ི��ོ��ལུ����རིས��ོ�������ི��ི�
+Description[eu]=Klaseen herentzien grafikoak marrazteko tresna.
+Description[ja]=ã?¯ã?©ã?¹ã?®ç¶?æ?¿é?¢ä¿?ã??è¦?è¦?ç??ã?«æ??ç?»ã??ã?¾ã??
+Description[ko]=í?´ë??ì?¤ ì??ì?? ê·¸ë??í?? ì??ì?±ê¸°.
+Description[mk]=Ð?Ñ?Ñ?Ñ?Ñ?Ñ?ваÑ? на гÑ?аÑ?икони за наÑ?ледÑ?ваÑ?еÑ?о на клаÑ?иÑ?е.
+Description[mr]=वर�� प�राप�य �र�ता ��राफ प���र.
+Description[ne]=वर���� �त�तराधि�ार��ा ला�ि ���ा ��राफ प�न��र
+Description[vi]=Má»?t trình vẽ Ä?á»? thá»? hiá»?n thá»? kế thừa các hạng.
diff --git a/plugins/class-inheritance/anjuta-class-inheritance.plugin.in b/plugins/class-inheritance/anjuta-class-inheritance.plugin.in
new file mode 100644
index 0000000..6c0bb6f
--- /dev/null
+++ b/plugins/class-inheritance/anjuta-class-inheritance.plugin.in
@@ -0,0 +1,5 @@
+[Anjuta Plugin]
+Location=anjuta-class-inheritance:AnjutaClassInheritance
+Icon=anjuta-class-inheritance-plugin-48.png
+_Name=Class Inheritance
+_Description=A graph painter for the inheritance of the classes.
diff --git a/plugins/class-inheritance/class-callbacks.c b/plugins/class-inheritance/class-callbacks.c
new file mode 100644
index 0000000..e9ca340
--- /dev/null
+++ b/plugins/class-inheritance/class-callbacks.c
@@ -0,0 +1,448 @@
+/*
+ * Copyright (C) Massimo Cora' 2005 <maxcvs email it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <libanjuta/anjuta-debug.h>
+#include <libanjuta/interfaces/ianjuta-document-manager.h>
+#include <libanjuta/interfaces/ianjuta-symbol-manager.h>
+
+#include "plugin.h"
+#include "class-callbacks.h"
+#include "class-inherit.h"
+
+gint
+on_canvas_event (FooCanvasItem *item, GdkEvent *event, gpointer data)
+{
+ AnjutaClassInheritance *plugin;
+ plugin = ANJUTA_PLUGIN_CLASS_INHERITANCE (data);
+
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS:
+ if (event->button.button == 3)
+ {
+ g_return_val_if_fail (plugin->menu != NULL, FALSE);
+
+ gtk_menu_popup (GTK_MENU (plugin->menu), NULL, NULL, NULL, NULL,
+ event->button.button, event->button.time);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+gint
+on_canvas_event_proxy (FooCanvasItem *item, GdkEvent *event,
+ FooCanvasItem *proxy_item)
+{
+ gint ret;
+
+ g_signal_emit_by_name (G_OBJECT (proxy_item), "event", event, &ret);
+ return ret;
+}
+
+gint
+on_expanded_class_title_event (FooCanvasItem *item, GdkEvent *event,
+ ClsNode *cls_node)
+{
+ FooCanvasItem *text_item;
+ text_item = g_object_get_data (G_OBJECT (item), "__text__");
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS: /* single click */
+ if (event->button.button == 1 &&
+ cls_node->expansion_status != CLS_NODE_COLLAPSED &&
+ cls_node_collapse (cls_node))
+ {
+ cls_inherit_draw(cls_node->plugin);
+ return TRUE;
+ }
+ break;
+
+ case GDK_ENTER_NOTIFY: /* mouse entered in title's area */
+ foo_canvas_item_set (item,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_TITLE_PRELIGHT_BG],
+ NULL);
+ foo_canvas_item_set (text_item,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_TITLE_PRELIGHT_FG],
+ NULL);
+ return TRUE;
+
+ case GDK_LEAVE_NOTIFY: /* mouse exited title's area */
+ foo_canvas_item_set (item,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_TITLE_BG],
+ NULL);
+ foo_canvas_item_set (text_item,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_TITLE_FG],
+ NULL);
+ return TRUE;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+static FooCanvasItem*
+create_class_item_tooltip (ClsNode *cls_node, const gchar *tooltip_text)
+{
+ FooCanvasItem *group, *canvas_item, *text_item;
+ gdouble text_width_value, text_height_value;
+
+ group =
+ foo_canvas_item_new (foo_canvas_root
+ (FOO_CANVAS (cls_node->canvas)),
+ foo_canvas_group_get_type (),
+ NULL);
+
+ text_item =
+ foo_canvas_item_new (FOO_CANVAS_GROUP (group),
+ foo_canvas_text_get_type (),
+ "text", tooltip_text,
+ "justification", GTK_JUSTIFY_LEFT,
+ "anchor", GTK_ANCHOR_CENTER,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_ITEM_FG],
+ NULL);
+
+ g_object_get (text_item, "text_width", &text_width_value,
+ "text_height", &text_height_value, NULL);
+
+ foo_canvas_item_set (text_item,
+ "x", (gdouble) 10 + text_width_value/2,
+ "y", (gdouble) 10 + text_height_value/2,
+ NULL);
+ /* Decoration */
+ FooCanvasPoints *points = foo_canvas_points_new (8);
+ gint i = 0;
+ points->coords[i++] = 0;
+ points->coords[i++] = 0;
+
+ points->coords[i++] = 30;
+ points->coords[i++] = 0;
+
+ points->coords[i++] = 40;
+ points->coords[i++] = -10;
+
+ points->coords[i++] = 50;
+ points->coords[i++] = 0;
+
+ points->coords[i++] = text_width_value + 20;
+ points->coords[i++] = 0;
+
+ points->coords[i++] = text_width_value + 20;
+ points->coords[i++] = text_height_value + 20;
+
+ points->coords[i++] = 0;
+ points->coords[i++] = text_height_value + 20;
+
+ points->coords[i++] = 0;
+ points->coords[i++] = 0;
+
+ /* background */
+ canvas_item =
+ foo_canvas_item_new (FOO_CANVAS_GROUP (group),
+ foo_canvas_polygon_get_type (),
+ "points", points,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_ITEM_BG],
+ NULL);
+ /* border */
+ canvas_item =
+ foo_canvas_item_new (FOO_CANVAS_GROUP (group),
+ foo_canvas_line_get_type (),
+ "points", points,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_ITEM_FG],
+ NULL);
+ /* shadow */
+ canvas_item =
+ foo_canvas_item_new (FOO_CANVAS_GROUP (group),
+ foo_canvas_polygon_get_type (),
+ "points", points,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_TITLE_BG],
+ NULL);
+ foo_canvas_points_unref (points);
+
+ /* Lower shadow */
+ foo_canvas_item_lower (canvas_item, 10);
+
+ /* Offset shadow */
+ foo_canvas_item_move (canvas_item, 5, 5);
+
+ /* Raise text */
+ foo_canvas_item_raise (text_item, 10);
+ return group;
+}
+
+static gboolean
+on_canvas_item_show_tooltip_timeout (ClsNodeItem *node_item)
+{
+ gchar *tooltip;
+ gdouble x, y, x1, x2, y2;
+
+ if (node_item->tooltip)
+ gtk_object_destroy (GTK_OBJECT (node_item->tooltip));
+ node_item->tooltip = NULL;
+
+ if (node_item->args && strlen (node_item->args) > 2)
+ {
+ tooltip = g_strdup_printf (_("Args: %s"), node_item->args);
+
+ node_item->tooltip =
+ create_class_item_tooltip (node_item->cls_node, tooltip);
+ g_free (tooltip);
+
+ g_object_get (node_item->cls_node->canvas_group, "x", &x, "y", &y, NULL);
+ g_object_get (node_item->canvas_node_item, "x1", &x1, "x2", &x2,
+ "y2", &y2, NULL);
+ x = x + x1 + 25;
+ y = y + y2 + 10;
+
+ foo_canvas_item_w2i (node_item->tooltip, &x, &y);
+ foo_canvas_item_move (node_item->tooltip, x, y);
+ node_item->tooltip_timeout = 0;
+ }
+ return FALSE;
+}
+
+gint
+on_expanded_class_item_event (FooCanvasItem *item, GdkEvent *event,
+ gpointer data)
+{
+ AnjutaClassInheritance *plugin;
+ ClsNodeItem *node_item;
+ FooCanvasItem *text_item;
+
+ text_item = g_object_get_data (G_OBJECT (item), "__text__");
+
+ node_item = (ClsNodeItem*)data;
+ plugin = node_item->cls_node->plugin;
+
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS: /* single click */
+ if (event->button.button == 1)
+ {
+ /* Goto uri line */
+ if (node_item->file)
+ {
+ IAnjutaDocumentManager *dm;
+ dm = anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin)->shell,
+ IAnjutaDocumentManager, NULL);
+ if (dm)
+ ianjuta_document_manager_goto_file_line (dm,
+ node_item->file,
+ node_item->line,
+ NULL);
+ }
+ }
+ break;
+
+ case GDK_ENTER_NOTIFY: /* mouse entered in item's area */
+ foo_canvas_item_set (node_item->canvas_node_item,
+ "fill_color_gdk",
+ &node_item->cls_node->plugin->style[STYLE_ITEM_PRELIGHT_BG],
+ NULL);
+ foo_canvas_item_set (text_item,
+ "fill_color_gdk",
+ &node_item->cls_node->plugin->style[STYLE_ITEM_PRELIGHT_FG],
+ NULL);
+ /* Show tooltip */
+ if (!node_item->tooltip)
+ {
+ if (node_item->tooltip_timeout)
+ g_source_remove (node_item->tooltip_timeout);
+ node_item->tooltip_timeout =
+ g_timeout_add (500,
+ (GSourceFunc)on_canvas_item_show_tooltip_timeout,
+ node_item);
+ }
+ return TRUE;
+
+ case GDK_LEAVE_NOTIFY: /* mouse exited item's area */
+ foo_canvas_item_set (node_item->canvas_node_item,
+ "fill_color_gdk",
+ &node_item->cls_node->plugin->style[STYLE_ITEM_BG],
+ NULL);
+ foo_canvas_item_set (text_item,
+ "fill_color_gdk",
+ &node_item->cls_node->plugin->style[STYLE_ITEM_FG],
+ NULL);
+ /* Hide tooltip */
+ if (node_item->tooltip_timeout)
+ g_source_remove (node_item->tooltip_timeout);
+ node_item->tooltip_timeout = 0;
+ if (node_item->tooltip)
+ gtk_object_destroy (GTK_OBJECT (node_item->tooltip));
+ node_item->tooltip = NULL;
+
+ return TRUE;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+gint
+on_expanded_class_more_event (FooCanvasItem *item, GdkEvent *event,
+ ClsNode *cls_node)
+{
+ FooCanvasItem *text_item;
+ text_item = g_object_get_data (G_OBJECT (item), "__text__");
+ switch (event->type)
+ {
+ case GDK_2BUTTON_PRESS: /* double click */
+ break;
+
+ case GDK_BUTTON_PRESS: /* single click */
+ if (event->button.button == 1 &&
+ cls_node->expansion_status == CLS_NODE_SEMI_EXPANDED &&
+ cls_node_expand (cls_node, CLS_NODE_FULL_EXPANDED))
+ {
+ cls_inherit_draw(cls_node->plugin);
+ }
+ break;
+
+ case GDK_ENTER_NOTIFY: /* mouse entered in more's area */
+ foo_canvas_item_set (item,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_TITLE_PRELIGHT_BG],
+ NULL);
+ foo_canvas_item_set (text_item,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_TITLE_PRELIGHT_FG],
+ NULL);
+ return TRUE;
+
+ case GDK_LEAVE_NOTIFY: /* mouse exited item's area */
+ foo_canvas_item_set (item,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_TITLE_BG],
+ NULL);
+ foo_canvas_item_set (text_item,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_TITLE_FG],
+ NULL);
+ return TRUE;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+gint
+on_collapsed_class_event (FooCanvasItem *item, GdkEvent *event, gpointer data)
+{
+ AnjutaClassInheritance *plugin;
+ ClsNode *cls_node;
+ FooCanvasItem *text_item;
+ text_item = g_object_get_data (G_OBJECT (item), "__text__");
+
+ cls_node = (ClsNode*)data;
+ plugin = cls_node->plugin;
+
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS: /* single click */
+ if (event->button.button == 1)
+ {
+ if (cls_node_expand (cls_node, CLS_NODE_SEMI_EXPANDED))
+ {
+ cls_inherit_draw(plugin);
+ }
+ return TRUE;
+ }
+ break;
+
+ case GDK_ENTER_NOTIFY: /* mouse entered in item's area */
+ /* Make the outline wide */
+ foo_canvas_item_set (item,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_ITEM_PRELIGHT_BG],
+ NULL);
+ foo_canvas_item_set (text_item,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_ITEM_PRELIGHT_FG],
+ NULL);
+ return TRUE;
+
+ case GDK_LEAVE_NOTIFY: /* mouse exited item's area */
+ /* Make the outline thin */
+ foo_canvas_item_set (item,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_BG],
+ NULL);
+ foo_canvas_item_set (text_item,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_FG],
+ NULL);
+ return TRUE;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+/*----------------------------------------------------------------------------
+ * callback for the canvas' right-click menu - update button.
+ */
+void
+on_update_menu_item_selected (GtkMenuItem *item,
+ AnjutaClassInheritance *plugin)
+{
+ cls_inherit_update (plugin);
+}
+
+/*----------------------------------------------------------------------------
+ * callback for theme/colors changes
+ */
+void
+on_style_set (GtkWidget *widget, GtkStyle *previous_style,
+ AnjutaClassInheritance *plugin)
+{
+ GtkStyle *style = plugin->canvas->style;
+
+ plugin->style[STYLE_BG] = style->base[GTK_STATE_NORMAL];
+ plugin->style[STYLE_FG] = style->text[GTK_STATE_NORMAL];
+ plugin->style[STYLE_TITLE_FG] = style->fg[GTK_STATE_ACTIVE];
+ plugin->style[STYLE_TITLE_BG] = style->bg[GTK_STATE_ACTIVE];
+ plugin->style[STYLE_TITLE_PRELIGHT_FG] = style->fg[GTK_STATE_PRELIGHT];
+ plugin->style[STYLE_TITLE_PRELIGHT_BG] = style->bg[GTK_STATE_PRELIGHT];
+ plugin->style[STYLE_ITEM_FG] = style->text[GTK_STATE_NORMAL];
+ plugin->style[STYLE_ITEM_BG] = style->base[GTK_STATE_NORMAL];
+ plugin->style[STYLE_ITEM_PRELIGHT_FG] = style->text[GTK_STATE_SELECTED];
+ plugin->style[STYLE_ITEM_PRELIGHT_BG] = style->base[GTK_STATE_SELECTED];
+
+ /* Use text background (normally white) for canvas background */
+ style->bg[GTK_STATE_NORMAL] = plugin->style[STYLE_BG];
+
+ /* FIXME: */
+ /* cls_inherit_update (plugin); */
+}
diff --git a/plugins/class-inheritance/class-callbacks.h b/plugins/class-inheritance/class-callbacks.h
new file mode 100644
index 0000000..5120b40
--- /dev/null
+++ b/plugins/class-inheritance/class-callbacks.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) Massimo Cora' 2005 <maxcvs email it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _CLASS_CALLBACKS_H
+#define _CLASS_CALLBACKS_H
+
+#include "plugin.h"
+#include "class-inherit.h"
+
+gint on_canvas_event (FooCanvasItem *item, GdkEvent *event, gpointer data);
+gint on_canvas_event_proxy (FooCanvasItem *item, GdkEvent *event,
+ FooCanvasItem *proxy_item);
+
+void on_update_menu_item_selected (GtkMenuItem *item,
+ AnjutaClassInheritance *plugin);
+void on_style_set (GtkWidget *widget, GtkStyle *previous_style,
+ AnjutaClassInheritance *plugin);
+
+
+/* Callbacks for expanded class node */
+gint on_expanded_class_title_event (FooCanvasItem *item, GdkEvent *event,
+ ClsNode *cls_node);
+gint on_expanded_class_item_event (FooCanvasItem *item, GdkEvent *event,
+ gpointer data);
+gint on_expanded_class_more_event (FooCanvasItem *item, GdkEvent *event,
+ ClsNode *cls_node);
+
+/* Callback for collapsed class node */
+gint on_collapsed_class_event (FooCanvasItem *item, GdkEvent *event,
+ gpointer data);
+
+#endif /* _CLASS_CALLBACKS_H */
diff --git a/plugins/class-inheritance/class-inherit.c b/plugins/class-inheritance/class-inherit.c
new file mode 100644
index 0000000..7e5ce15
--- /dev/null
+++ b/plugins/class-inheritance/class-inherit.c
@@ -0,0 +1,1174 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * Copyright (C) Massimo Cora' 2005 <maxcvs email it>
+ * 2010 Naba Kumar <naba gnome org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <glib.h>
+#include <graphviz/gvc.h> /* graphviz */
+#include <libanjuta/anjuta-debug.h>
+#include <libanjuta/interfaces/ianjuta-document-manager.h>
+#include <libanjuta/interfaces/ianjuta-symbol-manager.h>
+
+#include "plugin.h"
+#include "class-inherit.h"
+#include "class-callbacks.h"
+
+#define DEFAULT_GRAPH_NAME "Anjuta Graph"
+#define CANVAS_MIN_SIZE 250
+
+/* some macros to access deep graphviz's node structures */
+#define NODE_LOWER_LEFT(node,main_index,rec_index) \
+ (((field_t*)ND_shape_info (node))->fld[main_index]->fld[rec_index]->b.LL)
+
+#define NODE_UPPER_RIGHT(node,main_index,rec_index) \
+ (((field_t*)ND_shape_info (node))->fld[main_index]->fld[rec_index]->b.UR)
+
+#define NODE_NUM_FIELDS(node) \
+ ((field_t*)ND_shape_info (node))->n_flds
+
+#define NODE_NTH_FIELD(node,nth) \
+ ((field_t*)ND_shape_info (node))->fld[nth]
+
+#define NODE_NTH_TEXT(node,main_index,rec_index) \
+ ((field_t*)ND_shape_info (node))->fld[main_index]->fld[rec_index]->lp->text
+
+#define GRAPH_TO_CANVAS_Y(y) (-y)
+
+#define INCH_TO_PIXELS_CONVERSION_FACTOR 72
+#define INCH_TO_PIXELS(inch_size) \
+ INCH_TO_PIXELS_CONVERSION_FACTOR * inch_size
+
+#define NODE_EDGE_ARROW_LENGTH 10
+
+/* TODO: check for symbol_updated event, and check in the nodestatus's hashtable
+for the nodes that are gone. In case remove them.
+*/
+
+typedef enum {
+ CLS_ARROW_DIR_UP,
+ CLS_ARROW_DIR_DOWN,
+ CLS_ARROW_DIR_LEFT,
+ CLS_ARROW_DIR_RIGHT
+} CanvasArrowDir;
+
+typedef struct {
+ gdouble x1, y1, x2, y2;
+} ClsBox;
+
+static FooCanvasItem*
+create_canvas_arrow_item (FooCanvasGroup *canvas_group,
+ CanvasArrowDir direction,
+ const GdkColor *fill_color,
+ /* Bounding box */
+ gint x1, gint y1, gint x2, gint y2)
+{
+ FooCanvasItem *item;
+ const gint offset = 4;
+
+ /* FIXME: Use direction to draw different arrows, now it only is down */
+ /* Arrows */
+ FooCanvasPoints *triangle = foo_canvas_points_new (4);
+ triangle->coords[0] = x1 + offset;
+ triangle->coords[1] = y1 + offset;
+ triangle->coords[2] = x2 - offset;
+ triangle->coords[3] = y1 + offset;
+ triangle->coords[4] = x1 + (x2 - x1)/2;
+ triangle->coords[5] = y2 - offset;
+ triangle->coords[6] = x1 + offset;
+ triangle->coords[7] = y1 + offset;
+
+ item = foo_canvas_item_new (canvas_group,
+ foo_canvas_polygon_get_type (),
+ "points", triangle,
+ "fill_color_gdk",
+ fill_color,
+ NULL);
+ foo_canvas_points_unref (triangle);
+ return item;
+}
+
+static FooCanvasItem*
+create_canvas_line_item (FooCanvasGroup *canvas_group, GdkColor *fill_color,
+ gint x1, gint y1, gint x2, gint y2)
+{
+ FooCanvasItem *item;
+ FooCanvasPoints *points;
+
+ points = foo_canvas_points_new (2);
+ points->coords[0] = x1;
+ points->coords[1] = y1;
+ points->coords[2] = x2;
+ points->coords[3] = y2;
+
+ item =
+ foo_canvas_item_new (canvas_group,
+ foo_canvas_line_get_type(),
+ "points", points,
+ "fill_color_gdk",
+ fill_color,
+ "width_units", 1.0,
+ NULL);
+ foo_canvas_points_unref (points);
+ return item;
+}
+
+static void
+cls_node_item_free (ClsNodeItem *cls_item)
+{
+ g_free (cls_item->label);
+ g_free (cls_item->args);
+ g_free (cls_item->type_name);
+
+ if (cls_item->file)
+ g_object_unref (cls_item->file);
+ if (cls_item->icon)
+ gdk_pixbuf_unref (cls_item->icon);
+
+ if (cls_item->tooltip_timeout)
+ g_source_remove (cls_item->tooltip_timeout);
+
+ if (cls_item->tooltip)
+ gtk_object_destroy (GTK_OBJECT (cls_item->tooltip));
+
+ g_free (cls_item);
+}
+
+static void
+cls_node_edge_free (ClsNodeEdge *cls_edge)
+{
+ /* Delete agedeges here? */
+ gtk_object_destroy (GTK_OBJECT (cls_edge->canvas_line));
+ agdelete (cls_edge->cls_node_from->graph, cls_edge->agedge);
+ g_free (cls_edge);
+}
+
+static void
+cls_node_free (ClsNode *cls_node)
+{
+ g_free (cls_node->sym_name);
+ g_hash_table_destroy (cls_node->edges_to);
+ g_hash_table_destroy (cls_node->edges_from);
+ g_hash_table_destroy (cls_node->members);
+ if (cls_node->canvas_group)
+ gtk_object_destroy (GTK_OBJECT (cls_node->canvas_group));
+ agdelete (cls_node->graph, cls_node->agnode);
+ g_free (cls_node);
+}
+
+static gboolean
+cls_node_add_edge (ClsNode *cls_node_from, ClsNode *cls_node_to)
+{
+ ClsNodeEdge *cls_edge;
+
+ g_return_val_if_fail (cls_node_from->graph != NULL, FALSE);
+
+ /* Check if the edge already exists */
+ cls_edge = g_hash_table_lookup (cls_node_from->edges_to, cls_node_to);
+ if (cls_edge)
+ return TRUE;
+
+ cls_edge = g_new0 (ClsNodeEdge, 1);
+ cls_edge->cls_node_from = cls_node_from;
+ cls_edge->cls_node_to = cls_node_to;
+
+ if ((cls_edge->agedge = agedge (cls_node_from->graph,
+ cls_node_from->agnode,
+ cls_node_to->agnode)) == NULL)
+ {
+ g_free (cls_edge);
+ return FALSE;
+ }
+
+ g_hash_table_insert (cls_node_from->edges_to, cls_node_to, cls_edge);
+ g_hash_table_insert (cls_node_to->edges_from, cls_node_from, cls_edge);
+ return TRUE;
+}
+
+static void
+on_cls_node_unlink_from_foreach (ClsNode *cls_node_from, ClsNodeEdge *cls_edge,
+ ClsNode *cls_node_to)
+{
+ g_hash_table_remove (cls_node_from->edges_to, cls_node_to);
+}
+
+static void
+on_cls_node_unlink_to_foreach (ClsNode *cls_node_to, ClsNodeEdge *cls_edge,
+ ClsNode *cls_node_from)
+{
+ /* This is will also free cls_edge */
+ g_hash_table_remove (cls_node_to->edges_from, cls_node_from);
+}
+
+static void
+cls_node_unlink (ClsNode *cls_node)
+{
+ g_hash_table_foreach (cls_node->edges_from,
+ (GHFunc) on_cls_node_unlink_from_foreach,
+ cls_node);
+ g_hash_table_foreach (cls_node->edges_to,
+ (GHFunc) on_cls_node_unlink_to_foreach,
+ cls_node);
+}
+
+/*----------------------------------------------------------------------------
+ * add a node to an Agraph. Check also if the node is yet in the hash_table so
+ * that we can build the label of the node with the class-data.
+ */
+static ClsNode*
+cls_inherit_create_node (AnjutaClassInheritance *plugin,
+ const IAnjutaSymbol *node_sym)
+{
+ ClsNode *cls_node;
+ Agsym_t *sym;
+ gint font_size;
+ const gchar *font_name;
+
+#define FONT_SIZE_STR_LEN 16
+ gchar font_size_str[FONT_SIZE_STR_LEN];
+
+ cls_node = g_new0 (ClsNode, 1);
+ cls_node->graph = plugin->graph;
+ cls_node->canvas = plugin->canvas;
+ cls_node->plugin = plugin;
+ cls_node->sym_manager =
+ anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin)->shell,
+ IAnjutaSymbolManager, NULL);
+
+ cls_node->sym_name =
+ g_strdup (ianjuta_symbol_get_name (IANJUTA_SYMBOL (node_sym), NULL));
+ cls_node->klass_id =
+ ianjuta_symbol_get_id (IANJUTA_SYMBOL (node_sym), NULL);
+ cls_node->members =
+ g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) cls_node_item_free);
+ cls_node->expansion_status = CLS_NODE_COLLAPSED;
+ cls_node->drawn_expansion_status = CLS_NODE_COLLAPSED;
+ cls_node->edges_to =
+ g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify)cls_node_edge_free);
+ cls_node->edges_from = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ /* let's add the node to the graph */
+ if ((cls_node->agnode = agnode (cls_node->graph,
+ cls_node->sym_name)) == NULL)
+ {
+ cls_node_free (cls_node);
+ return NULL;
+ }
+
+ /* set the font */
+ if (!(sym = agfindattr(plugin->graph->proto->n, "fontname")))
+ sym = agnodeattr(plugin->graph, "fontname", "");
+ font_name =
+ pango_font_description_get_family (plugin->canvas->style->font_desc);
+ agxset(cls_node->agnode, sym->index, (char*)font_name);
+
+ /* set the font-size */
+ if (!(sym = agfindattr(plugin->graph->proto->n, "fontsize")))
+ sym = agnodeattr(plugin->graph, "fontsize", "");
+
+ font_size =
+ pango_font_description_get_size (plugin->canvas->style->font_desc)/
+ PANGO_SCALE;
+
+ /* The above font size in points is with real screen DPI, but graphviz
+ * rendering is done at fixed INCH_TO_PIXELS_CONVERSION_FACTOR dpi. So
+ * convert to the right font size points for graphviz.
+ */
+ font_size =
+ font_size * gdk_screen_get_resolution (gdk_screen_get_default ())
+ / INCH_TO_PIXELS_CONVERSION_FACTOR;
+
+ snprintf (font_size_str, FONT_SIZE_STR_LEN, "%d", font_size);
+ agxset(cls_node->agnode, sym->index, font_size_str);
+
+ if (!(sym = agfindattr(plugin->graph->proto->n, "ratio")))
+ sym = agnodeattr(plugin->graph, "ratio", "");
+ agxset(cls_node->agnode, sym->index, "expand");
+
+ /* Set an attribute - in this case one that affects the visible rendering */
+ if (!(sym = agfindattr(plugin->graph->proto->n, "shape")))
+ sym = agnodeattr(plugin->graph, "shape", "");
+ agxset(cls_node->agnode, sym->index, "box");
+
+ if (!(sym = agfindattr(plugin->graph->proto->n, "label")))
+ sym = agnodeattr(plugin->graph, "label", "");
+ agxset(cls_node->agnode, sym->index, cls_node->sym_name);
+
+ return cls_node;
+}
+
+gboolean
+cls_node_collapse (ClsNode *cls_node)
+{
+ Agsym_t *sym;
+
+ if (cls_node->expansion_status == CLS_NODE_COLLAPSED)
+ return FALSE;
+
+ if (!(sym = agfindattr(cls_node->graph->proto->n, "label")))
+ sym = agnodeattr(cls_node->graph, "label", "");
+ agxset(cls_node->agnode, sym->index, cls_node->sym_name);
+ cls_node->expansion_status = CLS_NODE_COLLAPSED;
+
+ return TRUE;
+}
+
+gboolean
+cls_node_expand (ClsNode *cls_node, ClsNodeExpansionType expansion_type)
+{
+ Agsym_t *sym;
+ GString *label;
+ gint max_label_items = 0;
+ gint real_items_length = 0;
+ gint var_order = -1000;
+ gint method_order = 0;
+ IAnjutaSymbol *node_sym;
+ IAnjutaIterable *iter;
+
+ if (cls_node->expansion_status == expansion_type ||
+ expansion_type == CLS_NODE_COLLAPSED)
+ return FALSE;
+
+ node_sym =
+ ianjuta_symbol_manager_get_symbol_by_id (cls_node->sym_manager,
+ cls_node->klass_id,
+ IANJUTA_SYMBOL_FIELD_SIMPLE,
+ NULL);
+ if (!node_sym)
+ return FALSE;
+
+ if (!(sym = agfindattr(cls_node->graph->proto->n, "shape")))
+ sym = agnodeattr(cls_node->graph, "shape", "");
+ agxset (cls_node->agnode, sym->index, "record");
+
+ if (!(sym = agfindattr(cls_node->graph->proto->n, "label")))
+ sym = agnodeattr(cls_node->graph, "label", "");
+
+ label = g_string_new ("");
+ g_string_printf (label, "{%s", cls_node->sym_name);
+
+ /* get members from the passed symbol node */
+ iter = ianjuta_symbol_manager_get_members (cls_node->sym_manager, node_sym,
+ IANJUTA_SYMBOL_FIELD_SIMPLE|
+ IANJUTA_SYMBOL_FIELD_KIND |
+ IANJUTA_SYMBOL_FIELD_TYPE |
+ IANJUTA_SYMBOL_FIELD_TYPE_NAME |
+ IANJUTA_SYMBOL_FIELD_ACCESS |
+ IANJUTA_SYMBOL_FIELD_FILE_PATH,
+ NULL);
+ real_items_length = ianjuta_iterable_get_length (iter, NULL);
+
+ /* set the max number of items to draw */
+ if (real_items_length <= NODE_HALF_DISPLAY_ELEM_NUM ||
+ expansion_type == CLS_NODE_FULL_EXPANDED)
+ {
+ max_label_items = real_items_length;
+ cls_node->expansion_status = CLS_NODE_FULL_EXPANDED;
+ }
+ else
+ {
+ max_label_items = NODE_HALF_DISPLAY_ELEM_NUM;
+ cls_node->expansion_status = CLS_NODE_SEMI_EXPANDED;
+ }
+
+ g_hash_table_remove_all (cls_node->members);
+ if (iter && real_items_length > 0)
+ {
+ gint i = 0;
+
+ /* First member variables */
+ do
+ {
+ const gchar *name, *args, *type_name;
+ IAnjutaSymbol *symbol;
+ GdkPixbuf *icon;
+
+ symbol = IANJUTA_SYMBOL (iter);
+ name = g_strdup (ianjuta_symbol_get_name (symbol, NULL));
+ args = ianjuta_symbol_get_args (symbol, NULL);
+ icon = (GdkPixbuf*) ianjuta_symbol_get_icon (symbol, NULL);
+
+ if (!args) /* Member variables */
+ {
+ ClsNodeItem *cls_item = g_new0 (ClsNodeItem, 1);
+
+ type_name = ianjuta_symbol_get_extra_info_string (symbol, IANJUTA_SYMBOL_FIELD_TYPE_NAME, NULL);
+ cls_item->cls_node = cls_node;
+ cls_item->label = g_strconcat (name, " : ", type_name, NULL);
+ cls_item->order = var_order++;
+ if (icon)
+ gdk_pixbuf_ref (icon);
+ cls_item->icon = icon;
+
+ g_hash_table_insert (cls_node->members,
+ g_strdup (cls_item->label),
+ cls_item);
+ g_string_append_printf (label, "|%s", cls_item->label);
+
+ /* Setup file and line */
+ cls_item->type_name = g_strdup (type_name);
+ cls_item->line = ianjuta_symbol_get_line (symbol, NULL);
+ cls_item->file = ianjuta_symbol_get_file (symbol, NULL);
+ g_object_ref (cls_item->file);
+ }
+ else /* Member methods */
+ {
+ ClsNodeItem *cls_item;
+ gchar *method_key = g_strconcat (name, args, NULL);
+ cls_item = g_hash_table_lookup (cls_node->members, method_key);
+
+ if (cls_item) /* We found an entry for this method */
+ {
+ IAnjutaSymbolType sym_type
+ = ianjuta_symbol_get_sym_type (symbol, NULL);
+ if (!(sym_type & IANJUTA_SYMBOL_TYPE_PROTOTYPE))
+ {
+ /* This one is method, so take this one instead */
+ g_free (cls_item->args);
+ cls_item->args = g_strdup (args);
+
+ if (cls_item->file) g_object_unref (cls_item->file);
+ cls_item->file = NULL;
+
+ /* Setup file and line */
+ cls_item->line = ianjuta_symbol_get_line (symbol, NULL);
+ cls_item->file = ianjuta_symbol_get_file (symbol, NULL);
+ g_object_ref (cls_item->file);
+ }
+ }
+ else /* We did not find a member entry, create a new one */
+ {
+ ClsNodeItem *cls_item = g_new0 (ClsNodeItem, 1);
+ type_name = ianjuta_symbol_get_returntype (symbol, NULL);
+
+ cls_item->cls_node = cls_node;
+ if (type_name)
+ {
+ if (strlen (args) > 2)
+ cls_item->label = g_strconcat (name, "(...)",
+ " : ", type_name,
+ NULL);
+ else
+ cls_item->label = g_strconcat (name, "()", " : ",
+ type_name, NULL);
+ }
+ else
+ {
+ if (strlen (args) > 2)
+ cls_item->label = g_strconcat (name, "(...)", NULL);
+ else
+ cls_item->label = g_strconcat (name, "()", NULL);
+ }
+ cls_item->args = g_strdup (args);
+ cls_item->type_name = g_strdup (type_name);
+ cls_item->order = method_order++;
+ if (icon)
+ gdk_pixbuf_ref (icon);
+ cls_item->icon = icon;
+
+ g_string_append_printf (label, "|%s", cls_item->label);
+ g_hash_table_insert (cls_node->members, method_key,
+ cls_item);
+
+ /* Setup file and line */
+ cls_item->line = ianjuta_symbol_get_line (symbol, NULL);
+ cls_item->file = ianjuta_symbol_get_file (symbol, NULL);
+ g_object_ref (cls_item->file);
+ }
+ }
+ i++;
+ }
+ while (ianjuta_iterable_next (iter, NULL) && i < max_label_items);
+ }
+ if (iter)
+ g_object_unref (iter);
+
+ if (cls_node->expansion_status == CLS_NODE_SEMI_EXPANDED &&
+ real_items_length > NODE_HALF_DISPLAY_ELEM_NUM)
+ {
+ g_string_append_printf (label, "|%s", NODE_SHOW_ALL_MEMBERS_STR);
+ }
+
+ g_string_append_printf (label, "}");
+ agxset(cls_node->agnode, sym->index, label->str);
+
+ /* set the margin for icons */
+ if (!(sym = agfindattr(cls_node->graph->proto->n, "margin")))
+ sym = agnodeattr(cls_node->graph, "margin", "0.11,0.055");
+ agxset(cls_node->agnode, sym->index, "0.3,0.03");
+
+ g_string_free (label, TRUE);
+
+ return TRUE;
+}
+
+static gint
+on_cls_node_item_compare (ClsNodeItem *a, ClsNodeItem *b)
+{
+ if (a->order > b->order) return 1;
+ if (a->order < b->order) return -1;
+ else return 0;
+}
+
+/*----------------------------------------------------------------------------
+ * Draw an expanded node. Function which simplifies cls_inherit_draw_graph().
+ */
+static void
+cls_node_draw_expanded (ClsNode *cls_node)
+{
+ FooCanvasItem *canvas_item, *text_item;
+ gint item_height, j;
+ GList *members, *member;
+
+ g_return_if_fail (cls_node->sym_manager != NULL);
+ g_return_if_fail (cls_node->expansion_status == CLS_NODE_SEMI_EXPANDED ||
+ cls_node->expansion_status == CLS_NODE_FULL_EXPANDED);
+
+ if (cls_node->canvas_group)
+ gtk_object_destroy (GTK_OBJECT (cls_node->canvas_group));
+ cls_node->canvas_group =
+ foo_canvas_item_new (foo_canvas_root
+ (FOO_CANVAS (cls_node->canvas)),
+ foo_canvas_group_get_type (),
+ NULL);
+ cls_node->drawn_expansion_status = cls_node->expansion_status;
+ members = g_hash_table_get_values (cls_node->members);
+ members = g_list_sort (members, (GCompareFunc)on_cls_node_item_compare);
+
+ if (cls_node->expansion_status == CLS_NODE_SEMI_EXPANDED)
+ item_height = cls_node->height / (g_list_length (members) + 2);
+ else
+ item_height = cls_node->height / (g_list_length (members) + 1);
+
+ /* Class title */
+ canvas_item =
+ foo_canvas_item_new (FOO_CANVAS_GROUP (cls_node->canvas_group),
+ foo_canvas_rect_get_type (),
+ "x1", 0.0,
+ "y1", 0.0,
+ "x2", (gdouble) cls_node->width,
+ "y2", (gdouble) item_height,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_TITLE_BG],
+ NULL);
+
+ g_signal_connect (GTK_OBJECT (canvas_item), "event",
+ G_CALLBACK (on_expanded_class_title_event),
+ cls_node);
+
+ /* Arrow on the right of class title */
+ create_canvas_arrow_item (FOO_CANVAS_GROUP (cls_node->canvas_group),
+ CLS_ARROW_DIR_DOWN,
+ &cls_node->plugin->style[STYLE_TITLE_FG],
+ cls_node->width - item_height,
+ 0, cls_node->width, item_height);
+ /* Class title text */
+ text_item =
+ foo_canvas_item_new (FOO_CANVAS_GROUP (cls_node->canvas_group),
+ foo_canvas_text_get_type (),
+ "text", cls_node->sym_name,
+ "justification", GTK_JUSTIFY_CENTER,
+ "anchor", GTK_ANCHOR_CENTER,
+ "x", (gdouble) 20.0,
+ "y", (gdouble) (j + 0.5) * item_height,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_TITLE_FG],
+ "anchor", GTK_ANCHOR_W,
+ NULL);
+ g_object_set_data (G_OBJECT (canvas_item), "__text__", text_item);
+ g_signal_connect (GTK_OBJECT (text_item), "event",
+ G_CALLBACK (on_canvas_event_proxy),
+ canvas_item);
+
+ /* Member items */
+ j = 1;
+ for (member = members; member; member = member->next)
+ {
+ ClsNodeItem *node_item = (ClsNodeItem*) member->data;
+
+ /* Member item background */
+ node_item->canvas_node_item =
+ foo_canvas_item_new (FOO_CANVAS_GROUP (cls_node->canvas_group),
+ foo_canvas_rect_get_type (),
+ "x1", 0.0,
+ "y1", (gdouble) j * item_height,
+ "x2", (gdouble) cls_node->width,
+ "y2", (gdouble) (j + 1) * item_height,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_ITEM_BG],
+ NULL);
+ g_signal_connect (GTK_OBJECT (node_item->canvas_node_item),
+ "event",
+ G_CALLBACK (on_expanded_class_item_event),
+ node_item);
+
+ /* Member item text */
+ text_item =
+ foo_canvas_item_new (FOO_CANVAS_GROUP (cls_node->canvas_group),
+ foo_canvas_text_get_type (),
+ "text", node_item->label,
+ "justification", GTK_JUSTIFY_CENTER,
+ "anchor", GTK_ANCHOR_CENTER,
+ "x", (gdouble) 20.0,
+ "y", (gdouble) (j + 0.5) * item_height,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_ITEM_FG],
+ "anchor", GTK_ANCHOR_W,
+ NULL);
+ g_object_set_data (G_OBJECT (node_item->canvas_node_item),
+ "__text__", text_item);
+ g_signal_connect (GTK_OBJECT (text_item), "event",
+ G_CALLBACK (on_canvas_event_proxy),
+ node_item->canvas_node_item);
+
+ /* Member item icon */
+ if (node_item->icon)
+ foo_canvas_item_new (FOO_CANVAS_GROUP (cls_node->canvas_group),
+ foo_canvas_pixbuf_get_type(),
+ "x", 2.0,
+ "y",
+ (gdouble) (j + 0.5) * item_height - 8,
+ "pixbuf", node_item->icon,
+ NULL);
+ if (node_item->order == 0 || j == 1) /* Variables and methods border */
+ create_canvas_line_item (FOO_CANVAS_GROUP (cls_node->canvas_group),
+ &cls_node->plugin->style[STYLE_FG],
+ 0, j * item_height,
+ cls_node->width, j * item_height);
+ j++;
+ }
+ g_list_free (members);
+
+ /* The last item for semi expanded item is "more" item */
+ if (cls_node->expansion_status == CLS_NODE_SEMI_EXPANDED)
+ {
+ /* More expand item background */
+ canvas_item =
+ foo_canvas_item_new (FOO_CANVAS_GROUP (cls_node->canvas_group),
+ foo_canvas_rect_get_type (),
+ "x1", 0.0,
+ "y1", (gdouble) j * item_height,
+ "x2", (gdouble) cls_node->width,
+ "y2", (gdouble) (j + 1) * item_height,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_TITLE_BG],
+ NULL);
+
+ g_signal_connect (GTK_OBJECT (canvas_item), "event",
+ G_CALLBACK (on_expanded_class_more_event),
+ cls_node);
+
+ /* More expand item text */
+ text_item =
+ foo_canvas_item_new (FOO_CANVAS_GROUP (cls_node->canvas_group),
+ foo_canvas_text_get_type (),
+ "text", NODE_SHOW_ALL_MEMBERS_STR,
+ "justification", GTK_JUSTIFY_CENTER,
+ "anchor", GTK_ANCHOR_CENTER,
+ "x", (gdouble) 20.0,
+ "y", (gdouble) (j + 0.5) * item_height,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_TITLE_FG],
+ "anchor", GTK_ANCHOR_W,
+ NULL);
+ g_object_set_data (G_OBJECT (canvas_item), "__text__", text_item);
+ g_signal_connect (GTK_OBJECT (text_item), "event",
+ G_CALLBACK (on_canvas_event_proxy),
+ canvas_item);
+
+ create_canvas_line_item (FOO_CANVAS_GROUP (cls_node->canvas_group),
+ &cls_node->plugin->style[STYLE_FG],
+ 0, j * item_height,
+ cls_node->width, j * item_height);
+ }
+
+ /* make the outline bounds */
+ foo_canvas_item_new (FOO_CANVAS_GROUP (cls_node->canvas_group),
+ foo_canvas_rect_get_type (),
+ "x1", (gdouble) 0.0,
+ "y1", (gdouble) 0.0,
+ "x2", (gdouble) cls_node->width,
+ "y2", (gdouble) cls_node->height,
+ "outline_color_gdk",
+ &cls_node->plugin->style[STYLE_FG],
+ "width_units", 1.0,
+ NULL);
+}
+
+static void
+cls_node_draw_collapsed (ClsNode *cls_node)
+{
+ FooCanvasItem *item, *text_item;
+ gdouble text_width_value;
+
+ g_return_if_fail (cls_node->agnode != NULL);
+ g_return_if_fail (cls_node->canvas);
+ g_return_if_fail (cls_node->expansion_status == CLS_NODE_COLLAPSED);
+
+ /* Clean up members */
+ g_hash_table_remove_all (cls_node->members);
+
+ /* Switch to collapsed canvas item */
+ if (cls_node->canvas_group)
+ gtk_object_destroy (GTK_OBJECT (cls_node->canvas_group));
+ cls_node->canvas_group =
+ foo_canvas_item_new (foo_canvas_root
+ (FOO_CANVAS (cls_node->canvas)),
+ foo_canvas_group_get_type (),
+ NULL);
+ cls_node->drawn_expansion_status = CLS_NODE_COLLAPSED;
+
+ item =
+ foo_canvas_item_new (FOO_CANVAS_GROUP (cls_node->canvas_group),
+ foo_canvas_rect_get_type (),
+ "x1", (gdouble) 0.0,
+ "y1", (gdouble) 0.0,
+ "x2", (gdouble) cls_node->width,
+ "y2", (gdouble) cls_node->height,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_BG],
+ "outline_color_gdk",
+ &cls_node->plugin->style[STYLE_FG],
+ "width_units", 1.0,
+ NULL);
+ g_signal_connect (GTK_OBJECT (item), "event",
+ G_CALLBACK (on_collapsed_class_event),
+ cls_node);
+
+ /* --- text --- */
+ text_item =
+ foo_canvas_item_new (FOO_CANVAS_GROUP (cls_node->canvas_group),
+ foo_canvas_text_get_type (),
+ "text", cls_node->sym_name,
+ "justification", GTK_JUSTIFY_CENTER,
+ "anchor", GTK_ANCHOR_CENTER,
+ "x", (gdouble) 0.0,
+ "y", (gdouble) cls_node->height/2,
+ "fill_color_gdk",
+ &cls_node->plugin->style[STYLE_FG],
+ "anchor", GTK_ANCHOR_W,
+ NULL );
+ g_object_set_data (G_OBJECT (item), "__text__", text_item);
+ g_signal_connect (GTK_OBJECT (text_item), "event",
+ G_CALLBACK (on_canvas_event_proxy), item);
+
+ /* center the text in the node... */
+ g_object_get (text_item, "text_width", &text_width_value, NULL);
+
+ foo_canvas_item_set (text_item,
+ "x", (gdouble)((cls_node->width/2 - text_width_value/2)),
+ NULL);
+}
+
+/* This function determins NODE_EDGE_ARROW_LENGTH long arrow destination
+ * as a projection of (x1, y1) - (x2, y2) line.
+ */
+static gboolean
+create_canvas_edge_arrow_ending (double x1, double y1, double x2, double y2,
+ double *end_x, double *end_y)
+{
+ double x = x2 - x1;
+ double y = y2 - y1;
+ double h1 = sqrt(y * y + x * x);
+
+ if (h1 == 0) /* Overlapping points, no direction! */
+ return FALSE;
+
+ *end_x = x2 + NODE_EDGE_ARROW_LENGTH * x / h1;
+ *end_y = y2 + NODE_EDGE_ARROW_LENGTH * y / h1;
+ return TRUE;
+}
+
+static void
+cls_node_draw_edge (ClsNode *cls_node_to, ClsNodeEdge *cls_edge, ClsNode *cls_node_from)
+{
+ Agedge_t *edge;
+ FooCanvasPoints *points;
+ gint i, num_points;
+
+ edge = cls_edge->agedge;
+ num_points = ED_spl(edge)->list->size;
+ points = foo_canvas_points_new(num_points + 1);
+
+ for (i = 0; i < num_points; i++)
+ {
+ points->coords[2 * i] = ((ED_spl(edge))->list->list[i]).x;
+ points->coords[2 * i + 1] = GRAPH_TO_CANVAS_Y (((ED_spl(edge))->list->list[i]).y);
+ }
+
+ /* Draw arrow destination point */
+
+ /* Arrow end position */
+ /* Sometimes, last 2 points overlap, resulting in failure to determine
+ * next point, track back until there is a usable pair of points.
+ */
+ do
+ {
+ num_points--;
+
+ /* starting point */
+ points->coords[2 * i] = ((ED_spl(edge))->list->list[num_points]).x;
+ points->coords[2 * i + 1] =
+ GRAPH_TO_CANVAS_Y (((ED_spl(edge))->list->list[num_points]).y);
+
+ }
+ while (num_points > 0 &&
+ !create_canvas_edge_arrow_ending (((ED_spl(edge))->list->list[num_points - 1]).x,
+ GRAPH_TO_CANVAS_Y (((ED_spl(edge))->list->list[num_points - 1]).y),
+ ((ED_spl(edge))->list->list[num_points]).x,
+ GRAPH_TO_CANVAS_Y (((ED_spl(edge))->list->list[num_points]).y),
+ &points->coords[2 * i], &points->coords[2 * i + 1]));
+ /* draw the line */
+ if (cls_edge->canvas_line)
+ {
+ foo_canvas_item_set (cls_edge->canvas_line, "points", points, NULL);
+ }
+ else
+ {
+ cls_edge->canvas_line =
+ foo_canvas_item_new (foo_canvas_root
+ (FOO_CANVAS (cls_node_from->canvas)),
+ foo_canvas_line_get_type(),
+ "smooth", TRUE,
+ "last_arrowhead", TRUE,
+ "arrow_shape_a", (gdouble) 8.0,
+ "arrow_shape_b", (gdouble) 10.0,
+ "arrow_shape_c", (gdouble) 3.0,
+ "fill_color_gdk",
+ &cls_node_from->plugin->style[STYLE_FG],
+ "width_units", 2.0,
+ "points", points,
+ NULL);
+ foo_canvas_item_lower_to_bottom (cls_edge->canvas_line);
+ }
+ foo_canvas_points_unref (points);
+}
+
+/* Ensures that a new canvas item is created for this node and moved to right
+ * position, or just moves an existing canvas item if already created.
+ */
+static void
+cls_node_ensure_draw (gpointer klass_id, ClsNode *cls_node, ClsBox *bounding_box)
+{
+ gdouble x, y;
+ point node_pos;
+#ifndef ND_coord_i
+ pointf node_posf;
+#endif
+
+ /* get some infos from the node */
+#ifdef ND_coord_i
+ node_pos = ND_coord_i (cls_node->agnode);
+#else
+ node_posf = ND_coord (cls_node->agnode);
+ PF2P (node_posf, node_pos);
+#endif
+
+ /* Determine node coords and size *in* canvas world coordinate system */
+ cls_node->width = INCH_TO_PIXELS (ND_width (cls_node->agnode));
+ cls_node->height = INCH_TO_PIXELS (ND_height (cls_node->agnode));
+ cls_node->x1 = node_pos.x - cls_node->width/2;
+ cls_node->y1 = GRAPH_TO_CANVAS_Y ((node_pos.y + cls_node->height/2));
+ cls_node->x2 = node_pos.x + cls_node->width/2;
+ cls_node->y2 = GRAPH_TO_CANVAS_Y ((node_pos.y - cls_node->height/2));
+
+ if (cls_node->x1 < bounding_box->x1) bounding_box->x1 = cls_node->x1;
+ if (cls_node->y1 < bounding_box->y1) bounding_box->y1 = cls_node->y1;
+ if (cls_node->x2 > bounding_box->x2) bounding_box->x2 = cls_node->x2;
+ if (cls_node->y2 > bounding_box->y2) bounding_box->y2 = cls_node->y2;
+
+ if (cls_node->canvas_group == NULL ||
+ cls_node->drawn_expansion_status != cls_node->expansion_status)
+ {
+ if (cls_node->expansion_status == CLS_NODE_COLLAPSED)
+ cls_node_draw_collapsed (cls_node);
+ else
+ cls_node_draw_expanded (cls_node);
+ }
+ /* Move the canvas item to right place */
+ x = cls_node->x1;
+ y = cls_node->y1;
+ foo_canvas_item_set (cls_node->canvas_group, "x", x, "y", y, NULL);
+ g_hash_table_foreach (cls_node->edges_to, (GHFunc) cls_node_draw_edge, cls_node);
+}
+
+/*----------------------------------------------------------------------------
+ * draw the graph on the canvas. So nodes, edges, arrows, texts..
+ */
+void
+cls_inherit_draw (AnjutaClassInheritance *plugin)
+{
+ ClsBox bounds;
+
+ gvLayout (plugin->gvc, plugin->graph, "dot");
+
+ /* set the size of the canvas. We need this to set the scrolling.. */
+ bounds.x1 = 0;
+ bounds.y1 = 0;
+ bounds.x2 = CANVAS_MIN_SIZE;
+ bounds.y2 = CANVAS_MIN_SIZE;
+
+ g_hash_table_foreach (plugin->nodes, (GHFunc) cls_node_ensure_draw, &bounds);
+
+ /* Request extra 20px along x and y for 10px margin around the canvas */
+ foo_canvas_set_scroll_region (FOO_CANVAS (plugin->canvas),
+ bounds.x1 - 10, bounds.y1 - 10,
+ bounds.x2 + 10, bounds.y2 + 10);
+ gvFreeLayout(plugin->gvc, plugin->graph);
+}
+
+static void
+on_cls_node_mark_for_deletion (gpointer key, ClsNode *cls_node, gpointer data)
+{
+ cls_node->marked_for_deletion = TRUE;
+}
+
+static gboolean
+on_cls_node_delete_marked (gpointer key, ClsNode *cls_node, gpointer data)
+{
+ if (!cls_node->marked_for_deletion) return FALSE;
+ cls_node_unlink (cls_node);
+ return TRUE;
+}
+
+/*----------------------------------------------------------------------------
+ * update the internal graphviz graph-structure, then redraw the graph on the
+ * canvas
+ */
+void
+cls_inherit_update (AnjutaClassInheritance *plugin)
+{
+ IAnjutaSymbolManager *sm;
+ IAnjutaIterable *iter;
+ IAnjutaSymbol *symbol;
+ ClsNode *cls_node;
+
+ g_return_if_fail (plugin != NULL);
+
+ /* Mark all nodes for deletion. Selectively, they will be unmarked below */
+ g_hash_table_foreach (plugin->nodes,
+ (GHFunc) on_cls_node_mark_for_deletion,
+ NULL);
+
+ if (plugin->top_dir == NULL)
+ goto cleanup;
+
+ sm = anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin)->shell,
+ IAnjutaSymbolManager, NULL);
+ if (!sm)
+ goto cleanup;
+
+ /* Get all classes */
+ iter = ianjuta_symbol_manager_search (sm, IANJUTA_SYMBOL_TYPE_CLASS,
+ TRUE,
+ IANJUTA_SYMBOL_FIELD_SIMPLE,
+ NULL, TRUE,
+ IANJUTA_SYMBOL_MANAGER_SEARCH_FS_PUBLIC,
+ FALSE, -1, -1, NULL);
+ if (!iter)
+ {
+ DEBUG_PRINT ("%s", "cls_inherit_update_graph (): search returned no items.");
+ goto cleanup;
+ }
+
+ ianjuta_iterable_first (iter, NULL);
+ if (ianjuta_iterable_get_length (iter, NULL) <= 0)
+ {
+ g_object_unref (iter);
+ goto cleanup;
+ }
+ do
+ {
+ gint klass_id;
+ IAnjutaIterable *parents;
+
+ /* a symbol representing a class */
+ symbol = IANJUTA_SYMBOL (iter);
+
+ /* get parents of the current class */
+ parents = ianjuta_symbol_manager_get_class_parents (sm, symbol,
+ IANJUTA_SYMBOL_FIELD_SIMPLE,
+ NULL);
+
+ /* if no parents are found then continue */
+ if (parents == NULL || ianjuta_iterable_get_length (parents, NULL) <= 0)
+ {
+ /*DEBUG_PRINT ("ClassInheritance: no parents found for class %s",
+ ianjuta_symbol_get_name (symbol, NULL));*/
+ continue;
+ }
+
+ if ((klass_id = ianjuta_symbol_get_id (symbol, NULL)) <= 0)
+ {
+ /*DEBUG_PRINT ("%s", "ClassInheritance: klass_id cannot be <= 0");*/
+ continue;
+ }
+
+ cls_node = g_hash_table_lookup (plugin->nodes, GINT_TO_POINTER (klass_id));
+ if (!cls_node)
+ {
+ cls_node = cls_inherit_create_node (plugin, symbol);
+ g_hash_table_insert (plugin->nodes, GINT_TO_POINTER (klass_id), cls_node);
+ }
+ cls_node->marked_for_deletion = FALSE;
+
+ /* Get parents */
+ do
+ {
+ gint parent_id;
+ ClsNode *parent_node;
+ IAnjutaSymbol *parent_symbol;
+ parent_symbol = IANJUTA_SYMBOL (parents);
+ parent_id = ianjuta_symbol_get_id (parent_symbol, NULL);
+
+ parent_node = g_hash_table_lookup (plugin->nodes,
+ GINT_TO_POINTER (parent_id));
+ if (!parent_node)
+ {
+ parent_node = cls_inherit_create_node (plugin, parent_symbol);
+ g_hash_table_insert (plugin->nodes, GINT_TO_POINTER (parent_id),
+ parent_node);
+ }
+ parent_node->marked_for_deletion = FALSE;
+ cls_node_add_edge (parent_node, cls_node);
+ } while (ianjuta_iterable_next (parents, NULL) == TRUE);
+ g_object_unref (parents);
+
+ } while (ianjuta_iterable_next (iter, NULL) == TRUE);
+
+ g_object_unref (iter);
+
+cleanup:
+
+ /* Delete all marked nodes that did not get unmarked above. */
+ g_hash_table_foreach_remove (plugin->nodes,
+ (GHRFunc) on_cls_node_delete_marked,
+ NULL);
+
+ cls_inherit_draw (plugin);
+}
+
+void
+cls_inherit_clear (AnjutaClassInheritance *plugin)
+{
+ g_hash_table_remove_all (plugin->nodes);
+}
+
+/*----------------------------------------------------------------------------
+ * Perform a dot_cleanup and a graph closing. Call this function at the end of
+ * call to draw_graph.
+ */
+void
+cls_inherit_free (AnjutaClassInheritance *plugin)
+{
+ if (plugin->nodes)
+ g_hash_table_destroy (plugin->nodes);
+ plugin->nodes = NULL;
+
+ /* Destroy graphics */
+ if (plugin->graph != NULL)
+ {
+ gvFreeLayout (plugin->gvc, plugin->graph);
+ agclose (plugin->graph);
+ }
+ if (plugin->gvc != NULL )
+ {
+ gvFreeContext (plugin->gvc);
+ }
+ plugin->graph = NULL;
+ plugin->gvc = NULL;
+}
+
+/*----------------------------------------------------------------------------
+ * initialize the internal graphviz structure.
+ */
+static void
+cls_inherit_graph_init (AnjutaClassInheritance *plugin, gchar* graph_label)
+{
+ Agsym_t *sym;
+ gchar dpi_text[16];
+
+ snprintf (dpi_text, 16, "%d", INCH_TO_PIXELS_CONVERSION_FACTOR);
+ aginit ();
+ plugin->graph = agopen (graph_label, AGDIGRAPH);
+ plugin->gvc = gvContext();
+
+ if (!(sym = agfindattr(plugin->graph->proto->n, "dpi")))
+ sym = agraphattr(plugin->graph, "dpi", dpi_text);
+ agxset(plugin->graph, sym->index, dpi_text);
+}
+
+void
+cls_inherit_init (AnjutaClassInheritance *plugin)
+{
+ GtkWidget *s_window;
+
+ /* Initialize graph layout engine */
+ cls_inherit_graph_init (plugin, _(DEFAULT_GRAPH_NAME));
+
+ s_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (s_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ plugin->canvas = foo_canvas_new ();
+ foo_canvas_set_scroll_region (FOO_CANVAS (plugin->canvas),
+ -CANVAS_MIN_SIZE/2,
+ -CANVAS_MIN_SIZE/2,
+ CANVAS_MIN_SIZE/2,
+ CANVAS_MIN_SIZE/2);
+ gtk_container_add (GTK_CONTAINER (s_window), plugin->canvas);
+
+ /* Initialize styles */
+ gtk_widget_ensure_style (plugin->canvas);
+ on_style_set (plugin->canvas, NULL, plugin);
+
+ g_signal_connect (G_OBJECT (plugin->canvas), "event",
+ G_CALLBACK (on_canvas_event),
+ plugin);
+ g_signal_connect (G_OBJECT (plugin->canvas), "style_set",
+ G_CALLBACK (on_style_set), plugin);
+
+ plugin->widget = gtk_vbox_new (FALSE, 2);
+
+ /* --packing-- */
+ /* vbox */
+ gtk_box_pack_start (GTK_BOX (plugin->widget), s_window, TRUE, TRUE, TRUE);
+
+ gtk_widget_show_all (plugin->widget);
+
+ /* create new GList */
+ plugin->nodes = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
+ (GDestroyNotify)cls_node_free);
+ /* menu create */
+ plugin->menu = gtk_menu_new ();
+
+ GtkWidget *menu_item = gtk_menu_item_new_with_label (_("Update"));
+ g_signal_connect (menu_item, "activate",
+ (GCallback)on_update_menu_item_selected, plugin);
+
+ /* set the user data on update selection */
+ gtk_menu_shell_append (GTK_MENU_SHELL (plugin->menu), menu_item);
+ gtk_widget_show_all (plugin->menu);
+
+ plugin->update = menu_item;
+
+ g_object_ref (plugin->menu);
+ g_object_ref (plugin->update);
+}
diff --git a/plugins/class-inheritance/class-inherit.h b/plugins/class-inheritance/class-inherit.h
new file mode 100644
index 0000000..3a901a9
--- /dev/null
+++ b/plugins/class-inheritance/class-inherit.h
@@ -0,0 +1,118 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * Copyright (C) Massimo Cora' 2005 <maxcvs email it>,
+ * 2010 Naba Kumar <naba gnome org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _CLASS_INHERIT_H
+#define _CLASS_INHERIT_H
+
+#include <libanjuta/interfaces/ianjuta-symbol-manager.h>
+#include <libfoocanvas/libfoocanvas.h>
+
+#include "plugin.h"
+
+G_BEGIN_DECLS
+
+#define NODE_HALF_DISPLAY_ELEM_NUM 30
+#define NODE_SHOW_ALL_MEMBERS_STR N_("Show all members...")
+
+typedef enum {
+ CLS_NODE_COLLAPSED,
+ CLS_NODE_SEMI_EXPANDED,
+ CLS_NODE_FULL_EXPANDED
+} ClsNodeExpansionType;
+
+typedef struct
+{
+ AnjutaClassInheritance *plugin; /* fixme: remove this */
+ Agraph_t *graph;
+ GtkWidget* canvas;
+
+ IAnjutaSymbolManager *sym_manager;
+ gint klass_id;
+ gchar *sym_name;
+
+ Agnode_t *agnode;
+
+ /* Node's expansion status */
+ ClsNodeExpansionType expansion_status;
+
+ /* Holds canvas group item for either collapsed or expanded node */
+ FooCanvasItem* canvas_group;
+
+ /* What expansion type is currently drawn */
+ ClsNodeExpansionType drawn_expansion_status;
+
+ /* Absolute coords in canvas. Only valid after node draw is ensured */
+ gint width, height, x1, y1, x2, y2;
+
+ /* Class members. Empty for collapsed nodes */
+ GHashTable *members;
+
+ /* All outgoing edges from this node */
+ GHashTable *edges_to;
+
+ /* All inbound edges to this node */
+ GHashTable *edges_from;
+
+ /* Used during update scan */
+ gboolean marked_for_deletion;
+} ClsNode;
+
+typedef struct {
+ Agedge_t *agedge;
+ FooCanvasItem *canvas_line;
+ ClsNode *cls_node_from;
+ ClsNode *cls_node_to;
+} ClsNodeEdge;
+
+typedef struct {
+ ClsNode *cls_node;
+ FooCanvasItem* canvas_node_item;
+
+ gint sym_id;
+ gchar *label;
+ gchar *args;
+ gchar *type_name;
+ GFile *file;
+ gint line;
+ GdkPixbuf *icon;
+
+ /* The order in which this item is shown in the list */
+ gint order;
+
+ /* Tooltip shown on item hover */
+ FooCanvasItem *tooltip;
+
+ /* Tooltip timout id */
+ guint tooltip_timeout;
+
+} ClsNodeItem;
+
+gboolean cls_node_collapse (ClsNode *cls_node);
+gboolean cls_node_expand (ClsNode *cls_node, ClsNodeExpansionType expand_type);
+
+void cls_inherit_init (AnjutaClassInheritance *plugin);
+void cls_inherit_draw (AnjutaClassInheritance *plugin);
+void cls_inherit_update (AnjutaClassInheritance *plugin);
+void cls_inherit_clear (AnjutaClassInheritance *plugin);
+void cls_inherit_free (AnjutaClassInheritance *plugin);
+
+G_END_DECLS
+
+#endif /* _CLASS_INHERIT_H */
diff --git a/plugins/class-inheritance/plugin.c b/plugins/class-inheritance/plugin.c
new file mode 100644
index 0000000..e5771ff
--- /dev/null
+++ b/plugins/class-inheritance/plugin.c
@@ -0,0 +1,197 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * plugin.c
+ * Copyright (C) Massimo Cora' 2005 <maxcvs email it>
+ *
+ * plugin.c is free software.
+ *
+ * You may redistribute it and/or modify it under the terms of the
+ * GNU General Public License, as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later version.
+ *
+ * plugin.c 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with plugin.c. See the file "COPYING". If not,
+ * write to: The Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include <libanjuta/anjuta-shell.h>
+#include <glib/gi18n.h>
+#include <libanjuta/interfaces/ianjuta-document-manager.h>
+#include <libanjuta/interfaces/ianjuta-project-manager.h>
+#include <libanjuta/anjuta-debug.h>
+#include "plugin.h"
+#include "class-inherit.h"
+
+#define ICON_FILE "anjuta-class-inheritance-plugin-48.png"
+
+static gpointer parent_class;
+
+static void
+project_root_added (AnjutaPlugin *plugin, const gchar *name,
+ const GValue *value, gpointer user_data)
+{
+ AnjutaClassInheritance *ci_plugin;
+ const gchar *root_uri;
+
+ ci_plugin = ANJUTA_PLUGIN_CLASS_INHERITANCE (plugin);
+ root_uri = g_value_get_string (value);
+
+ if (root_uri)
+ {
+ gchar *root_dir = anjuta_util_get_local_path_from_uri (root_uri);
+ if (root_dir)
+ {
+ ci_plugin->top_dir = g_strdup(root_dir);
+ }
+ else
+ ci_plugin->top_dir = NULL;
+ g_free (root_dir);
+ }
+ else
+ ci_plugin->top_dir = NULL;
+
+ /* let's update the graph */
+ cls_inherit_update (ci_plugin);
+}
+
+static void
+project_root_removed (AnjutaPlugin *plugin, const gchar *name,
+ gpointer user_data)
+{
+ AnjutaClassInheritance *ci_plugin;
+ ci_plugin = ANJUTA_PLUGIN_CLASS_INHERITANCE (plugin);
+
+ /* clean up the canvas */
+ cls_inherit_clear (ci_plugin);
+
+ if (ci_plugin->top_dir)
+ g_free(ci_plugin->top_dir);
+ ci_plugin->top_dir = NULL;
+}
+
+static void
+register_stock_icons (AnjutaPlugin *plugin)
+{
+ static gboolean registered = FALSE;
+
+ if (registered)
+ return;
+ registered = TRUE;
+
+ BEGIN_REGISTER_ICON (plugin);
+ REGISTER_ICON (PACKAGE_PIXMAPS_DIR"/"ICON_FILE,
+ "class-inheritance-plugin-icon");
+ END_REGISTER_ICON;
+}
+
+static gboolean
+activate_plugin (AnjutaPlugin *plugin)
+{
+ AnjutaClassInheritance *cls_inherit;
+ static gboolean initialized = FALSE;
+
+ DEBUG_PRINT ("%s", "AnjutaClassInheritance: Activating plugin ...");
+
+ register_stock_icons (plugin);
+
+ cls_inherit = ANJUTA_PLUGIN_CLASS_INHERITANCE (plugin);
+
+ cls_inherit_init (cls_inherit);
+
+ anjuta_shell_add_widget (plugin->shell, cls_inherit->widget,
+ "AnjutaClassInheritance", _("Inheritance Graph"),
+ "class-inheritance-plugin-icon",
+ ANJUTA_SHELL_PLACEMENT_CENTER, NULL);
+ cls_inherit->top_dir = NULL;
+
+ /* set up project directory watch */
+ cls_inherit->root_watch_id = anjuta_plugin_add_watch (plugin,
+ IANJUTA_PROJECT_MANAGER_PROJECT_ROOT_URI,
+ project_root_added,
+ project_root_removed, NULL);
+
+ initialized = TRUE;
+ return TRUE;
+}
+
+static gboolean
+deactivate_plugin (AnjutaPlugin *plugin)
+{
+ DEBUG_PRINT ("%s", "AnjutaClassInheritance: Dectivating plugin ...");
+ AnjutaClassInheritance* class_inheritance;
+ class_inheritance = ANJUTA_PLUGIN_CLASS_INHERITANCE (plugin);
+
+ /* clean up the canvas [e.g. destroys it's elements */
+ cls_inherit_free (class_inheritance);
+
+ /* Container holds the last ref to this widget so it will be destroyed as
+ * soon as removed. No need to separately destroy it. */
+ /* In most cases, only toplevel widgets (windows) require explicit
+ * destruction, because when you destroy a toplevel its children will
+ * be destroyed as well. */
+ anjuta_shell_remove_widget (plugin->shell,
+ class_inheritance->widget,
+ NULL);
+
+ /* Remove watches */
+ anjuta_plugin_remove_watch (plugin,
+ class_inheritance->root_watch_id,
+ TRUE);
+ return TRUE;
+}
+
+static void
+class_inheritance_finalize (GObject *obj)
+{
+ AnjutaClassInheritance *ci_plugin;
+ ci_plugin = ANJUTA_PLUGIN_CLASS_INHERITANCE (obj);
+
+ if (ci_plugin->top_dir)
+ g_free (ci_plugin->top_dir);
+
+ /* Finalization codes here */
+ G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static void
+class_inheritance_dispose (GObject *obj)
+{
+ /* Disposition codes */
+ G_OBJECT_CLASS (parent_class)->dispose (obj);
+}
+
+static void
+class_inheritance_instance_init (GObject *obj)
+{
+ AnjutaClassInheritance *plugin = ANJUTA_PLUGIN_CLASS_INHERITANCE (obj);
+
+ plugin->uiid = 0;
+
+ plugin->widget = NULL;
+ plugin->graph = NULL;
+ plugin->gvc = NULL;
+}
+
+static void
+class_inheritance_class_init (GObjectClass *klass)
+{
+ AnjutaPluginClass *plugin_class = ANJUTA_PLUGIN_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ plugin_class->activate = activate_plugin;
+ plugin_class->deactivate = deactivate_plugin;
+ klass->finalize = class_inheritance_finalize;
+ klass->dispose = class_inheritance_dispose;
+}
+
+ANJUTA_PLUGIN_BOILERPLATE (AnjutaClassInheritance, class_inheritance);
+ANJUTA_SIMPLE_PLUGIN (AnjutaClassInheritance, class_inheritance);
diff --git a/plugins/class-inheritance/plugin.h b/plugins/class-inheritance/plugin.h
new file mode 100644
index 0000000..f1b72a4
--- /dev/null
+++ b/plugins/class-inheritance/plugin.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * plugin.h
+ * Copyright (C) Massimo Cora' 2005 <maxcvs email it>
+ *
+ * plugin.h is free software.
+ *
+ * You may redistribute it and/or modify it under the terms of the
+ * GNU General Public License, as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later version.
+ *
+ * plugin.h 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with plugin.h. See the file "COPYING". If not,
+ * write to: The Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _CLASS_INHERITANCE_H_
+#define _CLASS_INHERITANCE_H_
+
+#include <libanjuta/anjuta-plugin.h>
+
+#include <gvc.h> /* graphviz */
+
+G_BEGIN_DECLS
+
+extern GType class_inheritance_get_type (GTypeModule *module);
+#define ANJUTA_TYPE_PLUGIN_CLASS_INHERITANCE (class_inheritance_get_type (NULL))
+#define ANJUTA_PLUGIN_CLASS_INHERITANCE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), ANJUTA_TYPE_PLUGIN_CLASS_INHERITANCE, AnjutaClassInheritance))
+#define ANJUTA_PLUGIN_CLASS_INHERITANCE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), ANJUTA_TYPE_PLUGIN_CLASS_INHERITANCE, AnjutaClassInheritanceClass))
+#define ANJUTA_IS_PLUGIN_CLASS_INHERITANCE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), ANJUTA_TYPE_PLUGIN_CLASS_INHERITANCE))
+#define ANJUTA_IS_PLUGIN_CLASS_INHERITANCE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), ANJUTA_TYPE_PLUGIN_CLASS_INHERITANCE))
+#define ANJUTA_PLUGIN_CLASS_INHERITANCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), ANJUTA_TYPE_PLUGIN_CLASS_INHERITANCE, AnjutaClassInheritanceClass))
+
+typedef struct _AnjutaClassInheritance AnjutaClassInheritance;
+typedef struct _AnjutaClassInheritanceClass AnjutaClassInheritanceClass;
+
+enum {
+ STYLE_BG,
+ STYLE_FG,
+ STYLE_TITLE_BG,
+ STYLE_TITLE_FG,
+ STYLE_TITLE_PRELIGHT_FG,
+ STYLE_TITLE_PRELIGHT_BG,
+ STYLE_ITEM_BG,
+ STYLE_ITEM_FG,
+ STYLE_ITEM_PRELIGHT_FG,
+ STYLE_ITEM_PRELIGHT_BG,
+ N_STYLES
+};
+
+struct _AnjutaClassInheritance {
+ AnjutaPlugin parent;
+
+ GtkWidget *widget; /* a vbox */
+ GtkWidget *update_button;
+ GtkWidget *menu;
+ GtkWidget *update;
+
+ GtkWidget *canvas;
+ GHashTable *nodes;
+
+ /* graphviz stuff */
+ GVC_t *gvc;
+ Agraph_t *graph;
+
+ gchar *top_dir;
+ guint root_watch_id;
+ gint uiid;
+
+ /* Style colors */
+ GdkColor style[N_STYLES];
+};
+
+struct _AnjutaClassInheritanceClass {
+ AnjutaPluginClass parent_class;
+};
+
+G_END_DECLS
+
+#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]