diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2010-06-14 06:37:27 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2010-06-14 09:31:58 +0800 |
commit | e6972011f01eab9f8d0a4584f32ee1e2a00f3231 (patch) | |
tree | 69e2a5e846965c3b369724b825f5b35c6d88056a /libgnomecanvas | |
parent | 7f3377c78a560aa762e04d596b79f847c4acd870 (diff) | |
download | gsoc2013-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')
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, + >K_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; +} |