[cairo-commit] goocanvas/src Makefile.am, 1.2, 1.3 demo-paths.c, NONE, 1.1 demo.c, 1.6, 1.7 goocanvaspath.c, NONE, 1.1 goocanvaspath.h, NONE, 1.1 goocanvaspathview.c, NONE, 1.1 goocanvaspathview.h, NONE, 1.1 goocanvaspolylineview.c, 1.5, 1.6 goocanvaspolylineview.h, 1.1.1.1, 1.2

Damon Chaplin commit at pdx.freedesktop.org
Mon Apr 10 13:30:26 PDT 2006


Committed by: damon

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

Modified Files:
	Makefile.am demo.c goocanvaspolylineview.c 
	goocanvaspolylineview.h 
Added Files:
	demo-paths.c goocanvaspath.c goocanvaspath.h 
	goocanvaspathview.c goocanvaspathview.h 
Log Message:
2006-04-10  Damon Chaplin  <damon at gnome.org>

	* src/goocanvaspathview.[hc]: 
	* src/goocanvaspath.[hc]: new path item and view, that uses the same
	path spec strings as SVG. I think everything works, except the
	elliptical arc, which I haven't finished yet.

	* src/demo-paths.c: new demo page to test paths.



Index: Makefile.am
===================================================================
RCS file: /cvs/cairo/goocanvas/src/Makefile.am,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- Makefile.am	8 Apr 2006 13:14:17 -0000	1.2
+++ Makefile.am	10 Apr 2006 20:30:24 -0000	1.3
@@ -13,7 +13,7 @@
 
 demo_SOURCES = \
 	demo.c demo-fifteen.c demo-scalability.c demo-grabs.c \
-	demo-arrowhead.c demo-features.c demo-events.c \
+	demo-arrowhead.c demo-features.c demo-events.c demo-paths.c \
 	goocanvasmarshal.list \
 	goocanvasellipse.h goocanvasellipse.c \
 	goocanvasellipseview.h goocanvasellipseview.c \
@@ -28,6 +28,8 @@
 	goocanvasmodelsimple.h goocanvasmodelsimple.c \
 	goocanvaspolyline.h goocanvaspolyline.c \
 	goocanvaspolylineview.h goocanvaspolylineview.c \
+	goocanvaspath.h goocanvaspath.c \
+	goocanvaspathview.h goocanvaspathview.c \
 	goocanvasrect.h goocanvasrect.c \
 	goocanvasrectview.h goocanvasrectview.c \
 	goocanvastext.h goocanvastext.c \

--- NEW FILE: demo-paths.c ---
#include <config.h>
#include <string.h>
#include <gtk/gtk.h>
#include "goocanvasmodelsimple.h"
#include "goocanvasview.h"
#include "goocanvasitemview.h"
#include "goocanvasgroup.h"
#include "goocanvasgroupview.h"
#include "goocanvaspath.h"


static void
create_paths (GooCanvasModelSimple   *canvas_model)
{
  GooCanvasItem *root, *path;

  root = goo_canvas_model_get_root_item (GOO_CANVAS_MODEL (canvas_model));

  /* Test the simple commands like moveto and lineto: MmZzLlHhVv. */
  path = goo_canvas_path_new (root, "M 20 20 L 40 40", NULL);
  path = goo_canvas_path_new (root, "M30 20 l20, 20", NULL);
  path = goo_canvas_path_new (root, "M 60 20 H 80", NULL);
  path = goo_canvas_path_new (root, "M60 40 h20", NULL);
  path = goo_canvas_path_new (root, "M 100,20 V 40", NULL);
  path = goo_canvas_path_new (root, "M 120 20 v 20", NULL);
  path = goo_canvas_path_new (root, "M 140 20 h20 v20 h-20 z", NULL);
  path = goo_canvas_path_new (root,
			      "M 180 20 h20 v20 h-20 z m 5,5 h10 v10 h-10 z",
			      "fill-color", "red",
			      "fill-rule", CAIRO_FILL_RULE_EVEN_ODD,
			      NULL);

  path = goo_canvas_path_new (root, "M 220 20 L 260 20 L 240 40 z",
			      "fill-color", "red",
			      "stroke-color", "blue",
			      "line-width", 3.0,
			      NULL);

  /* Test the bezier curve commands: CcSsQqTt. */
  path = goo_canvas_path_new (root,
			      "M20,100 C20,50 100,50 100,100 S180,150 180,100",
			      NULL);

  path = goo_canvas_path_new (root,
			      "M220,100 c0,-50 80,-50 80,0 s80,50 80,0",
			      NULL);

  path = goo_canvas_path_new (root,
			      "M20,200 Q60,130 100,200 T180,200",
			      NULL);

  path = goo_canvas_path_new (root,
			      "M220,200 q40,-70 80,0 t80,0",
			      NULL);

  /* Test the elliptical arc commands: Aa. */

}


