[cairo-commit] goocanvas/src goocanvas.c, NONE, 1.1 goocanvasitemmodel.c, NONE, 1.1 goocanvasitemmodel.h, NONE, 1.1 goocanvasstyle.c, NONE, 1.1 goocanvasstyle.h, NONE, 1.1 goocanvaswidget.c, NONE, 1.1 goocanvaswidget.h, NONE, 1.1

Damon Chaplin commit at pdx.freedesktop.org
Wed Nov 29 10:27:10 PST 2006


Committed by: damon

Update of /cvs/cairo/goocanvas/src
In directory kemper:/tmp/cvs-serv29333/src

Added Files:
	goocanvas.c goocanvasitemmodel.c goocanvasitemmodel.h 
	goocanvasstyle.c goocanvasstyle.h goocanvaswidget.c 
	goocanvaswidget.h 
Log Message:
added bunch of new files, part of new model/view architecture


--- NEW FILE: goocanvas.c ---
/*
 * GooCanvas. Copyright (C) 2005 Damon Chaplin.
 * Released under the GNU LGPL license. See COPYING for details.
 *
 * goocanvas.c - the main canvas widget.
 */

/**
 * SECTION: goocanvas
 * @Title: GooCanvas
 * @Short_Description: the main canvas widget.
 *
 * #GooCanvas is the main widget containing a number of canvas items.
 *
 *
 * Here is a simple example:
 *
 * <informalexample><programlisting>
 *  &num;include &lt;goocanvas.h&gt;
[...3172 lines suppressed...]
{
  GooCanvas *canvas;
  GList *tmp_list;
  GooCanvasWidget *witem;

  g_return_if_fail (GOO_IS_CANVAS (container));
  g_return_if_fail (callback != NULL);

  canvas = GOO_CANVAS (container);

  tmp_list = canvas->widget_items;
  while (tmp_list)
    {
      witem = tmp_list->data;
      tmp_list = tmp_list->next;

      if (witem->widget)
	(* callback) (witem->widget, callback_data);
    }
}

--- NEW FILE: goocanvasitemmodel.c ---
/*
 * GooCanvas. Copyright (C) 2005 Damon Chaplin.
 * Released under the GNU LGPL license. See COPYING for details.
 *
 * goocanvasitem.c - interface for canvas items & groups.
 */

/**
 * SECTION:goocanvasitemmodel
 * @Title: GooCanvasItemModel
 * @Short_Description: the interface for canvas item models.
 *
 * #GooCanvasItemModel defines the interface that models for canvas items must
 * implement, and contains methods for operating on canvas item models.
 */
#include <config.h>
#include <math.h>
#include <glib/gi18n-lib.h>
#include <gtk/gtk.h>
#include "goocanvasprivate.h"
#include <goocanvasenumtypes.h>
#include "goocanvasitemmodel.h"
#include "goocanvasutils.h"
#include "goocanvasmarshal.h"


static const char *animation_key = "GooCanvasItemModelAnimation";

enum {
  CHILD_ADDED,
  CHILD_MOVED,
  CHILD_REMOVED,
  CHANGED,

  LAST_SIGNAL
};

static guint item_model_signals[LAST_SIGNAL] = { 0 };

static void goo_canvas_item_model_base_init (gpointer g_class);
extern void _goo_canvas_style_init (void);


GType
goo_canvas_item_model_get_type (void)
{
  static GType item_model_type = 0;

  if (!item_model_type)
    {
      static const GTypeInfo item_model_info =
      {
        sizeof (GooCanvasItemModelIface),  /* class_size */
	goo_canvas_item_model_base_init,   /* base_init */
	NULL,			           /* base_finalize */
      };

      item_model_type = g_type_register_static (G_TYPE_INTERFACE,
						"GooCanvasItemModel",
						&item_model_info, 0);

      g_type_interface_add_prerequisite (item_model_type, G_TYPE_OBJECT);
    }

  return item_model_type;
}



static void
goo_canvas_item_model_base_init (gpointer g_iface)
{
  static gboolean initialized = FALSE;
  
  if (!initialized)
    {
      GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);

      /**
       * GooCanvasItemModel::child-added
       * @model: the item that received the signal.
       * @child_num: the index of the new child.
       *
       * Emitted when a child has been added to the container item.
       */
      item_model_signals[CHILD_ADDED] =
	g_signal_new ("child-added",
		      iface_type,
		      G_SIGNAL_RUN_LAST,
		      G_STRUCT_OFFSET (GooCanvasItemModelIface, child_added),
		      NULL, NULL,
		      goo_canvas_marshal_VOID__INT,
		      G_TYPE_NONE, 1,
		      G_TYPE_INT);

      /**
       * GooCanvasItemModel::child-moved
       * @model: the item that received the signal.
       * @old_child_num: the old index of the child.
       * @new_child_num: the new index of the child.
       *
       * Emitted when a child has been moved in the stacking order of a
       * container item.
       */
      item_model_signals[CHILD_MOVED] =
	g_signal_new ("child-moved",
		      iface_type,
		      G_SIGNAL_RUN_LAST,
		      G_STRUCT_OFFSET (GooCanvasItemModelIface, child_moved),
		      NULL, NULL,
		      goo_canvas_marshal_VOID__INT_INT,
		      G_TYPE_NONE, 2,
		      G_TYPE_INT, G_TYPE_INT);

      /**
       * GooCanvasItemModel::child-removed
       * @model: the item that received the signal.
       * @child_num: the index of the child that was removed.
       *
       * Emitted when a child has been removed from the container item.
       */
      item_model_signals[CHILD_REMOVED] =
	g_signal_new ("child-removed",
		      iface_type,
		      G_SIGNAL_RUN_LAST,
		      G_STRUCT_OFFSET (GooCanvasItemModelIface, child_removed),
		      NULL, NULL,
		      goo_canvas_marshal_VOID__INT,
		      G_TYPE_NONE, 1,
		      G_TYPE_INT);

      /**
       * GooCanvasItemModel::changed
       * @model: the item that received the signal.
       * @recompute_bounds: if the bounds of the item need to be recomputed.
       *
       * Emitted when the item has been changed.
       */
      item_model_signals[CHANGED] =
	g_signal_new ("changed",
		      iface_type,
		      G_SIGNAL_RUN_LAST,
		      G_STRUCT_OFFSET (GooCanvasItemModelIface, changed),
		      NULL, NULL,
		      goo_canvas_marshal_VOID__BOOLEAN,
		      G_TYPE_NONE, 1,
		      G_TYPE_BOOLEAN);


      g_object_interface_install_property (g_iface,
					   g_param_spec_object ("parent",
								_("Parent"),
								_("The parent item"),
								GOO_TYPE_CANVAS_ITEM,
								G_PARAM_READWRITE));

      g_object_interface_install_property (g_iface,
					   g_param_spec_enum ("visibility",
							      _("Visibility"),
							      _("When the canvas item is visible"),
							      GOO_TYPE_CANVAS_ITEM_VISIBILITY,
							      GOO_CANVAS_ITEM_VISIBLE,
							      G_PARAM_READWRITE));

      g_object_interface_install_property (g_iface,
					   g_param_spec_double ("visibility-threshold",
								_("Visibility Threshold"),
								_("The scale threshold at which the item becomes visible"),
								0.0,
								G_MAXDOUBLE,
								0.0,
								G_PARAM_READWRITE));

      g_object_interface_install_property (g_iface,
					   g_param_spec_boxed ("transform",
							       _("Transform"),
							       _("The transformation matrix of the item"),
							       GOO_TYPE_CAIRO_MATRIX,
							       G_PARAM_READWRITE));

      g_object_interface_install_property (g_iface,
					   g_param_spec_flags ("pointer-events",
							       _("Pointer Events"),
							       _("Specifies when the item receives pointer events"),
							       GOO_TYPE_CANVAS_POINTER_EVENTS,
							       GOO_CANVAS_EVENTS_VISIBLE_PAINTED,
							       G_PARAM_READWRITE));

      g_object_interface_install_property (g_iface,
					   g_param_spec_string ("title",
								_("Title"),
								_("A short context-rich description of the item for use by assistive technologies"),
								NULL,
								G_PARAM_READWRITE));

      g_object_interface_install_property (g_iface,
					   g_param_spec_string ("description",
								_("Description"),
								_("A description of the item for use by assistive technologies"),
								NULL,
								G_PARAM_READWRITE));

      g_object_interface_install_property (g_iface,
					   g_param_spec_boolean ("can-focus",
								 _("Can Focus"),
								 _("If the item can take the keyboard focus"),
								 FALSE,
								 G_PARAM_READWRITE));

      _goo_canvas_style_init ();

      initialized = TRUE;
    }
}


