1079 lines
24 KiB
C
1079 lines
24 KiB
C
|
/*
|
||
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||
|
*
|
||
|
* This is a plug-in for PIKA.
|
||
|
*
|
||
|
* 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 <libpika/pika.h>
|
||
|
#include <libpika/pikaui.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 "libpika/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 (PikaGfig *gfig,
|
||
|
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 (gfig);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!operation_obj)
|
||
|
return;/* None to work on */
|
||
|
|
||
|
gfig_context->selected_obj = operation_obj;
|
||
|
|
||
|
setup_undo (gfig);
|
||
|
|
||
|
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, 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 (PikaGfig *gfig,
|
||
|
GFigObj *fobj,
|
||
|
GList *nobj)
|
||
|
{
|
||
|
setup_undo (gfig); /* 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 (PikaGfig *gfig,
|
||
|
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 (gfig, 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 (PikaGfig *gfig,
|
||
|
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 (gfig, pnt, shift_down);
|
||
|
break;
|
||
|
case RECTANGLE:
|
||
|
d_rectangle_end (gfig, pnt, shift_down);
|
||
|
break;
|
||
|
case CIRCLE:
|
||
|
d_circle_end (gfig, pnt, shift_down);
|
||
|
break;
|
||
|
case ELLIPSE:
|
||
|
d_ellipse_end (gfig, pnt, shift_down);
|
||
|
break;
|
||
|
case POLY:
|
||
|
d_poly_end (gfig, pnt, shift_down);
|
||
|
break;
|
||
|
case STAR:
|
||
|
d_star_end (gfig, pnt, shift_down);
|
||
|
break;
|
||
|
case ARC:
|
||
|
d_arc_end (gfig, pnt, shift_down);
|
||
|
break;
|
||
|
case SPIRAL:
|
||
|
d_spiral_end (gfig, pnt, shift_down);
|
||
|
break;
|
||
|
case BEZIER:
|
||
|
d_bezier_end (gfig, 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 (PikaGfig *gfig)
|
||
|
{
|
||
|
int lv;
|
||
|
GAction *action;
|
||
|
|
||
|
for (lv = undo_level; lv >= 0; lv--)
|
||
|
{
|
||
|
free_all_objs (undo_table[lv]);
|
||
|
undo_table[lv] = NULL;
|
||
|
}
|
||
|
|
||
|
undo_level = -1;
|
||
|
|
||
|
action = g_action_map_lookup_action (G_ACTION_MAP (gfig->app), "undo");
|
||
|
g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
|
||
|
FALSE);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
setup_undo (PikaGfig *gfig)
|
||
|
{
|
||
|
GAction *action;
|
||
|
|
||
|
/* 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);
|
||
|
|
||
|
action = g_action_map_lookup_action (G_ACTION_MAP (gfig->app), "undo");
|
||
|
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
|
||
|
|
||
|
gfig_context->current_obj->obj_status |= GFIG_MODIFIED;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
new_obj_2edit (PikaGfig *gfig,
|
||
|
GFigObj *obj)
|
||
|
{
|
||
|
GAction *action;
|
||
|
GFigObj *old_current = gfig_context->current_obj;
|
||
|
|
||
|
/* Clear undo levels */
|
||
|
/* redraw the preview */
|
||
|
/* Set up options as define in the selected object */
|
||
|
|
||
|
clear_undo (gfig);
|
||
|
|
||
|
/* 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);
|
||
|
|
||
|
action = g_action_map_lookup_action (G_ACTION_MAP (gfig->app), "save");
|
||
|
if (obj->obj_status & GFIG_READONLY)
|
||
|
{
|
||
|
g_message (_("Editing read-only object - "
|
||
|
"you will not be able to save it"));
|
||
|
|
||
|
g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
|
||
|
FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
|
||
|
FALSE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* 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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|