GtkWidget *
create_paths_page (void)
{
  GtkWidget *vbox, *alignment, *frame, *label, *canvas;
  GooCanvasModelSimple *canvas_model;

  vbox = gtk_vbox_new (FALSE, 4);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  gtk_widget_show (vbox);

  /* Instructions */

  label = gtk_label_new ("Demo Paths");
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
  /*gtk_widget_show (label);*/

  /* Frame and canvas */

  alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
  gtk_widget_show (alignment);

  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_container_add (GTK_CONTAINER (alignment), frame);
  gtk_widget_show (frame);

  canvas = goo_canvas_view_new ();

  canvas_model = goo_canvas_model_simple_new ();

  gtk_widget_set_size_request (canvas, 600, 450);
  goo_canvas_view_set_bounds (GOO_CANVAS_VIEW (canvas), 0, 0, 600, 450);
  gtk_container_add (GTK_CONTAINER (frame), canvas);
  gtk_widget_show (canvas);

  create_paths (canvas_model);

  goo_canvas_view_set_model (GOO_CANVAS_VIEW (canvas),
			     GOO_CANVAS_MODEL (canvas_model));
  g_object_unref (canvas_model);

  return vbox;
}

Index: demo.c
===================================================================
RCS file: /cvs/cairo/goocanvas/src/demo.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- demo.c	8 Apr 2006 13:14:17 -0000	1.6
+++ demo.c	10 Apr 2006 20:30:24 -0000	1.7
@@ -32,6 +32,7 @@
 GtkWidget *create_canvas_scalability (void);
 GtkWidget *create_grabs_page (void);
 GtkWidget *create_events_page (void);
+GtkWidget *create_paths_page (void);
 
 
 static void
@@ -1268,6 +1269,11 @@
 			    create_events_page (),
 			    gtk_label_new ("Events"));
 #endif
+#if 1
+  gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
+			    create_paths_page (),
+			    gtk_label_new ("Paths"));
+#endif
 
   return window;
 }

--- NEW FILE: goocanvaspath.c ---
/*
 * GooCanvas. Copyright (C) 2005-6 Damon Chaplin.
 * Released under the GNU LGPL license. See COPYING for details.
 *
 * goocanvaspath.c - a path item, very similar to the SVG path element.
 */
#include <config.h>
#include <gtk/gtk.h>
#include "goocanvaspath.h"
#include "goocanvaspathview.h"


enum {
  PROP_0,

  PROP_DATA,
};

static void goo_canvas_path_finalize (GObject *object);
static void item_interface_init (GooCanvasItemIface *iface);
static void goo_canvas_path_get_property (GObject            *object,
					  guint               param_id,
					  GValue             *value,
					  GParamSpec         *pspec);
static void goo_canvas_path_set_property (GObject            *object,
					  guint               param_id,
					  const GValue       *value,
					  GParamSpec         *pspec);

G_DEFINE_TYPE_WITH_CODE (GooCanvasPath, goo_canvas_path,
			 GOO_TYPE_CANVAS_ITEM_SIMPLE,
			 G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM,
						item_interface_init));


static void
goo_canvas_path_class_init (GooCanvasPathClass *klass)
{
  GObjectClass *gobject_class = (GObjectClass*) klass;

  gobject_class->finalize = goo_canvas_path_finalize;

  gobject_class->get_property = goo_canvas_path_get_property;
  gobject_class->set_property = goo_canvas_path_set_property;

  g_object_class_install_property (gobject_class, PROP_DATA,
				   g_param_spec_string ("data",
							NULL, NULL,
							NULL,
							G_PARAM_WRITABLE));
}


static void
goo_canvas_path_init (GooCanvasPath *path)
{

}


static void
goo_canvas_path_finalize (GObject *object)
{
  GooCanvasPath *path = (GooCanvasPath*) object;

  if (path->commands)
    g_array_free (path->commands, TRUE);

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


static void
goo_canvas_path_get_property (GObject              *object,
			      guint                 prop_id,
			      GValue               *value,
			      GParamSpec           *pspec)
{
  /*GooCanvasPath *path = (GooCanvasPath*) object;*/

  switch (prop_id)
    {
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}


static gdouble
parse_double (gchar    **pos,
	      gboolean  *error)
{
  gdouble result;
  gchar *p;

  /* If an error has already occurred, just return. */
  if (*error)
    return 0;

  /* Skip whitespace and commas. */
  p = *pos;
  while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ',')
    p++;

  /* Parse the double, and set pos to the first char after it. */
  result = g_ascii_strtod (p, pos);

  /* If no characters were parsed, set the error flag. */
  if (p == *pos)
    *error = TRUE;

  return result;
}


static gint
parse_flag (gchar    **pos,
	    gboolean  *error)
{
  gint result = 0;
  gchar *p;

  /* If an error has already occurred, just return. */
  if (*error)
    return 0;

  /* Skip whitespace and commas. */
  p = *pos;
  while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ',')
    p++;

  /* The flag must be a '0' or a '1'. */
  if (*p == '0')
    result = 0;
  else if (*p == '1')
    result = 1;
  else
    {
      *error = TRUE;
      return 0;
    }

  *pos = p + 1;

  return result;
}