/**
 * goo_canvas_item_model_add_child:
 * @model: the container to add the item to.
 * @child: the item to add.
 * @position: the position of the item, or -1 to place it last (at the top of
 *  the stacking order).
 * 
 * Adds a child item to a container item at the given stack position.
 **/
void
goo_canvas_item_model_add_child      (GooCanvasItemModel  *model,
				      GooCanvasItemModel  *child,
				      gint                 position)
{
  GooCanvasItemModelIface *iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);

  g_return_if_fail (iface->add_child != NULL);

  iface->add_child (model, child, position);
}


/**
 * goo_canvas_item_model_move_child:
 * @model: a container item.
 * @old_position: the current position of the child item.
 * @new_position: the new position of the child item.
 * 
 * Moves a child item to a new stack position within the container.
 **/
void
goo_canvas_item_model_move_child     (GooCanvasItemModel  *model,
				      gint                 old_position,
				      gint                 new_position)
{
  GooCanvasItemModelIface *iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);

  g_return_if_fail (iface->move_child != NULL);

  iface->move_child (model, old_position, new_position);
}


/**
 * goo_canvas_item_model_remove_child:
 * @model: a container item.
 * @child_num: the position of the child item to remove.
 * 
 * Removes the child item at the given position.
 **/
void
goo_canvas_item_model_remove_child   (GooCanvasItemModel  *model,
				      gint                 child_num)
{
  GooCanvasItemModelIface *iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);

  g_return_if_fail (iface->remove_child != NULL);

  iface->remove_child (model, child_num);
}


/**
 * goo_canvas_item_model_find_child:
 * @model: a container item.
 * @child: the child item to find.
 * 
 * Attempts to find the given child item with the container's stack.
 * 
 * Returns: the position of the given @child item, or -1 if it isn't found.
 **/
gint
goo_canvas_item_model_find_child     (GooCanvasItemModel *model,
				      GooCanvasItemModel *child)
{
  GooCanvasItemModel *item;
  int n_children, i;

  /* Find the current position of item and above. */
  n_children = goo_canvas_item_model_get_n_children (model);
  for (i = 0; i < n_children; i++)
    {
      item = goo_canvas_item_model_get_child (model, i);
      if (child == item)
	return i;
    }
  return -1;
}


/**
 * goo_canvas_item_model_is_container:
 * @model: an item model.
 * 
 * Tests to see if the given item is a container.
 * 
 * Returns: %TRUE if the item is a container.
 **/
gboolean
goo_canvas_item_model_is_container (GooCanvasItemModel       *model)
{
  GooCanvasItemModelIface *iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);

  return iface->get_n_children ? TRUE : FALSE;
}


/**
 * goo_canvas_item_model_get_n_children:
 * @model: a container item.
 * 
 * Gets the number of children of the container.
 * 
 * Returns: the number of children.
 **/
gint
goo_canvas_item_model_get_n_children (GooCanvasItemModel       *model)
{
  GooCanvasItemModelIface *iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);

  return iface->get_n_children ? iface->get_n_children (model) : 0;
}


/**
 * goo_canvas_item_model_get_child:
 * @model: a container item.
 * @child_num: the position of a child in the container's stack.
 * 
 * Gets the child item at the given stack position.
 * 
 * Returns: the child item at the given stack position.
 **/
GooCanvasItemModel*
goo_canvas_item_model_get_child (GooCanvasItemModel  *model,
				 gint                 child_num)
{
  GooCanvasItemModelIface *iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);

  return iface->get_child ? iface->get_child (model, child_num) : NULL;
}


/**
 * goo_canvas_item_model_get_parent:
 * @model: an item model.
 * 
 * Gets the parent of the given item.
 * 
 * Returns: the parent item, or %NULL if the item has no parent.
 **/
GooCanvasItemModel*
goo_canvas_item_model_get_parent  (GooCanvasItemModel *model)
{
  return GOO_CANVAS_ITEM_MODEL_GET_IFACE (model)->get_parent (model);
}


/**
 * goo_canvas_item_model_set_parent:
 * @model: an item model.
 * @parent: the new parent item.
 * 
 * Sets the parent of the item.
 **/
void
goo_canvas_item_model_set_parent (GooCanvasItemModel *model,
				  GooCanvasItemModel *parent)
{
  GOO_CANVAS_ITEM_MODEL_GET_IFACE (model)->set_parent (model, parent);
}


/**
 * goo_canvas_item_model_raise:
 * @model: an item model.
 * @above: the item to raise @model above, or %NULL to raise @model to the top
 *  of the stack.
 * 
 * Raises an item in the stacking order.
 **/
void
goo_canvas_item_model_raise          (GooCanvasItemModel *model,
				      GooCanvasItemModel *above)
{
  GooCanvasItemModel *parent, *child;
  int n_children, i, model_pos = -1, above_pos = -1;

  parent = goo_canvas_item_model_get_parent (model);
  if (!parent || model == above)
    return;

  /* Find the current position of model and above. */
  n_children = goo_canvas_item_model_get_n_children (parent);
  for (i = 0; i < n_children; i++)
    {
      child = goo_canvas_item_model_get_child (parent, i);
      if (child == model)
	model_pos = i;
      if (child == above)
	above_pos = i;
    }

  /* If above is NULL we raise the model to the top of the stack. */
  if (!above)
    above_pos = n_children - 1;

  g_return_if_fail (model_pos != -1);
  g_return_if_fail (above_pos != -1);

  /* Only move the model if the new position is higher in the stack. */
  if (above_pos > model_pos)
    goo_canvas_item_model_move_child (parent, model_pos, above_pos);
}


/**
 * goo_canvas_item_model_lower:
 * @model: an item model.
 * @below: the item to lower @model below, or %NULL to lower @model to the
 *  bottom of the stack.
 * 
 * Lowers an item in the stacking order.
 **/
void
goo_canvas_item_model_lower          (GooCanvasItemModel *model,
				      GooCanvasItemModel *below)
{
  GooCanvasItemModel *parent, *child;
  int n_children, i, model_pos = -1, below_pos = -1;

  parent = goo_canvas_item_model_get_parent (model);
  if (!parent || model == below)
    return;

  /* Find the current position of model and below. */
  n_children = goo_canvas_item_model_get_n_children (parent);
  for (i = 0; i < n_children; i++)
    {
      child = goo_canvas_item_model_get_child (parent, i);
      if (child == model)
	model_pos = i;
      if (child == below)
	below_pos = i;
    }

  /* If below is NULL we lower the model to the bottom of the stack. */
  if (!below)
    below_pos = 0;

  g_return_if_fail (model_pos != -1);
  g_return_if_fail (below_pos != -1);

  /* Only move the model if the new position is lower in the stack. */
  if (below_pos < model_pos)
    goo_canvas_item_model_move_child (parent, model_pos, below_pos);
}


/**
 * goo_canvas_item_model_get_transform:
 * @model: an item model.
 * 
 * Gets the transformation matrix of an item model.
 * 
 * Returns: the item's transformation matrix.
 **/
cairo_matrix_t*
goo_canvas_item_model_get_transform  (GooCanvasItemModel *model)
{
  GooCanvasItemModelIface *iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);

  return iface->get_transform ? iface->get_transform (model) : NULL;
}


/**
 * goo_canvas_item_model_set_transform:
 * @model: an item model.
 * @matrix: the new transformation matrix, or %NULL to reset the
 *  transformation to the identity matrix.
 * 
 * Sets the transformation matrix of an item model.
 **/
void
goo_canvas_item_model_set_transform  (GooCanvasItemModel *model,
				      cairo_matrix_t     *matrix)
{
  GOO_CANVAS_ITEM_MODEL_GET_IFACE (model)->set_transform (model, matrix);
}


/**
 * goo_canvas_item_model_translate:
 * @model: an item model.
 * @tx: the amount to move the origin in the horizontal direction.
 * @ty: the amount to move the origin in the vertical direction.
 * 
 * Translates the origin of the item's coordinate system by the given amounts.
 **/
void
goo_canvas_item_model_translate      (GooCanvasItemModel *model,
				      double              tx,
				      double              ty)
{
  GooCanvasItemModelIface *iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);
  cairo_matrix_t *matrix, new_matrix = { 1, 0, 0, 1, 0, 0 };

  matrix = iface->get_transform (model);
  if (matrix)
    new_matrix = *matrix;
  cairo_matrix_translate (&new_matrix, tx, ty);
  iface->set_transform (model, &new_matrix);
}


/**
 * goo_canvas_item_model_scale:
 * @model: an item model.
 * @sx: the amount to scale the horizontal axis.
 * @sy: the amount to scale the vertical axis.
 * 
 * Scales the item's coordinate system by the given amounts.
 **/
