1 /*
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This is a plug-in for GIMP.
5  *
6  * Generates images containing vector type drawings.
7  *
8  * Copyright (C) 1997 Andy Thomas  alt@picnic.demon.co.uk
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
22  */
23 
24 #include "config.h"
25 
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include <libgimp/gimp.h>
30 #include <libgimp/gimpui.h>
31 
32 #include "gfig.h"
33 #include "gfig-dialog.h"
34 #include "gfig-style.h"
35 #include "gfig-arc.h"
36 #include "gfig-bezier.h"
37 #include "gfig-circle.h"
38 #include "gfig-dobject.h"
39 #include "gfig-ellipse.h"
40 #include "gfig-line.h"
41 #include "gfig-poly.h"
42 #include "gfig-rectangle.h"
43 #include "gfig-spiral.h"
44 #include "gfig-star.h"
45 
46 #include "libgimp/stdplugins-intl.h"
47 
48 static GfigObject *operation_obj = NULL;
49 static GdkPoint   *move_all_pnt; /* Point moving all from */
50 
51 
52 static void draw_one_obj         (GfigObject *obj,
53                                   cairo_t    *cr);
54 static void do_move_obj          (GfigObject *obj,
55                                   GdkPoint   *to_pnt);
56 static void do_move_all_obj      (GdkPoint   *to_pnt);
57 static void do_move_obj_pnt      (GfigObject *obj,
58                                   GdkPoint   *to_pnt);
59 static void remove_obj_from_list (GFigObj    *obj,
60                                   GfigObject *del_obj);
61 static gint scan_obj_points      (DobjPoints *opnt,
62                                   GdkPoint   *pnt);
63 
64 void
d_save_object(GfigObject * obj,GString * string)65 d_save_object (GfigObject *obj,
66                GString    *string)
67 {
68   do_save_obj (obj, string);
69 
70   switch (obj->type)
71     {
72     case BEZIER:
73     case POLY:
74     case SPIRAL:
75     case STAR:
76       g_string_append_printf (string, "<EXTRA>\n");
77       g_string_append_printf (string, "%d\n</EXTRA>\n", obj->type_data);
78       break;
79     default:
80       break;
81     }
82 }
83 
84 static DobjType
gfig_read_object_type(gchar * desc)85 gfig_read_object_type (gchar *desc)
86 {
87   gchar    *ptr = desc;
88   DobjType  type;
89 
90   if (*ptr != '<')
91     return OBJ_TYPE_NONE;
92 
93   ptr++;
94 
95   for (type = LINE; type < NUM_OBJ_TYPES; type++)
96     {
97       if (ptr == strstr (ptr, dobj_class[type].name))
98         return type;
99     }
100 
101   return OBJ_TYPE_NONE;
102 }
103 
104 GfigObject *
d_load_object(gchar * desc,FILE * fp)105 d_load_object (gchar *desc,
106                FILE  *fp)
107 {
108   GfigObject *new_obj = NULL;
109   gint        xpnt;
110   gint        ypnt;
111   gchar       buf[MAX_LOAD_LINE];
112   DobjType    type;
113 
114   type = gfig_read_object_type (desc);
115   if (type == OBJ_TYPE_NONE)
116     {
117       g_message ("Error loading object: type not recognized.");
118       return NULL;
119     }
120 
121   while (get_line (buf, MAX_LOAD_LINE, fp, 0))
122     {
123       if (sscanf (buf, "%d %d", &xpnt, &ypnt) != 2)
124         {
125           /* Read <EXTRA> block if there is one */
126           if (!strcmp ("<EXTRA>", buf))
127             {
128               if ( !new_obj)
129                 {
130                   g_message ("Error while loading object (no points)");
131                   return NULL;
132                 }
133 
134               get_line (buf, MAX_LOAD_LINE, fp, 0);
135 
136               if (sscanf (buf, "%d", &new_obj->type_data) != 1)
137                 {
138                   g_message ("Error while loading object (no type data)");
139                   g_free (new_obj);
140                   return NULL;
141                 }
142 
143               get_line (buf, MAX_LOAD_LINE, fp, 0);
144               if (strcmp ("</EXTRA>", buf))
145                 {
146                   g_message ("Syntax error while loading object");
147                   g_free (new_obj);
148                   return NULL;
149                 }
150               /* Go around and read the last line */
151               continue;
152             }
153           else
154             return new_obj;
155         }
156 
157       if (!new_obj)
158         new_obj = d_new_object (type, xpnt, ypnt);
159       else
160         d_pnt_add_line (new_obj, xpnt, ypnt, -1);
161     }
162 
163   return new_obj;
164 }
165 
166 GfigObject *
d_new_object(DobjType type,gint x,gint y)167 d_new_object (DobjType    type,
168               gint        x,
169               gint        y)
170 {
171   GfigObject *nobj = g_new0 (GfigObject, 1);
172 
173   nobj->type = type;
174   nobj->class = &dobj_class[type];
175   nobj->points = new_dobjpoint (x, y);
176 
177   nobj->type_data = 0;
178 
179   if (type == BEZIER)
180     {
181       nobj->type_data = 4;
182     }
183   else if (type == POLY)
184     {
185       nobj->type_data = 3;  /* default to 3 sides */
186     }
187   else if (type == SPIRAL)
188     {
189       nobj->type_data = 4;  /* default to 4 turns */
190     }
191   else if (type == STAR)
192     {
193       nobj->type_data = 3;  /* default to 3 sides 6 points */
194     }
195 
196   return nobj;
197 }
198 
199 void
gfig_init_object_classes(void)200 gfig_init_object_classes (void)
201 {
202   d_arc_object_class_init ();
203   d_line_object_class_init ();
204   d_rectangle_object_class_init ();
205   d_circle_object_class_init ();
206   d_ellipse_object_class_init ();
207   d_poly_object_class_init ();
208   d_spiral_object_class_init ();
209   d_star_object_class_init ();
210   d_bezier_object_class_init ();
211 }
212 
213 /* Delete a list of points */
214 void
d_delete_dobjpoints(DobjPoints * pnts)215 d_delete_dobjpoints (DobjPoints * pnts)
216 {
217   DobjPoints *next;
218   DobjPoints *pnt2del = pnts;
219 
220   while (pnt2del)
221     {
222       next = pnt2del->next;
223       g_free (pnt2del);
224       pnt2del = next;
225     }
226 }
227 
228 DobjPoints *
new_dobjpoint(gint x,gint y)229 new_dobjpoint (gint x, gint y)
230 {
231   DobjPoints *npnt = g_new0 (DobjPoints, 1);
232 
233   npnt->pnt.x = x;
234   npnt->pnt.y = y;
235 
236   return npnt;
237 }
238 
239 DobjPoints *
d_copy_dobjpoints(DobjPoints * pnts)240 d_copy_dobjpoints (DobjPoints *pnts)
241 {
242   DobjPoints *ret = NULL;
243   DobjPoints *head = NULL;
244   DobjPoints *newpnt;
245   DobjPoints *pnt2copy;
246 
247   for (pnt2copy = pnts; pnt2copy; pnt2copy = pnt2copy->next)
248     {
249       newpnt = new_dobjpoint (pnt2copy->pnt.x, pnt2copy->pnt.y);
250 
251       if (!ret)
252         {
253           head = ret = newpnt;
254         }
255       else
256         {
257           head->next = newpnt;
258           head = newpnt;
259         }
260     }
261 
262   return ret;
263 }
264 
265 static DobjPoints *
get_diffs(GfigObject * obj,gint * xdiff,gint * ydiff,GdkPoint * to_pnt)266 get_diffs (GfigObject *obj,
267            gint       *xdiff,
268            gint       *ydiff,
269            GdkPoint   *to_pnt)
270 {
271   DobjPoints *spnt;
272 
273   g_assert (obj != NULL);
274 
275   for (spnt = obj->points; spnt; spnt = spnt->next)
276     {
277       if (spnt->found_me)
278         {
279           *xdiff = spnt->pnt.x - to_pnt->x;
280           *ydiff = spnt->pnt.y - to_pnt->y;
281           return spnt;
282         }
283     }
284   return NULL;
285 }
286 
287 static gboolean
inside_sqr(GdkPoint * cpnt,GdkPoint * testpnt)288 inside_sqr (GdkPoint *cpnt,
289             GdkPoint *testpnt)
290 {
291   /* Return TRUE if testpnt is near cpnt */
292   gint x  = cpnt->x;
293   gint y  = cpnt->y;
294   gint tx = testpnt->x;
295   gint ty = testpnt->y;
296 
297   return (abs (x - tx) <= SQ_SIZE && abs (y - ty) < SQ_SIZE);
298 }
299 
300 static gboolean
scan_obj_points(DobjPoints * opnt,GdkPoint * pnt)301 scan_obj_points (DobjPoints *opnt,
302                  GdkPoint   *pnt)
303 {
304   while (opnt)
305     {
306       if (inside_sqr (&opnt->pnt, pnt))
307         {
308           opnt->found_me = TRUE;
309           return TRUE;
310         }
311       opnt->found_me = FALSE;
312       opnt = opnt->next;
313     }
314   return FALSE;
315 }
316 
317 static GfigObject *
get_nearest_objs(GFigObj * obj,GdkPoint * pnt)318 get_nearest_objs (GFigObj  *obj,
319                   GdkPoint *pnt)
320 {
321   /* Nearest object to given point or NULL */
322   GList      *all;
323   GfigObject *test_obj;
324   gint        count = 0;
325 
326   if (!obj)
327     return NULL;
328 
329   for (all = obj->obj_list; all; all = g_list_next (all))
330     {
331       test_obj = all->data;
332 
333       if (count == obj_show_single || obj_show_single == -1)
334         if (scan_obj_points (test_obj->points, pnt))
335           {
336             return test_obj;
337           }
338       count++;
339     }
340   return NULL;
341 }
342 
343 void
object_operation_start(GdkPoint * pnt,gboolean shift_down)344 object_operation_start (GdkPoint *pnt,
345                         gboolean  shift_down)
346 {
347   GfigObject *new_obj;
348 
349   /* Find point in given object list */
350   operation_obj = get_nearest_objs (gfig_context->current_obj, pnt);
351 
352   /* Special case if shift down && move obj then moving all objs */
353 
354   if (shift_down && selvals.otype == MOVE_OBJ)
355     {
356       move_all_pnt = g_new (GdkPoint, 1);
357       *move_all_pnt = *pnt; /* Structure copy */
358       setup_undo ();
359       return;
360     }
361 
362   if (!operation_obj)
363     return;/* None to work on */
364 
365   gfig_context->selected_obj = operation_obj;
366 
367   setup_undo ();
368 
369   switch (selvals.otype)
370     {
371     case MOVE_OBJ:
372       if (operation_obj->type == BEZIER)
373         {
374           tmp_bezier = operation_obj;
375         }
376       break;
377 
378     case MOVE_POINT:
379       if (operation_obj->type == BEZIER)
380         {
381           tmp_bezier = operation_obj;
382         }
383       /* If shift is down the break into sep lines */
384       if ((operation_obj->type == POLY
385           || operation_obj->type == STAR)
386          && shift_down)
387         {
388           switch (operation_obj->type)
389             {
390             case POLY:
391               d_poly2lines (operation_obj);
392               break;
393             case STAR:
394               d_star2lines (operation_obj);
395               break;
396             default:
397               break;
398             }
399           /* Re calc which object point we are looking at */
400           scan_obj_points (operation_obj->points, pnt);
401           gtk_widget_queue_draw (gfig_context->preview);
402         }
403       break;
404 
405     case COPY_OBJ:
406       /* Copy the "operation object" */
407       /* Then bung us into "copy/move" mode */
408 
409       new_obj = (GfigObject*) operation_obj->class->copyfunc (operation_obj);
410       if (new_obj)
411         {
412           gfig_style_copy (&new_obj->style, &operation_obj->style, "Object");
413           scan_obj_points (new_obj->points, pnt);
414           add_to_all_obj (gfig_context->current_obj, new_obj);
415           operation_obj = new_obj;
416           selvals.otype = MOVE_COPY_OBJ;
417           gtk_widget_queue_draw (gfig_context->preview);
418         }
419       break;
420 
421     case DEL_OBJ:
422       remove_obj_from_list (gfig_context->current_obj, operation_obj);
423       break;
424 
425     case SELECT_OBJ:
426       /* don't need to do anything */
427       break;
428 
429     case MOVE_COPY_OBJ: /* Never when button down */
430     default:
431       g_warning ("Internal error selvals.otype object operation start");
432       break;
433     }
434 
435 }
436 
437 void
object_operation_end(GdkPoint * pnt,gboolean shift_down)438 object_operation_end (GdkPoint *pnt,
439                       gboolean  shift_down)
440 {
441   if (selvals.otype != DEL_OBJ && operation_obj &&
442       operation_obj->type == BEZIER)
443     {
444       tmp_bezier = NULL; /* use as switch */
445     }
446 
447   if (operation_obj && selvals.otype != DEL_OBJ)
448     gfig_style_set_context_from_style (&operation_obj->style);
449 
450   operation_obj = NULL;
451 
452   if (move_all_pnt)
453     {
454       g_free (move_all_pnt);
455       move_all_pnt = NULL;
456     }
457 
458   /* Special case - if copying mode MUST be copy when button up received */
459   if (selvals.otype == MOVE_COPY_OBJ)
460     selvals.otype = COPY_OBJ;
461 }
462 
463 /* Move object around */
464 void
object_operation(GdkPoint * to_pnt,gboolean shift_down)465 object_operation (GdkPoint *to_pnt,
466                   gboolean  shift_down)
467 {
468   /* Must do different things depending on object type */
469   /* but must have object to operate on! */
470 
471   /* Special case - if shift own and move_obj then move ALL objects */
472   if (move_all_pnt && shift_down && selvals.otype == MOVE_OBJ)
473     {
474       do_move_all_obj (to_pnt);
475       return;
476     }
477 
478   if (!operation_obj)
479     return;
480 
481   switch (selvals.otype)
482     {
483     case MOVE_OBJ:
484     case MOVE_COPY_OBJ:
485       switch (operation_obj->type)
486         {
487         case LINE:
488         case RECTANGLE:
489         case CIRCLE:
490         case ELLIPSE:
491         case POLY:
492         case ARC:
493         case STAR:
494         case SPIRAL:
495         case BEZIER:
496           do_move_obj (operation_obj, to_pnt);
497           break;
498         default:
499           /* Internal error */
500           g_warning ("Internal error in operation_obj->type");
501           break;
502         }
503       break;
504     case MOVE_POINT:
505       switch (operation_obj->type)
506         {
507         case LINE:
508         case RECTANGLE:
509         case CIRCLE:
510         case ELLIPSE:
511         case POLY:
512         case ARC:
513         case STAR:
514         case SPIRAL:
515         case BEZIER:
516           do_move_obj_pnt (operation_obj, to_pnt);
517           break;
518         default:
519           /* Internal error */
520           g_warning ("Internal error in operation_obj->type");
521           break;
522         }
523       break;
524     case DEL_OBJ:
525     case SELECT_OBJ:
526       break;
527     case COPY_OBJ: /* Should have been changed to MOVE_COPY_OBJ */
528     default:
529       g_warning ("Internal error selvals.otype");
530       break;
531     }
532 }
533 
534 static void
update_pnts(GfigObject * obj,gint xdiff,gint ydiff)535 update_pnts (GfigObject *obj,
536              gint        xdiff,
537              gint        ydiff)
538 {
539   DobjPoints *spnt;
540 
541   g_assert (obj != NULL);
542 
543   /* Update all pnts */
544   for (spnt = obj->points; spnt; spnt = spnt->next)
545     {
546       spnt->pnt.x -= xdiff;
547       spnt->pnt.y -= ydiff;
548     }
549 }
550 
551 static void
remove_obj_from_list(GFigObj * obj,GfigObject * del_obj)552 remove_obj_from_list (GFigObj    *obj,
553                       GfigObject *del_obj)
554 {
555   /* Nearest object to given point or NULL */
556 
557   g_assert (del_obj != NULL);
558 
559   if (g_list_find (obj->obj_list, del_obj))
560     {
561       obj->obj_list = g_list_remove (obj->obj_list, del_obj);
562 
563       free_one_obj (del_obj);
564 
565       if (obj->obj_list)
566         gfig_context->selected_obj = obj->obj_list->data;
567       else
568         gfig_context->selected_obj = NULL;
569 
570       if (obj_show_single != -1)
571         {
572           /* We've just deleted the only visible one */
573           draw_grid_clear ();
574           obj_show_single = -1; /* Show entry again */
575         }
576 
577       gtk_widget_queue_draw (gfig_context->preview);
578     }
579   else
580     g_warning (_("Hey, where has the object gone?"));
581 }
582 
583 static void
do_move_all_obj(GdkPoint * to_pnt)584 do_move_all_obj (GdkPoint *to_pnt)
585 {
586   /* Move all objects in one go */
587   /* Undraw/then draw in new pos */
588   gint xdiff = move_all_pnt->x - to_pnt->x;
589   gint ydiff = move_all_pnt->y - to_pnt->y;
590 
591   if (xdiff || ydiff)
592     {
593       GList *all;
594 
595       for (all = gfig_context->current_obj->obj_list; all; all = all->next)
596         {
597           GfigObject *obj = all->data;
598 
599           update_pnts (obj, xdiff, ydiff);
600         }
601 
602       *move_all_pnt = *to_pnt;
603 
604       gtk_widget_queue_draw (gfig_context->preview);
605     }
606 }
607 
608 void
do_save_obj(GfigObject * obj,GString * string)609 do_save_obj (GfigObject *obj,
610              GString    *string)
611 {
612   DobjPoints *spnt;
613 
614   for (spnt = obj->points; spnt; spnt = spnt->next)
615     {
616       g_string_append_printf (string, "%d %d\n", spnt->pnt.x, spnt->pnt.y);
617     }
618 }
619 
620 static void
do_move_obj(GfigObject * obj,GdkPoint * to_pnt)621 do_move_obj (GfigObject *obj,
622              GdkPoint   *to_pnt)
623 {
624   /* Move the whole line - undraw the line to start with */
625   /* Then draw in new pos */
626   gint xdiff = 0;
627   gint ydiff = 0;
628 
629   get_diffs (obj, &xdiff, &ydiff, to_pnt);
630 
631   if (xdiff || ydiff)
632     {
633       update_pnts (obj, xdiff, ydiff);
634 
635       gtk_widget_queue_draw (gfig_context->preview);
636     }
637 }
638 
639 static void
do_move_obj_pnt(GfigObject * obj,GdkPoint * to_pnt)640 do_move_obj_pnt (GfigObject *obj,
641                  GdkPoint   *to_pnt)
642 {
643   /* Move the whole line - undraw the line to start with */
644   /* Then draw in new pos */
645   DobjPoints *spnt;
646   gint xdiff = 0;
647   gint ydiff = 0;
648 
649   spnt = get_diffs (obj, &xdiff, &ydiff, to_pnt);
650 
651   if ((!xdiff && !ydiff) || !spnt)
652     return;
653 
654   spnt->pnt.x = spnt->pnt.x - xdiff;
655   spnt->pnt.y = spnt->pnt.y - ydiff;
656 
657   /* Draw in new pos */
658   gtk_widget_queue_draw (gfig_context->preview);
659 }
660 
661 /* copy objs */
662 GList *
copy_all_objs(GList * objs)663 copy_all_objs (GList *objs)
664 {
665   GList *new_all_objs = NULL;
666 
667   while (objs)
668     {
669       GfigObject *object = objs->data;
670       GfigObject *new_object = (GfigObject *) object->class->copyfunc (object);
671       gfig_style_copy (&new_object->style, &object->style, "Object");
672 
673       new_all_objs = g_list_prepend (new_all_objs, new_object);
674 
675       objs = objs->next;
676     }
677 
678   new_all_objs = g_list_reverse (new_all_objs);
679 
680   return new_all_objs;
681 }
682 
683 /* Screen refresh */
684 static void
draw_one_obj(GfigObject * obj,cairo_t * cr)685 draw_one_obj (GfigObject *obj,
686               cairo_t    *cr)
687 {
688   obj->class->drawfunc (obj, cr);
689 }
690 
691 void
draw_objects(GList * objs,gboolean show_single,cairo_t * cr)692 draw_objects (GList    *objs,
693               gboolean  show_single,
694               cairo_t  *cr)
695 {
696   /* Show_single - only one object to draw Unless shift
697    * is down in which case show all.
698    */
699 
700   gint count = 0;
701 
702   while (objs)
703     {
704       if (!show_single || count == obj_show_single || obj_show_single == -1)
705         draw_one_obj (objs->data, cr);
706 
707       objs = g_list_next (objs);
708       count++;
709     }
710 }
711 
712 void
prepend_to_all_obj(GFigObj * fobj,GList * nobj)713 prepend_to_all_obj (GFigObj *fobj,
714                     GList   *nobj)
715 {
716   setup_undo (); /* Remember ME */
717 
718   fobj->obj_list = g_list_concat (fobj->obj_list, nobj);
719 }
720 
721 static void
scale_obj_points(DobjPoints * opnt,gdouble scale_x,gdouble scale_y)722 scale_obj_points (DobjPoints *opnt,
723                   gdouble     scale_x,
724                   gdouble     scale_y)
725 {
726   while (opnt)
727     {
728       opnt->pnt.x = (gint) (opnt->pnt.x * scale_x);
729       opnt->pnt.y = (gint) (opnt->pnt.y * scale_y);
730       opnt = opnt->next;
731     }
732 }
733 
734 void
add_to_all_obj(GFigObj * fobj,GfigObject * obj)735 add_to_all_obj (GFigObj    *fobj,
736                 GfigObject *obj)
737 {
738   GList *nobj = NULL;
739 
740   nobj = g_list_append (nobj, obj);
741 
742   if (need_to_scale)
743     scale_obj_points (obj->points, scale_x_factor, scale_y_factor);
744 
745   prepend_to_all_obj (fobj, nobj);
746 
747   /* initialize style when we add the object */
748   gfig_context->selected_obj = obj;
749 }
750 
751 /* First button press -- start drawing object */
752 /*
753  * object_start() creates a new object of the type specified in the
754  * button panel.  It is activated by a button press, and causes
755  * a small square to be drawn at the initial point.  The style of
756  * the new object is set to values taken from the style control
757  * widgets.
758  */
759 void
object_start(GdkPoint * pnt,gboolean shift_down)760 object_start (GdkPoint *pnt,
761               gboolean  shift_down)
762 {
763   /* start for the current object */
764   if (!selvals.scaletoimage)
765     {
766       need_to_scale = 1;
767       selvals.scaletoimage = 1;
768     }
769   else
770     {
771       need_to_scale = 0;
772     }
773 
774   switch (selvals.otype)
775     {
776     case LINE:
777       /* Shift means we are still drawing */
778       d_line_start (pnt, shift_down);
779       break;
780     case RECTANGLE:
781       d_rectangle_start (pnt, shift_down);
782       break;
783     case CIRCLE:
784       d_circle_start (pnt, shift_down);
785       break;
786     case ELLIPSE:
787       d_ellipse_start (pnt, shift_down);
788       break;
789     case POLY:
790       d_poly_start (pnt, shift_down);
791       break;
792     case ARC:
793       d_arc_start (pnt, shift_down);
794       break;
795     case STAR:
796       d_star_start (pnt, shift_down);
797       break;
798     case SPIRAL:
799       d_spiral_start (pnt, shift_down);
800       break;
801     case BEZIER:
802       d_bezier_start (pnt, shift_down);
803       break;
804     default:
805       /* Internal error */
806       break;
807     }
808 
809   if (obj_creating)
810     {
811       if (gfig_context->debug_styles)
812         g_printerr ("Creating object, setting style from context\n");
813       gfig_style_set_style_from_context (&obj_creating->style);
814     }
815 
816 }
817 
818 void
object_end(GdkPoint * pnt,gboolean shift_down)819 object_end (GdkPoint *pnt,
820             gboolean  shift_down)
821 {
822   /* end for the current object */
823   /* Add onto global object list */
824 
825   /* If shift is down may carry on drawing */
826   switch (selvals.otype)
827     {
828     case LINE:
829       d_line_end (pnt, shift_down);
830       break;
831     case RECTANGLE:
832       d_rectangle_end (pnt, shift_down);
833       break;
834     case CIRCLE:
835       d_circle_end (pnt, shift_down);
836       break;
837     case ELLIPSE:
838       d_ellipse_end (pnt, shift_down);
839       break;
840     case POLY:
841       d_poly_end (pnt, shift_down);
842       break;
843     case STAR:
844       d_star_end (pnt, shift_down);
845       break;
846     case ARC:
847       d_arc_end (pnt, shift_down);
848       break;
849     case SPIRAL:
850       d_spiral_end (pnt, shift_down);
851       break;
852     case BEZIER:
853       d_bezier_end (pnt, shift_down);
854       break;
855     default:
856       /* Internal error */
857       break;
858     }
859 
860   if (need_to_scale)
861     {
862       need_to_scale = 0;
863       selvals.scaletoimage = 0;
864     }
865 }
866 
867 /* Stuff for the generation/deletion of objects. */
868 
869 /* Objects are easy one they are created - you just go down the object
870  * list calling the draw function for each object but... when they
871  * are been created we have to be a little more careful. When
872  * the first point is placed on the canvas we create the object,
873  * the mouse position then defines the next point that can move around.
874  * careful how we draw this position.
875  */
876 
877 void
free_one_obj(GfigObject * obj)878 free_one_obj (GfigObject *obj)
879 {
880   d_delete_dobjpoints (obj->points);
881   g_free (obj);
882 }
883 
884 void
free_all_objs(GList * objs)885 free_all_objs (GList *objs)
886 {
887   g_list_free_full (objs, (GDestroyNotify) free_one_obj);
888 }
889 
890 gchar *
get_line(gchar * buf,gint s,FILE * from,gint init)891 get_line (gchar *buf,
892           gint   s,
893           FILE  *from,
894           gint   init)
895 {
896   gint slen;
897   char *ret;
898 
899   if (init)
900     line_no = 1;
901   else
902     line_no++;
903 
904   do
905     {
906       ret = fgets (buf, s, from);
907     }
908   while (!ferror (from) && buf[0] == '#');
909 
910   slen = strlen (buf);
911 
912   /* The last newline is a pain */
913   if (slen > 0)
914     buf[slen - 1] = '\0';
915 
916   /* Check and remove an '\r' too from Windows */
917   if ((slen > 1) && (buf[slen - 2] == '\r'))
918     buf[slen - 2] = '\0';
919 
920   if (ferror (from))
921     {
922       g_warning (_("Error reading file"));
923       return NULL;
924     }
925 
926 #ifdef DEBUG
927   printf ("Processing line '%s'\n", buf);
928 #endif /* DEBUG */
929 
930   return ret;
931 }
932 
933 void
clear_undo(void)934 clear_undo (void)
935 {
936   int lv;
937 
938   for (lv = undo_level; lv >= 0; lv--)
939     {
940       free_all_objs (undo_table[lv]);
941       undo_table[lv] = NULL;
942     }
943 
944   undo_level = -1;
945 
946   gfig_dialog_action_set_sensitive ("undo", FALSE);
947 }
948 
949 void
setup_undo(void)950 setup_undo (void)
951 {
952   /* Copy object list to undo buffer */
953 #if DEBUG
954   printf ("setup undo level [%d]\n", undo_level);
955 #endif /*DEBUG*/
956 
957   if (!gfig_context->current_obj)
958     {
959       /* If no current_obj must be loading -> no undo */
960       return;
961     }
962 
963   if (undo_level >= selvals.maxundo - 1)
964     {
965       int loop;
966       /* the little one in the bed said "roll over".. */
967       if (undo_table[0])
968         free_one_obj (undo_table[0]->data);
969       for (loop = 0; loop < undo_level; loop++)
970         {
971           undo_table[loop] = undo_table[loop + 1];
972         }
973     }
974   else
975     {
976       undo_level++;
977     }
978   undo_table[undo_level] =
979     copy_all_objs (gfig_context->current_obj->obj_list);
980 
981   gfig_dialog_action_set_sensitive ("undo", TRUE);
982 
983   gfig_context->current_obj->obj_status |= GFIG_MODIFIED;
984 }
985 
986 void
new_obj_2edit(GFigObj * obj)987 new_obj_2edit (GFigObj *obj)
988 {
989   GFigObj *old_current = gfig_context->current_obj;
990 
991   /* Clear undo levels */
992   /* redraw the preview */
993   /* Set up options as define in the selected object */
994 
995   clear_undo ();
996 
997   /* Point at this one */
998   gfig_context->current_obj = obj;
999 
1000   /* Show all objects to start with */
1001   obj_show_single = -1;
1002 
1003   /* Change options */
1004   options_update (old_current);
1005 
1006   /* redraw with new */
1007   gtk_widget_queue_draw (gfig_context->preview);
1008 
1009   if (obj->obj_status & GFIG_READONLY)
1010     {
1011       g_message (_("Editing read-only object - "
1012                    "you will not be able to save it"));
1013 
1014       gfig_dialog_action_set_sensitive ("save", FALSE);
1015     }
1016   else
1017     {
1018       gfig_dialog_action_set_sensitive ("save", TRUE);
1019     }
1020 }
1021 
1022 /* Add a point to a line (given x, y)
1023  * pos = 0 = head
1024  * pos = -1 = tail
1025  * 0 < pos = nth position
1026  */
1027 
1028 void
d_pnt_add_line(GfigObject * obj,gint x,gint y,gint pos)1029 d_pnt_add_line (GfigObject *obj,
1030                 gint        x,
1031                 gint        y,
1032                 gint        pos)
1033 {
1034   DobjPoints *npnts = new_dobjpoint (x, y);
1035 
1036   g_assert (obj != NULL);
1037 
1038   if (!pos)
1039     {
1040       /* Add to head */
1041       npnts->next = obj->points;
1042       obj->points = npnts;
1043     }
1044   else
1045     {
1046       DobjPoints *pnt = obj->points;
1047 
1048       /* Go down chain until the end if pos */
1049       while (pos < 0 || pos-- > 0)
1050         {
1051           if (!(pnt->next) || !pos)
1052             {
1053               npnts->next = pnt->next;
1054               pnt->next = npnts;
1055               break;
1056             }
1057           else
1058             {
1059               pnt = pnt->next;
1060             }
1061         }
1062     }
1063 }
1064