static void
goo_canvas_path_parse_data (GooCanvasPath *path,
			    const gchar   *path_data)
{
  GooCanvasPathCommand cmd;
  gchar *pos, command = 0, next_command;
  gboolean error = FALSE;

  /* Free the current path data. */
  if (path->commands)
    g_array_free (path->commands, TRUE);

  path->commands = g_array_new (0, 0, sizeof (GooCanvasPathCommand));

  pos = (gchar*) path_data;
  for (;;)
    {
      while (*pos == ' ' || *pos == '\t' || *pos == '\r' || *pos == '\n')
	pos++;
      if (!*pos)
	break;

      next_command = *pos;

      /* If there is no command letter, we use the same command as the last
	 one, except for the first command, and 'moveto' (which becomes
	 'lineto'). */
      if ((next_command < 'a' || next_command > 'z')
	  && (next_command < 'A' || next_command > 'Z'))
	{
	  /* If this is the first command, then set the error flag and assume
	     a simple close-path command. */
	  if (!command)
	    {
	      error = TRUE;
	      command = 'Z';
	    }
	  /* moveto commands change to lineto. */
	  else if (command == 'm')
	    command = 'l';
	  else if (command == 'M')
	    command = 'L';
	}
      else
	{
	  command = next_command;
	  pos++;
	}

      cmd.simple.relative = 0;
      switch (command)
	{
	  /* Simple commands like moveto and lineto: MmZzLlHhVv. */
	case 'm':
	  cmd.simple.relative = 1;
	case 'M':
	  cmd.simple.type = GOO_CANVAS_PATH_MOVE_TO;
	  cmd.simple.x = parse_double (&pos, &error);
	  cmd.simple.y = parse_double (&pos, &error);
	  break;

	case 'Z':
	case 'z':
	  cmd.simple.type = GOO_CANVAS_PATH_CLOSE_PATH;
	  break;

	case 'l':
	  cmd.simple.relative = 1;
	case 'L':
	  cmd.simple.type = GOO_CANVAS_PATH_LINE_TO;
	  cmd.simple.x = parse_double (&pos, &error);
	  cmd.simple.y = parse_double (&pos, &error);
	  break;

	case 'h':
	  cmd.simple.relative = 1;
	case 'H':
	  cmd.simple.type = GOO_CANVAS_PATH_HORIZONTAL_LINE_TO;
	  cmd.simple.x = parse_double (&pos, &error);
	  break;

	case 'v':
	  cmd.simple.relative = 1;
	case 'V':
	  cmd.simple.type = GOO_CANVAS_PATH_VERTICAL_LINE_TO;
	  cmd.simple.y = parse_double (&pos, &error);
	  break;

	  /* Bezier curve commands: CcSsQqTt. */
	case 'c':
	  cmd.curve.relative = 1;
	case 'C':
	  cmd.curve.type = GOO_CANVAS_PATH_CURVE_TO;
	  cmd.curve.x1 = parse_double (&pos, &error);
	  cmd.curve.y1 = parse_double (&pos, &error);
	  cmd.curve.x2 = parse_double (&pos, &error);
	  cmd.curve.y2 = parse_double (&pos, &error);
	  cmd.curve.x = parse_double (&pos, &error);
	  cmd.curve.y = parse_double (&pos, &error);
	  break;

	case 's':
	  cmd.curve.relative = 1;
	case 'S':
	  cmd.curve.type = GOO_CANVAS_PATH_SMOOTH_CURVE_TO;
	  cmd.curve.x2 = parse_double (&pos, &error);
	  cmd.curve.y2 = parse_double (&pos, &error);
	  cmd.curve.x = parse_double (&pos, &error);
	  cmd.curve.y = parse_double (&pos, &error);
	  break;

	case 'q':
	  cmd.curve.relative = 1;
	case 'Q':
	  cmd.curve.type = GOO_CANVAS_PATH_QUADRATIC_CURVE_TO;
	  cmd.curve.x1 = parse_double (&pos, &error);
	  cmd.curve.y1 = parse_double (&pos, &error);
	  cmd.curve.x = parse_double (&pos, &error);
	  cmd.curve.y = parse_double (&pos, &error);
	  break;

	case 't':
	  cmd.curve.relative = 1;
	case 'T':
	  cmd.curve.type = GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO;
	  cmd.curve.x = parse_double (&pos, &error);
	  cmd.curve.y = parse_double (&pos, &error);
	  break;

	  /* The elliptical arc commands: Aa. */
	case 'a':
	  cmd.arc.relative = 1;
	case 'A':
	  cmd.arc.type = GOO_CANVAS_PATH_ELLIPTICAL_ARC;
	  cmd.arc.rx = parse_double (&pos, &error);
	  cmd.arc.ry = parse_double (&pos, &error);
	  cmd.arc.x_axis_rotation = parse_double (&pos, &error);
	  cmd.arc.large_arc_flag = parse_flag (&pos, &error);
	  cmd.arc.sweep_flag = parse_flag (&pos, &error);
	  cmd.arc.x = parse_double (&pos, &error);
	  cmd.arc.y = parse_double (&pos, &error);
	  break;

	default:
	  error = TRUE;
	  break;
	}

      /* If an error has occurred, return without adding the new command.
	 Thus we include everything in the path up to the error, like SVG. */
      if (error)
	return;

      g_array_append_val (path->commands, cmd);
    }
}