void
goo_canvas_item_model_scale          (GooCanvasItemModel *model,
				      double              sx,
				      double              sy)
{
  GooCanvasItemModelIface *iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);
  cairo_matrix_t *matrix, new_matrix = { 1, 0, 0, 1, 0, 0 };

  matrix = iface->get_transform (model);
  if (matrix)
    new_matrix = *matrix;
  cairo_matrix_scale (&new_matrix, sx, sy);
  iface->set_transform (model, &new_matrix);
}


/**
 * goo_canvas_item_model_rotate:
 * @model: an item model.
 * @degrees: the clockwise angle of rotation.
 * @cx: the x coordinate of the origin of the rotation.
 * @cy: the y coordinate of the origin of the rotation.
 * 
 * Rotates the item's coordinate system by the given amount, about the given
 * origin.
 **/
void
goo_canvas_item_model_rotate         (GooCanvasItemModel *model,
				      double              degrees,
				      double              cx,
				      double              cy)
{
  GooCanvasItemModelIface *iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);
  cairo_matrix_t *matrix, new_matrix = { 1, 0, 0, 1, 0, 0 };
  double radians = degrees * (M_PI / 180);

  matrix = iface->get_transform (model);
  if (matrix)
    new_matrix = *matrix;
  cairo_matrix_translate (&new_matrix, cx, cy);
  cairo_matrix_rotate (&new_matrix, radians);
  cairo_matrix_translate (&new_matrix, -cx, -cy);
  iface->set_transform (model, &new_matrix);
}


/**
 * goo_canvas_item_model_skew_x:
 * @model: an item model.
 * @degrees: the skew angle.
 * @cx: the x coordinate of the origin of the skew transform.
 * @cy: the y coordinate of the origin of the skew transform.
 * 
 * Skews the item's coordinate system along the x axis by the given amount,
 * about the given origin.
 **/
void
goo_canvas_item_model_skew_x         (GooCanvasItemModel *model,
				      double              degrees,
				      double              cx,
				      double              cy)
{
  GooCanvasItemModelIface *iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);
  cairo_matrix_t *matrix, tmp, new_matrix = { 1, 0, 0, 1, 0, 0 };
  double radians = degrees * (M_PI / 180);

  matrix = iface->get_transform (model);
  if (matrix)
    new_matrix = *matrix;
  cairo_matrix_translate (&new_matrix, cx, cy);
  cairo_matrix_init (&tmp, 1, 0, tan (radians), 1, 0, 0);
  cairo_matrix_multiply (&new_matrix, &tmp, &new_matrix);
  cairo_matrix_translate (&new_matrix, -cx, -cy);
  iface->set_transform (model, &new_matrix);
}


/**
 * goo_canvas_item_model_skew_y:
 * @model: an item model.
 * @degrees: the skew angle.
 * @cx: the x coordinate of the origin of the skew transform.
 * @cy: the y coordinate of the origin of the skew transform.
 * 
 * Skews the item's coordinate system along the y axis by the given amount,
 * about the given origin.
 **/
void
goo_canvas_item_model_skew_y         (GooCanvasItemModel *model,
				      double              degrees,
				      double              cx,
				      double              cy)
{
  GooCanvasItemModelIface *iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);
  cairo_matrix_t *matrix, tmp, new_matrix = { 1, 0, 0, 1, 0, 0 };
  double radians = degrees * (M_PI / 180);

  matrix = iface->get_transform (model);
  if (matrix)
    new_matrix = *matrix;
  cairo_matrix_translate (&new_matrix, cx, cy);
  cairo_matrix_init (&tmp, 1, tan (radians), 0, 1, 0, 0);
  cairo_matrix_multiply (&new_matrix, &tmp, &new_matrix);
  cairo_matrix_translate (&new_matrix, -cx, -cy);
  iface->set_transform (model, &new_matrix);
}


/**
 * goo_canvas_item_model_get_style:
 * @model: an item model.
 * 
 * Gets the item's style. If the item doesn't have its own style it will return
 * its parent's style.
 * 
 * Returns: the item's style.
 **/
GooCanvasStyle*
goo_canvas_item_model_get_style      (GooCanvasItemModel   *model)
{
  GooCanvasItemModelIface *iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);

  return iface->get_style ? iface->get_style (model) : NULL;
}


/**
 * goo_canvas_item_model_set_style:
 * @model: an item model.
 * @style: a style.
 * 
 * Sets the item's style, by copying the properties from the given style.
 **/
void
goo_canvas_item_model_set_style      (GooCanvasItemModel *model,
				      GooCanvasStyle     *style)
{
  GooCanvasItemModelIface *iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);

  if (iface->set_style)
    iface->set_style (model, style);
}


typedef struct _GooCanvasItemModelAnimation  GooCanvasItemModelAnimation;
struct _GooCanvasItemModelAnimation
{
  GooCanvasAnimateType type;
  GooCanvasItemModel *model;
  int num_steps_left, total_steps;
  cairo_matrix_t start, step;
  gboolean forward;
  guint timeout_id;
};


static void
goo_canvas_item_model_free_animation (GooCanvasItemModelAnimation *anim)
{
  if (anim->timeout_id)
    {
      g_source_remove (anim->timeout_id);
      anim->timeout_id = 0;
    }

  g_free (anim);
}


static gboolean
goo_canvas_item_model_animate_cb (GooCanvasItemModelAnimation *anim)
{
  GooCanvasItemModelIface *iface;
  cairo_matrix_t *matrix, new_matrix = { 1, 0, 0, 1, 0, 0 };
  gboolean keep_source = TRUE;

  GDK_THREADS_ENTER ();

  iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (anim->model);
  matrix = iface->get_transform (anim->model);
  if (matrix)
    new_matrix = *matrix;

  new_matrix.xx += anim->step.xx;
  new_matrix.yx += anim->step.yx;
  new_matrix.xy += anim->step.xy;
  new_matrix.yy += anim->step.yy;
  new_matrix.x0 += anim->step.x0;
  new_matrix.y0 += anim->step.y0;

  iface->set_transform (anim->model, &new_matrix);

  if (--anim->num_steps_left == 0)
    {
      switch (anim->type)
	{
	case GOO_CANVAS_ANIMATE_RESET:
	  /* Reset the transform to the initial value. */
	  /* FIXME: Need to wait one cycle at finish position? */
	  iface->set_transform (anim->model, &anim->start);

	  /* Fall through.. */
	case GOO_CANVAS_ANIMATE_FREEZE:
	  keep_source = FALSE;
	  anim->timeout_id = 0;
	  /* This will result in call to goo_canvas_item_model_free_animation()
	     above. We've set the timeout_id to 0 so it isn't removed twice. */
	  g_object_set_data (G_OBJECT (anim->model), animation_key, NULL);
	  break;

	case GOO_CANVAS_ANIMATE_RESTART:
	  iface->set_transform (anim->model, &anim->start);
	  break;

	case GOO_CANVAS_ANIMATE_BOUNCE:
	  /* Switch all the step values around. */
	  anim->step.xx = -anim->step.xx;
	  anim->step.yx = -anim->step.yx;
	  anim->step.xy = -anim->step.xy;
	  anim->step.yy = -anim->step.yy;
	  anim->step.x0 = -anim->step.x0;
	  anim->step.y0 = -anim->step.y0;

	  /* FIXME: don't need this? Might be wise to reset to the initial
	     transform each time we restart, to avoid little errors. */
	  anim->forward = !anim->forward;
	  break;
	}

      anim->num_steps_left = anim->total_steps;
    }

  GDK_THREADS_LEAVE ();

  /* Return FALSE to remove the timeout handler when we are finished. */
  return keep_source;
}


/**
 * goo_canvas_item_model_animate:
 * @model: an item model.
 * @x: the final x offset from the current position.
 * @y: the final y offset from the current position.
 * @scale: the final scale of the item.
 * @degrees: the final rotation of the item.
 * @duration: the duration of the animation, in milliseconds (1/1000ths of a
 *  second).
 * @step_time: the time between each animation step, in milliseconds.
 * @type: specifies what happens when the animation finishes.
 * 
 * Animates an item from its current position to the given offsets, scale
 * and rotation.
 **/
