summaryrefslogtreecommitdiffstats
path: root/plug-ins/gfig/gfig-dobject.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 03:13:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 03:13:10 +0000
commit3c57dd931145d43f2b0aef96c4d178135956bf91 (patch)
tree3de698981e9f0cc2c4f9569b19a5f3595e741f6b /plug-ins/gfig/gfig-dobject.c
parentInitial commit. (diff)
downloadgimp-3c57dd931145d43f2b0aef96c4d178135956bf91.tar.xz
gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.zip
Adding upstream version 2.10.36.upstream/2.10.36
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plug-ins/gfig/gfig-dobject.c')
-rw-r--r--plug-ins/gfig/gfig-dobject.c1063
1 files changed, 1063 insertions, 0 deletions
diff --git a/plug-ins/gfig/gfig-dobject.c b/plug-ins/gfig/gfig-dobject.c
new file mode 100644
index 0000000..1340cec
--- /dev/null
+++ b/plug-ins/gfig/gfig-dobject.c
@@ -0,0 +1,1063 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * 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 3 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gfig.h"
+#include "gfig-dialog.h"
+#include "gfig-style.h"
+#include "gfig-arc.h"
+#include "gfig-bezier.h"
+#include "gfig-circle.h"
+#include "gfig-dobject.h"
+#include "gfig-ellipse.h"
+#include "gfig-line.h"
+#include "gfig-poly.h"
+#include "gfig-rectangle.h"
+#include "gfig-spiral.h"
+#include "gfig-star.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static GfigObject *operation_obj = NULL;
+static GdkPoint *move_all_pnt; /* Point moving all from */
+
+
+static void draw_one_obj (GfigObject *obj,
+ cairo_t *cr);
+static void do_move_obj (GfigObject *obj,
+ GdkPoint *to_pnt);
+static void do_move_all_obj (GdkPoint *to_pnt);
+static void do_move_obj_pnt (GfigObject *obj,
+ GdkPoint *to_pnt);
+static void remove_obj_from_list (GFigObj *obj,
+ GfigObject *del_obj);
+static gint scan_obj_points (DobjPoints *opnt,
+ GdkPoint *pnt);
+
+void
+d_save_object (GfigObject *obj,
+ GString *string)
+{
+ do_save_obj (obj, string);
+
+ switch (obj->type)
+ {
+ case BEZIER:
+ case POLY:
+ case SPIRAL:
+ case STAR:
+ g_string_append_printf (string, "<EXTRA>\n");
+ g_string_append_printf (string, "%d\n</EXTRA>\n", obj->type_data);
+ break;
+ default:
+ break;
+ }
+}
+
+static DobjType
+gfig_read_object_type (gchar *desc)
+{
+ gchar *ptr = desc;
+ DobjType type;
+
+ if (*ptr != '<')
+ return OBJ_TYPE_NONE;
+
+ ptr++;
+
+ for (type = LINE; type < NUM_OBJ_TYPES; type++)
+ {
+ if (ptr == strstr (ptr, dobj_class[type].name))
+ return type;
+ }
+
+ return OBJ_TYPE_NONE;
+}
+
+GfigObject *
+d_load_object (gchar *desc,
+ FILE *fp)
+{
+ GfigObject *new_obj = NULL;
+ gint xpnt;
+ gint ypnt;
+ gchar buf[MAX_LOAD_LINE];
+ DobjType type;
+
+ type = gfig_read_object_type (desc);
+ if (type == OBJ_TYPE_NONE)
+ {
+ g_message ("Error loading object: type not recognized.");
+ return NULL;
+ }
+
+ while (get_line (buf, MAX_LOAD_LINE, fp, 0))
+ {
+ if (sscanf (buf, "%d %d", &xpnt, &ypnt) != 2)
+ {
+ /* Read <EXTRA> block if there is one */
+ if (!strcmp ("<EXTRA>", buf))
+ {
+ if ( !new_obj)
+ {
+ g_message ("Error while loading object (no points)");
+ return NULL;
+ }
+
+ get_line (buf, MAX_LOAD_LINE, fp, 0);
+
+ if (sscanf (buf, "%d", &new_obj->type_data) != 1)
+ {
+ g_message ("Error while loading object (no type data)");
+ g_free (new_obj);
+ return NULL;
+ }
+
+ get_line (buf, MAX_LOAD_LINE, fp, 0);
+ if (strcmp ("</EXTRA>", buf))
+ {
+ g_message ("Syntax error while loading object");
+ g_free (new_obj);
+ return NULL;
+ }
+ /* Go around and read the last line */
+ continue;
+ }
+ else
+ return new_obj;
+ }
+
+ if (!new_obj)
+ new_obj = d_new_object (type, xpnt, ypnt);
+ else
+ d_pnt_add_line (new_obj, xpnt, ypnt, -1);
+ }
+
+ return new_obj;
+}
+
+GfigObject *
+d_new_object (DobjType type,
+ gint x,
+ gint y)
+{
+ GfigObject *nobj = g_new0 (GfigObject, 1);
+
+ nobj->type = type;
+ nobj->class = &dobj_class[type];
+ nobj->points = new_dobjpoint (x, y);
+
+ nobj->type_data = 0;
+
+ if (type == BEZIER)
+ {
+ nobj->type_data = 4;
+ }
+ else if (type == POLY)
+ {
+ nobj->type_data = 3; /* default to 3 sides */
+ }
+ else if (type == SPIRAL)
+ {
+ nobj->type_data = 4; /* default to 4 turns */
+ }
+ else if (type == STAR)
+ {
+ nobj->type_data = 3; /* default to 3 sides 6 points */
+ }
+
+ return nobj;
+}
+
+void
+gfig_init_object_classes (void)
+{
+ d_arc_object_class_init ();
+ d_line_object_class_init ();
+ d_rectangle_object_class_init ();
+ d_circle_object_class_init ();
+ d_ellipse_object_class_init ();
+ d_poly_object_class_init ();
+ d_spiral_object_class_init ();
+ d_star_object_class_init ();
+ d_bezier_object_class_init ();
+}
+
+/* Delete a list of points */
+void
+d_delete_dobjpoints (DobjPoints * pnts)
+{
+ DobjPoints *next;
+ DobjPoints *pnt2del = pnts;
+
+ while (pnt2del)
+ {
+ next = pnt2del->next;
+ g_free (pnt2del);
+ pnt2del = next;
+ }
+}
+
+DobjPoints *
+new_dobjpoint (gint x, gint y)
+{
+ DobjPoints *npnt = g_new0 (DobjPoints, 1);
+
+ npnt->pnt.x = x;
+ npnt->pnt.y = y;
+
+ return npnt;
+}
+
+DobjPoints *
+d_copy_dobjpoints (DobjPoints *pnts)
+{
+ DobjPoints *ret = NULL;
+ DobjPoints *head = NULL;
+ DobjPoints *newpnt;
+ DobjPoints *pnt2copy;
+
+ for (pnt2copy = pnts; pnt2copy; pnt2copy = pnt2copy->next)
+ {
+ newpnt = new_dobjpoint (pnt2copy->pnt.x, pnt2copy->pnt.y);
+
+ if (!ret)
+ {
+ head = ret = newpnt;
+ }
+ else
+ {
+ head->next = newpnt;
+ head = newpnt;
+ }
+ }
+
+ return ret;
+}
+
+static DobjPoints *
+get_diffs (GfigObject *obj,
+ gint *xdiff,
+ gint *ydiff,
+ GdkPoint *to_pnt)
+{
+ DobjPoints *spnt;
+
+ g_assert (obj != NULL);
+
+ for (spnt = obj->points; spnt; spnt = spnt->next)
+ {
+ if (spnt->found_me)
+ {
+ *xdiff = spnt->pnt.x - to_pnt->x;
+ *ydiff = spnt->pnt.y - to_pnt->y;
+ return spnt;
+ }
+ }
+ return NULL;
+}
+
+static gboolean
+inside_sqr (GdkPoint *cpnt,
+ GdkPoint *testpnt)
+{
+ /* Return TRUE if testpnt is near cpnt */
+ gint x = cpnt->x;
+ gint y = cpnt->y;
+ gint tx = testpnt->x;
+ gint ty = testpnt->y;
+
+ return (abs (x - tx) <= SQ_SIZE && abs (y - ty) < SQ_SIZE);
+}
+
+static gboolean
+scan_obj_points (DobjPoints *opnt,
+ GdkPoint *pnt)
+{
+ while (opnt)
+ {
+ if (inside_sqr (&opnt->pnt, pnt))
+ {
+ opnt->found_me = TRUE;
+ return TRUE;
+ }
+ opnt->found_me = FALSE;
+ opnt = opnt->next;
+ }
+ return FALSE;
+}
+
+static GfigObject *
+get_nearest_objs (GFigObj *obj,
+ GdkPoint *pnt)
+{
+ /* Nearest object to given point or NULL */
+ GList *all;
+ GfigObject *test_obj;
+ gint count = 0;
+
+ if (!obj)
+ return NULL;
+
+ for (all = obj->obj_list; all; all = g_list_next (all))
+ {
+ test_obj = all->data;
+
+ if (count == obj_show_single || obj_show_single == -1)
+ if (scan_obj_points (test_obj->points, pnt))
+ {
+ return test_obj;
+ }
+ count++;
+ }
+ return NULL;
+}
+
+void
+object_operation_start (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ GfigObject *new_obj;
+
+ /* Find point in given object list */
+ operation_obj = get_nearest_objs (gfig_context->current_obj, pnt);
+
+ /* Special case if shift down && move obj then moving all objs */
+
+ if (shift_down && selvals.otype == MOVE_OBJ)
+ {
+ move_all_pnt = g_new (GdkPoint, 1);
+ *move_all_pnt = *pnt; /* Structure copy */
+ setup_undo ();
+ return;
+ }
+
+ if (!operation_obj)
+ return;/* None to work on */
+
+ gfig_context->selected_obj = operation_obj;
+
+ setup_undo ();
+
+ switch (selvals.otype)
+ {
+ case MOVE_OBJ:
+ if (operation_obj->type == BEZIER)
+ {
+ tmp_bezier = operation_obj;
+ }
+ break;
+
+ case MOVE_POINT:
+ if (operation_obj->type == BEZIER)
+ {
+ tmp_bezier = operation_obj;
+ }
+ /* If shift is down the break into sep lines */
+ if ((operation_obj->type == POLY
+ || operation_obj->type == STAR)
+ && shift_down)
+ {
+ switch (operation_obj->type)
+ {
+ case POLY:
+ d_poly2lines (operation_obj);
+ break;
+ case STAR:
+ d_star2lines (operation_obj);
+ break;
+ default:
+ break;
+ }
+ /* Re calc which object point we are looking at */
+ scan_obj_points (operation_obj->points, pnt);
+ gtk_widget_queue_draw (gfig_context->preview);
+ }
+ break;
+
+ case COPY_OBJ:
+ /* Copy the "operation object" */
+ /* Then bung us into "copy/move" mode */
+
+ new_obj = (GfigObject*) operation_obj->class->copyfunc (operation_obj);
+ if (new_obj)
+ {
+ gfig_style_copy (&new_obj->style, &operation_obj->style, "Object");
+ scan_obj_points (new_obj->points, pnt);
+ add_to_all_obj (gfig_context->current_obj, new_obj);
+ operation_obj = new_obj;
+ selvals.otype = MOVE_COPY_OBJ;
+ gtk_widget_queue_draw (gfig_context->preview);
+ }
+ break;
+
+ case DEL_OBJ:
+ remove_obj_from_list (gfig_context->current_obj, operation_obj);
+ break;
+
+ case SELECT_OBJ:
+ /* don't need to do anything */
+ break;
+
+ case MOVE_COPY_OBJ: /* Never when button down */
+ default:
+ g_warning ("Internal error selvals.otype object operation start");
+ break;
+ }
+
+}
+
+void
+object_operation_end (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ if (selvals.otype != DEL_OBJ && operation_obj &&
+ operation_obj->type == BEZIER)
+ {
+ tmp_bezier = NULL; /* use as switch */
+ }
+
+ if (operation_obj && selvals.otype != DEL_OBJ)
+ gfig_style_set_context_from_style (&operation_obj->style);
+
+ operation_obj = NULL;
+
+ if (move_all_pnt)
+ {
+ g_free (move_all_pnt);
+ move_all_pnt = NULL;
+ }
+
+ /* Special case - if copying mode MUST be copy when button up received */
+ if (selvals.otype == MOVE_COPY_OBJ)
+ selvals.otype = COPY_OBJ;
+}
+
+/* Move object around */
+void
+object_operation (GdkPoint *to_pnt,
+ gboolean shift_down)
+{
+ /* Must do different things depending on object type */
+ /* but must have object to operate on! */
+
+ /* Special case - if shift own and move_obj then move ALL objects */
+ if (move_all_pnt && shift_down && selvals.otype == MOVE_OBJ)
+ {
+ do_move_all_obj (to_pnt);
+ return;
+ }
+
+ if (!operation_obj)
+ return;
+
+ switch (selvals.otype)
+ {
+ case MOVE_OBJ:
+ case MOVE_COPY_OBJ:
+ switch (operation_obj->type)
+ {
+ case LINE:
+ case RECTANGLE:
+ case CIRCLE:
+ case ELLIPSE:
+ case POLY:
+ case ARC:
+ case STAR:
+ case SPIRAL:
+ case BEZIER:
+ do_move_obj (operation_obj, to_pnt);
+ break;
+ default:
+ /* Internal error */
+ g_warning ("Internal error in operation_obj->type");
+ break;
+ }
+ break;
+ case MOVE_POINT:
+ switch (operation_obj->type)
+ {
+ case LINE:
+ case RECTANGLE:
+ case CIRCLE:
+ case ELLIPSE:
+ case POLY:
+ case ARC:
+ case STAR:
+ case SPIRAL:
+ case BEZIER:
+ do_move_obj_pnt (operation_obj, to_pnt);
+ break;
+ default:
+ /* Internal error */
+ g_warning ("Internal error in operation_obj->type");
+ break;
+ }
+ break;
+ case DEL_OBJ:
+ case SELECT_OBJ:
+ break;
+ case COPY_OBJ: /* Should have been changed to MOVE_COPY_OBJ */
+ default:
+ g_warning ("Internal error selvals.otype");
+ break;
+ }
+}
+
+static void
+update_pnts (GfigObject *obj,
+ gint xdiff,
+ gint ydiff)
+{
+ DobjPoints *spnt;
+
+ g_assert (obj != NULL);
+
+ /* Update all pnts */
+ for (spnt = obj->points; spnt; spnt = spnt->next)
+ {
+ spnt->pnt.x -= xdiff;
+ spnt->pnt.y -= ydiff;
+ }
+}
+
+static void
+remove_obj_from_list (GFigObj *obj,
+ GfigObject *del_obj)
+{
+ /* Nearest object to given point or NULL */
+
+ g_assert (del_obj != NULL);
+
+ if (g_list_find (obj->obj_list, del_obj))
+ {
+ obj->obj_list = g_list_remove (obj->obj_list, del_obj);
+
+ free_one_obj (del_obj);
+
+ if (obj->obj_list)
+ gfig_context->selected_obj = obj->obj_list->data;
+ else
+ gfig_context->selected_obj = NULL;
+
+ if (obj_show_single != -1)
+ {
+ /* We've just deleted the only visible one */
+ draw_grid_clear ();
+ obj_show_single = -1; /* Show entry again */
+ }
+
+ gtk_widget_queue_draw (gfig_context->preview);
+ }
+ else
+ g_warning (_("Hey, where has the object gone?"));
+}
+
+static void
+do_move_all_obj (GdkPoint *to_pnt)
+{
+ /* Move all objects in one go */
+ /* Undraw/then draw in new pos */
+ gint xdiff = move_all_pnt->x - to_pnt->x;
+ gint ydiff = move_all_pnt->y - to_pnt->y;
+
+ if (xdiff || ydiff)
+ {
+ GList *all;
+
+ for (all = gfig_context->current_obj->obj_list; all; all = all->next)
+ {
+ GfigObject *obj = all->data;
+
+ update_pnts (obj, xdiff, ydiff);
+ }
+
+ *move_all_pnt = *to_pnt;
+
+ gtk_widget_queue_draw (gfig_context->preview);
+ }
+}
+
+void
+do_save_obj (GfigObject *obj,
+ GString *string)
+{
+ DobjPoints *spnt;
+
+ for (spnt = obj->points; spnt; spnt = spnt->next)
+ {
+ g_string_append_printf (string, "%d %d\n", spnt->pnt.x, spnt->pnt.y);
+ }
+}
+
+static void
+do_move_obj (GfigObject *obj,
+ GdkPoint *to_pnt)
+{
+ /* Move the whole line - undraw the line to start with */
+ /* Then draw in new pos */
+ gint xdiff = 0;
+ gint ydiff = 0;
+
+ get_diffs (obj, &xdiff, &ydiff, to_pnt);
+
+ if (xdiff || ydiff)
+ {
+ update_pnts (obj, xdiff, ydiff);
+
+ gtk_widget_queue_draw (gfig_context->preview);
+ }
+}
+
+static void
+do_move_obj_pnt (GfigObject *obj,
+ GdkPoint *to_pnt)
+{
+ /* Move the whole line - undraw the line to start with */
+ /* Then draw in new pos */
+ DobjPoints *spnt;
+ gint xdiff = 0;
+ gint ydiff = 0;
+
+ spnt = get_diffs (obj, &xdiff, &ydiff, to_pnt);
+
+ if ((!xdiff && !ydiff) || !spnt)
+ return;
+
+ spnt->pnt.x = spnt->pnt.x - xdiff;
+ spnt->pnt.y = spnt->pnt.y - ydiff;
+
+ /* Draw in new pos */
+ gtk_widget_queue_draw (gfig_context->preview);
+}
+
+/* copy objs */
+GList *
+copy_all_objs (GList *objs)
+{
+ GList *new_all_objs = NULL;
+
+ while (objs)
+ {
+ GfigObject *object = objs->data;
+ GfigObject *new_object = (GfigObject *) object->class->copyfunc (object);
+ gfig_style_copy (&new_object->style, &object->style, "Object");
+
+ new_all_objs = g_list_prepend (new_all_objs, new_object);
+
+ objs = objs->next;
+ }
+
+ new_all_objs = g_list_reverse (new_all_objs);
+
+ return new_all_objs;
+}
+
+/* Screen refresh */
+static void
+draw_one_obj (GfigObject *obj,
+ cairo_t *cr)
+{
+ obj->class->drawfunc (obj, cr);
+}
+
+void
+draw_objects (GList *objs,
+ gboolean show_single,
+ cairo_t *cr)
+{
+ /* Show_single - only one object to draw Unless shift
+ * is down in which case show all.
+ */
+
+ gint count = 0;
+
+ while (objs)
+ {
+ if (!show_single || count == obj_show_single || obj_show_single == -1)
+ draw_one_obj (objs->data, cr);
+
+ objs = g_list_next (objs);
+ count++;
+ }
+}
+
+void
+prepend_to_all_obj (GFigObj *fobj,
+ GList *nobj)
+{
+ setup_undo (); /* Remember ME */
+
+ fobj->obj_list = g_list_concat (fobj->obj_list, nobj);
+}
+
+static void
+scale_obj_points (DobjPoints *opnt,
+ gdouble scale_x,
+ gdouble scale_y)
+{
+ while (opnt)
+ {
+ opnt->pnt.x = (gint) (opnt->pnt.x * scale_x);
+ opnt->pnt.y = (gint) (opnt->pnt.y * scale_y);
+ opnt = opnt->next;
+ }
+}
+
+void
+add_to_all_obj (GFigObj *fobj,
+ GfigObject *obj)
+{
+ GList *nobj = NULL;
+
+ nobj = g_list_append (nobj, obj);
+
+ if (need_to_scale)
+ scale_obj_points (obj->points, scale_x_factor, scale_y_factor);
+
+ prepend_to_all_obj (fobj, nobj);
+
+ /* initialize style when we add the object */
+ gfig_context->selected_obj = obj;
+}
+
+/* First button press -- start drawing object */
+/*
+ * object_start() creates a new object of the type specified in the
+ * button panel. It is activated by a button press, and causes
+ * a small square to be drawn at the initial point. The style of
+ * the new object is set to values taken from the style control
+ * widgets.
+ */
+void
+object_start (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ /* start for the current object */
+ if (!selvals.scaletoimage)
+ {
+ need_to_scale = 1;
+ selvals.scaletoimage = 1;
+ }
+ else
+ {
+ need_to_scale = 0;
+ }
+
+ switch (selvals.otype)
+ {
+ case LINE:
+ /* Shift means we are still drawing */
+ d_line_start (pnt, shift_down);
+ break;
+ case RECTANGLE:
+ d_rectangle_start (pnt, shift_down);
+ break;
+ case CIRCLE:
+ d_circle_start (pnt, shift_down);
+ break;
+ case ELLIPSE:
+ d_ellipse_start (pnt, shift_down);
+ break;
+ case POLY:
+ d_poly_start (pnt, shift_down);
+ break;
+ case ARC:
+ d_arc_start (pnt, shift_down);
+ break;
+ case STAR:
+ d_star_start (pnt, shift_down);
+ break;
+ case SPIRAL:
+ d_spiral_start (pnt, shift_down);
+ break;
+ case BEZIER:
+ d_bezier_start (pnt, shift_down);
+ break;
+ default:
+ /* Internal error */
+ break;
+ }
+
+ if (obj_creating)
+ {
+ if (gfig_context->debug_styles)
+ g_printerr ("Creating object, setting style from context\n");
+ gfig_style_set_style_from_context (&obj_creating->style);
+ }
+
+}
+
+void
+object_end (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ /* end for the current object */
+ /* Add onto global object list */
+
+ /* If shift is down may carry on drawing */
+ switch (selvals.otype)
+ {
+ case LINE:
+ d_line_end (pnt, shift_down);
+ break;
+ case RECTANGLE:
+ d_rectangle_end (pnt, shift_down);
+ break;
+ case CIRCLE:
+ d_circle_end (pnt, shift_down);
+ break;
+ case ELLIPSE:
+ d_ellipse_end (pnt, shift_down);
+ break;
+ case POLY:
+ d_poly_end (pnt, shift_down);
+ break;
+ case STAR:
+ d_star_end (pnt, shift_down);
+ break;
+ case ARC:
+ d_arc_end (pnt, shift_down);
+ break;
+ case SPIRAL:
+ d_spiral_end (pnt, shift_down);
+ break;
+ case BEZIER:
+ d_bezier_end (pnt, shift_down);
+ break;
+ default:
+ /* Internal error */
+ break;
+ }
+
+ if (need_to_scale)
+ {
+ need_to_scale = 0;
+ selvals.scaletoimage = 0;
+ }
+}
+
+/* Stuff for the generation/deletion of objects. */
+
+/* Objects are easy one they are created - you just go down the object
+ * list calling the draw function for each object but... when they
+ * are been created we have to be a little more careful. When
+ * the first point is placed on the canvas we create the object,
+ * the mouse position then defines the next point that can move around.
+ * careful how we draw this position.
+ */
+
+void
+free_one_obj (GfigObject *obj)
+{
+ d_delete_dobjpoints (obj->points);
+ g_free (obj);
+}
+
+void
+free_all_objs (GList *objs)
+{
+ g_list_free_full (objs, (GDestroyNotify) free_one_obj);
+}
+
+gchar *
+get_line (gchar *buf,
+ gint s,
+ FILE *from,
+ gint init)
+{
+ gint slen;
+ char *ret;
+
+ if (init)
+ line_no = 1;
+ else
+ line_no++;
+
+ do
+ {
+ ret = fgets (buf, s, from);
+ }
+ while (!ferror (from) && buf[0] == '#');
+
+ slen = strlen (buf);
+
+ /* The last newline is a pain */
+ if (slen > 0)
+ buf[slen - 1] = '\0';
+
+ /* Check and remove an '\r' too from Windows */
+ if ((slen > 1) && (buf[slen - 2] == '\r'))
+ buf[slen - 2] = '\0';
+
+ if (ferror (from))
+ {
+ g_warning (_("Error reading file"));
+ return NULL;
+ }
+
+#ifdef DEBUG
+ printf ("Processing line '%s'\n", buf);
+#endif /* DEBUG */
+
+ return ret;
+}
+
+void
+clear_undo (void)
+{
+ int lv;
+
+ for (lv = undo_level; lv >= 0; lv--)
+ {
+ free_all_objs (undo_table[lv]);
+ undo_table[lv] = NULL;
+ }
+
+ undo_level = -1;
+
+ gfig_dialog_action_set_sensitive ("undo", FALSE);
+}
+
+void
+setup_undo (void)
+{
+ /* Copy object list to undo buffer */
+#if DEBUG
+ printf ("setup undo level [%d]\n", undo_level);
+#endif /*DEBUG*/
+
+ if (!gfig_context->current_obj)
+ {
+ /* If no current_obj must be loading -> no undo */
+ return;
+ }
+
+ if (undo_level >= selvals.maxundo - 1)
+ {
+ int loop;
+ /* the little one in the bed said "roll over".. */
+ if (undo_table[0])
+ free_one_obj (undo_table[0]->data);
+ for (loop = 0; loop < undo_level; loop++)
+ {
+ undo_table[loop] = undo_table[loop + 1];
+ }
+ }
+ else
+ {
+ undo_level++;
+ }
+ undo_table[undo_level] =
+ copy_all_objs (gfig_context->current_obj->obj_list);
+
+ gfig_dialog_action_set_sensitive ("undo", TRUE);
+
+ gfig_context->current_obj->obj_status |= GFIG_MODIFIED;
+}
+
+void
+new_obj_2edit (GFigObj *obj)
+{
+ GFigObj *old_current = gfig_context->current_obj;
+
+ /* Clear undo levels */
+ /* redraw the preview */
+ /* Set up options as define in the selected object */
+
+ clear_undo ();
+
+ /* Point at this one */
+ gfig_context->current_obj = obj;
+
+ /* Show all objects to start with */
+ obj_show_single = -1;
+
+ /* Change options */
+ options_update (old_current);
+
+ /* redraw with new */
+ gtk_widget_queue_draw (gfig_context->preview);
+
+ if (obj->obj_status & GFIG_READONLY)
+ {
+ g_message (_("Editing read-only object - "
+ "you will not be able to save it"));
+
+ gfig_dialog_action_set_sensitive ("save", FALSE);
+ }
+ else
+ {
+ gfig_dialog_action_set_sensitive ("save", TRUE);
+ }
+}
+
+/* Add a point to a line (given x, y)
+ * pos = 0 = head
+ * pos = -1 = tail
+ * 0 < pos = nth position
+ */
+
+void
+d_pnt_add_line (GfigObject *obj,
+ gint x,
+ gint y,
+ gint pos)
+{
+ DobjPoints *npnts = new_dobjpoint (x, y);
+
+ g_assert (obj != NULL);
+
+ if (!pos)
+ {
+ /* Add to head */
+ npnts->next = obj->points;
+ obj->points = npnts;
+ }
+ else
+ {
+ DobjPoints *pnt = obj->points;
+
+ /* Go down chain until the end if pos */
+ while (pos < 0 || pos-- > 0)
+ {
+ if (!(pnt->next) || !pos)
+ {
+ npnts->next = pnt->next;
+ pnt->next = npnts;
+ break;
+ }
+ else
+ {
+ pnt = pnt->next;
+ }
+ }
+ }
+}