aboutsummaryrefslogtreecommitdiffstats
path: root/libgnomecanvas
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2010-06-14 06:37:27 +0800
committerMatthew Barnes <mbarnes@redhat.com>2010-06-14 09:31:58 +0800
commite6972011f01eab9f8d0a4584f32ee1e2a00f3231 (patch)
tree69e2a5e846965c3b369724b825f5b35c6d88056a /libgnomecanvas
parent7f3377c78a560aa762e04d596b79f847c4acd870 (diff)
downloadgsoc2013-evolution-e6972011f01eab9f8d0a4584f32ee1e2a00f3231.tar
gsoc2013-evolution-e6972011f01eab9f8d0a4584f32ee1e2a00f3231.tar.gz
gsoc2013-evolution-e6972011f01eab9f8d0a4584f32ee1e2a00f3231.tar.bz2
gsoc2013-evolution-e6972011f01eab9f8d0a4584f32ee1e2a00f3231.tar.lz
gsoc2013-evolution-e6972011f01eab9f8d0a4584f32ee1e2a00f3231.tar.xz
gsoc2013-evolution-e6972011f01eab9f8d0a4584f32ee1e2a00f3231.tar.zst
gsoc2013-evolution-e6972011f01eab9f8d0a4584f32ee1e2a00f3231.zip
Embed libart_lgpl and libgnomecanvas.
Both of these modules are deprecated and going away in GNOME 3 but we still rely heavily on them for GnomeCalendar and ETable. So, welcome to the island of unwanted libraries...
Diffstat (limited to 'libgnomecanvas')
-rw-r--r--libgnomecanvas/Makefile.am83
-rw-r--r--libgnomecanvas/gailcanvas.c266
-rw-r--r--libgnomecanvas/gailcanvas.h74
-rw-r--r--libgnomecanvas/gailcanvasgroup.c102
-rw-r--r--libgnomecanvas/gailcanvasgroup.h55
-rw-r--r--libgnomecanvas/gailcanvasgroupfactory.c77
-rw-r--r--libgnomecanvas/gailcanvasgroupfactory.h53
-rw-r--r--libgnomecanvas/gailcanvasitem.c514
-rw-r--r--libgnomecanvas/gailcanvasitem.h54
-rw-r--r--libgnomecanvas/gailcanvasitemfactory.c57
-rw-r--r--libgnomecanvas/gailcanvasitemfactory.h53
-rw-r--r--libgnomecanvas/gailcanvastext.c522
-rw-r--r--libgnomecanvas/gailcanvastext.h52
-rw-r--r--libgnomecanvas/gailcanvastextfactory.c57
-rw-r--r--libgnomecanvas/gailcanvastextfactory.h53
-rw-r--r--libgnomecanvas/gailcanvaswidget.c113
-rw-r--r--libgnomecanvas/gailcanvaswidget.h55
-rw-r--r--libgnomecanvas/gailcanvaswidgetfactory.c56
-rw-r--r--libgnomecanvas/gailcanvaswidgetfactory.h53
-rw-r--r--libgnomecanvas/gnome-canvas-bpath.c175
-rw-r--r--libgnomecanvas/gnome-canvas-bpath.h61
-rw-r--r--libgnomecanvas/gnome-canvas-clipgroup.c450
-rw-r--r--libgnomecanvas/gnome-canvas-clipgroup.h58
-rw-r--r--libgnomecanvas/gnome-canvas-i18n.h68
-rw-r--r--libgnomecanvas/gnome-canvas-line.c1423
-rw-r--r--libgnomecanvas/gnome-canvas-line.h154
-rw-r--r--libgnomecanvas/gnome-canvas-marshal.list2
-rw-r--r--libgnomecanvas/gnome-canvas-path-def.c1287
-rw-r--r--libgnomecanvas/gnome-canvas-path-def.h96
-rw-r--r--libgnomecanvas/gnome-canvas-pixbuf.c1077
-rw-r--r--libgnomecanvas/gnome-canvas-pixbuf.h62
-rw-r--r--libgnomecanvas/gnome-canvas-polygon.c246
-rw-r--r--libgnomecanvas/gnome-canvas-polygon.h88
-rw-r--r--libgnomecanvas/gnome-canvas-rect-ellipse.c431
-rw-r--r--libgnomecanvas/gnome-canvas-rect-ellipse.h159
-rw-r--r--libgnomecanvas/gnome-canvas-rich-text.c2202
-rw-r--r--libgnomecanvas/gnome-canvas-rich-text.h79
-rw-r--r--libgnomecanvas/gnome-canvas-shape-private.h103
-rw-r--r--libgnomecanvas/gnome-canvas-shape.c1558
-rw-r--r--libgnomecanvas/gnome-canvas-shape.h81
-rw-r--r--libgnomecanvas/gnome-canvas-text.c1746
-rw-r--r--libgnomecanvas/gnome-canvas-text.h170
-rw-r--r--libgnomecanvas/gnome-canvas-util.c700
-rw-r--r--libgnomecanvas/gnome-canvas-util.h155
-rw-r--r--libgnomecanvas/gnome-canvas-widget.c599
-rw-r--r--libgnomecanvas/gnome-canvas-widget.h103
-rw-r--r--libgnomecanvas/gnome-canvas.c4090
-rw-r--r--libgnomecanvas/gnome-canvas.h635
-rw-r--r--libgnomecanvas/libgnomecanvas.h48
-rw-r--r--libgnomecanvas/libgnomecanvastypes.c43
50 files changed, 20498 insertions, 0 deletions
diff --git a/libgnomecanvas/Makefile.am b/libgnomecanvas/Makefile.am
new file mode 100644
index 0000000000..46fdcc81ef
--- /dev/null
+++ b/libgnomecanvas/Makefile.am
@@ -0,0 +1,83 @@
+privsolib_LTLIBRARIES = libgnomecanvas-2.la
+
+libgnomecanvas_2_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir) \
+ $(GNOME_PLATFORM_CFLAGS) \
+ -DGNOMECANVASLIBDIR=\""$(libdir)"\" \
+ -DGNOMECANVASDATADIR=\""$(datadir)"\" \
+ -DGNOMECANVASPIXMAPDIR=\""$(datadir)/pixmaps"\" \
+ -DGNOMECANVASBINDIR=\""$(bindir)"\" \
+ -DGNOMECANVASLOCALSTATEDIR=\""$(localstatedir)"\" \
+ -DG_LOG_DOMAIN=\"GnomeCanvas\"
+
+libgnomecanvas_2_la_LIBADD = \
+ $(top_builddir)/libart_lgpl/libart_lgpl_2.la \
+ $(GNOME_PLATFORM_LIBS)
+
+libgnomecanvas_2_la_LDFLAGS = $(NO_UNDEFINED)
+
+libgnomecanvas_2_la_SOURCES = \
+ gailcanvas.c \
+ gailcanvas.h \
+ gailcanvasgroup.c \
+ gailcanvasgroup.h \
+ gailcanvasgroupfactory.c \
+ gailcanvasgroupfactory.h \
+ gailcanvasitem.c \
+ gailcanvasitem.h \
+ gailcanvasitemfactory.c \
+ gailcanvasitemfactory.h \
+ gailcanvastext.c \
+ gailcanvastext.h \
+ gailcanvastextfactory.c \
+ gailcanvastextfactory.h \
+ gailcanvaswidget.c \
+ gailcanvaswidget.h \
+ gailcanvaswidgetfactory.c \
+ gailcanvaswidgetfactory.h \
+ gnome-canvas-bpath.c \
+ gnome-canvas-bpath.h \
+ gnome-canvas-clipgroup.c \
+ gnome-canvas-clipgroup.h \
+ gnome-canvas-i18n.h \
+ gnome-canvas-line.c \
+ gnome-canvas-line.h \
+ gnome-canvas-path-def.c \
+ gnome-canvas-path-def.h \
+ gnome-canvas-pixbuf.c \
+ gnome-canvas-pixbuf.h \
+ gnome-canvas-polygon.c \
+ gnome-canvas-polygon.h \
+ gnome-canvas-rect-ellipse.c \
+ gnome-canvas-rect-ellipse.h \
+ gnome-canvas-rich-text.c \
+ gnome-canvas-rich-text.h \
+ gnome-canvas-shape-private.h \
+ gnome-canvas-shape.c \
+ gnome-canvas-shape.h \
+ gnome-canvas-text.c \
+ gnome-canvas-text.h \
+ gnome-canvas-util.c \
+ gnome-canvas-util.h \
+ gnome-canvas-widget.c \
+ gnome-canvas-widget.h \
+ gnome-canvas.c \
+ gnome-canvas.h \
+ libgnomecanvas.h \
+ libgnomecanvastypes.c
+
+MARSHAL_GENERATED = \
+ gnome-canvas-marshal.c \
+ gnome-canvas-marshal.h
+
+@EVO_MARSHAL_RULE@
+
+BUILT_SOURCES = $(MARSHAL_GENERATED)
+
+CLEANFILES = $(BUILT_SOURCES)
+
+EXTRA_DIST = \
+ gnome-canvas-marshal.list
+
+-include $(top_srcdir)/git.mk
diff --git a/libgnomecanvas/gailcanvas.c b/libgnomecanvas/gailcanvas.c
new file mode 100644
index 0000000000..4ec0b67de3
--- /dev/null
+++ b/libgnomecanvas/gailcanvas.c
@@ -0,0 +1,266 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001, 2002, 2003 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 <gtk/gtk.h>
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-text.h>
+#include <libgnomecanvas/gnome-canvas-rich-text.h>
+#include <libgnomecanvas/gnome-canvas-widget.h>
+#include "gailcanvas.h"
+#include "gailcanvasitem.h"
+#include "gailcanvasgroupfactory.h"
+#include "gailcanvastextfactory.h"
+#include "gailcanvasitemfactory.h"
+#include "gailcanvaswidgetfactory.h"
+
+static void gail_canvas_class_init (GailCanvasClass *klass);
+static void gail_canvas_real_initialize (AtkObject *obj,
+ gpointer data);
+
+static gint gail_canvas_get_n_children (AtkObject *obj);
+static AtkObject* gail_canvas_ref_child (AtkObject *obj,
+ gint i);
+
+static void adjustment_changed (GtkAdjustment *adjustment,
+ GnomeCanvas *canvas);
+
+static AtkObject* gail_canvas_factory_create_accessible (GObject *obj);
+
+static GType gail_canvas_factory_get_accessible_type (void);
+
+G_DEFINE_TYPE (GailCanvasFactory,
+ gail_canvas_factory,
+ ATK_TYPE_OBJECT_FACTORY);
+
+static void
+gail_canvas_factory_init (GailCanvasFactory *foo)
+{
+ ;
+}
+
+static void
+gail_canvas_factory_class_init (GailCanvasFactoryClass *klass)
+{
+ AtkObjectFactoryClass *class = ATK_OBJECT_FACTORY_CLASS (klass);
+
+ class->create_accessible = gail_canvas_factory_create_accessible;
+ class->get_accessible_type = gail_canvas_factory_get_accessible_type;
+}
+
+static AtkObject*
+gail_canvas_factory_create_accessible (GObject *obj)
+{
+ return gail_canvas_new (GTK_WIDGET (obj));
+}
+
+static GType
+gail_canvas_factory_get_accessible_type (void)
+{
+ return GAIL_TYPE_CANVAS;
+}
+
+GType
+gail_canvas_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type)
+ {
+ GType parent_type = g_type_parent (GNOME_TYPE_CANVAS);
+ AtkObjectFactory *factory = atk_registry_get_factory (
+ atk_get_default_registry (),
+ parent_type);
+ GType atkobject_parent_type = atk_object_factory_get_accessible_type (factory);
+ GTypeQuery query;
+ static GTypeInfo tinfo =
+ {
+ 0, /* class size */
+ (GBaseInitFunc) NULL, /* base init */
+ (GBaseFinalizeFunc) NULL, /* base finalize */
+ (GClassInitFunc) gail_canvas_class_init, /* class init */
+ (GClassFinalizeFunc) NULL, /* class finalize */
+ NULL, /* class data */
+ 0, /* instance size */
+ 0, /* nb preallocs */
+ (GInstanceInitFunc) NULL, /* instance init */
+ NULL /* value table */
+ };
+ g_type_query (atkobject_parent_type, &query);
+ tinfo.class_size = query.class_size;
+ tinfo.instance_size = query.instance_size;
+
+ /* use the size obtained from the parent type factory */
+ type = g_type_register_static (atkobject_parent_type,
+ "GailCanvas", &tinfo, 0);
+ }
+
+ return type;
+}
+
+static AtkObjectClass *parent_atk_object_class;
+
+/**
+ * Tell ATK how to create the appropriate AtkObject peers
+ **/
+void
+gail_canvas_init (void)
+{
+ atk_registry_set_factory_type (atk_get_default_registry (),
+ GNOME_TYPE_CANVAS,
+ gail_canvas_factory_get_type ());
+ atk_registry_set_factory_type (atk_get_default_registry (),
+ GNOME_TYPE_CANVAS_GROUP,
+ gail_canvas_group_factory_get_type ());
+ atk_registry_set_factory_type (atk_get_default_registry (),
+ GNOME_TYPE_CANVAS_TEXT,
+ gail_canvas_text_factory_get_type ());
+ atk_registry_set_factory_type (atk_get_default_registry (),
+ GNOME_TYPE_CANVAS_RICH_TEXT,
+ gail_canvas_text_factory_get_type ());
+ atk_registry_set_factory_type (atk_get_default_registry (),
+ GNOME_TYPE_CANVAS_WIDGET,
+ gail_canvas_widget_factory_get_type());
+ atk_registry_set_factory_type (atk_get_default_registry (),
+ GNOME_TYPE_CANVAS_ITEM,
+ gail_canvas_item_factory_get_type ());
+}
+
+static void
+gail_canvas_class_init (GailCanvasClass *klass)
+{
+ AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
+ parent_atk_object_class = g_type_class_peek_parent (klass);
+
+ class->get_n_children = gail_canvas_get_n_children;
+ class->ref_child = gail_canvas_ref_child;
+ class->initialize = gail_canvas_real_initialize;
+}
+
+AtkObject*
+gail_canvas_new (GtkWidget *widget)
+{
+ GObject *object;
+ AtkObject *accessible;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS (widget), NULL);
+
+ object = g_object_new (GAIL_TYPE_CANVAS, NULL);
+
+ accessible = ATK_OBJECT (object);
+ atk_object_initialize (accessible, widget);
+
+ return accessible;
+}
+
+
+static void
+gail_canvas_real_initialize (AtkObject *obj,
+ gpointer data)
+{
+ GnomeCanvas *canvas;
+ GtkAdjustment *adj;
+
+ parent_atk_object_class->initialize (obj, data);
+
+ canvas = GNOME_CANVAS (data);
+
+ adj = canvas->layout.hadjustment;
+ g_signal_connect (adj,
+ "value_changed",
+ G_CALLBACK (adjustment_changed),
+ canvas);
+
+ adj = canvas->layout.vadjustment;
+ g_signal_connect (adj,
+ "value_changed",
+ G_CALLBACK (adjustment_changed),
+ canvas);
+
+ obj->role = ATK_ROLE_LAYERED_PANE;
+}
+
+static gint
+gail_canvas_get_n_children (AtkObject* obj)
+{
+ GtkAccessible *accessible;
+ GtkWidget *widget;
+ GnomeCanvas *canvas;
+ GnomeCanvasGroup *root_group;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS (obj), 0);
+
+ accessible = GTK_ACCESSIBLE (obj);
+ widget = accessible->widget;
+ if (widget == NULL)
+ /* State is defunct */
+ return 0;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS (widget), 0);
+
+ canvas = GNOME_CANVAS (widget);
+ root_group = gnome_canvas_root (canvas);
+ g_return_val_if_fail (root_group, 0);
+ return 1;
+}
+
+static AtkObject*
+gail_canvas_ref_child (AtkObject *obj,
+ gint i)
+{
+ GtkAccessible *accessible;
+ GtkWidget *widget;
+ GnomeCanvas *canvas;
+ GnomeCanvasGroup *root_group;
+ AtkObject *atk_object;
+
+ /* Canvas only has one child, so return NULL if anything else is requested */
+ if (i != 0)
+ return NULL;
+ g_return_val_if_fail (GAIL_IS_CANVAS (obj), NULL);
+
+ accessible = GTK_ACCESSIBLE (obj);
+ widget = accessible->widget;
+ if (widget == NULL)
+ /* State is defunct */
+ return NULL;
+ g_return_val_if_fail (GNOME_IS_CANVAS (widget), NULL);
+
+ canvas = GNOME_CANVAS (widget);
+ root_group = gnome_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);
+ return atk_object;
+}
+
+static void
+adjustment_changed (GtkAdjustment *adjustment,
+ GnomeCanvas *canvas)
+{
+ AtkObject *atk_obj;
+
+ /*
+ * The scrollbars have changed
+ */
+ atk_obj = gtk_widget_get_accessible (GTK_WIDGET (canvas));
+
+ g_signal_emit_by_name (atk_obj, "visible_data_changed");
+}
+
diff --git a/libgnomecanvas/gailcanvas.h b/libgnomecanvas/gailcanvas.h
new file mode 100644
index 0000000000..46cd7b7187
--- /dev/null
+++ b/libgnomecanvas/gailcanvas.h
@@ -0,0 +1,74 @@
+/* gailcanvas.h - code from GAIL, the
+ * Gnome Accessibility Implementation Library
+ * Copyright 2001-2006 Sun Microsystems Inc.
+ *
+ * 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 __GAIL_CANVAS_H__
+#define __GAIL_CANVAS_H__
+
+#include <gtk/gtk.h>
+
+/* This code provides the ATK implementation for gnome-canvas widgets. */
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS (gail_canvas_get_type ())
+#define GAIL_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS, GailCanvas))
+#define GAIL_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS, GailCanvasClass))
+#define GAIL_IS_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS))
+#define GAIL_IS_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS))
+#define GAIL_CANVAS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS, GailCanvasClass))
+
+#define GAIL_TYPE_CANVAS_FACTORY (gail_canvas_factory_get_type ())
+#define GAIL_CANVAS_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_FACTORY, GailCanvasFactory))
+#define GAIL_CANVAS_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_FACTORY, GailCanvasFactoryClass))
+#define GAIL_IS_CANVAS_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_FACTORY))
+#define GAIL_IS_CANVAS_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_FACTORY))
+#define GAIL_CANVAS_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_FACTORY, GailCanvasFactoryClass))
+
+typedef struct _GailCanvas GailCanvas;
+typedef struct _GailCanvasClass GailCanvasClass;
+typedef struct _GailCanvasFactory GailCanvasFactory;
+typedef struct _GailCanvasFactoryClass GailCanvasFactoryClass;
+
+struct _GailCanvasFactory
+{
+ AtkObjectFactory parent;
+};
+
+struct _GailCanvasFactoryClass
+{
+ AtkObjectFactoryClass parent_class;
+};
+
+
+GType gail_canvas_get_type (void);
+
+struct _GailCanvas;
+
+struct _GailCanvasClass;
+
+AtkObject* gail_canvas_new (GtkWidget *widget);
+
+void gail_canvas_init (void);
+
+GType gail_canvas_factory_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_H__ */
diff --git a/libgnomecanvas/gailcanvasgroup.c b/libgnomecanvas/gailcanvasgroup.c
new file mode 100644
index 0000000000..1750f679c2
--- /dev/null
+++ b/libgnomecanvas/gailcanvasgroup.c
@@ -0,0 +1,102 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 <gtk/gtk.h>
+#include <libgnomecanvas/gnome-canvas.h>
+#include "gailcanvasgroup.h"
+
+static gint gail_canvas_group_get_n_children (AtkObject *obj);
+static AtkObject* gail_canvas_group_ref_child (AtkObject *obj,
+ gint i);
+
+G_DEFINE_TYPE(GailCanvasGroup,
+ gail_canvas_group,
+ GAIL_TYPE_CANVAS_ITEM);
+
+static void
+gail_canvas_group_init (GailCanvasGroup *foo)
+{
+ ;
+}
+
+AtkObject*
+gail_canvas_group_new (GObject *obj)
+{
+ gpointer object;
+ AtkObject *atk_object;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS_GROUP (obj), NULL);
+ object = g_object_new (GAIL_TYPE_CANVAS_GROUP, NULL);
+ atk_object = ATK_OBJECT (object);
+ atk_object_initialize (atk_object, obj);
+ atk_object->role = ATK_ROLE_PANEL;
+ return atk_object;
+}
+
+static void
+gail_canvas_group_class_init (GailCanvasGroupClass *klass)
+{
+ AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
+
+ class->get_n_children = gail_canvas_group_get_n_children;
+ class->ref_child = gail_canvas_group_ref_child;
+}
+
+static gint
+gail_canvas_group_get_n_children (AtkObject *obj)
+{
+ AtkGObjectAccessible *atk_gobject;
+ GnomeCanvasGroup *group;
+ GObject *g_obj;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_ITEM (obj), 0);
+ atk_gobject = ATK_GOBJECT_ACCESSIBLE (obj);
+ g_obj = atk_gobject_accessible_get_object (atk_gobject);
+ g_return_val_if_fail (GNOME_IS_CANVAS_GROUP (g_obj), 0);
+ group = GNOME_CANVAS_GROUP (g_obj);
+ return g_list_length (group->item_list);
+}
+
+
+static AtkObject *
+gail_canvas_group_ref_child (AtkObject *obj,
+ gint i)
+{
+ AtkGObjectAccessible *atk_gobject;
+ GnomeCanvasGroup *group;
+ GnomeCanvasItem *item;
+ AtkObject *accessible;
+ GObject *g_obj;
+ GList *list_item;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_ITEM (obj), NULL);
+ atk_gobject = ATK_GOBJECT_ACCESSIBLE (obj);
+ g_obj = atk_gobject_accessible_get_object (atk_gobject);
+ g_return_val_if_fail (GNOME_IS_CANVAS_GROUP (g_obj), NULL);
+ group = GNOME_CANVAS_GROUP (g_obj);
+
+ list_item = g_list_nth (group->item_list, i);
+ if (!list_item)
+ return NULL;
+ g_return_val_if_fail (list_item->data, NULL);
+ item = GNOME_CANVAS_ITEM (list_item->data);
+ accessible = atk_gobject_accessible_for_object (G_OBJECT (item));
+ g_object_ref (accessible);
+ return accessible;
+}
diff --git a/libgnomecanvas/gailcanvasgroup.h b/libgnomecanvas/gailcanvasgroup.h
new file mode 100644
index 0000000000..4f50b9da26
--- /dev/null
+++ b/libgnomecanvas/gailcanvasgroup.h
@@ -0,0 +1,55 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * 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 __GAIL_CANVAS_GROUP_H__
+#define __GAIL_CANVAS_GROUP_H__
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <atk/atk.h>
+#include "gailcanvasitem.h"
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS_GROUP (gail_canvas_group_get_type ())
+#define GAIL_CANVAS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_GROUP, GailCanvasGroup))
+#define GAIL_CANVAS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_GROUP, GailCanvasGroupClass))
+#define GAIL_IS_CANVAS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_GROUP))
+#define GAIL_IS_CANVAS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_GROUP))
+#define GAIL_CANVAS_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_GROUP, GailCanvasGroupClass))
+
+typedef struct _GailCanvasGroup GailCanvasGroup;
+typedef struct _GailCanvasGroupClass GailCanvasGroupClass;
+
+struct _GailCanvasGroup
+{
+ GailCanvasItem parent;
+};
+
+GType gail_canvas_group_get_type (void);
+
+struct _GailCanvasGroupClass
+{
+ GailCanvasItemClass parent_class;
+};
+
+AtkObject* gail_canvas_group_new (GObject *obj);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_GROUP_H__ */
diff --git a/libgnomecanvas/gailcanvasgroupfactory.c b/libgnomecanvas/gailcanvasgroupfactory.c
new file mode 100644
index 0000000000..10a7d2e056
--- /dev/null
+++ b/libgnomecanvas/gailcanvasgroupfactory.c
@@ -0,0 +1,77 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 <gtk/gtk.h>
+#include "gailcanvasgroupfactory.h"
+#include "gailcanvasgroup.h"
+
+static void gail_canvas_group_factory_class_init (GailCanvasGroupFactoryClass *klass);
+
+static AtkObject * gail_canvas_group_factory_create_accessible (GObject *obj);
+
+static GType gail_canvas_group_factory_get_accessible_type (void);
+
+GType
+gail_canvas_group_factory_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type)
+ {
+ static const GTypeInfo tinfo =
+ {
+ sizeof (GailCanvasGroupFactoryClass),
+ (GBaseInitFunc) NULL, /* base init */
+ (GBaseFinalizeFunc) NULL, /* base finalize */
+ (GClassInitFunc) gail_canvas_group_factory_class_init, /* class init */
+ (GClassFinalizeFunc) NULL, /* class finalize */
+ NULL, /* class data */
+ sizeof (GailCanvasGroupFactory), /* instance size */
+ 0, /* nb preallocs */
+ (GInstanceInitFunc) NULL, /* instance init */
+ NULL /* value table */
+ };
+ type = g_type_register_static (
+ ATK_TYPE_OBJECT_FACTORY,
+ "GailCanvasGroupFactory" , &tinfo, 0);
+ }
+
+ return type;
+}
+
+static void
+gail_canvas_group_factory_class_init (GailCanvasGroupFactoryClass *klass)
+{
+ AtkObjectFactoryClass *class = ATK_OBJECT_FACTORY_CLASS (klass);
+
+ class->create_accessible = gail_canvas_group_factory_create_accessible;
+ class->get_accessible_type = gail_canvas_group_factory_get_accessible_type;
+}
+
+static AtkObject*
+gail_canvas_group_factory_create_accessible (GObject *obj)
+{
+ return gail_canvas_group_new (obj);
+}
+
+static GType
+gail_canvas_group_factory_get_accessible_type (void)
+{
+ return GAIL_TYPE_CANVAS_GROUP;
+}
diff --git a/libgnomecanvas/gailcanvasgroupfactory.h b/libgnomecanvas/gailcanvasgroupfactory.h
new file mode 100644
index 0000000000..7ba4014eb6
--- /dev/null
+++ b/libgnomecanvas/gailcanvasgroupfactory.h
@@ -0,0 +1,53 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * 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 __GAIL_CANVAS_GROUP_FACTORY_H__
+#define __GAIL_CANVAS_GROUP_FACTORY_H__
+
+#include <atk/atkobjectfactory.h>
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS_GROUP_FACTORY (gail_canvas_group_factory_get_type ())
+#define GAIL_CANVAS_GROUP_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_GROUP_FACTORY, GailCanvasGroupFactory))
+#define GAIL_CANVAS_GROUP_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_GROUP_FACTORY, GailCanvasGroupFactoryClass))
+#define GAIL_IS_CANVAS_GROUP_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_GROUP_FACTORY))
+#define GAIL_IS_CANVAS_GROUP_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_GROUP_FACTORY))
+#define GAIL_CANVAS_GROUP_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_GROUP_FACTORY, GailCanvasGroupFactoryClass))
+
+
+typedef struct _GailCanvasGroupFactory GailCanvasGroupFactory;
+typedef struct _GailCanvasGroupFactoryClass GailCanvasGroupFactoryClass;
+
+struct _GailCanvasGroupFactory
+{
+ AtkObjectFactory parent;
+};
+
+struct _GailCanvasGroupFactoryClass
+{
+ AtkObjectFactoryClass parent_class;
+};
+
+GType gail_canvas_group_factory_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_GROUP_FACTORY_H__ */
+
diff --git a/libgnomecanvas/gailcanvasitem.c b/libgnomecanvas/gailcanvasitem.c
new file mode 100644
index 0000000000..f059e1909c
--- /dev/null
+++ b/libgnomecanvas/gailcanvasitem.c
@@ -0,0 +1,514 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 <math.h>
+#include <gtk/gtk.h>
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-util.h>
+#include "gailcanvasitem.h"
+#include <libgail-util/gailmisc.h>
+
+static void gail_canvas_item_initialize (AtkObject *obj,
+ gpointer data);
+static AtkObject* gail_canvas_item_get_parent (AtkObject *obj);
+static gint gail_canvas_item_get_index_in_parent (AtkObject *obj);
+static AtkStateSet* gail_canvas_item_ref_state_set (AtkObject *obj);
+
+static void gail_canvas_item_component_interface_init (AtkComponentIface *iface);
+static guint gail_canvas_item_add_focus_handler (AtkComponent *component,
+ AtkFocusHandler handler);
+static void gail_canvas_item_get_extents (AtkComponent *component,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height,
+ AtkCoordType coord_type);
+static gint gail_canvas_item_get_mdi_zorder (AtkComponent *component);
+static gboolean gail_canvas_item_grab_focus (AtkComponent *component);
+static void gail_canvas_item_remove_focus_handler (AtkComponent *component,
+ guint handler_id);
+static gboolean is_item_on_screen (GnomeCanvasItem *item);
+static void get_item_extents (GnomeCanvasItem *item,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height);
+static gboolean is_item_in_window (GnomeCanvasItem *item,
+ gint x,
+ gint y,
+ gint width,
+ gint height);
+
+static AtkGObjectAccessibleClass *parent_class = NULL;
+
+G_DEFINE_TYPE_WITH_CODE (GailCanvasItem,
+ gail_canvas_item,
+ ATK_TYPE_GOBJECT_ACCESSIBLE,
+ G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT,
+ gail_canvas_item_component_interface_init));
+
+static void
+gail_canvas_item_init (GailCanvasItem *foo)
+{
+ ;
+}
+
+AtkObject*
+gail_canvas_item_new (GObject *obj)
+{
+ gpointer object;
+ AtkObject *atk_object;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS_ITEM (obj), NULL);
+ object = g_object_new (GAIL_TYPE_CANVAS_ITEM, NULL);
+ atk_object = ATK_OBJECT (object);
+ atk_object_initialize (atk_object, obj);
+ atk_object->role = ATK_ROLE_UNKNOWN;
+ return atk_object;
+}
+
+static void
+gail_canvas_item_initialize (AtkObject *obj,
+ gpointer data)
+{
+ ATK_OBJECT_CLASS (parent_class)->initialize (obj, data);
+
+ g_object_set_data (G_OBJECT (obj), "atk-component-layer",
+ GINT_TO_POINTER (ATK_LAYER_MDI));
+}
+
+static void
+gail_canvas_item_class_init (GailCanvasItemClass *klass)
+{
+ AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ class->get_parent = gail_canvas_item_get_parent;
+ class->get_index_in_parent = gail_canvas_item_get_index_in_parent;
+ class->ref_state_set = gail_canvas_item_ref_state_set;
+ class->initialize = gail_canvas_item_initialize;
+}
+
+static AtkObject *
+gail_canvas_item_get_parent (AtkObject *obj)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GObject *g_obj;
+ GnomeCanvasItem *item;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_ITEM (obj), NULL);
+ if (obj->accessible_parent)
+ return obj->accessible_parent;
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (obj);
+ g_obj = atk_gobject_accessible_get_object (atk_gobj);
+ if (g_obj == NULL)
+ /* Object is defunct */
+ return NULL;
+
+ item = GNOME_CANVAS_ITEM (g_obj);
+ if (item->parent)
+ return atk_gobject_accessible_for_object (G_OBJECT (item->parent));
+ else
+ return gtk_widget_get_accessible (GTK_WIDGET (item->canvas));
+}
+
+static gint
+gail_canvas_item_get_index_in_parent (AtkObject *obj)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GObject *g_obj;
+ GnomeCanvasItem *item;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_ITEM (obj), -1);
+ if (obj->accessible_parent)
+ {
+ gint n_children, i;
+ gboolean found = FALSE;
+
+ n_children = atk_object_get_n_accessible_children (obj->accessible_parent);
+ for (i = 0; i < n_children; i++)
+ {
+ AtkObject *child;
+
+ child = atk_object_ref_accessible_child (obj->accessible_parent, i);
+ if (child == obj)
+ found = TRUE;
+
+ g_object_unref (child);
+ if (found)
+ return i;
+ }
+ return -1;
+ }
+
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (obj);
+ g_obj = atk_gobject_accessible_get_object (atk_gobj);
+ if (g_obj == NULL)
+ /* Object is defunct */
+ return -1;
+
+ item = GNOME_CANVAS_ITEM (g_obj);
+ if (item->parent)
+ {
+ return g_list_index (GNOME_CANVAS_GROUP (item->parent)->item_list, item);
+ }
+ else
+ {
+ g_return_val_if_fail (item->canvas->root == item, -1);
+ return 0;
+ }
+}
+
+static AtkStateSet*
+gail_canvas_item_ref_state_set (AtkObject *obj)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GObject *g_obj;
+ GnomeCanvasItem *item;
+ AtkStateSet *state_set;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_ITEM (obj), NULL);
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (obj);
+
+ state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (obj);
+
+ g_obj = atk_gobject_accessible_get_object (atk_gobj);
+ if (g_obj == NULL)
+ {
+ /* Object is defunct */
+ atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
+ }
+ else
+ {
+ item = GNOME_CANVAS_ITEM (g_obj);
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+ {
+ atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
+ if (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
+gail_canvas_item_component_interface_init (AtkComponentIface *iface)
+{
+ g_return_if_fail (iface != NULL);
+
+ iface->add_focus_handler = gail_canvas_item_add_focus_handler;
+ iface->get_extents = gail_canvas_item_get_extents;
+ iface->get_mdi_zorder = gail_canvas_item_get_mdi_zorder;
+ iface->grab_focus = gail_canvas_item_grab_focus;
+ iface->remove_focus_handler = gail_canvas_item_remove_focus_handler;
+}
+
+static guint
+gail_canvas_item_add_focus_handler (AtkComponent *component,
+ AtkFocusHandler handler)
+{
+ GSignalMatchType match_type;
+ gulong ret;
+ guint signal_id;
+
+ match_type = G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC;
+ signal_id = g_signal_lookup ("focus-event", ATK_TYPE_OBJECT);
+
+ ret = g_signal_handler_find (component, match_type, signal_id, 0, NULL,
+ (gpointer) handler, NULL);
+ if (!ret)
+ {
+ return g_signal_connect_closure_by_id (component,
+ signal_id, 0,
+ g_cclosure_new (
+ G_CALLBACK (handler), NULL,
+ (GClosureNotify) NULL),
+ FALSE);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+static void
+gail_canvas_item_get_extents (AtkComponent *component,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height,
+ AtkCoordType coord_type)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GObject *obj;
+ GnomeCanvasItem *item;
+ gint window_x, window_y;
+ gint toplevel_x, toplevel_y;
+ gint local_x, local_y;
+
+ g_return_if_fail (GAIL_IS_CANVAS_ITEM (component));
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
+ obj = atk_gobject_accessible_get_object (atk_gobj);
+
+ if (obj == NULL)
+ /* item is defunct */
+ return;
+
+ /* Get the GnomeCanvasItem */
+ item = GNOME_CANVAS_ITEM (obj);
+
+ /* If this item has no parent canvas, something's broken */
+ g_return_if_fail (GTK_IS_WIDGET (item->canvas));
+
+ get_item_extents (item, &local_x, &local_y, width, height);
+ if (!is_item_in_window (item, local_x, local_y, *width, *height))
+ {
+ *x = G_MININT;
+ *y = G_MININT;
+ return;
+ }
+
+ gail_misc_get_origins (GTK_WIDGET (item->canvas), &window_x, &window_y,
+ &toplevel_x, &toplevel_y);
+ *x = local_x + window_x - toplevel_x;
+ *y = local_y + window_y - toplevel_y;
+
+ /* If screen coordinates are requested, modify x and y appropriately */
+ if (coord_type == ATK_XY_SCREEN)
+ {
+ *x += toplevel_x;
+ *y += toplevel_y;
+ }
+ return;
+}
+
+static gint
+gail_canvas_item_get_mdi_zorder (AtkComponent *component)
+{
+ g_return_val_if_fail (ATK_OBJECT (component), -1);
+
+ return gail_canvas_item_get_index_in_parent (ATK_OBJECT (component));
+}
+
+static gboolean
+gail_canvas_item_grab_focus (AtkComponent *component)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GObject *obj;
+ GnomeCanvasItem *item;
+ GtkWidget *toplevel;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_ITEM (component), FALSE);
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
+ obj = atk_gobject_accessible_get_object (atk_gobj);
+
+ /* Get the GnomeCanvasItem */
+ item = GNOME_CANVAS_ITEM (obj);
+ if (item == NULL)
+ /* item is defunct */
+ return FALSE;
+
+ gnome_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
+gail_canvas_item_remove_focus_handler (AtkComponent *component,
+ guint handler_id)
+{
+ g_signal_handler_disconnect (ATK_OBJECT (component), handler_id);
+}
+
+static gboolean
+is_item_on_screen (GnomeCanvasItem *item)
+{
+ gint x, y, width, height;
+
+ get_item_extents (item, &x, &y, &width, &height);
+ return is_item_in_window (item, x, y, width, height);
+}
+
+static void
+get_item_extents (GnomeCanvasItem *item,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height)
+{
+ double bx1, by1, bx2, by2;
+ double i2c[6];
+ ArtPoint p1, p2, p3, p4;
+ ArtPoint q1, q2, q3, q4;
+ double min_x1, min_y1, min_x2, min_y2;
+ double max_x1, max_y1, max_x2, max_y2;
+ int x1, y1, x2, y2;
+ int scroll_x, scroll_y;
+
+ /* Get the bounding box in item-relative coordinates */
+
+ bx1 = by1 = bx2 = by2 = 0.0;
+
+ if (GNOME_CANVAS_ITEM_CLASS (G_OBJECT_GET_CLASS (item))->bounds)
+ (* GNOME_CANVAS_ITEM_CLASS (G_OBJECT_GET_CLASS (item))->bounds) (item, &bx1, &by1, &bx2, &by2);
+
+ /* Get the item coordinates -> canvas pixel coordinates affine */
+
+ gnome_canvas_item_i2c_affine (item, i2c);
+
+ /* Convert the bounding box to canvas pixel coordinates and find its minimum
+ * surrounding rectangle.
+ */
+
+ p1.x = p2.x = bx1;
+ p1.y = p4.y = by1;
+ p3.x = p4.x = bx2;
+ p2.y = p3.y = by2;
+
+ art_affine_point (&q1, &p1, i2c);
+ art_affine_point (&q2, &p2, i2c);
+ art_affine_point (&q3, &p3, i2c);
+ art_affine_point (&q4, &p4, i2c);
+
+ if (q1.x < q2.x)
+ {
+ min_x1 = q1.x;
+ max_x1 = q2.x;
+ }
+ else
+ {
+ min_x1 = q2.x;
+ max_x1 = q1.x;
+ }
+
+ if (q1.y < q2.y)
+ {
+ min_y1 = q1.y;
+ max_y1 = q2.y;
+ }
+ else
+ {
+ min_y1 = q2.y;
+ max_y1 = q1.y;
+ }
+
+ if (q3.x < q4.x)
+ {
+ min_x2 = q3.x;
+ max_x2 = q4.x;
+ }
+ else
+ {
+ min_x2 = q4.x;
+ max_x2 = q3.x;
+ }
+
+ if (q3.y < q4.y)
+ {
+ min_y2 = q3.y;
+ max_y2 = q4.y;
+ }
+ else
+ {
+ min_y2 = q4.y;
+ max_y2 = q3.y;
+ }
+
+ bx1 = MIN (min_x1, min_x2);
+ by1 = MIN (min_y1, min_y2);
+ bx2 = MAX (max_x1, max_x2);
+ by2 = MAX (max_y1, max_y2);
+
+ /* Convert to integer coordinates */
+
+ x1 = floor (bx1);
+ y1 = floor (by1);
+ x2 = ceil (bx2);
+ y2 = ceil (by2);
+
+ gnome_canvas_get_scroll_offsets (item->canvas, &scroll_x, &scroll_y);
+
+ if (x)
+ *x = x1 - scroll_x;
+
+ if (y)
+ *y = y1 - scroll_y;
+
+ if (width)
+ *width = x2 - x1;
+
+ if (height)
+ *height = y2 - y1;
+}
+
+static gboolean
+is_item_in_window (GnomeCanvasItem *item,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ 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 (x + width <= 0 ||
+ y + height <= 0 ||
+ x > window_width ||
+ y > window_height)
+ {
+ retval = FALSE;
+ }
+ else
+ {
+ retval = TRUE;
+ }
+ }
+ else
+ {
+ retval = FALSE;
+ }
+ return retval;
+}
diff --git a/libgnomecanvas/gailcanvasitem.h b/libgnomecanvas/gailcanvasitem.h
new file mode 100644
index 0000000000..9f89d7bbc3
--- /dev/null
+++ b/libgnomecanvas/gailcanvasitem.h
@@ -0,0 +1,54 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * 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 __GAIL_CANVAS_ITEM_H__
+#define __GAIL_CANVAS_ITEM_H__
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <atk/atk.h>
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS_ITEM (gail_canvas_item_get_type ())
+#define GAIL_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_ITEM, GailCanvasItem))
+#define GAIL_CANVAS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_ITEM, GailCanvasItemClass))
+#define GAIL_IS_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_ITEM))
+#define GAIL_IS_CANVAS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_ITEM))
+#define GAIL_CANVAS_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_ITEM, GailCanvasItemClass))
+
+typedef struct _GailCanvasItem GailCanvasItem;
+typedef struct _GailCanvasItemClass GailCanvasItemClass;
+
+struct _GailCanvasItem
+{
+ AtkGObjectAccessible parent;
+};
+
+GType gail_canvas_item_get_type (void);
+
+struct _GailCanvasItemClass
+{
+ AtkGObjectAccessibleClass parent_class;
+};
+
+AtkObject* gail_canvas_item_new (GObject *obj);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_ITEM_H__ */
diff --git a/libgnomecanvas/gailcanvasitemfactory.c b/libgnomecanvas/gailcanvasitemfactory.c
new file mode 100644
index 0000000000..5f59ef2823
--- /dev/null
+++ b/libgnomecanvas/gailcanvasitemfactory.c
@@ -0,0 +1,57 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 <gtk/gtk.h>
+#include "gailcanvasitemfactory.h"
+#include "gailcanvasitem.h"
+
+static AtkObject* gail_canvas_item_factory_create_accessible (GObject *obj);
+
+static GType gail_canvas_item_factory_get_accessible_type (void);
+
+G_DEFINE_TYPE (GailCanvasItemFactory,
+ gail_canvas_item_factory,
+ ATK_TYPE_OBJECT_FACTORY);
+
+static void
+gail_canvas_item_factory_init (GailCanvasItemFactory *foo)
+{
+ ;
+}
+
+static void
+gail_canvas_item_factory_class_init (GailCanvasItemFactoryClass *klass)
+{
+ AtkObjectFactoryClass *class = ATK_OBJECT_FACTORY_CLASS (klass);
+
+ class->create_accessible = gail_canvas_item_factory_create_accessible;
+ class->get_accessible_type = gail_canvas_item_factory_get_accessible_type;
+}
+
+static AtkObject*
+gail_canvas_item_factory_create_accessible (GObject *obj)
+{
+ return gail_canvas_item_new (obj);
+}
+
+static GType
+gail_canvas_item_factory_get_accessible_type (void)
+{
+ return GAIL_TYPE_CANVAS_ITEM;
+}
diff --git a/libgnomecanvas/gailcanvasitemfactory.h b/libgnomecanvas/gailcanvasitemfactory.h
new file mode 100644
index 0000000000..dd55815cbe
--- /dev/null
+++ b/libgnomecanvas/gailcanvasitemfactory.h
@@ -0,0 +1,53 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * 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 __GAIL_CANVAS_ITEM_FACTORY_H__
+#define __GAIL_CANVAS_ITEM_FACTORY_H__
+
+#include <atk/atkobjectfactory.h>
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS_ITEM_FACTORY (gail_canvas_item_factory_get_type ())
+#define GAIL_CANVAS_ITEM_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_ITEM_FACTORY, GailCanvasItemFactory))
+#define GAIL_CANVAS_ITEM_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_ITEM_FACTORY, GailCanvasItemFactoryClass))
+#define GAIL_IS_CANVAS_ITEM_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_ITEM_FACTORY))
+#define GAIL_IS_CANVAS_ITEM_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_ITEM_FACTORY))
+#define GAIL_CANVAS_ITEM_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_ITEM_FACTORY, GailCanvasItemFactoryClass))
+
+
+typedef struct _GailCanvasItemFactory GailCanvasItemFactory;
+typedef struct _GailCanvasItemFactoryClass GailCanvasItemFactoryClass;
+
+struct _GailCanvasItemFactory
+{
+ AtkObjectFactory parent;
+};
+
+struct _GailCanvasItemFactoryClass
+{
+ AtkObjectFactoryClass parent_class;
+};
+
+GType gail_canvas_item_factory_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_ITEM_FACTORY_H__ */
+
diff --git a/libgnomecanvas/gailcanvastext.c b/libgnomecanvas/gailcanvastext.c
new file mode 100644
index 0000000000..542683c6c3
--- /dev/null
+++ b/libgnomecanvas/gailcanvastext.c
@@ -0,0 +1,522 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 <gtk/gtk.h>
+#include <libgnomecanvas/libgnomecanvas.h>
+#include "gailcanvasitem.h"
+#include "gailcanvastext.h"
+#include <libgail-util/gail-util.h>
+
+struct _GailCanvasText
+{
+ GailCanvasItem parent;
+ GailTextUtil *textutil;
+};
+
+static void gail_canvas_text_text_interface_init (AtkTextIface *iface);
+static gchar* gail_canvas_text_get_text (AtkText *text,
+ gint start_offset,
+ gint end_offset);
+static gchar* gail_canvas_text_get_text_after_offset
+ (AtkText *text,
+ gint offset,
+ AtkTextBoundary boundary_type,
+ gint *start_offset,
+ gint *end_offset);
+static gchar* gail_canvas_text_get_text_at_offset (AtkText *text,
+ gint offset,
+ AtkTextBoundary boundary_type,
+ gint *start_offset,
+ gint *end_offset);
+static gchar* gail_canvas_text_get_text_before_offset
+ (AtkText *text,
+ gint offset,
+ AtkTextBoundary boundary_type,
+ gint *start_offset,
+ gint *end_offset);
+static gunichar gail_canvas_text_get_character_at_offset
+ (AtkText *text,
+ gint offset);
+static gint gail_canvas_text_get_character_count (AtkText *text);
+static gint gail_canvas_text_get_caret_offset (AtkText *text);
+static gboolean gail_canvas_text_set_caret_offset (AtkText *text,
+ gint offset);
+static gint gail_canvas_text_get_offset_at_point (AtkText *text,
+ gint x,
+ gint y,
+ AtkCoordType coords);
+static void gail_canvas_text_get_character_extents (AtkText *text,
+ gint offset,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height,
+ AtkCoordType coords);
+static AtkAttributeSet*
+ gail_canvas_text_get_run_attributes (AtkText *text,
+ gint offset,
+ gint *start_offset,
+ gint *end_offset);
+static AtkAttributeSet*
+ gail_canvas_text_get_default_attributes (AtkText *text);
+static gint gail_canvas_text_get_n_selections (AtkText *text);
+static gchar* gail_canvas_text_get_selection (AtkText *text,
+ gint selection_num,
+ gint *start_pos,
+ gint *end_pos);
+static gboolean gail_canvas_text_add_selection (AtkText *text,
+ gint start_pos,
+ gint end_pos);
+static gboolean gail_canvas_text_remove_selection (AtkText *text,
+ gint selection_num);
+static gboolean gail_canvas_text_set_selection (AtkText *text,
+ gint selection_num,
+ gint start_pos,
+ gint end_pos);
+static gchar* get_text_near_offset (AtkText *text,
+ GailOffsetType function,
+ AtkTextBoundary boundary_type,
+ gint offset,
+ gint *start_offset,
+ gint *end_offset);
+
+G_DEFINE_TYPE_WITH_CODE(GailCanvasText,
+ gail_canvas_text,
+ GAIL_TYPE_CANVAS_ITEM,
+ G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT,
+ gail_canvas_text_text_interface_init);)
+
+static void
+gail_canvas_text_init (GailCanvasText *foo)
+{
+ ;
+}
+
+AtkObject*
+gail_canvas_text_new (GObject *obj)
+{
+ gpointer object;
+ AtkObject *atk_object;
+ GailCanvasText *gail_text;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS_ITEM (obj), NULL);
+ object = g_object_new (GAIL_TYPE_CANVAS_TEXT, NULL);
+ atk_object = ATK_OBJECT (object);
+ gail_text = GAIL_CANVAS_TEXT (object);
+
+ atk_object_initialize (atk_object, obj);
+ gail_text->textutil = gail_text_util_new ();
+
+ if (GNOME_IS_CANVAS_RICH_TEXT (obj))
+ {
+ gail_text_util_buffer_setup (gail_text->textutil,
+ gnome_canvas_rich_text_get_buffer (GNOME_CANVAS_RICH_TEXT (obj)));
+ }
+ else if (GNOME_IS_CANVAS_TEXT (obj))
+ {
+ gail_text_util_text_setup (gail_text->textutil,
+ GNOME_CANVAS_TEXT (obj)->text);
+ }
+
+ atk_object->role = ATK_ROLE_TEXT;
+ return atk_object;
+}
+
+static void
+gail_canvas_text_class_init (GailCanvasTextClass *klass)
+{
+}
+
+static void
+gail_canvas_text_text_interface_init (AtkTextIface *iface)
+{
+ g_return_if_fail (iface != NULL);
+
+ iface->get_text = gail_canvas_text_get_text;
+ iface->get_text_after_offset = gail_canvas_text_get_text_after_offset;
+ iface->get_text_at_offset = gail_canvas_text_get_text_at_offset;
+ iface->get_text_before_offset = gail_canvas_text_get_text_before_offset;
+ iface->get_character_at_offset = gail_canvas_text_get_character_at_offset;
+ iface->get_character_count = gail_canvas_text_get_character_count;
+ iface->get_caret_offset = gail_canvas_text_get_caret_offset;
+ iface->set_caret_offset = gail_canvas_text_set_caret_offset;
+ iface->get_offset_at_point = gail_canvas_text_get_offset_at_point;
+ iface->get_character_extents = gail_canvas_text_get_character_extents;
+ iface->get_n_selections = gail_canvas_text_get_n_selections;
+ iface->get_selection = gail_canvas_text_get_selection;
+ iface->add_selection = gail_canvas_text_add_selection;
+ iface->remove_selection = gail_canvas_text_remove_selection;
+ iface->set_selection = gail_canvas_text_set_selection;
+ iface->get_run_attributes = gail_canvas_text_get_run_attributes;
+ iface->get_default_attributes = gail_canvas_text_get_default_attributes;
+}
+
+static gchar*
+gail_canvas_text_get_text (AtkText *text,
+ gint start_offset,
+ gint end_offset)
+{
+ GailCanvasText *gail_text;
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), NULL);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, NULL);
+
+ buffer = gail_text->textutil->buffer;
+ gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
+ gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset);
+
+ return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+}
+
+static gchar*
+gail_canvas_text_get_text_after_offset (AtkText *text,
+ gint offset,
+ AtkTextBoundary boundary_type,
+ gint *start_offset,
+ gint *end_offset)
+{
+ return get_text_near_offset (text, GAIL_AFTER_OFFSET,
+ boundary_type, offset,
+ start_offset, end_offset);
+}
+
+static gchar*
+gail_canvas_text_get_text_at_offset (AtkText *text,
+ gint offset,
+ AtkTextBoundary boundary_type,
+ gint *start_offset,
+ gint *end_offset)
+{
+ return get_text_near_offset (text, GAIL_AT_OFFSET,
+ boundary_type, offset,
+ start_offset, end_offset);
+}
+
+static gchar*
+gail_canvas_text_get_text_before_offset (AtkText *text,
+ gint offset,
+ AtkTextBoundary boundary_type,
+ gint *start_offset,
+ gint *end_offset)
+{
+ return get_text_near_offset (text, GAIL_BEFORE_OFFSET,
+ boundary_type, offset,
+ start_offset, end_offset);
+}
+
+static gunichar
+gail_canvas_text_get_character_at_offset (AtkText *text,
+ gint offset)
+{
+ GailCanvasText *gail_item;
+ GtkTextIter start, end;
+ GtkTextBuffer *buffer;
+ gchar *string;
+ gchar *index;
+ gunichar unichar;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), '\0');
+ gail_item = GAIL_CANVAS_TEXT (text);
+ buffer = gail_item->textutil->buffer;
+ if (offset >= gtk_text_buffer_get_char_count (buffer))
+ return '\0';
+
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+ string = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+ index = g_utf8_offset_to_pointer (string, offset);
+
+ unichar = g_utf8_get_char (index);
+ g_free (string);
+ return unichar;
+}
+
+static gint
+gail_canvas_text_get_character_count (AtkText *text)
+{
+ GtkTextBuffer *buffer;
+ GailCanvasText *gail_text;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), 0);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, 0);
+ buffer = gail_text->textutil->buffer;
+ return gtk_text_buffer_get_char_count (buffer);
+}
+
+static gint
+gail_canvas_text_get_caret_offset (AtkText *text)
+{
+ GailCanvasText *gail_text;
+ GtkTextBuffer *buffer;
+ GtkTextMark *cursor_mark;
+ GtkTextIter cursor_itr;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), 0);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, 0);
+ buffer = gail_text->textutil->buffer;
+ cursor_mark = gtk_text_buffer_get_insert (buffer);
+ gtk_text_buffer_get_iter_at_mark (buffer, &cursor_itr, cursor_mark);
+ return gtk_text_iter_get_offset (&cursor_itr);
+}
+
+static gboolean
+gail_canvas_text_set_caret_offset (AtkText *text,
+ gint offset)
+{
+ GailCanvasText *gail_text;
+ GtkTextBuffer *buffer;
+ GtkTextIter pos_itr;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), FALSE);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, FALSE);
+ buffer = gail_text->textutil->buffer;
+ gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, offset);
+ gtk_text_buffer_move_mark_by_name (buffer, "insert", &pos_itr);
+ return TRUE;
+}
+
+static gint
+gail_canvas_text_get_offset_at_point (AtkText *text,
+ gint x,
+ gint y,
+ AtkCoordType coords)
+{
+ return -1;
+}
+
+static void
+gail_canvas_text_get_character_extents (AtkText *text,
+ gint offset,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height,
+ AtkCoordType coords)
+{
+ return;
+}
+
+static AtkAttributeSet*
+gail_canvas_text_get_run_attributes (AtkText *text,
+ gint offset,
+ gint *start_offset,
+ gint *end_offset)
+{
+ GailCanvasText *gail_text;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), NULL);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, NULL);
+
+ return gail_misc_buffer_get_run_attributes (gail_text->textutil->buffer,
+ offset, start_offset, end_offset);
+}
+
+static AtkAttributeSet*
+gail_canvas_text_get_default_attributes (AtkText *text)
+{
+ return NULL;
+}
+
+static gint
+gail_canvas_text_get_n_selections (AtkText *text)
+{
+ GailCanvasText *gail_text;
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+ gint select_start, select_end;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), -1);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, -1);
+ buffer = gail_text->textutil->buffer;
+
+ gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+ select_start = gtk_text_iter_get_offset (&start);
+ select_end = gtk_text_iter_get_offset (&end);
+
+ if (select_start != select_end)
+ return 1;
+ else
+ return 0;
+}
+
+static gchar*
+gail_canvas_text_get_selection (AtkText *text,
+ gint selection_num,
+ gint *start_pos,
+ gint *end_pos)
+{
+ GailCanvasText *gail_text;
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+
+ /* Only let the user get the selection if one is set, and if the
+ * selection_num is 0.
+ */
+ if (selection_num != 0)
+ return NULL;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), NULL);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, NULL);
+ buffer = gail_text->textutil->buffer;
+
+ gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+ *start_pos = gtk_text_iter_get_offset (&start);
+ *end_pos = gtk_text_iter_get_offset (&end);
+
+ if (*start_pos != *end_pos)
+ return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+ else
+ return NULL;
+}
+
+static gboolean
+gail_canvas_text_add_selection (AtkText *text,
+ gint start_pos,
+ gint end_pos)
+{
+ GailCanvasText *gail_text;
+ GtkTextBuffer *buffer;
+ GtkTextIter pos_itr;
+ GtkTextIter start, end;
+ gint select_start, select_end;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), FALSE);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, FALSE);
+ buffer = gail_text->textutil->buffer;
+
+ gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+ select_start = gtk_text_iter_get_offset (&start);
+ select_end = gtk_text_iter_get_offset (&end);
+
+ /* If there is already a selection, then don't allow another to be added,
+ * since GtkTextView only supports one selected region.
+ */
+ if (select_start == select_end)
+ {
+ gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, start_pos);
+ gtk_text_buffer_move_mark_by_name (buffer, "insert", &pos_itr);
+ gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, end_pos);
+ gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &pos_itr);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static gboolean
+gail_canvas_text_remove_selection (AtkText *text,
+ gint selection_num)
+{
+ GailCanvasText *gail_text;
+ GtkTextBuffer *buffer;
+ GtkTextMark *cursor_mark;
+ GtkTextIter cursor_itr;
+ GtkTextIter start, end;
+ gint select_start, select_end;
+
+ if (selection_num != 0)
+ return FALSE;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), FALSE);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, FALSE);
+ buffer = gail_text->textutil->buffer;
+
+ gtk_text_buffer_get_selection_bounds(buffer, &start, &end);
+ select_start = gtk_text_iter_get_offset(&start);
+ select_end = gtk_text_iter_get_offset(&end);
+
+ if (select_start != select_end)
+ {
+ /* Setting the start & end of the selected region to the caret position
+ * turns off the selection.
+ */
+ cursor_mark = gtk_text_buffer_get_insert (buffer);
+ gtk_text_buffer_get_iter_at_mark (buffer, &cursor_itr, cursor_mark);
+ gtk_text_buffer_move_mark_by_name (buffer, "insert", &cursor_itr);
+ gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &cursor_itr);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+
+static gboolean
+gail_canvas_text_set_selection (AtkText *text,
+ gint selection_num,
+ gint start_pos,
+ gint end_pos)
+{
+ GailCanvasText *gail_text;
+ GtkTextBuffer *buffer;
+ GtkTextIter pos_itr;
+ GtkTextIter start, end;
+ gint select_start, select_end;
+
+ /* Only let the user move the selection if one is set, and if the
+ * selection_num is 0
+ */
+ if (selection_num != 0)
+ return FALSE;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), FALSE);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, FALSE);
+ buffer = gail_text->textutil->buffer;
+
+ gtk_text_buffer_get_selection_bounds(buffer, &start, &end);
+ select_start = gtk_text_iter_get_offset(&start);
+ select_end = gtk_text_iter_get_offset(&end);
+
+ if (select_start != select_end)
+ {
+ gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, start_pos);
+ gtk_text_buffer_move_mark_by_name (buffer, "insert", &pos_itr);
+ gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, end_pos);
+ gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &pos_itr);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static gchar*
+get_text_near_offset (AtkText *text,
+ GailOffsetType function,
+ AtkTextBoundary boundary_type,
+ gint offset,
+ gint *start_offset,
+ gint *end_offset)
+{
+ return gail_text_util_get_text (GAIL_CANVAS_TEXT (text)->textutil, NULL,
+ function, boundary_type, offset,
+ start_offset, end_offset);
+}
diff --git a/libgnomecanvas/gailcanvastext.h b/libgnomecanvas/gailcanvastext.h
new file mode 100644
index 0000000000..54a0239cc2
--- /dev/null
+++ b/libgnomecanvas/gailcanvastext.h
@@ -0,0 +1,52 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * 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 __GAIL_CANVAS_TEXT_H__
+#define __GAIL_CANVAS_TEXT_H__
+
+#include <libgnomecanvas/libgnomecanvas.h>
+#include <atk/atk.h>
+#include "gailcanvasitem.h"
+#include <libgail-util/gailtextutil.h>
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS_TEXT (gail_canvas_text_get_type ())
+#define GAIL_CANVAS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_TEXT, GailCanvasText))
+#define GAIL_CANVAS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_TEXT, GailCanvasTextClass))
+#define GAIL_IS_CANVAS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_TEXT))
+#define GAIL_IS_CANVAS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_TEXT))
+#define GAIL_CANVAS_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_TEXT, GailCanvasTextClass))
+
+typedef struct _GailCanvasText GailCanvasText;
+typedef struct _GailCanvasTextClass GailCanvasTextClass;
+
+GType
+gail_canvas_text_get_type (void);
+
+struct _GailCanvasTextClass
+{
+ GailCanvasItemClass parent_class;
+};
+
+AtkObject* gail_canvas_text_new (GObject *obj);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_TEXT_H__ */
diff --git a/libgnomecanvas/gailcanvastextfactory.c b/libgnomecanvas/gailcanvastextfactory.c
new file mode 100644
index 0000000000..987e385530
--- /dev/null
+++ b/libgnomecanvas/gailcanvastextfactory.c
@@ -0,0 +1,57 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 <gtk/gtk.h>
+#include "gailcanvastextfactory.h"
+#include "gailcanvastext.h"
+
+static AtkObject * gail_canvas_text_factory_create_accessible (GObject *obj);
+
+static GType gail_canvas_text_factory_get_accessible_type (void);
+
+G_DEFINE_TYPE (GailCanvasTextFactory,
+ gail_canvas_text_factory,
+ ATK_TYPE_OBJECT_FACTORY);
+
+static void
+gail_canvas_text_factory_init (GailCanvasTextFactory *foo)
+{
+ ;
+}
+
+static void
+gail_canvas_text_factory_class_init (GailCanvasTextFactoryClass *klass)
+{
+ AtkObjectFactoryClass *class = ATK_OBJECT_FACTORY_CLASS (klass);
+
+ class->create_accessible = gail_canvas_text_factory_create_accessible;
+ class->get_accessible_type = gail_canvas_text_factory_get_accessible_type;
+}
+
+static AtkObject*
+gail_canvas_text_factory_create_accessible (GObject *obj)
+{
+ return gail_canvas_text_new (obj);
+}
+
+static GType
+gail_canvas_text_factory_get_accessible_type (void)
+{
+ return GAIL_TYPE_CANVAS_TEXT;
+}
diff --git a/libgnomecanvas/gailcanvastextfactory.h b/libgnomecanvas/gailcanvastextfactory.h
new file mode 100644
index 0000000000..3d99f778e5
--- /dev/null
+++ b/libgnomecanvas/gailcanvastextfactory.h
@@ -0,0 +1,53 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * 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 __GAIL_CANVAS_TEXT_FACTORY_H__
+#define __GAIL_CANVAS_TEXT_FACTORY_H__
+
+#include <atk/atkobjectfactory.h>
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS_TEXT_FACTORY (gail_canvas_text_factory_get_type ())
+#define GAIL_CANVAS_TEXT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_TEXT_FACTORY, GailCanvasTextFactory))
+#define GAIL_CANVAS_TEXT_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_TEXT_FACTORY, GailCanvasTextFactoryClass))
+#define GAIL_IS_CANVAS_TEXT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_TEXT_FACTORY))
+#define GAIL_IS_CANVAS_TEXT_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_TEXT_FACTORY))
+#define GAIL_CANVAS_TEXT_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_TEXT_FACTORY, GailCanvasTextFactoryClass))
+
+
+typedef struct _GailCanvasTextFactory GailCanvasTextFactory;
+typedef struct _GailCanvasTextFactoryClass GailCanvasTextFactoryClass;
+
+struct _GailCanvasTextFactory
+{
+ AtkObjectFactory parent;
+};
+
+struct _GailCanvasTextFactoryClass
+{
+ AtkObjectFactoryClass parent_class;
+};
+
+GType gail_canvas_text_factory_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_TEXT_FACTORY_H__ */
+
diff --git a/libgnomecanvas/gailcanvaswidget.c b/libgnomecanvas/gailcanvaswidget.c
new file mode 100644
index 0000000000..c3d38f0699
--- /dev/null
+++ b/libgnomecanvas/gailcanvaswidget.c
@@ -0,0 +1,113 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 <gtk/gtk.h>
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-widget.h>
+#include "gailcanvaswidget.h"
+
+static gint gail_canvas_widget_get_n_children (AtkObject *obj);
+static AtkObject* gail_canvas_widget_ref_child (AtkObject *obj,
+ gint i);
+
+G_DEFINE_TYPE (GailCanvasWidget,
+ gail_canvas_widget,
+ GAIL_TYPE_CANVAS_ITEM);
+
+static void
+gail_canvas_widget_init (GailCanvasWidget *foo)
+{
+ ;
+}
+
+AtkObject*
+gail_canvas_widget_new (GObject *obj)
+{
+ gpointer object;
+ AtkObject *atk_object;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS_WIDGET (obj), NULL);
+ object = g_object_new (GAIL_TYPE_CANVAS_WIDGET, NULL);
+ atk_object = ATK_OBJECT (object);
+ atk_object_initialize (atk_object, obj);
+ atk_object->role = ATK_ROLE_PANEL;
+ return atk_object;
+}
+
+static void
+gail_canvas_widget_class_init (GailCanvasWidgetClass *klass)
+{
+ AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
+
+ class->get_n_children = gail_canvas_widget_get_n_children;
+ class->ref_child = gail_canvas_widget_ref_child;
+}
+
+static gint
+gail_canvas_widget_get_n_children (AtkObject *obj)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GnomeCanvasWidget *canvas_widget;
+ GObject *g_obj;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_WIDGET (obj), 0);
+
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (obj);
+ g_obj = atk_gobject_accessible_get_object (atk_gobj);
+ if (g_obj == NULL)
+ /* State is defunct */
+ return 0;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS_WIDGET (g_obj), 0);
+
+ canvas_widget = GNOME_CANVAS_WIDGET (g_obj);
+ g_return_val_if_fail (canvas_widget->widget, 0);
+ return 1;
+}
+
+static AtkObject *
+gail_canvas_widget_ref_child (AtkObject *obj,
+ gint i)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GnomeCanvasWidget *canvas_widget;
+ GObject *g_obj;
+ AtkObject *atk_child;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_WIDGET (obj), NULL);
+
+ if (i != 0)
+ return NULL;
+
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (obj);
+ g_obj = atk_gobject_accessible_get_object (atk_gobj);
+ if (g_obj == NULL)
+ /* State is defunct */
+ return NULL;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS_WIDGET (g_obj), NULL);
+
+ canvas_widget = GNOME_CANVAS_WIDGET (g_obj);
+ g_return_val_if_fail (canvas_widget->widget, NULL);
+
+ atk_child = gtk_widget_get_accessible (canvas_widget->widget);
+ g_object_ref (atk_child);
+ atk_object_set_parent (atk_child, obj);
+ return atk_child;
+}
diff --git a/libgnomecanvas/gailcanvaswidget.h b/libgnomecanvas/gailcanvaswidget.h
new file mode 100644
index 0000000000..016c7a7313
--- /dev/null
+++ b/libgnomecanvas/gailcanvaswidget.h
@@ -0,0 +1,55 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * 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 __GAIL_CANVAS_WIDGET_H__
+#define __GAIL_CANVAS_WIDGET_H__
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <atk/atk.h>
+#include "gailcanvasitem.h"
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS_WIDGET (gail_canvas_widget_get_type ())
+#define GAIL_CANVAS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_WIDGET, GailCanvasWidget))
+#define GAIL_CANVAS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_WIDGET, GailCanvasWidgetClass))
+#define GAIL_IS_CANVAS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_WIDGET))
+#define GAIL_IS_CANVAS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_WIDGET))
+#define GAIL_CANVAS_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_WIDGET, GailCanvasWidgetClass))
+
+typedef struct _GailCanvasWidget GailCanvasWidget;
+typedef struct _GailCanvasWidgetClass GailCanvasWidgetClass;
+
+struct _GailCanvasWidget
+{
+ GailCanvasItem parent;
+};
+
+GType gail_canvas_widget_get_type (void);
+
+struct _GailCanvasWidgetClass
+{
+ GailCanvasItemClass parent_class;
+};
+
+AtkObject* gail_canvas_widget_new (GObject *obj);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_WIDGET_H__ */
diff --git a/libgnomecanvas/gailcanvaswidgetfactory.c b/libgnomecanvas/gailcanvaswidgetfactory.c
new file mode 100644
index 0000000000..5923c165d2
--- /dev/null
+++ b/libgnomecanvas/gailcanvaswidgetfactory.c
@@ -0,0 +1,56 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 "gailcanvaswidgetfactory.h"
+#include "gailcanvaswidget.h"
+
+static AtkObject * gail_canvas_widget_factory_create_accessible (GObject *obj);
+
+static GType gail_canvas_widget_factory_get_accessible_type (void);
+
+G_DEFINE_TYPE (GailCanvasWidgetFactory,
+ gail_canvas_widget_factory,
+ ATK_TYPE_OBJECT_FACTORY);
+
+static void
+gail_canvas_widget_factory_init (GailCanvasWidgetFactory *foo)
+{
+ ;
+}
+
+static void
+gail_canvas_widget_factory_class_init (GailCanvasWidgetFactoryClass *klass)
+{
+ AtkObjectFactoryClass *class = ATK_OBJECT_FACTORY_CLASS (klass);
+
+ class->create_accessible = gail_canvas_widget_factory_create_accessible;
+ class->get_accessible_type = gail_canvas_widget_factory_get_accessible_type;
+}
+
+static AtkObject*
+gail_canvas_widget_factory_create_accessible (GObject *obj)
+{
+ return gail_canvas_widget_new (obj);
+}
+
+static GType
+gail_canvas_widget_factory_get_accessible_type (void)
+{
+ return GAIL_TYPE_CANVAS_WIDGET;
+}
diff --git a/libgnomecanvas/gailcanvaswidgetfactory.h b/libgnomecanvas/gailcanvaswidgetfactory.h
new file mode 100644
index 0000000000..caca8131a2
--- /dev/null
+++ b/libgnomecanvas/gailcanvaswidgetfactory.h
@@ -0,0 +1,53 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * 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 __GAIL_CANVAS_WIDGET_FACTORY_H__
+#define __GAIL_CANVAS_WIDGET_FACTORY_H__
+
+#include <atk/atkobjectfactory.h>
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS_WIDGET_FACTORY (gail_canvas_widget_factory_get_type ())
+#define GAIL_CANVAS_WIDGET_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_WIDGET_FACTORY, GailCanvasWidgetFactory))
+#define GAIL_CANVAS_WIDGET_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_WIDGET_FACTORY, GailCanvasWidgetFactoryClass))
+#define GAIL_IS_CANVAS_WIDGET_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_WIDGET_FACTORY))
+#define GAIL_IS_CANVAS_WIDGET_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_WIDGET_FACTORY))
+#define GAIL_CANVAS_WIDGET_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_WIDGET_FACTORY, GailCanvasWidgetFactoryClass))
+
+
+typedef struct _GailCanvasWidgetFactory GailCanvasWidgetFactory;
+typedef struct _GailCanvasWidgetFactoryClass GailCanvasWidgetFactoryClass;
+
+struct _GailCanvasWidgetFactory
+{
+ AtkObjectFactory parent;
+};
+
+struct _GailCanvasWidgetFactoryClass
+{
+ AtkObjectFactoryClass parent_class;
+};
+
+GType gail_canvas_widget_factory_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_WIDGET_FACTORY_H__ */
+
diff --git a/libgnomecanvas/gnome-canvas-bpath.c b/libgnomecanvas/gnome-canvas-bpath.c
new file mode 100644
index 0000000000..f7f564a94d
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-bpath.c
@@ -0,0 +1,175 @@
+/* Bpath item type for GnomeCanvas widget
+ *
+ * GnomeCanvas 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.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico@nuclecu.unam.mx>
+ * Raph Levien <raph@acm.org>
+ * Lauris Kaplinski <lauris@ximian.com>
+ * Miguel de Icaza <miguel@kernel.org>
+ * Cody Russell <bratsche@gnome.org>
+ * Rusty Conover <rconover@bangtail.net>
+ */
+
+/* These includes are set up for standalone compile. If/when this codebase
+ is integrated into libgnomeui, the includes will need to change. */
+
+#include <math.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+
+#include "gnome-canvas-bpath.h"
+#include "gnome-canvas-shape.h"
+#include "gnome-canvas-shape-private.h"
+#include "gnome-canvas-path-def.h"
+
+enum {
+ PROP_0,
+ PROP_BPATH
+};
+
+static void gnome_canvas_bpath_class_init (GnomeCanvasBpathClass *class);
+static void gnome_canvas_bpath_init (GnomeCanvasBpath *bpath);
+static void gnome_canvas_bpath_destroy (GtkObject *object);
+static void gnome_canvas_bpath_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_bpath_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_bpath_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+
+
+static GnomeCanvasShapeClass *parent_class;
+
+GType
+gnome_canvas_bpath_get_type (void)
+{
+ static GType bpath_type;
+
+ if (!bpath_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasBpathClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_bpath_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasBpath),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_bpath_init,
+ NULL /* value_table */
+ };
+
+ bpath_type = g_type_register_static (GNOME_TYPE_CANVAS_SHAPE, "GnomeCanvasBpath",
+ &object_info, 0);
+ }
+
+ return bpath_type;
+}
+
+static void
+gnome_canvas_bpath_class_init (GnomeCanvasBpathClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ /* when this gets checked into libgnomeui, change the
+ GTK_TYPE_POINTER to GTK_TYPE_GNOME_CANVAS_BPATH, and add an
+ entry to gnome-boxed.defs */
+
+ gobject_class->set_property = gnome_canvas_bpath_set_property;
+ gobject_class->get_property = gnome_canvas_bpath_get_property;
+
+ object_class->destroy = gnome_canvas_bpath_destroy;
+
+ g_object_class_install_property (gobject_class,
+ PROP_BPATH,
+ g_param_spec_boxed ("bpath", NULL, NULL,
+ GNOME_TYPE_CANVAS_PATH_DEF,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ item_class->update = gnome_canvas_bpath_update;
+}
+
+static void
+gnome_canvas_bpath_init (GnomeCanvasBpath *bpath)
+{
+
+}
+
+static void
+gnome_canvas_bpath_destroy (GtkObject *object)
+{
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+gnome_canvas_bpath_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasPathDef *gpp;
+
+ item = GNOME_CANVAS_ITEM (object);
+
+ switch (param_id) {
+ case PROP_BPATH:
+ gpp = (GnomeCanvasPathDef*) g_value_get_boxed (value);
+
+ gnome_canvas_shape_set_path_def (GNOME_CANVAS_SHAPE (object), gpp);
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static void
+gnome_canvas_bpath_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasShape *shape;
+
+ shape = GNOME_CANVAS_SHAPE(object);
+
+ switch (param_id) {
+ case PROP_BPATH:
+ g_value_set_boxed (value, shape->priv->path);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gnome_canvas_bpath_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ if(GNOME_CANVAS_ITEM_CLASS(parent_class)->update) {
+ (* GNOME_CANVAS_ITEM_CLASS(parent_class)->update)(item, affine, clip_path, flags);
+ }
+}
diff --git a/libgnomecanvas/gnome-canvas-bpath.h b/libgnomecanvas/gnome-canvas-bpath.h
new file mode 100644
index 0000000000..42a06a8d19
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-bpath.h
@@ -0,0 +1,61 @@
+/* Bpath item type for GnomeCanvas widget
+ *
+ * GnomeCanvas 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.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico@nuclecu.unam.mx>
+ * Raph Levien <raph@acm.org>
+ * Lauris Kaplinski <lauris@ximian.com>
+ * Rusty Conover <rconover@bangtail.net>
+ */
+
+#ifndef GNOME_CANVAS_BPATH_H
+#define GNOME_CANVAS_BPATH_H
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-shape.h>
+#include <libgnomecanvas/gnome-canvas-path-def.h>
+
+G_BEGIN_DECLS
+
+
+/* Bpath item for the canvas.
+ *
+ * The following object arguments are available:
+ *
+ * name type read/write description
+ * ------------------------------------------------------------------------------------------
+ * bpath GnomeCanvasPathDef * RW Pointer to an GnomeCanvasPathDef structure.
+ * This can be created by a call to
+ * gp_path_new() in (gp-path.h).
+ */
+
+#define GNOME_TYPE_CANVAS_BPATH (gnome_canvas_bpath_get_type ())
+#define GNOME_CANVAS_BPATH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_BPATH, GnomeCanvasBpath))
+#define GNOME_CANVAS_BPATH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_BPATH, GnomeCanvasBpathClass))
+#define GNOME_IS_CANVAS_BPATH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_BPATH))
+#define GNOME_IS_CANVAS_BPATH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_BPATH))
+
+
+typedef struct _GnomeCanvasBpath GnomeCanvasBpath;
+typedef struct _GnomeCanvasBpathPriv GnomeCanvasBpathPriv;
+typedef struct _GnomeCanvasBpathClass GnomeCanvasBpathClass;
+
+struct _GnomeCanvasBpath {
+ GnomeCanvasShape item;
+
+};
+
+struct _GnomeCanvasBpathClass {
+ GnomeCanvasShapeClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_bpath_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-clipgroup.c b/libgnomecanvas/gnome-canvas-clipgroup.c
new file mode 100644
index 0000000000..adfc749e47
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-clipgroup.c
@@ -0,0 +1,450 @@
+#define GNOME_CANVAS_CLIPGROUP_C
+
+/* Clipping group for GnomeCanvas
+ *
+ * GnomeCanvas 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.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@ximian.com>
+ */
+
+/* These includes are set up for standalone compile. If/when this codebase
+ is integrated into libgnomeui, the includes will need to change. */
+
+#include <math.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_bpath.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_vpath_bpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_rect_svp.h>
+#include <libart_lgpl/art_gray_svp.h>
+#include <libart_lgpl/art_svp_intersect.h>
+#include <libart_lgpl/art_svp_ops.h>
+
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+#include "gnome-canvas-clipgroup.h"
+
+enum {
+ PROP_0,
+ PROP_PATH,
+ PROP_WIND
+};
+
+static void gnome_canvas_clipgroup_class_init (GnomeCanvasClipgroupClass *klass);
+static void gnome_canvas_clipgroup_init (GnomeCanvasClipgroup *clipgroup);
+static void gnome_canvas_clipgroup_destroy (GtkObject *object);
+static void gnome_canvas_clipgroup_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_clipgroup_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_clipgroup_update (GnomeCanvasItem *item,
+ double *affine,
+ ArtSVP *clip_path,
+ int flags);
+
+/*
+ * Generic clipping stuff
+ *
+ * This is somewhat slow and memory-hungry - we add extra
+ * composition, extra SVP render and allocate 65536
+ * bytes for each clip level. It could be done more
+ * efficently per-object basis - but to make clipping
+ * universal, there is no alternative to double
+ * buffering (although it should be done into RGBA
+ * buffer by other method than ::render to make global
+ * opacity possible).
+ * Using art-render could possibly optimize that a bit,
+ * although I am not sure.
+ */
+
+#define GCG_BUF_WIDTH 128
+#define GCG_BUF_HEIGHT 128
+#define GCG_BUF_PIXELS (GCG_BUF_WIDTH * GCG_BUF_HEIGHT)
+#define GCG_BUF_SIZE (GCG_BUF_WIDTH * GCG_BUF_HEIGHT * 3)
+
+#define noSHOW_SHADOW
+
+static guchar *gcg_buf_new (void);
+static void gcg_buf_free (guchar *buf);
+static guchar *gcg_mask_new (void);
+static void gcg_mask_free (guchar *mask);
+
+static void gnome_canvas_clipgroup_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+
+static GnomeCanvasGroupClass *parent_class;
+
+GType
+gnome_canvas_clipgroup_get_type (void)
+{
+ static GType clipgroup_type;
+
+ if (!clipgroup_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasClipgroupClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_clipgroup_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasClipgroup),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_clipgroup_init,
+ NULL /* value_table */
+ };
+
+ clipgroup_type = g_type_register_static (GNOME_TYPE_CANVAS_GROUP, "GnomeCanvasClipgroup",
+ &object_info, 0);
+ }
+
+ return clipgroup_type;
+}
+
+static void
+gnome_canvas_clipgroup_class_init (GnomeCanvasClipgroupClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) klass;
+ object_class = (GtkObjectClass *) klass;
+ item_class = (GnomeCanvasItemClass *) klass;
+ parent_class = g_type_class_ref (GNOME_TYPE_CANVAS_GROUP);
+
+ object_class->destroy = gnome_canvas_clipgroup_destroy;
+ gobject_class->set_property = gnome_canvas_clipgroup_set_property;
+ gobject_class->get_property = gnome_canvas_clipgroup_get_property;
+ item_class->update = gnome_canvas_clipgroup_update;
+ item_class->render = gnome_canvas_clipgroup_render;
+
+ g_object_class_install_property (gobject_class,
+ PROP_PATH,
+ g_param_spec_pointer ("path", NULL, NULL,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_WIND,
+ g_param_spec_uint ("wind", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+}
+
+static void
+gnome_canvas_clipgroup_init (GnomeCanvasClipgroup *clipgroup)
+{
+ clipgroup->path = NULL;
+ clipgroup->wind = ART_WIND_RULE_NONZERO; /* default winding rule */
+ clipgroup->svp = NULL;
+}
+
+static void
+gnome_canvas_clipgroup_destroy (GtkObject *object)
+{
+ GnomeCanvasClipgroup *clipgroup;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_CLIPGROUP (object));
+
+ clipgroup = GNOME_CANVAS_CLIPGROUP (object);
+
+ if (clipgroup->path) {
+ gnome_canvas_path_def_unref (clipgroup->path);
+ clipgroup->path = NULL;
+ }
+
+ if (clipgroup->svp) {
+ art_svp_free (clipgroup->svp);
+ clipgroup->svp = NULL;
+ }
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+
+static void
+gnome_canvas_clipgroup_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasClipgroup *cgroup;
+ GnomeCanvasPathDef *gpp;
+
+ item = GNOME_CANVAS_ITEM (object);
+ cgroup = GNOME_CANVAS_CLIPGROUP (object);
+
+ switch (param_id) {
+ case PROP_PATH:
+ gpp = g_value_get_pointer (value);
+
+ if (cgroup->path) {
+ gnome_canvas_path_def_unref (cgroup->path);
+ cgroup->path = NULL;
+ }
+ if (gpp != NULL) {
+ cgroup->path = gnome_canvas_path_def_closed_parts (gpp);
+ }
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIND:
+ cgroup->wind = g_value_get_uint (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+gnome_canvas_clipgroup_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasClipgroup * cgroup;
+
+ cgroup = GNOME_CANVAS_CLIPGROUP (object);
+
+ switch (param_id) {
+ case PROP_PATH:
+ g_value_set_pointer (value, cgroup->path);
+ break;
+
+ case PROP_WIND:
+ g_value_set_uint (value, cgroup->wind);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gnome_canvas_clipgroup_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasClipgroup *clipgroup;
+ ArtSvpWriter *swr;
+ ArtBpath *bp;
+ ArtBpath *bpath;
+ ArtVpath *vpath;
+ ArtSVP *svp, *svp1, *svp2;
+
+ clipgroup = GNOME_CANVAS_CLIPGROUP (item);
+
+ if (clipgroup->svp) {
+ art_svp_free (clipgroup->svp);
+ clipgroup->svp = NULL;
+ }
+
+ if (clipgroup->path) {
+ bp = gnome_canvas_path_def_bpath (clipgroup->path);
+ bpath = art_bpath_affine_transform (bp, affine);
+
+ vpath = art_bez_path_to_vec (bpath, 0.25);
+ art_free (bpath);
+
+ svp1 = art_svp_from_vpath (vpath);
+ art_free (vpath);
+
+ swr = art_svp_writer_rewind_new (clipgroup->wind);
+ art_svp_intersector (svp1, swr);
+
+ svp2 = art_svp_writer_rewind_reap (swr);
+ art_svp_free (svp1);
+
+ if (clip_path != NULL) {
+ svp = art_svp_intersect (svp2, clip_path);
+ art_svp_free (svp2);
+ } else {
+ svp = svp2;
+ }
+
+ clipgroup->svp = svp;
+ }
+
+ if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update)
+ (GNOME_CANVAS_ITEM_CLASS (parent_class)->update) (item, affine, NULL, flags);
+
+ if (clipgroup->svp) {
+ ArtDRect cbox;
+ art_drect_svp (&cbox, clipgroup->svp);
+ item->x1 = MAX (item->x1, cbox.x0 - 1.0);
+ item->y1 = MAX (item->y1, cbox.y0 - 1.0);
+ item->x2 = MIN (item->x2, cbox.x1 + 1.0);
+ item->y2 = MIN (item->y2, cbox.y1 + 1.0);
+ }
+}
+
+/* non-premultiplied composition into RGB */
+
+#define COMPOSEN11(fc,fa,bc) (((255 - (guint) (fa)) * (guint) (bc) + (guint) (fc) * (guint) (fa) + 127) / 255)
+
+static void
+gnome_canvas_clipgroup_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
+{
+ GnomeCanvasClipgroup *cg;
+ GnomeCanvasBuf lbuf;
+ guchar *mask;
+
+ cg = GNOME_CANVAS_CLIPGROUP (item);
+
+ if (cg->svp) {
+ gint bw, bh, sw, sh;
+ gint x, y;
+
+ /* fixme: We could optimize background handling (lauris) */
+
+ if (buf->is_bg) {
+ gnome_canvas_buf_ensure_buf (buf);
+ buf->is_bg = FALSE;
+ buf->is_buf = TRUE;
+ }
+
+ bw = buf->rect.x1 - buf->rect.x0;
+ bh = buf->rect.y1 - buf->rect.y0;
+ if ((bw < 1) || (bh < 1)) return;
+
+ if (bw * bh <= GCG_BUF_PIXELS) {
+ /* We can go with single buffer */
+ sw = bw;
+ sh = bh;
+ } else if (bw <= (GCG_BUF_PIXELS >> 3)) {
+ /* Go with row buffer */
+ sw = bw;
+ sh = GCG_BUF_PIXELS / bw;
+ } else if (bh <= (GCG_BUF_PIXELS >> 3)) {
+ /* Go with column buffer */
+ sw = GCG_BUF_PIXELS / bh;
+ sh = bh;
+ } else {
+ /* Tile buffer */
+ sw = GCG_BUF_WIDTH;
+ sh = GCG_BUF_HEIGHT;
+ }
+
+ /* Set up local buffer */
+ lbuf.buf = gcg_buf_new ();
+ lbuf.bg_color = buf->bg_color;
+ lbuf.is_bg = FALSE;
+ lbuf.is_buf = TRUE;
+ /* Allocate mask */
+ mask = gcg_mask_new ();
+
+ for (y = buf->rect.y0; y < buf->rect.y1; y += sh) {
+ for (x = buf->rect.x0; x < buf->rect.x1; x += sw) {
+ gint r, xx, yy;
+ /* Set up local buffer */
+ lbuf.rect.x0 = x;
+ lbuf.rect.y0 = y;
+ lbuf.rect.x1 = MIN (x + sw, buf->rect.x1);
+ lbuf.rect.y1 = MIN (y + sh, buf->rect.y1);
+ lbuf.buf_rowstride = 3 * (lbuf.rect.x1 - lbuf.rect.x0);
+ /* Copy background */
+ for (r = lbuf.rect.y0; r < lbuf.rect.y1; r++) {
+ memcpy (lbuf.buf + (r - lbuf.rect.y0) * lbuf.buf_rowstride,
+ buf->buf + (r - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3,
+ (lbuf.rect.x1 - lbuf.rect.x0) * 3);
+ }
+ /* Invoke render method */
+ if (((GnomeCanvasItemClass *) parent_class)->render)
+ ((GnomeCanvasItemClass *) parent_class)->render (item, &lbuf);
+ /* Render mask */
+ art_gray_svp_aa (cg->svp, lbuf.rect.x0, lbuf.rect.y0, lbuf.rect.x1, lbuf.rect.y1,
+ mask, lbuf.rect.x1 - lbuf.rect.x0);
+ /* Combine */
+ for (yy = lbuf.rect.y0; yy < lbuf.rect.y1; yy++) {
+ guchar *s, *m, *d;
+ s = lbuf.buf + (yy - lbuf.rect.y0) * lbuf.buf_rowstride;
+ m = mask + (yy - lbuf.rect.y0) * (lbuf.rect.x1 - lbuf.rect.x0);
+ d = buf->buf + (yy - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3;
+ for (xx = lbuf.rect.x0; xx < lbuf.rect.x1; xx++) {
+#ifndef SHOW_SHADOW
+ d[0] = COMPOSEN11 (s[0], m[0], d[0]);
+ d[1] = COMPOSEN11 (s[1], m[0], d[1]);
+ d[2] = COMPOSEN11 (s[2], m[0], d[2]);
+#else
+ d[0] = COMPOSEN11 (s[0], m[0] | 0x7f, d[0]);
+ d[1] = COMPOSEN11 (s[1], m[0] | 0x7f, d[1]);
+ d[2] = COMPOSEN11 (s[2], m[0] | 0x7f, d[2]);
+#endif
+ s += 3;
+ m += 1;
+ d += 3;
+ }
+ }
+ }
+ }
+ /* Free buffers */
+ gcg_mask_free (mask);
+ gcg_buf_free (lbuf.buf);
+ } else {
+ if (((GnomeCanvasItemClass *) parent_class)->render)
+ ((GnomeCanvasItemClass *) parent_class)->render (item, buf);
+ }
+}
+
+static GSList *gcg_buffers = NULL;
+static GSList *gcg_masks = NULL;
+
+static guchar *
+gcg_buf_new (void)
+{
+ guchar *buf;
+
+ if (!gcg_buffers) {
+ buf = g_new (guchar, GCG_BUF_SIZE);
+ } else {
+ buf = (guchar *) gcg_buffers->data;
+ gcg_buffers = g_slist_remove (gcg_buffers, buf);
+ }
+
+ return buf;
+}
+
+static void
+gcg_buf_free (guchar *buf)
+{
+ gcg_buffers = g_slist_prepend (gcg_buffers, buf);
+}
+
+static guchar *
+gcg_mask_new (void)
+{
+ guchar *mask;
+
+ if (!gcg_masks) {
+ mask = g_new (guchar, GCG_BUF_PIXELS);
+ } else {
+ mask = (guchar *) gcg_masks->data;
+ gcg_masks = g_slist_remove (gcg_masks, mask);
+ }
+
+ return mask;
+}
+
+static void
+gcg_mask_free (guchar *mask)
+{
+ gcg_masks = g_slist_prepend (gcg_masks, mask);
+}
diff --git a/libgnomecanvas/gnome-canvas-clipgroup.h b/libgnomecanvas/gnome-canvas-clipgroup.h
new file mode 100644
index 0000000000..e424497daa
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-clipgroup.h
@@ -0,0 +1,58 @@
+#ifndef GNOME_CANVAS_CLIPGROUP_H
+#define GNOME_CANVAS_CLIPGROUP_H
+
+/* Clipping group implementation for GnomeCanvas
+ *
+ * GnomeCanvas 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.
+ *
+ * TODO: Implement this in libgnomeui, possibly merge with real group
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@ximian.com>
+ */
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-util.h>
+
+#include <libart_lgpl/art_bpath.h>
+#include <libart_lgpl/art_svp_wind.h>
+#include <libart_lgpl/art_vpath_dash.h>
+#include <libgnomecanvas/gnome-canvas-path-def.h>
+
+G_BEGIN_DECLS
+
+
+#define GNOME_TYPE_CANVAS_CLIPGROUP (gnome_canvas_clipgroup_get_type ())
+#define GNOME_CANVAS_CLIPGROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_CLIPGROUP, GnomeCanvasClipgroup))
+#define GNOME_CANVAS_CLIPGROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_CLIPGROUP, GnomeCanvasClipgroupClass))
+#define GNOME_IS_CANVAS_CLIPGROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_CLIPGROUP))
+#define GNOME_IS_CANVAS_CLIPGROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_CLIPGROUP))
+
+
+typedef struct _GnomeCanvasClipgroup GnomeCanvasClipgroup;
+typedef struct _GnomeCanvasClipgroupClass GnomeCanvasClipgroupClass;
+
+struct _GnomeCanvasClipgroup {
+ GnomeCanvasGroup group;
+
+ GnomeCanvasPathDef * path;
+ ArtWindRule wind;
+
+ ArtSVP * svp;
+};
+
+struct _GnomeCanvasClipgroupClass {
+ GnomeCanvasGroupClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_clipgroup_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-i18n.h b/libgnomecanvas/gnome-canvas-i18n.h
new file mode 100644
index 0000000000..a768438c5f
--- /dev/null
+++ b/libgnomecanvas/gnome-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 __LIBGNOME_CANVAS_I18N_H__
+#define __LIBGNOME_CANVAS_I18N_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#if !defined(__LIBGNOME_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 /* __LIBGNOME_CANVAS_I18N_H__ */
diff --git a/libgnomecanvas/gnome-canvas-line.c b/libgnomecanvas/gnome-canvas-line.c
new file mode 100644
index 0000000000..48cafb71c7
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-line.c
@@ -0,0 +1,1423 @@
+/*
+ * 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 GnomeCanvas widget
+ *
+ * GnomeCanvas 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 <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_svp_vpath_stroke.h>
+#include "libgnomecanvas.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 gnome_canvas_line_class_init (GnomeCanvasLineClass *class);
+static void gnome_canvas_line_init (GnomeCanvasLine *line);
+static void gnome_canvas_line_destroy (GtkObject *object);
+static void gnome_canvas_line_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_line_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_line_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+static void gnome_canvas_line_realize (GnomeCanvasItem *item);
+static void gnome_canvas_line_unrealize (GnomeCanvasItem *item);
+static void gnome_canvas_line_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height);
+static double gnome_canvas_line_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy, GnomeCanvasItem **actual_item);
+static void gnome_canvas_line_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+static void gnome_canvas_line_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+
+
+static GnomeCanvasItemClass *parent_class;
+
+
+GType
+gnome_canvas_line_get_type (void)
+{
+ static GType line_type;
+
+ if (!line_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasLineClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_line_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasLine),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_line_init,
+ NULL /* value_table */
+ };
+
+ line_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasLine",
+ &object_info, 0);
+ }
+
+ return line_type;
+}
+
+static void
+gnome_canvas_line_class_init (GnomeCanvasLineClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = gnome_canvas_line_set_property;
+ gobject_class->get_property = gnome_canvas_line_get_property;
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_POINTS,
+ g_param_spec_boxed ("points", NULL, NULL,
+ GNOME_TYPE_CANVAS_POINTS,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FILL_COLOR,
+ g_param_spec_string ("fill_color", NULL, NULL,
+ NULL,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FILL_STIPPLE,
+ g_param_spec_object ("fill_stipple", NULL, NULL,
+ GDK_TYPE_DRAWABLE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDTH_PIXELS,
+ g_param_spec_uint ("width_pixels", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FIRST_ARROWHEAD,
+ g_param_spec_boolean ("first_arrowhead", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_LAST_ARROWHEAD,
+ g_param_spec_boolean ("last_arrowhead", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SMOOTH,
+ g_param_spec_boolean ("smooth", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+
+ object_class->destroy = gnome_canvas_line_destroy;
+
+ item_class->update = gnome_canvas_line_update;
+ item_class->realize = gnome_canvas_line_realize;
+ item_class->unrealize = gnome_canvas_line_unrealize;
+ item_class->draw = gnome_canvas_line_draw;
+ item_class->point = gnome_canvas_line_point;
+ item_class->bounds = gnome_canvas_line_bounds;
+
+ item_class->render = gnome_canvas_line_render;
+}
+
+static void
+gnome_canvas_line_init (GnomeCanvasLine *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;
+}
+
+static void
+gnome_canvas_line_destroy (GtkObject *object)
+{
+ GnomeCanvasLine *line;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
+
+ line = GNOME_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 (line->fill_svp)
+ art_svp_free (line->fill_svp);
+ line->fill_svp = NULL;
+
+ if (line->first_svp)
+ art_svp_free (line->first_svp);
+ line->first_svp = NULL;
+
+ if (line->last_svp)
+ art_svp_free (line->last_svp);
+ line->last_svp = 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 (GnomeCanvasLine *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 (gnome_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. Affine is the i2c transformation.
+ */
+static void
+get_bounds_canvas (GnomeCanvasLine *line, double *bx1, double *by1, double *bx2, double *by2, double affine[6])
+{
+ /* It would be possible to tighten the bounds somewhat by transforming the individual points before
+ aggregating them into the bbox. But it hardly seems worth it. */
+ ArtDRect bbox_world;
+ ArtDRect bbox_canvas;
+
+ get_bounds (line, &bbox_world.x0, &bbox_world.y0, &bbox_world.x1, &bbox_world.y1);
+
+ art_drect_affine_transform (&bbox_canvas, &bbox_world, affine);
+ /* include 1 pixel of fudge */
+ *bx1 = bbox_canvas.x0 - 1;
+ *by1 = bbox_canvas.y0 - 1;
+ *bx2 = bbox_canvas.x1 + 1;
+ *by2 = bbox_canvas.y1 + 1;
+}
+
+/* Recalculates the arrow polygons for the line */
+static void
+reconfigure_arrows (GnomeCanvasLine *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 < GNOME_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 < GNOME_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 (GnomeCanvasLine *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 (GnomeCanvasLine *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 (GnomeCanvasLine *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
+gnome_canvas_line_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasLine *line;
+ GnomeCanvasPoints *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 (GNOME_IS_CANVAS_LINE (object));
+
+ item = GNOME_CANVAS_ITEM (object);
+ line = GNOME_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.
+ */
+ gnome_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);
+ gnome_canvas_item_request_redraw_svp (item, line->fill_svp);
+ break;
+
+ case PROP_WIDTH_PIXELS:
+ line->width = g_value_get_uint (value);
+ line->width_pixels = TRUE;
+ set_line_gc_width (line);
+ gnome_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);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_CAP_STYLE:
+ line->cap = g_value_get_enum (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_JOIN_STYLE:
+ line->join = g_value_get_enum (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_LINE_STYLE:
+ line->line_style = g_value_get_enum (value);
+ set_line_gc_width (line);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_FIRST_ARROWHEAD:
+ line->first_arrow = g_value_get_boolean (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_LAST_ARROWHEAD:
+ line->last_arrow = g_value_get_boolean (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_SMOOTH:
+ /* FIXME */
+ break;
+
+ case PROP_SPLINE_STEPS:
+ /* FIXME */
+ break;
+
+ case PROP_ARROW_SHAPE_A:
+ line->shape_a = fabs (g_value_get_double (value));
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_ARROW_SHAPE_B:
+ line->shape_b = fabs (g_value_get_double (value));
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_ARROW_SHAPE_C:
+ line->shape_c = fabs (g_value_get_double (value));
+ gnome_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 = gnome_canvas_get_color_pixel (item->canvas,
+ line->fill_rgba);
+
+ if (!item->canvas->aa)
+ set_line_gc_foreground (line);
+
+ gnome_canvas_item_request_redraw_svp (item, line->fill_svp);
+
+ if (line->first_svp)
+ gnome_canvas_item_request_redraw_svp (item, line->first_svp);
+
+ if (line->last_svp)
+ gnome_canvas_item_request_redraw_svp (item, line->last_svp);
+
+ }
+}
+
+/* Returns a copy of the line's points without the endpoint adjustments for
+ * arrowheads.
+ */
+static GnomeCanvasPoints *
+get_points (GnomeCanvasLine *line)
+{
+ GnomeCanvasPoints *points;
+ int start_ofs, end_ofs;
+
+ if (line->num_points == 0)
+ return NULL;
+
+ start_ofs = end_ofs = 0;
+
+ points = gnome_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
+gnome_canvas_line_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasLine *line;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
+
+ line = GNOME_CANVAS_LINE (object);
+
+ switch (param_id) {
+ case PROP_POINTS:
+ /* get_points returns a copy */
+ g_value_take_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: {
+ GnomeCanvas *canvas = GNOME_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
+gnome_canvas_line_render (GnomeCanvasItem *item,
+ GnomeCanvasBuf *buf)
+{
+ GnomeCanvasLine *line;
+
+ line = GNOME_CANVAS_LINE (item);
+
+ if (line->fill_svp != NULL)
+ gnome_canvas_render_svp (buf, line->fill_svp, line->fill_rgba);
+
+ if (line->first_svp != NULL)
+ gnome_canvas_render_svp (buf, line->first_svp, line->fill_rgba);
+
+ if (line->last_svp != NULL)
+ gnome_canvas_render_svp (buf, line->last_svp, line->fill_rgba);
+}
+
+
+static ArtSVP *
+svp_from_points (const double *item_coords, int num_points, const double affine[6])
+{
+ ArtVpath *vpath;
+ ArtSVP *svp;
+ double x, y;
+ int i;
+
+ vpath = art_new (ArtVpath, num_points + 2);
+
+ for (i = 0; i < num_points; i++) {
+ vpath[i].code = i == 0 ? ART_MOVETO : ART_LINETO;
+ x = item_coords[i * 2];
+ y = item_coords[i * 2 + 1];
+ vpath[i].x = x * affine[0] + y * affine[2] + affine[4];
+ vpath[i].y = x * affine[1] + y * affine[3] + affine[5];
+ }
+#if 0
+ vpath[i].code = ART_LINETO;
+ vpath[i].x = vpath[0].x;
+ vpath[i].y = vpath[0].y;
+ i++;
+#endif
+ vpath[i].code = ART_END;
+ vpath[i].x = 0;
+ vpath[i].y = 0;
+
+ svp = art_svp_from_vpath (vpath);
+
+ art_free (vpath);
+
+ return svp;
+}
+
+static void
+gnome_canvas_line_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasLine *line;
+ int i;
+ ArtVpath *vpath;
+ ArtPoint pi, pc;
+ double width;
+ ArtSVP *svp;
+ double x1, y1, x2, y2;
+
+ line = GNOME_CANVAS_LINE (item);
+
+ if (parent_class->update)
+ (* parent_class->update) (item, affine, clip_path, flags);
+
+ reconfigure_arrows (line);
+
+ if (item->canvas->aa) {
+ gnome_canvas_item_reset_bounds (item);
+
+ vpath = art_new (ArtVpath, line->num_points + 2);
+
+ for (i = 0; i < line->num_points; i++) {
+ pi.x = line->coords[i * 2];
+ pi.y = line->coords[i * 2 + 1];
+ art_affine_point (&pc, &pi, affine);
+ vpath[i].code = i == 0 ? ART_MOVETO : ART_LINETO;
+ vpath[i].x = pc.x;
+ vpath[i].y = pc.y;
+ }
+ vpath[i].code = ART_END;
+ vpath[i].x = 0;
+ vpath[i].y = 0;
+
+ if (line->width_pixels)
+ width = line->width;
+ else
+ width = line->width * art_affine_expansion (affine);
+
+ if (width < 0.5)
+ width = 0.5;
+
+ svp = art_svp_vpath_stroke (vpath,
+ gnome_canvas_join_gdk_to_art (line->join),
+ gnome_canvas_cap_gdk_to_art (line->cap),
+ width,
+ 4,
+ 0.25);
+ art_free (vpath);
+
+ gnome_canvas_item_update_svp_clip (item, &line->fill_svp, svp, clip_path);
+
+ if (line->first_arrow && line->first_coords) {
+ svp = svp_from_points (line->first_coords, NUM_ARROW_POINTS, affine);
+ gnome_canvas_item_update_svp_clip (item,
+ &line->first_svp, svp, clip_path);
+ }
+
+
+ if (line->last_arrow && line->last_coords) {
+ svp = svp_from_points (line->last_coords, NUM_ARROW_POINTS, affine);
+ gnome_canvas_item_update_svp_clip (item,
+ &line->last_svp, svp, clip_path);
+ }
+
+
+ } else {
+ set_line_gc_foreground (line);
+ set_line_gc_width (line);
+ set_stipple (line, line->stipple, TRUE);
+
+ get_bounds_canvas (line, &x1, &y1, &x2, &y2, affine);
+ gnome_canvas_update_bbox (item, x1, y1, x2, y2);
+ }
+}
+
+static void
+gnome_canvas_line_realize (GnomeCanvasItem *item)
+{
+ GnomeCanvasLine *line;
+
+ line = GNOME_CANVAS_LINE (item);
+
+ if (parent_class->realize)
+ (* parent_class->realize) (item);
+
+ line->gc = gdk_gc_new (item->canvas->layout.bin_window);
+
+#if 0
+ (* GNOME_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0);
+#endif
+}
+
+static void
+gnome_canvas_line_unrealize (GnomeCanvasItem *item)
+{
+ GnomeCanvasLine *line;
+
+ line = GNOME_CANVAS_LINE (item);
+
+ g_object_unref (line->gc);
+ line->gc = NULL;
+
+ if (parent_class->unrealize)
+ (* parent_class->unrealize) (item);
+}
+
+static void
+item_to_canvas (GnomeCanvas *canvas, double *item_coords, GdkPoint *canvas_coords, int num_points,
+ int *num_drawn_points, double i2c[6], int x, int y)
+{
+ int i;
+ int old_cx, old_cy;
+ int cx, cy;
+ ArtPoint pi, pc;
+
+#ifdef VERBOSE
+ {
+ char str[128];
+ art_affine_to_string (str, i2c);
+ g_print ("line item_to_canvas %s\n", str);
+ }
+#endif
+
+ /* the first point is always drawn */
+
+ pi.x = item_coords[0];
+ pi.y = item_coords[1];
+ art_affine_point (&pc, &pi, i2c);
+ cx = floor (pc.x + 0.5);
+ cy = floor (pc.y + 0.5);
+ canvas_coords->x = cx - x;
+ canvas_coords->y = cy - y;
+ canvas_coords++;
+ old_cx = cx;
+ old_cy = cy;
+ *num_drawn_points = 1;
+
+ for (i = 1; i < num_points; i++) {
+ pi.x = item_coords[i * 2];
+ pi.y = item_coords[i * 2 + 1];
+ art_affine_point (&pc, &pi, i2c);
+ cx = floor (pc.x + 0.5);
+ cy = floor (pc.y + 0.5);
+ if (old_cx != cx || old_cy != cy) {
+ canvas_coords->x = cx - x;
+ canvas_coords->y = cy - y;
+ old_cx = cx;
+ old_cy = cy;
+ canvas_coords++;
+ (*num_drawn_points)++;
+ }
+ }
+}
+
+static void
+gnome_canvas_line_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height)
+{
+ GnomeCanvasLine *line;
+ GdkPoint static_points[NUM_STATIC_POINTS];
+ GdkPoint *points;
+ int actual_num_points_drawn;
+ double i2c[6];
+
+ line = GNOME_CANVAS_LINE (item);
+
+ if (line->num_points == 0)
+ return;
+
+ /* Build array of canvas pixel coordinates */
+
+ if (line->num_points <= NUM_STATIC_POINTS)
+ points = static_points;
+ else
+ points = g_new (GdkPoint, line->num_points);
+
+
+ gnome_canvas_item_i2c_affine (item, i2c);
+
+ item_to_canvas (item->canvas, line->coords, points, line->num_points,
+ &actual_num_points_drawn, i2c, x, y);
+
+ if (line->stipple)
+ gnome_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, i2c, x, y);
+ 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, i2c, x, y);
+ gdk_draw_polygon (drawable, line->gc, TRUE, points, actual_num_points_drawn );
+ }
+}
+
+static double
+gnome_canvas_line_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy, GnomeCanvasItem **actual_item)
+{
+ GnomeCanvasLine *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 ("gnome_canvas_line_point x, y = (%g, %g); cx, cy = (%d, %d)\n", x, y, cx, cy);
+#endif
+
+ line = GNOME_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 < GNOME_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)
+ gnome_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 {
+ gnome_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 = gnome_canvas_polygon_to_point (poly, 5, x, y);
+ if (dist < GNOME_CANVAS_EPSILON) {
+ best = 0.0;
+ goto done;
+ } else if (dist < best)
+ best = dist;
+
+ changed_miter_to_bevel = FALSE;
+ }
+ }
+
+ if (i == 2)
+ gnome_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 (!gnome_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;
+ gnome_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
+ width, FALSE,
+ poly + 4, poly + 5, poly + 6, poly + 7);
+ }
+ } else
+ gnome_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 = gnome_canvas_polygon_to_point (poly, 5, x, y);
+ if (dist < GNOME_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 < GNOME_CANVAS_EPSILON) {
+ best = 0.0;
+ goto done;
+ } else
+ best = dist;
+ }
+
+ /* sometimes the GnomeCanvasItem::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 = gnome_canvas_polygon_to_point (line->first_coords, NUM_ARROW_POINTS, x, y);
+ if (dist < GNOME_CANVAS_EPSILON) {
+ best = 0.0;
+ goto done;
+ } else
+ best = dist;
+ }
+
+ if (line->last_arrow) {
+ dist = gnome_canvas_polygon_to_point (line->last_coords, NUM_ARROW_POINTS, x, y);
+ if (dist < GNOME_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
+gnome_canvas_line_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ GnomeCanvasLine *line;
+
+ line = GNOME_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/libgnomecanvas/gnome-canvas-line.h b/libgnomecanvas/gnome-canvas-line.h
new file mode 100644
index 0000000000..4ab6cfa62d
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-line.h
@@ -0,0 +1,154 @@
+/*
+ * 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 GnomeCanvas widget
+ *
+ * GnomeCanvas 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 GNOME_CANVAS_LINE_H
+#define GNOME_CANVAS_LINE_H
+
+
+#include <libgnomecanvas/gnome-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 GnomeCanvasPoints* RW Pointer to a GnomeCanvasPoints structure.
+ * This can be created by a call to
+ * gnome_canvas_points_new() (in gnome-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 GNOME_TYPE_CANVAS_LINE (gnome_canvas_line_get_type ())
+#define GNOME_CANVAS_LINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_LINE, GnomeCanvasLine))
+#define GNOME_CANVAS_LINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_LINE, GnomeCanvasLineClass))
+#define GNOME_IS_CANVAS_LINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_LINE))
+#define GNOME_IS_CANVAS_LINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_LINE))
+#define GNOME_CANVAS_LINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_LINE, GnomeCanvasLineClass))
+
+
+typedef struct _GnomeCanvasLine GnomeCanvasLine;
+typedef struct _GnomeCanvasLineClass GnomeCanvasLineClass;
+
+struct _GnomeCanvasLine {
+ GnomeCanvasItem 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 */
+
+ ArtSVP *fill_svp; /* The SVP for the outline shape */ /*AA*/
+ ArtSVP *first_svp; /* The SVP for the first arrow */ /*AA*/
+ ArtSVP *last_svp; /* The SVP for the last arrow */ /*AA*/
+
+ 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)? */
+};
+
+struct _GnomeCanvasLineClass {
+ GnomeCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_line_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-marshal.list b/libgnomecanvas/gnome-canvas-marshal.list
new file mode 100644
index 0000000000..5ad61bf721
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-marshal.list
@@ -0,0 +1,2 @@
+VOID:OBJECT,INT,INT,INT,INT
+BOOLEAN:BOXED
diff --git a/libgnomecanvas/gnome-canvas-path-def.c b/libgnomecanvas/gnome-canvas-path-def.c
new file mode 100644
index 0000000000..11b0924689
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-path-def.c
@@ -0,0 +1,1287 @@
+#define GNOME_CANVAS_PATH_DEF_C
+
+/*
+ * GnomeCanvasPathDef
+ *
+ * (C) 1999-2000 Lauris Kaplinski <lauris@ximian.com>
+ * Released under LGPL
+ *
+ * Authors: Lauris Kaplinski <lauris@ximian.com>
+ * Rusty Conover <rconover@bangtail.net>
+ *
+ * Copyright 1999-2001 Ximian Inc. and authors.
+ */
+
+#include <string.h>
+#include <libart_lgpl/art_misc.h>
+#include "gnome-canvas-path-def.h"
+
+/* The number of points to allocate at once */
+#define GNOME_CANVAS_PATH_DEF_LENSTEP 32
+
+struct _GnomeCanvasPathDef {
+ gint refcount;
+ ArtBpath * bpath;
+ gint end; /* ART_END position */
+ gint length; /* Num allocated Bpaths */
+ gint substart; /* subpath start */
+ gdouble x, y; /* previous moveto position */
+ guint sbpath : 1; /* Bpath is static */
+ guint hascpt : 1; /* Currentpoint is defined */
+ guint posset : 1; /* Previous was moveto */
+ guint moving : 1; /* Bpath end is moving */
+ guint allclosed : 1; /* All subpaths are closed */
+ guint allopen : 1; /* All subpaths are open */
+};
+
+static gboolean sp_bpath_good (ArtBpath * bpath);
+static ArtBpath * sp_bpath_check_subpath (ArtBpath * bpath);
+static gint sp_bpath_length (const ArtBpath * bpath);
+static gboolean sp_bpath_all_closed (const ArtBpath * bpath);
+static gboolean sp_bpath_all_open (const ArtBpath * bpath);
+
+/* Boxed Type Support */
+
+static GnomeCanvasPathDef *
+path_def_boxed_copy (GnomeCanvasPathDef * path_def)
+{
+ if (path_def)
+ gnome_canvas_path_def_ref (path_def);
+ return path_def;
+}
+
+GType
+gnome_canvas_path_def_get_type (void)
+{
+ static GType t = 0;
+ if (t == 0)
+ t = g_boxed_type_register_static
+ ("GnomeCanvasPathDef",
+ (GBoxedCopyFunc)path_def_boxed_copy,
+ (GBoxedFreeFunc)gnome_canvas_path_def_unref);
+ return t;
+}
+
+/* Constructors */
+
+/**
+ * gnome_canvas_path_def_new:
+ *
+ * This function creates a new empty #gnome_canvas_path_def.
+ *
+ * Returns: the new canvas path definition.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_new (void)
+{
+ GnomeCanvasPathDef * path;
+
+ path = gnome_canvas_path_def_new_sized (GNOME_CANVAS_PATH_DEF_LENSTEP);
+
+ return path;
+}
+
+/**
+ * gnome_canvas_path_def_new_sized:
+ * @length: number of points to allocate for the path
+ *
+ * This function creates a new #gnome_canvas_path_def with @length
+ * number of points allocated. It is useful, if you know the exact
+ * number of points in path, so you can avoid automatic point
+ * array reallocation.
+ *
+ * Returns: the new canvas path definition
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_new_sized (gint length)
+{
+ GnomeCanvasPathDef * path;
+
+ g_return_val_if_fail (length > 0, NULL);
+
+ path = g_new (GnomeCanvasPathDef, 1);
+
+ path->refcount = 1;
+ path->bpath = art_new (ArtBpath, length);
+ path->end = 0;
+ path->bpath[path->end].code = ART_END;
+ path->length = length;
+ path->sbpath = FALSE;
+ path->hascpt = FALSE;
+ path->posset = FALSE;
+ path->moving = FALSE;
+ path->allclosed = TRUE;
+ path->allopen = TRUE;
+
+ return path;
+}
+
+/**
+ * gnome_canvas_path_def_new_from_bpath:
+ * @bpath: libart bezier path
+ *
+ * This function constructs a new #gnome_canvas_path_def and uses the
+ * passed @bpath as the contents. The passed bpath should not be
+ * static as the path definition is editable when constructed with
+ * this function. Also, passed bpath will be freed with art_free, if
+ * path is destroyed, so use it with caution.
+ * For constructing a #gnome_canvas_path_def
+ * from (non-modifiable) bpath use
+ * #gnome_canvas_path_def_new_from_static_bpath.
+ *
+ * Returns: the new canvas path definition that is populated with the
+ * passed bezier path, if the @bpath is bad NULL is returned.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_new_from_bpath (ArtBpath * bpath)
+{
+ GnomeCanvasPathDef * path;
+
+ g_return_val_if_fail (sp_bpath_good (bpath), NULL);
+
+ path = g_new (GnomeCanvasPathDef, 1);
+
+ path->refcount = 1;
+ path->bpath = bpath;
+ path->length = sp_bpath_length (bpath);
+ path->end = path->length - 1;
+ path->sbpath = FALSE;
+ path->hascpt = FALSE;
+ path->posset = FALSE;
+ path->moving = FALSE;
+ path->allclosed = sp_bpath_all_closed (bpath);
+ path->allopen = sp_bpath_all_open (bpath);
+
+ return path;
+}
+
+/**
+ * gnome_canvas_path_def_new_from_static_bpath:
+ * @bpath: libart bezier path
+ *
+ * This function constructs a new #gnome_canvas_path_def and
+ * references the passed @bpath as its contents. The
+ * #gnome_canvas_path_def returned from this function is to be
+ * considered static and non-editable (meaning you cannot change the
+ * path from what you passed in @bpath). The bpath will not be freed,
+ * if path will be destroyed, so use it with caution.
+ *
+ * Returns: the new canvas path definition that references the passed
+ * @bpath, or if the path is bad NULL is returned.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_new_from_static_bpath (ArtBpath * bpath)
+{
+ GnomeCanvasPathDef * path;
+
+ g_return_val_if_fail (sp_bpath_good (bpath), NULL);
+
+ path = g_new (GnomeCanvasPathDef, 1);
+
+ path->refcount = 1;
+ path->bpath = bpath;
+ path->length = sp_bpath_length (bpath);
+ path->end = path->length - 1;
+ path->sbpath = TRUE;
+ path->hascpt = FALSE;
+ path->posset = FALSE;
+ path->moving = FALSE;
+ path->allclosed = sp_bpath_all_closed (bpath);
+ path->allopen = sp_bpath_all_open (bpath);
+
+ return path;
+}
+
+/**
+ * gnome_canvas_path_def_new_from_foreign_bpath:
+ * @bpath: libart bezier path
+ *
+ * This function constructs a new #gnome_canvas_path_def and
+ * duplicates the contents of the passed @bpath in the definition.
+ *
+ * Returns: the newly created #gnome_canvas_path_def or NULL if the
+ * path is invalid.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_new_from_foreign_bpath (ArtBpath * bpath)
+{
+ GnomeCanvasPathDef * path;
+ gint length;
+
+ g_return_val_if_fail (sp_bpath_good (bpath), NULL);
+
+ length = sp_bpath_length (bpath);
+
+ path = gnome_canvas_path_def_new_sized (length);
+ memcpy (path->bpath, bpath, sizeof (ArtBpath) * length);
+ path->end = length - 1;
+ path->allclosed = sp_bpath_all_closed (bpath);
+ path->allopen = sp_bpath_all_open (bpath);
+
+ return path;
+}
+
+/**
+ * gnome_canvas_path_def_ref:
+ * @path: a GnomeCanvasPathDef
+ *
+ * Increment the reference count of the GnomeCanvasPathDef.
+ */
+void
+gnome_canvas_path_def_ref (GnomeCanvasPathDef * path)
+{
+ g_return_if_fail (path != NULL);
+
+ path->refcount++;
+}
+
+/**
+ * gnome_canvas_path_def_finish:
+ * @path: a GnomeCanvasPathDef
+ *
+ * Trims dynamic point array to exact length of path.
+ */
+void
+gnome_canvas_path_def_finish (GnomeCanvasPathDef * path)
+{
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (path->sbpath);
+
+ if ((path->end + 1) < path->length) {
+ path->bpath = art_renew (path->bpath, ArtBpath, path->end + 1);
+ path->length = path->end + 1;
+ }
+
+ path->hascpt = FALSE;
+ path->posset = FALSE;
+ path->moving = FALSE;
+}
+/**
+ * gnome_canvas_path_def_ensure_space:
+ * @path: a GnomeCanvasPathDef
+ * @space: number of points to guarantee are allocated at the end of
+ * the path.
+ *
+ * This function ensures that enough space for @space points is
+ * allocated at the end of the path.
+ */
+void
+gnome_canvas_path_def_ensure_space (GnomeCanvasPathDef * path, gint space)
+{
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (space > 0);
+
+ if (path->end + space < path->length) return;
+
+ if (space < GNOME_CANVAS_PATH_DEF_LENSTEP) space = GNOME_CANVAS_PATH_DEF_LENSTEP;
+
+ path->bpath = art_renew (path->bpath, ArtBpath, path->length + space);
+
+ path->length += space;
+}
+
+/**
+ * gnome_canvas_path_def_copy:
+ * @dst: a GnomeCanvasPathDef where the contents of @src will be stored.
+ * @src: a GnomeCanvasPathDef whose contents will be copied it @src.
+ *
+ * This function copies the contents @src to @dest. The old @dest path
+ * array is freed and @dest is marked as non-static (editable),
+ * regardless of the status of @src.
+ */
+void
+gnome_canvas_path_def_copy (GnomeCanvasPathDef * dst, const GnomeCanvasPathDef * src)
+{
+ g_return_if_fail (dst != NULL);
+ g_return_if_fail (src != NULL);
+
+ if (!dst->sbpath) g_free (dst->bpath);
+
+ memcpy (dst, src, sizeof (GnomeCanvasPathDef));
+
+ dst->bpath = g_new (ArtBpath, src->end + 1);
+ memcpy (dst->bpath, src->bpath, (src->end + 1) * sizeof (ArtBpath));
+
+ dst->sbpath = FALSE;
+}
+
+
+/**
+ * gnome_canvas_path_def_duplicate:
+ * @path: a GnomeCanvasPathDef to duplicate
+ *
+ * This function duplicates the passed @path. The new path is
+ * marked as non-static regardless of the state of original.
+ *
+ * Returns: a GnomeCanvasPathDef which is a duplicate of @path.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_duplicate (const GnomeCanvasPathDef * path)
+{
+ GnomeCanvasPathDef * new;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ new = gnome_canvas_path_def_new_from_foreign_bpath (path->bpath);
+ new->x = path->x;
+ new->y = path->y;
+ new->hascpt = path->hascpt;
+ new->posset = path->posset;
+ new->moving = path->moving;
+ new->allclosed = path->allclosed;
+ new->allopen = path->allopen;
+
+ return new;
+}
+
+/**
+ * gnome_canvas_path_def_concat:
+ * @list: a GSList of GnomeCanvasPathDefs to concatenate into one new
+ * path.
+ *
+ * This function concatenates a list of GnomeCanvasPathDefs into one
+ * newly created GnomeCanvasPathDef.
+ *
+ * Returns: a new GnomeCanvasPathDef
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_concat (const GSList * list)
+{
+ GnomeCanvasPathDef * c, * new;
+ ArtBpath * bp;
+ const GSList * l;
+ gint length;
+
+ g_return_val_if_fail (list != NULL, NULL);
+
+ length = 1;
+
+ for (l = list; l != NULL; l = l->next) {
+ c = (GnomeCanvasPathDef *) l->data;
+ length += c->end;
+ }
+
+ new = gnome_canvas_path_def_new_sized (length);
+
+ bp = new->bpath;
+
+ for (l = list; l != NULL; l = l->next) {
+ c = (GnomeCanvasPathDef *) l->data;
+ memcpy (bp, c->bpath, c->end * sizeof (ArtBpath));
+ bp += c->end;
+ }
+
+ bp->code = ART_END;
+
+ new->end = length - 1;
+
+ new->allclosed = sp_bpath_all_closed (new->bpath);
+ new->allopen = sp_bpath_all_open (new->bpath);
+
+ return new;
+}
+
+/**
+ * gnome_canvas_path_def_split:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function splits the passed @path into a list of
+ * GnomeCanvasPathDefs which represent each segment of the origional
+ * path. The path is split when ever an ART_MOVETO or ART_MOVETO_OPEN
+ * is encountered. The closedness of resulting paths is set accordingly
+ * to closedness of corresponding segment.
+ *
+ * Returns: a list of GnomeCanvasPathDef(s).
+ */
+GSList *
+gnome_canvas_path_def_split (const GnomeCanvasPathDef * path)
+{
+ GnomeCanvasPathDef * new;
+ GSList * l;
+ gint p, i;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ p = 0;
+ l = NULL;
+
+ while (p < path->end) {
+ i = 1;
+ while ((path->bpath[p + i].code == ART_LINETO) || (path->bpath[p + i].code == ART_CURVETO)) i++;
+ new = gnome_canvas_path_def_new_sized (i + 1);
+ memcpy (new->bpath, path->bpath + p, i * sizeof (ArtBpath));
+ new->end = i;
+ new->bpath[i].code = ART_END;
+ new->allclosed = (new->bpath->code == ART_MOVETO);
+ new->allopen = (new->bpath->code == ART_MOVETO_OPEN);
+ l = g_slist_append (l, new);
+ p += i;
+ }
+
+ return l;
+}
+
+/**
+ * gnome_canvas_path_def_open_parts:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function creates a new GnomeCanvasPathDef that contains all of
+ * the open segments on the passed @path.
+ *
+ * Returns: a new GnomeCanvasPathDef that contains all of the open segemtns in @path.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_open_parts (const GnomeCanvasPathDef * path)
+{
+ GnomeCanvasPathDef * new;
+ ArtBpath * p, * d;
+ gint len;
+ gboolean closed;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ closed = TRUE;
+ len = 0;
+
+ for (p = path->bpath; p->code != ART_END; p++) {
+ switch (p->code) {
+ case ART_MOVETO_OPEN:
+ closed = FALSE;
+ len++;
+ break;
+ case ART_MOVETO:
+ closed = TRUE;
+ break;
+ case ART_LINETO:
+ case ART_CURVETO:
+ if (!closed) len++;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+
+ new = gnome_canvas_path_def_new_sized (len + 1);
+
+ closed = TRUE;
+ d = new->bpath;
+
+ for (p = path->bpath; p->code != ART_END; p++) {
+ switch (p->code) {
+ case ART_MOVETO_OPEN:
+ closed = FALSE;
+ *d++ = *p;
+ break;
+ case ART_MOVETO:
+ closed = TRUE;
+ break;
+ case ART_LINETO:
+ case ART_CURVETO:
+ if (!closed) *d++ = *p;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+
+ d->code = ART_END;
+
+ new->end = len;
+ new->allclosed = FALSE;
+ new->allopen = TRUE;
+
+ return new;
+}
+
+/**
+ * gnome_canvas_path_def_closed_parts:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a new GnomeCanvasPathDef that contains the
+ * all of close parts of passed @path.
+ *
+ * Returns: a new GnomeCanvasPathDef that contains all of the closed
+ * parts of passed @path.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_closed_parts (const GnomeCanvasPathDef * path)
+{
+ GnomeCanvasPathDef * new;
+ ArtBpath * p, * d;
+ gint len;
+ gboolean closed;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ closed = FALSE;
+ len = 0;
+
+ for (p = path->bpath; p->code != ART_END; p++) {
+ switch (p->code) {
+ case ART_MOVETO_OPEN:
+ closed = FALSE;
+ break;
+ case ART_MOVETO:
+ closed = TRUE;
+ len++;
+ break;
+ case ART_LINETO:
+ case ART_CURVETO:
+ if (closed) len++;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+
+ new = gnome_canvas_path_def_new_sized (len + 1);
+
+ closed = FALSE;
+ d = new->bpath;
+
+ for (p = path->bpath; p->code != ART_END; p++) {
+ switch (p->code) {
+ case ART_MOVETO_OPEN:
+ closed = FALSE;
+ break;
+ case ART_MOVETO:
+ closed = TRUE;
+ *d++ = *p;
+ break;
+ case ART_LINETO:
+ case ART_CURVETO:
+ if (closed) *d++ = *p;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+
+ d->code = ART_END;
+
+ new->end = len;
+ new->allclosed = TRUE;
+ new->allopen = FALSE;
+
+ return new;
+}
+
+/**
+ * gnome_canvas_path_def_close_all:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function closes all of the open segments in the passed path
+ * and returns a new GnomeCanvasPathDef.
+ *
+ * Returns: a GnomeCanvasPathDef that contains the contents of @path
+ * but has modified the path is fully closed
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_close_all (const GnomeCanvasPathDef * path)
+{
+ GnomeCanvasPathDef * new;
+ ArtBpath * p, * d, * start;
+ gint len;
+ gboolean closed;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ if (path->allclosed) {
+ new = gnome_canvas_path_def_duplicate (path);
+ return new;
+ }
+
+ len = 1;
+
+ /* Count MOVETO_OPEN */
+
+ for (p = path->bpath; p->code != ART_END; p++) {
+ len += 1;
+ if (p->code == ART_MOVETO_OPEN) len += 2;
+ }
+
+ new = gnome_canvas_path_def_new_sized (len);
+
+ d = start = new->bpath;
+ closed = TRUE;
+
+ for (p = path->bpath; p->code != ART_END; p++) {
+ switch (p->code) {
+ case ART_MOVETO_OPEN:
+ start = p;
+ closed = FALSE;
+ case ART_MOVETO:
+ if ((!closed) && ((start->x3 != p->x3) || (start->y3 != p->y3))) {
+ d->code = ART_LINETO;
+ d->x3 = start->x3;
+ d->y3 = start->y3;
+ d++;
+ }
+ if (p->code == ART_MOVETO) closed = TRUE;
+ d->code = ART_MOVETO;
+ d->x3 = p->x3;
+ d->y3 = p->y3;
+ d++;
+ break;
+ case ART_LINETO:
+ case ART_CURVETO:
+ *d++ = *p;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+
+ if ((!closed) && ((start->x3 != p->x3) || (start->y3 != p->y3))) {
+ d->code = ART_LINETO;
+ d->x3 = start->x3;
+ d->y3 = start->y3;
+ d++;
+ }
+
+ d->code = ART_END;
+
+ new->end = d - new->bpath;
+ new->allclosed = TRUE;
+ new->allopen = FALSE;
+
+ return new;
+}
+
+/* Destructor */
+
+/**
+ * gnome_canvas_path_def_unref:
+ * @path: a GnomeCanvasPathDef
+ *
+ * Decrease the reference count of the passed @path. If the reference
+ * count is < 1 the path is deallocated.
+ */
+void
+gnome_canvas_path_def_unref (GnomeCanvasPathDef * path)
+{
+ g_return_if_fail (path != NULL);
+
+ if (--path->refcount < 1) {
+ if ((!path->sbpath) && (path->bpath)) art_free (path->bpath);
+ g_free (path);
+ }
+}
+
+
+/* Methods */
+/**
+ * gnome_canvas_path_def_reset:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function clears the contents of the passed @path.
+ */
+void
+gnome_canvas_path_def_reset (GnomeCanvasPathDef * path)
+{
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (!path->sbpath);
+
+ path->bpath->code = ART_END;
+ path->end = 0;
+ path->hascpt = FALSE;
+ path->posset = FALSE;
+ path->moving = FALSE;
+ path->allclosed = TRUE;
+ path->allopen = TRUE;
+}
+
+/* Several consequtive movetos are ALLOWED */
+
+/**
+ * gnome_canvas_path_def_moveto:
+ * @path: a GnomeCanvasPathDef
+ * @x: x coordinate
+ * @y: y coordinate
+ *
+ * This function adds starts new subpath on @path, and sets its
+ * starting point to @x and @y. If current subpath is empty, it
+ * simply changes its starting coordinates to new values.
+ */
+void
+gnome_canvas_path_def_moveto (GnomeCanvasPathDef * path, gdouble x, gdouble y)
+{
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (!path->sbpath);
+ g_return_if_fail (!path->moving);
+
+ path->substart = path->end;
+ path->hascpt = TRUE;
+ path->posset = TRUE;
+ path->x = x;
+ path->y = y;
+
+ path->allclosed = FALSE;
+}
+
+/**
+ * gnome_canvas_path_def_lineto:
+ * @path: a GnomeCanvasPathDef
+ * @x: x coordinate
+ * @y: y coordinate
+ *
+ * This function add a line segment to the passed @path with the
+ * specified @x and @y coordinates.
+ */
+void
+gnome_canvas_path_def_lineto (GnomeCanvasPathDef * path, gdouble x, gdouble y)
+{
+ ArtBpath * bp;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (!path->sbpath);
+ g_return_if_fail (path->hascpt);
+
+ if (path->moving) {
+ /* simply fix endpoint */
+ g_return_if_fail (!path->posset);
+ g_return_if_fail (path->end > 1);
+ bp = path->bpath + path->end - 1;
+ g_return_if_fail (bp->code == ART_LINETO);
+ bp->x3 = x;
+ bp->y3 = y;
+ path->moving = FALSE;
+ return;
+ }
+
+ if (path->posset) {
+ /* start a new segment */
+ gnome_canvas_path_def_ensure_space (path, 2);
+ bp = path->bpath + path->end;
+ bp->code = ART_MOVETO_OPEN;
+ bp->x3 = path->x;
+ bp->y3 = path->y;
+ bp++;
+ bp->code = ART_LINETO;
+ bp->x3 = x;
+ bp->y3 = y;
+ bp++;
+ bp->code = ART_END;
+ path->end += 2;
+ path->posset = FALSE;
+ path->allclosed = FALSE;
+ return;
+ }
+
+ /* Simply add line */
+
+ g_return_if_fail (path->end > 1);
+ gnome_canvas_path_def_ensure_space (path, 1);
+ bp = path->bpath + path->end;
+ bp->code = ART_LINETO;
+ bp->x3 = x;
+ bp->y3 = y;
+ bp++;
+ bp->code = ART_END;
+ path->end++;
+}
+
+
+/**
+ * gnome_canvas_path_def_lineto_moving:
+ * @path: a GnomeCanvasPathDef
+ * @x: x coordinate
+ * @y: y coordinate
+ *
+ * This functions adds a new line segment with loose endpoint to the path, or
+ * if endpoint is already loose, changes its coordinates to @x, @y. You
+ * can change the coordinates of loose endpoint as many times as you want,
+ * the last ones set will be fixed, if you continue line. This is useful
+ * for handling drawing with mouse.
+ */
+void
+gnome_canvas_path_def_lineto_moving (GnomeCanvasPathDef * path, gdouble x, gdouble y)
+{
+ ArtBpath * bp;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (!path->sbpath);
+ g_return_if_fail (path->hascpt);
+
+ if (path->moving) {
+ /* simply change endpoint */
+ g_return_if_fail (!path->posset);
+ g_return_if_fail (path->end > 1);
+ bp = path->bpath + path->end - 1;
+ g_return_if_fail (bp->code == ART_LINETO);
+ bp->x3 = x;
+ bp->y3 = y;
+ return;
+ }
+
+ if (path->posset) {
+ /* start a new segment */
+ gnome_canvas_path_def_ensure_space (path, 2);
+ bp = path->bpath + path->end;
+ bp->code = ART_MOVETO_OPEN;
+ bp->x3 = path->x;
+ bp->y3 = path->y;
+ bp++;
+ bp->code = ART_LINETO;
+ bp->x3 = x;
+ bp->y3 = y;
+ bp++;
+ bp->code = ART_END;
+ path->end += 2;
+ path->posset = FALSE;
+ path->moving = TRUE;
+ path->allclosed = FALSE;
+ return;
+ }
+
+ /* Simply add line */
+
+ g_return_if_fail (path->end > 1);
+ gnome_canvas_path_def_ensure_space (path, 1);
+ bp = path->bpath + path->end;
+ bp->code = ART_LINETO;
+ bp->x3 = x;
+ bp->y3 = y;
+ bp++;
+ bp->code = ART_END;
+ path->end++;
+ path->moving = TRUE;
+}
+
+/**
+ * gnome_canvas_path_def_curveto:
+ * @path: a GnomeCanvasPathDef
+ * @x0: first control point x coordinate
+ * @y0: first control point y coordinate
+ * @x1: second control point x coordinate
+ * @y1: second control point y coordinate
+ * @x2: end of curve x coordinate
+ * @y2: end of curve y coordinate
+ *
+ * This function adds a bezier curve segment to the path definition.
+ */
+
+void
+gnome_canvas_path_def_curveto (GnomeCanvasPathDef * path, gdouble x0, gdouble y0, gdouble x1, gdouble y1, gdouble x2, gdouble y2)
+{
+ ArtBpath * bp;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (!path->sbpath);
+ g_return_if_fail (path->hascpt);
+ g_return_if_fail (!path->moving);
+
+ if (path->posset) {
+ /* start a new segment */
+ gnome_canvas_path_def_ensure_space (path, 2);
+ bp = path->bpath + path->end;
+ bp->code = ART_MOVETO_OPEN;
+ bp->x3 = path->x;
+ bp->y3 = path->y;
+ bp++;
+ bp->code = ART_CURVETO;
+ bp->x1 = x0;
+ bp->y1 = y0;
+ bp->x2 = x1;
+ bp->y2 = y1;
+ bp->x3 = x2;
+ bp->y3 = y2;
+ bp++;
+ bp->code = ART_END;
+ path->end += 2;
+ path->posset = FALSE;
+ path->allclosed = FALSE;
+ return;
+ }
+
+ /* Simply add path */
+
+ g_return_if_fail (path->end > 1);
+ gnome_canvas_path_def_ensure_space (path, 1);
+ bp = path->bpath + path->end;
+ bp->code = ART_CURVETO;
+ bp->x1 = x0;
+ bp->y1 = y0;
+ bp->x2 = x1;
+ bp->y2 = y1;
+ bp->x3 = x2;
+ bp->y3 = y2;
+ bp++;
+ bp->code = ART_END;
+ path->end++;
+}
+
+/**
+ * gnome_canvas_path_def_closepath:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function closes the last subpath of @path, adding a ART_LINETO to
+ * subpath starting point, if needed and changing starting pathcode to
+ * ART_MOVETO
+ */
+void
+gnome_canvas_path_def_closepath (GnomeCanvasPathDef * path)
+{
+ ArtBpath * bs, * be;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (!path->sbpath);
+ g_return_if_fail (path->hascpt);
+ g_return_if_fail (!path->posset);
+ g_return_if_fail (!path->moving);
+ g_return_if_fail (!path->allclosed);
+ /* We need at last M + L + L + E */
+ g_return_if_fail (path->end - path->substart > 2);
+
+ bs = path->bpath + path->substart;
+ be = path->bpath + path->end - 1;
+
+ if ((bs->x3 != be->x3) || (bs->y3 != be->y3)) {
+ gnome_canvas_path_def_lineto (path, bs->x3, bs->y3);
+ }
+
+ bs = path->bpath + path->substart; /* NB. def_lineto can
+ realloc bpath */
+ bs->code = ART_MOVETO;
+
+ path->allclosed = sp_bpath_all_closed (path->bpath);
+ path->allopen = sp_bpath_all_open (path->bpath);
+
+ path->hascpt = FALSE;
+}
+
+/**
+ * gnome_canvas_path_def_closepath_current:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function closes the last subpath by setting the coordinates of
+ * the endpoint of the last segment (line or curve) to starting point.
+ */
+void
+gnome_canvas_path_def_closepath_current (GnomeCanvasPathDef * path)
+{
+ ArtBpath * bs, * be;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (!path->sbpath);
+ g_return_if_fail (path->hascpt);
+ g_return_if_fail (!path->posset);
+ g_return_if_fail (!path->allclosed);
+ /* We need at last M + L + L + E */
+ g_return_if_fail (path->end - path->substart > 2);
+
+ bs = path->bpath + path->substart;
+ be = path->bpath + path->end - 1;
+
+ be->x3 = bs->x3;
+ be->y3 = bs->y3;
+
+ bs->code = ART_MOVETO;
+
+ path->allclosed = sp_bpath_all_closed (path->bpath);
+ path->allopen = sp_bpath_all_open (path->bpath);
+
+ path->hascpt = FALSE;
+ path->moving = FALSE;
+}
+
+/**
+ * gnome_canvas_path_def_bpath:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a ArtBpath that consists of the path
+ * definition.
+ *
+ * Returns: ArtBpath
+ */
+ArtBpath * gnome_canvas_path_def_bpath (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, NULL);
+
+ return path->bpath;
+}
+
+/**
+ * gnome_canvas_path_def_length:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns the length of the path definition. Not
+ * Euclidian length of the path but rather the number of points on the
+ * path.
+ *
+ * Returns: integer, number of points on the path.
+ */
+gint gnome_canvas_path_def_length (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, -1);
+
+ return path->end + 1;
+}
+
+/**
+ * gnome_canvas_path_def_is_empty:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function is a boolean test to see if the path is empty,
+ * meaning containing no line segments.
+ *
+ * Returns: boolean, indicating if the path is empty.
+ */
+gboolean
+gnome_canvas_path_def_is_empty (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, TRUE);
+
+ return (path->bpath->code == ART_END);
+}
+
+/**
+ * gnome_canvas_path_def_has_currentpoint:
+ * @path: a GnomeCanvasPathdef
+ *
+ * This function is a boolean test checking to see if the path has a
+ * current point defined. Current point will be set by line operators,
+ * and cleared by closing subpath.
+ *
+ * Returns: boolean, indicating if the path has a current point defined.
+ */
+gboolean
+gnome_canvas_path_def_has_currentpoint (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ return (path->hascpt);
+}
+
+/**
+ * gnome_canvas_path_def_currentpoint:
+ * @path: a GnomeCanvasPathDef
+ * @p: a ArtPoint where to store the current point
+ *
+ * Stores the current point of the path definition in the passed ArtPoint @p.
+ */
+void
+gnome_canvas_path_def_currentpoint (const GnomeCanvasPathDef * path, ArtPoint * p)
+{
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (p != NULL);
+ g_return_if_fail (path->hascpt);
+
+ if (path->posset) {
+ p->x = path->x;
+ p->y = path->y;
+ } else {
+ p->x = (path->bpath + path->end - 1)->x3;
+ p->y = (path->bpath + path->end - 1)->y3;
+ }
+}
+
+/**
+ * gnome_canvas_path_def_last_bpath:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns pointer to the last ArtBpath segment in the path
+ * definition.
+ *
+ * Returns: ArtBpath, being the last segment in the path definition or
+ * null if no line segments have been defined.
+ */
+ArtBpath *
+gnome_canvas_path_def_last_bpath (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, NULL);
+
+ if (path->end == 0) return NULL;
+
+ return path->bpath + path->end - 1;
+}
+
+/**
+ * gnome_canvas_path_def_first_bpath:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns the first ArtBpath point in the definition.
+ *
+ * Returns: ArtBpath being the first point in the path definition or
+ * null if no points are defined
+*/
+ArtBpath *
+gnome_canvas_path_def_first_bpath (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, NULL);
+
+ if (path->end == 0) return NULL;
+
+ return path->bpath;
+}
+
+/**
+ * gnome_canvas_path_def_any_open:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a boolean value indicating if the path has
+ * any open segments.
+ *
+ * Returns: boolean, indicating if the path has any open segments.
+ */
+gboolean
+gnome_canvas_path_def_any_open (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ return (!path->allclosed);
+}
+
+/**
+ * gnome_canvas_path_def_all_open:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a boolean value indicating if the path only
+ * contains open segments.
+ *
+ * Returns: boolean, indicating if the path has all open segments.
+ */
+gboolean
+gnome_canvas_path_def_all_open (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ return (path->allopen);
+}
+
+/**
+ * gnome_canvas_path_def_any_closed:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a boolean valid indicating if the path has
+ * any closed segements.
+ *
+ * Returns: boolean, indicating if the path has any closed segments.
+ */
+gboolean
+gnome_canvas_path_def_any_closed (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ return (!path->allopen);
+}
+
+/**
+ * gnome_canvas_path_def_all_closed:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a boolean value indicating if the path only
+ * contains closed segments.
+ *
+ * Returns: boolean, indicating if the path has all closed segments.
+ */
+gboolean
+gnome_canvas_path_def_all_closed (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ return (path->allclosed);
+}
+
+/* Private methods */
+
+static
+gboolean sp_bpath_good (ArtBpath * bpath)
+{
+ ArtBpath * bp;
+
+ g_return_val_if_fail (bpath != NULL, FALSE);
+
+ if (bpath->code == ART_END)
+ return TRUE;
+
+ bp = bpath;
+
+ while (bp->code != ART_END) {
+ bp = sp_bpath_check_subpath (bp);
+ if (bp == NULL) return FALSE;
+ }
+
+ return TRUE;
+}
+
+static ArtBpath *
+sp_bpath_check_subpath (ArtBpath * bpath)
+{
+ gint i, len;
+ gboolean closed;
+
+ g_return_val_if_fail (bpath != NULL, NULL);
+
+ if (bpath->code == ART_MOVETO) {
+ closed = TRUE;
+ } else if (bpath->code == ART_MOVETO_OPEN) {
+ closed = FALSE;
+ } else {
+ return NULL;
+ }
+
+ len = 0;
+
+ for (i = 1; (bpath[i].code != ART_END) && (bpath[i].code != ART_MOVETO) && (bpath[i].code != ART_MOVETO_OPEN); i++) {
+ switch (bpath[i].code) {
+ case ART_LINETO:
+ case ART_CURVETO:
+ len++;
+ break;
+ default:
+ return NULL;
+ }
+ }
+
+ if (closed) {
+ if (len < 2) return NULL;
+ if ((bpath->x3 != bpath[i-1].x3) || (bpath->y3 != bpath[i-1].y3)) return NULL;
+ } else {
+ if (len < 1) return NULL;
+ }
+
+ return bpath + i;
+}
+
+static gint
+sp_bpath_length (const ArtBpath * bpath)
+{
+ gint l;
+
+ g_return_val_if_fail (bpath != NULL, FALSE);
+
+ for (l = 0; bpath[l].code != ART_END; l++) ;
+
+ l++;
+
+ return l;
+}
+
+static gboolean
+sp_bpath_all_closed (const ArtBpath * bpath)
+{
+ const ArtBpath * bp;
+
+ g_return_val_if_fail (bpath != NULL, FALSE);
+
+ for (bp = bpath; bp->code != ART_END; bp++)
+ if (bp->code == ART_MOVETO_OPEN) return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+sp_bpath_all_open (const ArtBpath * bpath)
+{
+ const ArtBpath * bp;
+
+ g_return_val_if_fail (bpath != NULL, FALSE);
+
+ for (bp = bpath; bp->code != ART_END; bp++)
+ if (bp->code == ART_MOVETO) return FALSE;
+
+ return TRUE;
+}
+
+
diff --git a/libgnomecanvas/gnome-canvas-path-def.h b/libgnomecanvas/gnome-canvas-path-def.h
new file mode 100644
index 0000000000..c3f6b25147
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-path-def.h
@@ -0,0 +1,96 @@
+#ifndef GNOME_CANVAS_PATH_DEF_H
+#define GNOME_CANVAS_PATH_DEF_H
+
+/*
+ * GnomeCanvasPathDef
+ *
+ * (C) 1999-2000 Lauris Kaplinski <lauris@ximian.com>
+ * Released under LGPL
+ *
+ * This is mostly like GnomeCanvasBpathDef, but with added functionality:
+ * - can be constructed from scratch, from existing bpath of from static bpath
+ * - Path is always terminated with ART_END
+ * - Has closed flag
+ * - has concat, split and copy methods
+ *
+ */
+
+#include <glib-object.h>
+#include <libart_lgpl/art_bpath.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GnomeCanvasPathDef GnomeCanvasPathDef;
+
+#define GNOME_TYPE_CANVAS_PATH_DEF (gnome_canvas_path_def_get_type ())
+GType gnome_canvas_path_def_get_type (void) G_GNUC_CONST;
+
+/* Constructors */
+
+GnomeCanvasPathDef * gnome_canvas_path_def_new (void);
+GnomeCanvasPathDef * gnome_canvas_path_def_new_sized (gint length);
+GnomeCanvasPathDef * gnome_canvas_path_def_new_from_bpath (ArtBpath * bpath);
+GnomeCanvasPathDef * gnome_canvas_path_def_new_from_static_bpath (ArtBpath * bpath);
+GnomeCanvasPathDef * gnome_canvas_path_def_new_from_foreign_bpath (ArtBpath * bpath);
+
+void gnome_canvas_path_def_ref (GnomeCanvasPathDef * path);
+void gnome_canvas_path_def_finish (GnomeCanvasPathDef * path);
+void gnome_canvas_path_def_ensure_space (GnomeCanvasPathDef * path, gint space);
+
+/*
+ * Misc constructors
+ * All these return NEW path, not unrefing old
+ * Also copy and duplicate force bpath to be private (otherwise you
+ * would use ref :)
+ */
+
+void gnome_canvas_path_def_copy (GnomeCanvasPathDef * dst, const GnomeCanvasPathDef * src);
+GnomeCanvasPathDef * gnome_canvas_path_def_duplicate (const GnomeCanvasPathDef * path);
+GnomeCanvasPathDef * gnome_canvas_path_def_concat (const GSList * list);
+GSList * gnome_canvas_path_def_split (const GnomeCanvasPathDef * path);
+GnomeCanvasPathDef * gnome_canvas_path_def_open_parts (const GnomeCanvasPathDef * path);
+GnomeCanvasPathDef * gnome_canvas_path_def_closed_parts (const GnomeCanvasPathDef * path);
+GnomeCanvasPathDef * gnome_canvas_path_def_close_all (const GnomeCanvasPathDef * path);
+
+/* Destructor */
+
+void gnome_canvas_path_def_unref (GnomeCanvasPathDef * path);
+
+/* Methods */
+
+/* Sets GnomeCanvasPathDef to zero length */
+
+void gnome_canvas_path_def_reset (GnomeCanvasPathDef * path);
+
+/* Drawing methods */
+
+void gnome_canvas_path_def_moveto (GnomeCanvasPathDef * path, gdouble x, gdouble y);
+void gnome_canvas_path_def_lineto (GnomeCanvasPathDef * path, gdouble x, gdouble y);
+
+/* Does not create new ArtBpath, but simply changes last lineto position */
+
+void gnome_canvas_path_def_lineto_moving (GnomeCanvasPathDef * path, gdouble x, gdouble y);
+void gnome_canvas_path_def_curveto (GnomeCanvasPathDef * path, gdouble x0, gdouble y0,gdouble x1, gdouble y1, gdouble x2, gdouble y2);
+void gnome_canvas_path_def_closepath (GnomeCanvasPathDef * path);
+
+/* Does not draw new line to startpoint, but moves last lineto */
+
+void gnome_canvas_path_def_closepath_current (GnomeCanvasPathDef * path);
+
+/* Various methods */
+
+ArtBpath * gnome_canvas_path_def_bpath (const GnomeCanvasPathDef * path);
+gint gnome_canvas_path_def_length (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_is_empty (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_has_currentpoint (const GnomeCanvasPathDef * path);
+void gnome_canvas_path_def_currentpoint (const GnomeCanvasPathDef * path, ArtPoint * p);
+ArtBpath * gnome_canvas_path_def_last_bpath (const GnomeCanvasPathDef * path);
+ArtBpath * gnome_canvas_path_def_first_bpath (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_any_open (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_all_open (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_any_closed (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_all_closed (const GnomeCanvasPathDef * path);
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-pixbuf.c b/libgnomecanvas/gnome-canvas-pixbuf.c
new file mode 100644
index 0000000000..9619e86df8
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-pixbuf.c
@@ -0,0 +1,1077 @@
+/* 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 <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-util.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <libart_lgpl/art_rgb_affine.h>
+#include <libart_lgpl/art_rgb_rgba_affine.h>
+#include "gnome-canvas-pixbuf.h"
+
+/* Private part of the GnomeCanvasPixbuf structure */
+typedef struct {
+ /* Our gdk-pixbuf */
+ GdkPixbuf *pixbuf;
+
+ /* 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;
+
+ /* Anchor */
+ GtkAnchorType anchor;
+} 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
+};
+
+static void gnome_canvas_pixbuf_class_init (GnomeCanvasPixbufClass *class);
+static void gnome_canvas_pixbuf_init (GnomeCanvasPixbuf *cpb);
+static void gnome_canvas_pixbuf_destroy (GtkObject *object);
+static void gnome_canvas_pixbuf_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_pixbuf_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_pixbuf_update (GnomeCanvasItem *item, double *affine,
+ ArtSVP *clip_path, int flags);
+static void gnome_canvas_pixbuf_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height);
+static void gnome_canvas_pixbuf_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+static double gnome_canvas_pixbuf_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
+ GnomeCanvasItem **actual_item);
+static void gnome_canvas_pixbuf_bounds (GnomeCanvasItem *item,
+ double *x1, double *y1, double *x2, double *y2);
+
+static GnomeCanvasItemClass *parent_class;
+
+
+
+/**
+ * gnome_canvas_pixbuf_get_type:
+ * @void:
+ *
+ * Registers the #GnomeCanvasPixbuf class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the #GnomeCanvasPixbuf class.
+ **/
+GType
+gnome_canvas_pixbuf_get_type (void)
+{
+ static GType pixbuf_type;
+
+ if (!pixbuf_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasPixbufClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_pixbuf_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasPixbuf),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_pixbuf_init,
+ NULL /* value_table */
+ };
+
+ pixbuf_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasPixbuf",
+ &object_info, 0);
+ }
+
+ return pixbuf_type;
+}
+
+/* Class initialization function for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_class_init (GnomeCanvasPixbufClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = gnome_canvas_pixbuf_set_property;
+ gobject_class->get_property = gnome_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_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDTH,
+ g_param_spec_double ("width", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDTH_SET,
+ g_param_spec_boolean ("width_set", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDTH_IN_PIXELS,
+ g_param_spec_boolean ("width_in_pixels", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_HEIGHT,
+ g_param_spec_double ("height", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_HEIGHT_SET,
+ g_param_spec_boolean ("height_set", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_HEIGHT_IN_PIXELS,
+ g_param_spec_boolean ("height_in_pixels", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X,
+ g_param_spec_double ("x", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X_IN_PIXELS,
+ g_param_spec_boolean ("x_in_pixels", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y,
+ g_param_spec_double ("y", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y_IN_PIXELS,
+ g_param_spec_boolean ("y_in_pixels", NULL, NULL,
+ FALSE,
+ (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_NW,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ object_class->destroy = gnome_canvas_pixbuf_destroy;
+
+ item_class->update = gnome_canvas_pixbuf_update;
+ item_class->draw = gnome_canvas_pixbuf_draw;
+ item_class->render = gnome_canvas_pixbuf_render;
+ item_class->point = gnome_canvas_pixbuf_point;
+ item_class->bounds = gnome_canvas_pixbuf_bounds;
+}
+
+/* Object initialization function for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_init (GnomeCanvasPixbuf *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;
+}
+
+/* Destroy handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_destroy (GtkObject *object)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_PIXBUF (object));
+
+ item = GNOME_CANVAS_ITEM (object);
+ gcp = (GNOME_CANVAS_PIXBUF (object));
+ priv = gcp->priv;
+
+ /* remember, destroy can be run multiple times! */
+
+ if (priv) {
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+
+ if (priv->pixbuf)
+ g_object_unref (priv->pixbuf);
+
+ 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
+gnome_canvas_pixbuf_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+ GdkPixbuf *pixbuf;
+ double val;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_PIXBUF (object));
+
+ item = GNOME_CANVAS_ITEM (object);
+ gcp = GNOME_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;
+ }
+
+ priv->need_pixbuf_update = TRUE;
+ gnome_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;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIDTH_SET:
+ priv->width_set = g_value_get_boolean (value);
+ priv->need_xform_update = TRUE;
+ gnome_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;
+ gnome_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;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_HEIGHT_SET:
+ priv->height_set = g_value_get_boolean (value);
+ priv->need_xform_update = TRUE;
+ gnome_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;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_X:
+ priv->x = g_value_get_double (value);
+ priv->need_xform_update = TRUE;
+ gnome_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;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_Y:
+ priv->y = g_value_get_double (value);
+ priv->need_xform_update = TRUE;
+ gnome_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;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_ANCHOR:
+ priv->anchor = g_value_get_enum (value);
+ priv->need_xform_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+/* Get_property handler for the pixbuf canvasi item */
+static void
+gnome_canvas_pixbuf_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_PIXBUF (object));
+
+ gcp = GNOME_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;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+
+
+/* Bounds and utilities */
+
+/* Computes the amount by which the unit horizontal and vertical vectors will be
+ * scaled by an affine transformation.
+ */
+static void
+compute_xform_scaling (double *affine, ArtPoint *i_c, ArtPoint *j_c)
+{
+ ArtPoint orig, orig_c;
+ ArtPoint i, j;
+
+ /* Origin */
+
+ orig.x = 0.0;
+ orig.y = 0.0;
+ art_affine_point (&orig_c, &orig, affine);
+
+ /* Horizontal and vertical vectors */
+
+ i.x = 1.0;
+ i.y = 0.0;
+ art_affine_point (i_c, &i, affine);
+ i_c->x -= orig_c.x;
+ i_c->y -= orig_c.y;
+
+ j.x = 0.0;
+ j.y = 1.0;
+ art_affine_point (j_c, &j, affine);
+ j_c->x -= orig_c.x;
+ j_c->y -= orig_c.y;
+}
+
+/* computes the addtional resolution dependent affine needed to
+ * fit the image within its viewport defined by x,y,width and height
+ * args
+ */
+static void
+compute_viewport_affine (GnomeCanvasPixbuf *gcp, double *viewport_affine, double *i2c)
+{
+ PixbufPrivate *priv;
+ ArtPoint i_c, j_c;
+ double i_len, j_len;
+ double si_len, sj_len;
+ double ti_len, tj_len;
+ double scale[6], translate[6];
+ double w, h;
+ double x, y;
+
+ priv = gcp->priv;
+
+ /* Compute scaling vectors and required width/height */
+
+ compute_xform_scaling (i2c, &i_c, &j_c);
+
+ i_len = sqrt (i_c.x * i_c.x + i_c.y * i_c.y);
+ j_len = sqrt (j_c.x * j_c.x + j_c.y * j_c.y);
+
+ if (priv->width_set)
+ w = priv->width;
+ else
+ w = gdk_pixbuf_get_width (priv->pixbuf);
+
+ if (priv->height_set)
+ h = priv->height;
+ else
+ h = gdk_pixbuf_get_height (priv->pixbuf);
+
+ x = priv->x;
+ y = priv->y;
+
+ /* Convert i_len and j_len into scaling factors */
+
+ if (priv->width_in_pixels) {
+ if (i_len > GNOME_CANVAS_EPSILON)
+ si_len = 1.0 / i_len;
+ else
+ si_len = 0.0;
+ } else
+ si_len = 1.0;
+
+ si_len *= w / gdk_pixbuf_get_width (priv->pixbuf);
+
+ if (priv->height_in_pixels) {
+ if (j_len > GNOME_CANVAS_EPSILON)
+ sj_len = 1.0 / j_len;
+ else
+ sj_len = 0.0;
+ } else
+ sj_len = 1.0;
+
+ sj_len *= h / gdk_pixbuf_get_height (priv->pixbuf);
+
+ /* Calculate translation offsets */
+
+ if (priv->x_in_pixels) {
+ if (i_len > GNOME_CANVAS_EPSILON)
+ ti_len = 1.0 / i_len;
+ else
+ ti_len = 0.0;
+ } else
+ ti_len = 1.0;
+
+ switch (priv->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_SW:
+ ti_len *= x;
+ break;
+
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_S:
+ ti_len *= x - w * si_len / 2;
+ break;
+
+ case GTK_ANCHOR_NE:
+ case GTK_ANCHOR_E:
+ case GTK_ANCHOR_SE:
+ ti_len *= x - w * si_len;
+ break;
+
+ default:
+ break;
+ }
+
+ if (priv->y_in_pixels) {
+ if (j_len > GNOME_CANVAS_EPSILON)
+ tj_len = 1.0 / j_len;
+ else
+ tj_len = 0.0;
+ } else
+ tj_len = 1.0;
+
+ switch (priv->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_NE:
+ tj_len *= y;
+ break;
+
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_E:
+ tj_len *= y - h * sj_len / 2;
+ break;
+
+ case GTK_ANCHOR_SW:
+ case GTK_ANCHOR_S:
+ case GTK_ANCHOR_SE:
+ tj_len *= y - h * sj_len;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Compute the final affine */
+
+ art_affine_scale (scale, si_len, sj_len);
+ art_affine_translate (translate, ti_len, tj_len);
+ art_affine_multiply (viewport_affine, scale, translate);
+}
+
+/* Computes the affine transformation with which the pixbuf needs to be
+ * transformed to render it on the canvas. This is not the same as the
+ * item_to_canvas transformation because we may need to scale the pixbuf
+ * by some other amount.
+ */
+static void
+compute_render_affine (GnomeCanvasPixbuf *gcp, double *ra, double *i2c)
+{
+ double va[6];
+
+ compute_viewport_affine (gcp, va, i2c);
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+ g_print ("va %g %g %g %g %g %g\n", va[0], va[1], va[2], va[3], va[4], va[5]);
+#endif
+ art_affine_multiply (ra, va, i2c);
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+ g_print ("ra %g %g %g %g %g %g\n", ra[0], ra[1], ra[2], ra[3], ra[4], ra[5]);
+#endif
+}
+
+/* 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.
+ */
+static void
+recompute_bounding_box (GnomeCanvasPixbuf *gcp, gdouble *i2c)
+{
+ GnomeCanvasItem *item;
+ PixbufPrivate *priv;
+ double ra[6];
+ ArtDRect rect;
+
+ item = GNOME_CANVAS_ITEM (gcp);
+ priv = gcp->priv;
+
+ if (!priv->pixbuf) {
+ item->x1 = item->y1 = item->x2 = item->y2 = 0.0;
+ return;
+ }
+
+ rect.x0 = 0.0;
+ rect.x1 = gdk_pixbuf_get_width (priv->pixbuf);
+
+ rect.y0 = 0.0;
+ rect.y1 = gdk_pixbuf_get_height (priv->pixbuf);
+
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+ g_print ("i2c %g %g %g %g %g %g\n", i2c[0], i2c[1], i2c[2], i2c[3], i2c[4], i2c[5]);
+#endif
+ gnome_canvas_item_i2c_affine (item, i2c);
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+ g_print ("i2c %g %g %g %g %g %g\n", i2c[0], i2c[1], i2c[2], i2c[3], i2c[4], i2c[5]);
+#endif
+ compute_render_affine (gcp, ra, i2c);
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+ g_print ("ra %g %g %g %g %g %g\n", ra[0], ra[1], ra[2], ra[3], ra[4], ra[5]);
+#endif
+ art_drect_affine_transform (&rect, &rect, ra);
+
+ item->x1 = floor (rect.x0);
+ item->y1 = floor (rect.y0);
+ item->x2 = ceil (rect.x1);
+ item->y2 = ceil (rect.y1);
+}
+
+
+
+/* Update sequence */
+
+/* Update handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+
+ gcp = GNOME_CANVAS_PIXBUF (item);
+ priv = gcp->priv;
+
+ if (parent_class->update)
+ (* parent_class->update) (item, affine, clip_path, flags);
+
+ /* the optimzations below cause rarely triggered redrawing bugs and
+ * don't seem to actually save much performance. so it's probably
+ * better to turn them off, than to chase subtle optimization bugs
+ * throughgout all of gnome-canvas-pixbuf.c - TIMJ
+ */
+#ifdef USE_BROKEN_OPTIMIZATIONS
+ if (((flags & GNOME_CANVAS_UPDATE_VISIBILITY)
+ && !(GTK_OBJECT_FLAGS (item) & GNOME_CANVAS_ITEM_VISIBLE))
+ || (flags & GNOME_CANVAS_UPDATE_AFFINE)
+ || priv->need_pixbuf_update
+ || priv->need_xform_update) {
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+ }
+
+ /* 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 & GNOME_CANVAS_UPDATE_VISIBILITY)
+ && (GTK_OBJECT_FLAGS (gcp) & GNOME_CANVAS_ITEM_VISIBLE))
+ || (flags & GNOME_CANVAS_UPDATE_AFFINE)) {
+ recompute_bounding_box (gcp, affine);
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+ g_print ("BBox is %g %g %g %g\n", item->x1, item->y1, item->x2, item->y2);
+#endif
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+ priv->need_pixbuf_update = FALSE;
+ priv->need_xform_update = FALSE;
+ }
+#else /* ordinary update logic */
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+ recompute_bounding_box (gcp, affine);
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+ priv->need_pixbuf_update = FALSE;
+ priv->need_xform_update = FALSE;
+#endif
+}
+
+
+
+/* Rendering */
+
+/* This is private to libart, but we need it. Sigh. */
+extern void art_rgb_affine_run (int *p_x0, int *p_x1, int y, int src_width, int src_height,
+ const double affine[6]);
+
+/* Fills the specified buffer with the transformed version of a pixbuf */
+static void
+transform_pixbuf (guchar *dest, int x, int y, int width, int height, int rowstride,
+ GdkPixbuf *pixbuf, double *affine)
+{
+ int xx, yy;
+ double inv[6];
+ guchar *src, *d;
+ ArtPoint src_p, dest_p;
+ int run_x1, run_x2;
+ int src_x, src_y;
+ int i;
+
+ art_affine_invert (inv, affine);
+
+ for (yy = 0; yy < height; yy++) {
+ dest_p.y = y + yy + 0.5;
+
+ run_x1 = x;
+ run_x2 = x + width;
+ art_rgb_affine_run (&run_x1, &run_x2, yy + y,
+ gdk_pixbuf_get_width (pixbuf),
+ gdk_pixbuf_get_height (pixbuf),
+ inv);
+
+ d = dest + yy * rowstride + (run_x1 - x) * 4;
+
+ for (xx = run_x1; xx < run_x2; xx++) {
+ dest_p.x = xx + 0.5;
+ art_affine_point (&src_p, &dest_p, inv);
+ src_x = floor (src_p.x);
+ src_y = floor (src_p.y);
+
+ src =
+ gdk_pixbuf_get_pixels (pixbuf) + src_y *
+ gdk_pixbuf_get_rowstride (pixbuf) + src_x *
+ gdk_pixbuf_get_n_channels (pixbuf);
+
+ for (i = 0; i < gdk_pixbuf_get_n_channels (pixbuf); i++)
+ *d++ = *src++;
+
+ if (!gdk_pixbuf_get_has_alpha(pixbuf))
+ *d++ = 255; /* opaque */
+ }
+ }
+}
+
+/* Draw handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height)
+{
+ GnomeCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+ double i2c[6], render_affine[6];
+ guchar *buf;
+ GdkPixbuf *pixbuf;
+ ArtIRect p_rect, a_rect, d_rect;
+ int w, h;
+
+ gcp = GNOME_CANVAS_PIXBUF (item);
+ priv = gcp->priv;
+
+ if (!priv->pixbuf)
+ return;
+
+ gnome_canvas_item_i2c_affine (item, i2c);
+ compute_render_affine (gcp, render_affine, i2c);
+
+ /* Compute the area we need to repaint */
+
+ p_rect.x0 = item->x1;
+ p_rect.y0 = item->y1;
+ p_rect.x1 = item->x2;
+ p_rect.y1 = item->y2;
+
+ a_rect.x0 = x;
+ a_rect.y0 = y;
+ a_rect.x1 = x + width;
+ a_rect.y1 = y + height;
+
+ art_irect_intersect (&d_rect, &p_rect, &a_rect);
+ if (art_irect_empty (&d_rect))
+ return;
+
+ /* Create a temporary buffer and transform the pixbuf there */
+
+ w = d_rect.x1 - d_rect.x0;
+ h = d_rect.y1 - d_rect.y0;
+
+ buf = g_new0 (guchar, w * h * 4);
+ transform_pixbuf (buf,
+ d_rect.x0, d_rect.y0,
+ w, h,
+ w * 4,
+ priv->pixbuf, render_affine);
+
+ pixbuf = gdk_pixbuf_new_from_data (buf, GDK_COLORSPACE_RGB,
+ TRUE,
+ 8, w, h,
+ w * 4,
+ NULL, NULL);
+
+ gdk_draw_pixbuf (drawable, NULL, pixbuf,
+ 0, 0,
+ d_rect.x0 - x, d_rect.y0 - y,
+ w, h,
+ GDK_RGB_DITHER_MAX,
+ d_rect.x0, d_rect.y0);
+
+ g_object_unref (pixbuf);
+ g_free (buf);
+}
+
+/* Render handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
+{
+ GnomeCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+ double i2c[6], render_affine[6];
+
+ gcp = GNOME_CANVAS_PIXBUF (item);
+ priv = gcp->priv;
+
+ if (!priv->pixbuf)
+ return;
+
+ gnome_canvas_item_i2c_affine (item, i2c);
+ compute_render_affine (gcp, render_affine, i2c);
+ gnome_canvas_buf_ensure_buf (buf);
+
+
+ if ((fabs (render_affine[1]) < GNOME_CANVAS_EPSILON) &&
+ (fabs (render_affine[2]) < GNOME_CANVAS_EPSILON) &&
+ render_affine[0] > 0.0 &&
+ render_affine[3] > 0.0)
+ {
+ GdkPixbuf *dest_pixbuf;
+ int x0, y0, x1, y1;
+
+ dest_pixbuf = gdk_pixbuf_new_from_data (buf->buf,
+ GDK_COLORSPACE_RGB,
+ FALSE,
+ 8,
+ buf->rect.x1 - buf->rect.x0,
+ buf->rect.y1 - buf->rect.y0,
+ buf->buf_rowstride,
+ NULL, NULL);
+
+
+ x0 = floor (render_affine[4] - buf->rect.x0 + 0.5);
+ y0 = floor (render_affine[5] - buf->rect.y0 + 0.5);
+
+ x1 = x0 + floor (gdk_pixbuf_get_width (priv->pixbuf) * render_affine[0] + 0.5);
+ y1 = y0 + floor (gdk_pixbuf_get_height (priv->pixbuf) * render_affine[3] + 0.5);
+
+ x0 = MAX (x0, 0);
+ x0 = MIN (x0, buf->rect.x1 - buf->rect.x0);
+ y0 = MAX (y0, 0);
+ y0 = MIN (y0, buf->rect.y1 - buf->rect.y0);
+
+ x1 = MAX (x1, 0);
+ x1 = MIN (x1, buf->rect.x1 - buf->rect.x0);
+ y1 = MAX (y1, 0);
+ y1 = MIN (y1, buf->rect.y1 - buf->rect.y0);
+
+ gdk_pixbuf_composite (priv->pixbuf,
+ dest_pixbuf,
+ x0, y0,
+ x1 - x0, y1 - y0,
+ render_affine[4] - buf->rect.x0,
+ render_affine[5] - buf->rect.y0,
+ render_affine[0],
+ render_affine[3],
+ GDK_INTERP_BILINEAR,
+ 255);
+
+ g_object_unref (dest_pixbuf);
+ }
+ else if (gdk_pixbuf_get_has_alpha(priv->pixbuf))
+ art_rgb_rgba_affine (buf->buf,
+ buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
+ buf->buf_rowstride,
+ gdk_pixbuf_get_pixels(priv->pixbuf),
+ gdk_pixbuf_get_width(priv->pixbuf),
+ gdk_pixbuf_get_height(priv->pixbuf),
+ gdk_pixbuf_get_rowstride(priv->pixbuf),
+ render_affine,
+ ART_FILTER_NEAREST,
+ NULL);
+ else
+ art_rgb_affine (buf->buf,
+ buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
+ buf->buf_rowstride,
+ gdk_pixbuf_get_pixels(priv->pixbuf),
+ gdk_pixbuf_get_width(priv->pixbuf),
+ gdk_pixbuf_get_height(priv->pixbuf),
+ gdk_pixbuf_get_rowstride(priv->pixbuf),
+ render_affine,
+ ART_FILTER_NEAREST,
+ NULL);
+
+ buf->is_bg = 0;
+}
+
+
+
+/* Point handler for the pixbuf canvas item */
+static double
+gnome_canvas_pixbuf_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
+ GnomeCanvasItem **actual_item)
+{
+ GnomeCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+ double i2c[6], render_affine[6], inv[6];
+ ArtPoint c, p;
+ int px, py;
+ double no_hit;
+ guchar *src;
+ GdkPixbuf *pixbuf;
+
+ gcp = GNOME_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;
+
+ gnome_canvas_item_i2c_affine (item, i2c);
+ compute_render_affine (gcp, render_affine, i2c);
+ art_affine_invert (inv, render_affine);
+
+ c.x = cx;
+ c.y = cy;
+ art_affine_point (&p, &c, inv);
+ px = p.x;
+ py = p.y;
+
+ if (px < 0 || px >= gdk_pixbuf_get_width (pixbuf) ||
+ py < 0 || py >= gdk_pixbuf_get_height (pixbuf))
+ return no_hit;
+
+ if (!gdk_pixbuf_get_has_alpha (pixbuf))
+ return 0.0;
+
+ 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;
+}
+
+
+
+/* Bounds handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ GnomeCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+ double i2c[6], viewport_affine[6];
+ ArtDRect rect;
+
+ gcp = GNOME_CANVAS_PIXBUF (item);
+ priv = gcp->priv;
+
+ if (!priv->pixbuf) {
+ *x1 = *y1 = *x2 = *y2 = 0.0;
+ return;
+ }
+
+ rect.x0 = 0.0;
+ rect.x1 = gdk_pixbuf_get_width (priv->pixbuf);
+
+ rect.y0 = 0.0;
+ rect.y1 = gdk_pixbuf_get_height (priv->pixbuf);
+
+ gnome_canvas_item_i2c_affine (item, i2c);
+ compute_viewport_affine (gcp, viewport_affine, i2c);
+ art_drect_affine_transform (&rect, &rect, viewport_affine);
+
+ *x1 = rect.x0;
+ *y1 = rect.y0;
+ *x2 = rect.x1;
+ *y2 = rect.y1;
+}
diff --git a/libgnomecanvas/gnome-canvas-pixbuf.h b/libgnomecanvas/gnome-canvas-pixbuf.h
new file mode 100644
index 0000000000..0554ab8624
--- /dev/null
+++ b/libgnomecanvas/gnome-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 GNOME_CANVAS_PIXBUF_H
+#define GNOME_CANVAS_PIXBUF_H
+
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+G_BEGIN_DECLS
+
+
+
+#define GNOME_TYPE_CANVAS_PIXBUF (gnome_canvas_pixbuf_get_type ())
+#define GNOME_CANVAS_PIXBUF(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_PIXBUF, GnomeCanvasPixbuf))
+#define GNOME_CANVAS_PIXBUF_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_PIXBUF, GnomeCanvasPixbufClass))
+#define GNOME_IS_CANVAS_PIXBUF(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_PIXBUF))
+#define GNOME_IS_CANVAS_PIXBUF_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_PIXBUF))
+#define GNOME_CANVAS_PIXBUF_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_PIXBUF, GnomeCanvasPixbufClass))
+
+
+typedef struct _GnomeCanvasPixbuf GnomeCanvasPixbuf;
+typedef struct _GnomeCanvasPixbufClass GnomeCanvasPixbufClass;
+
+struct _GnomeCanvasPixbuf {
+ GnomeCanvasItem item;
+
+ /* Private data */
+ gpointer priv;
+};
+
+struct _GnomeCanvasPixbufClass {
+ GnomeCanvasItemClass parent_class;
+};
+
+
+GType gnome_canvas_pixbuf_get_type (void) G_GNUC_CONST;
+
+
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-polygon.c b/libgnomecanvas/gnome-canvas-polygon.c
new file mode 100644
index 0000000000..53c7965a88
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-polygon.c
@@ -0,0 +1,246 @@
+/*
+ * 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 GnomeCanvas widget
+ *
+ * GnomeCanvas 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>
+ * Rusty Conover <rconover@bangtail.net>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_svp_vpath_stroke.h>
+#include "libgnomecanvas.h"
+
+#include "gnome-canvas-shape.h"
+
+#define NUM_STATIC_POINTS 256 /* Number of static points to use to avoid allocating arrays */
+
+enum {
+ PROP_0,
+ PROP_POINTS
+};
+
+static void gnome_canvas_polygon_class_init (GnomeCanvasPolygonClass *class);
+static void gnome_canvas_polygon_init (GnomeCanvasPolygon *poly);
+static void gnome_canvas_polygon_destroy (GtkObject *object);
+static void gnome_canvas_polygon_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_polygon_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_polygon_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+
+static GnomeCanvasItemClass *parent_class;
+
+GType
+gnome_canvas_polygon_get_type (void)
+{
+ static GType polygon_type;
+
+ if (!polygon_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasPolygonClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_polygon_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasPolygon),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_polygon_init,
+ NULL /* value_table */
+ };
+
+ polygon_type = g_type_register_static (GNOME_TYPE_CANVAS_SHAPE, "GnomeCanvasPolygon",
+ &object_info, 0);
+ }
+
+ return polygon_type;
+}
+
+static void
+gnome_canvas_polygon_class_init (GnomeCanvasPolygonClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = gnome_canvas_polygon_set_property;
+ gobject_class->get_property = gnome_canvas_polygon_get_property;
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_POINTS,
+ g_param_spec_boxed ("points", NULL, NULL,
+ GNOME_TYPE_CANVAS_POINTS,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ object_class->destroy = gnome_canvas_polygon_destroy;
+
+ item_class->update = gnome_canvas_polygon_update;
+}
+
+static void
+gnome_canvas_polygon_init (GnomeCanvasPolygon *poly)
+{
+ poly->path_def = NULL;
+}
+
+static void
+gnome_canvas_polygon_destroy (GtkObject *object)
+{
+ GnomeCanvasPolygon *poly;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_POLYGON (object));
+
+ poly = GNOME_CANVAS_POLYGON (object);
+
+ /* remember, destroy can be run multiple times! */
+
+ if(poly->path_def)
+ gnome_canvas_path_def_unref(poly->path_def);
+
+ poly->path_def = NULL;
+
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+set_points (GnomeCanvasPolygon *poly, GnomeCanvasPoints *points)
+{
+ int i;
+
+
+ if (poly->path_def)
+ gnome_canvas_path_def_unref(poly->path_def);
+
+ if (!points) {
+ poly->path_def = gnome_canvas_path_def_new();
+ gnome_canvas_shape_set_path_def (GNOME_CANVAS_SHAPE (poly), poly->path_def);
+ return;
+ }
+
+
+ /* Optomize the path def to the number of points */
+ poly->path_def = gnome_canvas_path_def_new_sized(points->num_points+1);
+
+#if 0
+ /* No need for explicit duplicate, as closepaths does it for us (Lauris) */
+ /* 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]));
+#endif
+
+
+ gnome_canvas_path_def_moveto (poly->path_def, points->coords[0], points->coords[1]);
+
+ for (i = 1; i < points->num_points; i++) {
+ gnome_canvas_path_def_lineto(poly->path_def, points->coords[i * 2], points->coords[(i * 2) + 1]);
+ }
+
+ gnome_canvas_path_def_closepath (poly->path_def);
+
+ gnome_canvas_shape_set_path_def (GNOME_CANVAS_SHAPE (poly), poly->path_def);
+}
+
+
+static void
+gnome_canvas_polygon_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasPolygon *poly;
+ GnomeCanvasPoints *points;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_POLYGON (object));
+
+ item = GNOME_CANVAS_ITEM (object);
+ poly = GNOME_CANVAS_POLYGON (object);
+
+ switch (param_id) {
+ case PROP_POINTS:
+ points = g_value_get_boxed (value);
+
+ set_points (poly, points);
+
+ gnome_canvas_item_request_update (item);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gnome_canvas_polygon_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_POLYGON (object));
+
+ switch (param_id) {
+ case PROP_POINTS:
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gnome_canvas_polygon_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ /* Since the path has already been defined just pass the update up. */
+
+ if (parent_class->update)
+ (* parent_class->update) (item, affine, clip_path, flags);
+}
diff --git a/libgnomecanvas/gnome-canvas-polygon.h b/libgnomecanvas/gnome-canvas-polygon.h
new file mode 100644
index 0000000000..738f0f453d
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-polygon.h
@@ -0,0 +1,88 @@
+/*
+ * 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 GnomeCanvas widget
+ *
+ * GnomeCanvas 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>
+ * Rusty Conover <rconover@bangtail.net>
+ */
+
+#ifndef GNOME_CANVAS_POLYGON_H
+#define GNOME_CANVAS_POLYGON_H
+
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-shape.h>
+#include <libgnomecanvas/gnome-canvas-path-def.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 GnomeCanvasPoints* RW Pointer to a GnomeCanvasPoints structure.
+ * This can be created by a call to
+ * gnome_canvas_points_new() (in gnome-canvas-util.h).
+ * X coordinates are in the even indices of the
+ * points->coords array, Y coordinates are in
+ * the odd indices.
+ */
+
+#define GNOME_TYPE_CANVAS_POLYGON (gnome_canvas_polygon_get_type ())
+#define GNOME_CANVAS_POLYGON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_POLYGON, GnomeCanvasPolygon))
+#define GNOME_CANVAS_POLYGON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_POLYGON, GnomeCanvasPolygonClass))
+#define GNOME_IS_CANVAS_POLYGON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_POLYGON))
+#define GNOME_IS_CANVAS_POLYGON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_POLYGON))
+#define GNOME_CANVAS_POLYGON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_POLYGON, GnomeCanvasPolygonClass))
+
+
+typedef struct _GnomeCanvasPolygon GnomeCanvasPolygon;
+typedef struct _GnomeCanvasPolygonClass GnomeCanvasPolygonClass;
+
+struct _GnomeCanvasPolygon {
+ GnomeCanvasShape item;
+
+ GnomeCanvasPathDef *path_def;
+};
+
+struct _GnomeCanvasPolygonClass {
+ GnomeCanvasShapeClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_polygon_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+#endif
diff --git a/libgnomecanvas/gnome-canvas-rect-ellipse.c b/libgnomecanvas/gnome-canvas-rect-ellipse.c
new file mode 100644
index 0000000000..10136866ef
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-rect-ellipse.c
@@ -0,0 +1,431 @@
+/*
+ * 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 GnomeCanvas widget
+ *
+ * GnomeCanvas 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>
+ * Rusty Conover <rconover@bangtail.net>
+ */
+
+#include <config.h>
+#include <math.h>
+#include "gnome-canvas-rect-ellipse.h"
+#include "gnome-canvas-util.h"
+#include "gnome-canvas-shape.h"
+
+
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_rgb_svp.h>
+
+/* Base class for rectangle and ellipse item types */
+
+#define noVERBOSE
+
+enum {
+ PROP_0,
+ PROP_X1,
+ PROP_Y1,
+ PROP_X2,
+ PROP_Y2
+};
+
+
+static void gnome_canvas_re_class_init (GnomeCanvasREClass *class);
+static void gnome_canvas_re_init (GnomeCanvasRE *re);
+static void gnome_canvas_re_destroy (GtkObject *object);
+static void gnome_canvas_re_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_re_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_rect_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+static void gnome_canvas_ellipse_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+
+static GnomeCanvasItemClass *re_parent_class;
+
+
+GType
+gnome_canvas_re_get_type (void)
+{
+ static GType re_type;
+
+ if (!re_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasREClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_re_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasRE),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_re_init,
+ NULL /* value_table */
+ };
+
+ re_type = g_type_register_static (GNOME_TYPE_CANVAS_SHAPE, "GnomeCanvasRE",
+ &object_info, 0);
+ }
+
+ return re_type;
+}
+
+static void
+gnome_canvas_re_class_init (GnomeCanvasREClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+
+ re_parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = gnome_canvas_re_set_property;
+ gobject_class->get_property = gnome_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_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y1,
+ g_param_spec_double ("y1", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X2,
+ g_param_spec_double ("x2", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y2,
+ g_param_spec_double ("y2", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ object_class->destroy = gnome_canvas_re_destroy;
+}
+
+static void
+gnome_canvas_re_init (GnomeCanvasRE *re)
+{
+ re->x1 = 0.0;
+ re->y1 = 0.0;
+ re->x2 = 0.0;
+ re->y2 = 0.0;
+ re->path_dirty = 0;
+}
+
+static void
+gnome_canvas_re_destroy (GtkObject *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_RE (object));
+
+ if (GTK_OBJECT_CLASS (re_parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (re_parent_class)->destroy) (object);
+}
+
+static void
+gnome_canvas_re_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasRE *re;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_RE (object));
+
+ item = GNOME_CANVAS_ITEM (object);
+ re = GNOME_CANVAS_RE (object);
+
+ switch (param_id) {
+ case PROP_X1:
+ re->x1 = g_value_get_double (value);
+ re->path_dirty = 1;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_Y1:
+ re->y1 = g_value_get_double (value);
+ re->path_dirty = 1;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_X2:
+ re->x2 = g_value_get_double (value);
+ re->path_dirty = 1;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_Y2:
+ re->y2 = g_value_get_double (value);
+ re->path_dirty = 1;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gnome_canvas_re_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasRE *re;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_RE (object));
+
+ re = GNOME_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;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+/* Rectangle item */
+static void gnome_canvas_rect_class_init (GnomeCanvasRectClass *class);
+
+
+
+GType
+gnome_canvas_rect_get_type (void)
+{
+ static GType rect_type;
+
+ if (!rect_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasRectClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_rect_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasRect),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) NULL,
+ NULL /* value_table */
+ };
+
+ rect_type = g_type_register_static (GNOME_TYPE_CANVAS_RE, "GnomeCanvasRect",
+ &object_info, 0);
+ }
+
+ return rect_type;
+}
+
+static void
+gnome_canvas_rect_class_init (GnomeCanvasRectClass *class)
+{
+ GnomeCanvasItemClass *item_class;
+
+ item_class = (GnomeCanvasItemClass *) class;
+
+ item_class->update = gnome_canvas_rect_update;
+}
+
+static void
+gnome_canvas_rect_update (GnomeCanvasItem *item, double affine[6], ArtSVP *clip_path, gint flags)
+{ GnomeCanvasRE *re;
+
+ GnomeCanvasPathDef *path_def;
+
+ re = GNOME_CANVAS_RE(item);
+
+ if (re->path_dirty) {
+ path_def = gnome_canvas_path_def_new ();
+
+ gnome_canvas_path_def_moveto(path_def, re->x1, re->y1);
+ gnome_canvas_path_def_lineto(path_def, re->x2, re->y1);
+ gnome_canvas_path_def_lineto(path_def, re->x2, re->y2);
+ gnome_canvas_path_def_lineto(path_def, re->x1, re->y2);
+ gnome_canvas_path_def_lineto(path_def, re->x1, re->y1);
+ gnome_canvas_path_def_closepath_current(path_def);
+ gnome_canvas_shape_set_path_def (GNOME_CANVAS_SHAPE (item), path_def);
+ gnome_canvas_path_def_unref(path_def);
+ re->path_dirty = 0;
+ }
+
+ if (re_parent_class->update)
+ (* re_parent_class->update) (item, affine, clip_path, flags);
+}
+
+/* Ellipse item */
+
+
+static void gnome_canvas_ellipse_class_init (GnomeCanvasEllipseClass *class);
+
+
+GType
+gnome_canvas_ellipse_get_type (void)
+{
+ static GType ellipse_type;
+
+ if (!ellipse_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasEllipseClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_ellipse_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasEllipse),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) NULL,
+ NULL /* value_table */
+ };
+
+ ellipse_type = g_type_register_static (GNOME_TYPE_CANVAS_RE, "GnomeCanvasEllipse",
+ &object_info, 0);
+ }
+
+ return ellipse_type;
+}
+
+static void
+gnome_canvas_ellipse_class_init (GnomeCanvasEllipseClass *class)
+{
+ GnomeCanvasItemClass *item_class;
+
+ item_class = (GnomeCanvasItemClass *) class;
+
+ item_class->update = gnome_canvas_ellipse_update;
+}
+
+#define N_PTS 90
+
+static void
+gnome_canvas_ellipse_update (GnomeCanvasItem *item, double affine[6], ArtSVP *clip_path, gint flags) {
+ GnomeCanvasPathDef *path_def;
+ GnomeCanvasRE *re;
+
+ re = GNOME_CANVAS_RE(item);
+
+ if (re->path_dirty) {
+ gdouble cx, cy, rx, ry;
+ gdouble beta = 0.26521648983954400922; /* 4*(1-cos(pi/8))/(3*sin(pi/8)) */
+ gdouble sincosA = 0.70710678118654752440; /* sin (pi/4), cos (pi/4) */
+ gdouble dx1, dy1, dx2, dy2;
+ gdouble mx, my;
+
+ path_def = gnome_canvas_path_def_new();
+
+ cx = (re->x2 + re->x1) * 0.5;
+ cy = (re->y2 + re->y1) * 0.5;
+ rx = re->x2 - cx;
+ ry = re->y2 - cy;
+
+ dx1 = beta * rx;
+ dy1 = beta * ry;
+ dx2 = beta * rx * sincosA;
+ dy2 = beta * ry * sincosA;
+ mx = rx * sincosA;
+ my = ry * sincosA;
+
+ gnome_canvas_path_def_moveto (path_def, cx + rx, cy);
+ gnome_canvas_path_def_curveto (path_def,
+ cx + rx, cy - dy1,
+ cx + mx + dx2, cy - my + dy2,
+ cx + mx, cy - my);
+ gnome_canvas_path_def_curveto (path_def,
+ cx + mx - dx2, cy - my - dy2,
+ cx + dx1, cy - ry,
+ cx, cy - ry);
+ gnome_canvas_path_def_curveto (path_def,
+ cx - dx1, cy - ry,
+ cx - mx + dx2, cy - my - dy2,
+ cx - mx, cy - my);
+ gnome_canvas_path_def_curveto (path_def,
+ cx - mx - dx2, cy - my + dy2,
+ cx - rx, cy - dy1,
+ cx - rx, cy);
+
+ gnome_canvas_path_def_curveto (path_def,
+ cx - rx, cy + dy1,
+ cx - mx - dx2, cy + my - dy2,
+ cx - mx, cy + my);
+ gnome_canvas_path_def_curveto (path_def,
+ cx - mx + dx2, cy + my + dy2,
+ cx - dx1, cy + ry,
+ cx, cy + ry);
+ gnome_canvas_path_def_curveto (path_def,
+ cx + dx1, cy + ry,
+ cx + mx - dx2, cy + my + dy2,
+ cx + mx, cy + my);
+ gnome_canvas_path_def_curveto (path_def,
+ cx + mx + dx2, cy + my - dy2,
+ cx + rx, cy + dy1,
+ cx + rx, cy);
+
+ gnome_canvas_path_def_closepath_current(path_def);
+
+ gnome_canvas_shape_set_path_def (GNOME_CANVAS_SHAPE (item), path_def);
+ gnome_canvas_path_def_unref(path_def);
+ re->path_dirty = 0;
+ }
+
+ if (re_parent_class->update)
+ (* re_parent_class->update) (item, affine, clip_path, flags);
+}
diff --git a/libgnomecanvas/gnome-canvas-rect-ellipse.h b/libgnomecanvas/gnome-canvas-rect-ellipse.h
new file mode 100644
index 0000000000..494e6cf485
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-rect-ellipse.h
@@ -0,0 +1,159 @@
+/*
+ * 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 GnomeCanvas widget
+ *
+ * GnomeCanvas 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 GNOME_CANVAS_RECT_ELLIPSE_H
+#define GNOME_CANVAS_RECT_ELLIPSE_H
+
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+#include <libgnomecanvas/gnome-canvas-shape.h>
+
+#include <libart_lgpl/art_svp.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 GNOME_TYPE_CANVAS_RE (gnome_canvas_re_get_type ())
+#define GNOME_CANVAS_RE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_RE, GnomeCanvasRE))
+#define GNOME_CANVAS_RE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_RE, GnomeCanvasREClass))
+#define GNOME_IS_CANVAS_RE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_RE))
+#define GNOME_IS_CANVAS_RE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_RE))
+#define GNOME_CANVAS_RE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_RE, GnomeCanvasREClass))
+
+
+typedef struct _GnomeCanvasRE GnomeCanvasRE;
+typedef struct _GnomeCanvasREClass GnomeCanvasREClass;
+
+struct _GnomeCanvasRE {
+ GnomeCanvasShape item;
+
+ double x1, y1, x2, y2; /* Corners of item */
+
+ unsigned int path_dirty : 1;
+};
+
+struct _GnomeCanvasREClass {
+ GnomeCanvasShapeClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_re_get_type (void) G_GNUC_CONST;
+
+
+/* Rectangle item. No configurable or queryable arguments are available (use those in
+ * GnomeCanvasRE).
+ */
+
+
+#define GNOME_TYPE_CANVAS_RECT (gnome_canvas_rect_get_type ())
+#define GNOME_CANVAS_RECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_RECT, GnomeCanvasRect))
+#define GNOME_CANVAS_RECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_RECT, GnomeCanvasRectClass))
+#define GNOME_IS_CANVAS_RECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_RECT))
+#define GNOME_IS_CANVAS_RECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_RECT))
+#define GNOME_CANVAS_RECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_RECT, GnomeCanvasRectClass))
+
+
+typedef struct _GnomeCanvasRect GnomeCanvasRect;
+typedef struct _GnomeCanvasRectClass GnomeCanvasRectClass;
+
+struct _GnomeCanvasRect {
+ GnomeCanvasRE re;
+};
+
+struct _GnomeCanvasRectClass {
+ GnomeCanvasREClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_rect_get_type (void) G_GNUC_CONST;
+
+
+/* Ellipse item. No configurable or queryable arguments are available (use those in
+ * GnomeCanvasRE).
+ */
+
+
+#define GNOME_TYPE_CANVAS_ELLIPSE (gnome_canvas_ellipse_get_type ())
+#define GNOME_CANVAS_ELLIPSE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_ELLIPSE, GnomeCanvasEllipse))
+#define GNOME_CANVAS_ELLIPSE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_ELLIPSE, GnomeCanvasEllipseClass))
+#define GNOME_IS_CANVAS_ELLIPSE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_ELLIPSE))
+#define GNOME_IS_CANVAS_ELLIPSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_ELLIPSE))
+#define GNOME_CANVAS_ELLIPSE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_ELLIPSE, GnomeCanvasEllipseClass))
+
+
+typedef struct _GnomeCanvasEllipse GnomeCanvasEllipse;
+typedef struct _GnomeCanvasEllipseClass GnomeCanvasEllipseClass;
+
+struct _GnomeCanvasEllipse {
+ GnomeCanvasRE re;
+};
+
+struct _GnomeCanvasEllipseClass {
+ GnomeCanvasREClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_ellipse_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-rich-text.c b/libgnomecanvas/gnome-canvas-rich-text.c
new file mode 100644
index 0000000000..2a50f2ed4d
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-rich-text.c
@@ -0,0 +1,2202 @@
+/* Editable GnomeCanvas text item based on GtkTextLayout, borrowed heavily
+ * from GtkTextView.
+ *
+ * Copyright (c) 2000 Red Hat, Inc.
+ * Copyright (c) 2001 Joe Shaw
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
+#include <gtk/gtktextdisplay.h>
+
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+#include "gnome-canvas-rich-text.h"
+#include "gnome-canvas-i18n.h"
+
+struct _GnomeCanvasRichTextPrivate {
+ GtkTextLayout *layout;
+ GtkTextBuffer *buffer;
+
+ char *text;
+
+ /* Position at anchor */
+ double x, y;
+ /* Dimensions */
+ double width, height;
+ /* Top-left canvas coordinates for text */
+ int cx, cy;
+
+ gboolean cursor_visible;
+ gboolean cursor_blink;
+ gboolean editable;
+ gboolean visible;
+ gboolean grow_height;
+ GtkWrapMode wrap_mode;
+ GtkJustification justification;
+ GtkTextDirection direction;
+ GtkAnchorType anchor;
+ int pixels_above_lines;
+ int pixels_below_lines;
+ int pixels_inside_wrap;
+ int left_margin;
+ int right_margin;
+ int indent;
+
+ guint preblink_timeout;
+ guint blink_timeout;
+
+ guint selection_drag_handler;
+
+ gint drag_start_x;
+ gint drag_start_y;
+
+ gboolean just_selected_element;
+
+ int clicks;
+ guint click_timeout;
+};
+
+enum {
+ PROP_0,
+ PROP_TEXT,
+ PROP_X,
+ PROP_Y,
+ PROP_WIDTH,
+ PROP_HEIGHT,
+ PROP_EDITABLE,
+ PROP_VISIBLE,
+ PROP_CURSOR_VISIBLE,
+ PROP_CURSOR_BLINK,
+ PROP_GROW_HEIGHT,
+ PROP_WRAP_MODE,
+ PROP_JUSTIFICATION,
+ PROP_DIRECTION,
+ PROP_ANCHOR,
+ PROP_PIXELS_ABOVE_LINES,
+ PROP_PIXELS_BELOW_LINES,
+ PROP_PIXELS_INSIDE_WRAP,
+ PROP_LEFT_MARGIN,
+ PROP_RIGHT_MARGIN,
+ PROP_INDENT
+};
+
+enum {
+ TAG_CHANGED,
+ LAST_SIGNAL
+};
+
+static GnomeCanvasItemClass *parent_class;
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void gnome_canvas_rich_text_class_init(GnomeCanvasRichTextClass *klass);
+static void gnome_canvas_rich_text_init(GnomeCanvasRichText *text);
+static void gnome_canvas_rich_text_set_property(GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec);
+static void gnome_canvas_rich_text_get_property(GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec);
+static void gnome_canvas_rich_text_update(GnomeCanvasItem *item, double *affine,
+ ArtSVP *clip_path, int flags);
+static void gnome_canvas_rich_text_realize(GnomeCanvasItem *item);
+static void gnome_canvas_rich_text_unrealize(GnomeCanvasItem *item);
+static double gnome_canvas_rich_text_point(GnomeCanvasItem *item,
+ double x, double y,
+ int cx, int cy,
+ GnomeCanvasItem **actual_item);
+static void gnome_canvas_rich_text_draw(GnomeCanvasItem *item,
+ GdkDrawable *drawable,
+ int x, int y, int width, int height);
+static void gnome_canvas_rich_text_render(GnomeCanvasItem *item,
+ GnomeCanvasBuf *buf);
+static gint gnome_canvas_rich_text_event(GnomeCanvasItem *item,
+ GdkEvent *event);
+static void gnome_canvas_rich_text_get_bounds(GnomeCanvasItem *text, double *px1, double *py1,
+ double *px2, double *py2);
+
+static void gnome_canvas_rich_text_ensure_layout(GnomeCanvasRichText *text);
+static void gnome_canvas_rich_text_destroy_layout(GnomeCanvasRichText *text);
+static void gnome_canvas_rich_text_start_cursor_blink(GnomeCanvasRichText *text, gboolean delay);
+static void gnome_canvas_rich_text_stop_cursor_blink(GnomeCanvasRichText *text);
+static void gnome_canvas_rich_text_move_cursor(GnomeCanvasRichText *text,
+ GtkMovementStep step,
+ gint count,
+ gboolean extend_selection);
+
+
+
+static GtkTextBuffer *get_buffer(GnomeCanvasRichText *text);
+static gint blink_cb(gpointer data);
+
+#define PREBLINK_TIME 300
+#define CURSOR_ON_TIME 800
+#define CURSOR_OFF_TIME 400
+
+GType
+gnome_canvas_rich_text_get_type(void)
+{
+ static GType rich_text_type;
+
+ if (!rich_text_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasRichTextClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_rich_text_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasRichText),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_rich_text_init,
+ NULL /* value_table */
+ };
+
+ rich_text_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasRichText",
+ &object_info, 0);
+ }
+
+ return rich_text_type;
+}
+
+static void
+gnome_canvas_rich_text_finalize(GObject *object)
+{
+ GnomeCanvasRichText *text;
+
+ text = GNOME_CANVAS_RICH_TEXT(object);
+
+ g_free (text->_priv);
+ text->_priv = NULL;
+
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gnome_canvas_rich_text_class_init(GnomeCanvasRichTextClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+ GtkObjectClass *object_class = GTK_OBJECT_CLASS(klass);
+ GnomeCanvasItemClass *item_class = GNOME_CANVAS_ITEM_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->set_property = gnome_canvas_rich_text_set_property;
+ gobject_class->get_property = gnome_canvas_rich_text_get_property;
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_TEXT,
+ g_param_spec_string ("text",
+ _("Text"),
+ _("Text to display"),
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_X,
+ g_param_spec_double ("x",
+ _("X"),
+ _("X position"),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_Y,
+ g_param_spec_double ("y",
+ _("Y"),
+ _("Y position"),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_WIDTH,
+ g_param_spec_double ("width",
+ _("Width"),
+ _("Width for text box"),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_HEIGHT,
+ g_param_spec_double ("height",
+ _("Height"),
+ _("Height for text box"),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_EDITABLE,
+ g_param_spec_boolean ("editable",
+ _("Editable"),
+ _("Is this rich text item editable?"),
+ TRUE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_VISIBLE,
+ g_param_spec_boolean ("visible",
+ _("Visible"),
+ _("Is this rich text item visible?"),
+ TRUE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_CURSOR_VISIBLE,
+ g_param_spec_boolean ("cursor_visible",
+ _("Cursor Visible"),
+ _("Is the cursor visible in this rich text item?"),
+ TRUE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_CURSOR_BLINK,
+ g_param_spec_boolean ("cursor_blink",
+ _("Cursor Blink"),
+ _("Does the cursor blink in this rich text item?"),
+ TRUE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_GROW_HEIGHT,
+ g_param_spec_boolean ("grow_height",
+ _("Grow Height"),
+ _("Should the text box height grow if the text does not fit?"),
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_WRAP_MODE,
+ g_param_spec_enum ("wrap_mode",
+ _("Wrap Mode"),
+ _("Wrap mode for multiline text"),
+ GTK_TYPE_WRAP_MODE,
+ GTK_WRAP_WORD,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_JUSTIFICATION,
+ g_param_spec_enum ("justification",
+ _("Justification"),
+ _("Justification mode"),
+ GTK_TYPE_JUSTIFICATION,
+ GTK_JUSTIFY_LEFT,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_DIRECTION,
+ g_param_spec_enum ("direction",
+ _("Direction"),
+ _("Text direction"),
+ GTK_TYPE_DIRECTION_TYPE,
+ gtk_widget_get_default_direction (),
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_ANCHOR,
+ g_param_spec_enum ("anchor",
+ _("Anchor"),
+ _("Anchor point for text"),
+ GTK_TYPE_ANCHOR_TYPE,
+ GTK_ANCHOR_NW,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_PIXELS_ABOVE_LINES,
+ g_param_spec_int ("pixels_above_lines",
+ _("Pixels Above Lines"),
+ _("Number of pixels to put above lines"),
+ G_MININT, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_PIXELS_BELOW_LINES,
+ g_param_spec_int ("pixels_below_lines",
+ _("Pixels Below Lines"),
+ _("Number of pixels to put below lines"),
+ G_MININT, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_PIXELS_INSIDE_WRAP,
+ g_param_spec_int ("pixels_inside_wrap",
+ _("Pixels Inside Wrap"),
+ _("Number of pixels to put inside the wrap"),
+ G_MININT, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_LEFT_MARGIN,
+ g_param_spec_int ("left_margin",
+ _("Left Margin"),
+ _("Number of pixels in the left margin"),
+ G_MININT, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_RIGHT_MARGIN,
+ g_param_spec_int ("right_margin",
+ _("Right Margin"),
+ _("Number of pixels in the right margin"),
+ G_MININT, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_INDENT,
+ g_param_spec_int ("indent",
+ _("Indentation"),
+ _("Number of pixels for indentation"),
+ G_MININT, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ /* Signals */
+ signals[TAG_CHANGED] = g_signal_new(
+ "tag_changed",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(GnomeCanvasRichTextClass, tag_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+
+ gobject_class->finalize = gnome_canvas_rich_text_finalize;
+
+ item_class->update = gnome_canvas_rich_text_update;
+ item_class->realize = gnome_canvas_rich_text_realize;
+ item_class->unrealize = gnome_canvas_rich_text_unrealize;
+ item_class->draw = gnome_canvas_rich_text_draw;
+ item_class->point = gnome_canvas_rich_text_point;
+ item_class->render = gnome_canvas_rich_text_render;
+ item_class->event = gnome_canvas_rich_text_event;
+ item_class->bounds = gnome_canvas_rich_text_get_bounds;
+} /* gnome_canvas_rich_text_class_init */
+
+static void
+gnome_canvas_rich_text_init(GnomeCanvasRichText *text)
+{
+#if 0
+ GtkObject *object = GTK_OBJECT(text);
+
+ object->flags |= GNOME_CANVAS_ITEM_ALWAYS_REDRAW;
+#endif
+ text->_priv = g_new0(GnomeCanvasRichTextPrivate, 1);
+
+ /* Try to set some sane defaults */
+ text->_priv->cursor_visible = TRUE;
+ text->_priv->cursor_blink = TRUE;
+ text->_priv->editable = TRUE;
+ text->_priv->visible = TRUE;
+ text->_priv->grow_height = FALSE;
+ text->_priv->wrap_mode = GTK_WRAP_WORD;
+ text->_priv->justification = GTK_JUSTIFY_LEFT;
+ text->_priv->direction = gtk_widget_get_default_direction();
+ text->_priv->anchor = GTK_ANCHOR_NW;
+
+ text->_priv->blink_timeout = 0;
+ text->_priv->preblink_timeout = 0;
+
+ text->_priv->clicks = 0;
+ text->_priv->click_timeout = 0;
+} /* gnome_canvas_rich_text_init */
+
+static void
+gnome_canvas_rich_text_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(object);
+
+ switch (property_id) {
+ case PROP_TEXT:
+ if (text->_priv->text)
+ g_free(text->_priv->text);
+
+ text->_priv->text = g_value_dup_string (value);
+
+ gtk_text_buffer_set_text(
+ get_buffer(text), text->_priv->text, strlen(text->_priv->text));
+
+ break;
+ case PROP_X:
+ text->_priv->x = g_value_get_double (value);
+ break;
+ case PROP_Y:
+ text->_priv->y = g_value_get_double (value);
+ break;
+ case PROP_WIDTH:
+ text->_priv->width = g_value_get_double (value);
+ break;
+ case PROP_HEIGHT:
+ text->_priv->height = g_value_get_double (value);
+ break;
+ case PROP_EDITABLE:
+ text->_priv->editable = g_value_get_boolean (value);
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->editable =
+ text->_priv->editable;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_VISIBLE:
+ text->_priv->visible = g_value_get_boolean (value);
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->invisible =
+ !text->_priv->visible;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_CURSOR_VISIBLE:
+ text->_priv->cursor_visible = g_value_get_boolean (value);
+ if (text->_priv->layout) {
+ gtk_text_layout_set_cursor_visible(
+ text->_priv->layout, text->_priv->cursor_visible);
+
+ if (text->_priv->cursor_visible && text->_priv->cursor_blink) {
+ gnome_canvas_rich_text_start_cursor_blink(
+ text, FALSE);
+ }
+ else
+ gnome_canvas_rich_text_stop_cursor_blink(text);
+ }
+ break;
+ case PROP_CURSOR_BLINK:
+ text->_priv->cursor_blink = g_value_get_boolean (value);
+ if (text->_priv->layout && text->_priv->cursor_visible) {
+ if (text->_priv->cursor_blink && !text->_priv->blink_timeout) {
+ gnome_canvas_rich_text_start_cursor_blink(
+ text, FALSE);
+ }
+ else if (!text->_priv->cursor_blink && text->_priv->blink_timeout) {
+ gnome_canvas_rich_text_stop_cursor_blink(text);
+ gtk_text_layout_set_cursor_visible(
+ text->_priv->layout, TRUE);
+ }
+ }
+ break;
+ case PROP_GROW_HEIGHT:
+ text->_priv->grow_height = g_value_get_boolean (value);
+ /* FIXME: Recalc here */
+ break;
+ case PROP_WRAP_MODE:
+ text->_priv->wrap_mode = g_value_get_enum (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->wrap_mode =
+ text->_priv->wrap_mode;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_JUSTIFICATION:
+ text->_priv->justification = g_value_get_enum (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->justification =
+ text->_priv->justification;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_DIRECTION:
+ text->_priv->direction = g_value_get_enum (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->direction =
+ text->_priv->direction;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_ANCHOR:
+ text->_priv->anchor = g_value_get_enum (value);
+ break;
+ case PROP_PIXELS_ABOVE_LINES:
+ text->_priv->pixels_above_lines = g_value_get_int (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->pixels_above_lines =
+ text->_priv->pixels_above_lines;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_PIXELS_BELOW_LINES:
+ text->_priv->pixels_below_lines = g_value_get_int (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->pixels_below_lines =
+ text->_priv->pixels_below_lines;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_PIXELS_INSIDE_WRAP:
+ text->_priv->pixels_inside_wrap = g_value_get_int (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->pixels_inside_wrap =
+ text->_priv->pixels_inside_wrap;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_LEFT_MARGIN:
+ text->_priv->left_margin = g_value_get_int (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->left_margin =
+ text->_priv->left_margin;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_RIGHT_MARGIN:
+ text->_priv->right_margin = g_value_get_int (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->right_margin =
+ text->_priv->right_margin;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_INDENT:
+ text->_priv->pixels_above_lines = g_value_get_int (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->indent = text->_priv->indent;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+
+ gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(text));
+}
+
+static void
+gnome_canvas_rich_text_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(object);
+
+ switch (property_id) {
+ case PROP_TEXT:
+ g_value_set_string (value, text->_priv->text);
+ break;
+ case PROP_X:
+ g_value_set_double (value, text->_priv->x);
+ break;
+ case PROP_Y:
+ g_value_set_double (value, text->_priv->y);
+ break;
+ case PROP_HEIGHT:
+ g_value_set_double (value, text->_priv->height);
+ break;
+ case PROP_WIDTH:
+ g_value_set_double (value, text->_priv->width);
+ break;
+ case PROP_EDITABLE:
+ g_value_set_boolean (value, text->_priv->editable);
+ break;
+ case PROP_CURSOR_VISIBLE:
+ g_value_set_boolean (value, text->_priv->cursor_visible);
+ break;
+ case PROP_CURSOR_BLINK:
+ g_value_set_boolean (value, text->_priv->cursor_blink);
+ break;
+ case PROP_GROW_HEIGHT:
+ g_value_set_boolean (value, text->_priv->grow_height);
+ break;
+ case PROP_WRAP_MODE:
+ g_value_set_enum (value, text->_priv->wrap_mode);
+ break;
+ case PROP_JUSTIFICATION:
+ g_value_set_enum (value, text->_priv->justification);
+ break;
+ case PROP_DIRECTION:
+ g_value_set_enum (value, text->_priv->direction);
+ break;
+ case PROP_ANCHOR:
+ g_value_set_enum (value, text->_priv->anchor);
+ break;
+ case PROP_PIXELS_ABOVE_LINES:
+ g_value_set_enum (value, text->_priv->pixels_above_lines);
+ break;
+ case PROP_PIXELS_BELOW_LINES:
+ g_value_set_int (value, text->_priv->pixels_below_lines);
+ break;
+ case PROP_PIXELS_INSIDE_WRAP:
+ g_value_set_int (value, text->_priv->pixels_inside_wrap);
+ break;
+ case PROP_LEFT_MARGIN:
+ g_value_set_int (value, text->_priv->left_margin);
+ break;
+ case PROP_RIGHT_MARGIN:
+ g_value_set_int (value, text->_priv->right_margin);
+ break;
+ case PROP_INDENT:
+ g_value_set_int (value, text->_priv->indent);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gnome_canvas_rich_text_realize(GnomeCanvasItem *item)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+
+ (* GNOME_CANVAS_ITEM_CLASS(parent_class)->realize)(item);
+
+ gnome_canvas_rich_text_ensure_layout(text);
+} /* gnome_canvas_rich_text_realize */
+
+static void
+gnome_canvas_rich_text_unrealize(GnomeCanvasItem *item)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+
+ gnome_canvas_rich_text_destroy_layout(text);
+
+ (* GNOME_CANVAS_ITEM_CLASS(parent_class)->unrealize)(item);
+} /* gnome_canvas_rich_text_unrealize */
+
+static void
+gnome_canvas_rich_text_move_iter_by_lines(GnomeCanvasRichText *text,
+ GtkTextIter *newplace, gint count)
+{
+ while (count < 0) {
+ gtk_text_layout_move_iter_to_previous_line(
+ text->_priv->layout, newplace);
+ count++;
+ }
+
+ while (count > 0) {
+ gtk_text_layout_move_iter_to_next_line(
+ text->_priv->layout, newplace);
+ count--;
+ }
+} /* gnome_canvas_rich_text_move_iter_by_lines */
+
+static gint
+gnome_canvas_rich_text_get_cursor_x_position(GnomeCanvasRichText *text)
+{
+ GtkTextIter insert;
+ GdkRectangle rect;
+
+ gtk_text_buffer_get_iter_at_mark(
+ get_buffer(text), &insert,
+ gtk_text_buffer_get_mark(get_buffer(text), "insert"));
+ gtk_text_layout_get_cursor_locations(
+ text->_priv->layout, &insert, &rect, NULL);
+
+ return rect.x;
+} /* gnome_canvas_rich_text_get_cursor_x_position */
+
+static void
+gnome_canvas_rich_text_move_cursor(GnomeCanvasRichText *text,
+ GtkMovementStep step,
+ gint count, gboolean extend_selection)
+{
+ GtkTextIter insert, newplace;
+
+ gtk_text_buffer_get_iter_at_mark(
+ get_buffer(text), &insert,
+ gtk_text_buffer_get_mark(get_buffer(text), "insert"));
+
+ newplace = insert;
+
+ switch (step) {
+ case GTK_MOVEMENT_LOGICAL_POSITIONS:
+ gtk_text_iter_forward_cursor_positions(&newplace, count);
+ break;
+ case GTK_MOVEMENT_VISUAL_POSITIONS:
+ gtk_text_layout_move_iter_visually(
+ text->_priv->layout, &newplace, count);
+ break;
+ case GTK_MOVEMENT_WORDS:
+ if (count < 0)
+ gtk_text_iter_backward_word_starts(&newplace, -count);
+ else if (count > 0)
+ gtk_text_iter_forward_word_ends(&newplace, count);
+ break;
+ case GTK_MOVEMENT_DISPLAY_LINES:
+ gnome_canvas_rich_text_move_iter_by_lines(
+ text, &newplace, count);
+ gtk_text_layout_move_iter_to_x(
+ text->_priv->layout, &newplace,
+ gnome_canvas_rich_text_get_cursor_x_position(text));
+ break;
+ case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
+ if (count > 1) {
+ gnome_canvas_rich_text_move_iter_by_lines(
+ text, &newplace, --count);
+ }
+ else if (count < -1) {
+ gnome_canvas_rich_text_move_iter_by_lines(
+ text, &newplace, ++count);
+ }
+
+ if (count != 0) {
+ gtk_text_layout_move_iter_to_line_end(
+ text->_priv->layout, &newplace, count);
+ }
+ break;
+ case GTK_MOVEMENT_PARAGRAPHS:
+ /* FIXME: Busted in gtktextview.c too */
+ break;
+ case GTK_MOVEMENT_PARAGRAPH_ENDS:
+ if (count > 0)
+ gtk_text_iter_forward_to_line_end(&newplace);
+ else if (count < 0)
+ gtk_text_iter_set_line_offset(&newplace, 0);
+ break;
+ case GTK_MOVEMENT_BUFFER_ENDS:
+ if (count > 0) {
+ gtk_text_buffer_get_end_iter(
+ get_buffer(text), &newplace);
+ }
+ else if (count < 0) {
+ gtk_text_buffer_get_iter_at_offset(
+ get_buffer(text), &newplace, 0);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!gtk_text_iter_equal(&insert, &newplace)) {
+ if (extend_selection) {
+ gtk_text_buffer_move_mark(
+ get_buffer(text),
+ gtk_text_buffer_get_mark(
+ get_buffer(text), "insert"),
+ &newplace);
+ }
+ else {
+ gtk_text_buffer_place_cursor(
+ get_buffer(text), &newplace);
+ }
+ }
+
+ gnome_canvas_rich_text_start_cursor_blink(text, TRUE);
+} /* gnome_canvas_rich_text_move_cursor */
+
+static gboolean
+whitespace(gunichar ch, gpointer user_data)
+{
+ return (ch == ' ' || ch == '\t');
+} /* whitespace */
+
+static gboolean
+not_whitespace(gunichar ch, gpointer user_data)
+{
+ return !whitespace(ch, user_data);
+} /* not_whitespace */
+
+static gboolean
+find_whitespace_region(const GtkTextIter *center,
+ GtkTextIter *start, GtkTextIter *end)
+{
+ *start = *center;
+ *end = *center;
+
+ if (gtk_text_iter_backward_find_char(start, not_whitespace, NULL, NULL))
+ gtk_text_iter_forward_char(start);
+ if (whitespace(gtk_text_iter_get_char(end), NULL))
+ gtk_text_iter_forward_find_char(end, not_whitespace, NULL, NULL);
+
+ return !gtk_text_iter_equal(start, end);
+} /* find_whitespace_region */
+
+static void
+gnome_canvas_rich_text_delete_from_cursor(GnomeCanvasRichText *text,
+ GtkDeleteType type,
+ gint count)
+{
+ GtkTextIter insert, start, end;
+
+ /* Special case: If the user wants to delete a character and there is
+ a selection, then delete the selection and return */
+ if (type == GTK_DELETE_CHARS) {
+ if (gtk_text_buffer_delete_selection(get_buffer(text), TRUE,
+ text->_priv->editable))
+ return;
+ }
+
+ gtk_text_buffer_get_iter_at_mark(
+ get_buffer(text), &insert,
+ gtk_text_buffer_get_mark(get_buffer(text), "insert"));
+
+ start = insert;
+ end = insert;
+
+ switch (type) {
+ case GTK_DELETE_CHARS:
+ gtk_text_iter_forward_cursor_positions(&end, count);
+ break;
+ case GTK_DELETE_WORD_ENDS:
+ if (count > 0)
+ gtk_text_iter_forward_word_ends(&end, count);
+ else if (count < 0)
+ gtk_text_iter_backward_word_starts(&start, -count);
+ break;
+ case GTK_DELETE_WORDS:
+ break;
+ case GTK_DELETE_DISPLAY_LINE_ENDS:
+ break;
+ case GTK_DELETE_PARAGRAPH_ENDS:
+ if (gtk_text_iter_ends_line(&end)) {
+ gtk_text_iter_forward_line(&end);
+ --count;
+ }
+
+ while (count > 0) {
+ if (!gtk_text_iter_forward_to_line_end(&end))
+ break;
+
+ --count;
+ }
+ break;
+ case GTK_DELETE_PARAGRAPHS:
+ if (count > 0) {
+ gtk_text_iter_set_line_offset(&start, 0);
+ gtk_text_iter_forward_to_line_end(&end);
+
+ /* Do the lines beyond the first. */
+ while (count > 1) {
+ gtk_text_iter_forward_to_line_end(&end);
+ --count;
+ }
+ }
+ break;
+ case GTK_DELETE_WHITESPACE:
+ find_whitespace_region(&insert, &start, &end);
+ break;
+
+ default:
+ break;
+ }
+
+ if (!gtk_text_iter_equal(&start, &end)) {
+ gtk_text_buffer_begin_user_action(get_buffer(text));
+ gtk_text_buffer_delete_interactive(
+ get_buffer(text), &start, &end, text->_priv->editable);
+ gtk_text_buffer_end_user_action(get_buffer(text));
+ }
+} /* gnome_canvas_rich_text_delete_from_cursor */
+
+static gint
+selection_motion_event_handler(GnomeCanvasRichText *text, GdkEvent *event,
+ gpointer data)
+{
+ GtkTextIter newplace;
+ GtkTextMark *mark;
+ double newx, newy;
+
+ /* We only want to handle motion events... */
+ if (event->type != GDK_MOTION_NOTIFY)
+ return FALSE;
+
+ newx = (event->motion.x - text->_priv->x) *
+ GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit;
+ newy = (event->motion.y - text->_priv->y) *
+ GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit;
+
+ gtk_text_layout_get_iter_at_pixel(text->_priv->layout, &newplace, newx, newy);
+ mark = gtk_text_buffer_get_mark(get_buffer(text), "insert");
+ gtk_text_buffer_move_mark(get_buffer(text), mark, &newplace);
+
+ return TRUE;
+} /* selection_motion_event_handler */
+
+static void
+gnome_canvas_rich_text_start_selection_drag(GnomeCanvasRichText *text,
+ const GtkTextIter *iter,
+ GdkEventButton *button)
+{
+ GtkTextIter newplace;
+
+ g_return_if_fail(text->_priv->selection_drag_handler == 0);
+
+#if 0
+ gnome_canvas_item_grab_focus(GNOME_CANVAS_ITEM(text));
+#endif
+
+ newplace = *iter;
+
+ gtk_text_buffer_place_cursor(get_buffer(text), &newplace);
+
+ text->_priv->selection_drag_handler = g_signal_connect(
+ text, "event",
+ G_CALLBACK (selection_motion_event_handler),
+ NULL);
+} /* gnome_canvas_rich_text_start_selection_drag */
+
+static gboolean
+gnome_canvas_rich_text_end_selection_drag(GnomeCanvasRichText *text,
+ GdkEventButton *event)
+{
+ if (text->_priv->selection_drag_handler == 0)
+ return FALSE;
+
+ g_signal_handler_disconnect (text, text->_priv->selection_drag_handler);
+ text->_priv->selection_drag_handler = 0;
+
+#if 0
+ gnome_canvas_item_grab(NULL);
+#endif
+
+ return TRUE;
+} /* gnome_canvas_rich_text_end_selection_drag */
+
+static void
+gnome_canvas_rich_text_emit_tag_changed(GnomeCanvasRichText *text,
+ GtkTextTag *tag)
+{
+ g_signal_emit(G_OBJECT(text), signals[TAG_CHANGED], 0, tag);
+} /* gnome_canvas_rich_text_emit_tag_changed */
+
+static gint
+gnome_canvas_rich_text_key_press_event(GnomeCanvasItem *item,
+ GdkEventKey *event)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+ gboolean extend_selection = FALSE;
+ gboolean handled = FALSE;
+
+#if 0
+ printf("Key press event\n");
+#endif
+
+ if (!text->_priv->layout || !text->_priv->buffer)
+ return FALSE;
+
+ if (event->state & GDK_SHIFT_MASK)
+ extend_selection = TRUE;
+
+ switch (event->keyval) {
+ case GDK_Return:
+ case GDK_KP_Enter:
+ gtk_text_buffer_delete_selection(
+ get_buffer(text), TRUE, text->_priv->editable);
+ gtk_text_buffer_insert_interactive_at_cursor(
+ get_buffer(text), "\n", 1, text->_priv->editable);
+ handled = TRUE;
+ break;
+
+ case GDK_Tab:
+ gtk_text_buffer_insert_interactive_at_cursor(
+ get_buffer(text), "\t", 1, text->_priv->editable);
+ handled = TRUE;
+ break;
+
+ /* MOVEMENT */
+ case GDK_Right:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_WORDS, 1,
+ extend_selection);
+ handled = TRUE;
+ }
+ else {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_VISUAL_POSITIONS, 1,
+ extend_selection);
+ handled = TRUE;
+ }
+ break;
+ case GDK_Left:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_WORDS, -1,
+ extend_selection);
+ handled = TRUE;
+ }
+ else {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_VISUAL_POSITIONS, -1,
+ extend_selection);
+ handled = TRUE;
+ }
+ break;
+ case GDK_f:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_LOGICAL_POSITIONS, 1,
+ extend_selection);
+ handled = TRUE;
+ }
+ else if (event->state & GDK_MOD1_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_WORDS, 1,
+ extend_selection);
+ handled = TRUE;
+ }
+ break;
+ case GDK_b:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_LOGICAL_POSITIONS, -1,
+ extend_selection);
+ handled = TRUE;
+ }
+ else if (event->state & GDK_MOD1_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_WORDS, -1,
+ extend_selection);
+ handled = TRUE;
+ }
+ break;
+ case GDK_Up:
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_DISPLAY_LINES, -1,
+ extend_selection);
+ handled = TRUE;
+ break;
+ case GDK_Down:
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_DISPLAY_LINES, 1,
+ extend_selection);
+ handled = TRUE;
+ break;
+ case GDK_p:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_DISPLAY_LINES, -1,
+ extend_selection);
+ handled = TRUE;
+ }
+ break;
+ case GDK_n:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_DISPLAY_LINES, 1,
+ extend_selection);
+ handled = TRUE;
+ }
+ break;
+ case GDK_Home:
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_PARAGRAPH_ENDS, -1,
+ extend_selection);
+ handled = TRUE;
+ break;
+ case GDK_End:
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_PARAGRAPH_ENDS, 1,
+ extend_selection);
+ handled = TRUE;
+ break;
+ case GDK_a:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_PARAGRAPH_ENDS, -1,
+ extend_selection);
+ handled = TRUE;
+ }
+ break;
+ case GDK_e:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_PARAGRAPH_ENDS, 1,
+ extend_selection);
+ handled = TRUE;
+ }
+ break;
+
+ /* DELETING TEXT */
+ case GDK_Delete:
+ case GDK_KP_Delete:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_WORD_ENDS, 1);
+ handled = TRUE;
+ }
+ else {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_CHARS, 1);
+ handled = TRUE;
+ }
+ break;
+ case GDK_d:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_CHARS, 1);
+ handled = TRUE;
+ }
+ else if (event->state & GDK_MOD1_MASK) {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_WORD_ENDS, 1);
+ handled = TRUE;
+ }
+ break;
+ case GDK_BackSpace:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_WORD_ENDS, -1);
+ handled = TRUE;
+ }
+ else {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_CHARS, -1);
+ }
+ handled = TRUE;
+ break;
+ case GDK_k:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_PARAGRAPH_ENDS, 1);
+ handled = TRUE;
+ }
+ break;
+ case GDK_u:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_PARAGRAPHS, 1);
+ handled = TRUE;
+ }
+ break;
+ case GDK_space:
+ if (event->state & GDK_MOD1_MASK) {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_WHITESPACE, 1);
+ handled = TRUE;
+ }
+ break;
+ case GDK_backslash:
+ if (event->state & GDK_MOD1_MASK) {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_WHITESPACE, 1);
+ handled = TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* An empty string, click just pressing "Alt" by itself or whatever. */
+ if (!event->length)
+ return FALSE;
+
+ if (!handled) {
+ gtk_text_buffer_delete_selection(
+ get_buffer(text), TRUE, text->_priv->editable);
+ gtk_text_buffer_insert_interactive_at_cursor(
+ get_buffer(text), event->string, event->length,
+ text->_priv->editable);
+ }
+
+ gnome_canvas_rich_text_start_cursor_blink(text, TRUE);
+
+ return TRUE;
+} /* gnome_canvas_rich_text_key_press_event */
+
+static gint
+gnome_canvas_rich_text_key_release_event(GnomeCanvasItem *item,
+ GdkEventKey *event)
+{
+ return FALSE;
+} /* gnome_canvas_rich_text_key_release_event */
+
+static gboolean
+_click(gpointer data)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+ text->_priv->clicks = 0;
+ text->_priv->click_timeout = 0;
+
+ return FALSE;
+} /* _click */
+
+static gint
+gnome_canvas_rich_text_button_press_event(GnomeCanvasItem *item,
+ GdkEventButton *event)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+ GtkTextIter iter;
+ GdkEventType event_type;
+ double newx, newy;
+
+ newx = (event->x - text->_priv->x) * item->canvas->pixels_per_unit;
+ newy = (event->y - text->_priv->y) * item->canvas->pixels_per_unit;
+
+ gtk_text_layout_get_iter_at_pixel(text->_priv->layout, &iter, newx, newy);
+
+ /* The canvas doesn't give us double- or triple-click events, so
+ we have to synthesize them ourselves. Yay. */
+ event_type = event->type;
+ if (event_type == GDK_BUTTON_PRESS) {
+ text->_priv->clicks++;
+ text->_priv->click_timeout = g_timeout_add(400, _click, text);
+
+ if (text->_priv->clicks > 3)
+ text->_priv->clicks = text->_priv->clicks % 3;
+
+ if (text->_priv->clicks == 1)
+ event_type = GDK_BUTTON_PRESS;
+ else if (text->_priv->clicks == 2)
+ event_type = GDK_2BUTTON_PRESS;
+ else if (text->_priv->clicks == 3)
+ event_type = GDK_3BUTTON_PRESS;
+ else
+ printf("ZERO CLICKS!\n");
+ }
+
+ if (event->button == 1 && event_type == GDK_BUTTON_PRESS) {
+ GtkTextIter start, end;
+
+ if (gtk_text_buffer_get_selection_bounds(
+ get_buffer(text), &start, &end) &&
+ gtk_text_iter_in_range(&iter, &start, &end)) {
+ text->_priv->drag_start_x = event->x;
+ text->_priv->drag_start_y = event->y;
+ }
+ else {
+ gnome_canvas_rich_text_start_selection_drag(
+ text, &iter, event);
+ }
+
+ return TRUE;
+ }
+ else if (event->button == 1 && event_type == GDK_2BUTTON_PRESS) {
+ GtkTextIter start, end;
+
+#if 0
+ printf("double-click\n");
+#endif
+
+ gnome_canvas_rich_text_end_selection_drag(text, event);
+
+ start = iter;
+ end = start;
+
+ if (gtk_text_iter_inside_word(&start)) {
+ if (!gtk_text_iter_starts_word(&start))
+ gtk_text_iter_backward_word_start(&start);
+
+ if (!gtk_text_iter_ends_word(&end))
+ gtk_text_iter_forward_word_end(&end);
+ }
+
+ gtk_text_buffer_move_mark(
+ get_buffer(text),
+ gtk_text_buffer_get_selection_bound(get_buffer(text)),
+ &start);
+ gtk_text_buffer_move_mark(
+ get_buffer(text),
+ gtk_text_buffer_get_insert(get_buffer(text)), &end);
+
+ text->_priv->just_selected_element = TRUE;
+
+ return TRUE;
+ }
+ else if (event->button == 1 && event_type == GDK_3BUTTON_PRESS) {
+ GtkTextIter start, end;
+
+#if 0
+ printf("triple-click\n");
+#endif
+
+ gnome_canvas_rich_text_end_selection_drag(text, event);
+
+ start = iter;
+ end = start;
+
+ if (gtk_text_layout_iter_starts_line(text->_priv->layout, &start)) {
+ gtk_text_layout_move_iter_to_line_end(
+ text->_priv->layout, &start, -1);
+ }
+ else {
+ gtk_text_layout_move_iter_to_line_end(
+ text->_priv->layout, &start, -1);
+
+ if (!gtk_text_layout_iter_starts_line(
+ text->_priv->layout, &end)) {
+ gtk_text_layout_move_iter_to_line_end(
+ text->_priv->layout, &end, 1);
+ }
+ }
+
+ gtk_text_buffer_move_mark(
+ get_buffer(text),
+ gtk_text_buffer_get_selection_bound(get_buffer(text)),
+ &start);
+ gtk_text_buffer_move_mark(
+ get_buffer(text),
+ gtk_text_buffer_get_insert(get_buffer(text)), &end);
+
+ text->_priv->just_selected_element = TRUE;
+
+ return TRUE;
+ }
+ else if (event->button == 2 && event_type == GDK_BUTTON_PRESS) {
+ gtk_text_buffer_paste_clipboard(
+ get_buffer(text),
+ gtk_clipboard_get (GDK_SELECTION_PRIMARY),
+ &iter, text->_priv->editable);
+ }
+
+ return FALSE;
+} /* gnome_canvas_rich_text_button_press_event */
+
+static gint
+gnome_canvas_rich_text_button_release_event(GnomeCanvasItem *item,
+ GdkEventButton *event)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+ double newx, newy;
+
+ newx = (event->x - text->_priv->x) * item->canvas->pixels_per_unit;
+ newy = (event->y - text->_priv->y) * item->canvas->pixels_per_unit;
+
+ if (event->button == 1) {
+ if (text->_priv->drag_start_x >= 0) {
+ text->_priv->drag_start_x = -1;
+ text->_priv->drag_start_y = -1;
+ }
+
+ if (gnome_canvas_rich_text_end_selection_drag(text, event))
+ return TRUE;
+ else if (text->_priv->just_selected_element) {
+ text->_priv->just_selected_element = FALSE;
+ return FALSE;
+ }
+ else {
+ GtkTextIter iter;
+
+ gtk_text_layout_get_iter_at_pixel(
+ text->_priv->layout, &iter, newx, newy);
+
+ gtk_text_buffer_place_cursor(get_buffer(text), &iter);
+
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+} /* gnome_canvas_rich_text_button_release_event */
+
+static gint
+gnome_canvas_rich_text_focus_in_event(GnomeCanvasItem *item,
+ GdkEventFocus *event)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+
+ if (text->_priv->cursor_visible && text->_priv->layout) {
+ gtk_text_layout_set_cursor_visible(text->_priv->layout, TRUE);
+ gnome_canvas_rich_text_start_cursor_blink(text, FALSE);
+ }
+
+ return FALSE;
+} /* gnome_canvas_rich_text_focus_in_event */
+
+static gint
+gnome_canvas_rich_text_focus_out_event(GnomeCanvasItem *item,
+ GdkEventFocus *event)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+
+ if (text->_priv->cursor_visible && text->_priv->layout) {
+ gtk_text_layout_set_cursor_visible(text->_priv->layout, FALSE);
+ gnome_canvas_rich_text_stop_cursor_blink(text);
+ }
+
+ return FALSE;
+} /* gnome_canvas_rich_text_focus_out_event */
+
+static gboolean
+get_event_coordinates(GdkEvent *event, gint *x, gint *y)
+{
+ g_return_val_if_fail(event, FALSE);
+
+ switch (event->type) {
+ case GDK_MOTION_NOTIFY:
+ *x = event->motion.x;
+ *y = event->motion.y;
+ return TRUE;
+ case GDK_BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ case GDK_BUTTON_RELEASE:
+ *x = event->button.x;
+ *y = event->button.y;
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+} /* get_event_coordinates */
+
+static void
+emit_event_on_tags(GnomeCanvasRichText *text, GdkEvent *event,
+ GtkTextIter *iter)
+{
+ GSList *tags;
+ GSList *i;
+
+ tags = gtk_text_iter_get_tags(iter);
+
+ i = tags;
+ while (i) {
+ GtkTextTag *tag = i->data;
+
+ gtk_text_tag_event(tag, G_OBJECT(text), event, iter);
+
+ /* The cursor has been moved to within this tag. Emit the
+ tag_changed signal */
+ if (event->type == GDK_BUTTON_RELEASE ||
+ event->type == GDK_KEY_PRESS ||
+ event->type == GDK_KEY_RELEASE) {
+ gnome_canvas_rich_text_emit_tag_changed(
+ text, tag);
+ }
+
+ i = g_slist_next(i);
+ }
+
+ g_slist_free(tags);
+} /* emit_event_on_tags */
+
+static gint
+gnome_canvas_rich_text_event(GnomeCanvasItem *item, GdkEvent *event)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+ int x, y;
+
+ if (get_event_coordinates(event, &x, &y)) {
+ GtkTextIter iter;
+
+ x -= text->_priv->x;
+ y -= text->_priv->y;
+
+ gtk_text_layout_get_iter_at_pixel(text->_priv->layout, &iter, x, y);
+ emit_event_on_tags(text, event, &iter);
+ }
+ else if (event->type == GDK_KEY_PRESS ||
+ event->type == GDK_KEY_RELEASE) {
+ GtkTextMark *insert;
+ GtkTextIter iter;
+
+ insert = gtk_text_buffer_get_mark(get_buffer(text), "insert");
+ gtk_text_buffer_get_iter_at_mark(
+ get_buffer(text), &iter, insert);
+ emit_event_on_tags(text, event, &iter);
+ }
+
+ switch (event->type) {
+ case GDK_KEY_PRESS:
+ return gnome_canvas_rich_text_key_press_event(
+ item, (GdkEventKey *) event);
+ case GDK_KEY_RELEASE:
+ return gnome_canvas_rich_text_key_release_event(
+ item, (GdkEventKey *) event);
+ case GDK_BUTTON_PRESS:
+ return gnome_canvas_rich_text_button_press_event(
+ item, (GdkEventButton *) event);
+ case GDK_BUTTON_RELEASE:
+ return gnome_canvas_rich_text_button_release_event(
+ item, (GdkEventButton *) event);
+ case GDK_FOCUS_CHANGE:
+ if (((GdkEventFocus *) event)->window !=
+ item->canvas->layout.bin_window)
+ return FALSE;
+
+ if (((GdkEventFocus *) event)->in)
+ return gnome_canvas_rich_text_focus_in_event(
+ item, (GdkEventFocus *) event);
+ else
+ return gnome_canvas_rich_text_focus_out_event(
+ item, (GdkEventFocus *) event);
+ default:
+ return FALSE;
+ }
+} /* gnome_canvas_rich_text_event */
+
+/* Cut/Copy/Paste */
+
+/**
+ * gnome_canvas_rich_text_cut_clipboard:
+ * @text: a #GnomeCanvasRichText.
+ *
+ * Copies the currently selected @text to clipboard, then deletes said text
+ * if it's editable.
+ **/
+void
+gnome_canvas_rich_text_cut_clipboard(GnomeCanvasRichText *text)
+{
+ g_return_if_fail(text);
+ g_return_if_fail(get_buffer(text));
+
+ gtk_text_buffer_cut_clipboard(get_buffer(text),
+ gtk_clipboard_get (GDK_SELECTION_PRIMARY),
+ text->_priv->editable);
+} /* gnome_canvas_rich_text_cut_clipboard */
+
+
+/**
+ * gnome_canvas_rich_text_copy_clipboard:
+ * @text: a #GnomeCanvasRichText.
+ *
+ * Copies the currently selected @text to clipboard.
+ **/
+void
+gnome_canvas_rich_text_copy_clipboard(GnomeCanvasRichText *text)
+{
+ g_return_if_fail(text);
+ g_return_if_fail(get_buffer(text));
+
+ gtk_text_buffer_copy_clipboard(get_buffer(text),
+ gtk_clipboard_get (GDK_SELECTION_PRIMARY));
+} /* gnome_canvas_rich_text_cut_clipboard */
+
+
+/**
+ * gnome_canvas_rich_text_paste_clipboard:
+ * @text: a #GnomeCanvasRichText.
+ *
+ * Pastes the contents of the clipboard at the insertion point.
+ **/
+void
+gnome_canvas_rich_text_paste_clipboard(GnomeCanvasRichText *text)
+{
+ g_return_if_fail(text);
+ g_return_if_fail(get_buffer(text));
+
+ gtk_text_buffer_paste_clipboard(get_buffer(text),
+ gtk_clipboard_get (GDK_SELECTION_PRIMARY),
+ NULL,
+ text->_priv->editable);
+} /* gnome_canvas_rich_text_cut_clipboard */
+
+static gint
+preblink_cb(gpointer data)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+ text->_priv->preblink_timeout = 0;
+ gnome_canvas_rich_text_start_cursor_blink(text, FALSE);
+
+ /* Remove ourselves */
+ return FALSE;
+} /* preblink_cb */
+
+static gint
+blink_cb(gpointer data)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+ gboolean visible;
+
+ g_assert(text->_priv->layout);
+ g_assert(text->_priv->cursor_visible);
+
+ visible = gtk_text_layout_get_cursor_visible(text->_priv->layout);
+ if (visible)
+ text->_priv->blink_timeout = g_timeout_add(
+ CURSOR_OFF_TIME, blink_cb, text);
+ else
+ text->_priv->blink_timeout = g_timeout_add(
+ CURSOR_ON_TIME, blink_cb, text);
+
+ gtk_text_layout_set_cursor_visible(text->_priv->layout, !visible);
+
+ /* Remove ourself */
+ return FALSE;
+} /* blink_cb */
+
+static void
+gnome_canvas_rich_text_start_cursor_blink(GnomeCanvasRichText *text,
+ gboolean with_delay)
+{
+ if (!text->_priv->layout)
+ return;
+
+ if (!text->_priv->cursor_visible || !text->_priv->cursor_blink)
+ return;
+
+ if (text->_priv->preblink_timeout != 0) {
+ g_source_remove(text->_priv->preblink_timeout);
+ text->_priv->preblink_timeout = 0;
+ }
+
+ if (with_delay) {
+ if (text->_priv->blink_timeout != 0) {
+ g_source_remove(text->_priv->blink_timeout);
+ text->_priv->blink_timeout = 0;
+ }
+
+ gtk_text_layout_set_cursor_visible(text->_priv->layout, TRUE);
+
+ text->_priv->preblink_timeout = g_timeout_add(
+ PREBLINK_TIME, preblink_cb, text);
+ }
+ else {
+ if (text->_priv->blink_timeout == 0) {
+ gtk_text_layout_set_cursor_visible(text->_priv->layout, TRUE);
+ text->_priv->blink_timeout = g_timeout_add(
+ CURSOR_ON_TIME, blink_cb, text);
+ }
+ }
+} /* gnome_canvas_rich_text_start_cursor_blink */
+
+static void
+gnome_canvas_rich_text_stop_cursor_blink(GnomeCanvasRichText *text)
+{
+ if (text->_priv->blink_timeout) {
+ g_source_remove(text->_priv->blink_timeout);
+ text->_priv->blink_timeout = 0;
+ }
+} /* gnome_canvas_rich_text_stop_cursor_blink */
+
+/* We have to request updates this way because the update cycle is not
+ re-entrant. This will fire off a request in an idle loop. */
+static gboolean
+request_update(gpointer data)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+ gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(text));
+
+ return FALSE;
+} /* request_update */
+
+static void
+invalidated_handler(GtkTextLayout *layout, gpointer data)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+#if 0
+ printf("Text is being invalidated.\n");
+#endif
+
+ gtk_text_layout_validate(text->_priv->layout, 2000);
+
+ /* We are called from the update cycle; gotta put this in an idle
+ loop. */
+ g_idle_add(request_update, text);
+} /* invalidated_handler */
+
+static void
+scale_fonts(GtkTextTag *tag, gpointer data)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+ if (!tag->values)
+ return;
+
+ g_object_set(
+ G_OBJECT(tag), "scale",
+ text->_priv->layout->default_style->font_scale, NULL);
+} /* scale_fonts */
+
+static void
+changed_handler(GtkTextLayout *layout, gint start_y,
+ gint old_height, gint new_height, gpointer data)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+#if 0
+ printf("Layout %p is being changed.\n", text->_priv->layout);
+#endif
+
+ if (text->_priv->layout->default_style->font_scale !=
+ GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit) {
+ GtkTextTagTable *tag_table;
+
+ text->_priv->layout->default_style->font_scale =
+ GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit;
+
+ tag_table = gtk_text_buffer_get_tag_table(get_buffer(text));
+ gtk_text_tag_table_foreach(tag_table, scale_fonts, text);
+
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+
+ if (text->_priv->grow_height) {
+ int width, height;
+
+ gtk_text_layout_get_size(text->_priv->layout, &width, &height);
+
+ if (height > text->_priv->height)
+ text->_priv->height = height;
+ }
+
+ /* We are called from the update cycle; gotta put this in an idle
+ loop. */
+ g_idle_add(request_update, text);
+} /* changed_handler */
+
+
+/**
+ * gnome_canvas_rich_text_set_buffer:
+ * @text: a #GnomeCanvasRichText.
+ * @buffer: a #GtkTextBuffer.
+ *
+ * Sets the buffer field of the @text to @buffer.
+ **/
+void
+gnome_canvas_rich_text_set_buffer(GnomeCanvasRichText *text,
+ GtkTextBuffer *buffer)
+{
+ g_return_if_fail(GNOME_IS_CANVAS_RICH_TEXT(text));
+ g_return_if_fail(buffer == NULL || GTK_IS_TEXT_BUFFER(buffer));
+
+ if (text->_priv->buffer == buffer)
+ return;
+
+ if (text->_priv->buffer != NULL) {
+ g_object_unref(G_OBJECT(text->_priv->buffer));
+ }
+
+ text->_priv->buffer = buffer;
+
+ if (buffer) {
+ g_object_ref(G_OBJECT(buffer));
+
+ if (text->_priv->layout)
+ gtk_text_layout_set_buffer(text->_priv->layout, buffer);
+ }
+
+ gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(text));
+} /* gnome_canvas_rich_text_set_buffer */
+
+static GtkTextBuffer *
+get_buffer(GnomeCanvasRichText *text)
+{
+ if (!text->_priv->buffer) {
+ GtkTextBuffer *b;
+
+ b = gtk_text_buffer_new(NULL);
+ gnome_canvas_rich_text_set_buffer(text, b);
+ g_object_unref(G_OBJECT(b));
+ }
+
+ return text->_priv->buffer;
+} /* get_buffer */
+
+
+/**
+ * gnome_canvas_rich_text_get_buffer:
+ * @text: a #GnomeCanvasRichText.
+ *
+ * Returns a #GtkTextBuffer associated with the #GnomeCanvasRichText.
+ * This function creates a new #GtkTextBuffer if the text buffer is NULL.
+ *
+ * Return value: the #GtkTextBuffer.
+ **/
+GtkTextBuffer *
+gnome_canvas_rich_text_get_buffer(GnomeCanvasRichText *text)
+{
+ g_return_val_if_fail(GNOME_IS_CANVAS_RICH_TEXT(text), NULL);
+
+ return get_buffer(text);
+} /* gnome_canvas_rich_text_get_buffer */
+
+
+/**
+ * gnome_canvas_rich_text_get_iter_location:
+ * @text: a #GnomeCanvasRichText.
+ * @iter: a #GtkTextIter.
+ * @location: a #GdkRectangle containing the bounds of the character at @iter.
+ *
+ * Gets a rectangle which roughly contains the character at @iter.
+ **/
+void
+gnome_canvas_rich_text_get_iter_location (GnomeCanvasRichText *text,
+ const GtkTextIter *iter,
+ GdkRectangle *location)
+{
+ g_return_if_fail (GNOME_IS_CANVAS_RICH_TEXT (text));
+ g_return_if_fail (gtk_text_iter_get_buffer (iter) == text->_priv->buffer);
+
+ gtk_text_layout_get_iter_location (text->_priv->layout, iter, location);
+}
+
+
+/**
+ * gnome_canvas_rich_text_get_iter_at_location:
+ * @text: a #GnomeCanvasRichText.
+ * @iter: a #GtkTextIter.
+ * @x: x position, in buffer coordinates.
+ * @y: y position, in buffer coordinates.
+ *
+ * Retrieves the iterator at the buffer coordinates x and y.
+ **/
+void
+gnome_canvas_rich_text_get_iter_at_location (GnomeCanvasRichText *text,
+ GtkTextIter *iter,
+ gint x,
+ gint y)
+{
+ g_return_if_fail (GNOME_IS_CANVAS_RICH_TEXT (text));
+ g_return_if_fail (iter != NULL);
+ g_return_if_fail (text->_priv->layout != NULL);
+
+ gtk_text_layout_get_iter_at_pixel (text->_priv->layout,
+ iter,
+ x,
+ y);
+}
+
+
+static void
+gnome_canvas_rich_text_set_attributes_from_style(GnomeCanvasRichText *text,
+ GtkTextAttributes *values,
+ GtkStyle *style)
+{
+ values->appearance.bg_color = style->base[GTK_STATE_NORMAL];
+ values->appearance.fg_color = style->fg[GTK_STATE_NORMAL];
+
+ if (values->font)
+ pango_font_description_free (values->font);
+
+ values->font = pango_font_description_copy (style->font_desc);
+
+} /* gnome_canvas_rich_text_set_attributes_from_style */
+
+static void
+gnome_canvas_rich_text_ensure_layout(GnomeCanvasRichText *text)
+{
+ if (!text->_priv->layout) {
+ GtkWidget *canvas;
+ GtkTextAttributes *style;
+ PangoContext *ltr_context, *rtl_context;
+
+ text->_priv->layout = gtk_text_layout_new();
+
+ gtk_text_layout_set_screen_width(text->_priv->layout, text->_priv->width);
+
+ if (get_buffer(text)) {
+ gtk_text_layout_set_buffer(
+ text->_priv->layout, get_buffer(text));
+ }
+
+ /* Setup the cursor stuff */
+ gtk_text_layout_set_cursor_visible(
+ text->_priv->layout, text->_priv->cursor_visible);
+ if (text->_priv->cursor_visible && text->_priv->cursor_blink)
+ gnome_canvas_rich_text_start_cursor_blink(text, FALSE);
+ else
+ gnome_canvas_rich_text_stop_cursor_blink(text);
+
+ canvas = GTK_WIDGET(GNOME_CANVAS_ITEM(text)->canvas);
+
+ ltr_context = gtk_widget_create_pango_context(canvas);
+ pango_context_set_base_dir(ltr_context, PANGO_DIRECTION_LTR);
+ rtl_context = gtk_widget_create_pango_context(canvas);
+ pango_context_set_base_dir(rtl_context, PANGO_DIRECTION_RTL);
+
+ gtk_text_layout_set_contexts(
+ text->_priv->layout, ltr_context, rtl_context);
+
+ g_object_unref(G_OBJECT(ltr_context));
+ g_object_unref(G_OBJECT(rtl_context));
+
+ style = gtk_text_attributes_new();
+
+ gnome_canvas_rich_text_set_attributes_from_style(
+ text, style, canvas->style);
+
+ style->pixels_above_lines = text->_priv->pixels_above_lines;
+ style->pixels_below_lines = text->_priv->pixels_below_lines;
+ style->pixels_inside_wrap = text->_priv->pixels_inside_wrap;
+ style->left_margin = text->_priv->left_margin;
+ style->right_margin = text->_priv->right_margin;
+ style->indent = text->_priv->indent;
+ style->tabs = NULL;
+ style->wrap_mode = text->_priv->wrap_mode;
+ style->justification = text->_priv->justification;
+ style->direction = text->_priv->direction;
+ style->editable = text->_priv->editable;
+ style->invisible = !text->_priv->visible;
+
+ gtk_text_layout_set_default_style(text->_priv->layout, style);
+
+ gtk_text_attributes_unref(style);
+
+ g_signal_connect(
+ G_OBJECT(text->_priv->layout), "invalidated",
+ G_CALLBACK (invalidated_handler), text);
+
+ g_signal_connect(
+ G_OBJECT(text->_priv->layout), "changed",
+ G_CALLBACK (changed_handler), text);
+ }
+} /* gnome_canvas_rich_text_ensure_layout */
+
+static void
+gnome_canvas_rich_text_destroy_layout(GnomeCanvasRichText *text)
+{
+ if (text->_priv->layout) {
+ g_signal_handlers_disconnect_by_func(
+ G_OBJECT(text->_priv->layout), invalidated_handler, text);
+ g_signal_handlers_disconnect_by_func(
+ G_OBJECT(text->_priv->layout), changed_handler, text);
+ g_object_unref(G_OBJECT(text->_priv->layout));
+ text->_priv->layout = NULL;
+ }
+} /* gnome_canvas_rich_text_destroy_layout */
+
+static void
+adjust_for_anchors(GnomeCanvasRichText *text, double *ax, double *ay)
+{
+ double x, y;
+
+ x = text->_priv->x;
+ y = text->_priv->y;
+
+ /* Anchor text */
+ /* X coordinates */
+ switch (text->_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 -= text->_priv->width / 2;
+ break;
+
+ case GTK_ANCHOR_NE:
+ case GTK_ANCHOR_E:
+ case GTK_ANCHOR_SE:
+ x -= text->_priv->width;
+ break;
+ default:
+ break;
+ }
+
+ /* Y coordinates */
+ switch (text->_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 -= text->_priv->height / 2;
+ break;
+
+ case GTK_ANCHOR_SW:
+ case GTK_ANCHOR_S:
+ case GTK_ANCHOR_SE:
+ y -= text->_priv->height;
+ break;
+ default:
+ break;
+ }
+
+ if (ax)
+ *ax = x;
+ if (ay)
+ *ay = y;
+} /* adjust_for_anchors */
+
+static void
+get_bounds(GnomeCanvasRichText *text, double *px1, double *py1,
+ double *px2, double *py2)
+{
+ GnomeCanvasItem *item = GNOME_CANVAS_ITEM(text);
+ double x, y;
+ double x1, x2, y1, y2;
+ int cx1, cx2, cy1, cy2;
+
+ adjust_for_anchors(text, &x, &y);
+
+ x1 = x;
+ y1 = y;
+ x2 = x + text->_priv->width;
+ y2 = y + text->_priv->height;
+
+ gnome_canvas_item_i2w(item, &x1, &y1);
+ gnome_canvas_item_i2w(item, &x2, &y2);
+ gnome_canvas_w2c(item->canvas, x1, y1, &cx1, &cy1);
+ gnome_canvas_w2c(item->canvas, x2, y2, &cx2, &cy2);
+
+ *px1 = cx1;
+ *py1 = cy1;
+ *px2 = cx2;
+ *py2 = cy2;
+} /* get_bounds */
+
+static void gnome_canvas_rich_text_get_bounds(GnomeCanvasItem *item, double *px1, double *py1,
+ double *px2, double *py2)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+ get_bounds (text, px1, py1, px2, py2);
+}
+
+static void
+gnome_canvas_rich_text_update(GnomeCanvasItem *item, double *affine,
+ ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+ double x1, y1, x2, y2;
+ GtkTextIter start;
+
+ (* GNOME_CANVAS_ITEM_CLASS(parent_class)->update)(
+ item, affine, clip_path, flags);
+
+ get_bounds(text, &x1, &y1, &x2, &y2);
+
+ gtk_text_buffer_get_iter_at_offset(text->_priv->buffer, &start, 0);
+ if (text->_priv->layout)
+ gtk_text_layout_validate_yrange(
+ text->_priv->layout, &start, 0, y2 - y1);
+
+ gnome_canvas_update_bbox(item, x1, y1, x2, y2);
+} /* gnome_canvas_rich_text_update */
+
+static double
+gnome_canvas_rich_text_point(GnomeCanvasItem *item, double x, double y,
+ int cx, int cy, GnomeCanvasItem **actual_item)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+ double ax, ay;
+ double x1, x2, y1, y2;
+ double dx, dy;
+
+ *actual_item = item;
+
+ /* This is a lame cop-out. Anywhere inside of the bounding box. */
+
+ adjust_for_anchors(text, &ax, &ay);
+
+ x1 = ax;
+ y1 = ay;
+ x2 = ax + text->_priv->width;
+ y2 = ay + text->_priv->height;
+
+ if ((x > x1) && (y > y1) && (x < x2) && (y < y2))
+ return 0.0;
+
+ 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);
+} /* gnome_canvas_rich_text_point */
+
+static void
+gnome_canvas_rich_text_draw(GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+ double i2w[6], w2c[6], i2c[6];
+ double ax, ay;
+ int x1, y1, x2, y2;
+ ArtPoint i1, i2;
+ ArtPoint c1, c2;
+
+ gnome_canvas_item_i2w_affine(item, i2w);
+ gnome_canvas_w2c_affine(item->canvas, w2c);
+ art_affine_multiply(i2c, i2w, w2c);
+
+ adjust_for_anchors(text, &ax, &ay);
+
+ i1.x = ax;
+ i1.y = ay;
+ i2.x = ax + text->_priv->width;
+ i2.y = ay + text->_priv->height;
+ art_affine_point(&c1, &i1, i2c);
+ art_affine_point(&c2, &i2, i2c);
+
+ x1 = c1.x;
+ y1 = c1.y;
+ x2 = c2.x;
+ y2 = c2.y;
+
+ gtk_text_layout_set_screen_width(text->_priv->layout, x2 - x1);
+
+ /* FIXME: should last arg be NULL? */
+ gtk_text_layout_draw(
+ text->_priv->layout,
+ GTK_WIDGET(item->canvas),
+ drawable,
+ GTK_WIDGET (item->canvas)->style->text_gc[GTK_STATE_NORMAL],
+ x - x1, y - y1,
+ 0, 0, (x2 - x1) - (x - x1), (y2 - y1) - (y - y1),
+ NULL);
+} /* gnome_canvas_rich_text_draw */
+
+static void
+gnome_canvas_rich_text_render(GnomeCanvasItem *item, GnomeCanvasBuf *buf)
+{
+ g_warning ("rich text item not implemented for anti-aliased canvas");
+} /* gnome_canvas_rich_text_render */
+
+#if 0
+static GtkTextTag *
+gnome_canvas_rich_text_add_tag(GnomeCanvasRichText *text, char *tag_name,
+ int start_offset, int end_offset,
+ const char *first_property_name, ...)
+{
+ GtkTextTag *tag;
+ GtkTextIter start, end;
+ va_list var_args;
+
+ g_return_val_if_fail(text, NULL);
+ g_return_val_if_fail(start_offset >= 0, NULL);
+ g_return_val_if_fail(end_offset >= 0, NULL);
+
+ if (tag_name) {
+ GtkTextTagTable *tag_table;
+
+ tag_table = gtk_text_buffer_get_tag_table(get_buffer(text));
+ g_return_val_if_fail(gtk_text_tag_table_lookup(tag_table, tag_name) == NULL, NULL);
+ }
+
+ tag = gtk_text_buffer_create_tag(
+ get_buffer(text), tag_name, NULL);
+
+ va_start(var_args, first_property_name);
+ g_object_set_valist(G_OBJECT(tag), first_property_name, var_args);
+ va_end(var_args);
+
+ gtk_text_buffer_get_iter_at_offset(
+ get_buffer(text), &start, start_offset);
+ gtk_text_buffer_get_iter_at_offset(
+ get_buffer(text), &end, end_offset);
+ gtk_text_buffer_apply_tag(get_buffer(text), tag, &start, &end);
+
+ return tag;
+} /* gnome_canvas_rich_text_add_tag */
+#endif
diff --git a/libgnomecanvas/gnome-canvas-rich-text.h b/libgnomecanvas/gnome-canvas-rich-text.h
new file mode 100644
index 0000000000..4fa569e921
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-rich-text.h
@@ -0,0 +1,79 @@
+/* Editable GnomeCanvas text item based on GtkTextLayout, borrowed heavily
+ * from GtkTextView.
+ *
+ * Copyright (c) 2000 Red Hat, Inc.
+ * Copyright (c) 2001 Joe Shaw
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 GNOME_CANVAS_RICH_TEXT_H
+#define GNOME_CANVAS_RICH_TEXT_H
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GNOME_TYPE_CANVAS_RICH_TEXT (gnome_canvas_rich_text_get_type ())
+#define GNOME_CANVAS_RICH_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_RICH_TEXT, GnomeCanvasRichText))
+#define GNOME_CANVAS_RICH_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_RICH_TEXT, GnomeCanvasRichTextClass))
+#define GNOME_IS_CANVAS_RICH_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_RICH_TEXT))
+#define GNOME_IS_CANVAS_RICH_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_RICH_TEXT))
+#define GNOME_CANVAS_RICH_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_RICH_TEXT, GnomeCanvasRichTextClass))
+
+typedef struct _GnomeCanvasRichText GnomeCanvasRichText;
+typedef struct _GnomeCanvasRichTextPrivate GnomeCanvasRichTextPrivate;
+typedef struct _GnomeCanvasRichTextClass GnomeCanvasRichTextClass;
+
+struct _GnomeCanvasRichText {
+ GnomeCanvasItem item;
+
+ GnomeCanvasRichTextPrivate *_priv;
+};
+
+struct _GnomeCanvasRichTextClass {
+ GnomeCanvasItemClass parent_class;
+
+ void (* tag_changed)(GnomeCanvasRichText *text,
+ GtkTextTag *tag);
+};
+
+GType gnome_canvas_rich_text_get_type(void) G_GNUC_CONST;
+
+void gnome_canvas_rich_text_cut_clipboard(GnomeCanvasRichText *text);
+
+void gnome_canvas_rich_text_copy_clipboard(GnomeCanvasRichText *text);
+
+void gnome_canvas_rich_text_paste_clipboard(GnomeCanvasRichText *text);
+
+void gnome_canvas_rich_text_set_buffer(GnomeCanvasRichText *text,
+ GtkTextBuffer *buffer);
+
+GtkTextBuffer *gnome_canvas_rich_text_get_buffer(GnomeCanvasRichText *text);
+void
+gnome_canvas_rich_text_get_iter_location (GnomeCanvasRichText *text,
+ const GtkTextIter *iter,
+ GdkRectangle *location);
+void
+gnome_canvas_rich_text_get_iter_at_location (GnomeCanvasRichText *text,
+ GtkTextIter *iter,
+ gint x,
+ gint y);
+
+G_END_DECLS
+
+#endif /* GNOME_CANVAS_RICH_TEXT_H */
diff --git a/libgnomecanvas/gnome-canvas-shape-private.h b/libgnomecanvas/gnome-canvas-shape-private.h
new file mode 100644
index 0000000000..ffdd3ce24b
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-shape-private.h
@@ -0,0 +1,103 @@
+#ifndef GNOME_CANVAS_SHAPE_PRIVATE_H
+#define GNOME_CANVAS_SHAPE_PRIVATE_H
+
+/* Bpath item type for GnomeCanvas widget
+ *
+ * GnomeCanvas 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.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico@nuclecu.unam.mx>
+ * Raph Levien <raph@acm.org>
+ * Lauris Kaplinski <lauris@ariman.ee>
+ */
+
+#include <gdk/gdk.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_vpath_dash.h>
+#include <libart_lgpl/art_svp_wind.h>
+#include <libgnomecanvas/gnome-canvas.h>
+
+#include <libgnomecanvas/gnome-canvas-path-def.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GnomeCanvasShapePrivGdk GnomeCanvasShapePrivGdk;
+typedef struct _GCBPDrawCtx GCBPDrawCtx;
+
+/* Per canvas private structure, holding necessary data for rendering
+ * temporary masks, which are needed for drawing multipart bpaths.
+ * As canvas cannot multithread, we can be sure, that masks are used
+ * serially, also one set of masks per canvas is sufficent to guarantee,
+ * that masks are created on needed X server. Masks grow as needed.
+ * Full structure is refcounted in Bpath implementation
+ */
+
+struct _GCBPDrawCtx {
+ gint refcount;
+
+ GnomeCanvas * canvas;
+
+ gint width;
+ gint height;
+
+ GdkBitmap * mask;
+ GdkBitmap * clip;
+
+ GdkGC * clear_gc;
+ GdkGC * xor_gc;
+};
+
+/* Per Bpath private structure, holding Gdk specific data */
+
+struct _GnomeCanvasShapePrivGdk {
+ gulong fill_pixel; /* Color for fill */
+ gulong outline_pixel; /* Color for outline */
+
+ GdkBitmap *fill_stipple; /* Stipple for fill */
+ GdkBitmap *outline_stipple; /* Stipple for outline */
+
+ GdkGC * fill_gc; /* GC for filling */
+ GdkGC * outline_gc; /* GC for outline */
+
+ gint len_points; /* Size of allocated points array */
+ gint num_points; /* Gdk points in canvas coords */
+ GdkPoint * points; /* Ivariant: closed paths are before open ones */
+ GSList * closed_paths; /* List of lengths */
+ GSList * open_paths; /* List of lengths */
+
+ GCBPDrawCtx * ctx; /* Pointer to per-canvas drawing context */
+};
+
+struct _GnomeCanvasShapePriv {
+ GnomeCanvasPathDef * path; /* Our bezier path representation */
+
+ gdouble scale; /* CTM scaling (for pen) */
+
+ 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? */
+
+ double width; /* Width of outline, in user coords */
+
+ guint32 fill_rgba; /* Fill color, RGBA */
+ guint32 outline_rgba; /* Outline color, RGBA */
+
+ GdkCapStyle cap; /* Cap style for line */
+ GdkJoinStyle join; /* Join style for line */
+ ArtWindRule wind; /* Winding rule */
+ double miterlimit; /* Miter limit */
+
+ ArtVpathDash dash; /* Dashing pattern */
+
+ ArtSVP * fill_svp; /* The SVP for the filled shape */
+ ArtSVP * outline_svp; /* The SVP for the outline shape */
+
+ GnomeCanvasShapePrivGdk * gdk; /* Gdk specific things */
+};
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-shape.c b/libgnomecanvas/gnome-canvas-shape.c
new file mode 100644
index 0000000000..79d8124525
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-shape.c
@@ -0,0 +1,1558 @@
+/* Generic bezier shape item for GnomeCanvasWidget. Most code taken
+ * from gnome-canvas-bpath but made into a shape item.
+ *
+ * GnomeCanvas 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.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico@nuclecu.unam.mx>
+ * Raph Levien <raph@acm.org>
+ * Lauris Kaplinski <lauris@ximian.com>
+ * Miguel de Icaza <miguel@kernel.org>
+ * Cody Russell <bratsche@gnome.org>
+ * Rusty Conover <rconover@bangtail.net>
+ */
+
+/* These includes are set up for standalone compile. If/when this codebase
+ is integrated into libgnomeui, the includes will need to change. */
+
+#include <math.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+
+#include "gnome-canvas-shape.h"
+#include "gnome-canvas-shape-private.h"
+#include "gnome-canvas-path-def.h"
+
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_bpath.h>
+#include <libart_lgpl/art_vpath_bpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_point.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_vpath_dash.h>
+#include <libart_lgpl/art_svp_wind.h>
+#include <libart_lgpl/art_svp_intersect.h>
+#include <libart_lgpl/art_rect_svp.h>
+
+enum {
+ PROP_0,
+ 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,
+ PROP_CAP_STYLE,
+ PROP_JOIN_STYLE,
+ PROP_WIND,
+ PROP_MITERLIMIT,
+ PROP_DASH
+};
+
+static void gnome_canvas_shape_class_init (GnomeCanvasShapeClass *class);
+static void gnome_canvas_shape_init (GnomeCanvasShape *bpath);
+static void gnome_canvas_shape_destroy (GtkObject *object);
+static void gnome_canvas_shape_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_shape_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_shape_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+static void gnome_canvas_shape_realize (GnomeCanvasItem *item);
+static void gnome_canvas_shape_unrealize (GnomeCanvasItem *item);
+static void gnome_canvas_shape_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height);
+static double gnome_canvas_shape_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy, GnomeCanvasItem **actual_item);
+static void gnome_canvas_shape_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+static void gnome_canvas_shape_bounds (GnomeCanvasItem *item,
+ double *x1, double *y1, double *x2, double *y2);
+
+static gulong get_pixel_from_rgba (GnomeCanvasItem *item, guint32 rgba_color);
+static guint32 get_rgba_from_color (GdkColor * color);
+static void set_gc_foreground (GdkGC *gc, gulong pixel);
+static void gcbp_ensure_gdk (GnomeCanvasShape * bpath);
+static void gcbp_destroy_gdk (GnomeCanvasShape * bpath);
+static void set_stipple (GdkGC *gc, GdkBitmap **internal_stipple, GdkBitmap *stipple, int reconfigure);
+static void gcbp_ensure_mask (GnomeCanvasShape * bpath, gint width, gint height);
+static void gcbp_draw_ctx_unref (GCBPDrawCtx * ctx);
+
+static GnomeCanvasItemClass *parent_class;
+
+GType
+gnome_canvas_shape_get_type (void)
+{
+ static GType shape_type;
+
+ if (!shape_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasShapeClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_shape_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasShape),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_shape_init,
+ NULL /* value_table */
+ };
+
+ shape_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasShape",
+ &object_info, 0);
+ }
+
+ return shape_type;
+}
+
+static void
+gnome_canvas_shape_class_init (GnomeCanvasShapeClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ /* when this gets checked into libgnomeui, change the
+ GTK_TYPE_POINTER to GTK_TYPE_GNOME_CANVAS_SHAPE, and add an
+ entry to gnome-boxed.defs */
+
+ gobject_class->set_property = gnome_canvas_shape_set_property;
+ gobject_class->get_property = gnome_canvas_shape_get_property;
+
+
+
+ g_object_class_install_property (gobject_class,
+ PROP_FILL_COLOR,
+ g_param_spec_string ("fill_color", NULL, NULL,
+ NULL,
+ (G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_OUTLINE_COLOR,
+ g_param_spec_string ("outline_color", NULL, NULL,
+ NULL,
+ (G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_FILL_STIPPLE,
+ g_param_spec_object ("fill_stipple", NULL, NULL,
+ GDK_TYPE_DRAWABLE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_OUTLINE_STIPPLE,
+ g_param_spec_object ("outline_stipple", NULL, NULL,
+ GDK_TYPE_DRAWABLE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_WIDTH_PIXELS,
+ g_param_spec_uint ("width_pixels", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_WIND,
+ g_param_spec_uint ("wind", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_MITERLIMIT,
+ g_param_spec_double ("miterlimit", NULL, NULL,
+ 0.0, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_DASH,
+ g_param_spec_pointer ("dash", NULL, NULL,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ object_class->destroy = gnome_canvas_shape_destroy;
+
+ item_class->update = gnome_canvas_shape_update;
+ item_class->realize = gnome_canvas_shape_realize;
+ item_class->unrealize = gnome_canvas_shape_unrealize;
+ item_class->draw = gnome_canvas_shape_draw;
+ item_class->point = gnome_canvas_shape_point;
+ item_class->render = gnome_canvas_shape_render;
+ item_class->bounds = gnome_canvas_shape_bounds;
+}
+
+static void
+gnome_canvas_shape_init (GnomeCanvasShape *shape)
+{
+ shape->priv = g_new (GnomeCanvasShapePriv, 1);
+
+ shape->priv->path = NULL;
+
+ shape->priv->scale = 1.0;
+
+ shape->priv->fill_set = FALSE;
+ shape->priv->outline_set = FALSE;
+ shape->priv->width_pixels = FALSE;
+
+ shape->priv->width = 1.0;
+
+ shape->priv->fill_rgba = 0x0000003f;
+ shape->priv->outline_rgba = 0x0000007f;
+
+ shape->priv->cap = GDK_CAP_BUTT;
+ shape->priv->join = GDK_JOIN_MITER;
+ shape->priv->wind = ART_WIND_RULE_ODDEVEN;
+ shape->priv->miterlimit = 10.43; /* X11 default */
+
+ shape->priv->dash.n_dash = 0;
+ shape->priv->dash.dash = NULL;
+
+ shape->priv->fill_svp = NULL;
+ shape->priv->outline_svp = NULL;
+
+ shape->priv->gdk = NULL;
+}
+
+static void
+gnome_canvas_shape_destroy (GtkObject *object)
+{
+ GnomeCanvasShape *shape;
+ GnomeCanvasShapePriv *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_SHAPE (object));
+
+ shape = GNOME_CANVAS_SHAPE (object);
+
+ if (shape->priv) {
+ priv = shape->priv;
+ if (priv->gdk) gcbp_destroy_gdk (shape);
+
+ if (priv->path) gnome_canvas_path_def_unref (priv->path);
+
+ if (priv->dash.dash) g_free (priv->dash.dash);
+ if (priv->fill_svp) art_svp_free (priv->fill_svp);
+ if (priv->outline_svp) art_svp_free (priv->outline_svp);
+
+ g_free (shape->priv);
+ shape->priv = NULL;
+ }
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+/**
+ * gnome_canvas_shape_set_path_def:
+ * @shape: a GnomeCanvasShape
+ * @def: a GnomeCanvasPathDef
+ *
+ * This function sets the the GnomeCanvasPathDef used by the
+ * GnomeCanvasShape. Notice, that it does not request updates, as
+ * it is meant to be used from item implementations, from inside
+ * update queue.
+ */
+
+void
+gnome_canvas_shape_set_path_def (GnomeCanvasShape *shape, GnomeCanvasPathDef *def)
+{
+ GnomeCanvasShapePriv *priv;
+
+ g_return_if_fail (shape != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_SHAPE (shape));
+
+ priv = shape->priv;
+
+ if (priv->path) {
+ gnome_canvas_path_def_unref (priv->path);
+ priv->path = NULL;
+ }
+
+ if (def) {
+ priv->path = gnome_canvas_path_def_duplicate (def);
+ }
+}
+
+static void
+gnome_canvas_shape_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasShape *shape;
+ GnomeCanvasShapePriv *priv;
+ GnomeCanvasShapePrivGdk *gdk;
+ GdkColor color;
+ GdkColor *colorptr;
+ ArtVpathDash *dash;
+
+ item = GNOME_CANVAS_ITEM (object);
+ shape = GNOME_CANVAS_SHAPE (object);
+ priv = shape->priv;
+
+ if (!item->canvas->aa) {
+ gcbp_ensure_gdk (shape);
+ gdk = priv->gdk;
+ } else {
+ gdk = NULL;
+ }
+
+ switch (param_id) {
+ case PROP_FILL_COLOR:
+ if (gnome_canvas_get_color (item->canvas, g_value_get_string (value), &color)) {
+ priv->fill_set = TRUE;
+ priv->fill_rgba = get_rgba_from_color (&color);
+ if (gdk) gdk->fill_pixel = color.pixel;
+ } else if (priv->fill_set)
+ priv->fill_set = FALSE;
+ else
+ break;
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_FILL_COLOR_GDK:
+ colorptr = g_value_get_boxed (value);
+ if (colorptr != NULL) {
+ priv->fill_set = TRUE;
+ priv->fill_rgba = get_rgba_from_color (colorptr);
+ if (gdk) {
+ GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+ GdkColor tmp = *colorptr;
+ gdk_rgb_find_color (colormap, &tmp);
+ gdk->fill_pixel = tmp.pixel;
+ }
+ } else if (priv->fill_set)
+ priv->fill_set = FALSE;
+ else
+ break;
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_FILL_COLOR_RGBA:
+ priv->fill_set = TRUE;
+ priv->fill_rgba = g_value_get_uint (value);
+ if (gdk) gdk->fill_pixel = get_pixel_from_rgba (item, priv->fill_rgba);
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_OUTLINE_COLOR:
+ if (gnome_canvas_get_color (item->canvas, g_value_get_string (value), &color)) {
+ priv->outline_set = TRUE;
+ priv->outline_rgba = get_rgba_from_color (&color);
+ if (gdk) gdk->outline_pixel = color.pixel;
+ } else if (priv->outline_set)
+ priv->outline_set = FALSE;
+ else
+ break;
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_OUTLINE_COLOR_GDK:
+ colorptr = g_value_get_boxed (value);
+ if (colorptr != NULL) {
+ priv->outline_set = TRUE;
+ priv->outline_rgba = get_rgba_from_color (colorptr);
+ if (gdk) {
+ GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+ GdkColor tmp = *colorptr;
+ gdk_rgb_find_color (colormap, &tmp);
+ gdk->outline_pixel = tmp.pixel;
+ }
+ } else if (priv->outline_set)
+ priv->outline_set = FALSE;
+ else
+ break;
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_OUTLINE_COLOR_RGBA:
+ priv->outline_set = TRUE;
+ priv->outline_rgba = g_value_get_uint (value);
+ if (gdk) gdk->outline_pixel = get_pixel_from_rgba (item, priv->outline_rgba);
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_FILL_STIPPLE:
+ if (gdk) {
+ set_stipple (gdk->fill_gc, &gdk->fill_stipple, (GdkBitmap*) g_value_get_object (value), FALSE);
+ gnome_canvas_item_request_update (item);
+ }
+ break;
+
+ case PROP_OUTLINE_STIPPLE:
+ if (gdk) {
+ set_stipple (gdk->outline_gc, &gdk->outline_stipple, (GdkBitmap*) g_value_get_object (value), FALSE);
+ gnome_canvas_item_request_update (item);
+ }
+ break;
+
+ case PROP_WIDTH_PIXELS:
+ priv->width = g_value_get_uint (value);
+ priv->width_pixels = TRUE;
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIDTH_UNITS:
+ priv->width = fabs (g_value_get_double (value));
+ priv->width_pixels = FALSE;
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIND:
+ priv->wind = g_value_get_uint (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_CAP_STYLE:
+ priv->cap = g_value_get_enum (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_JOIN_STYLE:
+ priv->join = g_value_get_enum (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_MITERLIMIT:
+ priv->miterlimit = g_value_get_double (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_DASH:
+ dash = g_value_get_pointer (value);
+ if (priv->dash.dash) g_free (priv->dash.dash);
+ priv->dash.dash = NULL;
+
+ if (dash) {
+ priv->dash.offset = dash->offset;
+ priv->dash.n_dash = dash->n_dash;
+ if (dash->dash != NULL) {
+ priv->dash.dash = g_new (double, dash->n_dash);
+ memcpy (priv->dash.dash, dash->dash, dash->n_dash * sizeof (double));
+ }
+ }
+ gnome_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 (GnomeCanvasShape *shape, gulong pixel, GValue *value)
+{
+ GnomeCanvas *canvas = GNOME_CANVAS_ITEM (shape)->canvas;
+ GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+ GdkColor color;
+
+ gdk_colormap_query_color (colormap, pixel, &color);
+ g_value_set_boxed (value, &color);
+}
+
+/**
+ * gnome_canvas_shape_get_path_def:
+ * @shape: a GnomeCanvasShape
+ *
+ * This function returns the #GnomeCanvasPathDef that the shape
+ * currently uses. It adds a reference to the #GnomeCanvasPathDef and
+ * returns it, if there is not a #GnomeCanvasPathDef set for the shape
+ * it returns NULL.
+ *
+ * Returns: a #GnomeCanvasPathDef or NULL if none is set for the shape.
+ */
+
+GnomeCanvasPathDef *
+gnome_canvas_shape_get_path_def (GnomeCanvasShape *shape)
+{
+ GnomeCanvasShapePriv *priv;
+
+ g_return_val_if_fail (shape != NULL, NULL);
+ g_return_val_if_fail (GNOME_IS_CANVAS_SHAPE (shape), NULL);
+
+ priv = shape->priv;
+
+ if (priv->path) {
+ gnome_canvas_path_def_ref (priv->path);
+ return priv->path;
+ }
+
+ return NULL;
+}
+
+static void
+gnome_canvas_shape_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item = GNOME_CANVAS_ITEM (object);
+ GnomeCanvasShape *shape = GNOME_CANVAS_SHAPE (object);
+ GnomeCanvasShapePriv *priv = shape->priv;
+ GnomeCanvasShapePrivGdk *gdk;
+
+ if (!item->canvas->aa) {
+ gcbp_ensure_gdk (shape);
+ gdk = priv->gdk;
+ }
+ else {
+ gdk = NULL;
+ }
+
+ switch (param_id) {
+ case PROP_FILL_COLOR_GDK:
+ if (gdk) {
+ get_color_value (shape, gdk->fill_pixel, value);
+ } else {
+ get_color_value (shape, 0, value);
+ }
+ break;
+
+ case PROP_OUTLINE_COLOR_GDK:
+ if (gdk) {
+ get_color_value (shape, gdk->outline_pixel, value);
+ } else {
+ get_color_value (shape, 0, value);
+ }
+ break;
+
+ case PROP_FILL_COLOR_RGBA:
+ g_value_set_uint (value, priv->fill_rgba);
+ break;
+
+ case PROP_OUTLINE_COLOR_RGBA:
+ g_value_set_uint (value, priv->outline_rgba);
+ break;
+
+ case PROP_FILL_STIPPLE:
+ if (gdk) {
+ g_value_set_object (value, gdk->fill_stipple);
+ } else {
+ g_value_set_object (value, NULL);
+ }
+ break;
+
+ case PROP_OUTLINE_STIPPLE:
+ if (gdk) {
+ g_value_set_object (value, gdk->outline_stipple);
+ } else {
+ g_value_set_object (value, NULL);
+ }
+ break;
+
+ case PROP_WIND:
+ g_value_set_uint (value, priv->wind);
+ break;
+
+ case PROP_CAP_STYLE:
+ g_value_set_enum (value, priv->cap);
+ break;
+
+ case PROP_JOIN_STYLE:
+ g_value_set_enum (value, priv->join);
+ break;
+
+ case PROP_WIDTH_PIXELS:
+ g_value_set_uint (value, priv->width);
+ break;
+
+ case PROP_WIDTH_UNITS:
+ g_value_set_double (value, priv->width);
+ break;
+
+ case PROP_MITERLIMIT:
+ g_value_set_double (value, priv->miterlimit);
+ break;
+
+ case PROP_DASH:
+ g_value_set_pointer (value, &priv->dash);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gnome_canvas_shape_realize (GnomeCanvasItem *item)
+{
+ GnomeCanvasShape *shape;
+
+ shape = GNOME_CANVAS_SHAPE (item);
+
+ if (parent_class->realize)
+ (* parent_class->realize) (item);
+
+ if (!item->canvas->aa) {
+ gcbp_ensure_gdk (shape);
+
+ g_assert(item->canvas->layout.bin_window != NULL);
+
+ shape->priv->gdk->fill_gc = gdk_gc_new (item->canvas->layout.bin_window);
+ shape->priv->gdk->outline_gc = gdk_gc_new (item->canvas->layout.bin_window);
+ }
+}
+
+static void
+gnome_canvas_shape_unrealize (GnomeCanvasItem *item)
+{
+ GnomeCanvasShape *shape;
+
+ shape = GNOME_CANVAS_SHAPE (item);
+
+ if (!item->canvas->aa) {
+ g_assert (shape->priv->gdk != NULL);
+
+ g_object_unref (shape->priv->gdk->fill_gc);
+ shape->priv->gdk->fill_gc = NULL;
+
+ g_object_unref (shape->priv->gdk->outline_gc);
+ shape->priv->gdk->outline_gc = NULL;
+ }
+
+ if (parent_class->unrealize)
+ (* parent_class->unrealize) (item);
+}
+
+static void
+gnome_canvas_shape_render (GnomeCanvasItem *item,
+ GnomeCanvasBuf *buf)
+{
+ GnomeCanvasShape *shape;
+
+ shape = GNOME_CANVAS_SHAPE (item);
+
+ if (shape->priv->fill_svp != NULL)
+ gnome_canvas_render_svp (buf,
+ shape->priv->fill_svp,
+ shape->priv->fill_rgba);
+
+ if (shape->priv->outline_svp != NULL)
+ gnome_canvas_render_svp (buf,
+ shape->priv->outline_svp,
+ shape->priv->outline_rgba);
+}
+
+static void
+gnome_canvas_shape_draw (GnomeCanvasItem *item,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ static GdkPoint * dpoints = NULL;
+ static gint num_dpoints = 0;
+
+ GnomeCanvasShape * shape;
+ GnomeCanvasShapePriv * priv;
+ GnomeCanvasShapePrivGdk * gdk;
+ gint i, pos, len;
+ GSList * l;
+
+ shape = GNOME_CANVAS_SHAPE (item);
+ priv = shape->priv;
+
+ /* We have to be realized, so gdk struct should exist! */
+
+ gdk = shape->priv->gdk;
+ g_assert (gdk != NULL);
+
+ /* Build temporary point list, translated by -x, -y */
+
+ if (dpoints == NULL) {
+ dpoints = g_new (GdkPoint, gdk->num_points);
+ num_dpoints = gdk->num_points;
+ } else if (num_dpoints < gdk->num_points) {
+ dpoints = g_renew (GdkPoint, dpoints, gdk->num_points);
+ num_dpoints = gdk->num_points;
+ }
+
+ for (i = 0; i < gdk->num_points; i++) {
+ dpoints[i].x = gdk->points[i].x - x;
+ dpoints[i].y = gdk->points[i].y - y;
+ }
+
+ if (priv->fill_set) {
+
+ /* Ensure, that we have mask and it is big enough */
+
+ gcbp_ensure_mask (shape, width, height);
+
+ /* Clear mask */
+
+ gdk_draw_rectangle (gdk->ctx->mask,
+ gdk->ctx->clear_gc,
+ TRUE,
+ 0, 0,
+ width, height);
+
+ /* Draw subpaths, using XOR gc */
+
+ pos = 0;
+
+ for (l = gdk->closed_paths; l != NULL; l = l->next) {
+ len = GPOINTER_TO_INT (l->data);
+
+ gdk_draw_polygon (gdk->ctx->mask,
+ gdk->ctx->xor_gc,
+ TRUE,
+ &dpoints[pos],
+ len);
+
+ pos += len;
+ }
+
+ /* Set bitmap to clipping mask */
+
+ gdk_gc_set_clip_mask (gdk->fill_gc, gdk->ctx->mask);
+
+ /* Stipple offset */
+
+ if (gdk->fill_stipple) gnome_canvas_set_stipple_origin (item->canvas, gdk->fill_gc);
+
+ /* Draw clipped rect to drawable */
+
+ gdk_draw_rectangle (drawable,
+ gdk->fill_gc,
+ TRUE,
+ 0, 0,
+ width, height);
+ }
+
+ if (priv->outline_set) {
+
+ /* Stipple offset */
+
+ if (gdk->outline_stipple) gnome_canvas_set_stipple_origin (item->canvas, gdk->outline_gc);
+ /* Draw subpaths */
+
+ pos = 0;
+
+ for (l = gdk->closed_paths; l != NULL; l = l->next) {
+ len = GPOINTER_TO_INT (l->data);
+
+ gdk_draw_polygon (drawable,
+ gdk->outline_gc,
+ FALSE,
+ &dpoints[pos],
+ len);
+
+ pos += len;
+ }
+
+ for (l = gdk->open_paths; l != NULL; l = l->next) {
+ len = GPOINTER_TO_INT (l->data);
+
+ gdk_draw_lines (drawable,
+ gdk->outline_gc,
+ &dpoints[pos],
+ len);
+
+ pos += len;
+ }
+ }
+}
+
+#define GDK_POINTS_BLOCK 32
+
+static void
+gnome_canvas_shape_ensure_gdk_points (GnomeCanvasShapePrivGdk *gdk, gint num)
+{
+ if (gdk->len_points < gdk->num_points + num) {
+ gdk->len_points = MAX (gdk->len_points + GDK_POINTS_BLOCK, gdk->len_points + num);
+ gdk->points = g_renew (GdkPoint, gdk->points, gdk->len_points);
+ }
+}
+
+static void
+gnome_canvas_shape_update_gdk (GnomeCanvasShape * shape, double * affine, ArtSVP * clip, int flags)
+{
+ GnomeCanvasShapePriv * priv;
+ GnomeCanvasShapePrivGdk * gdk;
+ int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+ gboolean bbox_set = FALSE;
+ gint width = 0; /* silence gcc */
+
+ g_assert (!((GnomeCanvasItem *) shape)->canvas->aa);
+
+ priv = shape->priv;
+ gdk = priv->gdk;
+ g_assert (gdk != NULL);
+
+ if (priv->outline_set) {
+ GdkLineStyle style;
+
+ if (priv->width_pixels) {
+ width = (int) floor (priv->width + 0.5);
+ /* Never select 0 pixels unless the user asked for it,
+ * since that is the X11 zero width lines are non-specified */
+ if (priv->width_pixels != 0 && width == 0) {
+ width = 1;
+ }
+ } else {
+ width = (int) floor ((priv->width * priv->scale) + 0.5);
+ /* Never select 0 pixels unless the user asked for it,
+ * since that is the X11 zero width lines are non-speciifed */
+ if (priv->width != 0 && width == 0) {
+ width = 1;
+ }
+ }
+
+ /* If dashed, set it in GdkGC */
+
+ if ((shape->priv->dash.dash != NULL) && (shape->priv->dash.n_dash > 0)) {
+ gint8 * dash_list;
+ gint i;
+
+ dash_list = g_new (gint8, shape->priv->dash.n_dash);
+
+ for (i = 0; i < priv->dash.n_dash; i++) {
+ dash_list[i] = (gint8) shape->priv->dash.dash[i];
+ }
+
+ gdk_gc_set_dashes (gdk->outline_gc,
+ (gint) priv->dash.offset,
+ dash_list,
+ priv->dash.n_dash);
+
+ g_free (dash_list);
+
+ style = GDK_LINE_ON_OFF_DASH;
+ } else {
+ style = GDK_LINE_SOLID;
+ }
+
+ /* Set line width, cap, join */
+ if(gdk->outline_gc) {
+
+ gdk_gc_set_line_attributes (gdk->outline_gc,
+ width,
+ style,
+ priv->cap,
+ priv->join);
+
+ /* Colors and stipples */
+ set_gc_foreground (gdk->outline_gc, gdk->outline_pixel);
+ set_stipple (gdk->outline_gc, &gdk->outline_stipple, gdk->outline_stipple, TRUE);
+ }
+ }
+
+ if (priv->fill_set) {
+
+ /* Colors and stipples */
+ if(gdk->fill_gc) {
+ set_gc_foreground (gdk->fill_gc, gdk->fill_pixel);
+ set_stipple (gdk->fill_gc, &gdk->fill_stipple, gdk->fill_stipple, TRUE);
+ }
+ }
+
+ /* Now the crazy part */
+
+ /* Free existing GdkPoint array */
+
+ if (gdk->points) {
+ g_free (gdk->points);
+ gdk->points = NULL;
+ gdk->len_points = 0;
+ gdk->num_points = 0;
+ }
+
+ /* Free subpath lists */
+
+ while (gdk->closed_paths) gdk->closed_paths = g_slist_remove (gdk->closed_paths, gdk->closed_paths->data);
+ while (gdk->open_paths) gdk->open_paths = g_slist_remove (gdk->open_paths, gdk->open_paths->data);
+
+ /* Calcualte new GdkPoints array and subpath lists */
+
+ if (priv->path) {
+ GnomeCanvasPathDef * apath, * cpath, * opath;
+ ArtBpath * abpath;
+ GSList * clist, * olist;
+ gint pos;
+
+#if 0
+ /* Allocate array */
+ gdk->num_points = gnome_canvas_path_def_length (priv->path) * 1000 - 1;
+ gdk->points = g_new (GdkPoint, gdk->num_points);
+ g_print ("Points %d\n", gdk->num_points);
+ /* Transform path */
+#endif
+
+ abpath = art_bpath_affine_transform (gnome_canvas_path_def_bpath (priv->path), affine);
+ apath = gnome_canvas_path_def_new_from_bpath (abpath);
+
+ /* Split path into open and closed parts */
+
+ cpath = gnome_canvas_path_def_closed_parts (apath);
+ opath = gnome_canvas_path_def_open_parts (apath);
+ gnome_canvas_path_def_unref (apath);
+
+ /* Split partial paths into subpaths */
+
+ clist = gnome_canvas_path_def_split (cpath);
+ gnome_canvas_path_def_unref (cpath);
+ olist = gnome_canvas_path_def_split (opath);
+ gnome_canvas_path_def_unref (opath);
+
+ pos = 0;
+
+ /* Fill GdkPoints and add subpaths to list: closed subpaths */
+
+ while (clist) {
+ GnomeCanvasPathDef * path;
+ ArtBpath * bpath;
+ ArtVpath * vpath;
+ gint len, i;
+
+ path = (GnomeCanvasPathDef *) clist->data;
+ bpath = gnome_canvas_path_def_bpath (path);
+ vpath = art_bez_path_to_vec (bpath, 0.1);
+ for (len = 0; vpath[len].code != ART_END; len++) ;
+
+ gnome_canvas_shape_ensure_gdk_points (gdk, len);
+ for (i = 0; i < len; i++) {
+ gdk->points[pos + i].x = (gint) floor (vpath[i].x + 0.5);
+ gdk->points[pos + i].y = (gint) floor (vpath[i].y + 0.5);
+
+ if (bbox_set) {
+ x1 = MIN (x1, gdk->points[pos + i].x);
+ x2 = MAX (x2, gdk->points[pos + i].x);
+ y1 = MIN (y1, gdk->points[pos + i].y);
+ y2 = MAX (y2, gdk->points[pos + i].y);
+ } else {
+ bbox_set = TRUE;
+ x1 = x2 = gdk->points[pos + i].x;
+ y1 = y2 = gdk->points[pos + i].y;
+ }
+ }
+ gdk->num_points += len;
+
+ art_free (vpath);
+
+ if (len > 0) {
+ pos += len;
+ gdk->closed_paths = g_slist_append (gdk->closed_paths, GINT_TO_POINTER (len));
+ }
+
+ gnome_canvas_path_def_unref (path);
+ clist = g_slist_remove (clist, clist->data);
+ }
+
+ /* Fill GdkPoints and add subpaths to list: open subpaths */
+
+ while (olist) {
+ GnomeCanvasPathDef * path;
+ ArtBpath * bpath;
+ ArtVpath * vpath;
+ gint len, i;
+
+ path = (GnomeCanvasPathDef *) olist->data;
+ bpath = gnome_canvas_path_def_bpath (path);
+ vpath = art_bez_path_to_vec (bpath, 0.1);
+ for (len = 0; vpath[len].code != ART_END; len++) ;
+
+ gnome_canvas_shape_ensure_gdk_points (gdk, len);
+ for (i = 0; i < len; i++) {
+ gdk->points[pos + i].x = (gint) floor (vpath[i].x + 0.5);
+ gdk->points[pos + i].y = (gint) floor (vpath[i].y + 0.5);
+
+ if (bbox_set) {
+ x1 = MIN (x1, gdk->points[pos + i].x);
+ x2 = MAX (x2, gdk->points[pos + i].x);
+ y1 = MIN (y1, gdk->points[pos + i].y);
+ y2 = MAX (y2, gdk->points[pos + i].y);
+ } else {
+ bbox_set = TRUE;
+ x1 = x2 = gdk->points[pos + i].x;
+ y1 = y2 = gdk->points[pos + i].y;
+ }
+ }
+ gdk->num_points += len;
+
+ art_free (vpath);
+
+ if (len > 0) {
+ pos += len;
+ gdk->open_paths = g_slist_append (gdk->open_paths, GINT_TO_POINTER (len));
+ }
+
+ gnome_canvas_path_def_unref (path);
+ olist = g_slist_remove (olist, olist->data);
+ }
+
+ }
+
+ if (bbox_set) {
+ if (priv->outline_set) {
+ int stroke_border = (priv->join == GDK_JOIN_MITER)
+ ? ceil (10.43*width/2) /* 10.43 is the miter limit for X11 */
+ : ceil (width/2);
+ x1 -= stroke_border;
+ x2 += stroke_border;
+ y1 -= stroke_border;
+ y2 += stroke_border;
+ }
+
+ gnome_canvas_update_bbox (GNOME_CANVAS_ITEM (shape),
+ x1, y1,
+ x2 + 1, y2 + 1);
+ }
+
+}
+
+static void
+gnome_canvas_shape_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasShape * shape;
+ GnomeCanvasShapePriv * priv;
+ ArtSVP * svp;
+
+ shape = GNOME_CANVAS_SHAPE (item);
+
+ priv = shape->priv;
+
+ /* Common part */
+ if (parent_class->update) {
+ (* parent_class->update) (item, affine, clip_path, flags);
+ }
+
+ /* Outline width */
+ shape->priv->scale = art_affine_expansion (affine);
+
+ /* Reset bbox */
+ if (item->canvas->aa) {
+ gnome_canvas_item_reset_bounds (item);
+ }
+
+ /* Clipped fill SVP */
+
+ if ((priv->fill_set) && (priv->path) && (gnome_canvas_path_def_any_closed (priv->path))) {
+ GnomeCanvasPathDef * cpath;
+ ArtSvpWriter *swr;
+ ArtVpath *vpath;
+ ArtBpath *abp;
+ ArtSVP *svp2;
+
+ /* Get closed part of path */
+
+ cpath = gnome_canvas_path_def_closed_parts (shape->priv->path);
+ abp = art_bpath_affine_transform (gnome_canvas_path_def_bpath (cpath), affine);
+ gnome_canvas_path_def_unref (cpath);
+
+ /* Render, until SVP */
+
+ vpath = art_bez_path_to_vec (abp, 0.1);
+ art_free (abp);
+
+ svp = art_svp_from_vpath (vpath);
+ art_free (vpath);
+
+ swr = art_svp_writer_rewind_new (shape->priv->wind);
+ art_svp_intersector (svp, swr);
+
+ svp2 = art_svp_writer_rewind_reap (swr);
+ art_svp_free (svp);
+
+ if (item->canvas->aa) {
+ /* Update clipped path */
+ gnome_canvas_item_update_svp_clip (item,
+ &shape->priv->fill_svp,
+ svp2,
+ clip_path);
+ } else {
+ if (priv->fill_svp) {
+ art_svp_free (priv->fill_svp);
+ priv->fill_svp = NULL;
+ }
+ /* No clipping */
+ shape->priv->fill_svp = svp2;
+ }
+ }
+
+ if (priv->outline_set && priv->path && !gnome_canvas_path_def_is_empty (priv->path)) {
+ gdouble width;
+ ArtBpath * abp;
+ ArtVpath * vpath;
+
+ /* Set linewidth */
+
+ if (priv->width_pixels) {
+ width = priv->width;
+ } else {
+ width = priv->width * priv->scale;
+ }
+
+ if (width < 0.5) width = 0.5;
+
+ /* Render full path until vpath */
+
+ abp = art_bpath_affine_transform (gnome_canvas_path_def_bpath (priv->path), affine);
+
+ vpath = art_bez_path_to_vec (abp, 0.1);
+ art_free (abp);
+
+ /* If dashed, apply dash */
+
+ if (priv->dash.dash != NULL)
+ {
+ ArtVpath *old = vpath;
+
+ vpath = art_vpath_dash (old, &priv->dash);
+ art_free (old);
+ }
+
+ /* Stroke vpath to SVP */
+
+ svp = art_svp_vpath_stroke (vpath,
+ gnome_canvas_join_gdk_to_art (priv->join),
+ gnome_canvas_cap_gdk_to_art (priv->cap),
+ width,
+ priv->miterlimit,
+ 0.25);
+ art_free (vpath);
+
+ if (item->canvas->aa) {
+ /* Update clipped */
+ gnome_canvas_item_update_svp_clip (item, &priv->outline_svp, svp, clip_path);
+ } else {
+ if (priv->outline_svp) {
+ art_svp_free (priv->outline_svp);
+ priv->outline_svp = NULL;
+ }
+ /* No clipping (yet) */
+ shape->priv->outline_svp = svp;
+ }
+ }
+
+ /* Gdk requires additional handling */
+
+ if (!item->canvas->aa) {
+ gnome_canvas_shape_update_gdk (shape, affine, clip_path, flags);
+ }
+}
+
+static double
+gnome_canvas_shape_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy, GnomeCanvasItem **actual_item)
+{
+ GnomeCanvasShape *shape;
+ double dist;
+ int wind;
+
+#if 0
+ /* fixme: This is just for debugging, canvas should ensure that */
+ /* fixme: IF YOU ARE SURE THAT IT IS CORRECT BEHAVIOUR, you can remove warning */
+ /* fixme: and make it to return silently */
+ g_return_val_if_fail (!item->canvas->need_update, 1e18);
+#endif
+
+ shape = GNOME_CANVAS_SHAPE (item);
+
+ /* todo: update? */
+ if (shape->priv->fill_set && shape->priv->fill_svp) {
+ wind = art_svp_point_wind (shape->priv->fill_svp, cx, cy);
+ if ((shape->priv->wind == ART_WIND_RULE_NONZERO) && (wind != 0)) {
+ *actual_item = item;
+ return 0.0;
+ }
+ if ((shape->priv->wind == ART_WIND_RULE_ODDEVEN) && ((wind & 0x1) != 0)) {
+ *actual_item = item;
+ return 0.0;
+ }
+ }
+
+ if (shape->priv->outline_set && shape->priv->outline_svp) {
+ wind = art_svp_point_wind (shape->priv->outline_svp, cx, cy);
+ if (wind) {
+ *actual_item = item;
+ return 0.0;
+ }
+ }
+
+ if (shape->priv->outline_set && shape->priv->outline_svp) {
+ dist = art_svp_point_dist (shape->priv->outline_svp, cx, cy);
+ } else if (shape->priv->fill_set && shape->priv->outline_svp) {
+ dist = art_svp_point_dist (shape->priv->fill_svp, cx, cy);
+ } else {
+ return 1e12;
+ }
+
+ *actual_item = item;
+
+ return dist;
+}
+
+/* Helpers */
+
+/* Get 32bit rgba color from GdkColor */
+
+static guint32
+get_rgba_from_color (GdkColor * color)
+{
+ return ((color->red & 0xff00) << 16) | ((color->green & 0xff00) << 8) | (color->blue & 0xff00) | 0xff;
+}
+
+/* Get Gdk pixel value from 32bit rgba color */
+
+static gulong
+get_pixel_from_rgba (GnomeCanvasItem *item, guint32 rgba_color)
+{
+ return gnome_canvas_get_color_pixel (item->canvas, rgba_color);
+}
+
+/* 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;
+
+ g_assert (gc != NULL);
+
+ 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);
+ }
+}
+
+/* Creates private Gdk struct, if not present */
+/* We cannot do it during ::init, as we have to know canvas */
+
+static void
+gcbp_ensure_gdk (GnomeCanvasShape * shape)
+{
+ g_assert (!((GnomeCanvasItem *) shape)->canvas->aa);
+
+ if (!shape->priv->gdk) {
+ GnomeCanvasShapePrivGdk * gdk;
+
+ gdk = g_new (GnomeCanvasShapePrivGdk, 1);
+
+ gdk->fill_pixel = get_pixel_from_rgba ((GnomeCanvasItem *) shape, shape->priv->fill_rgba);
+ gdk->outline_pixel = get_pixel_from_rgba ((GnomeCanvasItem *) shape, shape->priv->outline_rgba);
+
+ gdk->fill_stipple = NULL;
+ gdk->outline_stipple = NULL;
+
+ gdk->fill_gc = NULL;
+ gdk->outline_gc = NULL;
+
+ gdk->len_points = 0;
+ gdk->num_points = 0;
+ gdk->points = NULL;
+ gdk->closed_paths = NULL;
+ gdk->open_paths = NULL;
+
+ gdk->ctx = NULL;
+
+ shape->priv->gdk = gdk;
+ }
+}
+
+/* Destroy private Gdk struct */
+/* It is here, to make ::destroy implementation shorter :) */
+
+static void
+gcbp_destroy_gdk (GnomeCanvasShape * shape)
+{
+ GnomeCanvasShapePrivGdk * gdk;
+
+ g_assert (!((GnomeCanvasItem *)shape)->canvas->aa);
+
+ gdk = shape->priv->gdk;
+
+ if (gdk) {
+ g_assert (!gdk->fill_gc);
+ g_assert (!gdk->outline_gc);
+
+ if (gdk->fill_stipple)
+ g_object_unref (gdk->fill_stipple);
+
+ if (gdk->outline_stipple)
+ g_object_unref (gdk->outline_stipple);
+
+ if (gdk->points)
+ g_free (gdk->points);
+
+ while (gdk->closed_paths)
+ gdk->closed_paths = g_slist_remove (gdk->closed_paths, gdk->closed_paths->data);
+ while (gdk->open_paths)
+ gdk->open_paths = g_slist_remove (gdk->open_paths, gdk->open_paths->data);
+
+ if (gdk->ctx)
+ gcbp_draw_ctx_unref (gdk->ctx);
+
+ g_free (gdk);
+
+ shape->priv->gdk = NULL;
+ }
+}
+
+/*
+ * Ensure, that per-canvas Ctx struct is present and bitmaps are
+ * big enough, to mask full redraw area. Ctx is refcounted and
+ * defined as "BpathDrawCtx" data member on parent canvas
+ */
+
+static void
+gcbp_ensure_mask (GnomeCanvasShape * shape, gint width, gint height)
+{
+ GnomeCanvasShapePrivGdk * gdk;
+ GCBPDrawCtx * ctx;
+
+ gdk = shape->priv->gdk;
+ g_assert (gdk != NULL);
+ ctx = gdk->ctx;
+
+ if (!ctx) {
+ /* Ctx is not yet defined for us */
+
+ GnomeCanvas * canvas;
+
+ canvas = GNOME_CANVAS_ITEM (shape)->canvas;
+
+ ctx = g_object_get_data (G_OBJECT (canvas), "BpathDrawCtx");
+
+ if (!ctx) {
+ /* Ctx is not defined for parent canvas yet */
+
+ ctx = g_new (GCBPDrawCtx, 1);
+
+ ctx->refcount = 1;
+ ctx->canvas = canvas;
+ ctx->width = 0;
+ ctx->height = 0;
+
+ ctx->mask = NULL;
+ ctx->clip = NULL;
+
+ ctx->clear_gc = NULL;
+ ctx->xor_gc = NULL;
+
+ g_object_set_data (G_OBJECT (canvas), "BpathDrawCtx", ctx);
+
+ } else {
+ ctx->refcount++;
+ }
+
+ gdk->ctx = ctx;
+
+ }
+
+ /* Now we are sure, that ctx is present and properly refcounted */
+
+ if ((width > ctx->width) || (height > ctx->height)) {
+ /* Ctx is too small */
+
+ GdkWindow * window;
+
+ window = ((GtkWidget *) (((GnomeCanvasItem *) shape)->canvas))->window;
+
+ if (ctx->clear_gc) g_object_unref (ctx->clear_gc);
+ if (ctx->xor_gc) g_object_unref (ctx->xor_gc);
+ if (ctx->mask) g_object_unref (ctx->mask);
+ if (ctx->clip) g_object_unref (ctx->clip);
+
+ ctx->mask = gdk_pixmap_new (window, width, height, 1);
+ ctx->clip = NULL;
+
+ ctx->clear_gc = gdk_gc_new (ctx->mask);
+ gdk_gc_set_function (ctx->clear_gc, GDK_CLEAR);
+
+ ctx->xor_gc = gdk_gc_new (ctx->mask);
+ gdk_gc_set_function (ctx->xor_gc, GDK_INVERT);
+ }
+}
+
+/* It is cleaner to have it here, not in parent function */
+
+static void
+gcbp_draw_ctx_unref (GCBPDrawCtx * ctx)
+{
+ if (--ctx->refcount < 1) {
+ if (ctx->clear_gc)
+ g_object_unref (ctx->clear_gc);
+ if (ctx->xor_gc)
+ g_object_unref (ctx->xor_gc);
+
+ if (ctx->mask)
+ g_object_unref (ctx->mask);
+ if (ctx->clip)
+ g_object_unref (ctx->clip);
+
+ g_object_set_data (G_OBJECT (ctx->canvas), "BpathDrawCtx", NULL);
+ g_free (ctx);
+ }
+}
+
+static void
+gnome_canvas_shape_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ GnomeCanvasShape * shape;
+ GnomeCanvasShapePriv * priv;
+ ArtDRect bbox;
+ ArtSVP * svp;
+
+ shape = GNOME_CANVAS_SHAPE (item);
+
+ priv = shape->priv;
+
+ bbox.x0 = *x1;
+ bbox.y0 = *y1;
+ bbox.x1 = *x2;
+ bbox.y1 = *y2;
+
+ if (priv->outline_set && priv->path && !gnome_canvas_path_def_is_empty (priv->path)) {
+ gdouble width;
+ ArtVpath * vpath;
+
+ /* Set linewidth */
+
+ if (priv->width_pixels) {
+ width = priv->width;
+ } else {
+ width = priv->width * priv->scale;
+ }
+
+ if (width < 0.5) width = 0.5;
+
+ /* Render full path until vpath */
+
+ vpath = art_bez_path_to_vec (gnome_canvas_path_def_bpath (priv->path), 0.1);
+
+ /* If dashed, apply dash */
+
+ if (priv->dash.dash != NULL)
+ {
+ ArtVpath *old = vpath;
+
+ vpath = art_vpath_dash (old, &priv->dash);
+ art_free (old);
+ }
+
+ /* Stroke vpath to SVP */
+
+ svp = art_svp_vpath_stroke (vpath,
+ gnome_canvas_join_gdk_to_art (priv->join),
+ gnome_canvas_cap_gdk_to_art (priv->cap),
+ width,
+ priv->miterlimit,
+ 0.25);
+ art_free (vpath);
+ art_drect_svp (&bbox, svp);
+ art_svp_free (svp);
+ } else if ((priv->fill_set) && (priv->path) && (gnome_canvas_path_def_any_closed (priv->path))) {
+ GnomeCanvasPathDef *cpath;
+ ArtSvpWriter *swr;
+ ArtVpath *vpath;
+ ArtSVP *svp2;
+
+ /* Get closed part of path */
+ cpath = gnome_canvas_path_def_closed_parts (shape->priv->path);
+ /* Render, until SVP */
+ vpath = art_bez_path_to_vec (gnome_canvas_path_def_bpath (cpath), 0.1);
+ gnome_canvas_path_def_unref (cpath);
+
+ svp = art_svp_from_vpath (vpath);
+ art_free (vpath);
+
+ swr = art_svp_writer_rewind_new (shape->priv->wind);
+ art_svp_intersector (svp, swr);
+
+ svp2 = art_svp_writer_rewind_reap (swr);
+ art_svp_free (svp);
+
+ art_drect_svp (&bbox, svp2);
+ art_svp_free (svp2);
+ }
+
+ *x1 = bbox.x0;
+ *y1 = bbox.y0;
+ *x2 = bbox.x1;
+ *y2 = bbox.y1;
+}
diff --git a/libgnomecanvas/gnome-canvas-shape.h b/libgnomecanvas/gnome-canvas-shape.h
new file mode 100644
index 0000000000..b91578ba6b
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-shape.h
@@ -0,0 +1,81 @@
+/* Generic bezier shape item for GnomeCanvas
+ *
+ * GnomeCanvas 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.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico@nuclecu.unam.mx>
+ * Raph Levien <raph@acm.org>
+ * Lauris Kaplinski <lauris@ximian.com>
+ * Rusty Conover <rconover@bangtail.net>
+ */
+
+#ifndef GNOME_CANVAS_SHAPE_H
+#define GNOME_CANVAS_SHAPE_H
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-path-def.h>
+
+G_BEGIN_DECLS
+
+
+/* Shape item for the canvas.
+ *
+ * The following object arguments are available:
+ *
+ * name type read/write description
+ * ------------------------------------------------------------------------------------------
+ * 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.
+ * cap_style GdkCapStyle RW Cap ("endpoint") style for the bpath.
+ * join_style GdkJoinStyle RW Join ("vertex") style for the bpath.
+ * wind ArtWindRule RW Winding rule for the bpath.
+ * dash ArtVpathDash RW Dashing pattern
+ * miterlimit double RW Minimum angle between segments, where miter join
+ * rule is applied.
+ */
+
+#define GNOME_TYPE_CANVAS_SHAPE (gnome_canvas_shape_get_type ())
+#define GNOME_CANVAS_SHAPE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_SHAPE, GnomeCanvasShape))
+#define GNOME_CANVAS_SHAPE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_SHAPE, GnomeCanvasShapeClass))
+#define GNOME_IS_CANVAS_SHAPE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_SHAPE))
+#define GNOME_IS_CANVAS_SHAPE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_SHAPE))
+
+
+typedef struct _GnomeCanvasShape GnomeCanvasShape;
+typedef struct _GnomeCanvasShapePriv GnomeCanvasShapePriv;
+typedef struct _GnomeCanvasShapeClass GnomeCanvasShapeClass;
+
+struct _GnomeCanvasShape {
+ GnomeCanvasItem item;
+
+ GnomeCanvasShapePriv *priv; /* Private data */
+};
+
+struct _GnomeCanvasShapeClass {
+ GnomeCanvasItemClass parent_class;
+};
+
+
+/* WARNING! These are not usable from modifying shapes from user programs */
+/* These are meant, to set master shape from subclass ::update method */
+void gnome_canvas_shape_set_path_def (GnomeCanvasShape *shape, GnomeCanvasPathDef *def);
+GnomeCanvasPathDef *gnome_canvas_shape_get_path_def (GnomeCanvasShape *shape);
+
+/* Standard Gtk function */
+GType gnome_canvas_shape_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-text.c b/libgnomecanvas/gnome-canvas-text.c
new file mode 100644
index 0000000000..81cc357e41
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-text.c
@@ -0,0 +1,1746 @@
+/* -*- 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 GnomeCanvas widget
+ *
+ * GnomeCanvas 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 "gnome-canvas-text.h"
+#include <pango/pangoft2.h>
+
+#include <libart_lgpl/art_affine.h>
+#include <libart_lgpl/art_rgb_a_affine.h>
+#include <libart_lgpl/art_rgb.h>
+#include <libart_lgpl/art_rgb_bitmap_affine.h>
+#include "gnome-canvas-util.h"
+#include "gnome-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_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 _GnomeCanvasTextPrivate {
+ guint render_dirty : 1;
+ FT_Bitmap bitmap;
+};
+
+
+static void gnome_canvas_text_class_init (GnomeCanvasTextClass *class);
+static void gnome_canvas_text_init (GnomeCanvasText *text);
+static void gnome_canvas_text_destroy (GtkObject *object);
+static void gnome_canvas_text_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_text_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_text_update (GnomeCanvasItem *item, double *affine,
+ ArtSVP *clip_path, int flags);
+static void gnome_canvas_text_realize (GnomeCanvasItem *item);
+static void gnome_canvas_text_unrealize (GnomeCanvasItem *item);
+static void gnome_canvas_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height);
+static double gnome_canvas_text_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
+ GnomeCanvasItem **actual_item);
+static void gnome_canvas_text_bounds (GnomeCanvasItem *item,
+ double *x1, double *y1, double *x2, double *y2);
+static void gnome_canvas_text_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+
+static void gnome_canvas_text_set_markup (GnomeCanvasText *textitem,
+ const gchar *markup);
+
+static void gnome_canvas_text_set_font_desc (GnomeCanvasText *textitem,
+ PangoFontDescription *font_desc);
+
+static void gnome_canvas_text_apply_font_desc (GnomeCanvasText *textitem);
+static void gnome_canvas_text_apply_attributes (GnomeCanvasText *textitem);
+
+static void add_attr (PangoAttrList *attr_list,
+ PangoAttribute *attr);
+
+static GnomeCanvasItemClass *parent_class;
+
+
+
+/**
+ * gnome_canvas_text_get_type:
+ * @void:
+ *
+ * Registers the &GnomeCanvasText class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &GnomeCanvasText class.
+ **/
+GType
+gnome_canvas_text_get_type (void)
+{
+ static GType text_type;
+
+ if (!text_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasTextClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_text_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasText),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_text_init,
+ NULL /* value_table */
+ };
+
+ text_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasText",
+ &object_info, 0);
+ }
+
+ return text_type;
+}
+
+/* Class initialization function for the text item */
+static void
+gnome_canvas_text_class_init (GnomeCanvasTextClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = gnome_canvas_text_set_property;
+ gobject_class->get_property = gnome_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_READABLE | G_PARAM_WRITABLE)));
+
+ 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_READABLE | G_PARAM_WRITABLE)));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y,
+ g_param_spec_double ("y", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+
+ /* Font */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FONT,
+ g_param_spec_string ("font",
+ _("Font"),
+ _("Font description as a string"),
+ NULL,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ 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_READABLE | G_PARAM_WRITABLE)));
+
+ 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_READABLE | G_PARAM_WRITABLE)));
+
+ /* Style */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_ATTRIBUTES,
+ g_param_spec_boxed ("attributes", NULL, NULL,
+ PANGO_TYPE_ATTR_LIST,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ 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 (as a multiple of PANGO_SCALE, eg. 12*PANGO_SCALE for a 12pt 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 (eg. 12 for a 12pt font size)"),
+ 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_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_JUSTIFICATION,
+ g_param_spec_enum ("justification", NULL, NULL,
+ GTK_TYPE_JUSTIFICATION,
+ GTK_JUSTIFY_LEFT,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_CLIP,
+ g_param_spec_boolean ("clip", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FILL_COLOR,
+ g_param_spec_string ("fill_color",
+ _("Color"),
+ _("Text color, as string"),
+ NULL,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FILL_STIPPLE,
+ g_param_spec_object ("fill_stipple", NULL, NULL,
+ GDK_TYPE_DRAWABLE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE));
+ 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_READABLE));
+
+ /* 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 = gnome_canvas_text_destroy;
+
+ item_class->update = gnome_canvas_text_update;
+ item_class->realize = gnome_canvas_text_realize;
+ item_class->unrealize = gnome_canvas_text_unrealize;
+ item_class->draw = gnome_canvas_text_draw;
+ item_class->point = gnome_canvas_text_point;
+ item_class->bounds = gnome_canvas_text_bounds;
+ item_class->render = gnome_canvas_text_render;
+}
+
+/* Object initialization function for the text item */
+static void
+gnome_canvas_text_init (GnomeCanvasText *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 (GnomeCanvasTextPrivate, 1);
+ text->priv->bitmap.buffer = NULL;
+ text->priv->render_dirty = 1;
+}
+
+/* Destroy handler for the text item */
+static void
+gnome_canvas_text_destroy (GtkObject *object)
+{
+ GnomeCanvasText *text;
+
+ g_return_if_fail (GNOME_IS_CANVAS_TEXT (object));
+
+ text = GNOME_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;
+
+ if (text->priv && text->priv->bitmap.buffer) {
+ g_free (text->priv->bitmap.buffer);
+ }
+ 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 (GnomeCanvasText *text, double *px1, double *py1, double *px2, double *py2)
+{
+ GnomeCanvasItem *item;
+ double wx, wy;
+
+ item = GNOME_CANVAS_ITEM (text);
+
+ /* Get canvas pixel coordinates for text position */
+
+
+ wx = text->x;
+ wy = text->y;
+ gnome_canvas_item_i2w (item, &wx, &wy);
+ gnome_canvas_w2c (item->canvas, wx + text->xofs, wy + text->yofs, &text->cx, &text->cy);
+
+ /* Get canvas pixel coordinates for clip rectangle position */
+
+ gnome_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 (GnomeCanvasText *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 (GnomeCanvasText *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 (GnomeCanvasText *text)
+{
+ if (!text->font_desc)
+ text->font_desc = pango_font_description_new ();
+}
+
+/* Set_arg handler for the text item */
+static void
+gnome_canvas_text_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasText *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 (GNOME_IS_CANVAS_TEXT (object));
+
+ item = GNOME_CANVAS_ITEM (object);
+ text = GNOME_CANVAS_TEXT (object);
+
+ color_changed = FALSE;
+ have_pixel = FALSE;
+
+
+ if (!text->layout) {
+
+ PangoContext *gtk_context, *context;
+ gtk_context = gtk_widget_get_pango_context (GTK_WIDGET (item->canvas));
+
+ if (item->canvas->aa) {
+ PangoFontMap *fontmap;
+ PangoLanguage *language;
+ gint pixels, mm;
+ double dpi_x;
+ double dpi_y;
+
+ pixels = gdk_screen_width ();
+ mm = gdk_screen_width_mm ();
+ dpi_x = (((double) pixels * 25.4) / (double) mm);
+
+ pixels = gdk_screen_height ();
+ mm = gdk_screen_height_mm ();
+ dpi_y = (((double) pixels * 25.4) / (double) mm);
+
+ /* XXX This used to call pango_ft2_get_context().
+ * Is there a better way to do this? */
+ fontmap = pango_ft2_font_map_new ();
+ pango_ft2_font_map_set_resolution (PANGO_FT2_FONT_MAP (fontmap), dpi_x, dpi_y);
+ context = pango_font_map_create_context (fontmap);
+
+ language = pango_context_get_language (gtk_context);
+ pango_context_set_language (context, language);
+ pango_context_set_base_dir (context,
+ pango_context_get_base_dir (gtk_context));
+ pango_context_set_font_description (context,
+ pango_context_get_font_description (gtk_context));
+
+ } else
+ context = gtk_context;
+
+
+ text->layout = pango_layout_new (context);
+
+ if (item->canvas->aa)
+ g_object_unref (G_OBJECT (context));
+ }
+
+ switch (param_id) {
+ case PROP_TEXT:
+ g_free (text->text);
+
+ text->text = g_value_dup_string (value);
+ pango_layout_set_text (text->layout, text->text, -1);
+
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_MARKUP:
+ gnome_canvas_text_set_markup (text,
+ g_value_get_string (value));
+ text->priv->render_dirty = 1;
+ 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;
+
+ gnome_canvas_text_set_font_desc (text, font_desc);
+ if (font_desc)
+ pango_font_description_free (font_desc);
+
+ break;
+ }
+
+ case PROP_FONT_DESC:
+ gnome_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;
+ }
+
+ gnome_canvas_text_apply_font_desc (text);
+ text->priv->render_dirty = 1;
+ 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;
+
+ gnome_canvas_text_apply_font_desc (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_SCALE_SET:
+ text->scale_set = g_value_get_boolean (value);
+
+ gnome_canvas_text_apply_font_desc (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_UNDERLINE:
+ text->underline = g_value_get_enum (value);
+ text->underline_set = TRUE;
+
+ gnome_canvas_text_apply_attributes (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_UNDERLINE_SET:
+ text->underline_set = g_value_get_boolean (value);
+
+ gnome_canvas_text_apply_attributes (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_STRIKETHROUGH:
+ text->strikethrough = g_value_get_boolean (value);
+ text->strike_set = TRUE;
+
+ gnome_canvas_text_apply_attributes (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_STRIKETHROUGH_SET:
+ text->strike_set = g_value_get_boolean (value);
+
+ gnome_canvas_text_apply_attributes (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_RISE:
+ text->rise = g_value_get_int (value);
+ text->rise_set = TRUE;
+
+ gnome_canvas_text_apply_attributes (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_RISE_SET:
+ text->rise_set = TRUE;
+
+ gnome_canvas_text_apply_attributes (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_ATTRIBUTES:
+ if (text->attr_list)
+ pango_attr_list_unref (text->attr_list);
+
+ text->attr_list = g_value_peek_pointer (value);
+ pango_attr_list_ref (text->attr_list);
+
+ gnome_canvas_text_apply_attributes (text);
+ text->priv->render_dirty = 1;
+ 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);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_CLIP_WIDTH:
+ text->clip_width = fabs (g_value_get_double (value));
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_CLIP_HEIGHT:
+ text->clip_height = fabs (g_value_get_double (value));
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_CLIP:
+ text->clip = g_value_get_boolean (value);
+ text->priv->render_dirty = 1;
+ 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;
+ }
+ text->priv->render_dirty = 1;
+ 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;
+ text->priv->render_dirty = 1;
+ 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 = gnome_canvas_get_color_pixel (item->canvas, text->rgba);
+
+ if (!item->canvas->aa)
+ 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;
+ }
+
+ gnome_canvas_item_request_update (item);
+}
+
+/* Get_arg handler for the text item */
+static void
+gnome_canvas_text_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasText *text;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_TEXT (object));
+
+ text = GNOME_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_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: {
+ GnomeCanvas *canvas = GNOME_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
+gnome_canvas_text_apply_font_desc (GnomeCanvasText *text)
+{
+ PangoFontDescription *font_desc =
+ pango_font_description_copy (
+ GTK_WIDGET (GNOME_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
+gnome_canvas_text_apply_attributes (GnomeCanvasText *text)
+{
+ PangoAttrList *attr_list;
+
+ 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));
+
+ pango_layout_set_attributes (text->layout, attr_list);
+ pango_attr_list_unref (attr_list);
+}
+
+static void
+gnome_canvas_text_set_font_desc (GnomeCanvasText *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;
+
+ gnome_canvas_text_apply_font_desc (text);
+ text->priv->render_dirty = 1;
+}
+
+/* Setting the text from a Pango markup string */
+static void
+gnome_canvas_text_set_markup (GnomeCanvasText *textitem,
+ const gchar *markup)
+{
+ PangoAttrList *attr_list = NULL;
+ gchar *text = NULL;
+ GError *error = NULL;
+
+ 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;
+ }
+
+ g_free (textitem->text);
+ if (textitem->attr_list)
+ pango_attr_list_unref (textitem->attr_list);
+
+ textitem->text = text;
+ textitem->attr_list = attr_list;
+
+ pango_layout_set_text (textitem->layout, text, -1);
+
+ gnome_canvas_text_apply_attributes (textitem);
+}
+
+/* Update handler for the text item */
+static void
+gnome_canvas_text_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasText *text;
+ double x1, y1, x2, y2;
+
+ text = GNOME_CANVAS_TEXT (item);
+
+ if (parent_class->update)
+ (* parent_class->update) (item, affine, clip_path, flags);
+
+ set_text_gc_foreground (text);
+ set_stipple (text, text->stipple, TRUE);
+ get_bounds (text, &x1, &y1, &x2, &y2);
+
+ gnome_canvas_update_bbox (item,
+ floor (x1), floor (y1),
+ ceil (x2), ceil (y2));
+}
+
+/* Realize handler for the text item */
+static void
+gnome_canvas_text_realize (GnomeCanvasItem *item)
+{
+ GnomeCanvasText *text;
+
+ text = GNOME_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
+gnome_canvas_text_unrealize (GnomeCanvasItem *item)
+{
+ GnomeCanvasText *text;
+
+ text = GNOME_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
+gnome_canvas_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height)
+{
+ GnomeCanvasText *text;
+ GdkRectangle rect;
+
+ text = GNOME_CANVAS_TEXT (item);
+
+ if (!text->text)
+ return;
+
+ if (text->clip) {
+ rect.x = text->clip_cx - x;
+ rect.y = text->clip_cy - y;
+ rect.width = text->clip_cwidth;
+ rect.height = text->clip_cheight;
+
+ gdk_gc_set_clip_rectangle (text->gc, &rect);
+ }
+
+ if (text->stipple)
+ gnome_canvas_set_stipple_origin (item->canvas, text->gc);
+
+
+ gdk_draw_layout (drawable, text->gc, text->cx - x, text->cy - y, text->layout);
+
+ if (text->clip)
+ gdk_gc_set_clip_rectangle (text->gc, NULL);
+}
+
+
+/* Render handler for the text item */
+static void
+gnome_canvas_text_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
+{
+ GnomeCanvasText *text;
+ guint32 fg_color;
+ int render_x = 0, render_y = 0; /* offsets for text rendering,
+ * for clipping rectangles */
+ int x, y;
+ int w, h;
+ guchar *dst, *src;
+ int src_dx, src_dy;
+ int i, alpha;
+ int bm_rows, bm_width;
+
+ text = GNOME_CANVAS_TEXT (item);
+
+ if (!text->text)
+ return;
+
+ fg_color = text->rgba;
+
+ gnome_canvas_buf_ensure_buf (buf);
+
+ bm_rows = (text->clip) ? text->clip_cheight : text->height;
+ bm_width = (text->clip) ? text->clip_cwidth : text->max_width;
+ if(text->priv->render_dirty ||
+ bm_rows != text->priv->bitmap.rows ||
+ bm_width != text->priv->bitmap.width) {
+ if(text->priv->bitmap.buffer) {
+ g_free(text->priv->bitmap.buffer);
+ }
+ text->priv->bitmap.rows = bm_rows;
+ text->priv->bitmap.width = bm_width;
+ text->priv->bitmap.pitch = (text->priv->bitmap.width+3)&~3;
+ text->priv->bitmap.buffer = g_malloc0 (text->priv->bitmap.rows * text->priv->bitmap.pitch);
+ text->priv->bitmap.num_grays = 256;
+ text->priv->bitmap.pixel_mode = ft_pixel_mode_grays;
+
+ /* What this does is when a clipping rectangle is
+ being used shift the rendering of the text by the
+ correct amount so that the correct result is
+ obtained as if all text was rendered, then clipped.
+ In this sense we can use smaller buffers and less
+ rendeirng since hopefully FreeType2 checks to see
+ if the glyph falls in the bounding box before
+ rasterizing it. */
+
+ if(text->clip) {
+ render_x = text->cx - text->clip_cx;
+ render_y = text->cy - text->clip_cy;
+ }
+
+ pango_ft2_render_layout (&text->priv->bitmap, text->layout, render_x, render_y);
+
+ text->priv->render_dirty = 0;
+ }
+
+ if (text->clip) {
+ x = text->clip_cx - buf->rect.x0;
+ y = text->clip_cy - buf->rect.y0;
+ } else {
+ x = text->cx - buf->rect.x0;
+ y = text->cy - buf->rect.y0;
+ }
+
+ w = text->priv->bitmap.width;
+ h = text->priv->bitmap.rows;
+
+ src_dx = src_dy = 0;
+
+ if (x + w > buf->rect.x1 - buf->rect.x0) {
+ w = buf->rect.x1 - buf->rect.x0 - x;
+ }
+
+ if (y + h > buf->rect.y1 - buf->rect.y0) {
+ h = buf->rect.y1 - buf->rect.y0 - y;
+ }
+
+ if (x < 0) {
+ w -= - x;
+ src_dx += - x;
+ x = 0;
+ }
+
+ if (y < 0) {
+ h -= -y;
+ src_dy += - y;
+ y = 0;
+ }
+
+ dst = buf->buf + y * buf->buf_rowstride + x * 3;
+ src = text->priv->bitmap.buffer +
+ src_dy * text->priv->bitmap.pitch + src_dx;
+ while (h-- > 0) {
+ i = w;
+ while (i-- > 0) {
+ /* FIXME: Do the libart thing instead of divide by 255 */
+ alpha = ((fg_color & 0xff) * (*src)) / 255;
+ dst[0] = (dst[0] * (255 - alpha) + ((fg_color >> 24) & 0xff) * alpha) / 255;
+ dst[1] = (dst[1] * (255 - alpha) + ((fg_color >> 16) & 0xff) * alpha) / 255;
+ dst[2] = (dst[2] * (255 - alpha) + ((fg_color >> 8) & 0xff) * alpha) / 255;
+ dst += 3;
+ src += 1;
+ }
+ dst += buf->buf_rowstride - w*3;
+ src += text->priv->bitmap.pitch - w;
+ }
+
+ buf->is_bg = 0;
+ return;
+}
+
+/* Point handler for the text item */
+static double
+gnome_canvas_text_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy, GnomeCanvasItem **actual_item)
+{
+ GnomeCanvasText *text;
+ PangoLayoutIter *iter;
+ int x1, y1, x2, y2;
+ int dx, dy;
+ double dist, best;
+
+ text = GNOME_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);
+
+ x1 = text->cx + PANGO_PIXELS (log_rect.x);
+ y1 = text->cy + PANGO_PIXELS (log_rect.y);
+ x2 = x1 + PANGO_PIXELS (log_rect.width);
+ y2 = y1 + PANGO_PIXELS (log_rect.height);
+
+ if (text->clip) {
+ 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;
+ }
+
+ /* 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;
+}
+
+/* Bounds handler for the text item */
+static void
+gnome_canvas_text_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ GnomeCanvasText *text;
+ double width, height;
+
+ text = GNOME_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/libgnomecanvas/gnome-canvas-text.h b/libgnomecanvas/gnome-canvas-text.h
new file mode 100644
index 0000000000..ed86633371
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-text.h
@@ -0,0 +1,170 @@
+/* -*- 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 GnomeCanvas widget
+ *
+ * GnomeCanvas 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 GNOME_CANVAS_TEXT_H
+#define GNOME_CANVAS_TEXT_H
+
+
+#include <libgnomecanvas/gnome-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 GNOME_TYPE_CANVAS_TEXT (gnome_canvas_text_get_type ())
+#define GNOME_CANVAS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_TEXT, GnomeCanvasText))
+#define GNOME_CANVAS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_TEXT, GnomeCanvasTextClass))
+#define GNOME_IS_CANVAS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_TEXT))
+#define GNOME_IS_CANVAS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_TEXT))
+#define GNOME_CANVAS_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_TEXT, GnomeCanvasTextClass))
+
+
+typedef struct _GnomeCanvasText GnomeCanvasText;
+typedef struct _GnomeCanvasTextClass GnomeCanvasTextClass;
+
+typedef struct _GnomeCanvasTextPrivate GnomeCanvasTextPrivate;
+
+struct _GnomeCanvasText {
+ GnomeCanvasItem 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 */
+
+ double affine[6]; /* The item -> canvas affine */ /*AA*/
+
+ 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? */
+
+ GnomeCanvasTextPrivate *priv;
+};
+
+struct _GnomeCanvasTextClass {
+ GnomeCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_text_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-util.c b/libgnomecanvas/gnome-canvas-util.c
new file mode 100644
index 0000000000..d306292144
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-util.c
@@ -0,0 +1,700 @@
+/*
+ * 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 GnomeCanvas widget
+ *
+ * GnomeCanvas 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>
+
+/* needed for M_PI_2 under 'gcc -ansi -predantic' on GNU/Linux */
+#ifndef _BSD_SOURCE
+# define _BSD_SOURCE 1
+#endif
+#include <sys/types.h>
+
+#include <glib.h>
+#include <math.h>
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+#include <libart_lgpl/art_uta.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_ops.h>
+#include <libart_lgpl/art_rgb.h>
+#include <libart_lgpl/art_rgb_svp.h>
+#include <libart_lgpl/art_uta_svp.h>
+#include <libart_lgpl/art_rect_svp.h>
+
+/**
+ * gnome_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.
+ **/
+GnomeCanvasPoints *
+gnome_canvas_points_new (int num_points)
+{
+ GnomeCanvasPoints *points;
+
+ g_return_val_if_fail (num_points > 1, NULL);
+
+ points = g_new (GnomeCanvasPoints, 1);
+ points->num_points = num_points;
+ points->coords = g_new (double, 2 * num_points);
+ points->ref_count = 1;
+
+ return points;
+}
+
+/**
+ * gnome_canvas_points_ref:
+ * @points: A canvas points structure.
+ *
+ * Increases the reference count of the specified points structure.
+ *
+ * Return value: The canvas points structure itself.
+ **/
+GnomeCanvasPoints *
+gnome_canvas_points_ref (GnomeCanvasPoints *points)
+{
+ g_return_val_if_fail (points != NULL, NULL);
+
+ points->ref_count += 1;
+ return points;
+}
+
+/**
+ * gnome_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
+gnome_canvas_points_free (GnomeCanvasPoints *points)
+{
+ g_return_if_fail (points != NULL);
+
+ points->ref_count -= 1;
+ if (points->ref_count == 0) {
+ g_free (points->coords);
+ g_free (points);
+ }
+}
+
+/**
+ * gnome_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
+gnome_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 */
+
+#define ELEVEN_DEGREES (11.0 * G_PI / 180.0)
+
+ if (y2 == y1)
+ theta1 = (x2 < x1) ? 0.0 : G_PI;
+ else if (x2 == x1)
+ theta1 = (y2 < y1) ? G_PI_2 : -G_PI_2;
+ else
+ theta1 = atan2 (y1 - y2, x1 - x2);
+
+ if (y3 == y2)
+ theta2 = (x3 > x2) ? 0 : G_PI;
+ else if (x3 == x2)
+ theta2 = (y3 > y2) ? G_PI_2 : -G_PI_2;
+ else
+ theta2 = atan2 (y3 - y2, x3 - x2);
+
+ theta = theta1 - theta2;
+
+ if (theta > G_PI)
+ theta -= 2.0 * G_PI;
+ else if (theta < -G_PI)
+ theta += 2.0 * G_PI;
+
+ if ((theta < ELEVEN_DEGREES) && (theta > -ELEVEN_DEGREES))
+ return FALSE;
+
+ dist = 0.5 * width / sin (0.5 * theta);
+ if (dist < 0.0)
+ dist = -dist;
+
+ theta3 = (theta1 + theta2) / 2.0;
+ if (sin (theta3 - (theta1 + G_PI)) < 0.0)
+ theta3 += G_PI;
+
+ dx = dist * cos (theta3);
+ dy = dist * sin (theta3);
+
+ *mx1 = x2 + dx;
+ *mx2 = x2 - dx;
+ *my1 = y2 + dy;
+ *my2 = y2 - dy;
+
+ return TRUE;
+}
+
+/**
+ * gnome_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
+gnome_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 < GNOME_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;
+ }
+ }
+}
+
+/**
+ * gnome_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
+gnome_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;
+ 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;
+}
+
+/* Here are some helper functions for aa rendering: */
+
+/**
+ * gnome_canvas_render_svp:
+ * @buf: the canvas buffer to render over
+ * @svp: the vector path to render
+ * @rgba: the rgba color to render
+ *
+ * Render the svp over the buf.
+ **/
+void
+gnome_canvas_render_svp (GnomeCanvasBuf *buf, ArtSVP *svp, guint32 rgba)
+{
+ guint32 fg_color, bg_color;
+ int alpha;
+
+ if (buf->is_bg) {
+ bg_color = buf->bg_color;
+ alpha = rgba & 0xff;
+ if (alpha == 0xff)
+ fg_color = rgba >> 8;
+ else {
+ /* composite over background color */
+ int bg_r, bg_g, bg_b;
+ int fg_r, fg_g, fg_b;
+ int tmp;
+
+ bg_r = (bg_color >> 16) & 0xff;
+ fg_r = (rgba >> 24) & 0xff;
+ tmp = (fg_r - bg_r) * alpha;
+ fg_r = bg_r + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+ bg_g = (bg_color >> 8) & 0xff;
+ fg_g = (rgba >> 16) & 0xff;
+ tmp = (fg_g - bg_g) * alpha;
+ fg_g = bg_g + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+ bg_b = bg_color & 0xff;
+ fg_b = (rgba >> 8) & 0xff;
+ tmp = (fg_b - bg_b) * alpha;
+ fg_b = bg_b + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+ fg_color = (fg_r << 16) | (fg_g << 8) | fg_b;
+ }
+ art_rgb_svp_aa (svp,
+ buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
+ fg_color, bg_color,
+ buf->buf, buf->buf_rowstride,
+ NULL);
+ buf->is_bg = 0;
+ buf->is_buf = 1;
+ } else {
+ art_rgb_svp_alpha (svp,
+ buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
+ rgba,
+ buf->buf, buf->buf_rowstride,
+ NULL);
+ }
+}
+
+/**
+ * gnome_canvas_update_svp:
+ * @canvas: the canvas containing the svp that needs updating.
+ * @p_svp: a pointer to the existing svp
+ * @new_svp: the new svp
+ *
+ * Sets the svp to the new value, requesting repaint on what's changed. This
+ * function takes responsibility for freeing new_svp.
+ **/
+void
+gnome_canvas_update_svp (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp)
+{
+ ArtSVP *old_svp;
+ ArtSVP *diff G_GNUC_UNUSED;
+ ArtUta *repaint_uta;
+
+ old_svp = *p_svp;
+
+ if (old_svp != NULL) {
+ ArtDRect bb;
+ art_drect_svp (&bb, old_svp);
+ if ((bb.x1 - bb.x0) * (bb.y1 - bb.y0) > (64 * 64)) {
+ repaint_uta = art_uta_from_svp (old_svp);
+ gnome_canvas_request_redraw_uta (canvas, repaint_uta);
+ } else {
+ ArtIRect ib;
+ art_drect_to_irect (&ib, &bb);
+ gnome_canvas_request_redraw (canvas, ib.x0, ib.y0, ib.x1, ib.y1);
+ }
+ art_svp_free (old_svp);
+ }
+
+ if (new_svp != NULL) {
+ ArtDRect bb;
+ art_drect_svp (&bb, new_svp);
+ if ((bb.x1 - bb.x0) * (bb.y1 - bb.y0) > (64 * 64)) {
+ repaint_uta = art_uta_from_svp (new_svp);
+ gnome_canvas_request_redraw_uta (canvas, repaint_uta);
+ } else {
+ ArtIRect ib;
+ art_drect_to_irect (&ib, &bb);
+ gnome_canvas_request_redraw (canvas, ib.x0, ib.y0, ib.x1, ib.y1);
+ }
+ }
+
+ *p_svp = new_svp;
+}
+
+/**
+ * gnome_canvas_update_svp_clip:
+ * @canvas: the canvas containing the svp that needs updating.
+ * @p_svp: a pointer to the existing svp
+ * @new_svp: the new svp
+ * @clip_svp: a clip path, if non-null
+ *
+ * Sets the svp to the new value, clipping if necessary, and requesting repaint
+ * on what's changed. This function takes responsibility for freeing new_svp.
+ **/
+void
+gnome_canvas_update_svp_clip (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp, ArtSVP *clip_svp)
+{
+ ArtSVP *clipped_svp;
+
+ if (clip_svp != NULL) {
+ clipped_svp = art_svp_intersect (new_svp, clip_svp);
+ art_svp_free (new_svp);
+ } else {
+ clipped_svp = new_svp;
+ }
+ gnome_canvas_update_svp (canvas, p_svp, clipped_svp);
+}
+
+/**
+ * gnome_canvas_item_reset_bounds:
+ * @item: A canvas item
+ *
+ * Resets the bounding box of a canvas item to an empty rectangle.
+ **/
+void
+gnome_canvas_item_reset_bounds (GnomeCanvasItem *item)
+{
+ item->x1 = 0.0;
+ item->y1 = 0.0;
+ item->x2 = 0.0;
+ item->y2 = 0.0;
+}
+
+/**
+ * gnome_canvas_item_update_svp:
+ * @item: the canvas item containing the svp that needs updating.
+ * @p_svp: a pointer to the existing svp
+ * @new_svp: the new svp
+ *
+ * Sets the svp to the new value, requesting repaint on what's changed. This
+ * function takes responsibility for freeing new_svp. This routine also adds the
+ * svp's bbox to the item's.
+ **/
+void
+gnome_canvas_item_update_svp (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp)
+{
+ ArtDRect bbox;
+
+ gnome_canvas_update_svp (item->canvas, p_svp, new_svp);
+ if (new_svp) {
+ bbox.x0 = item->x1;
+ bbox.y0 = item->y1;
+ bbox.x1 = item->x2;
+ bbox.y1 = item->y2;
+ art_drect_svp_union (&bbox, new_svp);
+ item->x1 = bbox.x0;
+ item->y1 = bbox.y0;
+ item->x2 = bbox.x1;
+ item->y2 = bbox.y1;
+ }
+}
+
+/**
+ * gnome_canvas_item_update_svp_clip:
+ * @item: the canvas item containing the svp that needs updating.
+ * @p_svp: a pointer to the existing svp
+ * @new_svp: the new svp
+ * @clip_svp: a clip path, if non-null
+ *
+ * Sets the svp to the new value, clipping if necessary, and requesting repaint
+ * on what's changed. This function takes responsibility for freeing new_svp.
+ **/
+void
+gnome_canvas_item_update_svp_clip (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp,
+ ArtSVP *clip_svp)
+{
+ ArtSVP *clipped_svp;
+
+ if (clip_svp != NULL) {
+ clipped_svp = art_svp_intersect (new_svp, clip_svp);
+ art_svp_free (new_svp);
+ } else {
+ clipped_svp = new_svp;
+ }
+
+ gnome_canvas_item_update_svp (item, p_svp, clipped_svp);
+}
+
+/**
+ * gnome_canvas_item_request_redraw_svp
+ * @item: the item containing the svp
+ * @svp: the svp that needs to be redrawn
+ *
+ * Request redraw of the svp if in aa mode, or the entire item in in xlib mode.
+ **/
+void
+gnome_canvas_item_request_redraw_svp (GnomeCanvasItem *item, const ArtSVP *svp)
+{
+ GnomeCanvas *canvas;
+ ArtUta *uta;
+
+ canvas = item->canvas;
+ if (canvas->aa) {
+ if (svp != NULL) {
+ uta = art_uta_from_svp (svp);
+ gnome_canvas_request_redraw_uta (canvas, uta);
+ }
+ } else {
+ gnome_canvas_request_redraw (canvas, item->x1, item->y1, item->x2, item->y2);
+ }
+}
+
+/**
+ * gnome_canvas_update_bbox:
+ * @item: the canvas item 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
+gnome_canvas_update_bbox (GnomeCanvasItem *item, int x1, int y1, int x2, int y2)
+{
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+ item->x1 = x1;
+ item->y1 = y1;
+ item->x2 = x2;
+ item->y2 = y2;
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+}
+
+/**
+ * gnome_canvas_buf_ensure_buf:
+ * @buf: the buf that needs to be represened in RGB format
+ *
+ * Ensure that the buffer is in RGB format, suitable for compositing.
+ **/
+void
+gnome_canvas_buf_ensure_buf (GnomeCanvasBuf *buf)
+{
+ guchar *bufptr;
+ int y;
+
+ if (!buf->is_buf) {
+ bufptr = buf->buf;
+ for (y = buf->rect.y0; y < buf->rect.y1; y++) {
+ art_rgb_fill_run (bufptr,
+ buf->bg_color >> 16,
+ (buf->bg_color >> 8) & 0xff,
+ buf->bg_color & 0xff,
+ buf->rect.x1 - buf->rect.x0);
+ bufptr += buf->buf_rowstride;
+ }
+ buf->is_buf = 1;
+ }
+}
+
+/**
+ * gnome_canvas_join_gdk_to_art
+ * @gdk_join: a join type, represented in GDK format
+ *
+ * Convert from GDK line join specifier to libart.
+ *
+ * Return value: The line join specifier in libart format.
+ **/
+ArtPathStrokeJoinType
+gnome_canvas_join_gdk_to_art (GdkJoinStyle gdk_join)
+{
+ switch (gdk_join) {
+ case GDK_JOIN_MITER:
+ return ART_PATH_STROKE_JOIN_MITER;
+
+ case GDK_JOIN_ROUND:
+ return ART_PATH_STROKE_JOIN_ROUND;
+
+ case GDK_JOIN_BEVEL:
+ return ART_PATH_STROKE_JOIN_BEVEL;
+
+ default:
+ g_assert_not_reached ();
+ return ART_PATH_STROKE_JOIN_MITER; /* shut up the compiler */
+ }
+}
+
+/**
+ * gnome_canvas_cap_gdk_to_art
+ * @gdk_cap: a cap type, represented in GDK format
+ *
+ * Convert from GDK line cap specifier to libart.
+ *
+ * Return value: The line cap specifier in libart format.
+ **/
+ArtPathStrokeCapType
+gnome_canvas_cap_gdk_to_art (GdkCapStyle gdk_cap)
+{
+ switch (gdk_cap) {
+ case GDK_CAP_BUTT:
+ case GDK_CAP_NOT_LAST:
+ return ART_PATH_STROKE_CAP_BUTT;
+
+ case GDK_CAP_ROUND:
+ return ART_PATH_STROKE_CAP_ROUND;
+
+ case GDK_CAP_PROJECTING:
+ return ART_PATH_STROKE_CAP_SQUARE;
+
+ default:
+ g_assert_not_reached ();
+ return ART_PATH_STROKE_CAP_BUTT; /* shut up the compiler */
+ }
+}
diff --git a/libgnomecanvas/gnome-canvas-util.h b/libgnomecanvas/gnome-canvas-util.h
new file mode 100644
index 0000000000..cfd32c336e
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-util.h
@@ -0,0 +1,155 @@
+/*
+ * 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 GnomeCanvas widget
+ *
+ * GnomeCanvas 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 GNOME_CANVAS_UTIL_H
+#define GNOME_CANVAS_UTIL_H
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_svp_vpath_stroke.h>
+
+
+G_BEGIN_DECLS
+
+typedef struct _GnomeCanvasPoints GnomeCanvasPoints;
+
+/* 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.
+ */
+struct _GnomeCanvasPoints {
+ double *coords;
+ int num_points;
+ int ref_count;
+};
+
+
+/* Allocate a new GnomeCanvasPoints structure with enough space for the specified number of points */
+GnomeCanvasPoints *gnome_canvas_points_new (int num_points);
+
+/* Increate ref count */
+GnomeCanvasPoints *gnome_canvas_points_ref (GnomeCanvasPoints *points);
+#define gnome_canvas_points_unref gnome_canvas_points_free
+
+/* Decrease ref count and free structure if it has reached zero */
+void gnome_canvas_points_free (GnomeCanvasPoints *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 gnome_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 gnome_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 gnome_canvas_polygon_to_point (double *poly, int num_points, double x, double y);
+
+
+/* Render the svp over the buf. */
+void gnome_canvas_render_svp (GnomeCanvasBuf *buf, ArtSVP *svp, guint32 rgba);
+
+/* Sets the svp to the new value, requesting repaint on what's changed. This function takes responsibility for
+ * freeing new_svp.
+ */
+void gnome_canvas_update_svp (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp);
+
+/* Sets the svp to the new value, clipping if necessary, and requesting repaint
+ * on what's changed. This function takes responsibility for freeing new_svp.
+ */
+void gnome_canvas_update_svp_clip (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp,
+ ArtSVP *clip_svp);
+
+/* Sets the svp to the new value, requesting repaint on what's changed. This
+ * function takes responsibility for freeing new_svp. This routine also adds the
+ * svp's bbox to the item's.
+ */
+void gnome_canvas_item_reset_bounds (GnomeCanvasItem *item);
+
+/* Sets the svp to the new value, requesting repaint on what's changed. This function takes responsibility for
+ * freeing new_svp. This routine also adds the svp's bbox to the item's.
+ */
+void gnome_canvas_item_update_svp (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp);
+
+/* Sets the svp to the new value, clipping if necessary, and requesting repaint
+ * on what's changed. This function takes responsibility for freeing new_svp.
+ */
+void gnome_canvas_item_update_svp_clip (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp,
+ ArtSVP *clip_svp);
+
+/* Request redraw of the svp if in aa mode, or the entire item in in xlib
+ * mode.
+ */
+void gnome_canvas_item_request_redraw_svp (GnomeCanvasItem *item, const ArtSVP *svp);
+
+/* Sets the bbox to the new value, requesting full repaint. */
+void gnome_canvas_update_bbox (GnomeCanvasItem *item, int x1, int y1, int x2, int y2);
+
+/* Ensure that the buffer is in RGB format, suitable for compositing. */
+void gnome_canvas_buf_ensure_buf (GnomeCanvasBuf *buf);
+
+/* Convert from GDK line join specifier to libart. */
+ArtPathStrokeJoinType gnome_canvas_join_gdk_to_art (GdkJoinStyle gdk_join);
+
+/* Convert from GDK line cap specifier to libart. */
+ArtPathStrokeCapType gnome_canvas_cap_gdk_to_art (GdkCapStyle gdk_cap);
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-widget.c b/libgnomecanvas/gnome-canvas-widget.c
new file mode 100644
index 0000000000..d94942c8a5
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-widget.c
@@ -0,0 +1,599 @@
+/*
+ * 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 GnomeCanvas widget
+ *
+ * GnomeCanvas 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 <gtk/gtksignal.h>
+#include "gnome-canvas-widget.h"
+
+enum {
+ PROP_0,
+ PROP_WIDGET,
+ PROP_X,
+ PROP_Y,
+ PROP_WIDTH,
+ PROP_HEIGHT,
+ PROP_ANCHOR,
+ PROP_SIZE_PIXELS
+};
+
+
+static void gnome_canvas_widget_class_init (GnomeCanvasWidgetClass *class);
+static void gnome_canvas_widget_init (GnomeCanvasWidget *witem);
+static void gnome_canvas_widget_destroy (GtkObject *object);
+static void gnome_canvas_widget_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_widget_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_widget_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+static double gnome_canvas_widget_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy, GnomeCanvasItem **actual_item);
+static void gnome_canvas_widget_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+static void gnome_canvas_widget_render (GnomeCanvasItem *item,
+ GnomeCanvasBuf *buf);
+static void gnome_canvas_widget_draw (GnomeCanvasItem *item,
+ GdkDrawable *drawable,
+ int x, int y,
+ int width, int height);
+
+static GnomeCanvasItemClass *parent_class;
+
+
+GType
+gnome_canvas_widget_get_type (void)
+{
+ static GType widget_type;
+
+ if (!widget_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasWidgetClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_widget_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasWidget),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_widget_init,
+ NULL /* value_table */
+ };
+
+ widget_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasWidget",
+ &object_info, 0);
+ }
+
+ return widget_type;
+}
+
+static void
+gnome_canvas_widget_class_init (GnomeCanvasWidgetClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = gnome_canvas_widget_set_property;
+ gobject_class->get_property = gnome_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_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X,
+ g_param_spec_double ("x", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y,
+ g_param_spec_double ("y", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDTH,
+ g_param_spec_double ("width", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_HEIGHT,
+ g_param_spec_double ("height", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.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_NW,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SIZE_PIXELS,
+ g_param_spec_boolean ("size_pixels", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ object_class->destroy = gnome_canvas_widget_destroy;
+
+ item_class->update = gnome_canvas_widget_update;
+ item_class->point = gnome_canvas_widget_point;
+ item_class->bounds = gnome_canvas_widget_bounds;
+ item_class->render = gnome_canvas_widget_render;
+ item_class->draw = gnome_canvas_widget_draw;
+}
+
+static void
+gnome_canvas_widget_init (GnomeCanvasWidget *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
+gnome_canvas_widget_destroy (GtkObject *object)
+{
+ GnomeCanvasWidget *witem;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_WIDGET (object));
+
+ witem = GNOME_CANVAS_WIDGET (object);
+
+ if (witem->widget && !witem->in_destroy) {
+ g_signal_handler_disconnect (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 (GnomeCanvasWidget *witem)
+{
+ GnomeCanvasItem *item;
+ double wx, wy;
+
+ item = GNOME_CANVAS_ITEM (witem);
+
+ /* Get world coordinates */
+
+ wx = witem->x;
+ wy = witem->y;
+ gnome_canvas_item_i2w (item, &wx, &wy);
+
+ /* Get canvas pixel coordinates */
+
+ gnome_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 + item->canvas->zoom_xofs,
+ witem->cy + item->canvas->zoom_yofs);
+}
+
+static void
+do_destroy (GtkObject *object, gpointer data)
+{
+ GnomeCanvasWidget *witem;
+
+ witem = data;
+
+ witem->in_destroy = TRUE;
+
+ gtk_object_destroy (data);
+}
+
+static void
+gnome_canvas_widget_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasWidget *witem;
+ GObject *obj;
+ int update;
+ int calc_bounds;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_WIDGET (object));
+
+ item = GNOME_CANVAS_ITEM (object);
+ witem = GNOME_CANVAS_WIDGET (object);
+
+ update = FALSE;
+ calc_bounds = FALSE;
+
+ switch (param_id) {
+ case PROP_WIDGET:
+ if (witem->widget) {
+ g_signal_handler_disconnect (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 (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 != 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)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (item)->update) (item, NULL, NULL, 0);
+
+ if (calc_bounds)
+ recalc_bounds (witem);
+}
+
+static void
+gnome_canvas_widget_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasWidget *witem;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_WIDGET (object));
+
+ witem = GNOME_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
+gnome_canvas_widget_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasWidget *witem;
+
+ witem = GNOME_CANVAS_WIDGET (item);
+
+ if (parent_class->update)
+ (* parent_class->update) (item, affine, clip_path, flags);
+
+ if (witem->widget) {
+ 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);
+ }
+
+ gtk_widget_set_size_request (witem->widget, witem->cwidth, witem->cheight);
+ } else {
+ witem->cwidth = 0.0;
+ witem->cheight = 0.0;
+ }
+
+ recalc_bounds (witem);
+}
+
+static void
+gnome_canvas_widget_render (GnomeCanvasItem *item,
+ GnomeCanvasBuf *buf)
+{
+#if 0
+ GnomeCanvasWidget *witem;
+
+ witem = GNOME_CANVAS_WIDGET (item);
+
+ if (witem->widget)
+ gtk_widget_queue_draw (witem->widget);
+#endif
+
+}
+
+static void
+gnome_canvas_widget_draw (GnomeCanvasItem *item,
+ GdkDrawable *drawable,
+ int x, int y,
+ int width, int height)
+{
+#if 0
+ GnomeCanvasWidget *witem;
+
+ witem = GNOME_CANVAS_WIDGET (item);
+
+ if (witem->widget)
+ gtk_widget_queue_draw (witem->widget);
+#endif
+}
+
+static double
+gnome_canvas_widget_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy, GnomeCanvasItem **actual_item)
+{
+ GnomeCanvasWidget *witem;
+ double x1, y1, x2, y2;
+ double dx, dy;
+
+ witem = GNOME_CANVAS_WIDGET (item);
+
+ *actual_item = item;
+
+ gnome_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
+gnome_canvas_widget_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ GnomeCanvasWidget *witem;
+
+ witem = GNOME_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/libgnomecanvas/gnome-canvas-widget.h b/libgnomecanvas/gnome-canvas-widget.h
new file mode 100644
index 0000000000..f7517c3832
--- /dev/null
+++ b/libgnomecanvas/gnome-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 GnomeCanvas widget
+ *
+ * GnomeCanvas 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 GNOME_CANVAS_WIDGET_H
+#define GNOME_CANVAS_WIDGET_H
+
+
+#include <libgnomecanvas/gnome-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 GNOME_TYPE_CANVAS_WIDGET (gnome_canvas_widget_get_type ())
+#define GNOME_CANVAS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_WIDGET, GnomeCanvasWidget))
+#define GNOME_CANVAS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_WIDGET, GnomeCanvasWidgetClass))
+#define GNOME_IS_CANVAS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_WIDGET))
+#define GNOME_IS_CANVAS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_WIDGET))
+#define GNOME_CANVAS_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_WIDGET, GnomeCanvasWidgetClass))
+
+
+typedef struct _GnomeCanvasWidget GnomeCanvasWidget;
+typedef struct _GnomeCanvasWidgetClass GnomeCanvasWidgetClass;
+
+struct _GnomeCanvasWidget {
+ GnomeCanvasItem 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 _GnomeCanvasWidgetClass {
+ GnomeCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_widget_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas.c b/libgnomecanvas/gnome-canvas.c
new file mode 100644
index 0000000000..5345a59200
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas.c
@@ -0,0 +1,4090 @@
+/* -*- 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@
+ */
+/*
+ * GnomeCanvas widget - Tk-like canvas widget for Gnome
+ *
+ * GnomeCanvas 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 GnomeCanvasImage sizes are in units or pixels (scale or don't scale).
+ *
+ * - Implement a flag for gnome_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 *gnome_canvas_gimme_all_items_contained_in_this_area (GnomeCanvas *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 GnomeCanvasEllipse).
+ *
+ * - Sane font handling API.
+ *
+ * - Get_arg methods for items:
+ * - How to fetch the outline width and know whether it is in pixels or units?
+ */
+
+/*
+ * Raph's TODO list for the antialiased canvas integration:
+ *
+ * - ::point() method for text item not accurate when affine transformed.
+ *
+ * - Clip rectangle not implemented in aa renderer for text item.
+ *
+ * - Clip paths only partially implemented.
+ *
+ * - Add more image loading techniques to work around imlib deficiencies.
+ */
+
+#include <config.h>
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <gdk/gdkprivate.h>
+#include <gtk/gtk.h>
+#include "gailcanvas.h"
+#include "gnome-canvas.h"
+#include "gnome-canvas-i18n.h"
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_rect_uta.h>
+#include <libart_lgpl/art_uta_rect.h>
+#include <libart_lgpl/art_uta_ops.h>
+
+#include "gnome-canvas-marshal.h"
+#include "gnome-canvas-marshal.c"
+
+
+/* We must run our idle update handler *before* GDK wants to redraw. */
+#define CANVAS_IDLE_PRIORITY (GDK_PRIORITY_REDRAW - 5)
+
+
+static void gnome_canvas_request_update (GnomeCanvas *canvas);
+static void group_add (GnomeCanvasGroup *group,
+ GnomeCanvasItem *item);
+static void group_remove (GnomeCanvasGroup *group,
+ GnomeCanvasItem *item);
+static void add_idle (GnomeCanvas *canvas);
+
+
+/*** GnomeCanvasItem ***/
+
+/* Some convenience stuff */
+#define GCI_UPDATE_MASK (GNOME_CANVAS_UPDATE_REQUESTED | GNOME_CANVAS_UPDATE_AFFINE | GNOME_CANVAS_UPDATE_CLIP | GNOME_CANVAS_UPDATE_VISIBILITY)
+#define GCI_EPSILON 1e-18
+#define GCI_PRINT_MATRIX(s,a) g_print ("%s %g %g %g %g %g %g\n", s, (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5])
+
+enum {
+ ITEM_PROP_0,
+ ITEM_PROP_PARENT
+};
+
+enum {
+ ITEM_EVENT,
+ ITEM_LAST_SIGNAL
+};
+
+static void gnome_canvas_item_class_init (GnomeCanvasItemClass *class);
+static void gnome_canvas_item_init (GnomeCanvasItem *item);
+static int emit_event (GnomeCanvas *canvas, GdkEvent *event);
+
+static guint item_signals[ITEM_LAST_SIGNAL];
+
+static GtkObjectClass *item_parent_class;
+
+
+/**
+ * gnome_canvas_item_get_type:
+ *
+ * Registers the &GnomeCanvasItem class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &GnomeCanvasItem class.
+ **/
+GType
+gnome_canvas_item_get_type (void)
+{
+ static GType canvas_item_type;
+
+ if (!canvas_item_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasItemClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_item_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasItem),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_item_init,
+ NULL /* value_table */
+ };
+
+ canvas_item_type = g_type_register_static (GTK_TYPE_OBJECT, "GnomeCanvasItem",
+ &object_info, 0);
+ }
+
+ return canvas_item_type;
+}
+
+/* Object initialization function for GnomeCanvasItem */
+static void
+gnome_canvas_item_init (GnomeCanvasItem *item)
+{
+ item->object.flags |= GNOME_CANVAS_ITEM_VISIBLE;
+}
+
+/**
+ * gnome_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
+ * gnome_canvas_rect_get_type(). The list of object arguments/value pairs is
+ * used to configure the item. If you need to pass construct time parameters, you
+ * should use g_object_new() to pass the parameters and
+ * gnome_canvas_item_construct() to set up the canvas item.
+ *
+ * Return value: The newly-created item.
+ **/
+GnomeCanvasItem *
+gnome_canvas_item_new (GnomeCanvasGroup *parent, GType type, const gchar *first_arg_name, ...)
+{
+ GnomeCanvasItem *item;
+ va_list args;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS_GROUP (parent), NULL);
+ g_return_val_if_fail (g_type_is_a (type, gnome_canvas_item_get_type ()), NULL);
+
+ item = GNOME_CANVAS_ITEM (g_object_new (type, NULL));
+
+ va_start (args, first_arg_name);
+ gnome_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 (GnomeCanvasItem *item)
+{
+ group_add (GNOME_CANVAS_GROUP (item->parent), item);
+
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
+ item->canvas->need_repick = TRUE;
+}
+
+/* Set_property handler for canvas items */
+static void
+gnome_canvas_item_set_property (GObject *gobject, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (gobject));
+
+ item = GNOME_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 = GNOME_CANVAS_ITEM (g_value_get_object (value));
+ item->canvas = item->parent->canvas;
+ item_post_create_setup (item);
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ break;
+ }
+}
+
+/* Get_property handler for canvas items */
+static void
+gnome_canvas_item_get_property (GObject *gobject, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (gobject));
+
+ item = GNOME_CANVAS_ITEM (gobject);
+
+ switch (param_id) {
+ case ITEM_PROP_PARENT:
+ g_value_set_object (value, item->parent);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gnome_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
+gnome_canvas_item_construct (GnomeCanvasItem *item, GnomeCanvasGroup *parent,
+ const gchar *first_arg_name, va_list args)
+{
+ g_return_if_fail (GNOME_IS_CANVAS_GROUP (parent));
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ item->parent = GNOME_CANVAS_ITEM (parent);
+ item->canvas = item->parent->canvas;
+
+ g_object_set_valist (G_OBJECT (item), first_arg_name, args);
+
+ item_post_create_setup (item);
+}
+
+
+/* If the item is visible, requests a redraw of it. */
+static void
+redraw_if_visible (GnomeCanvasItem *item)
+{
+ if (item->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
+}
+
+/* Standard object dispose function for canvas items */
+static void
+gnome_canvas_item_dispose (GObject *object)
+{
+ GnomeCanvasItem *item;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (object));
+
+ item = GNOME_CANVAS_ITEM (object);
+
+ if (item->canvas)
+ redraw_if_visible (item);
+
+ /* Make the canvas forget about us */
+
+ if (item->canvas && item == item->canvas->current_item) {
+ item->canvas->current_item = NULL;
+ item->canvas->need_repick = TRUE;
+ }
+
+ if (item->canvas && item == item->canvas->new_current_item) {
+ item->canvas->new_current_item = NULL;
+ item->canvas->need_repick = TRUE;
+ }
+
+ if (item->canvas && item == item->canvas->grabbed_item) {
+ item->canvas->grabbed_item = NULL;
+ gdk_pointer_ungrab (GDK_CURRENT_TIME);
+ }
+
+ if (item->canvas && item == item->canvas->focused_item)
+ item->canvas->focused_item = NULL;
+
+ /* Normal destroy stuff */
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_MAPPED)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_REALIZED)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
+
+ if (item->parent)
+ group_remove (GNOME_CANVAS_GROUP (item->parent), item);
+
+ g_free (item->xform);
+ item->xform = NULL;
+
+ G_OBJECT_CLASS (item_parent_class)->dispose (object);
+ /* items should remove any reference to item->canvas after the
+ first ::destroy */
+ item->canvas = NULL;
+}
+
+/* Realize handler for canvas items */
+static void
+gnome_canvas_item_realize (GnomeCanvasItem *item)
+{
+ GTK_OBJECT_SET_FLAGS (item, GNOME_CANVAS_ITEM_REALIZED);
+
+ gnome_canvas_item_request_update (item);
+}
+
+/* Unrealize handler for canvas items */
+static void
+gnome_canvas_item_unrealize (GnomeCanvasItem *item)
+{
+ GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_REALIZED);
+}
+
+/* Map handler for canvas items */
+static void
+gnome_canvas_item_map (GnomeCanvasItem *item)
+{
+ GTK_OBJECT_SET_FLAGS (item, GNOME_CANVAS_ITEM_MAPPED);
+}
+
+/* Unmap handler for canvas items */
+static void
+gnome_canvas_item_unmap (GnomeCanvasItem *item)
+{
+ GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_MAPPED);
+}
+
+/* Update handler for canvas items */
+static void
+gnome_canvas_item_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_NEED_UPDATE);
+ GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_NEED_AFFINE);
+ GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_NEED_CLIP);
+ GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_NEED_VIS);
+}
+
+#define noHACKISH_AFFINE
+
+/*
+ * 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
+gnome_canvas_item_invoke_update (GnomeCanvasItem *item, double *p2cpx, ArtSVP *clip_path, int flags)
+{
+ int child_flags;
+ gdouble i2cpx[6];
+
+#ifdef HACKISH_AFFINE
+ double i2w[6], w2c[6], i2c[6];
+#endif
+
+ child_flags = flags;
+ if (!(item->object.flags & GNOME_CANVAS_ITEM_VISIBLE))
+ child_flags &= ~GNOME_CANVAS_UPDATE_IS_VISIBLE;
+
+ /* Calculate actual item transformation matrix */
+
+ if (item->xform) {
+ if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
+ /* Item has full affine */
+ art_affine_multiply (i2cpx, item->xform, p2cpx);
+ } else {
+ /* Item has only translation */
+ memcpy (i2cpx, p2cpx, 4 * sizeof (gdouble));
+ i2cpx[4] = item->xform[0] * p2cpx[0] + item->xform[1] * p2cpx[2] + p2cpx[4];
+ i2cpx[5] = item->xform[0] * p2cpx[1] + item->xform[1] * p2cpx[3] + p2cpx[5];
+ }
+ } else {
+ /* Item has no matrix (i.e. identity) */
+ memcpy (i2cpx, p2cpx, 6 * sizeof (gdouble));
+ }
+
+#ifdef HACKISH_AFFINE
+ gnome_canvas_item_i2w_affine (item, i2w);
+ gnome_canvas_w2c_affine (item->canvas, w2c);
+ art_affine_multiply (i2c, i2w, w2c);
+ /* invariant (doesn't hold now): child_affine == i2c */
+ child_affine = i2c;
+#endif
+
+ /* apply object flags to child flags */
+
+ child_flags &= ~GNOME_CANVAS_UPDATE_REQUESTED;
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_NEED_UPDATE)
+ child_flags |= GNOME_CANVAS_UPDATE_REQUESTED;
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)
+ child_flags |= GNOME_CANVAS_UPDATE_AFFINE;
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_NEED_CLIP)
+ child_flags |= GNOME_CANVAS_UPDATE_CLIP;
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_NEED_VIS)
+ child_flags |= GNOME_CANVAS_UPDATE_VISIBILITY;
+
+ if (child_flags & GCI_UPDATE_MASK) {
+ if (GNOME_CANVAS_ITEM_GET_CLASS (item)->update)
+ GNOME_CANVAS_ITEM_GET_CLASS (item)->update (item, i2cpx, clip_path, child_flags);
+ }
+}
+
+/*
+ * This routine invokes the point method of the item.
+ * The arguments x, y should be in the parent item local coordinates.
+ *
+ * This is potentially evil, as we are relying on matrix inversion (Lauris)
+ */
+
+static double
+gnome_canvas_item_invoke_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
+{
+ /* Calculate x & y in item local coordinates */
+
+ if (item->xform) {
+ if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
+ gdouble p2i[6], t;
+ /* Item has full affine */
+ art_affine_invert (p2i, item->xform);
+ t = x * p2i[0] + y * p2i[2] + p2i[4];
+ y = x * p2i[1] + y * p2i[3] + p2i[5];
+ x = t;
+ } else {
+ /* Item has only translation */
+ x -= item->xform[0];
+ y -= item->xform[1];
+ }
+ }
+
+#ifdef HACKISH_AFFINE
+ double i2w[6], w2c[6], i2c[6], c2i[6];
+ ArtPoint c, i;
+#endif
+
+#ifdef HACKISH_AFFINE
+ gnome_canvas_item_i2w_affine (item, i2w);
+ gnome_canvas_w2c_affine (item->canvas, w2c);
+ art_affine_multiply (i2c, i2w, w2c);
+ art_affine_invert (c2i, i2c);
+ c.x = cx;
+ c.y = cy;
+ art_affine_point (&i, &c, c2i);
+ x = i.x;
+ y = i.y;
+#endif
+
+ if (GNOME_CANVAS_ITEM_GET_CLASS (item)->point)
+ return GNOME_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy, actual_item);
+
+ return 1e18;
+}
+
+/**
+ * gnome_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
+gnome_canvas_item_set (GnomeCanvasItem *item, const gchar *first_arg_name, ...)
+{
+ va_list args;
+
+ va_start (args, first_arg_name);
+ gnome_canvas_item_set_valist (item, first_arg_name, args);
+ va_end (args);
+}
+
+
+/**
+ * gnome_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
+gnome_canvas_item_set_valist (GnomeCanvasItem *item, const gchar *first_arg_name, va_list args)
+{
+ g_return_if_fail (GNOME_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 */
+ redraw_if_visible (item);
+#endif
+
+ item->canvas->need_repick = TRUE;
+}
+
+
+/**
+ * gnome_canvas_item_affine_relative:
+ * @item: A canvas item.
+ * @affine: An affine transformation matrix.
+ *
+ * Combines the specified affine transformation matrix with the item's current
+ * transformation. NULL affine is not allowed.
+ **/
+#define GCIAR_EPSILON 1e-6
+void
+gnome_canvas_item_affine_relative (GnomeCanvasItem *item, const double affine[6])
+{
+ gdouble i2p[6];
+
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+ g_return_if_fail (affine != NULL);
+
+ /* Calculate actual item transformation matrix */
+
+ if (item->xform) {
+ if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
+ /* Item has full affine */
+ art_affine_multiply (i2p, affine, item->xform);
+ } else {
+ /* Item has only translation */
+ memcpy (i2p, affine, 6 * sizeof (gdouble));
+ i2p[4] += item->xform[0];
+ i2p[5] += item->xform[1];
+ }
+ } else {
+ /* Item has no matrix (i.e. identity) */
+ memcpy (i2p, affine, 6 * sizeof (gdouble));
+ }
+
+ gnome_canvas_item_affine_absolute (item, i2p);
+}
+
+/**
+ * gnome_canvas_item_affine_absolute:
+ * @item: A canvas item.
+ * @affine: An affine transformation matrix.
+ *
+ * Makes the item's affine transformation matrix be equal to the specified
+ * matrix. NULL affine is treated as identity.
+ **/
+void
+gnome_canvas_item_affine_absolute (GnomeCanvasItem *item, const double i2p[6])
+{
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ if (i2p &&
+ (fabs (i2p[0] - 1.0) < GCI_EPSILON) &&
+ (fabs (i2p[1] - 0.0) < GCI_EPSILON) &&
+ (fabs (i2p[2] - 0.0) < GCI_EPSILON) &&
+ (fabs (i2p[3] - 1.0) < GCI_EPSILON) &&
+ (fabs (i2p[4] - 0.0) < GCI_EPSILON) &&
+ (fabs (i2p[5] - 0.0) < GCI_EPSILON)) {
+ /* We are identity */
+ i2p = NULL;
+ }
+
+ if (i2p) {
+ if (item->xform && !(item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL)) {
+ /* We do not want to deal with translation-only affines */
+ g_free (item->xform);
+ item->xform = NULL;
+ }
+ if (!item->xform) item->xform = g_new (gdouble, 6);
+ memcpy (item->xform, i2p, 6 * sizeof (gdouble));
+ item->object.flags |= GNOME_CANVAS_ITEM_AFFINE_FULL;
+ } else {
+ if (item->xform) {
+ g_free (item->xform);
+ item->xform = NULL;
+ }
+ }
+
+ if (!(item->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
+ /* Request update */
+ item->object.flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
+ gnome_canvas_item_request_update (item);
+ }
+
+ item->canvas->need_repick = TRUE;
+}
+
+
+/**
+ * gnome_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
+gnome_canvas_item_move (GnomeCanvasItem *item, double dx, double dy)
+{
+ double translate[6];
+
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ art_affine_translate (translate, dx, dy);
+
+ gnome_canvas_item_affine_relative (item, translate);
+}
+
+/* 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)
+{
+ GnomeCanvasGroup *parent;
+ GList *old_before, *old_after;
+ GList *after;
+
+ parent = GNOME_CANVAS_GROUP (GNOME_CANVAS_ITEM (link->data)->parent);
+
+ if (before)
+ after = before->next;
+ else
+ after = parent->item_list;
+
+ if (before == link || after == link)
+ return FALSE;
+
+ /* Unlink */
+
+ old_before = link->prev;
+ old_after = link->next;
+
+ if (old_before)
+ old_before->next = old_after;
+ else
+ parent->item_list = old_after;
+
+ if (old_after)
+ old_after->prev = old_before;
+ else
+ parent->item_list_end = old_before;
+
+ /* Relink */
+
+ link->prev = before;
+ if (before)
+ before->next = link;
+ else
+ parent->item_list = link;
+
+ link->next = after;
+ if (after)
+ after->prev = link;
+ else
+ parent->item_list_end = link;
+
+ return TRUE;
+}
+
+
+/**
+ * gnome_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
+gnome_canvas_item_raise (GnomeCanvasItem *item, int positions)
+{
+ GList *link, *before;
+ GnomeCanvasGroup *parent;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+ g_return_if_fail (positions >= 0);
+
+ if (!item->parent || positions == 0)
+ return;
+
+ parent = GNOME_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_if_visible (item);
+ item->canvas->need_repick = TRUE;
+ }
+}
+
+
+/**
+ * gnome_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
+gnome_canvas_item_lower (GnomeCanvasItem *item, int positions)
+{
+ GList *link, *before;
+ GnomeCanvasGroup *parent;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+ g_return_if_fail (positions >= 1);
+
+ if (!item->parent || positions == 0)
+ return;
+
+ parent = GNOME_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_if_visible (item);
+ item->canvas->need_repick = TRUE;
+ }
+}
+
+
+/**
+ * gnome_canvas_item_raise_to_top:
+ * @item: A canvas item.
+ *
+ * Raises an item to the top of its parent's stack.
+ **/
+void
+gnome_canvas_item_raise_to_top (GnomeCanvasItem *item)
+{
+ GList *link;
+ GnomeCanvasGroup *parent;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ if (!item->parent)
+ return;
+
+ parent = GNOME_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_if_visible (item);
+ item->canvas->need_repick = TRUE;
+ }
+}
+
+
+/**
+ * gnome_canvas_item_lower_to_bottom:
+ * @item: A canvas item.
+ *
+ * Lowers an item to the bottom of its parent's stack.
+ **/
+void
+gnome_canvas_item_lower_to_bottom (GnomeCanvasItem *item)
+{
+ GList *link;
+ GnomeCanvasGroup *parent;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ if (!item->parent)
+ return;
+
+ parent = GNOME_CANVAS_GROUP (item->parent);
+ link = g_list_find (parent->item_list, item);
+ g_assert (link != NULL);
+
+ if (put_item_after (link, NULL)) {
+ redraw_if_visible (item);
+ item->canvas->need_repick = TRUE;
+ }
+}
+
+
+/**
+ * gnome_canvas_item_show:
+ * @item: A canvas item.
+ *
+ * Shows a canvas item. If the item was already shown, then no action is taken.
+ **/
+void
+gnome_canvas_item_show (GnomeCanvasItem *item)
+{
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ if (!(item->object.flags & GNOME_CANVAS_ITEM_VISIBLE)) {
+ item->object.flags |= GNOME_CANVAS_ITEM_VISIBLE;
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
+ item->canvas->need_repick = TRUE;
+ }
+}
+
+
+/**
+ * gnome_canvas_item_hide:
+ * @item: A canvas item.
+ *
+ * Hides a canvas item. If the item was already hidden, then no action is
+ * taken.
+ **/
+void
+gnome_canvas_item_hide (GnomeCanvasItem *item)
+{
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_VISIBLE) {
+ item->object.flags &= ~GNOME_CANVAS_ITEM_VISIBLE;
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
+ item->canvas->need_repick = TRUE;
+ }
+}
+
+
+/**
+ * gnome_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 gnome_canvas_item_hide(), then it
+ * returns %GDK_GRAB_NOT_VIEWABLE. Else, it returns the result of calling
+ * gdk_pointer_grab().
+ **/
+int
+gnome_canvas_item_grab (GnomeCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
+{
+ int retval;
+
+ g_return_val_if_fail (GNOME_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 & GNOME_CANVAS_ITEM_VISIBLE))
+ 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;
+}
+
+
+/**
+ * gnome_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
+gnome_canvas_item_ungrab (GnomeCanvasItem *item, guint32 etime)
+{
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ if (item->canvas->grabbed_item != item)
+ return;
+
+ item->canvas->grabbed_item = NULL;
+
+ gdk_pointer_ungrab (etime);
+}
+
+
+/**
+ * gnome_canvas_item_i2w_affine:
+ * @item: A canvas item
+ * @affine: An affine transformation matrix (return value).
+ *
+ * Gets the affine transform that converts from the item's coordinate system to
+ * world coordinates.
+ **/
+void
+gnome_canvas_item_i2w_affine (GnomeCanvasItem *item, double affine[6])
+{
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+ g_return_if_fail (affine != NULL);
+
+ art_affine_identity (affine);
+
+ while (item) {
+ if (item->xform != NULL) {
+ if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
+ art_affine_multiply (affine, affine, item->xform);
+ } else {
+ affine[4] += item->xform[0];
+ affine[5] += item->xform[1];
+ }
+ }
+
+ item = item->parent;
+ }
+}
+
+/**
+ * gnome_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
+gnome_canvas_item_w2i (GnomeCanvasItem *item, double *x, double *y)
+{
+ double affine[6], inv[6];
+ ArtPoint w, i;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+ g_return_if_fail (x != NULL);
+ g_return_if_fail (y != NULL);
+
+ gnome_canvas_item_i2w_affine (item, affine);
+ art_affine_invert (inv, affine);
+ w.x = *x;
+ w.y = *y;
+ art_affine_point (&i, &w, inv);
+ *x = i.x;
+ *y = i.y;
+}
+
+
+/**
+ * gnome_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
+gnome_canvas_item_i2w (GnomeCanvasItem *item, double *x, double *y)
+{
+ double affine[6];
+ ArtPoint w, i;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+ g_return_if_fail (x != NULL);
+ g_return_if_fail (y != NULL);
+
+ gnome_canvas_item_i2w_affine (item, affine);
+ i.x = *x;
+ i.y = *y;
+ art_affine_point (&w, &i, affine);
+ *x = w.x;
+ *y = w.y;
+}
+
+/**
+ * gnome_canvas_item_i2c_affine:
+ * @item: A canvas item.
+ * @affine: An affine transformation matrix (return value).
+ *
+ * Gets the affine transform that converts from item-relative coordinates to
+ * canvas pixel coordinates.
+ **/
+void
+gnome_canvas_item_i2c_affine (GnomeCanvasItem *item, double affine[6])
+{
+ double i2w[6], w2c[6];
+
+ gnome_canvas_item_i2w_affine (item, i2w);
+ gnome_canvas_w2c_affine (item->canvas, w2c);
+ art_affine_multiply (affine, i2w, w2c);
+}
+
+/* Returns whether the item is an inferior of or is equal to the parent. */
+static int
+is_descendant (GnomeCanvasItem *item, GnomeCanvasItem *parent)
+{
+ for (; item; item = item->parent)
+ if (item == parent)
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * gnome_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
+gnome_canvas_item_reparent (GnomeCanvasItem *item, GnomeCanvasGroup *new_group)
+{
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+ g_return_if_fail (GNOME_IS_CANVAS_GROUP (new_group));
+
+ /* Both items need to be in the same canvas */
+ g_return_if_fail (item->canvas == GNOME_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 (GNOME_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 */
+
+ redraw_if_visible (item);
+
+ group_remove (GNOME_CANVAS_GROUP (item->parent), item);
+ item->parent = GNOME_CANVAS_ITEM (new_group);
+ group_add (new_group, item);
+
+ /* Redraw and repick */
+
+ redraw_if_visible (item);
+ item->canvas->need_repick = TRUE;
+
+ g_object_unref (G_OBJECT (item));
+}
+
+/**
+ * gnome_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
+gnome_canvas_item_grab_focus (GnomeCanvasItem *item)
+{
+ GnomeCanvasItem *focused_item;
+ GdkEvent ev;
+
+ g_return_if_fail (GNOME_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);
+ }
+}
+
+
+/**
+ * gnome_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
+gnome_canvas_item_get_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ double tx1, ty1, tx2, ty2;
+ ArtPoint p1, p2, p3, p4;
+ ArtPoint q1, q2, q3, q4;
+ double min_x1, min_y1, min_x2, min_y2;
+ double max_x1, max_y1, max_x2, max_y2;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ tx1 = ty1 = tx2 = ty2 = 0.0;
+
+ /* Get the item's bounds in its coordinate system */
+
+ if (GNOME_CANVAS_ITEM_GET_CLASS (item)->bounds)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (item)->bounds) (item, &tx1, &ty1, &tx2, &ty2);
+
+ /* Make the bounds relative to the item's parent coordinate system */
+
+ if (item->xform && (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL)) {
+ p1.x = p2.x = tx1;
+ p1.y = p4.y = ty1;
+ p3.x = p4.x = tx2;
+ p2.y = p3.y = ty2;
+
+ art_affine_point (&q1, &p1, item->xform);
+ art_affine_point (&q2, &p2, item->xform);
+ art_affine_point (&q3, &p3, item->xform);
+ art_affine_point (&q4, &p4, item->xform);
+
+ if (q1.x < q2.x) {
+ min_x1 = q1.x;
+ max_x1 = q2.x;
+ } else {
+ min_x1 = q2.x;
+ max_x1 = q1.x;
+ }
+
+ if (q1.y < q2.y) {
+ min_y1 = q1.y;
+ max_y1 = q2.y;
+ } else {
+ min_y1 = q2.y;
+ max_y1 = q1.y;
+ }
+
+ if (q3.x < q4.x) {
+ min_x2 = q3.x;
+ max_x2 = q4.x;
+ } else {
+ min_x2 = q4.x;
+ max_x2 = q3.x;
+ }
+
+ if (q3.y < q4.y) {
+ min_y2 = q3.y;
+ max_y2 = q4.y;
+ } else {
+ min_y2 = q4.y;
+ max_y2 = q3.y;
+ }
+
+ tx1 = MIN (min_x1, min_x2);
+ ty1 = MIN (min_y1, min_y2);
+ tx2 = MAX (max_x1, max_x2);
+ ty2 = MAX (max_y1, max_y2);
+ } else if (item->xform) {
+ tx1 += item->xform[0];
+ ty1 += item->xform[1];
+ tx2 += item->xform[0];
+ ty2 += item->xform[1];
+ }
+
+ /* Return the values */
+
+ if (x1)
+ *x1 = tx1;
+
+ if (y1)
+ *y1 = ty1;
+
+ if (x2)
+ *x2 = tx2;
+
+ if (y2)
+ *y2 = ty2;
+}
+
+
+/**
+ * gnome_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
+gnome_canvas_item_request_update (GnomeCanvasItem *item)
+{
+ if (item->object.flags & GNOME_CANVAS_ITEM_NEED_UPDATE)
+ return;
+
+ item->object.flags |= GNOME_CANVAS_ITEM_NEED_UPDATE;
+
+ if (item->parent != NULL) {
+ /* Recurse up the tree */
+ gnome_canvas_item_request_update (item->parent);
+ } else {
+ /* Have reached the top of the tree, make sure the update call gets scheduled. */
+ gnome_canvas_request_update (item->canvas);
+ }
+}
+
+/*** GnomeCanvasGroup ***/
+
+
+enum {
+ GROUP_PROP_0,
+ GROUP_PROP_X,
+ GROUP_PROP_Y
+};
+
+
+static void gnome_canvas_group_class_init (GnomeCanvasGroupClass *class);
+static void gnome_canvas_group_init (GnomeCanvasGroup *group);
+static void gnome_canvas_group_set_property(GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_group_get_property(GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_group_destroy (GtkObject *object);
+
+static void gnome_canvas_group_update (GnomeCanvasItem *item, double *affine,
+ ArtSVP *clip_path, int flags);
+static void gnome_canvas_group_realize (GnomeCanvasItem *item);
+static void gnome_canvas_group_unrealize (GnomeCanvasItem *item);
+static void gnome_canvas_group_map (GnomeCanvasItem *item);
+static void gnome_canvas_group_unmap (GnomeCanvasItem *item);
+static void gnome_canvas_group_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height);
+static double gnome_canvas_group_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy,
+ GnomeCanvasItem **actual_item);
+static void gnome_canvas_group_bounds (GnomeCanvasItem *item, double *x1, double *y1,
+ double *x2, double *y2);
+static void gnome_canvas_group_render (GnomeCanvasItem *item,
+ GnomeCanvasBuf *buf);
+
+
+static GnomeCanvasItemClass *group_parent_class;
+
+
+/**
+ * gnome_canvas_group_get_type:
+ *
+ * Registers the &GnomeCanvasGroup class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &GnomeCanvasGroup class.
+ **/
+GType
+gnome_canvas_group_get_type (void)
+{
+ static GType canvas_group_type;
+
+ if (!canvas_group_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasGroupClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_group_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasGroup),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_group_init,
+ NULL /* value_table */
+ };
+
+ canvas_group_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasGroup",
+ &object_info, 0);
+ }
+
+ return canvas_group_type;
+}
+
+/* Class initialization function for GnomeCanvasGroupClass */
+static void
+gnome_canvas_group_class_init (GnomeCanvasGroupClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ group_parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = gnome_canvas_group_set_property;
+ gobject_class->get_property = gnome_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_READABLE | G_PARAM_WRITABLE)));
+ 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_READABLE | G_PARAM_WRITABLE)));
+
+ object_class->destroy = gnome_canvas_group_destroy;
+
+ item_class->update = gnome_canvas_group_update;
+ item_class->realize = gnome_canvas_group_realize;
+ item_class->unrealize = gnome_canvas_group_unrealize;
+ item_class->map = gnome_canvas_group_map;
+ item_class->unmap = gnome_canvas_group_unmap;
+ item_class->draw = gnome_canvas_group_draw;
+ item_class->render = gnome_canvas_group_render;
+ item_class->point = gnome_canvas_group_point;
+ item_class->bounds = gnome_canvas_group_bounds;
+}
+
+/* Object initialization function for GnomeCanvasGroup */
+static void
+gnome_canvas_group_init (GnomeCanvasGroup *group)
+{
+#if 0
+ group->xpos = 0.0;
+ group->ypos = 0.0;
+#endif
+}
+
+/* Translate handler for canvas groups */
+static double *
+gnome_canvas_ensure_translate (GnomeCanvasItem *item)
+{
+ if (item->xform == NULL) {
+ GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_AFFINE_FULL);
+ item->xform = g_new (double, 2);
+ item->xform[0] = 0.0;
+ item->xform[1] = 0.0;
+ return item->xform;
+ } else if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
+ return item->xform + 4;
+ } else {
+ return item->xform;
+ }
+}
+
+/* Set_property handler for canvas groups */
+static void
+gnome_canvas_group_set_property (GObject *gobject, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ double *xlat;
+
+ g_return_if_fail (GNOME_IS_CANVAS_GROUP (gobject));
+
+ item = GNOME_CANVAS_ITEM (gobject);
+
+ switch (param_id) {
+ case GROUP_PROP_X:
+ xlat = gnome_canvas_ensure_translate (item);
+ xlat[0] = g_value_get_double (value);
+ break;
+
+ case GROUP_PROP_Y:
+ xlat = gnome_canvas_ensure_translate (item);
+ xlat[1] = g_value_get_double (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ break;
+ }
+}
+
+/* Get_property handler for canvas groups */
+static void
+gnome_canvas_group_get_property (GObject *gobject, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+
+ g_return_if_fail (GNOME_IS_CANVAS_GROUP (gobject));
+
+ item = GNOME_CANVAS_ITEM (gobject);
+
+ switch (param_id) {
+ case GROUP_PROP_X:
+ if (item->xform == NULL)
+ g_value_set_double (value, 0);
+ else if (GTK_OBJECT (gobject)->flags & GNOME_CANVAS_ITEM_AFFINE_FULL)
+ g_value_set_double (value, item->xform[4]);
+ else
+ g_value_set_double (value, item->xform[0]);
+ break;
+
+ case GROUP_PROP_Y:
+ if (item->xform == NULL)
+ g_value_set_double (value, 0);
+ else if (GTK_OBJECT (gobject)->flags & GNOME_CANVAS_ITEM_AFFINE_FULL)
+ g_value_set_double (value, item->xform[5]);
+ else
+ g_value_set_double (value, item->xform[1]);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ break;
+ }
+}
+
+/* Destroy handler for canvas groups */
+static void
+gnome_canvas_group_destroy (GtkObject *object)
+{
+ GnomeCanvasGroup *group;
+
+ g_return_if_fail (GNOME_IS_CANVAS_GROUP (object));
+
+ group = GNOME_CANVAS_GROUP (object);
+
+ while (group->item_list) {
+ // child is unref'ed by the child's group_remove().
+ gtk_object_destroy (GTK_OBJECT (group->item_list->data));
+ }
+
+ if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
+}
+
+/* Update handler for canvas groups */
+static void
+gnome_canvas_group_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasGroup *group;
+ GList *list;
+ GnomeCanvasItem *i;
+ ArtDRect bbox, child_bbox;
+
+ group = GNOME_CANVAS_GROUP (item);
+
+ (* group_parent_class->update) (item, affine, clip_path, 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;
+
+ gnome_canvas_item_invoke_update (i, affine, clip_path, flags);
+
+ child_bbox.x0 = i->x1;
+ child_bbox.y0 = i->y1;
+ child_bbox.x1 = i->x2;
+ child_bbox.y1 = i->y2;
+ art_drect_union (&bbox, &bbox, &child_bbox);
+ }
+ item->x1 = bbox.x0;
+ item->y1 = bbox.y0;
+ item->x2 = bbox.x1;
+ item->y2 = bbox.y1;
+}
+
+/* Realize handler for canvas groups */
+static void
+gnome_canvas_group_realize (GnomeCanvasItem *item)
+{
+ GnomeCanvasGroup *group;
+ GList *list;
+ GnomeCanvasItem *i;
+
+ group = GNOME_CANVAS_GROUP (item);
+
+ for (list = group->item_list; list; list = list->next) {
+ i = list->data;
+
+ if (!(i->object.flags & GNOME_CANVAS_ITEM_REALIZED))
+ (* GNOME_CANVAS_ITEM_GET_CLASS (i)->realize) (i);
+ }
+
+ (* group_parent_class->realize) (item);
+}
+
+/* Unrealize handler for canvas groups */
+static void
+gnome_canvas_group_unrealize (GnomeCanvasItem *item)
+{
+ GnomeCanvasGroup *group;
+ GList *list;
+ GnomeCanvasItem *i;
+
+ group = GNOME_CANVAS_GROUP (item);
+
+ for (list = group->item_list; list; list = list->next) {
+ i = list->data;
+
+ if (i->object.flags & GNOME_CANVAS_ITEM_REALIZED)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (i)->unrealize) (i);
+ }
+
+ (* group_parent_class->unrealize) (item);
+}
+
+/* Map handler for canvas groups */
+static void
+gnome_canvas_group_map (GnomeCanvasItem *item)
+{
+ GnomeCanvasGroup *group;
+ GList *list;
+ GnomeCanvasItem *i;
+
+ group = GNOME_CANVAS_GROUP (item);
+
+ for (list = group->item_list; list; list = list->next) {
+ i = list->data;
+
+ if (!(i->object.flags & GNOME_CANVAS_ITEM_MAPPED))
+ (* GNOME_CANVAS_ITEM_GET_CLASS (i)->map) (i);
+ }
+
+ (* group_parent_class->map) (item);
+}
+
+/* Unmap handler for canvas groups */
+static void
+gnome_canvas_group_unmap (GnomeCanvasItem *item)
+{
+ GnomeCanvasGroup *group;
+ GList *list;
+ GnomeCanvasItem *i;
+
+ group = GNOME_CANVAS_GROUP (item);
+
+ for (list = group->item_list; list; list = list->next) {
+ i = list->data;
+
+ if (i->object.flags & GNOME_CANVAS_ITEM_MAPPED)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (i)->unmap) (i);
+ }
+
+ (* group_parent_class->unmap) (item);
+}
+
+/* Draw handler for canvas groups */
+static void
+gnome_canvas_group_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height)
+{
+ GnomeCanvasGroup *group;
+ GList *list;
+ GnomeCanvasItem *child = NULL;
+
+ group = GNOME_CANVAS_GROUP (item);
+
+ for (list = group->item_list; list; list = list->next) {
+ child = list->data;
+
+ if (((child->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+ && ((child->x1 < (x + width))
+ && (child->y1 < (y + height))
+ && (child->x2 > x)
+ && (child->y2 > y)))
+ || ((GTK_OBJECT_FLAGS (child) & GNOME_CANVAS_ITEM_ALWAYS_REDRAW)
+ && (child->x1 < child->canvas->redraw_x2)
+ && (child->y1 < child->canvas->redraw_y2)
+ && (child->x2 > child->canvas->redraw_x1)
+ && (child->y2 > child->canvas->redraw_y2)))
+ if (GNOME_CANVAS_ITEM_GET_CLASS (child)->draw)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (child)->draw) (
+ child, drawable, x, y, width, height);
+ }
+}
+
+/* Point handler for canvas groups */
+static double
+gnome_canvas_group_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
+ GnomeCanvasItem **actual_item)
+{
+ GnomeCanvasGroup *group;
+ GList *list;
+ GnomeCanvasItem *child, *point_item;
+ int x1, y1, x2, y2;
+ double gx, gy;
+ double dist, best;
+ int has_point;
+
+ group = GNOME_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;
+ gy = y;
+
+ 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 & GNOME_CANVAS_ITEM_VISIBLE)
+ && GNOME_CANVAS_ITEM_GET_CLASS (child)->point) {
+ dist = gnome_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;
+}
+
+/* Bounds handler for canvas groups */
+static void
+gnome_canvas_group_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ GnomeCanvasGroup *group;
+ GnomeCanvasItem *child;
+ GList *list;
+ double tx1, ty1, tx2, ty2;
+ double minx, miny, maxx, maxy;
+ int set;
+
+ group = GNOME_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 & GNOME_CANVAS_ITEM_VISIBLE) {
+ set = TRUE;
+ gnome_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 & GNOME_CANVAS_ITEM_VISIBLE))
+ continue;
+
+ gnome_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;
+ }
+
+ *x1 = minx;
+ *y1 = miny;
+ *x2 = maxx;
+ *y2 = maxy;
+}
+
+/* Render handler for canvas groups */
+static void
+gnome_canvas_group_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
+{
+ GnomeCanvasGroup *group;
+ GnomeCanvasItem *child;
+ GList *list;
+
+ group = GNOME_CANVAS_GROUP (item);
+
+ for (list = group->item_list; list; list = list->next) {
+ child = list->data;
+
+ if (((child->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+ && ((child->x1 < buf->rect.x1)
+ && (child->y1 < buf->rect.y1)
+ && (child->x2 > buf->rect.x0)
+ && (child->y2 > buf->rect.y0)))
+ || ((GTK_OBJECT_FLAGS (child) & GNOME_CANVAS_ITEM_ALWAYS_REDRAW)
+ && (child->x1 < child->canvas->redraw_x2)
+ && (child->y1 < child->canvas->redraw_y2)
+ && (child->x2 > child->canvas->redraw_x1)
+ && (child->y2 > child->canvas->redraw_y2)))
+ if (GNOME_CANVAS_ITEM_GET_CLASS (child)->render)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (child)->render) (
+ child, buf);
+ }
+}
+
+/* Adds an item to a group */
+static void
+group_add (GnomeCanvasGroup *group, GnomeCanvasItem *item)
+{
+ g_object_ref_sink (G_OBJECT (item));
+
+ 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 (group->item.object.flags & GNOME_CANVAS_ITEM_REALIZED)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
+
+ if (group->item.object.flags & GNOME_CANVAS_ITEM_MAPPED)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (item)->map) (item);
+
+ g_object_notify (G_OBJECT (item), "parent");
+}
+
+/* Removes an item from a group */
+static void
+group_remove (GnomeCanvasGroup *group, GnomeCanvasItem *item)
+{
+ GList *children;
+
+ g_return_if_fail (GNOME_IS_CANVAS_GROUP (group));
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ for (children = group->item_list; children; children = children->next)
+ if (children->data == item) {
+ if (item->object.flags & GNOME_CANVAS_ITEM_MAPPED)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_REALIZED)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
+
+ /* Unparent the child */
+
+ item->parent = 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;
+ }
+}
+
+
+/*** GnomeCanvas ***/
+
+
+enum {
+ DRAW_BACKGROUND,
+ RENDER_BACKGROUND,
+ LAST_SIGNAL
+};
+
+static void gnome_canvas_class_init (GnomeCanvasClass *class);
+static void gnome_canvas_init (GnomeCanvas *canvas);
+static void gnome_canvas_destroy (GtkObject *object);
+static void gnome_canvas_map (GtkWidget *widget);
+static void gnome_canvas_unmap (GtkWidget *widget);
+static void gnome_canvas_realize (GtkWidget *widget);
+static void gnome_canvas_unrealize (GtkWidget *widget);
+static void gnome_canvas_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static gint gnome_canvas_button (GtkWidget *widget,
+ GdkEventButton *event);
+static gint gnome_canvas_motion (GtkWidget *widget,
+ GdkEventMotion *event);
+static gint gnome_canvas_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static gboolean gnome_canvas_key (GtkWidget *widget,
+ GdkEventKey *event);
+static gint gnome_canvas_crossing (GtkWidget *widget,
+ GdkEventCrossing *event);
+static gint gnome_canvas_focus_in (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint gnome_canvas_focus_out (GtkWidget *widget,
+ GdkEventFocus *event);
+static void gnome_canvas_request_update_real (GnomeCanvas *canvas);
+static void gnome_canvas_draw_background (GnomeCanvas *canvas,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height);
+
+
+static GtkLayoutClass *canvas_parent_class;
+
+static guint canvas_signals[LAST_SIGNAL];
+
+enum {
+ PROP_AA = 1,
+ PROP_FOCUSED_ITEM
+};
+
+/**
+ * gnome_canvas_get_type:
+ *
+ * Registers the &GnomeCanvas class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &GnomeCanvas class.
+ **/
+GType
+gnome_canvas_get_type (void)
+{
+ static GType canvas_type;
+
+ if (!canvas_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvas),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_init,
+ NULL /* value_table */
+ };
+
+ canvas_type = g_type_register_static (GTK_TYPE_LAYOUT, "GnomeCanvas",
+ &object_info, 0);
+ }
+
+ return canvas_type;
+}
+
+static void
+gnome_canvas_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_AA:
+ g_value_set_boolean (value, GNOME_CANVAS (object)->aa);
+ break;
+ case PROP_FOCUSED_ITEM:
+ g_value_set_object (value, GNOME_CANVAS (object)->focused_item);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gnome_canvas_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_AA:
+ GNOME_CANVAS (object)->aa = g_value_get_boolean (value);
+ break;
+ case PROP_FOCUSED_ITEM:
+ GNOME_CANVAS (object)->focused_item = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/* Class initialization function for GnomeCanvasClass */
+static void
+gnome_canvas_class_init (GnomeCanvasClass *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 = gnome_canvas_set_property;
+ gobject_class->get_property = gnome_canvas_get_property;
+
+ object_class->destroy = gnome_canvas_destroy;
+
+ widget_class->map = gnome_canvas_map;
+ widget_class->unmap = gnome_canvas_unmap;
+ widget_class->realize = gnome_canvas_realize;
+ widget_class->unrealize = gnome_canvas_unrealize;
+ widget_class->size_allocate = gnome_canvas_size_allocate;
+ widget_class->button_press_event = gnome_canvas_button;
+ widget_class->button_release_event = gnome_canvas_button;
+ widget_class->motion_notify_event = gnome_canvas_motion;
+ widget_class->expose_event = gnome_canvas_expose;
+ widget_class->key_press_event = gnome_canvas_key;
+ widget_class->key_release_event = gnome_canvas_key;
+ widget_class->enter_notify_event = gnome_canvas_crossing;
+ widget_class->leave_notify_event = gnome_canvas_crossing;
+ widget_class->focus_in_event = gnome_canvas_focus_in;
+ widget_class->focus_out_event = gnome_canvas_focus_out;
+
+ klass->draw_background = gnome_canvas_draw_background;
+ klass->render_background = NULL;
+ klass->request_update = gnome_canvas_request_update_real;
+
+ g_object_class_install_property (G_OBJECT_CLASS (object_class),
+ PROP_AA,
+ g_param_spec_boolean ("aa",
+ _("Antialiased"),
+ _("The antialiasing mode of the canvas."),
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (gobject_class, PROP_FOCUSED_ITEM,
+ g_param_spec_object ("focused_item", NULL, NULL,
+ GNOME_TYPE_CANVAS_ITEM,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ canvas_signals[DRAW_BACKGROUND] =
+ g_signal_new ("draw_background",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GnomeCanvasClass, draw_background),
+ NULL, NULL,
+ gnome_canvas_marshal_VOID__OBJECT_INT_INT_INT_INT,
+ G_TYPE_NONE, 5, GDK_TYPE_DRAWABLE,
+ G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
+ canvas_signals[RENDER_BACKGROUND] =
+ g_signal_new ("render_background",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GnomeCanvasClass, render_background),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+ gail_canvas_init();
+}
+
+/* Callback used when the root item of a canvas is destroyed. The user should
+ * never ever do this, so we panic if this happens.
+ */
+G_GNUC_NORETURN 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 GnomeCanvas */
+static void
+gnome_canvas_init (GnomeCanvas *canvas)
+{
+ GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
+
+ canvas->need_update = FALSE;
+ canvas->need_redraw = FALSE;
+ canvas->redraw_area = NULL;
+ canvas->idle_id = 0;
+
+ 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;
+
+ canvas->dither = GDK_RGB_DITHER_MAX;
+
+ /* This may not be what people want, but it is set to be turned on by
+ * default to have the same initial behavior as the canvas in GNOME 1.4.
+ */
+ canvas->center_scroll_region = TRUE;
+
+ gtk_layout_set_hadjustment (GTK_LAYOUT (canvas), NULL);
+ gtk_layout_set_vadjustment (GTK_LAYOUT (canvas), NULL);
+
+ /* Disable the gtk+ double buffering since the canvas uses it's own. */
+ gtk_widget_set_double_buffered (GTK_WIDGET (canvas), FALSE);
+
+ /* Create the root item as a special case */
+
+ canvas->root = GNOME_CANVAS_ITEM (g_object_new (gnome_canvas_group_get_type (), NULL));
+ canvas->root->canvas = canvas;
+
+ g_object_ref_sink (canvas->root);
+
+ canvas->root_destroy_id = g_signal_connect (canvas->root, "destroy",
+ G_CALLBACK (panic_root_destroyed),
+ canvas);
+
+ canvas->need_repick = TRUE;
+}
+
+/* Convenience function to remove the idle handler of a canvas */
+static void
+remove_idle (GnomeCanvas *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 (GnomeCanvas *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;
+ art_uta_free (canvas->redraw_area);
+ canvas->redraw_area = NULL;
+ canvas->redraw_x1 = 0;
+ canvas->redraw_y1 = 0;
+ canvas->redraw_x2 = 0;
+ canvas->redraw_y2 = 0;
+ }
+
+ if (canvas->grabbed_item) {
+ canvas->grabbed_item = NULL;
+ gdk_pointer_ungrab (GDK_CURRENT_TIME);
+ }
+
+ remove_idle (canvas);
+}
+
+/* Destroy handler for GnomeCanvas */
+static void
+gnome_canvas_destroy (GtkObject *object)
+{
+ GnomeCanvas *canvas;
+
+ g_return_if_fail (GNOME_IS_CANVAS (object));
+
+ /* remember, destroy can be run multiple times! */
+
+ canvas = GNOME_CANVAS (object);
+
+ if (canvas->root_destroy_id) {
+ g_signal_handler_disconnect (canvas->root, canvas->root_destroy_id);
+ canvas->root_destroy_id = 0;
+ }
+ if (canvas->root) {
+ gtk_object_destroy (GTK_OBJECT (canvas->root));
+ g_object_unref (G_OBJECT (canvas->root));
+ canvas->root = NULL;
+ }
+
+ shutdown_transients (canvas);
+
+ if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
+}
+
+/**
+ * gnome_canvas_new:
+ *
+ * Creates a new empty canvas in non-antialiased mode.
+ *
+ * Return value: A newly-created canvas.
+ **/
+GtkWidget *
+gnome_canvas_new (void)
+{
+ return GTK_WIDGET (g_object_new (gnome_canvas_get_type (), NULL));
+}
+
+/**
+ * gnome_canvas_new_aa:
+ *
+ * Creates a new empty canvas in antialiased mode.
+ *
+ * Return value: A newly-created antialiased canvas.
+ **/
+GtkWidget *
+gnome_canvas_new_aa (void)
+{
+ return GTK_WIDGET (g_object_new (GNOME_TYPE_CANVAS,
+ "aa", TRUE,
+ NULL));
+}
+
+/* Map handler for the canvas */
+static void
+gnome_canvas_map (GtkWidget *widget)
+{
+ GnomeCanvas *canvas;
+
+ g_return_if_fail (GNOME_IS_CANVAS (widget));
+
+ /* Normal widget mapping stuff */
+
+ if (GTK_WIDGET_CLASS (canvas_parent_class)->map)
+ (* GTK_WIDGET_CLASS (canvas_parent_class)->map) (widget);
+
+ canvas = GNOME_CANVAS (widget);
+
+ if (canvas->need_update)
+ add_idle (canvas);
+
+ /* Map items */
+
+ if (GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->map)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->map) (canvas->root);
+}
+
+/* Unmap handler for the canvas */
+static void
+gnome_canvas_unmap (GtkWidget *widget)
+{
+ GnomeCanvas *canvas;
+
+ g_return_if_fail (GNOME_IS_CANVAS (widget));
+
+ canvas = GNOME_CANVAS (widget);
+
+ shutdown_transients (canvas);
+
+ /* Unmap items */
+
+ if (GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap)
+ (* GNOME_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
+gnome_canvas_realize (GtkWidget *widget)
+{
+ GnomeCanvas *canvas;
+
+ g_return_if_fail (GNOME_IS_CANVAS (widget));
+
+ /* Normal widget realization stuff */
+
+ if (GTK_WIDGET_CLASS (canvas_parent_class)->realize)
+ (* GTK_WIDGET_CLASS (canvas_parent_class)->realize) (widget);
+
+ canvas = GNOME_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);
+
+ (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->realize) (canvas->root);
+}
+
+/* Unrealize handler for the canvas */
+static void
+gnome_canvas_unrealize (GtkWidget *widget)
+{
+ GnomeCanvas *canvas;
+
+ g_return_if_fail (GNOME_IS_CANVAS (widget));
+
+ canvas = GNOME_CANVAS (widget);
+
+ shutdown_transients (canvas);
+
+ /* Unrealize items and parent widget */
+
+ (* GNOME_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 (GnomeCanvas *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 */
+ /* The reason is, that coverage UTA will be invalidated by offset change */
+ /* fixme: Strictly this is not correct - we have to remove our own idle (Lauris) */
+ /* More stuff - we have to mark root as needing fresh affine (Lauris) */
+ if (!(canvas->root->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
+ canvas->root->object.flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
+ gnome_canvas_request_update (canvas);
+ }
+ gtk_widget_queue_draw (GTK_WIDGET (canvas));
+ }
+
+ if (canvas->layout.hadjustment && ((int) canvas->layout.hadjustment->value) != cx) {
+ canvas->layout.hadjustment->value = cx;
+ changed_x = TRUE;
+ }
+
+ if (canvas->layout.vadjustment && ((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 (canvas->layout.hadjustment, "value_changed");
+
+ if (changed_y)
+ g_signal_emit_by_name (canvas->layout.vadjustment, "value_changed");
+}
+
+/* Size allocation handler for the canvas */
+static void
+gnome_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ GnomeCanvas *canvas;
+
+ g_return_if_fail (GNOME_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 = GNOME_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 (canvas->layout.hadjustment, "changed");
+ g_signal_emit_by_name (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 (GnomeCanvas *canvas, GdkEvent *event)
+{
+ GdkEvent *ev;
+ gint finished;
+ GnomeCanvasItem *item;
+ GnomeCanvasItem *parent;
+ guint mask;
+
+ /* Perform checks for grabbed items */
+
+ if (canvas->grabbed_item &&
+ !is_descendant (canvas->current_item, canvas->grabbed_item)) {
+ /* I think this warning is annoying and I don't know what it's for
+ * so I'll disable it for now.
+ */
+/* g_warning ("emit_event() returning FALSE!\n");*/
+ 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;
+
+ case GDK_SCROLL:
+ mask = GDK_SCROLL_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 = gdk_event_copy (event);
+
+ switch (ev->type)
+ {
+ case GDK_ENTER_NOTIFY:
+ case GDK_LEAVE_NOTIFY:
+ gnome_canvas_window_to_world (canvas,
+ ev->crossing.x, ev->crossing.y,
+ &ev->crossing.x, &ev->crossing.y);
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ case GDK_BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ case GDK_BUTTON_RELEASE:
+ gnome_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 (G_OBJECT (item));
+
+ g_signal_emit (item, item_signals[ITEM_EVENT], 0,
+ ev, &finished);
+
+ parent = item->parent;
+ g_object_unref (G_OBJECT (item));
+
+ item = parent;
+ }
+
+ gdk_event_free (ev);
+
+ 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 (GnomeCanvas *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 - canvas->zoom_xofs;
+ y = canvas->pick_event.crossing.y - canvas->zoom_yofs;
+ } else {
+ x = canvas->pick_event.motion.x - canvas->zoom_xofs;
+ y = canvas->pick_event.motion.y - canvas->zoom_yofs;
+ }
+
+ /* canvas pixel coords */
+
+ cx = (int) (x + 0.5);
+ cy = (int) (y + 0.5);
+
+ /* world coords */
+
+ x = canvas->scroll_x1 + x / canvas->pixels_per_unit;
+ y = canvas->scroll_y1 + y / canvas->pixels_per_unit;
+
+ /* find the closest item */
+
+ if (canvas->root->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+ gnome_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;
+
+ 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
+gnome_canvas_button (GtkWidget *widget, GdkEventButton *event)
+{
+ GnomeCanvas *canvas;
+ int mask;
+ int retval;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ retval = FALSE;
+
+ canvas = GNOME_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
+gnome_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
+{
+ GnomeCanvas *canvas;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ canvas = GNOME_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 gboolean
+gnome_canvas_key (GtkWidget *widget, GdkEventKey *event)
+{
+ GnomeCanvas *canvas;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ canvas = GNOME_CANVAS (widget);
+
+ if (!emit_event (canvas, (GdkEvent *) event)) {
+ GtkWidgetClass *widget_class;
+
+ widget_class = GTK_WIDGET_CLASS (canvas_parent_class);
+
+ if (event->type == GDK_KEY_PRESS) {
+ if (widget_class->key_press_event)
+ return (* widget_class->key_press_event) (widget, event);
+ } else if (event->type == GDK_KEY_RELEASE) {
+ if (widget_class->key_release_event)
+ return (* widget_class->key_release_event) (widget, event);
+ } else
+ g_assert_not_reached ();
+
+ return FALSE;
+ } else
+ return TRUE;
+}
+
+
+/* Crossing event handler for the canvas */
+static gint
+gnome_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
+{
+ GnomeCanvas *canvas;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ canvas = GNOME_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
+gnome_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
+{
+ GnomeCanvas *canvas;
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
+
+ canvas = GNOME_CANVAS (widget);
+
+ if (canvas->focused_item)
+ return emit_event (canvas, (GdkEvent *) event);
+ else
+ return FALSE;
+}
+
+/* Focus out handler for the canvas */
+static gint
+gnome_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
+{
+ GnomeCanvas *canvas;
+
+ GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
+
+ canvas = GNOME_CANVAS (widget);
+
+ if (canvas->focused_item)
+ return emit_event (canvas, (GdkEvent *) event);
+ else
+ return FALSE;
+}
+
+#define REDRAW_QUANTUM_SIZE 512
+
+static void
+gnome_canvas_paint_rect (GnomeCanvas *canvas, gint x0, gint y0, gint x1, gint y1)
+{
+ GtkWidget *widget;
+ gint draw_x1, draw_y1;
+ gint draw_x2, draw_y2;
+ gint draw_width, draw_height;
+
+ g_return_if_fail (!canvas->need_update);
+
+ widget = GTK_WIDGET (canvas);
+
+ draw_x1 = MAX (x0, canvas->layout.hadjustment->value - canvas->zoom_xofs);
+ draw_y1 = MAX (y0, canvas->layout.vadjustment->value - canvas->zoom_yofs);
+ draw_x2 = MIN (draw_x1 + GTK_WIDGET (canvas)->allocation.width, x1);
+ draw_y2 = MIN (draw_y1 + GTK_WIDGET (canvas)->allocation.height, y1);
+
+ draw_width = draw_x2 - draw_x1;
+ draw_height = draw_y2 - draw_y1;
+
+ if (draw_width < 1 || draw_height < 1)
+ return;
+
+ canvas->redraw_x1 = draw_x1;
+ canvas->redraw_y1 = draw_y1;
+ canvas->redraw_x2 = draw_x2;
+ canvas->redraw_y2 = draw_y2;
+ canvas->draw_xofs = draw_x1;
+ canvas->draw_yofs = draw_y1;
+
+ if (canvas->aa) {
+ GnomeCanvasBuf buf;
+ guchar *px;
+ GdkColor *color;
+
+ px = g_new (guchar, draw_width * 3 * draw_height);
+
+ buf.buf = px;
+ buf.buf_rowstride = draw_width * 3;
+ buf.rect.x0 = draw_x1;
+ buf.rect.y0 = draw_y1;
+ buf.rect.x1 = draw_x2;
+ buf.rect.y1 = draw_y2;
+ color = &widget->style->bg[GTK_STATE_NORMAL];
+ buf.bg_color = (((color->red & 0xff00) << 8) | (color->green & 0xff00) | (color->blue >> 8));
+ buf.is_bg = 1;
+ buf.is_buf = 0;
+
+ g_signal_emit (G_OBJECT (canvas), canvas_signals[RENDER_BACKGROUND], 0, &buf);
+
+ if (canvas->root->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->render) (canvas->root, &buf);
+
+ if (buf.is_bg) {
+ gdk_gc_set_rgb_fg_color (canvas->pixmap_gc, color);
+ gdk_draw_rectangle (canvas->layout.bin_window,
+ canvas->pixmap_gc,
+ TRUE,
+ (draw_x1 + canvas->zoom_xofs),
+ (draw_y1 + canvas->zoom_yofs),
+ draw_width, draw_height);
+ } else {
+ gdk_draw_rgb_image_dithalign (canvas->layout.bin_window,
+ canvas->pixmap_gc,
+ (draw_x1 + canvas->zoom_xofs),
+ (draw_y1 + canvas->zoom_yofs),
+ draw_width, draw_height,
+ canvas->dither,
+ buf.buf,
+ buf.buf_rowstride,
+ draw_x1, draw_y1);
+ }
+
+ g_free (px);
+ } else {
+ GdkPixmap *pixmap;
+
+ pixmap = gdk_pixmap_new (canvas->layout.bin_window,
+ draw_width, draw_height,
+ gtk_widget_get_visual (widget)->depth);
+
+ g_signal_emit (G_OBJECT (canvas), canvas_signals[DRAW_BACKGROUND], 0, pixmap,
+ draw_x1, draw_y1, draw_width, draw_height);
+
+ if (canvas->root->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->draw) (
+ canvas->root, pixmap,
+ draw_x1, draw_y1,
+ draw_width, draw_height);
+
+ /* Copy the pixmap to the window and clean up */
+
+ gdk_draw_drawable (canvas->layout.bin_window,
+ canvas->pixmap_gc,
+ pixmap,
+ 0, 0,
+ draw_x1 + canvas->zoom_xofs,
+ draw_y1 + canvas->zoom_yofs,
+ draw_width, draw_height);
+
+ g_object_unref (pixmap);
+ }
+}
+
+/* Expose handler for the canvas */
+static gint
+gnome_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+ GnomeCanvas *canvas;
+ GdkRectangle *rects;
+ gint n_rects;
+ int i;
+
+ canvas = GNOME_CANVAS (widget);
+
+ if (!gtk_widget_is_drawable (widget) || (event->window != canvas->layout.bin_window))
+ return FALSE;
+
+#ifdef VERBOSE
+ g_print ("Expose\n");
+#endif
+
+ gdk_region_get_rectangles (event->region, &rects, &n_rects);
+
+ for (i = 0; i < n_rects; i++) {
+ ArtIRect rect;
+
+ rect.x0 = rects[i].x - canvas->zoom_xofs;
+ rect.y0 = rects[i].y - canvas->zoom_yofs;
+ rect.x1 = rects[i].x + rects[i].width - canvas->zoom_xofs;
+ rect.y1 = rects[i].y + rects[i].height - canvas->zoom_yofs;
+
+ if (canvas->need_update || canvas->need_redraw) {
+ ArtUta *uta;
+
+ /* Update or drawing is scheduled, so just mark exposed area as dirty */
+ uta = art_uta_from_irect (&rect);
+ gnome_canvas_request_redraw_uta (canvas, uta);
+ } else {
+ /* No pending updates, draw exposed area immediately */
+ gnome_canvas_paint_rect (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
+
+ /* And call expose on parent container class */
+ if (GTK_WIDGET_CLASS (canvas_parent_class)->expose_event)
+ (* GTK_WIDGET_CLASS (canvas_parent_class)->expose_event) (
+ widget, event);
+ }
+ }
+
+ g_free (rects);
+
+ return FALSE;
+}
+
+/* Repaints the areas in the canvas that need it */
+static void
+paint (GnomeCanvas *canvas)
+{
+ ArtIRect *rects;
+ gint n_rects, i;
+ ArtIRect visible_rect;
+ GdkRegion *region;
+
+ /* Extract big rectangles from the microtile array */
+
+ rects = art_rect_list_from_uta (canvas->redraw_area,
+ REDRAW_QUANTUM_SIZE, REDRAW_QUANTUM_SIZE,
+ &n_rects);
+
+ art_uta_free (canvas->redraw_area);
+ canvas->redraw_area = NULL;
+ canvas->need_redraw = FALSE;
+
+ /* Turn those rectangles into a GdkRegion for exposing */
+
+ visible_rect.x0 = canvas->layout.hadjustment->value - canvas->zoom_xofs;
+ visible_rect.y0 = canvas->layout.vadjustment->value - canvas->zoom_yofs;
+ visible_rect.x1 = visible_rect.x0 + GTK_WIDGET (canvas)->allocation.width;
+ visible_rect.y1 = visible_rect.y0 + GTK_WIDGET (canvas)->allocation.height;
+
+ region = gdk_region_new ();
+
+ for (i = 0; i < n_rects; i++) {
+ ArtIRect clipped;
+
+ art_irect_intersect (&clipped, &visible_rect, rects + i);
+ if (!art_irect_empty (&clipped)) {
+ GdkRectangle gdkrect;
+
+ gdkrect.x = clipped.x0 + canvas->zoom_xofs;
+ gdkrect.y = clipped.y0 + canvas->zoom_yofs;
+ gdkrect.width = clipped.x1 - clipped.x0;
+ gdkrect.height = clipped.y1 - clipped.y0;
+
+ region = gdk_region_rectangle (&gdkrect);
+ gdk_window_invalidate_region (canvas->layout.bin_window, region, FALSE);
+ gdk_region_destroy (region);
+ }
+ }
+
+ art_free (rects);
+
+ canvas->redraw_x1 = 0;
+ canvas->redraw_y1 = 0;
+ canvas->redraw_x2 = 0;
+ canvas->redraw_y2 = 0;
+}
+
+static void
+gnome_canvas_draw_background (GnomeCanvas *canvas, GdkDrawable *drawable,
+ int x, int y, int width, int height)
+{
+ /* By default, we use the style background. */
+ gdk_gc_set_foreground (canvas->pixmap_gc,
+ &GTK_WIDGET (canvas)->style->bg[GTK_STATE_NORMAL]);
+ gdk_draw_rectangle (drawable,
+ canvas->pixmap_gc,
+ TRUE,
+ 0, 0,
+ width, height);
+}
+
+static void
+do_update (GnomeCanvas *canvas)
+{
+ /* Cause the update if necessary */
+
+update_again:
+ if (canvas->need_update) {
+ gdouble w2cpx[6];
+
+ /* We start updating root with w2cpx affine */
+ w2cpx[0] = canvas->pixels_per_unit;
+ w2cpx[1] = 0.0;
+ w2cpx[2] = 0.0;
+ w2cpx[3] = canvas->pixels_per_unit;
+ w2cpx[4] = -canvas->scroll_x1 * canvas->pixels_per_unit;
+ w2cpx[5] = -canvas->scroll_y1 * canvas->pixels_per_unit;
+
+ gnome_canvas_item_invoke_update (canvas->root, w2cpx, NULL, 0);
+
+ 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;
+ }
+
+ /* Paint if able to */
+
+ if (gtk_widget_is_drawable (GTK_WIDGET (canvas)) && canvas->need_redraw)
+ paint (canvas);
+}
+
+/* Idle handler for the canvas. It deals with pending updates and redraws. */
+static gboolean
+idle_handler (gpointer data)
+{
+ GnomeCanvas *canvas;
+
+ GDK_THREADS_ENTER ();
+
+ canvas = GNOME_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 (GnomeCanvas *canvas)
+{
+ g_assert (canvas->need_update || canvas->need_redraw);
+
+ if (!canvas->idle_id)
+ canvas->idle_id = g_idle_add_full (CANVAS_IDLE_PRIORITY,
+ idle_handler,
+ canvas,
+ NULL);
+
+/* canvas->idle_id = gtk_idle_add (idle_handler, canvas); */
+}
+
+/**
+ * gnome_canvas_root:
+ * @canvas: A canvas.
+ *
+ * Queries the root group of a canvas.
+ *
+ * Return value: The root group of the specified canvas.
+ **/
+GnomeCanvasGroup *
+gnome_canvas_root (GnomeCanvas *canvas)
+{
+ g_return_val_if_fail (GNOME_IS_CANVAS (canvas), NULL);
+
+ return GNOME_CANVAS_GROUP (canvas->root);
+}
+
+
+/**
+ * gnome_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
+gnome_canvas_set_scroll_region (GnomeCanvas *canvas, double x1, double y1, double x2, double y2)
+{
+ double wxofs, wyofs;
+ int xofs, yofs;
+
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ /*
+ * Set the new scrolling region. If possible, do not move the visible contents of the
+ * canvas.
+ */
+
+ gnome_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;
+
+ gnome_canvas_w2c (canvas, wxofs, wyofs, &xofs, &yofs);
+
+ scroll_to (canvas, xofs, yofs);
+
+ canvas->need_repick = TRUE;
+#if 0
+ /* todo: should be requesting update */
+ (* GNOME_CANVAS_ITEM_CLASS (canvas->root->object.klass)->update) (
+ canvas->root, NULL, NULL, 0);
+#endif
+}
+
+
+/**
+ * gnome_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
+gnome_canvas_get_scroll_region (GnomeCanvas *canvas, double *x1, double *y1, double *x2, double *y2)
+{
+ g_return_if_fail (GNOME_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;
+}
+
+/**
+ * gnome_canvas_set_center_scroll_region:
+ * @canvas: A canvas.
+ * @center_scroll_region: Whether to center the scrolling region in the canvas
+ * window when it is smaller than the canvas' allocation.
+ *
+ * When the scrolling region of the canvas is smaller than the canvas window,
+ * e.g. the allocation of the canvas, it can be either centered on the window
+ * or simply made to be on the upper-left corner on the window. This function
+ * lets you configure this property.
+ **/
+void
+gnome_canvas_set_center_scroll_region (GnomeCanvas *canvas, gboolean center_scroll_region)
+{
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ canvas->center_scroll_region = center_scroll_region != 0;
+
+ scroll_to (canvas,
+ canvas->layout.hadjustment->value,
+ canvas->layout.vadjustment->value);
+}
+
+/**
+ * gnome_canvas_get_center_scroll_region:
+ * @canvas: A canvas.
+ *
+ * Returns whether the canvas is set to center the scrolling region in the window
+ * if the former is smaller than the canvas' allocation.
+ *
+ * Return value: Whether the scroll region is being centered in the canvas window.
+ **/
+gboolean
+gnome_canvas_get_center_scroll_region (GnomeCanvas *canvas)
+{
+ g_return_val_if_fail (GNOME_IS_CANVAS (canvas), FALSE);
+
+ return canvas->center_scroll_region ? TRUE : FALSE;
+}
+
+/**
+ * gnome_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.
+ *
+ * The anchor point for zooming, i.e. the point that stays fixed and all others
+ * zoom inwards or outwards from it, depends on whether the canvas is set to
+ * center the scrolling region or not. You can control this using the
+ * gnome_canvas_set_center_scroll_region() function. If the canvas is set to
+ * center the scroll region, then the center of the canvas window is used as the
+ * anchor point for zooming. Otherwise, the upper-left corner of the canvas
+ * window is used as the anchor point.
+ **/
+void
+gnome_canvas_set_pixels_per_unit (GnomeCanvas *canvas, double n)
+{
+ double ax, ay;
+ int x1, y1;
+ int anchor_x, anchor_y;
+
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+ g_return_if_fail (n > GNOME_CANVAS_EPSILON);
+
+ if (canvas->center_scroll_region) {
+ anchor_x = GTK_WIDGET (canvas)->allocation.width / 2;
+ anchor_y = GTK_WIDGET (canvas)->allocation.height / 2;
+ } else
+ anchor_x = anchor_y = 0;
+
+ /* Find the coordinates of the anchor point in units. */
+ if(canvas->layout.hadjustment) {
+ ax = (canvas->layout.hadjustment->value + anchor_x) / canvas->pixels_per_unit + canvas->scroll_x1 + canvas->zoom_xofs;
+ } else {
+ ax = (0.0 + anchor_x) / canvas->pixels_per_unit + canvas->scroll_x1 + canvas->zoom_xofs;
+ }
+ if(canvas->layout.hadjustment) {
+ ay = (canvas->layout.vadjustment->value + anchor_y) / canvas->pixels_per_unit + canvas->scroll_y1 + canvas->zoom_yofs;
+ } else {
+ ay = (0.0 + anchor_y) / canvas->pixels_per_unit + canvas->scroll_y1 + canvas->zoom_yofs;
+ }
+
+ /* Now calculate the new offset of the upper left corner. */
+ x1 = ((ax - canvas->scroll_x1) * n) - anchor_x;
+ y1 = ((ay - canvas->scroll_y1) * n) - anchor_y;
+
+ canvas->pixels_per_unit = n;
+
+ scroll_to (canvas, x1, y1);
+
+ if (!(canvas->root->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
+ canvas->root->object.flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
+ gnome_canvas_request_update (canvas);
+ }
+
+ canvas->need_repick = TRUE;
+}
+
+/**
+ * gnome_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
+gnome_canvas_scroll_to (GnomeCanvas *canvas, int cx, int cy)
+{
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ scroll_to (canvas, cx, cy);
+}
+
+/**
+ * gnome_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
+gnome_canvas_get_scroll_offsets (GnomeCanvas *canvas, int *cx, int *cy)
+{
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ if (cx)
+ *cx = canvas->layout.hadjustment->value;
+
+ if (cy)
+ *cy = canvas->layout.vadjustment->value;
+}
+
+/**
+ * gnome_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
+gnome_canvas_update_now (GnomeCanvas *canvas)
+{
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ if (!(canvas->need_update || canvas->need_redraw)) {
+ g_assert (canvas->idle_id == 0);
+ g_assert (canvas->redraw_area == NULL);
+ return;
+ }
+
+ remove_idle (canvas);
+ do_update (canvas);
+}
+
+/**
+ * gnome_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.
+ **/
+GnomeCanvasItem *
+gnome_canvas_get_item_at (GnomeCanvas *canvas, double x, double y)
+{
+ GnomeCanvasItem *item;
+ double dist;
+ int cx, cy;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS (canvas), NULL);
+
+ gnome_canvas_w2c (canvas, x, y, &cx, &cy);
+
+ dist = gnome_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
+gnome_canvas_request_update (GnomeCanvas *canvas)
+{
+ GNOME_CANVAS_GET_CLASS (canvas)->request_update (canvas);
+}
+
+static void
+gnome_canvas_request_update_real (GnomeCanvas *canvas)
+{
+ if (canvas->need_update)
+ return;
+
+ canvas->need_update = TRUE;
+ if (gtk_widget_get_mapped ((GtkWidget *) canvas))
+ add_idle (canvas);
+}
+
+/* Computes the union of two microtile arrays while clipping the result to the
+ * specified rectangle. Any of the specified utas can be NULL, in which case it
+ * is taken to be an empty region.
+ */
+static ArtUta *
+uta_union_clip (ArtUta *uta1, ArtUta *uta2, ArtIRect *clip)
+{
+ ArtUta *uta;
+ ArtUtaBbox *utiles;
+ int clip_x1, clip_y1, clip_x2, clip_y2;
+ int union_x1, union_y1, union_x2, union_y2;
+ int new_x1, new_y1, new_x2, new_y2;
+ int x, y;
+ int ofs, ofs1, ofs2;
+
+ g_assert (clip != NULL);
+
+ /* Compute the tile indices for the clipping rectangle */
+
+ clip_x1 = clip->x0 >> ART_UTILE_SHIFT;
+ clip_y1 = clip->y0 >> ART_UTILE_SHIFT;
+ clip_x2 = (clip->x1 >> ART_UTILE_SHIFT) + 1;
+ clip_y2 = (clip->y1 >> ART_UTILE_SHIFT) + 1;
+
+ /* Get the union of the bounds of both utas */
+
+ if (!uta1) {
+ if (!uta2)
+ return art_uta_new (clip_x1, clip_y1, clip_x1 + 1, clip_y1 + 1);
+
+ union_x1 = uta2->x0;
+ union_y1 = uta2->y0;
+ union_x2 = uta2->x0 + uta2->width;
+ union_y2 = uta2->y0 + uta2->height;
+ } else {
+ if (!uta2) {
+ union_x1 = uta1->x0;
+ union_y1 = uta1->y0;
+ union_x2 = uta1->x0 + uta1->width;
+ union_y2 = uta1->y0 + uta1->height;
+ } else {
+ union_x1 = MIN (uta1->x0, uta2->x0);
+ union_y1 = MIN (uta1->y0, uta2->y0);
+ union_x2 = MAX (uta1->x0 + uta1->width, uta2->x0 + uta2->width);
+ union_y2 = MAX (uta1->y0 + uta1->height, uta2->y0 + uta2->height);
+ }
+ }
+
+ /* Clip the union of the bounds */
+
+ new_x1 = MAX (clip_x1, union_x1);
+ new_y1 = MAX (clip_y1, union_y1);
+ new_x2 = MIN (clip_x2, union_x2);
+ new_y2 = MIN (clip_y2, union_y2);
+
+ if (new_x1 >= new_x2 || new_y1 >= new_y2)
+ return art_uta_new (clip_x1, clip_y1, clip_x1 + 1, clip_y1 + 1);
+
+ /* Make the new clipped union */
+
+ uta = art_new (ArtUta, 1);
+ uta->x0 = new_x1;
+ uta->y0 = new_y1;
+ uta->width = new_x2 - new_x1;
+ uta->height = new_y2 - new_y1;
+ uta->utiles = utiles = art_new (ArtUtaBbox, uta->width * uta->height);
+
+ ofs = 0;
+ ofs1 = ofs2 = 0;
+
+ for (y = new_y1; y < new_y2; y++) {
+ if (uta1)
+ ofs1 = (y - uta1->y0) * uta1->width + new_x1 - uta1->x0;
+
+ if (uta2)
+ ofs2 = (y - uta2->y0) * uta2->width + new_x1 - uta2->x0;
+
+ for (x = new_x1; x < new_x2; x++) {
+ ArtUtaBbox bb1, bb2, bb;
+
+ if (!uta1
+ || x < uta1->x0 || y < uta1->y0
+ || x >= uta1->x0 + uta1->width || y >= uta1->y0 + uta1->height)
+ bb1 = 0;
+ else
+ bb1 = uta1->utiles[ofs1];
+
+ if (!uta2
+ || x < uta2->x0 || y < uta2->y0
+ || x >= uta2->x0 + uta2->width || y >= uta2->y0 + uta2->height)
+ bb2 = 0;
+ else
+ bb2 = uta2->utiles[ofs2];
+
+ if (bb1 == 0)
+ bb = bb2;
+ else if (bb2 == 0)
+ bb = bb1;
+ else
+ bb = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb1),
+ ART_UTA_BBOX_X0 (bb2)),
+ MIN (ART_UTA_BBOX_Y0 (bb1),
+ ART_UTA_BBOX_Y0 (bb2)),
+ MAX (ART_UTA_BBOX_X1 (bb1),
+ ART_UTA_BBOX_X1 (bb2)),
+ MAX (ART_UTA_BBOX_Y1 (bb1),
+ ART_UTA_BBOX_Y1 (bb2)));
+
+ utiles[ofs] = bb;
+
+ ofs++;
+ ofs1++;
+ ofs2++;
+ }
+ }
+
+ return uta;
+}
+
+static inline void
+get_visible_region (GnomeCanvas *canvas, ArtIRect *visible)
+{
+ visible->x0 = canvas->layout.hadjustment->value - canvas->zoom_xofs;
+ visible->y0 = canvas->layout.vadjustment->value - canvas->zoom_yofs;
+ visible->x1 = visible->x0 + GTK_WIDGET (canvas)->allocation.width;
+ visible->y1 = visible->y0 + GTK_WIDGET (canvas)->allocation.height;
+}
+
+/**
+ * gnome_canvas_request_redraw_uta:
+ * @canvas: A canvas.
+ * @uta: Microtile array that specifies the area to be redrawn. It will
+ * be freed by this function, so the argument you pass will be invalid
+ * after you call this function.
+ *
+ * Informs a canvas that the specified area, given as a microtile array, needs
+ * to be repainted. To be used only by item implementations.
+ **/
+void
+gnome_canvas_request_redraw_uta (GnomeCanvas *canvas,
+ ArtUta *uta)
+{
+ ArtIRect visible;
+
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+ g_return_if_fail (uta != NULL);
+
+ if (!gtk_widget_is_drawable (GTK_WIDGET (canvas))) {
+ art_uta_free (uta);
+ return;
+ }
+
+ get_visible_region (canvas, &visible);
+
+ if (canvas->need_redraw) {
+ ArtUta *new_uta;
+
+ g_assert (canvas->redraw_area != NULL);
+ /* ALEX: This can fail if e.g. redraw_uta is called by an item
+ update function and we're called from update_now -> do_update
+ because update_now sets idle_id == 0. There is also some way
+ to get it from the expose handler (see bug #102811).
+ g_assert (canvas->idle_id != 0); */
+
+ new_uta = uta_union_clip (canvas->redraw_area, uta, &visible);
+ art_uta_free (canvas->redraw_area);
+ art_uta_free (uta);
+ canvas->redraw_area = new_uta;
+ if (canvas->idle_id == 0)
+ add_idle (canvas);
+ } else {
+ ArtUta *new_uta;
+
+ g_assert (canvas->redraw_area == NULL);
+
+ new_uta = uta_union_clip (uta, NULL, &visible);
+ art_uta_free (uta);
+ canvas->redraw_area = new_uta;
+
+ canvas->need_redraw = TRUE;
+ add_idle (canvas);
+ }
+}
+
+
+/**
+ * gnome_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. This function converts the rectangle to a microtile array
+ * and feeds it to gnome_canvas_request_redraw_uta(). The rectangle includes
+ * @x1 and @y1, but not @x2 and @y2. To be used only by item implementations.
+ **/
+void
+gnome_canvas_request_redraw (GnomeCanvas *canvas, int x1, int y1, int x2, int y2)
+{
+ ArtUta *uta;
+ ArtIRect bbox;
+ ArtIRect visible;
+ ArtIRect clip;
+
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ if (!gtk_widget_is_drawable (GTK_WIDGET (canvas)) || (x1 >= x2) || (y1 >= y2))
+ return;
+
+ bbox.x0 = x1;
+ bbox.y0 = y1;
+ bbox.x1 = x2;
+ bbox.y1 = y2;
+
+ get_visible_region (canvas, &visible);
+
+ art_irect_intersect (&clip, &bbox, &visible);
+
+ if (!art_irect_empty (&clip)) {
+ uta = art_uta_from_irect (&clip);
+ gnome_canvas_request_redraw_uta (canvas, uta);
+ }
+}
+
+
+/**
+ * gnome_canvas_w2c_affine:
+ * @canvas: A canvas.
+ * @affine: An affine transformation matrix (return value).
+ *
+ * Gets the affine transform that converts from world coordinates to canvas
+ * pixel coordinates.
+ **/
+void
+gnome_canvas_w2c_affine (GnomeCanvas *canvas, double affine[6])
+{
+ double zooom;
+
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+ g_return_if_fail (affine != NULL);
+
+ zooom = canvas->pixels_per_unit;
+
+ affine[0] = zooom;
+ affine[1] = 0;
+ affine[2] = 0;
+ affine[3] = zooom;
+ affine[4] = -canvas->scroll_x1 * zooom;
+ affine[5] = -canvas->scroll_y1 * zooom;
+}
+
+/**
+ * gnome_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
+gnome_canvas_w2c (GnomeCanvas *canvas, double wx, double wy, int *cx, int *cy)
+{
+ double affine[6];
+ ArtPoint w, c;
+
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ gnome_canvas_w2c_affine (canvas, affine);
+ w.x = wx;
+ w.y = wy;
+ art_affine_point (&c, &w, affine);
+ if (cx)
+ *cx = floor (c.x + 0.5);
+ if (cy)
+ *cy = floor (c.y + 0.5);
+}
+
+/**
+ * gnome_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 returns coordinates in floating point coordinates, for
+ * greater precision.
+ **/
+void
+gnome_canvas_w2c_d (GnomeCanvas *canvas, double wx, double wy, double *cx, double *cy)
+{
+ double affine[6];
+ ArtPoint w, c;
+
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ gnome_canvas_w2c_affine (canvas, affine);
+ w.x = wx;
+ w.y = wy;
+ art_affine_point (&c, &w, affine);
+ if (cx)
+ *cx = c.x;
+ if (cy)
+ *cy = c.y;
+}
+
+
+/**
+ * gnome_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
+gnome_canvas_c2w (GnomeCanvas *canvas, int cx, int cy, double *wx, double *wy)
+{
+ double affine[6], inv[6];
+ ArtPoint w, c;
+
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ gnome_canvas_w2c_affine (canvas, affine);
+ art_affine_invert (inv, affine);
+ c.x = cx;
+ c.y = cy;
+ art_affine_point (&w, &c, inv);
+ if (wx)
+ *wx = w.x;
+ if (wy)
+ *wy = w.y;
+}
+
+
+/**
+ * gnome_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.
+ **/
+void
+gnome_canvas_window_to_world (GnomeCanvas *canvas, double winx, double winy,
+ double *worldx, double *worldy)
+{
+ g_return_if_fail (GNOME_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);
+}
+
+
+/**
+ * gnome_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.
+ **/
+void
+gnome_canvas_world_to_window (GnomeCanvas *canvas, double worldx, double worldy,
+ double *winx, double *winy)
+{
+ g_return_if_fail (GNOME_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;
+}
+
+
+
+/**
+ * gnome_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
+gnome_canvas_get_color (GnomeCanvas *canvas, const char *spec, GdkColor *color)
+{
+ GdkColormap *colormap;
+
+ g_return_val_if_fail (GNOME_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;
+}
+
+/**
+ * gnome_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
+gnome_canvas_get_color_pixel (GnomeCanvas *canvas, guint rgba)
+{
+ GdkColormap *colormap;
+ GdkColor color;
+
+ g_return_val_if_fail (GNOME_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;
+}
+
+
+/**
+ * gnome_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
+gnome_canvas_set_stipple_origin (GnomeCanvas *canvas, GdkGC *gc)
+{
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+ g_return_if_fail (GDK_IS_GC (gc));
+
+ gdk_gc_set_ts_origin (gc, -canvas->draw_xofs, -canvas->draw_yofs);
+}
+
+/**
+ * gnome_canvas_set_dither:
+ * @canvas: A canvas.
+ * @dither: Type of dithering used to render an antialiased canvas.
+ *
+ * Controls dithered rendering for antialiased canvases. The value of
+ * dither should be #GDK_RGB_DITHER_NONE, #GDK_RGB_DITHER_NORMAL, or
+ * #GDK_RGB_DITHER_MAX. The default canvas setting is
+ * #GDK_RGB_DITHER_NORMAL.
+ **/
+void
+gnome_canvas_set_dither (GnomeCanvas *canvas, GdkRgbDither dither)
+{
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ canvas->dither = dither;
+}
+
+/**
+ * gnome_canvas_get_dither:
+ * @canvas: A canvas.
+ *
+ * Returns the type of dithering used to render an antialiased canvas.
+ *
+ * Return value: The dither setting.
+ **/
+GdkRgbDither
+gnome_canvas_get_dither (GnomeCanvas *canvas)
+{
+ g_return_val_if_fail (GNOME_IS_CANVAS (canvas), GDK_RGB_DITHER_NONE);
+
+ return canvas->dither;
+}
+
+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;
+}
+
+/* Class initialization function for GnomeCanvasItemClass */
+static void
+gnome_canvas_item_class_init (GnomeCanvasItemClass *class)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = (GObjectClass *) class;
+
+ item_parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = gnome_canvas_item_set_property;
+ gobject_class->get_property = gnome_canvas_item_get_property;
+
+ g_object_class_install_property
+ (gobject_class, ITEM_PROP_PARENT,
+ g_param_spec_object ("parent", NULL, NULL,
+ GNOME_TYPE_CANVAS_ITEM,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ item_signals[ITEM_EVENT] =
+ g_signal_new ("event",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GnomeCanvasItemClass, event),
+ boolean_handled_accumulator, NULL,
+ gnome_canvas_marshal_BOOLEAN__BOXED,
+ G_TYPE_BOOLEAN, 1,
+ GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+ gobject_class->dispose = gnome_canvas_item_dispose;
+
+ class->realize = gnome_canvas_item_realize;
+ class->unrealize = gnome_canvas_item_unrealize;
+ class->map = gnome_canvas_item_map;
+ class->unmap = gnome_canvas_item_unmap;
+ class->update = gnome_canvas_item_update;
+}
diff --git a/libgnomecanvas/gnome-canvas.h b/libgnomecanvas/gnome-canvas.h
new file mode 100644
index 0000000000..a9c70be4f5
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas.h
@@ -0,0 +1,635 @@
+/* -*- 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@
+ */
+/* GnomeCanvas widget - Tk-like canvas widget for Gnome
+ *
+ * GnomeCanvas 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 GNOME_CANVAS_H
+#define GNOME_CANVAS_H
+
+#include <gtk/gtk.h>
+#include <stdarg.h>
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_uta.h>
+#include <libart_lgpl/art_affine.h>
+
+G_BEGIN_DECLS
+
+
+/* "Small" value used by canvas stuff */
+#define GNOME_CANVAS_EPSILON 1e-10
+
+
+/* Macros for building colors that fit in a 32-bit integer. The values are in
+ * [0, 255].
+ */
+
+#define GNOME_CANVAS_COLOR(r, g, b) ((((unsigned int) (r) & 0xff) << 24) \
+ | (((unsigned int) (g) & 0xff) << 16) \
+ | (((unsigned int) (b) & 0xff) << 8) \
+ | 0xff)
+
+#define GNOME_CANVAS_COLOR_A(r, g, b, a) ((((unsigned int) (r) & 0xff) << 24) \
+ | (((unsigned int) (g) & 0xff) << 16) \
+ | (((unsigned int) (b) & 0xff) << 8) \
+ | ((unsigned int) (a) & 0xff))
+
+
+typedef struct _GnomeCanvas GnomeCanvas;
+typedef struct _GnomeCanvasClass GnomeCanvasClass;
+typedef struct _GnomeCanvasItem GnomeCanvasItem;
+typedef struct _GnomeCanvasItemClass GnomeCanvasItemClass;
+typedef struct _GnomeCanvasGroup GnomeCanvasGroup;
+typedef struct _GnomeCanvasGroupClass GnomeCanvasGroupClass;
+
+
+/* GnomeCanvasItem - base item class for canvas items
+ *
+ * All canvas items are derived from GnomeCanvasItem. The only information a
+ * GnomeCanvasItem contains is its parent canvas, its parent canvas item group,
+ * its bounding box in world coordinates, and its current affine transformation.
+ *
+ * Items inside a canvas are organized in a tree of GnomeCanvasItemGroup nodes
+ * and GnomeCanvasItem leaves. Each canvas has a single root group, which can
+ * be obtained with the gnome_canvas_get_root() function.
+ *
+ * The abstract GnomeCanvasItem class does not have any configurable or
+ * queryable attributes.
+ */
+
+/* Object flags for items */
+enum {
+ GNOME_CANVAS_ITEM_REALIZED = 1 << 4,
+ GNOME_CANVAS_ITEM_MAPPED = 1 << 5,
+ GNOME_CANVAS_ITEM_ALWAYS_REDRAW = 1 << 6,
+ GNOME_CANVAS_ITEM_VISIBLE = 1 << 7,
+ GNOME_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
+ GNOME_CANVAS_ITEM_NEED_AFFINE = 1 << 9,
+ GNOME_CANVAS_ITEM_NEED_CLIP = 1 << 10,
+ GNOME_CANVAS_ITEM_NEED_VIS = 1 << 11,
+ GNOME_CANVAS_ITEM_AFFINE_FULL = 1 << 12
+};
+
+/* Update flags for items */
+enum {
+ GNOME_CANVAS_UPDATE_REQUESTED = 1 << 0,
+ GNOME_CANVAS_UPDATE_AFFINE = 1 << 1,
+ GNOME_CANVAS_UPDATE_CLIP = 1 << 2,
+ GNOME_CANVAS_UPDATE_VISIBILITY = 1 << 3,
+ GNOME_CANVAS_UPDATE_IS_VISIBLE = 1 << 4 /* Deprecated. FIXME: remove this */
+};
+
+/* Data for rendering in antialiased mode */
+typedef struct {
+ /* 24-bit RGB buffer for rendering */
+ guchar *buf;
+
+ /* Rectangle describing the rendering area */
+ ArtIRect rect;
+
+ /* Rowstride for the buffer */
+ int buf_rowstride;
+
+ /* Background color, given as 0xrrggbb */
+ guint32 bg_color;
+
+ /* Invariant: at least one of the following flags is true. */
+
+ /* Set when the render rectangle area is the solid color bg_color */
+ unsigned int is_bg : 1;
+
+ /* Set when the render rectangle area is represented by the buf */
+ unsigned int is_buf : 1;
+} GnomeCanvasBuf;
+
+
+#define GNOME_TYPE_CANVAS_ITEM (gnome_canvas_item_get_type ())
+#define GNOME_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_ITEM, GnomeCanvasItem))
+#define GNOME_CANVAS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_ITEM, GnomeCanvasItemClass))
+#define GNOME_IS_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_ITEM))
+#define GNOME_IS_CANVAS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_ITEM))
+#define GNOME_CANVAS_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_ITEM, GnomeCanvasItemClass))
+
+
+struct _GnomeCanvasItem {
+ GtkObject object;
+
+ /* Parent canvas for this item */
+ GnomeCanvas *canvas;
+
+ /* Parent canvas group for this item (a GnomeCanvasGroup) */
+ GnomeCanvasItem *parent;
+
+ /* If NULL, assumed to be the identity tranform. If flags does not have
+ * AFFINE_FULL, then a two-element array containing a translation. If
+ * flags contains AFFINE_FULL, a six-element array containing an affine
+ * transformation.
+ */
+ double *xform;
+
+ /* Bounding box for this item (in canvas coordinates) */
+ double x1, y1, x2, y2;
+};
+
+struct _GnomeCanvasItemClass {
+ 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
+ * affine, if used, is a pointer to a 6-element array of doubles. The
+ * update method also recomputes the bounding box of the item.
+ */
+ void (* update) (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+
+ /* Realize an item -- create GCs, etc. */
+ void (* realize) (GnomeCanvasItem *item);
+
+ /* Unrealize an item */
+ void (* unrealize) (GnomeCanvasItem *item);
+
+ /* Map an item - normally only need by items with their own GdkWindows */
+ void (* map) (GnomeCanvasItem *item);
+
+ /* Unmap an item */
+ void (* unmap) (GnomeCanvasItem *item);
+
+ /* Return the microtile coverage of the item */
+ ArtUta *(* coverage) (GnomeCanvasItem *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) (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height);
+
+ /* Render the item over the buffer given. The buf data structure
+ * contains both a pointer to a packed 24-bit RGB array, and the
+ * coordinates. This method is only used for antialiased canvases.
+ *
+ * TODO: figure out where clip paths fit into the rendering framework.
+ */
+ void (* render) (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+
+ /* 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) (GnomeCanvasItem *item, double x, double y, int cx, int cy,
+ GnomeCanvasItem **actual_item);
+
+ /* Fetch the item's bounding box (need not be exactly tight). This
+ * should be in item-relative coordinates.
+ */
+ void (* bounds) (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+ /* Signal: an event occurred for an item of this type. The (x, y)
+ * coordinates are in the canvas world coordinate system.
+ */
+ gboolean (* event) (GnomeCanvasItem *item, GdkEvent *event);
+
+ /* Reserved for future expansion */
+ gpointer spare_vmethods [4];
+};
+
+
+GType gnome_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.
+ */
+GnomeCanvasItem *gnome_canvas_item_new (GnomeCanvasGroup *parent, GType type,
+ const gchar *first_arg_name, ...);
+
+/* Constructors for use in derived classes and language wrappers */
+void gnome_canvas_item_construct (GnomeCanvasItem *item, GnomeCanvasGroup *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 gnome_canvas_item_set (GnomeCanvasItem *item, const gchar *first_arg_name, ...);
+
+/* Used only for language wrappers and the like */
+void gnome_canvas_item_set_valist (GnomeCanvasItem *item,
+ const gchar *first_arg_name, va_list args);
+
+/* Move an item by the specified amount */
+void gnome_canvas_item_move (GnomeCanvasItem *item, double dx, double dy);
+
+/* Apply a relative affine transformation to the item. */
+void gnome_canvas_item_affine_relative (GnomeCanvasItem *item, const double affine[6]);
+
+/* Apply an absolute affine transformation to the item. */
+void gnome_canvas_item_affine_absolute (GnomeCanvasItem *item, const double affine[6]);
+
+/* Raise an item in the z-order of its parent group by the specified number of
+ * positions.
+ */
+void gnome_canvas_item_raise (GnomeCanvasItem *item, int positions);
+
+/* Lower an item in the z-order of its parent group by the specified number of
+ * positions.
+ */
+void gnome_canvas_item_lower (GnomeCanvasItem *item, int positions);
+
+/* Raise an item to the top of its parent group's z-order. */
+void gnome_canvas_item_raise_to_top (GnomeCanvasItem *item);
+
+/* Lower an item to the bottom of its parent group's z-order */
+void gnome_canvas_item_lower_to_bottom (GnomeCanvasItem *item);
+
+/* Show an item (make it visible). If the item is already shown, it has no
+ * effect.
+ */
+void gnome_canvas_item_show (GnomeCanvasItem *item);
+
+/* Hide an item (make it invisible). If the item is already invisible, it has
+ * no effect.
+ */
+void gnome_canvas_item_hide (GnomeCanvasItem *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 gnome_canvas_item_grab (GnomeCanvasItem *item, unsigned int event_mask,
+ GdkCursor *cursor, guint32 etime);
+
+/* Ungrabs the mouse -- the specified item must be the same that was passed to
+ * gnome_canvas_item_grab(). Time is a proper X event time parameter.
+ */
+void gnome_canvas_item_ungrab (GnomeCanvasItem *item, guint32 etime);
+
+/* These functions convert from a coordinate system to another. "w" is world
+ * coordinates and "i" is item coordinates.
+ */
+void gnome_canvas_item_w2i (GnomeCanvasItem *item, double *x, double *y);
+void gnome_canvas_item_i2w (GnomeCanvasItem *item, double *x, double *y);
+
+/* Gets the affine transform that converts from item-relative coordinates to
+ * world coordinates.
+ */
+void gnome_canvas_item_i2w_affine (GnomeCanvasItem *item, double affine[6]);
+
+/* Gets the affine transform that converts from item-relative coordinates to
+ * canvas pixel coordinates.
+ */
+void gnome_canvas_item_i2c_affine (GnomeCanvasItem *item, double affine[6]);
+
+/* 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 gnome_canvas_item_reparent (GnomeCanvasItem *item, GnomeCanvasGroup *new_group);
+
+/* Used to send all of the keystroke events to a specific item as well as
+ * GDK_FOCUS_CHANGE events.
+ */
+void gnome_canvas_item_grab_focus (GnomeCanvasItem *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 gnome_canvas_item_get_bounds (GnomeCanvasItem *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 gnome_canvas_item_request_update (GnomeCanvasItem *item);
+
+
+/* GnomeCanvasGroup - 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 gnome_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 GNOME_TYPE_CANVAS_GROUP (gnome_canvas_group_get_type ())
+#define GNOME_CANVAS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_GROUP, GnomeCanvasGroup))
+#define GNOME_CANVAS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_GROUP, GnomeCanvasGroupClass))
+#define GNOME_IS_CANVAS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_GROUP))
+#define GNOME_IS_CANVAS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_GROUP))
+#define GNOME_CANVAS_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_GROUP, GnomeCanvasGroupClass))
+
+
+struct _GnomeCanvasGroup {
+ GnomeCanvasItem item;
+
+ /* Children of the group */
+ GList *item_list;
+ GList *item_list_end;
+};
+
+struct _GnomeCanvasGroupClass {
+ GnomeCanvasItemClass parent_class;
+};
+
+
+GType gnome_canvas_group_get_type (void) G_GNUC_CONST;
+
+
+/*** GnomeCanvas ***/
+
+
+#define GNOME_TYPE_CANVAS (gnome_canvas_get_type ())
+#define GNOME_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS, GnomeCanvas))
+#define GNOME_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS, GnomeCanvasClass))
+#define GNOME_IS_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS))
+#define GNOME_IS_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS))
+#define GNOME_CANVAS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS, GnomeCanvasClass))
+
+
+struct _GnomeCanvas {
+ GtkLayout layout;
+
+ /* Root canvas group */
+ GnomeCanvasItem *root;
+
+ /* Area that needs redrawing, stored as a microtile array */
+ ArtUta *redraw_area;
+
+ /* The item containing the mouse pointer, or NULL if none */
+ GnomeCanvasItem *current_item;
+
+ /* Item that is about to become current (used to track deletions and such) */
+ GnomeCanvasItem *new_current_item;
+
+ /* Item that holds a pointer grab, or NULL if none */
+ GnomeCanvasItem *grabbed_item;
+
+ /* If non-NULL, the currently focused item */
+ GnomeCanvasItem *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;
+
+ /* Area that is being redrawn. Contains (x1, y1) but not (x2, y2).
+ * Specified in canvas pixel coordinates.
+ */
+ int redraw_x1, redraw_y1;
+ int redraw_x2, redraw_y2;
+
+ /* Offsets of the temprary drawing pixmap */
+ int draw_xofs, draw_yofs;
+
+ /* 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 scroll region 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;
+
+ /* 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;
+
+ /* Whether the canvas is in antialiased mode or not */
+ unsigned int aa : 1;
+
+ /* Which dither mode to use for antialiased mode drawing */
+ GdkRgbDither dither;
+};
+
+struct _GnomeCanvasClass {
+ GtkLayoutClass parent_class;
+
+ /* Draw the background for the area given. This method is only used
+ * for non-antialiased canvases.
+ */
+ void (* draw_background) (GnomeCanvas *canvas, GdkDrawable *drawable,
+ int x, int y, int width, int height);
+
+ /* Render the background for the buffer given. The buf data structure
+ * contains both a pointer to a packed 24-bit RGB array, and the
+ * coordinates. This method is only used for antialiased canvases.
+ */
+ void (* render_background) (GnomeCanvas *canvas, GnomeCanvasBuf *buf);
+
+ /* Private Virtual methods for groping the canvas inside bonobo */
+ void (* request_update) (GnomeCanvas *canvas);
+
+ /* Reserved for future expansion */
+ gpointer spare_vmethods [4];
+};
+
+
+GType gnome_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 gnome_canvas_set_scroll_region() soon after calling this
+ * function to set the desired scrolling limits for the canvas.
+ */
+GtkWidget *gnome_canvas_new (void);
+
+/* Creates a new antialiased empty canvas. You should push the GdkRgb colormap
+ * and visual for this.
+ */
+#ifndef GNOME_EXCLUDE_EXPERIMENTAL
+GtkWidget *gnome_canvas_new_aa (void);
+#endif
+
+/* Returns the root canvas item group of the canvas */
+GnomeCanvasGroup *gnome_canvas_root (GnomeCanvas *canvas);
+
+/* Sets the limits of the scrolling region, in world coordinates */
+void gnome_canvas_set_scroll_region (GnomeCanvas *canvas,
+ double x1, double y1, double x2, double y2);
+
+/* Gets the limits of the scrolling region, in world coordinates */
+void gnome_canvas_get_scroll_region (GnomeCanvas *canvas,
+ double *x1, double *y1, double *x2, double *y2);
+
+/* Whether the canvas centers the scroll region if it is smaller than the window */
+void gnome_canvas_set_center_scroll_region (GnomeCanvas *canvas, gboolean center_scroll_region);
+
+/* Returns whether the canvas is set to center the scroll region if it is smaller than the window */
+gboolean gnome_canvas_get_center_scroll_region (GnomeCanvas *canvas);
+
+/* Sets the number of pixels that correspond to one unit in world coordinates */
+void gnome_canvas_set_pixels_per_unit (GnomeCanvas *canvas, double n);
+
+/* Scrolls the canvas to the specified offsets, given in canvas pixel coordinates */
+void gnome_canvas_scroll_to (GnomeCanvas *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 gnome_canvas_get_scroll_offsets (GnomeCanvas *canvas, int *cx, int *cy);
+
+/* Requests that the canvas be repainted immediately instead of in the idle
+ * loop.
+ */
+void gnome_canvas_update_now (GnomeCanvas *canvas);
+
+/* Returns the item that is at the specified position in world coordinates, or
+ * NULL if no item is there.
+ */
+GnomeCanvasItem *gnome_canvas_get_item_at (GnomeCanvas *canvas, double x, double y);
+
+/* For use only by item type implementations. Request that the canvas eventually
+ * redraw the specified region. The region is specified as a microtile
+ * array. This function takes over responsibility for freeing the uta argument.
+ */
+void gnome_canvas_request_redraw_uta (GnomeCanvas *canvas, ArtUta *uta);
+
+/* 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 gnome_canvas_request_redraw (GnomeCanvas *canvas, int x1, int y1, int x2, int y2);
+
+/* Gets the affine transform that converts world coordinates into canvas pixel
+ * coordinates.
+ */
+void gnome_canvas_w2c_affine (GnomeCanvas *canvas, double affine[6]);
+
+/* 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 gnome_canvas_w2c (GnomeCanvas *canvas, double wx, double wy, int *cx, int *cy);
+void gnome_canvas_w2c_d (GnomeCanvas *canvas, double wx, double wy, double *cx, double *cy);
+void gnome_canvas_c2w (GnomeCanvas *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.
+ */
+void gnome_canvas_window_to_world (GnomeCanvas *canvas,
+ double winx, double winy, double *worldx, double *worldy);
+
+/* This is the inverse of gnome_canvas_window_to_world() */
+void gnome_canvas_world_to_window (GnomeCanvas *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 gnome_canvas_get_color (GnomeCanvas *canvas, const char *spec, GdkColor *color);
+
+/* Allocates a color from the RGB value passed into this function. */
+gulong gnome_canvas_get_color_pixel (GnomeCanvas *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 gnome_canvas_set_stipple_origin (GnomeCanvas *canvas, GdkGC *gc);
+
+/* Controls the dithering used when the canvas renders.
+ * Only applicable to antialiased canvases - ignored by non-antialiased canvases.
+ */
+void gnome_canvas_set_dither (GnomeCanvas *canvas, GdkRgbDither dither);
+
+/* Returns the dither mode of an antialiased canvas.
+ * Only applicable to antialiased canvases - ignored by non-antialiased canvases.
+ */
+GdkRgbDither gnome_canvas_get_dither (GnomeCanvas *canvas);
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/libgnomecanvas.h b/libgnomecanvas/libgnomecanvas.h
new file mode 100644
index 0000000000..93e21fa4bd
--- /dev/null
+++ b/libgnomecanvas/libgnomecanvas.h
@@ -0,0 +1,48 @@
+/*
+ * 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 LIBGNOMECANVAS_H
+#define LIBGNOMECANVAS_H
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-line.h>
+#include <libgnomecanvas/gnome-canvas-text.h>
+#include <libgnomecanvas/gnome-canvas-rich-text.h>
+#include <libgnomecanvas/gnome-canvas-polygon.h>
+#include <libgnomecanvas/gnome-canvas-pixbuf.h>
+#include <libgnomecanvas/gnome-canvas-widget.h>
+#include <libgnomecanvas/gnome-canvas-rect-ellipse.h>
+#include <libgnomecanvas/gnome-canvas-bpath.h>
+#include <libgnomecanvas/gnome-canvas-util.h>
+#include <libgnomecanvas/gnome-canvas-clipgroup.h>
+
+G_BEGIN_DECLS
+
+GType gnome_canvas_points_get_type (void);
+#define GNOME_TYPE_CANVAS_POINTS gnome_canvas_points_get_type()
+
+G_END_DECLS
+
+#endif /* LIBGNOMECANVAS_H */
diff --git a/libgnomecanvas/libgnomecanvastypes.c b/libgnomecanvas/libgnomecanvastypes.c
new file mode 100644
index 0000000000..9ea59664c8
--- /dev/null
+++ b/libgnomecanvas/libgnomecanvastypes.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 <libgnomecanvas/libgnomecanvas.h>
+
+GType
+gnome_canvas_points_get_type (void)
+{
+ static GType type_canvas_points = 0;
+
+ if (!type_canvas_points)
+ type_canvas_points = g_boxed_type_register_static
+ ("GnomeCanvasPoints",
+ (GBoxedCopyFunc) gnome_canvas_points_ref,
+ (GBoxedFreeFunc) gnome_canvas_points_unref);
+
+ return type_canvas_points;
+}