static void
goo_canvas_path_set_property (GObject              *object,
			      guint                 prop_id,
			      const GValue         *value,
			      GParamSpec           *pspec)
{
  GooCanvasPath *path = (GooCanvasPath*) object;
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;

  switch (prop_id)
    {
    case PROP_DATA:
      goo_canvas_path_parse_data (path, g_value_get_string (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }

  if (simple->parent)
    goo_canvas_item_simple_emit_changed (simple, TRUE);
}


static GooCanvasItemView*
goo_canvas_path_create_view (GooCanvasItem     *item,
			     GooCanvasView     *canvas_view,
			     GooCanvasItemView *parent_view)
{
  return goo_canvas_path_view_new (parent_view, (GooCanvasPath*) item);
}


static void
item_interface_init (GooCanvasItemIface *iface)
{
  iface->create_view = goo_canvas_path_create_view;
}


/**
 * goo_canvas_path_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.
 * @path_data: a string specifying the path, using the same syntax as the path
 *  element in SVG.
 * @...: optional pairs of property names and values, and a terminating %NULL.
 * 
 * Creates a new path item.
 * 
 * Returns: a new path item.
 **/
GooCanvasItem*
goo_canvas_path_new               (GooCanvasItem *parent,
				   gchar         *path_data,
				   ...)
{
  GooCanvasItem *item;
  GooCanvasPath *path;
  va_list var_args;
  const char *first_arg_name;

  item = g_object_new (GOO_TYPE_CANVAS_PATH, NULL);
  path = GOO_CANVAS_PATH (item);

  goo_canvas_path_parse_data (path, path_data);

  va_start (var_args, path_data);

  first_arg_name = va_arg (var_args, char*);
  g_object_set_valist (G_OBJECT (item), first_arg_name, var_args);

  va_end (var_args);

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

  return item;
}



--- NEW FILE: goocanvaspath.h ---
/*
 * GooCanvas. Copyright (C) 2005-6 Damon Chaplin.
 * Released under the GNU LGPL license. See COPYING for details.
 *
 * goocanvaspath.h - a path item, very similar to the SVG path element.
 */
#ifndef __GOO_CANVAS_PATH_H__
#define __GOO_CANVAS_PATH_H__

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

G_BEGIN_DECLS


#define GOO_TYPE_CANVAS_PATH            (goo_canvas_path_get_type ())
#define GOO_CANVAS_PATH(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOO_TYPE_CANVAS_PATH, GooCanvasPath))
#define GOO_CANVAS_PATH_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GOO_TYPE_CANVAS_PATH, GooCanvasPathClass))
#define GOO_IS_CANVAS_PATH(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOO_TYPE_CANVAS_PATH))
#define GOO_IS_CANVAS_PATH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GOO_TYPE_CANVAS_PATH))
#define GOO_CANVAS_PATH_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GOO_TYPE_CANVAS_PATH, GooCanvasPathClass))


typedef enum
{
  /* Simple commands like moveto and lineto: MmZzLlHhVv. */
  GOO_CANVAS_PATH_MOVE_TO,
  GOO_CANVAS_PATH_CLOSE_PATH,
  GOO_CANVAS_PATH_LINE_TO,
  GOO_CANVAS_PATH_HORIZONTAL_LINE_TO,
  GOO_CANVAS_PATH_VERTICAL_LINE_TO,

  /* Bezier curve commands: CcSsQqTt. */
  GOO_CANVAS_PATH_CURVE_TO,
  GOO_CANVAS_PATH_SMOOTH_CURVE_TO,
  GOO_CANVAS_PATH_QUADRATIC_CURVE_TO,
  GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO,

  /* The elliptical arc commands: Aa. */
  GOO_CANVAS_PATH_ELLIPTICAL_ARC
} GooCanvasPathCommandType;


typedef union _GooCanvasPathCommand  GooCanvasPathCommand;

/* Note that the command type is always the first element in each struct, so
   we can always use it whatever type of command it is. */
union _GooCanvasPathCommand
{
  /* Simple commands like moveto and lineto: MmZzLlHhVv. */
  struct {
    guint type : 5; /* GooCanvasPathCommandType */
    guint relative : 1;
    gdouble x, y;
  } simple;

  /* Bezier curve commands: CcSsQqTt. */
  struct {
    guint type : 5; /* GooCanvasPathCommandType */
    guint relative : 1;
    gdouble x, y, x1, y1, x2, y2;
  } curve;

  /* The elliptical arc commands: Aa. */
  struct {
    guint type : 5; /* GooCanvasPathCommandType */
    guint relative : 1;
    guint large_arc_flag : 1;
    guint sweep_flag : 1;
    gdouble rx, ry, x_axis_rotation, x, y;
  } arc;
};


typedef struct _GooCanvasPath       GooCanvasPath;
typedef struct _GooCanvasPathClass  GooCanvasPathClass;

struct _GooCanvasPath
{
  GooCanvasItemSimple parent;

  /* The path data, an array of GooCanvasPathCommand. Apps can modify this
     directly, but call goo_canvas_item_simple_emit_changed (item, TRUE)
     afterwards to notify the views. */
  GArray *commands;
};

struct _GooCanvasPathClass
{
  GooCanvasItemSimpleClass parent_class;
};


GType           goo_canvas_path_get_type          (void) G_GNUC_CONST;
GooCanvasItem*  goo_canvas_path_new               (GooCanvasItem *parent,
						   gchar         *path_data,
						   ...);


G_END_DECLS

#endif /* __GOO_CANVAS_PATH_H__ */

--- NEW FILE: goocanvaspathview.c ---
/*
 * GooCanvas. Copyright (C) 2005-6 Damon Chaplin.
 * Released under the GNU LGPL license. See COPYING for details.
 *
 * goocanvaspathview.c - view for path item.
 */
#include <config.h>
#include <gtk/gtk.h>
#include "goocanvasview.h"
#include "goocanvaspathview.h"


static void canvas_item_view_interface_init  (GooCanvasItemViewIface *iface);
static void goo_canvas_path_view_finalize (GObject *object);
static void on_path_changed (GooCanvasPath     *path,
			     gboolean           recompute_bounds,
			     GooCanvasPathView *view);

G_DEFINE_TYPE_WITH_CODE (GooCanvasPathView, goo_canvas_path_view,
			 G_TYPE_OBJECT,
			 G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM_VIEW,
						canvas_item_view_interface_init));


static void
goo_canvas_path_view_class_init (GooCanvasPathViewClass *klass)
{
  GObjectClass *gobject_class = (GObjectClass*) klass;

  gobject_class->finalize = goo_canvas_path_view_finalize;
}


static void
goo_canvas_path_view_init (GooCanvasPathView *path_view)
{
  GooCanvasBounds *bounds = &path_view->bounds;

  bounds->x1 = bounds->y1 = bounds->x2 = bounds->y2 = 0;
}


GooCanvasItemView*
goo_canvas_path_view_new (GooCanvasItemView *parent_view,
			  GooCanvasPath     *path)
{
  GooCanvasPathView *view;

  view = g_object_new (GOO_TYPE_CANVAS_PATH_VIEW, NULL);
  view->need_update = TRUE;
  view->parent_view = parent_view;
  view->path = path;

  g_signal_connect (path, "changed", G_CALLBACK (on_path_changed), view);

  return (GooCanvasItemView*) view;
}


static void
goo_canvas_path_view_finalize (GObject *object)
{
  /*GooCanvasPathView *o = (GooCanvasPathView*) object;*/

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


static void
goo_canvas_path_view_create_path (GooCanvasPath *path,
				  cairo_t       *cr)
{
  GooCanvasPathCommand *cmd;
  GooCanvasPathCommandType prev_cmd_type = GOO_CANVAS_PATH_CLOSE_PATH;
  gdouble x = 0, y = 0, path_start_x = 0, path_start_y = 0;
  gdouble last_control_point_x, last_control_point_y, x1, y1, x2, y2;
  gdouble qx1, qy1, qx2, qy2;
  gint i;

  cairo_new_path (cr);

  if (!path->commands || path->commands->len == 0)
    return;

  for (i = 0; i < path->commands->len; i++)
    {
      cmd = &g_array_index (path->commands, GooCanvasPathCommand, i);
      switch (cmd->simple.type)
	{
	  /* Simple commands like moveto and lineto: MmZzLlHhVv. */
	case GOO_CANVAS_PATH_MOVE_TO:
	  if (cmd->simple.relative)
	    {
	      x += cmd->simple.x;
	      y += cmd->simple.y;
	    }
	  else
	    {
	      x = cmd->simple.x;
	      y = cmd->simple.y;
	    }
	  path_start_x = x;
	  path_start_y = y;
	  cairo_move_to (cr, x, y);
	  break;

	case GOO_CANVAS_PATH_CLOSE_PATH:
	  x = path_start_x;
	  y = path_start_y;
	  cairo_close_path (cr);
	  break;

	case GOO_CANVAS_PATH_LINE_TO:
	  if (cmd->simple.relative)
	    {
	      x += cmd->simple.x;
	      y += cmd->simple.y;
	    }
	  else
	    {
	      x = cmd->simple.x;
	      y = cmd->simple.y;
	    }
	  cairo_line_to (cr, x, y);
	  break;

	case GOO_CANVAS_PATH_HORIZONTAL_LINE_TO:
	  if (cmd->simple.relative)
	    x += cmd->simple.x;
	  else
	    x = cmd->simple.x;
	  cairo_line_to (cr, x, y);
	  break;

	case GOO_CANVAS_PATH_VERTICAL_LINE_TO:
	  if (cmd->simple.relative)
	    y += cmd->simple.y;
	  else
	    y = cmd->simple.y;
	  cairo_line_to (cr, x, y);
	  break;

	  /* Bezier curve commands: CcSsQqTt. */
	case GOO_CANVAS_PATH_CURVE_TO:
	  if (cmd->curve.relative)
	    {
	      cairo_curve_to (cr, x + cmd->curve.x1, y + cmd->curve.y1,
			      x + cmd->curve.x2, y + cmd->curve.y2,
			      x + cmd->curve.x, y + cmd->curve.y);
	      last_control_point_x = x + cmd->curve.x2;
	      last_control_point_y = y + cmd->curve.y2;
	      x += cmd->curve.x;
	      y += cmd->curve.y;
	    }
	  else
	    {
	      cairo_curve_to (cr, cmd->curve.x1, cmd->curve.y1,
			      cmd->curve.x2, cmd->curve.y2,
			      cmd->curve.x, cmd->curve.y);
	      last_control_point_x = cmd->curve.x2;
	      last_control_point_y = cmd->curve.y2;
	      x = cmd->curve.x;
	      y = cmd->curve.y;
	    }
	  break;

	case GOO_CANVAS_PATH_SMOOTH_CURVE_TO:
	  /* If the last command was a curveto or smooth curveto, we use the
	     reflection of the last control point about the current point as
	     the first control point of this curve. Otherwise we use the
	     current point as the first control point. */
	  if (prev_cmd_type == GOO_CANVAS_PATH_CURVE_TO
	      || prev_cmd_type == GOO_CANVAS_PATH_SMOOTH_CURVE_TO)
	    {
	      x1 = x + (x - last_control_point_x);
	      y1 = y + (y - last_control_point_y);
	    }
	  else
	    {
	      x1 = x;
	      y1 = y;
	    }

	  if (cmd->curve.relative)
	    {
	      cairo_curve_to (cr, x1, y1, x + cmd->curve.x2, y + cmd->curve.y2,
			      x + cmd->curve.x, y + cmd->curve.y);
	      last_control_point_x = x + cmd->curve.x2;
	      last_control_point_y = y + cmd->curve.y2;
	      x += cmd->curve.x;
	      y += cmd->curve.y;
	    }
	  else
	    {
	      cairo_curve_to (cr, x1, y1, cmd->curve.x2, cmd->curve.y2,
			      cmd->curve.x, cmd->curve.y);
	      last_control_point_x = cmd->curve.x2;
	      last_control_point_y = cmd->curve.y2;
	      x = cmd->curve.x;
	      y = cmd->curve.y;
	    }
	  break;

	case GOO_CANVAS_PATH_QUADRATIC_CURVE_TO:
	  if (cmd->curve.relative)
	    {
	      qx1 = x + cmd->curve.x1;
	      qy1 = y + cmd->curve.y1;
	      qx2 = x + cmd->curve.x;
	      qy2 = y + cmd->curve.y;
	    }
	  else
	    {
	      qx1 = cmd->curve.x1;
	      qy1 = cmd->curve.y1;
	      qx2 = cmd->curve.x;
	      qy2 = cmd->curve.y;
	    }

	  /* We need to convert the quadratic into a cubic bezier. */
	  x1 = x + (qx1 - x) * 2.0 / 3.0;
	  y1 = y + (qy1 - y) * 2.0 / 3.0;

	  x2 = x1 + (qx2 - x) / 3.0;
	  y2 = y1 + (qy2 - y) / 3.0;

	  cairo_curve_to (cr, x1, y1, x2, y2, qx2, qy2);

	  x = qx2;
	  y = qy2;
	  last_control_point_x = qx1;
	  last_control_point_y = qy1;
	  break;

	case GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO:
	  /* If the last command was a quadratic or smooth quadratic, we use
	     the reflection of the last control point about the current point
	     as the first control point of this curve. Otherwise we use the
	     current point as the first control point. */
	  if (prev_cmd_type == GOO_CANVAS_PATH_QUADRATIC_CURVE_TO
	      || prev_cmd_type == GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO)
	    {
	      qx1 = x + (x - last_control_point_x);
	      qy1 = y + (y - last_control_point_y);
	    }
	  else
	    {
	      qx1 = x;
	      qy1 = y;
	    }

	  if (cmd->curve.relative)
	    {
	      qx2 = x + cmd->curve.x;
	      qy2 = y + cmd->curve.y;
	    }
	  else
	    {
	      qx2 = cmd->curve.x;
	      qy2 = cmd->curve.y;
	    }

	  /* We need to convert the quadratic into a cubic bezier. */
	  x1 = x + (qx1 - x) * 2.0 / 3.0;
	  y1 = y + (qy1 - y) * 2.0 / 3.0;

	  x2 = x1 + (qx2 - x) / 3.0;
	  y2 = y1 + (qy2 - y) / 3.0;

	  cairo_curve_to (cr, x1, y1, x2, y2, qx2, qy2);

	  x = qx2;
	  y = qy2;
	  last_control_point_x = qx1;
	  last_control_point_y = qy1;
	  break;

	  /* The elliptical arc commands: Aa. */
	case GOO_CANVAS_PATH_ELLIPTICAL_ARC:
	  /* If the endpoints are exactly the same, just return (See SVG). */
	  if (x == cmd->arc.x && y == cmd->arc.y)
	    return;

	  /* If either rx or ry is 0, do a simple lineto (See SVG). */
	  if (cmd->arc.rx == 0.0 || cmd->arc.ry == 0.0)
	    {
	      if (cmd->arc.relative)
		{
		  x += cmd->arc.x;
		  y += cmd->arc.y;
		}
	      else
		{
		  x = cmd->arc.x;
		  y = cmd->arc.y;
		}
	      cairo_line_to (cr, x, y);
	      return;
	    }

#if 0
	  /* Make sure rx & ry are positive. */
	  rx = cmd->arc.rx > 0 ? cmd->arc.rx : - cmd->arc.rx;
	  ry = cmd->arc.ry > 0 ? cmd->arc.ry : - cmd->arc.ry;


	  /* FIXME: Need to rotate according to x_axis_rotation, and scale
	     according to rx & ry. */


	  /*cairo_arc (cr, xc, yc, r, angle1, angle2);*/
#endif
	  break;
	}

      prev_cmd_type = cmd->simple.type;
    }
}


static GooCanvasItemView*
goo_canvas_path_view_get_parent_view (GooCanvasItemView   *view)
{
  GooCanvasPathView *path_view = (GooCanvasPathView*) view;
  return path_view->parent_view;
}


static void
goo_canvas_path_view_set_parent_view (GooCanvasItemView  *view,
				      GooCanvasItemView  *parent_view)
{
  GooCanvasPathView *path_view = (GooCanvasPathView*) view;
  path_view->parent_view = parent_view;
}


static GooCanvasItem*
goo_canvas_path_view_get_item (GooCanvasItemView  *view)
{
  GooCanvasPathView *path_view = (GooCanvasPathView*) view;
  return (GooCanvasItem*) path_view->path;
}


static GooCanvasBounds*
goo_canvas_path_view_get_bounds  (GooCanvasItemView   *view)
{
  GooCanvasPathView *path_view = (GooCanvasPathView*) view;

  if (path_view->need_update)
    goo_canvas_item_view_ensure_updated (view);

  return &path_view->bounds;
}


static GooCanvasItemView*
goo_canvas_path_view_get_item_at (GooCanvasItemView  *view,
				  gdouble             x,
				  gdouble             y,
				  cairo_t            *cr,
				  gboolean            is_pointer_event,
				  gboolean            parent_visible,
				  gdouble             scale)
{
  GooCanvasPathView *path_view = (GooCanvasPathView*) view;
  GooCanvasPath *path = path_view->path;
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) path;
  GooCanvasItemView *found_view = NULL;
  double user_x = x, user_y = y;
  GooCanvasPointerEvents pointer_events = GOO_CANVAS_EVENTS_ALL;

  if (path_view->need_update)
    goo_canvas_item_view_ensure_updated (view);

  if (!path->commands || path->commands->len == 0)
    return NULL;

#if 0
  g_print ("In path_view_get_item_at: %g, %g\n", x, y);
#endif

  /* Check if the item should receive events. */
  if (is_pointer_event)
    {
      if (simple->pointer_events == GOO_CANVAS_EVENTS_NONE)
	return NULL;
      if (simple->pointer_events & GOO_CANVAS_EVENTS_VISIBLE_MASK
	  && (!parent_visible
	      || simple->visibility == GOO_CANVAS_ITEM_INVISIBLE
	      || (simple->visibility == GOO_CANVAS_ITEM_VISIBLE_ABOVE_THRESHOLD
		  && scale < simple->visibility_threshold)))
	return NULL;

      pointer_events = simple->pointer_events;
    }

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

  cairo_device_to_user (cr, &user_x, &user_y);

  goo_canvas_path_view_create_path (path, cr);
  if (goo_canvas_item_simple_check_in_path (simple, user_x, user_y, cr,
					    pointer_events))
    found_view = view;

  cairo_restore (cr);

  return found_view;
}


static void
goo_canvas_path_view_compute_bounds (GooCanvasPathView *view,
				     cairo_t           *cr,
				     GooCanvasBounds   *bounds)
{
  GooCanvasPath *path = view->path;
  GooCanvasItemSimple *simple = GOO_CANVAS_ITEM_SIMPLE (path);

  goo_canvas_path_view_create_path (path, cr);
  goo_canvas_item_simple_get_path_bounds (simple, cr, bounds);

#if 0
  g_print ("Path view bounds: %g, %g - %g, %g\n",
	   bounds->x1, bounds->y1, bounds->x2, bounds->y2);
#endif
}


static GooCanvasBounds*
goo_canvas_path_view_update  (GooCanvasItemView  *view,
			      gboolean            entire_tree,
			      cairo_t            *cr)
{
  GooCanvasPathView *path_view = (GooCanvasPathView*) view;
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) path_view->path;
  GooCanvasView *canvas_view;
  GooCanvasBounds *bounds = &path_view->bounds;

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

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

      canvas_view = goo_canvas_item_view_get_canvas_view (path_view->parent_view);

      /* Request a redraw of the existing bounds. */
      goo_canvas_view_request_redraw (canvas_view, bounds);

      /* Compute the new bounds. */
      goo_canvas_path_view_compute_bounds (path_view, cr, bounds);

      /* Request a redraw of the new bounds. */
      goo_canvas_view_request_redraw (canvas_view, bounds);

      cairo_restore (cr);
    }

  return bounds;
}


