[dia] [substitute] new object 'Standard - Path'



commit 0ba0329b553d78f94a5cee1ebe20dc81d2b2e93b
Author: Hans Breuer <hans breuer org>
Date:   Tue Oct 2 22:52:25 2012 +0200

    [substitute] new object 'Standard - Path'
    
    Basically like "Standard - Beziergon" or "Standard - Bezierline" but
    without the limit of move-to at start. To edit offer a conversion
    to Bezier or other single standard elements: "Explode" (?).
    
    To create allow to convert a selection of single elements
    "Merge to Path". The default way of creation would be to import from
    SVG or other path supporting importers.

 lib/Makefile.am               |    5 +-
 lib/create.c                  |   22 +-
 lib/create.h                  |   11 +
 lib/dialib.c                  |    4 +
 lib/libdia.def                |    2 +
 lib/makefile.msc              |    5 +-
 lib/pixmaps/standard-path.png |  Bin 0 -> 346 bytes
 lib/standard-path.c           |  831 +++++++++++++++++++++++++++++++++++++++++
 lib/standard-path.h           |   30 ++
 9 files changed, 906 insertions(+), 4 deletions(-)
---
diff --git a/lib/Makefile.am b/lib/Makefile.am
index a13d55a..f0c8d0b 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -44,7 +44,8 @@ endif
 ICON_PNG_PAIRS= \
 	dia_broken_icon $(srcdir)/pixmaps/broken.png \
 	dia_broken_chain_icon $(srcdir)/pixmaps/broken-chain.png \
-	dia_unbroken_chain_icon $(srcdir)/pixmaps/unbroken-chain.png
+	dia_unbroken_chain_icon $(srcdir)/pixmaps/unbroken-chain.png \
+	stdpath_icon $(srcdir)/pixmaps/standard-path.png
 
 # Note that this doesn't seem to get run automatically.  It should, but
 # I'm not gonna chase goblins right now.
@@ -124,6 +125,8 @@ libdia_la_SOURCES =  \
 		units.h \
 		dia_image.c \
 		dia_image.h \
+		standard-path.c \
+		standard-path.h \
 		intl.c \
 		intl.h \
 		filter.c \
diff --git a/lib/create.c b/lib/create.c
index c2fa63d..66bcc07 100644
--- a/lib/create.c
+++ b/lib/create.c
@@ -162,7 +162,7 @@ create_standard_zigzagline(int num_points, const Point *points,
     }
 
     pcd.num_points = num_points;
-    pcd.points = points;
+    pcd.points = (Point *)points;
 
     new_obj = otype->ops->create(NULL, &pcd, &h1, &h2);
 
@@ -293,6 +293,26 @@ create_standard_beziergon(int num_points,
     return new_obj;
 }
 