void
goo_canvas_item_model_animate        (GooCanvasItemModel  *model,
				      double               x,
				      double               y,
				      double               scale,
				      double               degrees,
				      int                  duration,
				      int                  step_time,
				      GooCanvasAnimateType type)
{
  GooCanvasItemModelIface *iface = GOO_CANVAS_ITEM_MODEL_GET_IFACE (model);
  cairo_matrix_t *matrix, identity_matrix = { 1, 0, 0, 1, 0, 0 };
  cairo_matrix_t new_matrix = { 1, 0, 0, 1, 0, 0 };
  GooCanvasItemModelAnimation *anim;
  double radians = degrees * (M_PI / 180);

  matrix = iface->get_transform (model);
  if (!matrix)
    matrix = &identity_matrix;

  cairo_matrix_translate (&new_matrix, x, y);
  cairo_matrix_scale (&new_matrix, scale, scale);
  cairo_matrix_rotate (&new_matrix, radians);

  anim = g_new (GooCanvasItemModelAnimation, 1);
  anim->type = type;
  anim->model = model;
  anim->total_steps = anim->num_steps_left = duration / step_time;
  anim->start = *matrix;
  anim->step.xx = (new_matrix.xx - matrix->xx) / anim->total_steps; 
  anim->step.yx = (new_matrix.yx - matrix->yx) / anim->total_steps; 
  anim->step.xy = (new_matrix.xy - matrix->xy) / anim->total_steps; 
  anim->step.yy = (new_matrix.yy - matrix->yy) / anim->total_steps; 
  anim->step.x0 = (new_matrix.x0 - matrix->x0) / anim->total_steps; 
  anim->step.y0 = (new_matrix.y0 - matrix->y0) / anim->total_steps; 
  anim->forward = TRUE;

  /* Store a pointer to the new animation in the item. This will automatically
     stop any current animation and free it. */
  g_object_set_data_full (G_OBJECT (model), animation_key, anim,
			  (GDestroyNotify) goo_canvas_item_model_free_animation);

  anim->timeout_id = g_timeout_add (step_time,
				    (GSourceFunc) goo_canvas_item_model_animate_cb,
				    anim);
}


/**
 * goo_canvas_item_model_stop_animation:
 * @model: an item model.
 * 
 * Stops any current animation for the given item, leaving it at its current
 * position.
 **/
void
goo_canvas_item_model_stop_animation (GooCanvasItemModel *model)
{
  /* This will result in a call to goo_canvas_item_model_free_animation(). */
  g_object_set_data (G_OBJECT (model), animation_key, NULL);
}

--- NEW FILE: goocanvasitemmodel.h ---
/*
 * GooCanvas. Copyright (C) 2005 Damon Chaplin.
 * Released under the GNU LGPL license. See COPYING for details.
 *
 * goocanvasitemmodel.h - interface for canvas item models.
 */
#ifndef __GOO_CANVAS_ITEM_MODEL_H__
#define __GOO_CANVAS_ITEM_MODEL_H__

#include <gtk/gtk.h>
#include "goocanvasitem.h"

G_BEGIN_DECLS


#define GOO_TYPE_CANVAS_ITEM_MODEL            (goo_canvas_item_model_get_type ())
#define GOO_CANVAS_ITEM_MODEL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOO_TYPE_CANVAS_ITEM_MODEL, GooCanvasItemModel))
#define GOO_CANVAS_ITEM_MODEL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GOO_TYPE_CANVAS_ITEM_MODEL, GooCanvasItemModelClass))
#define GOO_IS_CANVAS_ITEM_MODEL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOO_TYPE_CANVAS_ITEM_MODEL))
#define GOO_CANVAS_ITEM_MODEL_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GOO_TYPE_CANVAS_ITEM_MODEL, GooCanvasItemModelIface))


/**
 * GooCanvasItemModel
 *
 * #GooCanvasItemModel is a typedef used for objects that implement the
 * #GooCanvasItemModel interface.
 *
 * (There is no actual #GooCanvasItemModel struct, since it is only an interface.
 * But using '#GooCanvasItemModel' is more helpful than using '#GObject'.)
 */
/* The typedef is in goocanvasitem.h */
/*typedef struct _GooCanvasItemModel       GooCanvasItemModel;*/


/**
 * GooCanvasItemModelIface
 *
 * #GooCanvasItemModelIFace holds the virtual methods that make up the
 * #GooCanvasItemModel interface.
 */
typedef struct _GooCanvasItemModelIface  GooCanvasItemModelIface;

struct _GooCanvasItemModelIface
{
  /*< private >*/
  GTypeInterface base_iface;

  /*< public >*/
  /* Virtual methods that group items must implement. */
  gint		       (* get_n_children)  (GooCanvasItemModel  *model);
  GooCanvasItemModel*  (* get_child)	   (GooCanvasItemModel  *model,
					    gint                 child_num);

  /* Virtual methods that group items may implement. */
  void                 (* add_child)       (GooCanvasItemModel  *model,
					    GooCanvasItemModel  *child,
					    gint                 position);
  void                 (* move_child)      (GooCanvasItemModel  *model,
					    gint                 old_position,
					    gint                 new_position);
  void                 (* remove_child)    (GooCanvasItemModel  *model,
					    gint                 child_num);
  void                 (*get_child_property) (GooCanvasItemModel *model,
					      GooCanvasItemModel *child,
					      guint               property_id,
					      GValue             *value,
					      GParamSpec         *pspec);
  void                 (*set_child_property) (GooCanvasItemModel *item,
					      GooCanvasItemModel *child,
					      guint               property_id,
					      const GValue       *value,
					      GParamSpec         *pspec);

  /* Virtual methods that all canvas items must implement. */
  GooCanvasItemModel*  (* get_parent)	  (GooCanvasItemModel   *model);
  void                 (* set_parent)	  (GooCanvasItemModel   *model,
					   GooCanvasItemModel   *parent);

  cairo_matrix_t*      (* get_transform)  (GooCanvasItemModel   *model);
  void                 (* set_transform)  (GooCanvasItemModel   *model,
					   cairo_matrix_t       *matrix);
  GooCanvasStyle*      (* get_style)      (GooCanvasItemModel   *model);
  void                 (* set_style)      (GooCanvasItemModel   *model,
					   GooCanvasStyle       *style);

  GooCanvasItem*       (* create_item)	  (GooCanvasItemModel   *model,
					   GooCanvas            *canvas);

  /* Signals. */
  void                 (* child_added)	  (GooCanvasItemModel   *model,
					   gint                  child_num);
  void                 (* child_moved)	  (GooCanvasItemModel   *model,
					   gint                  old_child_num,
					   gint                  new_child_num);
  void                 (* child_removed)  (GooCanvasItemModel   *model,
					   gint                  child_num);
  void                 (* changed)	  (GooCanvasItemModel   *model,
					   gboolean              recompute_bounds);
};


GType               goo_canvas_item_model_get_type       (void) G_GNUC_CONST;


/*
 * Group functions - these should only be called on container models.
 */
gint                goo_canvas_item_model_get_n_children (GooCanvasItemModel *model);
GooCanvasItemModel* goo_canvas_item_model_get_child      (GooCanvasItemModel *model,
							  gint                child_num);
void                goo_canvas_item_model_add_child      (GooCanvasItemModel *model,
							  GooCanvasItemModel *child,
							  gint                position);
void                goo_canvas_item_model_move_child     (GooCanvasItemModel *model,
							  gint                old_position,
							  gint                new_position);
void                goo_canvas_item_model_remove_child   (GooCanvasItemModel *model,
							  gint                child_num);
gint                goo_canvas_item_model_find_child     (GooCanvasItemModel *model,
							  GooCanvasItemModel *child);

void     goo_canvas_item_model_child_get	  (GooCanvasItemModel   *model,
						   GooCanvasItemModel   *child,
						   ...) G_GNUC_NULL_TERMINATED;
void     goo_canvas_item_model_child_set	  (GooCanvasItemModel   *model,
						   GooCanvasItemModel   *child,
						   ...) G_GNUC_NULL_TERMINATED;
void     goo_canvas_item_model_child_get_valist   (GooCanvasItemModel   *model,
						   GooCanvasItemModel   *child,
						   va_list	         var_args);
void     goo_canvas_item_model_child_set_valist   (GooCanvasItemModel   *model,
						   GooCanvasItemModel   *child,
						   va_list	         var_args);
void	 goo_canvas_item_model_child_get_property (GooCanvasItemModel   *model,
						   GooCanvasItemModel   *child,
						   const gchar	        *property,
						   GValue	        *value);
void	 goo_canvas_item_model_child_set_property (GooCanvasItemModel   *model,
						   GooCanvasItemModel   *child,
						   const gchar	        *property,
						   const GValue	        *value);

/*
 * Model functions - these are safe to call on all models.
 */
GooCanvasItemModel* goo_canvas_item_model_get_parent     (GooCanvasItemModel *model);
void                goo_canvas_item_model_set_parent	 (GooCanvasItemModel *model,
							  GooCanvasItemModel *parent);
gboolean            goo_canvas_item_model_is_container   (GooCanvasItemModel *model);

void                goo_canvas_item_model_raise          (GooCanvasItemModel *model,
							  GooCanvasItemModel *above);