static void
goo_canvas_path_view_paint (GooCanvasItemView *view,
			    cairo_t           *cr,
			    GooCanvasBounds   *bounds,
			    gdouble            scale)
{
  GooCanvasPathView *path_view = (GooCanvasPathView*) view;
  GooCanvasPath *path = path_view->path;
  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) path;

#if 0
  g_print ("Painting path item %p\n", view);
#endif

  if (!path->commands || path->commands->len == 0)
    return;

  /* Check if the item should be visible. */
  if (simple->visibility == GOO_CANVAS_ITEM_INVISIBLE
      || (simple->visibility == GOO_CANVAS_ITEM_VISIBLE_ABOVE_THRESHOLD
	  && scale < simple->visibility_threshold))
    return;

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

  goo_canvas_path_view_create_path (path, cr);
  goo_canvas_item_simple_paint_path (simple, cr);

  cairo_restore (cr);
}


static void
canvas_item_view_interface_init (GooCanvasItemViewIface *iface)
{
  iface->get_parent_view = goo_canvas_path_view_get_parent_view;
  iface->set_parent_view = goo_canvas_path_view_set_parent_view;
  iface->get_item        = goo_canvas_path_view_get_item;
  iface->get_bounds      = goo_canvas_path_view_get_bounds;
  iface->get_item_at     = goo_canvas_path_view_get_item_at;
  iface->update          = goo_canvas_path_view_update;
  iface->paint           = goo_canvas_path_view_paint;
}