+DiaObject *
+create_standard_path(int num_points, BezPoint *points)
+{
+    DiaObjectType *otype = object_get_type("Standard - Path");
+    DiaObject *new_obj;
+    Handle *h1, *h2;
+    BezierCreateData bcd;
+
+    if (otype == NULL){
+	message_error(_("Can't find standard object"));
+	return NULL;
+    }
+
+    bcd.num_points = num_points;
+    bcd.points = points;
+
+    new_obj = otype->ops->create(NULL, &bcd, &h1, &h2);
+    
+    return new_obj;
+}
 
 static PropDescription create_arc_prop_descs[] = {
     { "curve_distance", PROP_TYPE_REAL },
diff --git a/lib/create.h b/lib/create.h
index 3d59aa5..11ae917 100644
--- a/lib/create.h
+++ b/lib/create.h
@@ -99,6 +99,17 @@ DiaObject *create_standard_bezierline(int num_points, BezPoint *points,
  */
 DiaObject *create_standard_beziergon(int num_points, BezPoint *points);
 /*!
+ * \brief Create a \ref _Beziergon
+ * \ingroup ObjectCreate
+ */
+DiaObject *create_standard_path(int num_points, BezPoint *points);
+/*!
+ * \brief Create a \ref _StdPath from the given _Text
+ * \ingroup ObjectCreate
+ */
+DiaObject *create_standard_path_from_text (const Text *text);
+
+/*!
  * \brief Create an \ref _Arc with arrows 
  * @param x1 arc start position
  * @param y1 arc start position
diff --git a/lib/dialib.c b/lib/dialib.c
index f42ab1e..b6b3386 100644
--- a/lib/dialib.c
+++ b/lib/dialib.c
@@ -33,6 +33,7 @@
 #include "object.h"
 #include "dia_dirs.h"
 #include "properties.h" /* stdprops_init() */
+#include "standard-path.h"
 
 static void
 stderr_message_internal(const char *title, enum ShowAgainStyle showAgain,
@@ -119,5 +120,8 @@ libdia_init (guint flags)
   initialized = TRUE;
 
   object_registry_init();
+
+  /* The group_type is registered in app, but it needs to be exported anyway */
+  object_register_type(&stdpath_type);
 }
 
diff --git a/lib/libdia.def b/lib/libdia.def
index 979e74a..e325382 100644
--- a/lib/libdia.def
+++ b/lib/libdia.def
@@ -127,6 +127,8 @@ EXPORTS
  create_standard_ellipse
  create_standard_group
  create_standard_image
+ create_standard_path
+ create_standard_path_from_text
  create_standard_polygon
  create_standard_polyline
  create_standard_text
diff --git a/lib/makefile.msc b/lib/makefile.msc
index 6ee23fc..8548604 100644
--- a/lib/makefile.msc
+++ b/lib/makefile.msc
@@ -32,8 +32,8 @@ PKG_LINK = $(GTK2_LIBS)  $(PANGOWIN32_LIBS) $(GLIB_LIBS) \
 ICON_PNG_PAIRS = \
 	dia_broken_icon pixmaps\broken.png \
 	dia_broken_chain_icon pixmaps\broken-chain.png \
-	dia_unbroken_chain_icon pixmaps\unbroken-chain.png
-
+	dia_unbroken_chain_icon pixmaps\unbroken-chain.png \
+	stdpath_icon pixmaps\standard-path.png
 
 # Note that this doesn't seem to get run automatically.  It should, but
 # I'm not gonna chase goblins right now.
@@ -117,6 +117,7 @@ OBJECTS = \
 	propregistry.obj \
 	render_pixmap.obj \
 	sheet.obj \
+	standard-path.obj \
 	text.obj \
 	textline.obj \
 	units.obj \
diff --git a/lib/pixmaps/standard-path.png b/lib/pixmaps/standard-path.png
new file mode 100644
index 0000000..7fcdc50
Binary files /dev/null and b/lib/pixmaps/standard-path.png differ
diff --git a/lib/standard-path.c b/lib/standard-path.c
new file mode 100644
index 0000000..07b0869
--- /dev/null
+++ b/lib/standard-path.c
@@ -0,0 +1,831 @@
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * standard-path.c -- path based object drawing
+ * Copyright (C) 2012, Hans Breuer <Hans Breuer Org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*!
+ * \file standard-path.c - one to rule them all ;)
+ *
+ * One of Dia's early deficiencies is the lack of full path support. The basics
+ * are there, e.g. _BezierShape and _BezierConn, but both are bound to have only
+ * a single BEZ_MOVE_TO because the _DiaGdkRenderer does not support RENDER_HOLES
+ * and even worse the original file format of bezierconn_save() only works
+ * with a single move-to, too.
+ * Instead of breaking forward compatibility this is implementing a new bezier
+ * based object, which can render holes, even if the _DiaRenderer can not. It is 
+ * using a newer file format representaion through BezPointarrayProperty.
+ */
+#include <config.h>
+
+#include "object.h"
+#include "diarenderer.h"
+#include "attributes.h"
+#include "properties.h"
+#include "boundingbox.h"
+#include "standard-path.h"
+#include "create.h"
+#include "bezier-common.h"
+
+#include "dia-lib-icons.h"
+
+#define NUM_HANDLES 8
+
+typedef enum {
+  _HANDLE_OBJ_POS = HANDLE_CUSTOM1,
+  _HANDLE_SHEAR,
+  _HANDLE_ROTATE,
+  _HANDLE_PERSPECTIVE,
+  _HANDLE_REF_POS
+} MyHandleIds;
+
+typedef enum {
+  PDO_STROKE = (1<<0),
+  PDO_FILL   = (1<<1),
+  PDO_BOTH   = (PDO_STROKE|PDO_FILL)
+} PathDrawingOps;
+
+typedef struct _StdPath StdPath;
+
+/*!
+ * \brief 'Standard - Path' - bezier points for stroke or fill
+ *
+ * The _StdPath object can be used to represent many of the \ref StandardObjects.
+ * It is not intended to replace all the basic objects, but rather to extend
+ * facilities of them by offering conversion from and to these objects.
+ *
+ * Rather than duplicating all the specific editing of the standard objects this
+ * object supports some more generic approach.
+ *
+ * \extends _DiaObject
+ * \ingroup StandardObjects
+ */
+struct _StdPath {
+  DiaObject object; /**< inheritance */
+  
+  int num_points; /**< >= 2 */
+  BezPoint *points; /**< point data */
+
+  int stroke_or_fill;
+
+  Color line_color;
+  real line_width;
+  LineStyle line_style;
+  real dashlength;
+  LineJoin line_join;
+  LineCaps line_caps;
+  Color fill_color;
+
+  /* mostly useful for debugging transformations */
+  gboolean show_control_lines;
+
+  Handle handles[NUM_HANDLES];
+
+  HandleMoveReason move_reason;
+};
+
+static DiaObject *stdpath_create (Point *startpoint,
+				  void *user_data,
+				  Handle **handle1,
+				  Handle **handle2);
+static DiaObject *
+stdpath_load(ObjectNode obj_node, int version,DiaContext *ctx);
+
+static ObjectTypeOps stdpath_type_ops =
+{
+  (CreateFunc)stdpath_create,
+  (LoadFunc)  stdpath_load,
+  (SaveFunc)  object_save_using_properties, /* stdpath_save */
+  (GetDefaultsFunc)   NULL,
+  (ApplyDefaultsFunc) NULL
+};
+
+static PropEnumData _drawing_enum_data [] = {
+  { N_("Stroke"), PDO_STROKE },
+  { N_("Fill"), PDO_FILL },
+  { N_("Fill & Stroke"), PDO_BOTH },
+  { NULL, 0 }
+};
+static PropDescription stdpath_props[] = {
+  OBJECT_COMMON_PROPERTIES,
+  { "bez_points", PROP_TYPE_BEZPOINTARRAY, 0, N_("Bezier points"), NULL},
+  { "stroke_or_fill", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE, N_("Drawing"), NULL, _drawing_enum_data },
+  PROP_STD_LINE_WIDTH_OPTIONAL,
+  PROP_STD_LINE_COLOUR_OPTIONAL,
+  PROP_STD_LINE_STYLE_OPTIONAL,
+  PROP_STD_LINE_JOIN_OPTIONAL,
+  PROP_STD_LINE_CAPS_OPTIONAL,
+  PROP_STD_FILL_COLOUR_OPTIONAL,
+  { "show_control_lines", PROP_TYPE_BOOL, PROP_FLAG_OPTIONAL, N_("Draw Control Lines") },
+  PROP_DESC_END
+};
+
+static PropOffset stdpath_offsets[] = {
+  OBJECT_COMMON_PROPERTIES_OFFSETS,
+  { "bez_points", PROP_TYPE_BEZPOINTARRAY, offsetof(StdPath,points), offsetof(StdPath,num_points)},
+  { "stroke_or_fill", PROP_TYPE_ENUM, offsetof(StdPath, stroke_or_fill) },
+  { PROP_STDNAME_LINE_WIDTH, PROP_STDTYPE_LINE_WIDTH, offsetof(StdPath, line_width) },
+  { "line_colour", PROP_TYPE_COLOUR, offsetof(StdPath, line_color) },
+  { "line_style", PROP_TYPE_LINESTYLE, offsetof(StdPath, line_style), offsetof(StdPath, dashlength) },
+  { "line_caps", PROP_TYPE_ENUM, offsetof(StdPath, line_caps) },
+  { "fill_colour", PROP_TYPE_COLOUR, offsetof(StdPath, fill_color) },
+  { "show_control_lines", PROP_TYPE_BOOL, offsetof(StdPath, show_control_lines) },
+  { NULL, 0, 0 }
+};
+
+/*! 
+ * To make this static again we would need some registering function
+ */
+DiaObjectType stdpath_type =
+{
+  "Standard - Path",      /* name */
+  0,                      /* version */
+  (char **) stdpath_icon, /* pixmap */
+  
+  &stdpath_type_ops,      /* ops */
+  NULL,                   /* pixmap_file */
+  0,                      /* default_user_data */
+  stdpath_props,
+  stdpath_offsets
+};
+
+/* Class definition */
+static ObjectChange* stdpath_move_handle (StdPath *stdpath,
+                                          Handle *handle,
+					  Point *to, ConnectionPoint *cp,
+					  HandleMoveReason reason, ModifierKeys modifiers);
+static ObjectChange* stdpath_move (StdPath *stdpath, Point *to);
+static void stdpath_select(StdPath *stdpath, Point *clicked_point,
+			   DiaRenderer *interactive_renderer);
+static void stdpath_draw(StdPath *stdpath, DiaRenderer *renderer);
+static real stdpath_distance_from (StdPath *stdpath, Point *point);
+static void stdpath_update_data (StdPath *stdpath);
+static void stdpath_destroy (StdPath *stdpath);
+static DiaObject *stdpath_copy (StdPath *stdpath);
+static DiaMenu *stdpath_get_object_menu(StdPath *stdpath,
+					Point *clickedpoint);
+static void stdpath_set_props(StdPath *stdpath, GPtrArray *props);
+
+static ObjectOps stdpath_ops = {
+  (DestroyFunc)         stdpath_destroy,
+  (DrawFunc)            stdpath_draw,
+  (DistanceFunc)        stdpath_distance_from,
+  (SelectFunc)          stdpath_select,
+  (CopyFunc)            stdpath_copy,
+  (MoveFunc)            stdpath_move,
+  (MoveHandleFunc)      stdpath_move_handle,
+  (GetPropertiesFunc)   object_create_props_dialog,
+  (ApplyPropertiesDialogFunc) object_apply_props_from_dialog,
+  (ObjectMenuFunc)      stdpath_get_object_menu,
+  (DescribePropsFunc)   object_describe_props,
+  (GetPropsFunc)        object_get_props,
+  (SetPropsFunc)        stdpath_set_props,
+  (TextEditFunc) 0,
+  (ApplyPropertiesListFunc) object_apply_props,
+};
+
+/*!
+ * \brief Setup unchangeable part of the object handles
+ *
+ * The object must be initalized already.
+ * \protected \memberof StdPath
+ *
+ * \todo Create a new handle type HANDLE_INVISBLE couple with object lock
+ */
+static void
+stdpath_init_handles (StdPath *stdpath)
+{
+  DiaObject *obj = &stdpath->object;
+  int i;
+
+  g_return_if_fail (obj->handles != NULL || obj->num_handles == NUM_HANDLES);
+
+  for (i = 0; i < NUM_HANDLES; ++i) {
+    obj->handles[i] = &stdpath->handles[i];
+    obj->handles[i]->connect_type = HANDLE_NONCONNECTABLE;
+    obj->handles[i]->connected_to = NULL;
+  }
+  /* now almost every handle gets a special meaning */
+  /* from left to right, from top to bottom */
+  obj->handles[0]->type = HANDLE_NON_MOVABLE;
+  obj->handles[0]->id   = _HANDLE_OBJ_POS;
+  obj->handles[1]->type = HANDLE_MINOR_CONTROL;
+  obj->handles[1]->id   = _HANDLE_SHEAR;
+  obj->handles[2]->type = HANDLE_MINOR_CONTROL;
+  obj->handles[2]->id   = _HANDLE_ROTATE;
+
+  obj->handles[3]->type = HANDLE_NON_MOVABLE;
+  obj->handles[3]->id   = _HANDLE_REF_POS;
+  obj->handles[4]->type = HANDLE_MAJOR_CONTROL;
+  obj->handles[4]->id   = HANDLE_RESIZE_E;
+
+  obj->handles[5]->type = HANDLE_MINOR_CONTROL;
+  obj->handles[5]->id   = _HANDLE_PERSPECTIVE;
+  obj->handles[6]->type = HANDLE_MAJOR_CONTROL;
+  obj->handles[6]->id   = HANDLE_RESIZE_S;
+  obj->handles[7]->type = HANDLE_MAJOR_CONTROL;
+  obj->handles[7]->id   = HANDLE_RESIZE_SE;
+}
+/*! Factory function - create default object */
+static DiaObject *
+stdpath_create (Point *startpoint,
+		void *user_data,
+		Handle **handle1,
+		Handle **handle2)
+{
+  StdPath *stdpath;
+  DiaObject *obj;
+  Point sp = {0, 0};
+  
+  stdpath = g_new0 (StdPath,1);
+  obj = &stdpath->object;
+  obj->type = &stdpath_type;
+  obj->ops = &stdpath_ops;
+
+  object_init (obj, NUM_HANDLES, 0);
+  stdpath_init_handles (stdpath);
+  if (startpoint)
+    sp = *startpoint;
+
+  if (user_data == NULL) {
+    /* just to have something to play with 
+       <bezpoint type="moveto" p1="0,1"/>
+       <bezpoint type="curveto" p1="0,0" p2="2,2" p3="2,1"/>
+       <bezpoint type="curveto" p1="2,0" p2="0,2" p3="0,1"/>
+     */
+    BezPoint *bp;
+    stdpath->num_points = 3;
+    bp = stdpath->points = g_new (BezPoint, 3);
+    bp[0].type = BEZ_MOVE_TO;
+    bp[0].p1.x = sp.x + 0; bp[0].p1.y = sp.y + 1;
+    bp[0].p3 = bp[0].p2 = bp[0].p1; /* not strictly necessary */
+    bp[1].type = BEZ_CURVE_TO;
+    bp[1].p1 = sp;
+    bp[1].p2.x = sp.x + 2; bp[1].p2.y = sp.y + 2;
+    bp[1].p3.x = sp.x + 2; bp[1].p3.y = sp.y + 1;
+    bp[2].type = BEZ_CURVE_TO;
+    bp[2].p1.x = sp.x + 2; bp[2].p1.y = sp.y + 0;
+    bp[2].p2.x = sp.x + 0; bp[2].p2.y = sp.y + 2;
+    bp[2].p3.x = sp.x + 0; bp[2].p3.y = sp.y + 1;
+  } else {
+    BezierCreateData *bcd = (BezierCreateData*)user_data;
+
+    if (bcd->num_points < 2) {
+      g_warning ("'Standard - Path' needs at least two points");
+      /* this is a stress test - object might not be setup completely */
+      object_destroy (obj);
+      g_free (stdpath);
+      return NULL;
+    }
+    stdpath->num_points = bcd->num_points;
+    stdpath->points = g_memdup(bcd->points, bcd->num_points * sizeof(BezPoint));
+  }
+
+  stdpath->stroke_or_fill = PDO_BOTH; /* default: no stroke (later) */
+  stdpath->line_width = attributes_get_default_linewidth();
+  stdpath->line_color = attributes_get_foreground();
+  stdpath->fill_color = attributes_get_background();
+
+  *handle1 = stdpath->object.handles[0];
+  *handle2 = stdpath->object.handles[7];
+
+  stdpath_update_data (stdpath);
+
+  return obj;
+}
+
+static DiaObject *
+stdpath_load(ObjectNode obj_node, int version,DiaContext *ctx)
+{
+  return object_load_using_properties(&stdpath_type, obj_node, version, ctx);
+}
+
+/*!
+ * \brief Update handle positions to reflect the current object state
+ *
+ * The object bounding box needs to be calculated already.
+ * \protected \memberof StdPath
+ */
+static void
+stdpath_update_handles(StdPath *stdpath)
+{
+  DiaObject *obj = &stdpath->object;
+  const Rectangle *bb = &obj->bounding_box;
+
+  g_return_if_fail (obj->handles != NULL);
+
+  /* from left to right, from top to bottom */
+  obj->handles[0]->pos.x = bb->left;
+  obj->handles[0]->pos.y = bb->top;
+  obj->handles[1]->pos.x = (bb->left + bb->right) / 2.0;
+  obj->handles[1]->pos.y = bb->top;
+  obj->handles[2]->pos.x = bb->right;
+  obj->handles[2]->pos.y = bb->top;
+  obj->handles[3]->pos.x = bb->left;
+  obj->handles[3]->pos.y = (bb->top + bb->bottom) / 2.0;
+  obj->handles[4]->pos.x = bb->right;
+  obj->handles[4]->pos.y = (bb->top + bb->bottom) / 2.0;
+  obj->handles[5]->pos.x = bb->left;
+  obj->handles[5]->pos.y = bb->bottom;
+  obj->handles[6]->pos.x = (bb->left + bb->right) / 2.0;
+  obj->handles[6]->pos.y = bb->bottom;
+  obj->handles[7]->pos.x = bb->right;
+  obj->handles[7]->pos.y = bb->bottom;
+}
+/*!
+ * \brief Object update function called after property change
+ * 
+ * Not in the object interface but very important anyway.
+ * Used to recalculate the object data after a change
+ * \protected \memberof StdPath
+ */
+static void
+stdpath_update_data (StdPath *stdpath)
+{
+  DiaObject *obj = &stdpath->object;
+  Rectangle *bb = &obj->bounding_box;
+  PolyBBExtras extra = { 0 };
+  real lw = stdpath->stroke_or_fill & PDO_STROKE ? stdpath->line_width : 0.0;
+
+  extra.start_trans =
+  extra.end_trans = 
+  extra.start_long =
+  extra.end_long =
+  extra.middle_trans = lw/2.0;
+
+  /* recalculate the bounding box */
+  polybezier_bbox (stdpath->points, stdpath->num_points, &extra,
+		   (stdpath->stroke_or_fill & PDO_FILL), bb);
+  /* adjust position from it */
+  obj->position.x = bb->left;
+  obj->position.y = bb->top;
+  /* adjust handles */
+  stdpath_update_handles (stdpath);
+}
+
+/*!
+ * \brief Object drawing to the given renderer
+ *
+ * \memberof StdPath
+ */
+static void 
+stdpath_draw(StdPath *stdpath, DiaRenderer *renderer)
+{
+  DiaObject *obj = &stdpath->object;
+
+  DIA_RENDERER_GET_CLASS (renderer)->set_linewidth (renderer, stdpath->line_width);
+  DIA_RENDERER_GET_CLASS (renderer)->set_linestyle (renderer, stdpath->line_style);
+  DIA_RENDERER_GET_CLASS (renderer)->set_linejoin(renderer, stdpath->line_join);
+  DIA_RENDERER_GET_CLASS (renderer)->set_linecaps(renderer, stdpath->line_caps);
+
+  if (DIA_RENDERER_GET_CLASS (renderer)->is_capable_to(renderer, RENDER_HOLES)) {
+    if (stdpath->stroke_or_fill & PDO_FILL)
+      DIA_RENDERER_GET_CLASS (renderer)->fill_bezier(renderer, stdpath->points, stdpath->num_points, 
+						     &stdpath->fill_color);
+    if (stdpath->stroke_or_fill & PDO_STROKE)
+      DIA_RENDERER_GET_CLASS (renderer)->draw_bezier(renderer, stdpath->points, stdpath->num_points, 
+						     &stdpath->line_color);
+  } else {
+    /* step-wise approach */
+    if (stdpath->stroke_or_fill & PDO_FILL)
+      bezier_render_fill (renderer, stdpath->points, stdpath->num_points, &stdpath->fill_color);
+    if (stdpath->stroke_or_fill & PDO_STROKE)
+      bezier_render_stroke (renderer, stdpath->points, stdpath->num_points, &stdpath->line_color);
+  }
+  if (stdpath->show_control_lines)
+    bezier_draw_control_lines (stdpath->num_points, stdpath->points, renderer);
+}
+
+/*!
+ * \brief Convert _StdPath to one or more _BezierLine/BezierGon
+ */
+static ObjectChange *
+_convert_to_beziers_callback (DiaObject *obj, Point *clicked, gpointer data)
+{
+  StdPath *stdpath = (StdPath *)obj;
+  BezPoint *bezier = stdpath->points;
+  GList *list = NULL;
+  int i, n = 0;
+  ObjectChange *change;
+
+  for (i = 1; i < stdpath->num_points; ++i) {
+    if (bezier[i].type == BEZ_MOVE_TO || i+1 == stdpath->num_points) {
+      DiaObject *rep = (stdpath->stroke_or_fill & PDO_FILL)
+	? create_standard_beziergon (i - n + 1, &bezier[n])
+	: create_standard_bezierline (i - n + 1, &bezier[n], NULL, NULL);
+      if (!rep) /* no Standard objects? */
+	break;
+      list = g_list_append (list, rep);
+      n = i;
+    }
+  }
+  if (!list) {
+    change = change_list_create ();
+  } else if (g_list_length (list) == 1) {
+    change = object_substitute (obj, (DiaObject *)list->data);
+    g_list_free (list);
+  } else {
+    change = object_substitute (obj, create_standard_group (list));
+  }
+
+  return change;
+}
+static ObjectChange *
+_show_control_lines (DiaObject *obj, Point *clicked, gpointer data)
+{
+  StdPath *stdpath = (StdPath *)obj;
+
+  return object_toggle_prop(obj, "show_control_lines", !stdpath->show_control_lines);
+}
+
+static DiaMenuItem _stdpath_menu_items[] = {
+  { N_("Convert to Bezier"), _convert_to_beziers_callback, NULL, DIAMENU_ACTIVE },
+  { N_("Show Control Lines"), _show_control_lines, NULL, DIAMENU_ACTIVE }
+};
+static DiaMenu _stdpath_menu = {
+  "Path",
+  sizeof(_stdpath_menu_items)/sizeof(DiaMenuItem),
+  _stdpath_menu_items,
+  NULL
+};
+
+/*!
+ * \brief Optionally deliver an object specific menu
+ *
+ * returning NULL used to crash Dia, the method itself needed to be NULL if there is nothing to do
+ *
+ * \memberof StdPath
+ */
+static DiaMenu *
+stdpath_get_object_menu(StdPath *stdpath, Point *clickedpoint)
+{
+  return &_stdpath_menu;
+}
+/*!
+ * \brief Set the object state from the given proeprty vector
+ * \memberof StdPath
+ */
+static void 
+stdpath_set_props (StdPath *stdpath, GPtrArray *props)
+{
+  object_set_props_from_offsets(&stdpath->object, stdpath_offsets, props);
+  stdpath_update_data (stdpath);
+}
+/*!
+ * \brief Calculate the distance of the whole object to the given point
+ * \memberof StdPath
+ */
+static real
+stdpath_distance_from (StdPath *stdpath, Point *point)
+{
+  real lw = stdpath->stroke_or_fill & PDO_STROKE ? stdpath->line_width : 0.0;
+
+  if (stdpath->stroke_or_fill & PDO_FILL)
+    return distance_bez_shape_point (stdpath->points, stdpath->num_points, lw, point);
+  else
+    return distance_bez_line_point  (stdpath->points, stdpath->num_points, lw, point);
+}
+
+static void
+_stdpath_scale (StdPath *stdpath, real sx, real sy)
+{
+  Point p0 = stdpath->object.position;
+  int i;
+
+  for (i = 0; i < stdpath->num_points; ++i) {
+    BezPoint *bp = &stdpath->points[i];
+
+    bp->p1.x = p0.x + (bp->p1.x - p0.x) * sx;
+    bp->p1.y = p0.y + (bp->p1.y - p0.y) * sy;
+    bp->p2.x = p0.x + (bp->p2.x - p0.x) * sx;
+    bp->p2.y = p0.y + (bp->p2.y - p0.y) * sy;
+    bp->p3.x = p0.x + (bp->p3.x - p0.x) * sx;
+    bp->p3.y = p0.y + (bp->p3.y - p0.y) * sy;
+  }
+}
+
+/*!
+ * \brief Move one of the objects handles
+ * \memberof StdPath
+ */
+static ObjectChange* 
+stdpath_move_handle (StdPath *stdpath,
+		     Handle *handle,
+		     Point *to, ConnectionPoint *cp,
+		     HandleMoveReason reason, ModifierKeys modifiers)
+{
+  DiaObject *obj = &stdpath->object;
+  Point p0 = obj->position;
+
+  if (stdpath->move_reason != reason) {
+    if (HANDLE_MOVE_USER == reason) g_print ("move-user");
+    else if (HANDLE_MOVE_USER_FINAL == reason) g_print ("move-user-final\n");
+    else if (HANDLE_MOVE_CONNECTED == reason) g_print ("move-connected\n");
+    else g_print ("move-?reason?\n");
+    stdpath->move_reason = reason;
+  } else {
+    g_print ("*");
+  }
+  if (handle->id == HANDLE_RESIZE_SE) {
+    /* scale both directions - keep aspect ratio? */
+    Point p1 = stdpath->handles[7].pos;
+    real sx, sy;
+
+    g_assert(stdpath->handles[7].id == handle->id);
+
+    sx = (to->x - p0.x) / (p1.x - p0.x);
+    sy = (to->y - p0.y) / (p1.y - p0.y);
+    _stdpath_scale (stdpath, sx, sy);
+    stdpath_update_data (stdpath);
+
+  } else if (handle->id == HANDLE_RESIZE_S) {
+    /* scale height */
+    Point p1 = stdpath->handles[6].pos;
+    real sy;
+    int i;
+
+    g_assert(stdpath->handles[6].id == handle->id);
+    sy = (to->y - p0.y) / (p1.y - p0.y);
+    for (i = 0; i < stdpath->num_points; ++i) {
+      BezPoint *bp = &stdpath->points[i];
+
+      bp->p1.y = p0.y + (bp->p1.y - p0.y) * sy;
+      bp->p2.y = p0.y + (bp->p2.y - p0.y) * sy;
+      bp->p3.y = p0.y + (bp->p3.y - p0.y) * sy;
+    }
+    stdpath_update_data (stdpath);
+
+  } else if (handle->id == HANDLE_RESIZE_E) {
+    /* scale width */
+    Point p1 = stdpath->handles[4].pos;
+    real sx;
+    int i;
+
+    g_assert(stdpath->handles[4].id == handle->id);
+
+    sx = (to->x - p0.x) / (p1.x - p0.x);
+    for (i = 0; i < stdpath->num_points; ++i) {
+      BezPoint *bp = &stdpath->points[i];
+
+      bp->p1.x = p0.x + (bp->p1.x - p0.x) * sx;
+      bp->p2.x = p0.x + (bp->p2.x - p0.x) * sx;
+      bp->p3.x = p0.x + (bp->p3.x - p0.x) * sx;
+    }
+    stdpath_update_data (stdpath);
+
+  } else if (handle->id == _HANDLE_SHEAR) {
+    /* apply horizontal shear weighted by the vertical distance */
+    Point p1 = stdpath->handles[1].pos;
+    Point pr = stdpath->handles[3].pos;
+    real dx; /* delta x */
+    real rd = (pr.y - p0.y); /* reference distance */
+    int i;
+
+    g_assert(stdpath->handles[1].id == handle->id);
+    g_assert(stdpath->handles[3].id == _HANDLE_REF_POS);
+    g_return_val_if_fail(rd > 0, NULL);
+    dx = to->x - p1.x;
+    for (i = 0; i < stdpath->num_points; ++i) {
+      BezPoint *bp = &stdpath->points[i];
+      real sw;
+
+      /* scaling weight [0..1] to not move the left boundary */
+      sw = (pr.y - bp->p1.y) / rd;
+      g_print ("sw: %.3g; dx: %.3g\n", sw, dx);
+      bp->p1.x += dx * sw;
+      sw = (pr.y - bp->p2.y) / rd;
+      bp->p2.x += dx * sw;
+      sw = (pr.y - bp->p3.y) / rd;
+      bp->p3.x += dx * sw;
+    }
+    stdpath_update_data (stdpath);
+    /* keep to.x - it's real position depends on the bounding box calculation */
+    dx = to->x - stdpath->handles[1].pos.x;
+    for (i = 0; i < stdpath->num_points; ++i) {
+      BezPoint *bp = &stdpath->points[i];
+
+      bp->p1.x += dx;
+      bp->p2.x += dx;
+      bp->p3.x += dx;
+    }
+
+    stdpath_update_data (stdpath);
+
+  } else if (handle->id == _HANDLE_ROTATE) {
+    Point p1 = stdpath->handles[2].pos;
+    real dy = to->y - p1.y;
+    real dx = p1.x - p0.x;
+    DiaMatrix m;
+    int i;
+
+    g_assert(stdpath->handles[2].id == handle->id);
+
+    if (fabs(dy) < 0.001)
+      return NULL;
+    dia_matrix_set_angle_and_scales (&m, asin (dy/dx), 1.0, 1.0);
+
+    for (i = 0; i < stdpath->num_points; ++i) {
+      BezPoint *bp = &stdpath->points[i];
+      int j;
+
+      for (j = 0; j < 3; ++j) {
+	Point *p = j == 0 ? &bp->p1 : (j == 1 ? &bp->p2 : &bp->p3);
+        real w, h;
+
+	w = p->x - p0.x;
+	h = p->y - p0.y;
+
+	p->x = p0.x + w * m.xx + h * m.xy;
+	p->y = p1.y + w * m.yx + h * m.yy;
+      }
+    }
+
+    stdpath_update_data (stdpath);
+
+  } else if (handle->id == _HANDLE_PERSPECTIVE) {
+    Point p1 = stdpath->handles[5].pos;
+    Point pr = stdpath->handles[3].pos;
+    int i;
+
+    g_assert(stdpath->handles[5].id == handle->id);
+
+    for (i = 0; i < stdpath->num_points; ++i) {
+      BezPoint *bp = &stdpath->points[i];
+
+    }
+
+    stdpath_update_data (stdpath);
+
+  } else if (handle->type != HANDLE_NON_MOVABLE) {
+    g_warning ("stdpath_move_handle() %d not moving", handle->id);
+  }
+  return NULL;
+}
+
+
+/*!
+ * \brief Move the whole object to the given position
+ * 
+ * If the object position does not change the whole object should not either.
+ * This is used as a kludge to call the protected update_data() function
+ *
+ * \memberof StdPath
+ */
+static ObjectChange* 
+stdpath_move (StdPath *stdpath, Point *to)
+{
+  DiaObject *obj = &stdpath->object;
+  Point delta = *to;
+  int i;
+
+  point_sub (&delta, &obj->position);
+  
+  for (i = 0; i < stdpath->num_points; ++i) {
+     BezPoint *bp = &stdpath->points[i];
+     
+     point_add (&bp->p1, &delta);
+     point_add (&bp->p2, &delta);
+     point_add (&bp->p3, &delta);
+  }
+  stdpath_update_data (stdpath);
+  return NULL;
+}
+/*!
+ * \brief Create a deep copy of the object
+ * \memberof StdPath
+ */
+static DiaObject *
+stdpath_copy (StdPath *from)
+{
+  DiaObject *to;
+  to = object_copy_using_properties (&from->object);
+  return to;
+}
+/*!
+ * \brief Destruction of the object
+ * \memberof StdPath
+ */
+static void 
+stdpath_destroy (StdPath *stdpath)
+{
+  object_destroy(&stdpath->object);
+  /* but not the object itself */
+}
+/*!
+ * \brief Change the object state regarding selection 
+ * \memberof StdPath
+ */
+static void 
+stdpath_select (StdPath *stdpath, Point *clicked_point,
+		DiaRenderer *interactive_renderer)
+{
+  stdpath_update_handles (stdpath);
+}
+
+DiaObject *
+create_standard_path_from_text (const Text *text)
+{
+  cairo_t *cr;
+  cairo_surface_t *surface;
+  PangoLayout *layout;
+  PangoRectangle ink_rect;
+  char *str;
+  DiaObject *obj = NULL;
+
+  layout = pango_layout_new(dia_font_get_context());
+  pango_layout_set_font_description (layout, dia_font_get_description (text->font));
+  pango_layout_set_indent (layout, 0);
+  pango_layout_set_justify (layout, FALSE);
+  pango_layout_set_alignment (layout, 
+			      text->alignment == ALIGN_LEFT ? PANGO_ALIGN_LEFT :
+			      text->alignment == ALIGN_RIGHT ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_CENTER);
+
+  str = text_get_string_copy (text);
+  pango_layout_set_text (layout, str, -1);
+  g_free (str);
+
+  pango_layout_get_extents (layout, &ink_rect, NULL);
+  /* any surface should do - this one is always available */
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+					ink_rect.width / PANGO_SCALE, ink_rect.height / PANGO_SCALE);
+  cr = cairo_create (surface);
+  cairo_surface_destroy (surface);
+
+  pango_cairo_layout_path (cr, layout);
+  {
+    cairo_path_t *path;
+    GArray *points = g_array_new (FALSE, FALSE, sizeof(BezPoint));
+    int i;
+
+    path = cairo_copy_path (cr);
+    g_array_set_size (points, 0);
+
+    for (i=0; i < path->num_data; i += path->data[i].header.length) {
+      cairo_path_data_t *data = &path->data[i];
+      BezPoint bp;
+
+      switch (data->header.type) {
+      case CAIRO_PATH_MOVE_TO :
+	bp.type = BEZ_MOVE_TO;
+	bp.p1.x = data[1].point.x; bp.p1.y = data[1].point.y;
+	break;
+      case CAIRO_PATH_LINE_TO :
+        bp.type = BEZ_LINE_TO;
+	bp.p1.x = data[1].point.x; bp.p1.y = data[1].point.y;
+	break;
+      case CAIRO_PATH_CURVE_TO :
+        bp.type = BEZ_CURVE_TO;
+	bp.p1.x = data[1].point.x; bp.p1.y = data[1].point.y;
+	bp.p2.x = data[2].point.x; bp.p2.y = data[2].point.y;
+	bp.p3.x = data[3].point.x; bp.p3.y = data[3].point.y;
+	break;
+      case CAIRO_PATH_CLOSE_PATH :
+        /* can't do anything */
+      default :
+        continue;
+      }
+      g_array_append_val (points, bp);
+    }
+    obj = create_standard_path (points->len, &g_array_index (points, BezPoint, 0));
+    g_array_free (points, TRUE);
+  }
+  if (obj) {
+    StdPath *path = (StdPath *)obj;
+    Rectangle text_box;
+    const Rectangle *pbb = &path->object.bounding_box;
+    real sx, sy;
+    Point pos;
+
+    path->stroke_or_fill = PDO_FILL;
+    path->fill_color = text->color;
+
+    /* scale to fit the original size */
+    text_calc_boundingbox ((Text *)text, &text_box);
+    pos.x = text_box.left; pos.y = text_box.top;
+    sx = (text_box.right - text_box.left) / (pbb->right - pbb->left);
+    sy = (text_box.bottom - text_box.top) / (pbb->bottom - pbb->top);
+    _stdpath_scale (path, sx, sy);
+
+    /* also adjust top left corner - calling update, too */
+    stdpath_move (path, &pos);
+  }
+  g_object_unref (layout);
+  cairo_destroy (cr);
+
+  return obj;
+}
diff --git a/lib/standard-path.h b/lib/standard-path.h
new file mode 100644
index 0000000..b86d78d
--- /dev/null
+++ b/lib/standard-path.h
@@ -0,0 +1,30 @@
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * standard-path.c -- path based object drawing
+ * Copyright (C) 2012, Hans Breuer <Hans Breuer Org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef STANDARD_PATH_H
+#define STANDARD_PATH_H
+
+#include "diarenderer.h"
+
+/* there should be no need to use DIAVAR */
+extern DiaObjectType stdpath_type;
+
+#endif /* STANDARD_PATH_H */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]