void                goo_canvas_item_model_lower          (GooCanvasItemModel *model,
							  GooCanvasItemModel *below);

cairo_matrix_t*     goo_canvas_item_model_get_transform  (GooCanvasItemModel *model);
void                goo_canvas_item_model_set_transform  (GooCanvasItemModel *model,
							  cairo_matrix_t     *matrix);

void                goo_canvas_item_model_translate      (GooCanvasItemModel *model,
							  double              tx,
							  double              ty);
void                goo_canvas_item_model_scale          (GooCanvasItemModel *model,
							  double              sx,
							  double              sy);
void                goo_canvas_item_model_rotate         (GooCanvasItemModel *model,
							  double              degrees,
							  double              cx,
							  double              cy);
void                goo_canvas_item_model_skew_x         (GooCanvasItemModel *model,
							  double              degrees,
							  double              cx,
							  double              cy);
void                goo_canvas_item_model_skew_y         (GooCanvasItemModel *model,
							  double              degrees,
							  double              cx,
							  double              cy);

GooCanvasStyle*     goo_canvas_item_model_get_style      (GooCanvasItemModel *model);
void                goo_canvas_item_model_set_style      (GooCanvasItemModel *model,
							  GooCanvasStyle  *style);

void                goo_canvas_item_model_animate        (GooCanvasItemModel *model,
							  double              x,
							  double              y,
							  double              scale,
							  double              degrees,
							  gint                duration,
							  gint                step_time,
							  GooCanvasAnimateType type);
void                goo_canvas_item_model_stop_animation (GooCanvasItemModel *model);


G_END_DECLS

#endif /* __GOO_CANVAS_ITEM_MODEL_H__ */

--- NEW FILE: goocanvasstyle.c ---
/*
 * GooCanvas. Copyright (C) 2005-6 Damon Chaplin.
 * Released under the GNU LGPL license. See COPYING for details.
 *
 * goocanvasstyle.c - cascading styles.
 */

/**
 * SECTION:goocanvasstyle
 * @Title: GooCanvasStyle
 * @Short_Description: support for cascading style properties for canvas items.
 *
 * #GooCanvasStyle provides support for cascading style properties for canvas
 * items. It is intended to be used when implementing new canvas items.
 *
 * Style properties are identified by a unique #GQuark, and contain
 * arbitrary data stored in a #GValue.
 *
 * #GooCanvasStyle also provides a few convenience functions such as
 * goo_canvas_style_set_stroke_options() and
 * goo_canvas_style_set_fill_options() which efficiently apply an item's
 * standard style properties to the given cairo_t.
 */
#include <config.h>
#include <gtk/gtk.h>
#include "goocanvasstyle.h"
#include "goocanvasutils.h"

static void goo_canvas_style_finalize (GObject *object);

G_DEFINE_TYPE (GooCanvasStyle, goo_canvas_style, G_TYPE_OBJECT)


/* Create GQuarks for the basic properties. This is called by
   goo_canvas_style_class_init(), goo_canvas_item_base_init() and
   goo_canvas_item_model_base_init() to try to ensure the GQuarks are
   initialized before they are needed. */
void
_goo_canvas_style_init (void)
{
  static gboolean initialized = FALSE;
  
  if (!initialized)
    {
      goo_canvas_style_stroke_pattern_id = g_quark_from_static_string ("GooCanvasStyle:stroke_pattern");
      goo_canvas_style_fill_pattern_id = g_quark_from_static_string ("GooCanvasStyle:fill_pattern");
      goo_canvas_style_fill_rule_id = g_quark_from_static_string ("GooCanvasStyle:fill_rule");
      goo_canvas_style_operator_id = g_quark_from_static_string ("GooCanvasStyle:operator");
      goo_canvas_style_antialias_id = g_quark_from_static_string ("GooCanvasStyle:antialias");
      goo_canvas_style_line_width_id = g_quark_from_static_string ("GooCanvasStyle:line_width");
      goo_canvas_style_line_cap_id = g_quark_from_static_string ("GooCanvasStyle:line_cap");
      goo_canvas_style_line_join_id = g_quark_from_static_string ("GooCanvasStyle:line_join");
      goo_canvas_style_line_join_miter_limit_id = g_quark_from_static_string ("GooCanvasStyle:line_join_miter_limit");
      goo_canvas_style_line_dash_id = g_quark_from_static_string ("GooCanvasStyle:line_dash");
      goo_canvas_style_font_desc_id = g_quark_from_static_string ("GooCanvasStyle:font_desc");

      initialized = TRUE;
    }
}


static void
goo_canvas_style_class_init (GooCanvasStyleClass *klass)
{
  GObjectClass *gobject_class = (GObjectClass*) klass;

  gobject_class->finalize = goo_canvas_style_finalize;

  _goo_canvas_style_init ();
}


static void
goo_canvas_style_init (GooCanvasStyle *style)
{
  style->properties = g_array_new (0, 0, sizeof (GooCanvasStyleProperty));
}


GooCanvasStyle*
goo_canvas_style_new (void)
{
  return GOO_CANVAS_STYLE (g_object_new (GOO_TYPE_CANVAS_STYLE, NULL));
}


static void
goo_canvas_style_finalize (GObject *object)
{
  GooCanvasStyle *style = (GooCanvasStyle*) object;
  GooCanvasStyleProperty *property;
  gint i;

  if (style->parent)
    {
      g_object_unref (style->parent);
      style->parent = NULL;
    }

  /* Free the property GValues. */
  for (i = 0; i < style->properties->len; i++)
    {
      property = &g_array_index (style->properties, GooCanvasStyleProperty, i);
      g_value_unset (&property->value);
    }
  g_array_free (style->properties, TRUE);

  G_OBJECT_CLASS (goo_canvas_style_parent_class)->finalize (object);
}


GooCanvasStyle*
goo_canvas_style_copy               (GooCanvasStyle *style)
{
  GooCanvasStyle *copy;
  GooCanvasStyleProperty *property;
  gint i;

  copy = goo_canvas_style_new ();

  for (i = 0; i < style->properties->len; i++)
    {
      property = &g_array_index (style->properties, GooCanvasStyleProperty, i);
      goo_canvas_style_set_property (copy, property->id, &property->value);
    }

  return copy;
}


/**
 * goo_canvas_style_get_parent:
 * @style: a style.
 * 
 * Gets the parent of the style.
 * 
 * Returns: the parent of the given style, or %NULL.
 **/
GooCanvasStyle*
goo_canvas_style_get_parent         (GooCanvasStyle *style)
{
  return style->parent;
}


/**
 * goo_canvas_style_set_parent:
 * @style: a style.
 * @parent: the new parent.
 * 
 * Sets the parent of the style.
 **/
void
goo_canvas_style_set_parent         (GooCanvasStyle *style,
				     GooCanvasStyle *parent)
{
  if (style->parent == parent)
    return;

  if (style->parent)
    g_object_unref (style->parent);

  style->parent = parent;

  if (style->parent)
    g_object_ref (style->parent);
}


/**
 * goo_canvas_style_get_property:
 * @style: a style.
 * @property_id: the property identifier.
 * 
 * Gets the value of a property.
 * 
 * Returns: the property value, or %NULL if it isn't set.
 **/
GValue*
goo_canvas_style_get_property       (GooCanvasStyle *style,
				     GQuark          property_id)
{
  GooCanvasStyleProperty *property;
  gint i;

  /* Step up the hierarchy of styles until we find the property. Note that
     if style is passed in as NULL we simply return NULL. */
  while (style)
    {
      for (i = 0; i < style->properties->len; i++)
	{
	  property = &g_array_index (style->properties, GooCanvasStyleProperty,
				     i);
	  if (property->id == property_id)
	    return &property->value;
	}

      style = style->parent;
    }

  return NULL;
}


/**
 * goo_canvas_style_set_property:
 * @style: a style.
 * @property_id: the property identifier.
 * @value: the value of the property.
 * 
 * Sets a property in the style, replacing any current setting.
 **/
void
goo_canvas_style_set_property	    (GooCanvasStyle *style,
				     GQuark          property_id,
				     const GValue   *value)
{
  GooCanvasStyleProperty *property, new_property = { 0 };
  gint i;

  /* See if the property is already set. */
  for (i = 0; i < style->properties->len; i++)
    {
      property = &g_array_index (style->properties, GooCanvasStyleProperty, i);
      if (property->id == property_id)
	{
	  /* If the new value is NULL, remove the property setting, otherwise
	     update the property value. */
	  if (value)
	    {
	      g_value_copy (value, &property->value);
	    }
	  else
	    {
	      g_value_unset (&property->value);
	      g_array_remove_index_fast (style->properties, i);
	    }

	  return;
	}
    }

  /* The property isn't set, so append a new property. */
  if (value)
    {
      new_property.id = property_id;
      g_value_init (&new_property.value, G_VALUE_TYPE (value));
      g_value_copy (value, &new_property.value);
      g_array_append_val (style->properties, new_property);
    }
}