static void
on_path_changed (GooCanvasPath     *path,
		 gboolean           recompute_bounds,
		 GooCanvasPathView *view)
{
  GooCanvasView *canvas_view;

#if 0
  g_print ("Path changed\n");
#endif

  if (recompute_bounds)
    {
      if (!view->need_update)
	{
	  view->need_update = TRUE;
	  goo_canvas_item_view_request_update (view->parent_view);
	}
    }
  else
    {
      canvas_view = goo_canvas_item_view_get_canvas_view (view->parent_view);
      goo_canvas_view_request_redraw (canvas_view, &view->bounds);
    }
}

--- NEW FILE: goocanvaspathview.h ---
/*
 * GooCanvas. Copyright (C) 2005-6 Damon Chaplin.
 * Released under the GNU LGPL license. See COPYING for details.
 *
 * goocanvaspathview.h - view for path item.
 */
#ifndef __GOO_CANVAS_PATH_VIEW_H__
#define __GOO_CANVAS_PATH_VIEW_H__

#include <gtk/gtk.h>
#include "goocanvasitemview.h"
#include "goocanvaspath.h"

G_BEGIN_DECLS


#define GOO_TYPE_CANVAS_PATH_VIEW            (goo_canvas_path_view_get_type ())
#define GOO_CANVAS_PATH_VIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOO_TYPE_CANVAS_PATH_VIEW, GooCanvasPathView))
#define GOO_CANVAS_PATH_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GOO_TYPE_CANVAS_PATH_VIEW, GooCanvasPathViewClass))
#define GOO_IS_CANVAS_PATH_VIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOO_TYPE_CANVAS_PATH_VIEW))
#define GOO_IS_CANVAS_PATH_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GOO_TYPE_CANVAS_PATH_VIEW))
#define GOO_CANVAS_PATH_VIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GOO_TYPE_CANVAS_PATH_VIEW, GooCanvasPathViewClass))


typedef struct _GooCanvasPathView       GooCanvasPathView;
typedef struct _GooCanvasPathViewClass  GooCanvasPathViewClass;

struct _GooCanvasPathView
{
  GObject parent;

  /* The parent view. */
  GooCanvasItemView *parent_view;

  /* The item in the model. */
  GooCanvasPath *path;

  /* The bounds of the item, relative to the entire canvas. */
  GooCanvasBounds bounds;

  /* This is TRUE if we need to recompute our bounds & repaint. */
  guint need_update : 1;
};

struct _GooCanvasPathViewClass
{
  GObjectClass parent_class;
};


GType              goo_canvas_path_view_get_type (void) G_GNUC_CONST;
GooCanvasItemView* goo_canvas_path_view_new      (GooCanvasItemView *parent_view,
						  GooCanvasPath     *path);


G_END_DECLS

#endif /* __GOO_CANVAS_PATH_VIEW_H__ */

Index: goocanvaspolylineview.c
===================================================================
RCS file: /cvs/cairo/goocanvas/src/goocanvaspolylineview.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- goocanvaspolylineview.c	8 Apr 2006 13:14:17 -0000	1.5
+++ goocanvaspolylineview.c	10 Apr 2006 20:30:24 -0000	1.6
@@ -2,7 +2,7 @@
  * GooCanvas. Copyright (C) 2005 Damon Chaplin.
  * Released under the GNU LGPL license. See COPYING for details.
  *