/**
 * goo_canvas_style_set_stroke_options:
 * @style: a style.
 * @cr: a cairo context.
 * 
 * Sets the standard cairo stroke options using the given style.
 * 
 * Returns: %TRUE if a paint source is set, or %FALSE if the stroke should
 * be skipped.
 **/
gboolean
goo_canvas_style_set_stroke_options (GooCanvasStyle *style,
				     cairo_t        *cr)
{
  GooCanvasStyleProperty *property;
  gboolean operator_set = FALSE, antialias_set = FALSE;
  gboolean stroke_pattern_set = FALSE, line_width_set = FALSE;
  gboolean line_cap_set = FALSE, line_join_set = FALSE;
  gboolean miter_limit_set = FALSE, line_dash_set = FALSE;
  gboolean source_set = FALSE, need_stroke = TRUE;
  gint i;

  if (!style)
    return TRUE;

  /* Step up the hierarchy of styles looking for the properties. */
  while (style)
    {
      for (i = 0; i < style->properties->len; i++)
	{
	  property = &g_array_index (style->properties, GooCanvasStyleProperty,
				     i);

	  if (property->id == goo_canvas_style_operator_id && !operator_set)
	    {
	      cairo_set_operator (cr, property->value.data[0].v_long);
	      operator_set = TRUE;
	    }
	  else if (property->id == goo_canvas_style_antialias_id && !antialias_set)
	    {
	      cairo_set_antialias (cr, property->value.data[0].v_long);
	      antialias_set = TRUE;
	    }
	  else if (property->id == goo_canvas_style_stroke_pattern_id && !stroke_pattern_set)
	    {
	      if (property->value.data[0].v_pointer)
		{
		  cairo_set_source (cr, property->value.data[0].v_pointer);
		  source_set = TRUE;
		}
	      else
		{
		  /* If the stroke pattern has been explicitly set to NULL,
		     then we don't need to do the stroke. */
		  need_stroke = FALSE;
		}
	      stroke_pattern_set = TRUE;
	    }
	  else if (property->id == goo_canvas_style_line_width_id && !line_width_set)
	    {
	      cairo_set_line_width (cr, property->value.data[0].v_double);
	      line_width_set = TRUE;
	    }
	  else if (property->id == goo_canvas_style_line_cap_id && !line_cap_set)
	    {
	      cairo_set_line_cap (cr, property->value.data[0].v_long);
	      line_cap_set = TRUE;
	    }
	  else if (property->id == goo_canvas_style_line_join_id && !line_join_set)
	    {
	      cairo_set_line_join (cr, property->value.data[0].v_long);
	      line_join_set = TRUE;
	    }
	  else if (property->id == goo_canvas_style_line_join_miter_limit_id && !miter_limit_set)
	    {
	      cairo_set_miter_limit (cr, property->value.data[0].v_double);
	      miter_limit_set = TRUE;
	    }
	  else if (property->id == goo_canvas_style_line_dash_id && !line_dash_set)
	    {
	      GooCanvasLineDash *dash = property->value.data[0].v_pointer;
	      cairo_set_dash (cr, dash->dashes, dash->num_dashes,
			      dash->dash_offset);
	      line_dash_set = TRUE;
	    }
	}

      style = style->parent;
    }

  /* If a stroke pattern hasn't been set in the style we reset the source to
     black, just in case a fill pattern was used for the item. */
  if (!source_set)
    cairo_set_source_rgb (cr, 0, 0, 0);

  return need_stroke;
}


/**
 * goo_canvas_style_set_fill_options:
 * @style: a style.
 * @cr: a cairo context.
 * 
 * Sets the standard cairo fill options using the given style.
 * 
 * Returns: %TRUE if a paint source is set, or %FALSE if the fill should
 * be skipped.
 **/
gboolean
goo_canvas_style_set_fill_options   (GooCanvasStyle *style,
				     cairo_t        *cr)
{
  GooCanvasStyleProperty *property;
  gboolean operator_set = FALSE, antialias_set = FALSE;
  gboolean fill_rule_set = FALSE, fill_pattern_set = FALSE;
  gint i;

  if (!style)
    return FALSE;

  /* Step up the hierarchy of styles looking for the properties. */
  while (style)
    {
      for (i = 0; i < style->properties->len; i++)
	{
	  property = &g_array_index (style->properties, GooCanvasStyleProperty,
				     i);

	  if (property->id == goo_canvas_style_operator_id && !operator_set)
	    {
	      cairo_set_operator (cr, property->value.data[0].v_long);
	      operator_set = TRUE;
	    }
	  else if (property->id == goo_canvas_style_antialias_id && !antialias_set)
	    {
	      cairo_set_antialias (cr, property->value.data[0].v_long);
	      antialias_set = TRUE;
	    }
	  else if (property->id == goo_canvas_style_fill_rule_id && !fill_rule_set)
	    {
	      cairo_set_fill_rule (cr, property->value.data[0].v_long);
	      fill_rule_set = TRUE;
	    }
	  else if (property->id == goo_canvas_style_fill_pattern_id && !fill_pattern_set)
	    {
	      cairo_set_source (cr, property->value.data[0].v_pointer);
	      fill_pattern_set = TRUE;
	    }
	}

      style = style->parent;
    }

  return fill_pattern_set;
}

--- NEW FILE: goocanvasstyle.h ---
/*
 * GooCanvas. Copyright (C) 2005-6 Damon Chaplin.
 * Released under the GNU LGPL license. See COPYING for details.
 *
 * goocanvasstyle.h - cascading styles.
 */
#ifndef __GOO_CANVAS_STYLE_H__
#define __GOO_CANVAS_STYLE_H__

#include <gtk/gtk.h>

G_BEGIN_DECLS


/* GQuarks for the basic properties. */
GQuark goo_canvas_style_stroke_pattern_id;
GQuark goo_canvas_style_fill_pattern_id;
GQuark goo_canvas_style_fill_rule_id;
GQuark goo_canvas_style_operator_id;
GQuark goo_canvas_style_antialias_id;
GQuark goo_canvas_style_line_width_id;
GQuark goo_canvas_style_line_cap_id;
GQuark goo_canvas_style_line_join_id;
GQuark goo_canvas_style_line_join_miter_limit_id;
GQuark goo_canvas_style_line_dash_id;
GQuark goo_canvas_style_font_desc_id;


typedef struct _GooCanvasStyleProperty GooCanvasStyleProperty;
struct _GooCanvasStyleProperty
{
  /* The property id. */
  GQuark id;

  /* The property value. */
  GValue value;
};


#define GOO_TYPE_CANVAS_STYLE            (goo_canvas_style_get_type ())
#define GOO_CANVAS_STYLE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOO_TYPE_CANVAS_STYLE, GooCanvasStyle))
#define GOO_CANVAS_STYLE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GOO_TYPE_CANVAS_STYLE, GooCanvasStyleClass))
#define GOO_IS_CANVAS_STYLE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOO_TYPE_CANVAS_STYLE))
#define GOO_IS_CANVAS_STYLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GOO_TYPE_CANVAS_STYLE))
#define GOO_CANVAS_STYLE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GOO_TYPE_CANVAS_STYLE, GooCanvasStyleClass))


typedef struct _GooCanvasStyle       GooCanvasStyle;
typedef struct _GooCanvasStyleClass  GooCanvasStyleClass;

struct _GooCanvasStyle
{
  GObject parent_object;

  /* The parent style. */
  GooCanvasStyle *parent;

  /* An array of GooCanvasStyleProperty. */
  GArray *properties;
};

struct _GooCanvasStyleClass
{
  GObjectClass parent_class;
};


GType           goo_canvas_style_get_type           (void) G_GNUC_CONST;
GooCanvasStyle* goo_canvas_style_new                (void);
GooCanvasStyle* goo_canvas_style_copy               (GooCanvasStyle *style);

GooCanvasStyle* goo_canvas_style_get_parent         (GooCanvasStyle *style);
void            goo_canvas_style_set_parent         (GooCanvasStyle *style,
						     GooCanvasStyle *parent);

GValue*         goo_canvas_style_get_property       (GooCanvasStyle *style,
						     GQuark          property_id);
void            goo_canvas_style_set_property	    (GooCanvasStyle *style,
						     GQuark          property_id,
						     const GValue   *value);

/* Convenience functions to set the standard cairo stroke and fill options. */
gboolean        goo_canvas_style_set_stroke_options (GooCanvasStyle *style,
						     cairo_t        *cr);