- * goocanvaspolylineview.c - 
+ * goocanvaspolylineview.c - view for polyline item.
  */
 #include <config.h>
 #include <gtk/gtk.h>
@@ -370,7 +370,7 @@
 goo_canvas_polyline_view_paint (GooCanvasItemView *view,
 				cairo_t           *cr,
 				GooCanvasBounds   *bounds,
-				gdouble            effective_scale)
+				gdouble            scale)
 {
   GooCanvasPolylineView *polyline_view = (GooCanvasPolylineView*) view;
   GooCanvasPolyline *polyline = polyline_view->polyline;
@@ -386,7 +386,7 @@
   /* Check if the item should be visible. */
   if (simple->visibility == GOO_CANVAS_ITEM_INVISIBLE
       || (simple->visibility == GOO_CANVAS_ITEM_VISIBLE_ABOVE_THRESHOLD
-	  && effective_scale < simple->visibility_threshold))
+	  && scale < simple->visibility_threshold))
     return;
 
   cairo_save (cr);

Index: goocanvaspolylineview.h
===================================================================
RCS file: /cvs/cairo/goocanvas/src/goocanvaspolylineview.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -d -r1.1.1.1 -r1.2
--- goocanvaspolylineview.h	15 Dec 2005 15:32:02 -0000	1.1.1.1
+++ goocanvaspolylineview.h	10 Apr 2006 20:30:24 -0000	1.2
@@ -2,7 +2,7 @@
  * GooCanvas. Copyright (C) 2005 Damon Chaplin.
  * Released under the GNU LGPL license. See COPYING for details.
  *
- * goocanvaspolylineview.h - 
+ * goocanvaspolylineview.h - view for polyline item.
  */
 #ifndef __GOO_CANVAS_POLYLINE_VIEW_H__
 #define __GOO_CANVAS_POLYLINE_VIEW_H__



More information about the cairo-commit mailing list