gboolean        goo_canvas_style_set_fill_options   (GooCanvasStyle *style,
						     cairo_t        *cr);

G_END_DECLS

#endif /* __GOO_CANVAS_STYLE_H__ */

--- NEW FILE: goocanvaswidget.c ---
/*
 * GooCanvas. Copyright (C) 2005-6 Damon Chaplin.
 * Released under the GNU LGPL license. See COPYING for details.
 *
 * goocanvaswidget.c - wrapper item for an embedded GtkWidget.
 */

/**
 * SECTION:goocanvaswidget
 * @Title: GooCanvasWidget
 * @Short_Description: an embedded widget item.
 *
 * GooCanvasWidget provides support for placing any GtkWidget in the canvas.
 *
 * Note that there are a number of limitations in the use of #GooCanvasWidget:
 *
 * 1) It doesn't support any transformation besides simple translation.
 * This means you can't scale a canvas with a #GooCanvasWidget in it.
 *
 * 2) It doesn't support layering, so you can't place other items beneath
 * or above the #GooCanvasWidget.
 *
 * 3) It doesn't support rendering of widgets to a given cairo_t, which
 * means you can't output the widget to a pdf or postscript file.
 *
 * 4) It doesn't have a model/view variant like the other standard items,
 * so it can only be used in a simple canvas without a model.
 */
#include <config.h>
#include <glib/gi18n-lib.h>
#include <gtk/gtk.h>
#include "goocanvas.h"
#include "goocanvasatk.h"

enum {
  PROP_0,

  PROP_WIDGET,
  PROP_X,
  PROP_Y,
  PROP_WIDTH,
  PROP_HEIGHT,
  PROP_ANCHOR,
  PROP_VISIBILITY
};


static void canvas_item_interface_init      (GooCanvasItemIface  *iface);
static void goo_canvas_widget_finalize      (GObject             *object);
static void goo_canvas_widget_get_property  (GObject             *object,
					     guint                param_id,
					     GValue              *value,
					     GParamSpec          *pspec);
static void goo_canvas_widget_set_property  (GObject             *object,
					     guint                param_id,
					     const GValue        *value,
					     GParamSpec          *pspec);

G_DEFINE_TYPE_WITH_CODE (GooCanvasWidget, goo_canvas_widget,
			 GOO_TYPE_CANVAS_ITEM_SIMPLE,
			 G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM,
						canvas_item_interface_init))


static void
goo_canvas_widget_class_init (GooCanvasWidgetClass *klass)
{
  GObjectClass *gobject_class = (GObjectClass*) klass;

  gobject_class->finalize = goo_canvas_widget_finalize;

  gobject_class->get_property = goo_canvas_widget_get_property;
  gobject_class->set_property = goo_canvas_widget_set_property;

  /* Register our accessible factory, but only if accessibility is enabled. */
  if (!ATK_IS_NO_OP_OBJECT_FACTORY (atk_registry_get_factory (atk_get_default_registry (), GTK_TYPE_WIDGET)))
    {
      atk_registry_set_factory_type (atk_get_default_registry (),
				     GOO_TYPE_CANVAS_WIDGET,
				     goo_canvas_widget_accessible_factory_get_type ());
    }

  g_object_class_install_property (gobject_class, PROP_WIDGET,
				   g_param_spec_object ("widget",
							_("Widget"),
							_("The widget to place in the canvas"),
							GTK_TYPE_WIDGET,
							G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, PROP_X,
				   g_param_spec_double ("x",
							"X",
							_("The x coordinate of the widget"),
							-G_MAXDOUBLE,
							G_MAXDOUBLE, 0.0,
							G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, PROP_Y,
				   g_param_spec_double ("y",
							"Y",
							_("The y coordinate of the widget"),
							-G_MAXDOUBLE,
							G_MAXDOUBLE, 0.0,
							G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, PROP_WIDTH,
				   g_param_spec_double ("width",
							_("Width"),
							_("The width of the widget, or -1 to use its requested width"),
							-G_MAXDOUBLE,
							G_MAXDOUBLE, -1.0,
							G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, PROP_HEIGHT,
				   g_param_spec_double ("height",
							_("Height"),
							_("The height of the widget, or -1 to use its requested height"),
							-G_MAXDOUBLE,
							G_MAXDOUBLE, -1.0,
							G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, PROP_ANCHOR,
				   g_param_spec_enum ("anchor",
						      _("Anchor"),
						      _("How to position the widget relative to the item's x and y coordinate settings"),
						      GTK_TYPE_ANCHOR_TYPE,
						      GTK_ANCHOR_NW,
						      G_PARAM_READWRITE));

  g_object_class_override_property (gobject_class, PROP_VISIBILITY,
				    "visibility");
}


static void
goo_canvas_widget_init (GooCanvasWidget *witem)
{
  /* By default we place the widget at the top-left of the canvas at its
     requested size. */
  witem->x = 0.0;
  witem->y = 0.0;
  witem->width = -1.0;
  witem->height = -1.0;
  witem->anchor = GTK_ANCHOR_NW;
}


/**
 * goo_canvas_widget_new:
 * @parent: the parent item, or %NULL. If a parent is specified, it will assume
 *  ownership of the item, and the item will automatically be freed when it is
 *  removed from the parent. Otherwise call g_object_unref() to free it.
 * @widget: the widget.
 * @x: the x coordinate of the item.
 * @y: the y coordinate of the item.
 * @width: the width of the item, or -1 to use the widget's requested width.
 * @height: the height of the item, or -1 to use the widget's requested height.
 * @...: optional pairs of property names and values, and a terminating %NULL.
 *
 * Creates a new widget item.
 *
 * <!--PARAMETERS-->
 * 
 * Here's an example showing how to create an entry widget centered at (100.0,
 * 100.0):
 *
 * <informalexample><programlisting>
 *  GtkWidget *entry = gtk_entry_new ();
 *  GooCanvasItem *witem = goo_canvas_widget_new (mygroup, entry,
 *                                                100, 100, -1, -1,
 *                                                "anchor", GTK_ANCHOR_CENTER,
 *                                                NULL);
 * </programlisting></informalexample>
 * 
 * Returns: a new widget item.
 **/
GooCanvasItem*
goo_canvas_widget_new               (GooCanvasItem    *parent,
				     GtkWidget        *widget,
				     gdouble           x,
				     gdouble           y,
				     gdouble           width,
				     gdouble           height,
				     ...)
{
  GooCanvasItem *item;
  GooCanvasWidget *witem;
  const char *first_property;
  va_list var_args;

  item = g_object_new (GOO_TYPE_CANVAS_WIDGET, NULL);
  witem = (GooCanvasWidget*) item;

  witem->widget = widget;
  witem->x = x;
  witem->y = y;
  witem->width = width;
  witem->height = height;

  /* The widget defaults to being visible, like the canvas item, but this
     can be overridden by the object property below. */
  if (widget)
    gtk_widget_show (widget);

  va_start (var_args, height);
  first_property = va_arg (var_args, char*);
  if (first_property)
    g_object_set_valist ((GObject*) item, first_property, var_args);
  va_end (var_args);

  if (parent)
    {
      goo_canvas_item_add_child (parent, item, -1);
      g_object_unref (item);
    }

  return item;
}


static void
goo_canvas_widget_set_widget (GooCanvasWidget *witem,
			      GtkWidget       *widget)
{
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) witem;

  if (witem->widget)
    {
      g_object_set_data (G_OBJECT (witem->widget), "goo-canvas-item", NULL);
      gtk_widget_unparent (witem->widget);
      witem->widget = NULL;
    }

  if (widget)
    {
      witem->widget = widget;
      g_object_set_data (G_OBJECT (witem->widget), "goo-canvas-item", witem);

      if (simple->simple_data->visibility == GOO_CANVAS_ITEM_INVISIBLE)
	gtk_widget_hide (widget);
      else
	gtk_widget_show (widget);

      if (simple->canvas)
	{
	  if (GTK_WIDGET_REALIZED (simple->canvas))
	    gtk_widget_set_parent_window (widget,
					  simple->canvas->canvas_window);

	  gtk_widget_set_parent (widget, GTK_WIDGET (simple->canvas));
	}
    }
}


static void
goo_canvas_widget_finalize (GObject *object)
{
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
  GooCanvasWidget *witem = (GooCanvasWidget*) object;

  if (simple->canvas)
    goo_canvas_unregister_widget_item (simple->canvas, witem);

  goo_canvas_widget_set_widget (witem, NULL);

  G_OBJECT_CLASS (goo_canvas_widget_parent_class)->finalize (object);
}


static void
goo_canvas_widget_get_property  (GObject             *object,
				 guint                prop_id,
				 GValue              *value,
				 GParamSpec          *pspec)
{
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
  GooCanvasWidget *witem = (GooCanvasWidget*) object;

  switch (prop_id)
    {
    case PROP_WIDGET:
      g_value_set_object (value, 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_VISIBILITY:
      g_value_set_enum (value, simple->simple_data->visibility);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}


static void
goo_canvas_widget_set_property  (GObject             *object,
				 guint                prop_id,
				 const GValue        *value,
				 GParamSpec          *pspec)
{
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
  GooCanvasWidget *witem = (GooCanvasWidget*) object;

  switch (prop_id)
    {
    case PROP_WIDGET:
      goo_canvas_widget_set_widget (witem, g_value_get_object (value));
      break;
    case PROP_X:
      witem->x = g_value_get_double (value);
      break;
    case PROP_Y:
      witem->y = g_value_get_double (value);
      break;
    case PROP_WIDTH:
      witem->width = g_value_get_double (value);
      break;
    case PROP_HEIGHT:
      witem->height = g_value_get_double (value);
      break;
    case PROP_ANCHOR:
      witem->anchor = g_value_get_enum (value);
      break;
    case PROP_VISIBILITY:
      simple->simple_data->visibility = g_value_get_enum (value);
      if (simple->simple_data->visibility == GOO_CANVAS_ITEM_INVISIBLE)
	gtk_widget_hide (witem->widget);
      else
	gtk_widget_show (witem->widget);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }

  goo_canvas_item_simple_changed (simple, TRUE);
}


static void
goo_canvas_widget_set_canvas  (GooCanvasItem *item,
			       GooCanvas     *canvas)
{
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
  GooCanvasWidget *witem = (GooCanvasWidget*) item;

  if (simple->canvas != canvas)
    {
      if (simple->canvas)
	goo_canvas_unregister_widget_item (simple->canvas, witem);

      simple->canvas = canvas;

      if (simple->canvas)
	{
	  goo_canvas_register_widget_item (simple->canvas, witem);

	  if (witem->widget)
	    {
	      if (GTK_WIDGET_REALIZED (simple->canvas))
		gtk_widget_set_parent_window (witem->widget,
					      simple->canvas->canvas_window);

	      gtk_widget_set_parent (witem->widget,
				     GTK_WIDGET (simple->canvas));
	    }
	}
    }
}


static void
goo_canvas_widget_set_parent (GooCanvasItem  *item,
			      GooCanvasItem  *parent)
{
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
  GooCanvas *canvas;

  simple->parent = parent;
  simple->need_update = TRUE;
  simple->need_entire_subtree_update = TRUE;

  canvas = parent ? goo_canvas_item_get_canvas (parent) : NULL;
  goo_canvas_widget_set_canvas (item, canvas);
}


static void
goo_canvas_widget_update  (GooCanvasItem      *item,
			   gboolean            entire_tree,
			   cairo_t            *cr,
			   GooCanvasBounds    *bounds)
{
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
  GooCanvasItemSimpleData *simple_data = simple->simple_data;
  GooCanvasWidget *witem = (GooCanvasWidget*) item;
  GtkRequisition requisition;
  gdouble width, height;

  if (entire_tree || simple->need_update)
    {
      simple->need_update = FALSE;

      goo_canvas_item_simple_check_style (simple);

      cairo_save (cr);
      if (simple_data->transform)
	cairo_transform (cr, simple_data->transform);

      /* Request a redraw of the existing bounds. */
      goo_canvas_request_redraw (simple->canvas, &simple->bounds);

      if (witem->widget)
	{
	  /* Compute the new bounds. */
	  if (witem->width < 0 || witem->height < 0)
	    {
	      gtk_widget_size_request (witem->widget, &requisition);
	    }

	  simple->bounds.x1 = witem->x;
	  simple->bounds.y1 = witem->y;
	  width = witem->width < 0 ? requisition.width : witem->width;
	  height = witem->height < 0 ? requisition.height : witem->height;

	  switch (witem->anchor)
	    {
	    case GTK_ANCHOR_N:
	    case GTK_ANCHOR_CENTER:
	    case GTK_ANCHOR_S:
	      simple->bounds.x1 -= width / 2.0;
	      break;
	    case GTK_ANCHOR_NE:
	    case GTK_ANCHOR_E:
	    case GTK_ANCHOR_SE:
	      simple->bounds.x1 -= width;
	      break;
	    default:
	      break;
	    }

	  switch (witem->anchor)
	    {
	    case GTK_ANCHOR_W:
	    case GTK_ANCHOR_CENTER:
	    case GTK_ANCHOR_E:
	      simple->bounds.y1 -= height / 2.0;
	      break;
	    case GTK_ANCHOR_SW:
	    case GTK_ANCHOR_S:
	    case GTK_ANCHOR_SE:
	      simple->bounds.y1 -= height;
	      break;
	    default:
	      break;
	    }

	  simple->bounds.x2 = simple->bounds.x1 + width;
	  simple->bounds.y2 = simple->bounds.y1 + height;

	  goo_canvas_item_simple_user_bounds_to_device (simple, cr,
							&simple->bounds);

	  /* Request a redraw of the new bounds. */
	  goo_canvas_request_redraw (simple->canvas, &simple->bounds);

	  /* Queue a resize of the widget so it gets moved. */
	  gtk_widget_queue_resize (witem->widget);
	}
      else
	{
	  simple->bounds.x1 = simple->bounds.y1 = 0.0;
	  simple->bounds.x2 = simple->bounds.y2 = 0.0;
	}

      cairo_restore (cr);
    }

  *bounds = simple->bounds;
}


static void
goo_canvas_widget_paint (GooCanvasItem     *item,
			 cairo_t           *cr,
			 GooCanvasBounds   *bounds,
			 gdouble            scale)
{
  /* Do nothing for now. Maybe render for printing in future. */
}


static GooCanvasItem*
goo_canvas_widget_get_item_at (GooCanvasItem  *item,
			       gdouble         x,
			       gdouble         y,
			       cairo_t        *cr,
			       gboolean        is_pointer_event,
			       gboolean        parent_visible)
{
  /* FIXME: Should we return the widget here? */
  return NULL;
}


static void
canvas_item_interface_init (GooCanvasItemIface *iface)
{
  iface->set_canvas     = goo_canvas_widget_set_canvas;
  iface->set_parent	= goo_canvas_widget_set_parent;
  iface->get_item_at    = goo_canvas_widget_get_item_at;
  iface->update         = goo_canvas_widget_update;
  iface->paint          = goo_canvas_widget_paint;
}

--- NEW FILE: goocanvaswidget.h ---
/*
 * GooCanvas. Copyright (C) 2005-6 Damon Chaplin.
 * Released under the GNU LGPL license. See COPYING for details.
 *
 * goocanvaswidget.h - wrapper item for an embedded GtkWidget.
 */
#ifndef __GOO_CANVAS_WIDGET_H__
#define __GOO_CANVAS_WIDGET_H__

#include <gtk/gtk.h>
#include "goocanvasitemsimple.h"

G_BEGIN_DECLS


#define GOO_TYPE_CANVAS_WIDGET            (goo_canvas_widget_get_type ())
#define GOO_CANVAS_WIDGET(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOO_TYPE_CANVAS_WIDGET, GooCanvasWidget))
#define GOO_CANVAS_WIDGET_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GOO_TYPE_CANVAS_WIDGET, GooCanvasWidgetClass))
#define GOO_IS_CANVAS_WIDGET(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOO_TYPE_CANVAS_WIDGET))
#define GOO_IS_CANVAS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GOO_TYPE_CANVAS_WIDGET))
#define GOO_CANVAS_WIDGET_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GOO_TYPE_CANVAS_WIDGET, GooCanvasWidgetClass))


typedef struct _GooCanvasWidget       GooCanvasWidget;
typedef struct _GooCanvasWidgetClass  GooCanvasWidgetClass;

/**
 * GooCanvasWidget
 *
 * The #GooCanvasWidget-struct struct contains private data only.
 */
struct _GooCanvasWidget
{
  GooCanvasItemSimple parent_object;

  GtkWidget *widget;
  gdouble x, y, width, height;
  GtkAnchorType anchor;
};

struct _GooCanvasWidgetClass
{
  GooCanvasItemSimpleClass parent_class;
};


GType          goo_canvas_widget_get_type          (void) G_GNUC_CONST;
GooCanvasItem* goo_canvas_widget_new               (GooCanvasItem    *parent,
						    GtkWidget        *widget,
						    gdouble           x,
						    gdouble           y,
						    gdouble           width,
						    gdouble           height,
						    ...);

G_END_DECLS

#endif /* __GOO_CANVAS_WIDGET_H__ */



More information about the cairo-commit mailing list