/* * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This is a plug-in for the 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 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. * * Some of this code was taken from the Whirl plug-in * which was copyrighted by Federico Mena Quintero (as below). * * Whirl plug-in --- distort an image into a whirlpool * Copyright (C) 1997 Federico Mena Quintero * */ /* Change log: * 0.9 First public release. * 0.95 Second release. * * 0.96 Added patch from Rob Saunders that introduces a isometric type grid * Removed use of gtk_idle* stuff on position update. Not required. * * 1.0 Fixed to work with the new gtk+-0.99.4 (tooltips stuff has changed). * * 1.1 Fixed crashes when objects not fully defined * * 1.2 More bug fixes and prevent gtk warning when creating new figs * * 1.3 Portability fixes and fixed bug reports 257 and 258 from and 81 & 101 & 133 * http://www.wilberworks.com/bugs.cgi */ #include "config.h" #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #ifdef __GNUC__ #warning GTK_DISABLE_DEPRECATED #endif #undef GTK_DISABLE_DEPRECATED #include #ifdef G_OS_WIN32 # include # ifndef W_OK # define W_OK 2 # endif # ifndef S_ISDIR # define S_ISDIR(m) ((m) & _S_IFDIR) # endif # ifndef S_ISREG # define S_ISREG(m) ((m) & _S_IFREG) # endif #endif #if defined (GTK_CHECK_VERSION) && GTK_CHECK_VERSION (1,3,0) #define gdk_root_parent (*gdk_parent_root) #endif #ifdef G_OS_WIN32 extern __declspec(dllimport) void *gdk_root_parent; #else extern void * gdk_root_parent; #endif #include #include #include "libgimp/stdplugins-intl.h" #include "pix_data.h" /***** Magic numbers *****/ #define PREVIEW_SIZE 400 #define SCALE_WIDTH 120 #define MIN_GRID 10 #define MAX_GRID 50 #define MAX_UNDO 10 #define MIN_UNDO 1 #define MAX_LOAD_LINE 256 #define SMALL_PREVIEW_SZ 48 #define BRUSH_PREVIEW_SZ 32 #define GFIG_HEADER "GFIG Version 0.1\n" #define PREVIEW_MASK (GDK_EXPOSURE_MASK | \ GDK_POINTER_MOTION_MASK | \ GDK_BUTTON_PRESS_MASK | \ GDK_BUTTON_RELEASE_MASK | \ GDK_BUTTON_MOTION_MASK | \ GDK_KEY_PRESS_MASK | \ GDK_KEY_RELEASE_MASK) static GimpDrawable *gfig_select_drawable; static GtkWidget *gfig_preview; static GtkWidget *pic_preview; static GtkWidget *gfig_gtk_list; static gint gfig_preview_exp_id; static gint32 gfig_image; static gint32 gfig_drawable; static GtkWidget *brush_page_pw; static GtkWidget *brush_sel_button; static gint tile_width, tile_height; static gint img_width, img_height, img_bpp, real_img_bpp; static void query (void); static void run (gchar *name, gint nparams, GimpParam *param, gint *nreturn_vals, GimpParam **return_vals); static gint gfig_dialog (void); static void gfig_ok_callback (GtkWidget *widget, gpointer data); static void gfig_paint_callback (GtkWidget *widget, gpointer data); static void gfig_clear_callback (GtkWidget *widget, gpointer data); static void gfig_undo_callback (GtkWidget *widget, gpointer data); static gint gfig_preview_expose (GtkWidget *widget, GdkEvent *event); static gint pic_preview_expose (GtkWidget *widget, GdkEvent *event); static gint gfig_preview_events (GtkWidget *widget, GdkEvent *event); static gint gfig_brush_preview_events (GtkWidget *widget, GdkEvent *event); static void gfig_scale_update_scale (GtkAdjustment *adjustment, gdouble *value); static void gfig_scale2img_update (GtkWidget *widget, gpointer data); static gint gfig_scale_x (gint x); static gint gfig_scale_y (gint y); static gint gfig_invscale_x (gint x); static gint gfig_invscale_y (gint y); static GdkGC * gfig_get_grid_gc (GtkWidget *widget, gint gctype); static void gfig_pos_enable (GtkWidget *widget, gpointer data); static gint list_button_press (GtkWidget *widget, GdkEventButton *event, gpointer data); static void rescan_button_callback (GtkWidget *widget, gpointer data); static void load_button_callback (GtkWidget *widget, gpointer data); static void save_button_callback (GtkWidget *widget, gpointer data); static void new_button_callback (GtkWidget *widget, gpointer data); static void gfig_do_delete_gfig_callback (GtkWidget *widget, gboolean delete, gpointer data); static void gfig_delete_gfig_callback (GtkWidget *widget, gpointer data); static void edit_button_callback (GtkWidget *widget, gpointer data); static void merge_button_callback (GtkWidget *widget, gpointer data); static void about_button_callback (GtkWidget *widget, gpointer data); static void reload_button_callback (GtkWidget *widget, gpointer data); static void do_gfig (void); static void dialog_update_preview (void); static void draw_grid_clear (GtkWidget *widget, gpointer data); static void toggle_show_image (GtkWidget *widget, gpointer data); static void toggle_tooltips (GtkWidget *widget, gpointer data); static void toggle_obj_type (GtkWidget *widget, gpointer data); static void draw_grid (GtkWidget *widget, gpointer data); static void gfig_new_gc (void); static void find_grid_pos (GdkPoint *p, GdkPoint *gp, guint state); static void brush_list_button_callback (GtkWidget *widget, gpointer data); static gint calculate_point_to_line_distance (GdkPoint *p, GdkPoint *A, GdkPoint *B, GdkPoint *I); GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; /* The types of an object */ /* Also includes actions that can be performed on objects */ typedef enum { LINE, CIRCLE, ELLIPSE, ARC, POLY, STAR, SPIRAL, BEZIER, MOVE_OBJ, MOVE_POINT, COPY_OBJ, MOVE_COPY_OBJ, DEL_OBJ, NULL_OPER } DobjType; typedef enum { RECT_GRID = 0, POLAR_GRID, ISO_GRID } GridType; typedef enum { ORIGINAL_LAYER = 0, SINGLE_LAYER, MULTI_LAYER } DrawonLayers; typedef enum { LAYER_TRANS_BG = 0, LAYER_BG_BG, LAYER_FG_BG, LAYER_WHITE_BG, LAYER_COPY_BG } LayersBGType; typedef enum { PAINT_BRUSH_TYPE = 0, PAINT_SELECTION_TYPE, PAINT_SELECTION_FILL_TYPE } PaintType; typedef enum { BRUSH_BRUSH_TYPE = 0, BRUSH_PENCIL_TYPE, BRUSH_AIRBRUSH_TYPE, BRUSH_PATTERN_TYPE } BrushType; #define GRID_TYPE_MENU 1 #define GRID_RENDER_MENU 2 #define GRID_IGNORE 0 #define GRID_HIGHTLIGHT 1 #define GRID_RESTORE 2 #define GFIG_BLACK_GC -2 #define GFIG_WHITE_GC -3 #define GFIG_GREY_GC -4 #define PAINT_LAYERS_MENU 1 #define PAINT_BGS_MENU 2 #define PAINT_TYPE_MENU 3 #define SELECT_TYPE_MENU 1 #define SELECT_ARCTYPE_MENU 2 #define SELECT_TYPE_MENU_FILL 3 #define SELECT_TYPE_MENU_WHEN 4 #define OBJ_SELECT_GT 1 #define OBJ_SELECT_LT 2 #define OBJ_SELECT_EQ 4 typedef struct { gint gridspacing; GridType gridtype; gint drawgrid; gint snap2grid; gint lockongrid; gint showcontrol; } GfigOpts; /* Must keep in step with the above */ typedef struct { void *gridspacing; GtkWidget *gridtypemenu; GtkWidget *drawgrid; GtkWidget *snap2grid; GtkWidget *lockongrid; GtkWidget *showcontrol; } GfigOptWidgets; static GfigOptWidgets gfig_opt_widget; typedef struct { GfigOpts opts; gint showimage; gint maxundo; gint showpos; gdouble brushfade; gdouble brushgradient; gdouble airbrushpressure; gint showtooltips; DrawonLayers onlayers; LayersBGType onlayerbg; PaintType painttype; gint reverselines; gint scaletoimage; gdouble scaletoimagefp; gint approxcircles; BrushType brshtype; DobjType otype; } SelectItVals; /* Values when first invoked */ static SelectItVals selvals = { { MIN_GRID + (MAX_GRID - MIN_GRID)/2, /* Gridspacing */ RECT_GRID, /* Default to rectangle type */ 0, /* drawgrid */ 0, /* snap2grid */ 0, /* lockongrid */ 1, /* show control points */ }, 0, /* show image */ MIN_UNDO + (MAX_UNDO - MIN_UNDO)/2, /* Max level of undos */ FALSE, /* Show pos updates */ 0.0, /* Brush fade */ 0.0, /* Brush gradient */ 20.0, /* Air bursh pressure */ TRUE, /* show Tool tips */ ORIGINAL_LAYER, /* Draw all objects on one layer */ LAYER_TRANS_BG, /* New layers background */ PAINT_BRUSH_TYPE, /* Default to use brushes */ FALSE, /* reverse lines */ TRUE, /* Scale to image when painting */ 1.0, /* Scale to image fp */ FALSE, /* Approx circles by drawing lines */ BRUSH_BRUSH_TYPE, /* Default to use a brush */ LINE /* Initial object type */ }; typedef enum { ADD=0, SUBTRACT=1, REPLACE=2, INTERSECT=3 } SelectionType; typedef enum { ARC_SEGMENT, ARC_SECTOR } ArcType; typedef enum { FILL_FOREGROUND = 0, FILL_BACKGROUND = 1, FILL_PATTERN = 2 } FillType; typedef enum { FILL_EACH = 0, FILL_AFTER } FillWhen; struct selection_option { SelectionType type; /* ADD etc .. */ gint antia; /* Boolean for Antia */ gint feather; /* Feather it ? */ gdouble feather_radius; /* Radius to feather */ ArcType as_pie; /* Arc type selection segment/sector */ FillType fill_type; /* Fill type for selection */ FillWhen fill_when; /* Fill on each selection or after all? */ gdouble fill_opacity; /* You can guess this one */ } selopt = { ADD, /* type */ FALSE, /* Antia */ FALSE, /* Feather */ 10.0, /* feather radius */ ARC_SEGMENT, /* Arc as a segment */ FILL_PATTERN, /* Fill as pattern */ FILL_EACH, /* Fill after each selection */ 100.0, /* Max opacity */ }; static GList *gfig_path_list = NULL; static GList *gfig_list = NULL; static gint line_no; static gint poly_num_sides = 3; /* Default to three sided object */ static gint star_num_sides = 3; /* Default to three sided object */ static gint spiral_num_turns = 4; /* Default to 4 turns */ static gint spiral_toggle = 0; /* 0 = clockwise -1 = anti-clockwise */ static gint bezier_closed = 0; /* Closed curve 0 = false 1 = true */ static gint bezier_line_frame = 0; /* Show frame = false 1 = true */ static gint obj_show_single = -1; /* -1 all >= 0 object number */ /* Structures etc for the objects */ /* Points used to draw the object */ typedef struct DobjPoints { struct DobjPoints * next; GdkPoint pnt; gint found_me; } DobjPoints; struct Dobject; /* fwd declaration for DobjFunc */ typedef void (*DobjFunc) (struct Dobject *); typedef struct Dobject *(*DobjGenFunc) (struct Dobject *); typedef struct Dobject *(*DobjLoadFunc) (FILE *); typedef void (*DobjSaveFunc) (struct Dobject *, FILE *); /* The object itself */ typedef struct Dobject { DobjType type; /* What is the type? */ gpointer type_data; /* Extra data needed by the object */ DobjPoints *points; /* List of points */ DobjFunc drawfunc; /* How do I draw myself */ DobjFunc paintfunc; /* Draw me on canvas */ DobjGenFunc copyfunc; /* copy */ DobjLoadFunc loadfunc; /* Load this type of object */ DobjSaveFunc savefunc; /* Save me out */ } Dobject; static Dobject *obj_creating; /* Object we are creating */ static Dobject *tmp_line; /* Needed when drawing lines */ static Dobject *tmp_bezier; /* Neeed when drawing bezier curves */ typedef struct DAllObjs { struct DAllObjs *next; Dobject *obj; /* Object on list */ } DAllObjs; /* States of the object */ #define GFIG_OK 0x0 #define GFIG_MODIFIED 0x1 #define GFIG_READONLY 0x2 typedef struct DFigObj { gchar *name; /* Trailing name of file */ gchar *filename; /* Filename itself */ gchar *draw_name;/* Name of the drawing */ gfloat version; /* Version number of data file */ GfigOpts opts; /* Options enforced when fig saved */ DAllObjs *obj_list; /* Objects that make up this list */ gint obj_status; /* See above for possible values */ GtkWidget *list_item; GtkWidget *label_widget; GtkWidget *pixmap_widget; } GFigObj; typedef struct BrushDesc { gchar *bname; /* name of the brush */ gint32 width; /* Width of brush */ gint32 height; /* Height of brush */ guchar *pv_buf; /* Buffer where brush placed */ gint16 x_off; gint16 y_off; gint bpp; /* Depth - should ALWAYS be the same for all BrushDesc */ } BrushDesc; static GFigObj *current_obj; static Dobject *operation_obj; static GdkPoint *move_all_pnt; /* Point moving all from */ static GFigObj *pic_obj; static DAllObjs *undo_table[MAX_UNDO]; static gint need_to_scale; static gint32 brush_image_ID = -1; static GtkWidget *undo_widget; static GtkWidget *gfig_op_menu; /* Popup menu in the list box */ static GtkWidget *delete_frame_to_freeze; /* Top preview frame window */ static GtkWidget *fade_out_hbox; /* Fade out widget in brush page */ static GtkWidget *gradient_hbox; /* Gradient widget in brush page */ static GtkWidget *pressure_hbox; /* Pressure widget in brush page */ static GtkWidget *pencil_hbox; /* Dummy widget in brush page */ static GtkWidget *pos_label; /* XY pos marker */ static GtkWidget *brush_page_widget; /* Widget for the brush part of notebook */ static GtkWidget *select_page_widget; /* Widget for the selection part * of notebook */ static gint undo_water_mark = -1; /* Last slot filled in -1 = no undo */ static gint drawing_pic = FALSE; /* If true drawing to the small preview */ static GtkWidget *status_label_dname; static GtkWidget *status_label_fname; static GFigObj *gfig_obj_for_menu; /* More static data - * need to know which object was selected*/ static GtkWidget *save_menu_item; static GtkWidget *save_button; /* Don't up just like BIGGG source files? */ static void object_start (GdkPoint *pnt, gint); static void object_operation (GdkPoint *pnt, gint); static void object_operation_start (GdkPoint *pnt, gint shift_down); static void object_operation_end (GdkPoint *pnt, gint); static void object_end (GdkPoint *pnt, gint shift_down); static void object_update (GdkPoint * pnt); static void add_to_all_obj (GFigObj * fobj, Dobject *obj); static void d_delete_dobjpoints (DobjPoints *); static Dobject * d_new_line (gint x, gint y); static Dobject * d_new_circle (gint x, gint y); static DAllObjs * copy_all_objs (DAllObjs *objs); static void setup_undo (void); static void d_pnt_add_line (Dobject *obj, gint x, gint y, gint pos); static GFigObj * gfig_load (const gchar *filename, const gchar *name); static void free_all_objs (DAllObjs * objs); static void draw_objects (DAllObjs *objs, gint show_single); static Dobject * d_load_line (FILE *from); static Dobject * d_load_circle (FILE *from); static gchar * get_line (gchar *buf, gint s, FILE * from, gint init); static GFigObj * gfig_new (void); static void clear_undo (void); static void list_button_update (GFigObj *obj); static void prepend_to_all_obj (GFigObj *fobj, DAllObjs *nobj); static void gfig_update_stat_labels (void); static void gfig_obj_modified (GFigObj *obj, gint stat_type); static void gfig_op_menu_create (GtkWidget *window); static void gridtype_menu_callback (GtkWidget *widget, gpointer data); static void draw_one_obj (Dobject * obj); static void d_save_poly (Dobject * obj, FILE *to); static Dobject * d_load_poly (FILE *from); static void d_draw_poly (Dobject *obj); static void d_paint_poly (Dobject *obj); static Dobject * d_copy_poly (Dobject * obj); static Dobject * d_new_poly (gint x, gint y); static void d_update_poly (GdkPoint *pnt); static void d_poly_start (GdkPoint *pnt, gint shift_down); static void d_poly_end (GdkPoint *pnt, gint shift_down); static void d_save_star (Dobject * obj, FILE *to); static Dobject * d_load_star (FILE *from); static void d_draw_star (Dobject *obj); static void d_paint_star (Dobject *obj); static Dobject * d_copy_star (Dobject * obj); static Dobject * d_new_star (gint x, gint y); static void d_update_star (GdkPoint *pnt); static void d_star_start (GdkPoint *pnt, gint shift_down); static void d_star_end (GdkPoint *pnt, gint shift_down); static Dobject * d_load_spiral (FILE *from); static void d_draw_spiral (Dobject *obj); static void d_paint_spiral (Dobject *obj); static Dobject * d_copy_spiral (Dobject * obj); static Dobject * d_new_spiral (gint x, gint y); static void d_update_spiral (GdkPoint *pnt); static void d_spiral_start (GdkPoint *pnt, gint shift_down); static void d_spiral_end (GdkPoint *pnt, gint shift_down); static Dobject * d_load_bezier (FILE *from); static void d_draw_bezier (Dobject *obj); static void d_paint_bezier (Dobject *obj); static Dobject * d_copy_bezier (Dobject * obj); static Dobject * d_new_bezier (gint x, gint y); static void d_update_bezier (GdkPoint *pnt); static void d_bezier_start (GdkPoint *pnt, gint shift_down); static void d_bezier_end (GdkPoint *pnt, gint shift_down); static void new_obj_2edit (GFigObj *obj); static Dobject * d_new_ellipse (gint x, gint y); static Dobject * d_load_ellipse (FILE *from); static Dobject * d_new_arc (gint x, gint y); static Dobject * d_load_arc (FILE *from); static gint load_options (GFigObj *gfig, FILE *fp); static gint gfig_obj_counts (DAllObjs * objs); static void gfig_brush_fill_preview_xy (GtkWidget *pw, gint x , gint y); /* globals */ static gint gfig_run; static GdkGC *gfig_gc; static GdkGC *grid_hightlight_drawgc; static gint grid_gc_type = GTK_STATE_NORMAL; static guchar *pv_cache = NULL; static guchar preview_row[PREVIEW_SIZE*4]; /* Stuff for the preview bit */ static gint sel_x1, sel_y1, sel_x2, sel_y2; static gint sel_width, sel_height; static gint preview_width, preview_height; static gint has_alpha; static gdouble scale_x_factor, scale_y_factor; static gdouble org_scale_x_factor, org_scale_y_factor; MAIN () static void query (void) { static GimpParamDef args[] = { { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" }, { GIMP_PDB_IMAGE, "image", "Input image (unused)" }, { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }, { GIMP_PDB_INT32, "dummy", "dummy" } }; gimp_install_procedure ("plug_in_gfig", "Create Geometrical shapes with the Gimp", "More here later", "Andy Thomas", "Andy Thomas", "1997", N_("/Filters/Render/Gfig..."), "RGB*, GRAY*", GIMP_PLUGIN, G_N_ELEMENTS (args), 0, args, NULL); } static void run (gchar *name, gint nparams, GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { GimpParam * values = g_new (GimpParam, 1); GimpDrawable *drawable; GimpRunMode run_mode; GimpPDBStatusType status = GIMP_PDB_SUCCESS; gint pwidth, pheight; /*kill (getpid (), 19);*/ run_mode = param[0].data.d_int32; gfig_image = param[1].data.d_image; gfig_drawable = param[2].data.d_drawable; *nreturn_vals = 1; *return_vals = values; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; gfig_select_drawable = drawable = gimp_drawable_get (param[2].data.d_drawable); tile_width = gimp_tile_width (); tile_height = gimp_tile_height (); /* TMP Hack - clear any selections */ gimp_selection_clear (gfig_image); gimp_drawable_mask_bounds (drawable->drawable_id, &sel_x1, &sel_y1, &sel_x2, &sel_y2); sel_width = sel_x2 - sel_x1; sel_height = sel_y2 - sel_y1; /* Calculate preview size */ if (sel_width > sel_height) { pwidth = MIN (sel_width, PREVIEW_SIZE); pheight = sel_height * pwidth / sel_width; } else { pheight = MIN (sel_height, PREVIEW_SIZE); pwidth = sel_width * pheight / sel_height; } preview_width = MAX (pwidth, 2); /* Min size is 2 */ preview_height = MAX (pheight, 2); org_scale_x_factor = scale_x_factor = (gdouble) sel_width / (gdouble) preview_width; org_scale_y_factor = scale_y_factor = (gdouble) sel_height / (gdouble) preview_height; switch (run_mode) { case GIMP_RUN_INTERACTIVE: /*gimp_get_data ("plug_in_gfig", &selvals);*/ INIT_I18N_UI (); if (!gfig_dialog ()) { gimp_drawable_detach (drawable); return; } break; case GIMP_RUN_NONINTERACTIVE: status = GIMP_PDB_CALLING_ERROR; break; case GIMP_RUN_WITH_LAST_VALS: /*gimp_get_data ("plug_in_gfig", &selvals);*/ break; default: break; } if (gimp_drawable_is_rgb (drawable->drawable_id) || gimp_drawable_is_gray (drawable->drawable_id)) { /* Set the tile cache size */ gimp_tile_cache_ntiles ((drawable->width + gimp_tile_width () - 1) / gimp_tile_width ()); do_gfig (); if (run_mode != GIMP_RUN_NONINTERACTIVE) gimp_displays_flush (); #if 0 if (run_mode == GIMP_RUN_INTERACTIVE) gimp_set_data ("plug_in_gfig", &selvals, sizeof (SelectItVals)); #endif /* 0 */ } else { status = GIMP_PDB_EXECUTION_ERROR; } values[0].data.d_status = status; gimp_drawable_detach (drawable); } /* * Query gimprc for gfig-path, and parse it. */ static void plug_in_parse_gfig_path (void) { GList *fail_list = NULL; GList *list; gchar *gfig_path; if (gfig_path_list) gimp_path_free (gfig_path_list); gfig_path_list = NULL; gfig_path = gimp_gimprc_query ("gfig-path"); if (!gfig_path) { gchar *gimprc = gimp_personal_rc_file ("gimprc"); gchar *path = g_strescape ("${gimp_dir}" G_DIR_SEPARATOR_S "gfig" G_SEARCHPATH_SEPARATOR_S "${gimp_data_dir}" G_DIR_SEPARATOR_S "gfig", NULL); g_message (_("No gfig-path in gimprc:\n" "You need to add an entry like\n" "(gfig-path \"%s\")\n" "to your %s file."), path, gimprc); g_free (gimprc); g_free (path); return; } gfig_path_list = gimp_path_parse (gfig_path, 16, TRUE, &fail_list); g_free (gfig_path); if (fail_list) { GString *err = g_string_new (_("gfig-path misconfigured - " "the following folders were not found:")); for (list = fail_list; list; list = g_list_next (list)) { g_string_append_c (err, '\n'); g_string_append (err, (gchar *) list->data); } g_message (err->str); g_string_free (err, TRUE); gimp_path_free (fail_list); } } /* Translate SPACE to "\\040", etc. Taken from gflare plugin */ static void gfig_name_encode (gchar *dest, gchar *src) { gint cnt = MAX_LOAD_LINE - 1; while (*src && cnt--) { if (g_ascii_iscntrl (*src) || g_ascii_isspace (*src) || *src == '\\') { sprintf (dest, "\\%03o", *src++); dest += 4; } else *dest++ = *src++; } *dest = '\0'; } /* Translate "\\040" to SPACE, etc. */ static void gfig_name_decode (gchar *dest, gchar *src) { gint cnt = MAX_LOAD_LINE - 1; gint tmp; while (*src && cnt--) { if (*src == '\\' && *(src+1) && *(src+2) && *(src+3)) { sscanf (src+1, "%3o", &tmp); *dest++ = tmp; src += 4; } else *dest++ = *src++; } *dest = '\0'; } /* * Load all gfig, which are founded in gfig-path-list, into gfig_list. * gfig-path-list must be initialized first. (plug_in_parse_gfig_path ()) * based on code from Gflare. */ static gint gfig_list_pos (GFigObj *gfig) { GFigObj *g; gint n; GList *tmp; n = 0; for (tmp = gfig_list; tmp; tmp = g_list_next (tmp)) { g = tmp->data; if (strcmp (gfig->draw_name, g->draw_name) <= 0) break; n++; } return n; } static gint gfig_list_insert (GFigObj *gfig) { gint n; /* * Insert gfigs in alphabetical order */ n = gfig_list_pos (gfig); gfig_list = g_list_insert (gfig_list, gfig, n); #ifdef DEBUG printf ("gfig_list_insert %s => %d\n", gfig->draw_name, n); #endif /* DEBUG */ return n; } static void gfig_free (GFigObj *gfig) { g_assert (gfig != NULL); if (gfig->obj_list) free_all_objs (gfig->obj_list); g_free (gfig->name); g_free (gfig->filename); g_free (gfig->draw_name); g_free (gfig); } static void gfig_free_everything (GFigObj *gfig) { g_assert (gfig != NULL); if (gfig->filename) { #ifdef DEBUG printf ("Removing filename '%s'\n", gfig->filename); #endif /* DEBUG */ remove (gfig->filename); } gfig_free (gfig); } static void gfig_list_free_all (void) { GList *list; GFigObj *gfig; for (list = gfig_list; list; list = g_list_next (list)) { gfig = (GFigObj *) list->data; gfig_free (gfig); } g_list_free (gfig_list); gfig_list = NULL; } static void gfig_list_load_all (GList *plist) { GFigObj *gfig; GList *list; gchar *path; gchar *filename; GDir *dir; const gchar *dir_ent; /* Make sure to clear any existing gfigs */ current_obj = pic_obj = NULL; gfig_list_free_all (); list = plist; while (list) { path = list->data; list = list->next; /* Open directory */ dir = g_dir_open (path, 0, NULL); if (!dir) g_warning ("Error reading GFig folder \"%s\"", path); else { while ((dir_ent = g_dir_read_name (dir))) { filename = g_build_filename (path, dir_ent, NULL); /* Check the file and see that it is not a sub-directory */ if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) { gfig = gfig_load (filename, dir_ent); if (gfig) { /* Read only ?*/ if (access (filename, W_OK)) gfig->obj_status |= GFIG_READONLY; gfig_list_insert (gfig); } } g_free (filename); } g_dir_close (dir); } } if (!gfig_list) { /* lets have at least one! */ gfig = gfig_new (); gfig->draw_name = g_strdup (_("First Gfig")); gfig_list_insert (gfig); } pic_obj = current_obj = gfig_list->data; /* set to first entry */ } static GFigObj * gfig_new (void) { GFigObj * new; new = g_new0 (GFigObj, 1); return new; } static void gfig_load_objs (GFigObj *gfig, gint load_count, FILE *fp) { Dobject *obj; gchar load_buf[MAX_LOAD_LINE]; /* Loading object */ /*kill (getpid (), 19);*/ /* Read first line */ while (load_count-- > 0) { obj = NULL; get_line (load_buf, MAX_LOAD_LINE, fp, 0); if (!strcmp (load_buf, "")) { obj = d_load_line (fp); } else if (!strcmp (load_buf, "")) { obj = d_load_circle (fp); } else if (!strcmp (load_buf, "")) { obj = d_load_ellipse (fp); } else if (!strcmp (load_buf, "")) { obj = d_load_poly (fp); } else if (!strcmp (load_buf, "")) { obj = d_load_star (fp); } else if (!strcmp (load_buf, "")) { obj = d_load_spiral (fp); } else if (!strcmp (load_buf, "")) { obj = d_load_bezier (fp); } else if (!strcmp (load_buf, "")) { obj = d_load_arc (fp); } else { g_warning ("Unknown obj type file %s line %d\n", gfig->filename, line_no); } if (obj) { add_to_all_obj (gfig, obj); } } } static GFigObj * gfig_load (const gchar *filename, const gchar *name) { GFigObj * gfig; FILE * fp; gchar load_buf[MAX_LOAD_LINE]; gchar str_buf[MAX_LOAD_LINE]; gint chk_count; gint load_count = 0; g_assert (filename != NULL); #ifdef DEBUG printf ("Loading %s (%s)\n", filename, name); #endif /* DEBUG */ fp = fopen (filename, "r"); if (!fp) { g_warning ("Error opening: %s", filename); return NULL; } gfig = gfig_new (); gfig->name = g_strdup (name); gfig->filename = g_strdup (filename); /* HEADER * draw_name * version * obj_list */ get_line (load_buf, MAX_LOAD_LINE, fp, 1); if (strncmp (GFIG_HEADER, load_buf, strlen (load_buf))) { g_message ("File '%s' is not a gfig file", gfig->filename); return NULL; } get_line (load_buf, MAX_LOAD_LINE, fp, 0); sscanf (load_buf, "Name: %100s", str_buf); gfig_name_decode (load_buf, str_buf); gfig->draw_name = g_strdup (load_buf); get_line (load_buf, MAX_LOAD_LINE, fp, 0); if (strncmp (load_buf, "Version: ", 9) == 0) gfig->version = g_ascii_strtod (load_buf + 9, NULL); get_line (load_buf, MAX_LOAD_LINE, fp, 0); sscanf (load_buf, "ObjCount: %d", &load_count); if (load_options (gfig, fp)) { g_message ("File '%s' corrupt file - Line %d Option section incorrect", filename, line_no); return NULL; } /*return (NULL);*/ gfig_load_objs (gfig, load_count, fp); /* Check count ? */ chk_count = gfig_obj_counts (gfig->obj_list); if (chk_count != load_count) { g_message ("File '%s' corrupt file - Line %d Object count to small", filename, line_no); return NULL; } fclose (fp); if (!pic_obj) pic_obj = gfig; gfig->obj_status = GFIG_OK; return gfig; } static void save_options (FILE *fp) { /* Save options */ fprintf (fp, "\n"); fprintf (fp, "GridSpacing: %d\n", selvals.opts.gridspacing); if (selvals.opts.gridtype == RECT_GRID) fprintf (fp, "GridType: RECT_GRID\n"); else if (selvals.opts.gridtype == POLAR_GRID) fprintf (fp, "GridType: POLAR_GRID\n"); else if (selvals.opts.gridtype == ISO_GRID) fprintf (fp, "GridType: ISO_GRID\n"); else fprintf (fp, "GridType: RECT_GRID\n"); /* If in doubt, default to RECT_GRID */ fprintf (fp, "DrawGrid: %s\n", (selvals.opts.drawgrid)?"TRUE":"FALSE"); fprintf (fp, "Snap2Grid: %s\n", (selvals.opts.snap2grid)?"TRUE":"FALSE"); fprintf (fp, "LockOnGrid: %s\n", (selvals.opts.lockongrid)?"TRUE":"FALSE"); /* fprintf (fp, "ShowImage: %s\n", (selvals.opts.showimage)?"TRUE":"FALSE");*/ fprintf (fp, "ShowControl: %s\n", (selvals.opts.showcontrol)?"TRUE":"FALSE"); fprintf (fp, "\n"); } static gint load_bool (gchar *opt_buf, gint *toset) { if (!strcmp (opt_buf, "TRUE")) *toset = 1; else if (!strcmp (opt_buf, "FALSE")) *toset = 0; else return (-1); return (0); } static void update_options (GFigObj *old_obj) { /* Save old vals */ if (selvals.opts.gridspacing != old_obj->opts.gridspacing) { old_obj->opts.gridspacing = selvals.opts.gridspacing; } if (selvals.opts.gridtype != old_obj->opts.gridtype) { old_obj->opts.gridtype = selvals.opts.gridtype; } if (selvals.opts.drawgrid != old_obj->opts.drawgrid) { old_obj->opts.drawgrid = selvals.opts.drawgrid; } if (selvals.opts.snap2grid != old_obj->opts.snap2grid) { old_obj->opts.snap2grid = selvals.opts.snap2grid; } if (selvals.opts.lockongrid != old_obj->opts.lockongrid) { old_obj->opts.lockongrid = selvals.opts.lockongrid; } if (selvals.opts.showcontrol != old_obj->opts.showcontrol) { old_obj->opts.showcontrol = selvals.opts.showcontrol; } /* New vals */ if (selvals.opts.gridspacing != current_obj->opts.gridspacing) { gtk_adjustment_set_value (GTK_ADJUSTMENT (gfig_opt_widget.gridspacing), current_obj->opts.gridspacing); } if (selvals.opts.drawgrid != current_obj->opts.drawgrid) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_opt_widget.drawgrid), current_obj->opts.drawgrid); } if (selvals.opts.snap2grid != current_obj->opts.snap2grid) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_opt_widget.snap2grid), current_obj->opts.snap2grid); } if (selvals.opts.lockongrid != current_obj->opts.lockongrid) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_opt_widget.lockongrid), current_obj->opts.lockongrid); } if (selvals.opts.showcontrol != current_obj->opts.showcontrol) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_opt_widget.showcontrol), current_obj->opts.showcontrol); } if (selvals.opts.gridtype != current_obj->opts.gridtype) { gtk_option_menu_set_history (GTK_OPTION_MENU (gfig_opt_widget.gridtypemenu), current_obj->opts.gridtype); gridtype_menu_callback (gtk_menu_get_active (GTK_MENU (gtk_option_menu_get_menu (GTK_OPTION_MENU (gfig_opt_widget.gridtypemenu)))), (gpointer) GRID_TYPE_MENU); #ifdef DEBUG printf ("Gridtype set in options to "); if (current_obj->opts.gridtype == RECT_GRID) printf ("RECT_GRID\n"); else if (current_obj->opts.gridtype == POLAR_GRID) printf ("POLAR_GRID\n"); else if (current_obj->opts.gridtype == ISO_GRID) printf ("ISO_GRID\n"); else printf ("NONE\n"); #endif /* DEBUG */ } } static gint load_options (GFigObj *gfig, FILE *fp) { gchar load_buf[MAX_LOAD_LINE]; gchar str_buf[MAX_LOAD_LINE]; gchar opt_buf[MAX_LOAD_LINE]; get_line (load_buf, MAX_LOAD_LINE, fp, 0); #ifdef DEBUG printf ("load '%s'\n", load_buf); #endif /* DEBUG */ if (strcmp (load_buf, "")) return (-1); get_line (load_buf, MAX_LOAD_LINE, fp, 0); #ifdef DEBUG printf ("opt line '%s'\n", load_buf); #endif /* DEBUG */ while (strcmp (load_buf, "")) { /* Get option name */ #ifdef DEBUG printf ("num = %d\n", sscanf (load_buf, "%s %s", str_buf, opt_buf)); printf ("option %s val %s\n", str_buf, opt_buf); #else sscanf (load_buf, "%s %s", str_buf, opt_buf); #endif /* DEBUG */ if (!strcmp (str_buf, "GridSpacing:")) { /* Value is decimal */ int sp = 0; sp = atoi (opt_buf); if (sp <= 0) return (-1); gfig->opts.gridspacing = sp; } else if (!strcmp (str_buf, "DrawGrid:")) { /* Value is bool */ if (load_bool (opt_buf, &gfig->opts.drawgrid)) return (-1); } else if (!strcmp (str_buf, "Snap2Grid:")) { /* Value is bool */ if (load_bool (opt_buf, &gfig->opts.snap2grid)) return (-1); } else if (!strcmp (str_buf, "LockOnGrid:")) { /* Value is bool */ if (load_bool (opt_buf, &gfig->opts.lockongrid)) return (-1); } else if (!strcmp (str_buf, "ShowControl:")) { /* Value is bool */ if (load_bool (opt_buf, &gfig->opts.showcontrol)) return (-1); } else if (!strcmp (str_buf, "GridType:")) { /* Value is string */ if (!strcmp (opt_buf, "RECT_GRID")) gfig->opts.gridtype = RECT_GRID; else if (!strcmp (opt_buf, "POLAR_GRID")) gfig->opts.gridtype = POLAR_GRID; else if (!strcmp (opt_buf, "ISO_GRID")) gfig->opts.gridtype = ISO_GRID; else return (-1); } get_line (load_buf, MAX_LOAD_LINE, fp, 0); #ifdef DEBUG printf ("opt line '%s'\n", load_buf); #endif /* DEBUG */ } return (0); } static gint gfig_obj_counts (DAllObjs *objs) { gint count = 0; while (objs) { count++; objs = objs->next; } return (count); } static void gfig_save_callbk (void) { FILE *fp; DAllObjs * objs; gint count = 0; gchar *savename; gchar *message; gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; gchar conv_buf[MAX_LOAD_LINE*3 +1]; savename = current_obj->filename; fp = fopen (savename, "w+"); if (!fp) { message = g_strconcat (_("Error opening: %s"), "\n", _("Could not save."), savename); g_message (message); g_free (message); return; } /* Write header out */ fputs (GFIG_HEADER, fp); /* * draw_name * version * obj_list * */ gfig_name_encode (conv_buf, current_obj->draw_name); fprintf (fp, "Name: %s\n", conv_buf); fprintf (fp, "Version: %s\n", g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, "%f", current_obj->version)); objs = current_obj->obj_list; count = gfig_obj_counts (objs); fprintf (fp, "ObjCount: %d\n", count); save_options (fp); objs = current_obj->obj_list; while (objs) { objs->obj->savefunc (objs->obj, fp); objs = objs->next; } if (ferror (fp)) g_message ("Failed to write file\n"); else { gfig_obj_modified (current_obj, GFIG_OK); current_obj->obj_status &= ~(GFIG_MODIFIED | GFIG_READONLY); } fclose (fp); gfig_update_stat_labels (); } static void file_selection_ok (GtkWidget *w, GtkFileSelection *fs, gpointer data) { const gchar *filenamebuf; GFigObj *obj = (GFigObj *)gtk_object_get_user_data (GTK_OBJECT (fs)); GFigObj *real_current; filenamebuf = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)); #ifdef DEBUG g_print ("name selected '%s'\n", filenamebuf); #endif /* DEBUG */ /* Get the name */ if (strlen (filenamebuf) == 0) { g_message ("Save: No filename given"); return; } if (g_file_test (filenamebuf, G_FILE_TEST_IS_DIR)) { g_message ("Save: Can't save to a folder."); return; } obj->filename = g_strdup (filenamebuf); real_current = current_obj; current_obj = obj; gfig_save_callbk (); current_obj = current_obj; gtk_widget_destroy (GTK_WIDGET (fs)); } static void create_file_selection (GFigObj *obj, gchar *tpath) { static GtkWidget *window = NULL; if (!window) { window = gtk_file_selection_new (_("Save Gfig drawing")); gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_widget_destroyed), &window); gtk_object_set_user_data (GTK_OBJECT (window), obj); gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (window)->ok_button), "clicked", GTK_SIGNAL_FUNC (file_selection_ok), (gpointer) window); gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (window)->cancel_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (window)); } if (tpath) { gtk_file_selection_set_filename (GTK_FILE_SELECTION (window), tpath); } else if (gfig_path_list) { gchar *dir; dir = gimp_path_get_user_writable_dir (gfig_path_list); if (!dir) dir = g_strdup (gimp_directory ()); gtk_file_selection_set_filename (GTK_FILE_SELECTION (window), dir); g_free (dir); } else { const gchar *tmp = g_get_tmp_dir (); gtk_file_selection_set_filename (GTK_FILE_SELECTION (window), tmp); } if (!GTK_WIDGET_VISIBLE (window)) gtk_widget_show (window); } static void gfig_save (void) { /* Save the current object */ if (!current_obj->filename) { create_file_selection (current_obj, NULL); return; } gfig_save_callbk (); } /* HACK WARNING */ void * xxx; void * yyy; /* Cache the preview image - updates are a lot faster. */ /* The preview_cache will contain the small image */ static void cache_preview (void) { GimpPixelRgn src_rgn; int y, x; guchar *src_rows; guchar *p; int isgrey = 0; gimp_pixel_rgn_init (&src_rgn, gfig_select_drawable, sel_x1, sel_y1, sel_width, sel_height, FALSE, FALSE); src_rows = g_new (guchar , sel_width * 4); p = pv_cache = g_new (guchar , preview_width * preview_height * 4); real_img_bpp = gimp_drawable_bpp (gfig_select_drawable->drawable_id); has_alpha = gimp_drawable_has_alpha (gfig_select_drawable->drawable_id); if (real_img_bpp < 3) { img_bpp = 3 + has_alpha; } else { img_bpp = real_img_bpp; } switch (gimp_drawable_type (gfig_select_drawable->drawable_id)) { case GIMP_GRAYA_IMAGE: case GIMP_GRAY_IMAGE: isgrey = 1; default: break; } /*memset (p,-1, preview_width*preview_height*4); return;*/ for (y = 0; y < preview_height; y++) { gimp_pixel_rgn_get_row (&src_rgn, src_rows, sel_x1, sel_y1 + (y*sel_height)/preview_height, sel_width); for (x = 0; x < (preview_width); x ++) { /* Get the pixels of each col */ int i; for (i = 0 ; i < 3; i++) p[x*img_bpp+i] = src_rows[((x*sel_width)/preview_width)*src_rgn.bpp +((isgrey)?0:i)]; if (has_alpha) p[x*img_bpp+3] = src_rows[((x*sel_width)/preview_width)*src_rgn.bpp + ((isgrey)?1:3)]; } p += (preview_width*img_bpp); } g_free (src_rows); } static void refill_cache (void) { GdkCursorType ctype1 = GDK_WATCH; GdkCursorType ctype2 = GDK_TOP_LEFT_ARROW; static GdkCursor *preview_cursor1; static GdkCursor *preview_cursor2; if (!preview_cursor1) preview_cursor1 = gdk_cursor_new (ctype1); if (!preview_cursor2) preview_cursor2 = gdk_cursor_new (ctype2); gdk_window_set_cursor (gtk_widget_get_toplevel (GTK_WIDGET (gfig_preview))->window, preview_cursor1); gdk_window_set_cursor (gfig_preview->window, preview_cursor1); gdk_flush (); cache_preview (); gdk_window_set_cursor (gtk_widget_get_toplevel (GTK_WIDGET (gfig_preview))->window, preview_cursor2); toggle_obj_type (NULL, (gpointer) selvals.otype); } static GtkWidget * gfig_list_item_new_with_label_and_pixmap (GFigObj *obj, gchar *label, GtkWidget *pix_widget) { GtkWidget *list_item; GtkWidget *label_widget; GtkWidget *hbox; list_item = gtk_list_item_new (); hbox = gtk_hbox_new (FALSE, 1); gtk_container_add (GTK_CONTAINER (list_item), hbox); gtk_widget_show (hbox); gtk_box_pack_start (GTK_BOX (hbox), pix_widget, FALSE, FALSE, 0); label_widget = gtk_label_new (label); gtk_misc_set_alignment (GTK_MISC (label_widget), 0.0, 0.5); gtk_container_add (GTK_CONTAINER (hbox), label_widget); gtk_widget_show (obj->label_widget = label_widget); gtk_widget_show (obj->pixmap_widget = pix_widget); gtk_widget_show (obj->list_item = list_item); return list_item; } static void gfig_obj_modified (GFigObj *obj, gint stat_type) { g_assert (obj != NULL); if (obj->obj_status == stat_type) return; /* Set the new one up */ if (stat_type == GFIG_MODIFIED) gimp_pixmap_set (GIMP_PIXMAP (obj->pixmap_widget), Floppy6_xpm); else gimp_pixmap_set (GIMP_PIXMAP (obj->pixmap_widget), blank_xpm); } static gint select_button_press (GtkWidget *widget, GdkEventButton *event, gpointer data) { gint type = (gint) data; gint count = 0; DAllObjs * objs; if (current_obj) { objs = current_obj->obj_list; while (objs) { objs = objs->next; count++; } } switch (type) { case OBJ_SELECT_LT: obj_show_single--; if (obj_show_single < 0) obj_show_single = count - 1; break; case OBJ_SELECT_GT: obj_show_single++; if (obj_show_single >= count) obj_show_single = 0; break; case OBJ_SELECT_EQ: obj_show_single = -1; /* Reset to show all */ break; default: break; } draw_grid_clear (widget, data); return FALSE; } static GtkWidget * obj_select_buttons (void) { GtkWidget *button; GtkWidget *hbox, *vbox; vbox = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox); hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); gtk_widget_show (hbox); button = gtk_button_new_with_label ("<"); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); gtk_signal_connect (GTK_OBJECT (button), "button_press_event", GTK_SIGNAL_FUNC (select_button_press), (gpointer) OBJ_SELECT_LT); gtk_widget_show (button); button = gtk_button_new_with_label (">"); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); gtk_signal_connect (GTK_OBJECT (button), "button_press_event", GTK_SIGNAL_FUNC (select_button_press), (gpointer) OBJ_SELECT_GT); gtk_widget_show (button); button = gtk_button_new_with_label ("=="); gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0); gtk_signal_connect (GTK_OBJECT (button), "button_press_event", GTK_SIGNAL_FUNC (select_button_press), (gpointer) OBJ_SELECT_EQ); gtk_widget_show (button); return vbox; } static GtkWidget * but_with_pix (gchar **pixdata, GSList **group, gint baction) { GtkWidget *button; GtkWidget *alignment; GtkWidget *pixmap_widget; button = gtk_radio_button_new (*group); gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE); gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC (toggle_obj_type), (gpointer) baction); gtk_widget_show (button); *group = gtk_radio_button_group (GTK_RADIO_BUTTON (button)); alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); gtk_container_add (GTK_CONTAINER (button), alignment); gtk_widget_show (alignment); pixmap_widget = gimp_pixmap_new (pixdata); gtk_container_add (GTK_CONTAINER (alignment), pixmap_widget); gtk_widget_show (pixmap_widget); return button; } static GtkWidget * small_preview (GtkWidget *list) { GtkWidget *label; GtkWidget *frame; GtkWidget *button; GtkWidget *vbox; gint y; vbox = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox); label = gtk_label_new (_("Prev")); gtk_container_add (GTK_CONTAINER (vbox), label); gtk_widget_show (label); frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); gtk_container_add (GTK_CONTAINER (vbox), frame); gtk_widget_show (frame); pic_preview = gtk_preview_new (GTK_PREVIEW_COLOR); gtk_preview_size (GTK_PREVIEW (pic_preview), SMALL_PREVIEW_SZ, SMALL_PREVIEW_SZ); gtk_container_add (GTK_CONTAINER (frame), pic_preview); gtk_widget_show (pic_preview); /* Fill with white */ for (y = 0; y < SMALL_PREVIEW_SZ; y++) { guchar prow[SMALL_PREVIEW_SZ*3]; memset (prow, -1, SMALL_PREVIEW_SZ * 3); gtk_preview_draw_row (GTK_PREVIEW (pic_preview), prow, 0, y, SMALL_PREVIEW_SZ); } gtk_signal_connect_after (GTK_OBJECT (pic_preview), "expose_event", GTK_SIGNAL_FUNC (pic_preview_expose), NULL); /* More Buttons */ button = gtk_button_new_with_label (_("Edit")); gtk_container_add (GTK_CONTAINER (vbox), button); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (edit_button_callback), (gpointer) list); gimp_help_set_help_data (button, _("Edit Gfig object collection"), NULL); gtk_widget_show (button); button = gtk_button_new_with_label (_("Merge")); gtk_container_add (GTK_CONTAINER (vbox), button); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (merge_button_callback), (gpointer) list); gimp_help_set_help_data (button, _("Merge Gfig Object collection into the " "current edit session"), NULL); gtk_widget_show (button); return vbox; } /* Special case for now - options on poly/star/spiral button */ static void num_sides_dialog (gchar *d_title, gint *num_sides, gint *which_way, gint adj_min, gint adj_max) { GtkWidget *window; GtkWidget *table; GtkObject *size_data; window = gimp_dialog_new (d_title, "gfig", gimp_standard_help_func, "filters/gfig.html", GTK_WIN_POS_MOUSE, FALSE, TRUE, FALSE, GTK_STOCK_CLOSE, gtk_widget_destroy, NULL, 1, NULL, TRUE, TRUE, NULL); table = gtk_table_new (which_way ? 2 : 1, 3, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_table_set_row_spacings (GTK_TABLE (table), 2); gtk_container_set_border_width (GTK_CONTAINER (table), 6); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), table, FALSE, FALSE, 0); gtk_widget_show (table); size_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0, _("Number of Sides/Points/Turns:"), 0, 0, *num_sides, adj_min, adj_max, 1, 10, 0, TRUE, 0, 0, NULL, NULL); gtk_signal_connect (GTK_OBJECT (size_data), "value_changed", GTK_SIGNAL_FUNC (gimp_int_adjustment_update), num_sides); if (which_way) { GtkWidget *option_menu; option_menu = gimp_option_menu_new2 (FALSE, G_CALLBACK (gimp_menu_item_update), which_way, (gpointer) *which_way, _("Clockwise"), (gpointer) 0, NULL, _("Anti-Clockwise"), (gpointer) 1, NULL, NULL); gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, _("Orientation:"), 1.0, 0.5, option_menu, 1, TRUE); } gtk_widget_show (window); } static void bezier_dialog (void) { static GtkWidget *window = NULL; GtkWidget *vbox; GtkWidget *toggle; if (window) { gdk_window_raise (window->window); return; } window = gimp_dialog_new (_("Bezier Settings"), "gfig", gimp_standard_help_func, "filters/gfig.html", GTK_WIN_POS_MOUSE, FALSE, FALSE, FALSE, GTK_STOCK_CLOSE, gtk_widget_destroy, NULL, 1, NULL, TRUE, TRUE, NULL); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_widget_destroyed), &window); vbox = gtk_vbox_new (FALSE, 2); gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), vbox, FALSE, FALSE, 0); gtk_widget_show (vbox); toggle = gtk_check_button_new_with_label (_("Closed")); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gimp_toggle_button_update), &bezier_closed); gimp_help_set_help_data (toggle, _("Close curve on completion"), NULL); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bezier_closed); gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0); gtk_widget_show (toggle); toggle = gtk_check_button_new_with_label (_("Show Line Frame")); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gimp_toggle_button_update), &bezier_line_frame); gimp_help_set_help_data (toggle, _("Draws lines between the control points. " "Only during curve creation"), NULL); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bezier_line_frame); gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0); gtk_widget_show (toggle); gtk_widget_show (window); } static gint poly_button_press (GtkWidget *widget, GdkEventButton *event, gpointer data) { if ((event->type == GDK_2BUTTON_PRESS) && (event->button == 1)) num_sides_dialog (_("Regular Polygon Number of Sides"), &poly_num_sides, NULL, 3, 200); return FALSE; } static gint star_button_press (GtkWidget *widget, GdkEventButton *event, gpointer data) { if ((event->type == GDK_2BUTTON_PRESS) && (event->button == 1)) num_sides_dialog (_("Star Number of Points"), &star_num_sides, NULL, 3, 200); return FALSE; } static gint spiral_button_press (GtkWidget *widget, GdkEventButton *event, gpointer data) { if ((event->type == GDK_2BUTTON_PRESS) && (event->button == 1)) num_sides_dialog (_("Spiral Number of Points"), &spiral_num_turns, &spiral_toggle, 1, 20); return FALSE; } static gint bezier_button_press (GtkWidget *widget, GdkEventButton *event, gpointer data) { if ((event->type == GDK_2BUTTON_PRESS) && (event->button == 1)) bezier_dialog (); return FALSE; } static GtkWidget * draw_buttons (GtkWidget *ww) { GtkWidget *frame; GtkWidget *button; GtkWidget *vbox; GSList *group; frame = gtk_frame_new (_("Ops")); gtk_container_set_border_width (GTK_CONTAINER (frame), 1); /* Create group */ group = NULL; vbox = gtk_vbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_container_set_border_width (GTK_CONTAINER (vbox), 2); /* Put buttons in */ button = but_with_pix (line_xpm, &group, LINE); gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0); gtk_widget_show (button); gimp_help_set_help_data (button, _("Create line"), NULL); button = but_with_pix (circle_xpm, &group, CIRCLE); gtk_container_add (GTK_CONTAINER (vbox), button); gtk_widget_show (button); gimp_help_set_help_data (button, _("Create circle"), NULL); button = but_with_pix (ellipse_xpm, &group, ELLIPSE); gtk_container_add (GTK_CONTAINER (vbox), button); gtk_widget_show (button); gimp_help_set_help_data (button, _("Create ellipse"), NULL); button = but_with_pix (curve_xpm, &group, ARC); gtk_container_add (GTK_CONTAINER (vbox), button); gtk_widget_show (button); gimp_help_set_help_data (button, _("Create arch"), NULL); button = but_with_pix (poly_xpm, &group, POLY); gtk_container_add (GTK_CONTAINER (vbox), button); gtk_widget_show (button); gtk_signal_connect (GTK_OBJECT (button), "button_press_event", GTK_SIGNAL_FUNC (poly_button_press), NULL); gimp_help_set_help_data (button, _("Create reg polygon"), NULL); button = but_with_pix (star_xpm, &group, STAR); gtk_container_add (GTK_CONTAINER (vbox), button); gtk_widget_show (button); gtk_signal_connect (GTK_OBJECT (button), "button_press_event", GTK_SIGNAL_FUNC (star_button_press), NULL); gimp_help_set_help_data (button, _("Create star"), NULL); button = but_with_pix (spiral_xpm, &group, SPIRAL); gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0); gtk_widget_show (button); gtk_signal_connect (GTK_OBJECT (button), "button_press_event", GTK_SIGNAL_FUNC (spiral_button_press), NULL); gimp_help_set_help_data (button, _("Create spiral"), NULL); button = but_with_pix (bezier_xpm, &group, BEZIER); gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0); gtk_widget_show (button); gtk_signal_connect (GTK_OBJECT (button), "button_press_event", GTK_SIGNAL_FUNC (bezier_button_press), NULL); gimp_help_set_help_data (button, _("Create bezier curve. " "Shift + Button ends object creation."), NULL); button = but_with_pix (move_obj_xpm, &group, MOVE_OBJ); gtk_container_add (GTK_CONTAINER (vbox), button); gtk_widget_show (button); gimp_help_set_help_data (button, _("Move an object"), NULL); button = but_with_pix (move_point_xpm, &group, MOVE_POINT); gtk_container_add (GTK_CONTAINER (vbox), button); gtk_widget_show (button); gimp_help_set_help_data (button, _("Move a single point"), NULL); button = but_with_pix (copy_obj_xpm, &group, COPY_OBJ); gtk_container_add (GTK_CONTAINER (vbox), button); gtk_widget_show (button); gimp_help_set_help_data (button, _("Copy an object"), NULL); button = but_with_pix (delete_xpm, &group, DEL_OBJ); gtk_container_add (GTK_CONTAINER (vbox), button); gtk_widget_show (button); gimp_help_set_help_data (button, _("Delete an object"), NULL); button = obj_select_buttons (); gtk_container_add (GTK_CONTAINER (vbox), button); gtk_widget_show (button); #if 0 button = but_with_pix (blank_xpm, &group, NULL_OPER); gtk_container_add (GTK_CONTAINER (vbox), button); gtk_widget_set_sensitive (button, FALSE); gtk_widget_show (button); #endif /* 0 */ gtk_widget_show (vbox); gtk_widget_show (frame); return frame; } /* Brush preview stuff */ static gint gfig_brush_preview_events (GtkWidget *widget, GdkEvent *event) { GdkEventButton *bevent; GdkEventMotion *mevent; static GdkPoint point; static int have_start = 0; switch (event->type) { case GDK_EXPOSE: break; case GDK_BUTTON_PRESS: bevent = (GdkEventButton *) event; point.x = bevent->x; point.y = bevent->y; have_start = 1; break; case GDK_BUTTON_RELEASE: bevent = (GdkEventButton *) event; have_start = 0; break; case GDK_MOTION_NOTIFY: mevent = (GdkEventMotion *) event; if (!have_start || !(mevent->state & GDK_BUTTON1_MASK)) break; gfig_brush_fill_preview_xy (widget, point.x - mevent->x, point.y - mevent->y); gtk_widget_draw (widget, NULL); point.x = mevent->x; point.y = mevent->y; break; default: break; } return FALSE; } static void gfig_brush_update_preview (GtkWidget *widget, gpointer data) { GtkWidget *pw = (GtkWidget *) data; BrushDesc *bdesc; /* Must update the dialog area */ /* Use the same brush as already set in the dialog */ bdesc = gtk_object_get_user_data (GTK_OBJECT (pw)); brush_list_button_callback (NULL, bdesc); } static void gfig_brush_menu_callback (GtkWidget *widget, gpointer data) { gimp_menu_item_update (widget, &selvals.brshtype); switch (selvals.brshtype) { case BRUSH_BRUSH_TYPE: gtk_widget_hide (pressure_hbox); gtk_widget_hide (pencil_hbox); gtk_widget_show (fade_out_hbox); gtk_widget_show (gradient_hbox); break; case BRUSH_PENCIL_TYPE: gtk_widget_hide (fade_out_hbox); gtk_widget_hide (gradient_hbox); gtk_widget_hide (pressure_hbox); gtk_widget_show (pencil_hbox); break; case BRUSH_AIRBRUSH_TYPE: gtk_widget_hide (fade_out_hbox); gtk_widget_hide (gradient_hbox); gtk_widget_hide (pencil_hbox); gtk_widget_show (pressure_hbox); break; case BRUSH_PATTERN_TYPE: gtk_widget_hide (fade_out_hbox); gtk_widget_hide (gradient_hbox); gtk_widget_hide (pressure_hbox); gtk_widget_show (pencil_hbox); break; default: g_warning ("Internal error - invalid brush type"); break; } gfig_brush_update_preview (widget, data); } static GtkWidget * gfig_brush_preview (GtkWidget **pv) { GtkWidget *option_menu; GtkWidget *frame; GtkWidget *hbox; GtkWidget *vbox; gint y; hbox = gtk_hbox_new (FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox), 4); gtk_widget_show (hbox); frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); gtk_container_set_border_width (GTK_CONTAINER (frame), 0); gtk_widget_show (frame); *pv = gtk_preview_new (GTK_PREVIEW_COLOR); gtk_widget_show (*pv); gtk_widget_set_events (GTK_WIDGET (*pv), PREVIEW_MASK); gtk_signal_connect (GTK_OBJECT (*pv), "event", GTK_SIGNAL_FUNC (gfig_brush_preview_events), NULL); gtk_preview_size (GTK_PREVIEW (*pv), BRUSH_PREVIEW_SZ, BRUSH_PREVIEW_SZ); gtk_container_add (GTK_CONTAINER (frame), *pv); /* Fill with white */ for (y = 0; y < BRUSH_PREVIEW_SZ; y++) { guchar prow[BRUSH_PREVIEW_SZ*3]; memset (prow, -1, BRUSH_PREVIEW_SZ * 3); gtk_preview_draw_row (GTK_PREVIEW (*pv), prow, 0, y, BRUSH_PREVIEW_SZ); } /* Now the buttons */ vbox = gtk_vbox_new (FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); gtk_widget_show (vbox); option_menu = gimp_option_menu_new2 (FALSE, G_CALLBACK (gfig_brush_menu_callback), *pv, (gpointer) selvals.brshtype, _("Brush"), (gpointer) BRUSH_BRUSH_TYPE, NULL, _("Airbrush"), (gpointer) BRUSH_AIRBRUSH_TYPE, NULL, _("Pencil"), (gpointer) BRUSH_PENCIL_TYPE, NULL, _("Pattern"), (gpointer) BRUSH_PATTERN_TYPE, NULL, NULL); gtk_widget_show (option_menu); gtk_container_add (GTK_CONTAINER (vbox), option_menu); gimp_help_set_help_data (option_menu, _("Use the brush/pencil or the airbrush when drawing " "on the image. Pattern paints with currently " "selected brush with a pattern. Only applies to " "circles/ellipses if Approx. Circles/Ellipses " "toggle is set."), NULL); gtk_container_add (GTK_CONTAINER (hbox), vbox); gtk_container_add (GTK_CONTAINER (hbox), frame); return hbox; } static void gfig_brush_fill_preview_xy (GtkWidget *pw, gint x1, gint y1) { gint row_count; BrushDesc *bdesc = (BrushDesc*) gtk_object_get_user_data (GTK_OBJECT (pw)); /* Adjust start position */ bdesc->x_off += x1; bdesc->y_off += y1; if (bdesc->y_off < 0) bdesc->y_off = 0; if (bdesc->y_off > (bdesc->height - BRUSH_PREVIEW_SZ)) bdesc->y_off = bdesc->height - BRUSH_PREVIEW_SZ; if (bdesc->x_off < 0) bdesc->x_off = 0; if (bdesc->x_off > (bdesc->width - BRUSH_PREVIEW_SZ)) bdesc->x_off = bdesc->width - BRUSH_PREVIEW_SZ; /* Given an x and y fill preview in correctly offsetted */ for (row_count = 0; row_count < BRUSH_PREVIEW_SZ; row_count++) gtk_preview_draw_row (GTK_PREVIEW (pw), &bdesc->pv_buf[bdesc->x_off*bdesc->bpp + (bdesc->width *bdesc->bpp *(row_count + bdesc->y_off))], 0, row_count, BRUSH_PREVIEW_SZ); } static void gfig_brush_fill_preview (GtkWidget *pw, gint32 layer_ID, BrushDesc *bdesc) { GimpPixelRgn src_rgn; GimpDrawable *brushdrawable; gint bcount = 3; if (bdesc->pv_buf) { g_free (bdesc->pv_buf); /* Free old area */ } brushdrawable = gimp_drawable_get (layer_ID); bdesc->bpp = bcount; /* Fill the preview with the current brush name */ gimp_pixel_rgn_init (&src_rgn, brushdrawable, 0, 0, bdesc->width, bdesc->height, FALSE, FALSE); bdesc->pv_buf = g_new (guchar, bdesc->width * bdesc->height * bcount); bdesc->x_off = bdesc->y_off = 0; /* Start from top left */ gimp_pixel_rgn_get_rect (&src_rgn, bdesc->pv_buf, 0, 0, bdesc->width, bdesc->height); /* Dump the pv_buf into the preview area */ gfig_brush_fill_preview_xy (pw, 0, 0); } static void mygimp_brush_set (gchar *bname) { GimpParam *return_vals; int nreturn_vals; return_vals = gimp_run_procedure ("gimp_brushes_set_brush", &nreturn_vals, GIMP_PDB_STRING, bname, GIMP_PDB_END); if (return_vals[0].data.d_status != GIMP_PDB_SUCCESS) { g_message ("Can't set brush...(1)"); } gimp_destroy_params (return_vals, nreturn_vals); } static gchar * mygimp_brush_get (void) { GimpParam *return_vals; int nreturn_vals; static gchar saved_bname[1024]; /* required to be static - returned from proc */ return_vals = gimp_run_procedure ("gimp_brushes_get_brush", &nreturn_vals, GIMP_PDB_END); if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS) { strncpy (saved_bname, return_vals[1].data.d_string, sizeof (saved_bname)); } else { saved_bname[0] = '\0'; } gimp_destroy_params (return_vals, nreturn_vals); return (saved_bname); } static void mygimp_brush_info (gint32 *width, gint32 *height) { GimpParam *return_vals; int nreturn_vals; return_vals = gimp_run_procedure ("gimp_brushes_get_brush", &nreturn_vals, GIMP_PDB_END); if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS) { *width = MAX (return_vals[2].data.d_int32, 32); *height = MAX (return_vals[3].data.d_int32, 32); } else { g_message ("Failed to get brush info"); *width = *height = 48; } gimp_destroy_params (return_vals, nreturn_vals); } static void gfig_paint (BrushType brush_type, gint32 drawable_ID, gint seg_count, gdouble line_pnts[]) { switch (brush_type) { case BRUSH_BRUSH_TYPE: gimp_paintbrush (drawable_ID, selvals.brushfade, seg_count, line_pnts, GIMP_PAINT_CONSTANT, selvals.brushgradient); break; case BRUSH_PENCIL_TYPE: gimp_pencil (drawable_ID, seg_count, line_pnts); break; case BRUSH_AIRBRUSH_TYPE: gimp_airbrush (drawable_ID, selvals.airbrushpressure, seg_count, line_pnts); break; case BRUSH_PATTERN_TYPE: gimp_clone (drawable_ID, drawable_ID, GIMP_PATTERN_CLONE, 0.0, 0.0, seg_count, line_pnts); break; } } static gint32 gfig_gen_brush_preview (BrushDesc *bdesc) { /* Given the name of a brush then paint it and return the ID of the image * the preview can be got from */ static gint32 layer_ID = -1; gchar *saved_bname; gint32 width, height; gdouble line_pnts[2]; GimpRGB foreground; GimpRGB background; GimpRGB color; if (brush_image_ID == -1) { /* Create a new image */ brush_image_ID = gimp_image_new (48, 48, 0); if (brush_image_ID < 0) { g_message ("Failed to generate brush preview"); return -1; } if ((layer_ID = gimp_layer_new (brush_image_ID, "Brush preview", 48, 48, 0, /* RGB type */ 100.0, /* opacity */ 0 /* mode */)) < 0) { g_message ("Error in creating layer for brush preview"); return -1; } gimp_image_add_layer (brush_image_ID, layer_ID, -1); } /* Need this later to delete it */ /* Store foreground & backgroud colours set to black/white * paint with brush * restore colours */ gimp_palette_get_foreground (&foreground); gimp_palette_get_background (&background); saved_bname = mygimp_brush_get (); gimp_rgba_set (&color, 1.0, 1.0, 1.0, 1.0); gimp_palette_set_background (&color); gimp_rgba_set (&color, 0.0, 0.0, 0.0, 1.0); gimp_palette_set_foreground (&color); mygimp_brush_set (bdesc->bname); mygimp_brush_info (&width, &height); bdesc->width = width; bdesc->height = height; line_pnts[0] = (gdouble) width / 2; line_pnts[1] = (gdouble) height / 2; gimp_layer_resize (layer_ID, width, height, 0, 0); gimp_image_resize (brush_image_ID, width, height, 0, 0); gimp_drawable_fill (layer_ID, 1); /* Clear... Fill with white ... */ /* Blob of paint */ gfig_paint (selvals.brshtype, layer_ID, 2, line_pnts); gimp_palette_set_background (&background); gimp_palette_set_foreground (&foreground); mygimp_brush_set (saved_bname); return layer_ID; } static void brush_list_button_callback (GtkWidget *widget, gpointer data) { gint32 layer_ID; BrushDesc *bdesc = (BrushDesc *) data; if ((layer_ID = gfig_gen_brush_preview (bdesc)) != -1) { gtk_object_set_user_data (GTK_OBJECT (brush_page_pw), (gpointer) bdesc); gfig_brush_fill_preview (brush_page_pw, layer_ID, bdesc); gtk_widget_draw (brush_page_pw, NULL); } } /* Build the dialog up. This was the hard part! */ static GtkWidget *page_menu_bg; static GtkWidget *page_menu_layers; static void gfig_brush_pane_activate (GtkWidget *widget, gpointer data) { gint flag = (gint) data; if (flag) { gtk_widget_show (brush_page_widget); } else { gtk_widget_hide (brush_page_widget); } gtk_widget_set_sensitive (brush_page_widget, flag); } static void gfig_select_pane_activate (GtkWidget *widget, gpointer data) { gint flag = (gint) data; if (flag) { gtk_widget_show (select_page_widget); } else { gtk_widget_hide (select_page_widget); } gtk_widget_set_sensitive (select_page_widget, flag); } static void paint_menu_callback (GtkWidget *widget, gpointer data) { gint mtype = (gint)data; if (mtype == PAINT_LAYERS_MENU) { #ifdef DEBUG printf ("layer type set to %s\n", ((DrawonLayers)gtk_object_get_user_data (GTK_OBJECT (widget)) == SINGLE_LAYER)?"SINGLE_LAYER":"MULTI_LAYER"); #endif /* DEBUG */ selvals.onlayers = (DrawonLayers)gtk_object_get_user_data (GTK_OBJECT (widget)); /* Type only meaningful if creating new layers */ if (selvals.onlayers == ORIGINAL_LAYER) gtk_widget_set_sensitive (page_menu_bg, FALSE); else gtk_widget_set_sensitive (page_menu_bg, TRUE); } else if (mtype == PAINT_BGS_MENU) { #ifdef DEBUG printf ("BG type = %d\n", ((LayersBGType)gtk_object_get_user_data (GTK_OBJECT (widget)))); #endif /* DEBUG */ selvals.onlayerbg = (LayersBGType)gtk_object_get_user_data (GTK_OBJECT (widget)); } else if (mtype == PAINT_TYPE_MENU) { #ifdef DEBUG printf ("Got type menu = %d\n", (PaintType)gtk_object_get_user_data (GTK_OBJECT (widget))); #endif /* DEBUG */ selvals.painttype = (PaintType)gtk_object_get_user_data (GTK_OBJECT (widget)); switch (selvals.painttype) { case PAINT_BRUSH_TYPE: gtk_widget_set_sensitive (select_page_widget, FALSE); gtk_widget_set_sensitive (brush_page_widget, TRUE); gtk_widget_set_sensitive (page_menu_layers, TRUE); if (selvals.onlayers == ORIGINAL_LAYER) gtk_widget_set_sensitive (page_menu_bg, FALSE); else gtk_widget_set_sensitive (page_menu_bg, TRUE); break; case PAINT_SELECTION_TYPE: gtk_widget_set_sensitive (select_page_widget, TRUE); gtk_widget_set_sensitive (brush_page_widget, FALSE); gtk_widget_set_sensitive (page_menu_layers, FALSE); gtk_widget_set_sensitive (page_menu_bg, FALSE); break; case PAINT_SELECTION_FILL_TYPE: gtk_widget_set_sensitive (select_page_widget, TRUE); gtk_widget_set_sensitive (brush_page_widget, FALSE); gtk_widget_set_sensitive (page_menu_layers, TRUE); if (selvals.onlayers == ORIGINAL_LAYER) gtk_widget_set_sensitive (page_menu_bg, FALSE); else gtk_widget_set_sensitive (page_menu_bg, TRUE); break; default: break; } } } static GtkWidget * paint_page (void) { GtkWidget *table; GtkWidget *vbox; GtkWidget *vbox2; GtkWidget *hbox; GtkWidget *toggle; GtkWidget *page_menu_type; GtkWidget *scale_scale; GtkObject *scale_scale_data; GtkWidget *item1, *item2, *item3; vbox = gtk_vbox_new (FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); table = gtk_table_new (5, 2, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_table_set_row_spacings (GTK_TABLE (table), 2); gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); gtk_widget_show (table); page_menu_layers = gimp_option_menu_new2 (FALSE, G_CALLBACK (paint_menu_callback), (gpointer) PAINT_LAYERS_MENU, 0, _("Original"), (gpointer) ORIGINAL_LAYER, NULL, _("New"), (gpointer) SINGLE_LAYER, NULL, _("Multiple"), (gpointer) MULTI_LAYER, NULL, NULL); gimp_help_set_help_data (page_menu_layers, _("Draw all objects on one layer (original or new) " "or one object per layer"), NULL); if (gimp_drawable_is_channel (gfig_drawable)) gtk_widget_set_sensitive (page_menu_layers, FALSE); gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, _("Draw on:"), 1.0, 0.5, page_menu_layers, 1, TRUE); page_menu_type = gimp_option_menu_new2 (FALSE, G_CALLBACK (paint_menu_callback), (gpointer) PAINT_TYPE_MENU, 0, _("Brush"), (gpointer) PAINT_BRUSH_TYPE, &item1, _("Selection"), (gpointer) PAINT_SELECTION_TYPE, &item2, _("Selection+Fill"), (gpointer) PAINT_SELECTION_FILL_TYPE, &item3, NULL); gtk_signal_connect (GTK_OBJECT (item1), "activate", GTK_SIGNAL_FUNC (gfig_brush_pane_activate), (gpointer) 1); gtk_signal_connect (GTK_OBJECT (item1), "activate", GTK_SIGNAL_FUNC (gfig_select_pane_activate), (gpointer) 0); gtk_signal_connect (GTK_OBJECT (item2), "activate", GTK_SIGNAL_FUNC (gfig_brush_pane_activate), (gpointer) 0); gtk_signal_connect (GTK_OBJECT (item2), "activate", GTK_SIGNAL_FUNC (gfig_select_pane_activate), (gpointer) 1); gtk_signal_connect (GTK_OBJECT (item3), "activate", GTK_SIGNAL_FUNC (gfig_brush_pane_activate), (gpointer) 0); gtk_signal_connect (GTK_OBJECT (item3), "activate", GTK_SIGNAL_FUNC (gfig_select_pane_activate), (gpointer) 1); gimp_help_set_help_data (page_menu_type, _("Draw type. Either a brush or a selection. " "See brush page or selection page for more options"), NULL); gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, _("Using:"), 1.0, 0.5, page_menu_type, 1, TRUE); page_menu_bg = gimp_option_menu_new2 (FALSE, G_CALLBACK (paint_menu_callback), (gpointer) PAINT_BGS_MENU, 0, _("Transparent"), (gpointer) LAYER_TRANS_BG, NULL, _("Background"), (gpointer) LAYER_BG_BG, NULL, _("Foreground"), (gpointer) LAYER_FG_BG, NULL, _("White"), (gpointer) LAYER_WHITE_BG, NULL, _("Copy"), (gpointer) LAYER_COPY_BG, NULL, NULL); gimp_help_set_help_data (page_menu_bg, _("Layer background type. Copy causes previous " "layer to be copied before the draw is performed"), NULL); gtk_widget_set_sensitive (page_menu_bg, FALSE); gimp_table_attach_aligned (GTK_TABLE (table), 0, 2, _("With BG of:"), 1.0, 0.5, page_menu_bg, 1, TRUE); toggle = gtk_check_button_new_with_label (_("Reverse Line")); gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 4, 5, GTK_FILL, GTK_FILL, 0, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gimp_toggle_button_update), (gpointer)&selvals.reverselines); gimp_help_set_help_data (toggle, _("Draw lines in reverse order"), NULL); gtk_widget_show (toggle); vbox2 = gtk_vbox_new (FALSE, 0); gtk_table_attach (GTK_TABLE (table), vbox2, 0, 1, 3, 4, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (vbox2); toggle = gtk_check_button_new_with_label (_("Scale to Image")); gtk_box_pack_end (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), selvals.scaletoimage); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gfig_scale2img_update), &selvals.scaletoimage); gimp_help_set_help_data (toggle, _("Scale drawings to images size"), NULL); gtk_widget_show (toggle); hbox = gtk_hbox_new (FALSE, 1); scale_scale_data = gtk_adjustment_new (1.0, 0.1, 5.0, 0.01, 0.01, 0.0); scale_scale = gtk_hscale_new (GTK_ADJUSTMENT (scale_scale_data)); gtk_box_pack_start (GTK_BOX (hbox), scale_scale, TRUE, TRUE, 0); gtk_scale_set_value_pos (GTK_SCALE (scale_scale), GTK_POS_TOP); gtk_scale_set_digits (GTK_SCALE (scale_scale), 2); gtk_range_set_update_policy (GTK_RANGE (scale_scale), GTK_UPDATE_CONTINUOUS); gtk_signal_connect (GTK_OBJECT (scale_scale_data), "value_changed", GTK_SIGNAL_FUNC (gfig_scale_update_scale), &selvals.scaletoimagefp); gtk_widget_show (scale_scale); gtk_widget_show (hbox); gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 3, 4, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); gtk_widget_set_sensitive (GTK_WIDGET (scale_scale), FALSE); gtk_object_set_data (GTK_OBJECT (toggle), "inverse_sensitive", scale_scale); gtk_object_set_user_data (GTK_OBJECT (toggle), scale_scale_data); toggle = gtk_check_button_new_with_label (_("Approx. Circles/Ellipses")); gtk_table_attach (GTK_TABLE (table), toggle, 1, 2, 4, 5, GTK_FILL, GTK_FILL, 0, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gimp_toggle_button_update), &selvals.approxcircles); gimp_help_set_help_data (toggle, _("Approx. circles & ellipses using lines. Allows " "the use of brush fading with these types of " "objects."), NULL); gtk_widget_show (toggle); return vbox; } static void gfig_brush_invoker (gchar *name, gdouble opacity, gint spacing, gint paint_mode, gint width, gint height, gchar *mask_data, gint closing, gpointer udata) { BrushDesc *bdesc = g_new0 (BrushDesc, 1); /* Mem leak */ bdesc->bpp = 3; bdesc->width = width; bdesc->height = height; bdesc->bname = g_strdup (name); brush_list_button_callback (NULL, bdesc); } static void select_brush_callback (GtkWidget *widget, gpointer data) { BrushDesc *bdesc = g_new0 (BrushDesc, 1); gimp_interactive_selection_brush (_("Gfig brush selection"), mygimp_brush_get (), 100.0, /* Opacity */ -1, /* spacing (default)*/ 1, /* Paint mode */ gfig_brush_invoker, NULL); bdesc->bpp = 3; bdesc->bname = mygimp_brush_get (); brush_list_button_callback (NULL, bdesc); } static GtkWidget * brush_page (void) { GtkWidget *table; GtkWidget *label; GtkWidget *pw; GtkWidget *scale; GtkObject *fade_out_scale_data; GtkObject *gradient_scale_data; GtkObject *pressure_scale_data; GtkWidget *vbox; GtkWidget *button; BrushDesc *bdesc = g_new0 (BrushDesc, 1); /* Initial brush settings */ vbox = gtk_vbox_new (FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); table = gtk_table_new (2, 3, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_table_set_row_spacings (GTK_TABLE (table), 2); gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); gtk_widget_show (table); /* Fade option */ /* the fade-out scale From GIMP itself*/ fade_out_hbox = gtk_hbox_new (FALSE, 4); label = gtk_label_new (_("Fade out:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); gtk_box_pack_start (GTK_BOX (fade_out_hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); fade_out_scale_data = gtk_adjustment_new (0.0, 0.0, 3000.0, 1.0, 1.0, 0.0); scale = gtk_hscale_new (GTK_ADJUSTMENT (fade_out_scale_data)); gtk_box_pack_start (GTK_BOX (fade_out_hbox), scale, TRUE, TRUE, 0); gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP); gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED); gtk_signal_connect (GTK_OBJECT (fade_out_scale_data), "value_changed", GTK_SIGNAL_FUNC (gimp_double_adjustment_update), &selvals.brushfade); gtk_widget_show (scale); gtk_table_attach (GTK_TABLE (table), fade_out_hbox, 0, 2, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); gtk_widget_show (fade_out_hbox); /* Gradient drawing */ gradient_hbox = gtk_hbox_new (FALSE, 4); label = gtk_label_new (_("Gradient:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); gtk_box_pack_start (GTK_BOX (gradient_hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); gradient_scale_data = gtk_adjustment_new (0.0, 0.0, 3000.0, 1.0, 1.0, 0.0); scale = gtk_hscale_new (GTK_ADJUSTMENT (gradient_scale_data)); gtk_box_pack_start (GTK_BOX (gradient_hbox), scale, TRUE, TRUE, 0); gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP); gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED); gtk_signal_connect (GTK_OBJECT (gradient_scale_data), "value_changed", GTK_SIGNAL_FUNC (gimp_double_adjustment_update), &selvals.brushgradient); gtk_widget_show (scale); gtk_table_attach (GTK_TABLE (table), gradient_hbox, 0, 2, 2, 3, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); gtk_widget_show (gradient_hbox); pressure_hbox = gtk_hbox_new (FALSE, 4); label = gtk_label_new (_("Pressure:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); gtk_box_pack_start (GTK_BOX (pressure_hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); pressure_scale_data = gtk_adjustment_new (20.0, 0.0, 100.0, 1.0, 1.0, 0.0); scale = gtk_hscale_new (GTK_ADJUSTMENT (pressure_scale_data)); gtk_box_pack_start (GTK_BOX (pressure_hbox), scale, TRUE, TRUE, 0); gtk_widget_show (scale); gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP); gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED); gtk_signal_connect (GTK_OBJECT (pressure_scale_data), "value_changed", GTK_SIGNAL_FUNC (gimp_double_adjustment_update), &selvals.airbrushpressure); gtk_table_attach (GTK_TABLE (table), pressure_hbox, 0, 2, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); pencil_hbox = gtk_hbox_new (FALSE, 4); label = gtk_label_new (_("No Options...")); gtk_box_pack_start (GTK_BOX (pencil_hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); gtk_table_attach (GTK_TABLE (table), pencil_hbox, 0, 2, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); /* Preview widget */ pw = gfig_brush_preview (&brush_page_pw); gtk_table_attach (GTK_TABLE (table), pw, 0, 1, 0, 1, 0, 0, 0, 0); gtk_signal_connect (GTK_OBJECT (pressure_scale_data), "value_changed", GTK_SIGNAL_FUNC (gfig_brush_update_preview), (gpointer)brush_page_pw); /* Start of new brush selection code */ brush_sel_button = button = gtk_button_new_with_label (_("Set Brush...")); gtk_misc_set_padding (GTK_MISC (GTK_BIN (brush_sel_button)->child), 2, 0); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (select_brush_callback), NULL); gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, NULL, 0, 0, button, 1, TRUE); /* Setup initial brush settings */ bdesc->bpp = 3; bdesc->bname = mygimp_brush_get (); brush_list_button_callback (NULL, bdesc); return vbox; } static void select_menu_callback (GtkWidget *widget, gpointer data) { gint mtype = (gint) data; if (mtype == SELECT_TYPE_MENU) { SelectionType type = (SelectionType) gtk_object_get_user_data (GTK_OBJECT (widget)); selopt.type = type; } else if (mtype == SELECT_ARCTYPE_MENU) { ArcType type = (ArcType) gtk_object_get_user_data (GTK_OBJECT (widget)); selopt.as_pie = type; } else if (mtype == SELECT_TYPE_MENU_FILL) { FillType type = (FillType) gtk_object_get_user_data (GTK_OBJECT (widget)); selopt.fill_type = type; } else if (mtype == SELECT_TYPE_MENU_WHEN) { FillWhen type = (FillWhen) gtk_object_get_user_data (GTK_OBJECT (widget)); selopt.fill_when = type; } } static GtkWidget * select_page (void) { GtkWidget *menu; GtkWidget *toggle; GtkWidget *scale; GtkObject *scale_data; GtkWidget *table; GtkWidget *vbox; vbox = gtk_vbox_new (FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); table = gtk_table_new (4, 4, FALSE); gtk_table_set_row_spacings (GTK_TABLE (table), 4); gtk_table_set_col_spacings (GTK_TABLE (table), 2); gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); gtk_widget_show (table); /* The secltion settings - * 1) Type (option menu) * 2) Anti A (toggle) * 3) Feather (toggle) * 4) F radius (slider) * 5) Fill type (option menu) * 6) Opacity (slider) * 7) When to fill (toggle) * 8) Arc as segment/sector */ /* 1 */ menu = gimp_option_menu_new2 (FALSE, G_CALLBACK (select_menu_callback), (gpointer) SELECT_TYPE_MENU, 0, _("Add"), (gpointer) ADD, NULL, _("Subtract"), (gpointer) SUBTRACT, NULL, _("Replace"), (gpointer) REPLACE, NULL, _("Intersect"), (gpointer) INTERSECT, NULL, NULL); gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, _("Selection Type:"), 1.0, 0.5, menu, 1, FALSE); /* 2 */ toggle = gtk_check_button_new_with_label (_("Antialiasing")); gtk_table_attach (GTK_TABLE (table), toggle, 2, 4, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gimp_toggle_button_update), &selopt.antia); gtk_widget_show (toggle); /* 3 */ toggle = gtk_check_button_new_with_label (_("Feather")); gtk_table_attach (GTK_TABLE (table), toggle, 2, 4, 2, 3, GTK_FILL, GTK_FILL, 0, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gimp_toggle_button_update), &selopt.feather); gtk_widget_show (toggle); /* 4 */ scale_data = gtk_adjustment_new (selopt.feather_radius, 0.0, 100.0, 1.0, 1.0, 0.0); scale = gtk_hscale_new (GTK_ADJUSTMENT (scale_data)); gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP); gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED); gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed", GTK_SIGNAL_FUNC (gimp_double_adjustment_update), &selopt.feather_radius); gimp_table_attach_aligned (GTK_TABLE (table), 2, 3, _("Radius:"), 1.0, 1.0, scale, 1, FALSE); /* 5 */ menu = gimp_option_menu_new2 (FALSE, G_CALLBACK (select_menu_callback), (gpointer) SELECT_TYPE_MENU_FILL, 0, _("Pattern"), (gpointer) FILL_PATTERN, NULL, _("Foreground"), (gpointer) FILL_FOREGROUND, NULL, _("Background"), (gpointer) FILL_BACKGROUND, NULL, NULL); gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, _("Fill Type:"), 1.0, 0.5, menu, 1, FALSE); /* 6 */ scale_data = gtk_adjustment_new (selopt.fill_opacity, 0.0, 100.0, 1.0, 1.0, 0.0); scale = gtk_hscale_new (GTK_ADJUSTMENT (scale_data)); gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP); gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED); gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed", GTK_SIGNAL_FUNC (gimp_double_adjustment_update), &selopt.fill_opacity); gimp_table_attach_aligned (GTK_TABLE (table), 2, 1, _("Fill Opacity:"), 1.0, 1.0, scale, 1, FALSE); /* 7 */ menu = gimp_option_menu_new2 (FALSE, G_CALLBACK (select_menu_callback), (gpointer) SELECT_TYPE_MENU_WHEN, 0, _("Each Selection"), (gpointer) FILL_EACH, NULL, _("All Selections"), (gpointer) FILL_AFTER, NULL, NULL); gimp_table_attach_aligned (GTK_TABLE (table), 0, 2, _("Fill after:"), 1.0, 0.5, menu, 1, FALSE); /* 8 */ menu = gimp_option_menu_new2 (FALSE, G_CALLBACK (select_menu_callback), (gpointer) SELECT_ARCTYPE_MENU, 0, _("Segment"), (gpointer) ARC_SEGMENT, NULL, _("Sector"), (gpointer) ARC_SECTOR, NULL, NULL); gimp_table_attach_aligned (GTK_TABLE (table), 0, 3, _("Arc as:"), 1.0, 0.5, menu, 1, FALSE); return vbox; } static void gridtype_menu_callback (GtkWidget *widget, gpointer data) { gint mtype = (gint)data; if (mtype == GRID_TYPE_MENU) { #ifdef DEBUG printf ("Gridtype set to "); if (current_obj->opts.gridtype == RECT_GRID) printf ("RECT_GRID\n"); else if (current_obj->opts.gridtype == POLAR_GRID) printf ("POLAR_GRID\n"); else if (current_obj->opts.gridtype == ISO_GRID) printf ("ISO_GRID\n"); else printf ("NONE\n"); #endif /* DEBUG */ selvals.opts.gridtype = (GridType)gtk_object_get_user_data (GTK_OBJECT (widget)); } else { grid_gc_type = (gint) gtk_object_get_user_data (GTK_OBJECT (widget)); } draw_grid_clear (widget, 0); } static GtkWidget * options_page (void) { GtkWidget *table; GtkWidget *menu; GtkWidget *toggle; GtkWidget *button; GtkWidget *vbox; GtkObject *size_data; vbox = gtk_vbox_new (FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); table = gtk_table_new (6, 3, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_table_set_row_spacings (GTK_TABLE (table), 2); gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); gtk_widget_show (table); /* Put buttons in */ toggle = gtk_check_button_new_with_label (_("Show Image")); gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gimp_toggle_button_update), &selvals.showimage); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) toggle_show_image, (gpointer) 1); gtk_widget_show (toggle); button = gtk_button_new_with_label (_("Reload Image")); gtk_misc_set_padding (GTK_MISC (GTK_BIN (button)->child), 2, 0); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (reload_button_callback), NULL); gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, NULL, 0, 0, button, 1, TRUE); menu = gimp_option_menu_new2 (FALSE, G_CALLBACK (gridtype_menu_callback), (gpointer) GRID_TYPE_MENU, 0, _("Rectangle"), (gpointer) RECT_GRID, NULL, _("Polar"), (gpointer) POLAR_GRID, NULL, _("Isometric"), (gpointer) ISO_GRID, NULL, NULL); gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, _("Grid Type:"), 1.0, 0.5, menu, 1, TRUE); gfig_opt_widget.gridtypemenu = menu; menu = gimp_option_menu_new2 (FALSE, G_CALLBACK (gridtype_menu_callback), (gpointer) GRID_RENDER_MENU, 0, _("Normal"), (gpointer) GTK_STATE_NORMAL, NULL, _("Black"), (gpointer) GFIG_BLACK_GC, NULL, _("White"), (gpointer) GFIG_WHITE_GC, NULL, _("Grey"), (gpointer) GFIG_GREY_GC, NULL, _("Darker"), (gpointer) GTK_STATE_ACTIVE, NULL, _("Lighter"), (gpointer) GTK_STATE_PRELIGHT, NULL, _("Very Dark"), (gpointer) GTK_STATE_SELECTED, NULL, NULL); gimp_table_attach_aligned (GTK_TABLE (table), 0, 2, _("Grid Color:"), 1.0, 0.5, menu, 1, TRUE); size_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 3, _("Max Undo:"), 0, 50, selvals.maxundo, MIN_UNDO, MAX_UNDO, 1, 2, 0, TRUE, 0, 0, NULL, NULL); gtk_signal_connect (GTK_OBJECT (size_data), "value_changed", GTK_SIGNAL_FUNC (gimp_int_adjustment_update), &selvals.maxundo); toggle = gtk_check_button_new_with_label (_("Show Position")); gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 4, 5, GTK_FILL, GTK_FILL, 0, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gimp_toggle_button_update), &selvals.showpos); gtk_signal_connect_after (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) gfig_pos_enable, (gpointer) 1); gtk_widget_show (toggle); toggle = gtk_check_button_new_with_label (_("Hide Control Points")); gtk_table_attach (GTK_TABLE (table), toggle, 1, 3, 4, 5, GTK_FILL, GTK_FILL, 0, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gimp_toggle_button_update), &selvals.opts.showcontrol); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (toggle_show_image), (gpointer) 1); gtk_widget_show (toggle); gfig_opt_widget.showcontrol = toggle; toggle = gtk_check_button_new_with_label (_("Show Tooltips")); gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 5, 6, GTK_FILL, GTK_FILL, 0, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (toggle_tooltips), &selvals.showtooltips); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), selvals.showtooltips); gtk_widget_show (toggle); button = gtk_button_new_with_label (_("About")); gtk_misc_set_padding (GTK_MISC (GTK_BIN (button)->child), 8, 0); gtk_table_attach (GTK_TABLE (table), button, 1, 3, 5, 6, 0, 0, 0, 0); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (about_button_callback), NULL); gtk_widget_show (button); return vbox; } static GtkWidget * grid_frame (void) { GtkWidget *frame; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *table; GtkWidget *toggle; GtkObject *size_data; frame = gtk_frame_new (_("Grid")); vbox = gtk_vbox_new (FALSE, 2); gtk_container_set_border_width (GTK_CONTAINER (vbox), 2); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_widget_show (vbox); hbox = gtk_hbox_new (FALSE, 4); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); toggle = gtk_check_button_new_with_label (_("Snap to Grid")); gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gimp_toggle_button_update), &selvals.opts.snap2grid); gtk_widget_show (toggle); gfig_opt_widget.snap2grid = toggle; toggle = gtk_check_button_new_with_label (_("Display Grid")); gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gimp_toggle_button_update), &selvals.opts.drawgrid); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (draw_grid_clear), (gpointer) 1); gtk_widget_show (toggle); gfig_opt_widget.drawgrid = toggle; toggle = gtk_check_button_new_with_label (_("Lock on Grid")); gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gimp_toggle_button_update), &selvals.opts.lockongrid); gtk_widget_show (toggle); gfig_opt_widget.lockongrid = toggle; table = gtk_table_new (1, 3, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_table_set_row_spacings (GTK_TABLE (table), 2); gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); gtk_widget_show (table); size_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0, _("Grid Spacing:"), 0, 50, selvals.opts.gridspacing, MIN_GRID, MAX_GRID, 1, 10, 0, TRUE, 0, 0, NULL, NULL); gtk_signal_connect (GTK_OBJECT (size_data), "value_changed", GTK_SIGNAL_FUNC (gimp_int_adjustment_update), &selvals.opts.gridspacing); gtk_signal_connect (GTK_OBJECT (size_data), "value_changed", GTK_SIGNAL_FUNC (draw_grid_clear), (gpointer) 0); gfig_opt_widget.gridspacing = size_data; gtk_widget_show (frame); return frame; } static void clear_list_items (GtkList *list) { gtk_list_clear_items (list, 0, -1); } static void build_list_items (GtkWidget *list) { GList *tmp; GtkWidget *list_item; GtkWidget *list_pix; GFigObj *g; for (tmp = gfig_list; tmp; tmp = g_list_next (tmp)) { g = tmp->data; if (g->obj_status & GFIG_READONLY) list_pix = gimp_pixmap_new (mini_cross_xpm); else list_pix = gimp_pixmap_new (blank_xpm); list_item = gfig_list_item_new_with_label_and_pixmap (g, g->draw_name, list_pix); gtk_object_set_user_data (GTK_OBJECT (list_item), (gpointer) g); gtk_list_append_items (GTK_LIST (list), g_list_append (NULL, list_item)); gtk_signal_connect (GTK_OBJECT (list_item), "button_press_event", GTK_SIGNAL_FUNC (list_button_press), (gpointer) g); gtk_widget_show (list_item); } } static GtkWidget * add_objects_list (void) { GtkWidget *vbox; GtkWidget *table; GtkWidget *frame; GtkWidget *list_frame; GtkWidget *scrolled_win; GtkWidget *list; GtkWidget *button; frame = gtk_frame_new (_("Object")); gtk_widget_show (frame); table = gtk_table_new (4, 3, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 2); gtk_container_set_border_width (GTK_CONTAINER (table), 2); gtk_widget_show (table); delete_frame_to_freeze = list_frame = gtk_frame_new (NULL); gtk_widget_show (list_frame); scrolled_win = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add (GTK_CONTAINER (list_frame), scrolled_win); gtk_widget_show (scrolled_win); gfig_gtk_list = list = gtk_list_new (); /* gtk_list_set_selection_mode (GTK_LIST (list), GTK_SELECTION_MULTIPLE); */ gtk_list_set_selection_mode (GTK_LIST (list), GTK_SELECTION_BROWSE); gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_win), list); gtk_widget_show (list); /* Load saved objects */ gfig_list_load_all (gfig_path_list); /* Put list in */ build_list_items (list); /* Put buttons in */ button = gtk_button_new_with_label (_("Rescan")); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (rescan_button_callback), NULL); gimp_help_set_help_data (button, _("Select folder and rescan Gfig object " "collection"), NULL); gtk_table_attach (GTK_TABLE (table), button, 2, 3, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (button); button = gtk_button_new_with_label (_("Load")); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (load_button_callback), list); gimp_help_set_help_data (button, _("Load a single Gfig object collection"), NULL); gtk_table_attach (GTK_TABLE (table), button, 2, 3, 1, 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (button); button = gtk_button_new_with_label (_("New")); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (new_button_callback), "New gfig obj"); gimp_help_set_help_data (button, _("Create a new Gfig object collection " "for editing"), NULL); gtk_table_attach (GTK_TABLE (table), button, 2, 3, 2, 3, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (button); button = gtk_button_new_with_label (_("Delete")); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (gfig_delete_gfig_callback), (gpointer) list); gimp_help_set_help_data (button, _("Delete currently selected Gfig Object " "collection"), NULL); gtk_table_attach (GTK_TABLE (table), button, 2, 3, 3, 4, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (button); /* Attach the frame for the list Show the widgets */ gtk_table_attach (GTK_TABLE (table), list_frame, 1, 2, 0, 4, GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0); vbox = small_preview (list); gtk_table_attach (GTK_TABLE (table), vbox, 0, 1, 0, 4, 0, 0, 0, 0); gtk_container_add (GTK_CONTAINER (frame), table); return frame; } static gint x_pos_val; static gint y_pos_val; static gint pos_tag = -1; static void gfig_pos_enable (GtkWidget *widget, gpointer data) { gint enable = selvals.showpos; gtk_widget_set_sensitive (GTK_WIDGET (pos_label), enable); } static void gfig_pos_update_labels (gpointer data) { static gchar buf[256]; /*gtk_idle_remove (pos_tag);*/ pos_tag = -1; g_snprintf (buf, sizeof (buf), "%d, %d", x_pos_val, y_pos_val); gtk_label_set_text (GTK_LABEL (pos_label), buf); } static void gfig_pos_update (gint x, gint y) { gint update; if (x_pos_val != x || y_pos_val != y) update = 1; else update = 0; x_pos_val = x; y_pos_val = y; if (update && pos_tag == -1 && selvals.showpos) { /*pos_tag = gtk_idle_add ((GtkFunction)gfig_pos_update_labels, NULL);*/ gfig_pos_update_labels (NULL); } } #if 0 /* NOT USED */ static void gfig_obj_size_update (gint sz) { static gchar buf[256]; sprintf (buf, "%6d", sz); gtk_label_set_text (GTK_LABEL (obj_size_label), buf); } static GtkWidget * gfig_obj_size_label (void) { GtkWidget *label; GtkWidget *hbox; gchar buf[256]; hbox = gtk_hbox_new (TRUE, 6); /* Position labels */ label = gtk_label_new ("Size: "); gtk_widget_show (label); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); obj_size_label = gtk_label_new (""); gtk_misc_set_alignment (GTK_MISC (obj_size_label), 0.5, 0.5); gtk_widget_show (obj_size_label); gtk_box_pack_start (GTK_BOX (hbox), obj_size_label, FALSE, FALSE, 0); gtk_widget_show (hbox); sprintf (buf, "%6d", 0); gtk_label_set_text (GTK_LABEL (obj_size_label), buf); return (hbox); } #endif /* NOT USED */ static GtkWidget * gfig_pos_labels (void) { GtkWidget *label; GtkWidget *hbox; gchar buf[256]; hbox = gtk_hbox_new (FALSE, 6); gtk_widget_show (hbox); /* Position labels */ label = gtk_label_new (_("XY Position:")); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); pos_label = gtk_label_new (""); gtk_box_pack_start (GTK_BOX (hbox), pos_label, FALSE, FALSE, 0); gtk_widget_show (pos_label); g_snprintf (buf, sizeof (buf), "%d, %d", 0, 0); gtk_label_set_text (GTK_LABEL (pos_label), buf); return hbox; } static GtkWidget * make_pos_info (void) { GtkWidget *xframe; GtkWidget *hbox; GtkWidget *label; xframe = gtk_frame_new (_("Object Details")); hbox = gtk_hbox_new (TRUE, 6); gtk_container_set_border_width (GTK_CONTAINER (hbox), 2); gtk_container_add (GTK_CONTAINER (xframe), hbox); /* Add labels */ label = gfig_pos_labels (); gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); gfig_pos_enable (NULL, NULL); #if 0 label = gfig_obj_size_label (); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); #endif /* 0 */ gtk_widget_show (hbox); gtk_widget_show (xframe); return xframe; } static GtkWidget * make_status (void) { GtkWidget *xframe; GtkWidget *table; GtkWidget *label; xframe = gtk_frame_new (_("Collection Details")); gtk_frame_set_shadow_type (GTK_FRAME (xframe), GTK_SHADOW_ETCHED_IN); table = gtk_table_new (6, 6, FALSE); gtk_table_set_col_spacing (GTK_TABLE (table), 1, 6); gtk_container_set_border_width (GTK_CONTAINER (table), 2); label = gtk_label_new (_("Draw Name:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); label = gtk_label_new (_("Filename:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); status_label_dname = gtk_label_new (_("(none)")); gtk_misc_set_alignment (GTK_MISC (status_label_dname), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), status_label_dname, 2, 4, 0, 1, GTK_FILL | GTK_EXPAND, 0, 0, 0); gtk_widget_show (status_label_dname); status_label_fname = gtk_label_new (_("(none)")); gtk_misc_set_alignment (GTK_MISC (status_label_fname), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), status_label_fname, 2, 4, 1, 2, GTK_FILL | GTK_EXPAND, 0, 0, 0); gtk_widget_show (status_label_fname); gtk_container_add (GTK_CONTAINER (xframe), table); gtk_widget_show (table); gtk_widget_show (xframe); return xframe; } static GtkWidget * make_preview (void) { GtkWidget *xframe; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *table; GtkWidget *ruler; gfig_preview = gtk_preview_new (GTK_PREVIEW_COLOR); gtk_widget_set_events (GTK_WIDGET (gfig_preview), PREVIEW_MASK); gfig_preview_exp_id = gtk_signal_connect_after (GTK_OBJECT (gfig_preview), "expose_event", GTK_SIGNAL_FUNC (gfig_preview_expose), NULL); gtk_signal_connect (GTK_OBJECT (gfig_preview), "event", GTK_SIGNAL_FUNC (gfig_preview_events), NULL); gtk_preview_size (GTK_PREVIEW (gfig_preview), preview_width, preview_height); xframe = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (xframe), GTK_SHADOW_IN); table = gtk_table_new (3, 3, FALSE); gtk_table_attach (GTK_TABLE (table), gfig_preview, 1, 2, 1, 2, GTK_FILL , GTK_FILL , 0, 0); gtk_container_add (GTK_CONTAINER (xframe), table); ruler = gtk_hruler_new (); gtk_ruler_set_range (GTK_RULER (ruler), 0, preview_width, 0, PREVIEW_SIZE); gtk_signal_connect_object (GTK_OBJECT (gfig_preview), "motion_notify_event", (GtkSignalFunc) GTK_WIDGET_CLASS (G_OBJECT_GET_CLASS (ruler))->motion_notify_event, GTK_OBJECT (ruler)); gtk_table_attach (GTK_TABLE (table), ruler, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ruler); ruler = gtk_vruler_new (); gtk_ruler_set_range (GTK_RULER (ruler), 0, preview_height, 0, PREVIEW_SIZE); gtk_signal_connect_object (GTK_OBJECT (gfig_preview), "motion_notify_event", (GtkSignalFunc) GTK_WIDGET_CLASS (G_OBJECT_GET_CLASS (ruler))->motion_notify_event, GTK_OBJECT (ruler)); gtk_table_attach (GTK_TABLE (table), ruler, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ruler); gtk_widget_show (xframe); gtk_widget_show (table); vbox = gtk_vbox_new (FALSE, 0); hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), xframe, FALSE, FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (vbox), 2); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); xframe = make_pos_info (); gtk_box_pack_start (GTK_BOX (vbox), xframe, TRUE, TRUE, 0); xframe = make_status (); gtk_box_pack_start (GTK_BOX (vbox), xframe, TRUE, TRUE, 0); gtk_widget_show (vbox); gtk_widget_show (hbox); return vbox; } #if 0 scatch () { pause (); } #endif /* 0 */ static void gfig_grid_colours (GtkWidget *widget, GdkColormap *cmap) { GdkGCValues values; GdkColor new_col1; GdkColor new_col2; guchar stipple[8] = { 0xAA, /* ####---- */ 0x55, /* ###----# */ 0xAA, /* ##----## */ 0x55, /* #----### */ 0xAA, /* ----#### */ 0x55, /* ---####- */ 0xAA, /* --####-- */ 0x55, /* -####--- */ }; gdk_color_parse ("gray50", &new_col1); gdk_colormap_alloc_color (xxx, &new_col1, FALSE, TRUE); gdk_color_parse ("gray80", &new_col2); gdk_colormap_alloc_color (xxx, &new_col2, FALSE, TRUE); values.background.pixel = new_col1.pixel; values.foreground.pixel = new_col2.pixel; values.fill = GDK_OPAQUE_STIPPLED; values.stipple = gdk_bitmap_create_from_data (widget->window, (gchar *) stipple, 4, 4); grid_hightlight_drawgc = gdk_gc_new_with_values (widget->window, &values, GDK_GC_FOREGROUND | GDK_GC_BACKGROUND | GDK_GC_FILL | GDK_GC_STIPPLE); } static gint gfig_dialog (void) { GtkWidget *main_hbox; GtkWidget *frame; GtkWidget *vbox; GtkWidget *xframe; GtkWidget *oframe; GtkWidget *notebook; GtkWidget *page; GtkWidget *top_level_dlg; gimp_ui_init ("gfig", TRUE); yyy = gdk_rgb_get_visual (); xxx = gdk_rgb_get_colormap (); plug_in_parse_gfig_path (); /*cache_preview (); Get the preview image and store it also set has_alpha */ img_width = gimp_drawable_width (gfig_select_drawable->drawable_id); img_height = gimp_drawable_height (gfig_select_drawable->drawable_id); /* Start buildng the dialog up */ top_level_dlg = gimp_dialog_new (_("GFig"), "gfig", gimp_standard_help_func, "filters/gfig.html", GTK_WIN_POS_MOUSE, FALSE, FALSE, FALSE, GTK_STOCK_CANCEL, gtk_widget_destroy, NULL, 1, NULL, FALSE, TRUE, GTK_STOCK_UNDO, gfig_undo_callback, NULL, NULL, &undo_widget, FALSE, FALSE, GTK_STOCK_CLEAR, gfig_clear_callback, NULL, NULL, NULL, FALSE, FALSE, GTK_STOCK_SAVE, save_button_callback, NULL, NULL, &save_button, FALSE, FALSE, _("Paint"), gfig_paint_callback, NULL, NULL, NULL, FALSE, FALSE, _("Done"), gfig_ok_callback, NULL, NULL, NULL, TRUE, FALSE, NULL); gtk_signal_connect (GTK_OBJECT (top_level_dlg), "destroy", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); /* Tooltips bis */ gimp_help_init (); main_hbox = gtk_hbox_new (FALSE, 4); gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 4); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (top_level_dlg)->vbox), main_hbox, TRUE, TRUE, 0); /* Add buttons beside the preview frame */ xframe = draw_buttons (top_level_dlg); gtk_box_pack_start (GTK_BOX (main_hbox), xframe, FALSE, FALSE, 0); /*gtk_widget_show (xframe);*/ /* Start building the frame for the preview area */ frame = gtk_frame_new (_("Preview")); gtk_box_pack_start (GTK_BOX (main_hbox), frame, FALSE, FALSE, 0); gtk_widget_show (frame); /* Preview itself */ xframe = make_preview (); gtk_container_add (GTK_CONTAINER (frame), xframe); /* gtk_widget_show (xframe); */ gtk_widget_show (gfig_preview); frame = gtk_frame_new (_("Settings")); gtk_box_pack_start (GTK_BOX (main_hbox), frame, FALSE, FALSE, 0); gtk_widget_show (frame); vbox = gtk_vbox_new (FALSE, 2); gtk_container_set_border_width (GTK_CONTAINER (vbox), 2); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_widget_show (vbox); /* listbox + entry */ oframe = add_objects_list (); gtk_box_pack_start (GTK_BOX (vbox), oframe, FALSE, FALSE, 0); /* Grid entry */ xframe = grid_frame (); gtk_box_pack_start (GTK_BOX (vbox), xframe, FALSE, FALSE, 0); /* The notebook */ notebook = gtk_notebook_new (); gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP); gtk_box_pack_start (GTK_BOX (vbox), notebook, FALSE, FALSE, 0); gtk_widget_show (notebook); page = paint_page (); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, gtk_label_new (_("Paint"))); gtk_widget_show (page); brush_page_widget = brush_page (); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), brush_page_widget, gtk_label_new (_("Brush"))); gtk_widget_show (brush_page_widget); /* Sometime maybe allow all objects to be done by selections - this * would adjust the selection options. */ select_page_widget = select_page (); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), select_page_widget, gtk_label_new (_("Select"))); gtk_widget_set_sensitive (select_page_widget, FALSE); page = options_page (); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, gtk_label_new (_("Options"))); gtk_widget_show (page); gtk_widget_show (main_hbox); gtk_widget_show (top_level_dlg); dialog_update_preview (); gfig_new_gc (); /* Need this for drawing */ gfig_update_stat_labels (); gfig_grid_colours (gfig_preview, xxx); /* Popup for list area */ gfig_op_menu_create (top_level_dlg); /* signal (11, scatch); For debugging */ gtk_main (); gimp_image_delete (brush_image_ID); brush_image_ID = -1; gimp_help_free (); gdk_flush (); return gfig_run; } static void gfig_really_ok_callback (GtkWidget *widget, gboolean doit, gpointer data) { if (doit) { gfig_run = TRUE; gtk_widget_destroy (GTK_WIDGET (data)); } } static void gfig_ok_callback (GtkWidget *widget, gpointer data) { /* Check if outstanding saves */ GList *list; GFigObj *gfig; gint count = 0; for (list = gfig_list; list; list = g_list_next (list)) { gfig = (GFigObj *) list->data; if (gfig->obj_status & GFIG_MODIFIED) count++; } if (count) { GtkWidget *dialog; gchar *message; message = g_strdup_printf (_("%d unsaved Gfig objects.\nContinue with exiting?"), count); dialog = gimp_query_boolean_box (_("Warning"), gimp_standard_help_func, "filters/gfig.html", GTK_STOCK_DIALOG_WARNING, message, GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL, NULL, gfig_really_ok_callback, data); g_free (message); gtk_widget_show (dialog); } else { gfig_run = TRUE; gtk_widget_destroy (GTK_WIDGET (data)); } } /* Update the bits we put on the screen */ static void update_draw_area (GtkWidget *widget, GdkEvent *event) { if (!GTK_WIDGET_DRAWABLE (widget)) return; gtk_signal_handler_block (GTK_OBJECT (widget), gfig_preview_exp_id); gtk_widget_draw (widget, NULL); gtk_signal_handler_unblock (GTK_OBJECT (widget), gfig_preview_exp_id); draw_grid (widget, 0); draw_objects (current_obj->obj_list, TRUE); } static gint gfig_preview_expose (GtkWidget *widget, GdkEvent *event) { GdkCursor *preview_cursor; static gint changed_cursor = 0; if (!changed_cursor && gfig_preview->window) { changed_cursor = 1; preview_cursor = gdk_cursor_new (GDK_CROSSHAIR); gdk_window_set_cursor (gfig_preview->window, preview_cursor); } update_draw_area (widget, event); return FALSE; } static gint pic_preview_expose (GtkWidget *widget, GdkEvent *event) { if (pic_obj) { drawing_pic = TRUE; draw_objects (pic_obj->obj_list, FALSE); drawing_pic = FALSE; } return FALSE; } static gint adjust_pic_coords (gint coord, gint ratio) { /*return ((SMALL_PREVIEW_SZ * coord)/PREVIEW_SIZE);*/ static gint pratio = -1; if (pratio == -1) { pratio = MAX (preview_width, preview_height); } return (SMALL_PREVIEW_SZ * coord) / pratio; } static gint gfig_preview_events (GtkWidget *widget, GdkEvent *event) { GdkEventButton *bevent; GdkEventMotion *mevent; GdkPoint point; static gint tmp_show_single = 0; switch (event->type) { case GDK_EXPOSE: break; case GDK_BUTTON_PRESS: bevent = (GdkEventButton *) event; point.x = bevent->x; point.y = bevent->y; g_assert (need_to_scale == 0); /* If not out of step some how */ /* Start drawing of object */ if (selvals.otype >= MOVE_OBJ) { if (!selvals.scaletoimage) { point.x = gfig_invscale_x (point.x); point.y = gfig_invscale_y (point.y); } object_operation_start (&point, bevent->state & GDK_SHIFT_MASK); /* If constraining save start pnt */ if (selvals.opts.snap2grid) { /* Save point to constained point ... if button 3 down */ if (bevent->button == 3) { find_grid_pos (&point, &point, FALSE); } } } else { if (selvals.opts.snap2grid) { if (bevent->button == 3) { find_grid_pos (&point, &point, FALSE); } else { find_grid_pos (&point, &point, FALSE); } } object_start (&point, bevent->state & GDK_SHIFT_MASK); } break; case GDK_BUTTON_RELEASE: bevent = (GdkEventButton *) event; point.x = bevent->x; point.y = bevent->y; if (selvals.opts.snap2grid) find_grid_pos (&point, &point, bevent->button == 3); /* Still got shift down ?*/ if (selvals.otype >= MOVE_OBJ) { if (!selvals.scaletoimage) { point.x = gfig_invscale_x (point.x); point.y = gfig_invscale_y (point.y); } object_operation_end (&point, bevent->state & GDK_SHIFT_MASK); } else { if (obj_creating) { object_end (&point, bevent->state & GDK_SHIFT_MASK); } else break; } /* make small preview reflect changes ?*/ list_button_update (current_obj); break; case GDK_MOTION_NOTIFY: mevent = (GdkEventMotion *) event; point.x = mevent->x; point.y = mevent->y; if (selvals.opts.snap2grid) find_grid_pos (&point, &point, mevent->state & GDK_BUTTON3_MASK); if (selvals.otype >= MOVE_OBJ) { /* Moving objects around */ if (!selvals.scaletoimage) { point.x = gfig_invscale_x (point.x); point.y = gfig_invscale_y (point.y); } object_operation (&point, mevent->state & GDK_SHIFT_MASK); gfig_pos_update (point.x, point.y); return FALSE; } if (obj_creating) { object_update (&point); } gfig_pos_update (point.x, point.y); break; case GDK_KEY_PRESS: if ((tmp_show_single = obj_show_single) != -1) { obj_show_single = -1; draw_grid_clear (NULL, NULL); /*Args not used */ } break; case GDK_KEY_RELEASE: if (tmp_show_single != -1) { obj_show_single = tmp_show_single; draw_grid_clear (NULL, NULL); /*Args not used */ } break; default: break; } return FALSE; } /* * The edit gfig name attributes dialog * Modified from Gimp source - layer edit. */ typedef struct _GfigListOptions { GtkWidget *query_box; GtkWidget *name_entry; GtkWidget *list_entry; GFigObj *obj; gint created; } GfigListOptions; static GtkWidget * gfig_list_add (GFigObj *obj) { GList *list; gint pos; GtkWidget *list_item; GtkWidget *list_pix; list_pix = gimp_pixmap_new (Floppy6_xpm); list_item = gfig_list_item_new_with_label_and_pixmap (obj, obj->draw_name, list_pix); gtk_object_set_user_data (GTK_OBJECT (list_item), (gpointer) obj); pos = gfig_list_insert (obj); list = g_list_append (NULL, list_item); gtk_list_insert_items (GTK_LIST (gfig_gtk_list), list, pos); gtk_widget_show (list_item); gtk_list_select_item (GTK_LIST (gfig_gtk_list), pos); gtk_signal_connect (GTK_OBJECT (list_item), "button_press_event", GTK_SIGNAL_FUNC (list_button_press), (gpointer) obj); return list_item; } static void gfig_list_ok_callback (GtkWidget *widget, gpointer data) { GfigListOptions *options; GtkWidget *list; gint pos; options = (GfigListOptions *) data; list = options->list_entry; /* Set the new layer name */ #ifdef DEBUG printf ("Found obj %s\n", options->obj->draw_name); #endif /* DEBUG */ if (options->obj->draw_name) { g_free (options->obj->draw_name); } options->obj->draw_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (options->name_entry))); #ifdef DEBUG printf ("NEW name %s\n", options->obj->draw_name); #endif /* DEBUG */ /* Need to reorder the list */ /* gtk_label_set_text (GTK_LABEL (options->layer_widget->label), layer->name);*/ pos = gtk_list_child_position (GTK_LIST (gfig_gtk_list), list); #ifdef DEBUG printf ("pos = %d\n", pos); #endif /* DEBUG */ gtk_list_clear_items (GTK_LIST (gfig_gtk_list), pos, pos + 1); /* remove/Add again */ gfig_list = g_list_remove (gfig_list, options->obj); gfig_list_add (options->obj); options->obj->obj_status |= GFIG_MODIFIED; gtk_widget_destroy (options->query_box); g_free (options); gfig_update_stat_labels (); } static void gfig_list_cancel_callback (GtkWidget *widget, gpointer data) { GfigListOptions *options; options = (GfigListOptions *) data; if (options->created) { /* We are creating an entry so if cancelled * must del the list item as well */ gfig_do_delete_gfig_callback (widget, TRUE, gfig_gtk_list); } gtk_widget_destroy (options->query_box); g_free (options); } static void gfig_dialog_edit_list (GtkWidget *lwidget, GFigObj *obj, gint created) { GfigListOptions *options; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *label; /* the new options structure */ options = g_new (GfigListOptions, 1); options->list_entry = lwidget; options->obj = obj; options->created = created; /* the dialog */ options->query_box = gimp_dialog_new (_("Enter Gfig Entry Name"), "gfig", gimp_standard_help_func, "filters/gfig.html", GTK_WIN_POS_MOUSE, FALSE, TRUE, FALSE, GTK_STOCK_CANCEL, gfig_list_cancel_callback, options, NULL, NULL, FALSE, TRUE, GTK_STOCK_OK, gfig_list_ok_callback, options, NULL, NULL, TRUE, FALSE, NULL); /* the main vbox */ vbox = gtk_vbox_new (FALSE, 2); gtk_container_set_border_width (GTK_CONTAINER (vbox), 6); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (options->query_box)->vbox), vbox, FALSE, FALSE, 0); gtk_widget_show (vbox); /* the name entry hbox, label and entry */ hbox = gtk_hbox_new (FALSE, 4); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); label = gtk_label_new (_("Gfig Object Name:")); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); options->name_entry = gtk_entry_new (); gtk_box_pack_start (GTK_BOX (hbox), options->name_entry, TRUE, TRUE, 0); gtk_entry_set_text (GTK_ENTRY (options->name_entry), obj->draw_name); gtk_widget_show (options->name_entry); gtk_widget_show (options->query_box); } static void gfig_rescan_ok_callback (GtkWidget *widget, gpointer data) { GtkWidget *patheditor; gchar *raw_path; gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE); gimp_path_free (gfig_path_list); gfig_path_list = NULL; patheditor = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (data), "patheditor")); raw_path = gimp_path_editor_get_path (GIMP_PATH_EDITOR (patheditor)); gfig_path_list = gimp_path_parse (raw_path, 16, FALSE, NULL); g_free (raw_path); if (gfig_path_list) { clear_list_items (GTK_LIST (gfig_gtk_list)); gfig_list_load_all (gfig_path_list); build_list_items (gfig_gtk_list); list_button_update (current_obj); } gtk_widget_destroy (GTK_WIDGET (data)); } static void gfig_rescan_list (void) { static GtkWidget *dlg = NULL; GtkWidget *patheditor; gchar *path; if (dlg) { gdk_window_raise (dlg->window); return; } /* the dialog */ dlg = gimp_dialog_new (_("Rescan for Gfig Objects"), "gfig", gimp_standard_help_func, "filters/gfig.html", GTK_WIN_POS_MOUSE, FALSE, TRUE, FALSE, GTK_STOCK_CANCEL, gtk_widget_destroy, NULL, 1, NULL, FALSE, TRUE, GTK_STOCK_OK, gfig_rescan_ok_callback, NULL, NULL, NULL, TRUE, FALSE, NULL); gtk_signal_connect (GTK_OBJECT (dlg), "destroy", GTK_SIGNAL_FUNC (gtk_widget_destroyed), &dlg); path = gimp_path_to_str (gfig_path_list); patheditor = gimp_path_editor_new (_("Add Gfig Path"), path); gtk_container_set_border_width (GTK_CONTAINER (patheditor), 6); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), patheditor, TRUE, TRUE, 0); gtk_widget_show (patheditor); g_free (path); gtk_object_set_data (GTK_OBJECT (dlg), "patheditor", patheditor); gtk_widget_show (dlg); } static void list_button_update (GFigObj *obj) { g_return_if_fail (obj != NULL); pic_obj = (GFigObj *) obj; gtk_widget_draw (pic_preview, NULL); drawing_pic = TRUE; draw_objects (pic_obj->obj_list, FALSE); drawing_pic = FALSE; } static void gfig_load_file_selection_ok (GtkWidget *widget, GtkFileSelection *fs, gpointer data) { const gchar *filename; GFigObj *gfig; GFigObj *current_saved; filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)); #ifdef DEBUG printf ("Loading file '%s'\n", filename); #endif /* DEBUG */ if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) { /* Hack - current object MUST be NULL to prevent setup_undo () * from kicking in. */ current_saved = current_obj; current_obj = NULL; gfig = gfig_load (filename, filename); current_obj = current_saved; if (gfig) { /* Read only ?*/ if (access (filename, W_OK)) gfig->obj_status |= GFIG_READONLY; gfig_list_add (gfig); new_obj_2edit (gfig); } } gtk_widget_destroy (GTK_WIDGET (fs)); } static void load_button_callback (GtkWidget *widget, gpointer data) { static GtkWidget *window = NULL; /* Load a single object */ window = gtk_file_selection_new (_("Load Gfig obj")); gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_widget_destroyed), &window); gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (window)->ok_button), "clicked", GTK_SIGNAL_FUNC (gfig_load_file_selection_ok), (gpointer) window); gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (window)->cancel_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (window)); gtk_widget_show (window); } static void paint_layer_copy (gchar *new_name) { gint32 old_drawable = gfig_drawable; if ((gfig_drawable = gimp_layer_copy (gfig_drawable)) < 0) { g_warning (_("Error in copy layer for onlayers")); gfig_drawable = old_drawable; return; } gimp_layer_set_name (gfig_drawable, new_name); gimp_image_add_layer (gfig_image, gfig_drawable, -1); } static void paint_layer_new (gchar *new_name) { gint32 layer_id; gint32 fill_type; int isgrey = 0; switch (gimp_drawable_type (gfig_select_drawable->drawable_id)) { case GIMP_GRAYA_IMAGE: case GIMP_GRAY_IMAGE: isgrey = 2; default: break; } if ((layer_id = gimp_layer_new (gfig_image, new_name, img_width, img_height, 1 + isgrey, /* RGBA or GRAYA type */ 100.0, /* opacity */ 0 /* mode */)) < 0) g_warning (_("Error in creating layer")); else { gimp_image_add_layer (gfig_image, layer_id, -1); gimp_drawable_fill (layer_id, 1); } gfig_drawable = layer_id; switch (selvals.onlayerbg) { case LAYER_TRANS_BG: fill_type = 3; break; case LAYER_BG_BG: fill_type = 1; break; case LAYER_FG_BG: fill_type = 0; break; case LAYER_WHITE_BG: fill_type = 2; break; case LAYER_COPY_BG: default: fill_type = 1; g_warning ("Paint layer new internal error %d\n", selvals.onlayerbg); break; } /* Have to clear layer out since creating transparent layer * seems to leave rubbish in it. */ gimp_drawable_fill (layer_id, fill_type); } static void paint_layer_fill () { gimp_bucket_fill (gfig_drawable, selopt.fill_type, /* Fill mode */ GIMP_NORMAL_MODE, selopt.fill_opacity, /* Fill opacity */ 0.0, /* threshold - ignored */ FALSE, /* Sample merged - ignored */ 0.0, /* x - ignored */ 0.0); /* y - ignored */ } static void gfig_paint_callback (GtkWidget *widget, gpointer data) { DAllObjs * objs; gint layer_count = 0; gchar buf[128]; gint count; gint ccount = 0; BrushDesc *bdesc; objs = current_obj->obj_list; count = gfig_obj_counts (objs); #if 0 gtk_progress_bar_update (GTK_PROGRESS_BAR (progress_widget), (gfloat) 0.0); #endif /* 0 */ /* Set the brush up */ bdesc = gtk_object_get_user_data (GTK_OBJECT (brush_page_pw)); if (bdesc) mygimp_brush_set (bdesc->bname); while (objs) { if (ccount == obj_show_single || obj_show_single == -1) { sprintf (buf, _("Gfig Layer %d"), layer_count++); if (selvals.painttype != PAINT_SELECTION_TYPE) { switch (selvals.onlayers) { case SINGLE_LAYER: if (layer_count == 1) { if (selvals.onlayerbg == LAYER_COPY_BG) paint_layer_copy (buf); else paint_layer_new (buf); } break; case MULTI_LAYER: if (selvals.onlayerbg == LAYER_COPY_BG) paint_layer_copy (buf); else paint_layer_new (buf); break; case ORIGINAL_LAYER: /* Just use the given layer */ break; default: g_warning ("Error in onlayers val %d", selvals.onlayers); break; } } objs->obj->paintfunc (objs->obj); /* Fill layer if required */ if (selvals.painttype == PAINT_SELECTION_FILL_TYPE && selopt.fill_when == FILL_EACH) paint_layer_fill (); } objs = objs->next; ccount++; #if 0 gtk_progress_bar_update (GTK_PROGRESS_BAR (progress_widget), (gfloat)ccount/(gfloat)count); gtk_widget_draw (GTK_WIDGET (progress_widget), NULL); #endif /* 0 */ } /* Fill layer if required */ if (selvals.painttype == PAINT_SELECTION_FILL_TYPE && selopt.fill_when == FILL_AFTER) paint_layer_fill (); gimp_displays_flush (); } static void reload_button_callback (GtkWidget *widget, gpointer data) { refill_cache (); draw_grid_clear (widget, data); } static void about_button_callback (GtkWidget *widget, gpointer data) { GtkWidget *window; GtkWidget *label; GtkWidget *hbox; GtkWidget *vbox; GtkWidget *pm; window = gimp_dialog_new (_("About GFig"), "gfig", gimp_standard_help_func, "filters/gfig.html", GTK_WIN_POS_MOUSE, FALSE, FALSE, FALSE, GTK_STOCK_OK, gtk_widget_destroy, NULL, 1, NULL, TRUE, TRUE, NULL); /* Bits and bobs */ pm = gimp_pixmap_new (rulers_comp_xpm); gtk_widget_show (pm); hbox = gtk_hbox_new (FALSE, 1); gtk_widget_show (hbox); vbox = gtk_vbox_new (FALSE, 1); gtk_widget_show (vbox); gtk_box_pack_start (GTK_BOX (hbox), pm, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), hbox, TRUE, TRUE, 0); label = gtk_label_new (_("Gfig - GIMP plug-in")); gtk_misc_set_padding (GTK_MISC (label), 2, 2); gtk_widget_show (label); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); label = gtk_label_new (_("Release 1.3")); gtk_misc_set_padding (GTK_MISC (label), 2, 2); gtk_widget_show (label); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); label = gtk_label_new ("Andy Thomas"); gtk_misc_set_padding (GTK_MISC (label), 2, 2); gtk_widget_show (label); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); label = gtk_label_new (_("Email alt@picnic.demon.co.uk")); gtk_misc_set_padding (GTK_MISC (label), 2, 2); gtk_widget_show (label); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); label = gtk_label_new ("http://www.picnic.demon.co.uk/"); gtk_misc_set_padding (GTK_MISC (label), 2, 2); gtk_widget_show (label); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); label = gtk_label_new (_("Isometric grid By Rob Saunders")); gtk_misc_set_padding (GTK_MISC (label), 2, 2); gtk_widget_show (label); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); gtk_widget_show (window); } static void save_button_callback (GtkWidget *widget, gpointer data) { gfig_save (); /* Save current object */ } static void rescan_button_callback (GtkWidget *widget, gpointer data) { gfig_rescan_list (); } static GtkWidget * new_gfig_obj (gchar *name) { GFigObj *gfig; GtkWidget *new_list_item; /* Create a new entry */ gfig = gfig_new (); if (!name) name = _("New gfig obj"); gfig->draw_name = g_strdup (name); /* Leave options as before */ pic_obj = current_obj = gfig; new_list_item = gfig_list_add (gfig); tmp_bezier = obj_creating = tmp_line = NULL; /* Redraw areas */ update_draw_area (gfig_preview, NULL); list_button_update (gfig); return new_list_item; } static void new_button_callback (GtkWidget *widget, gpointer data) { GtkWidget *new_list_item; new_list_item = new_gfig_obj ((gchar*) data); gfig_dialog_edit_list (new_list_item, current_obj, TRUE); } static GtkWidget *delete_dialog = NULL; static void gfig_do_delete_gfig_callback (GtkWidget *widget, gboolean delete, gpointer data) { gint pos; GList *sellist; GFigObj *sel_obj; GtkWidget *list = (GtkWidget *) data; if (!delete) { gtk_widget_set_sensitive (delete_frame_to_freeze, TRUE); return; } #ifdef DEBUG printf ("Delete button pressed\n"); #endif /* DEBUG */ /* Must update which object we are editing */ /* Get the list and which item is selected */ /* Only allow single selections */ sellist = GTK_LIST (list)->selection; sel_obj = (GFigObj *) gtk_object_get_user_data (GTK_OBJECT (sellist->data)); pos = gtk_list_child_position (GTK_LIST (gfig_gtk_list), sellist->data); #ifdef DEBUG printf ("delete pos = %d\n", pos); #endif /* DEBUG */ /* Delete the current item + asssociated file */ gtk_list_clear_items (GTK_LIST (gfig_gtk_list), pos, pos + 1); /* Shadow copy for ordering info */ gfig_list = g_list_remove (gfig_list, sel_obj); if (sel_obj == current_obj) { clear_undo (); } /* Free current obj */ gfig_free_everything (sel_obj); /* Select previous one */ pos--; if (pos < 0) { if (g_list_length (gfig_list) == 0) { /* Warning - we have a problem here * since we are not really "creating an entry" * why call gfig_new? */ new_button_callback (NULL, NULL); } pos = 0; } gtk_widget_set_sensitive (delete_frame_to_freeze, TRUE); gtk_list_select_item (GTK_LIST (gfig_gtk_list), pos); current_obj = g_list_nth (gfig_list, pos)->data; update_draw_area (gfig_preview, NULL); list_button_update (current_obj); gfig_update_stat_labels (); } static void gfig_delete_gfig_callback (GtkWidget *widget, gpointer data) { gchar *str; GtkWidget *list = (GtkWidget *) data; GList * sellist; GFigObj * sel_obj; sellist = GTK_LIST (list)->selection; sel_obj = (GFigObj *) gtk_object_get_user_data (GTK_OBJECT (sellist->data)); if (delete_dialog) return; str = g_strdup_printf (_("Are you sure you want to delete\n" "\"%s\" from the list and from disk?"), sel_obj->draw_name); delete_dialog = gimp_query_boolean_box (_("Delete Gfig Drawing"), gimp_standard_help_func, "filters/gfig.html", FALSE, str, GTK_STOCK_DELETE, GTK_STOCK_CANCEL, NULL, NULL, gfig_do_delete_gfig_callback, data); g_free (str); gtk_signal_connect (GTK_OBJECT (delete_dialog), "destroy", GTK_SIGNAL_FUNC (gtk_widget_destroyed), &delete_dialog); gtk_widget_set_sensitive (GTK_WIDGET (delete_frame_to_freeze), FALSE); gtk_widget_show (delete_dialog); } static void gfig_update_stat_labels (void) { gchar str[45]; if (current_obj->draw_name) sprintf (str, "%.34s", current_obj->draw_name); else sprintf (str,_("")); gtk_label_set_text (GTK_LABEL (status_label_dname), str); if (current_obj->filename) { gint slen; gchar *hm = (gchar *) g_get_home_dir (); gchar *dfn = g_strdup (current_obj->filename); #ifdef __EMX__ if (hm) hm = _fnslashify (hm); #endif #ifndef __EMX__ if (hm != NULL && !strncmp (dfn, hm, strlen (hm)-1)) #else if (hm != NULL && !strnicmp (dfn, hm, strlen (hm)-1)) #endif { strcpy (dfn, "~"); strcat (dfn, &dfn[strlen (hm)]); } if ((slen = strlen (dfn)) > 40) { strncpy (str, dfn, 19); str[19] = '\0'; strcat (str, "..."); strncat (str, &dfn[slen - 21], 19); str[40] ='\0'; } else sprintf (str, "%.40s", dfn); g_free (dfn); #ifdef __EMX__ g_free (hm); #endif } else sprintf (str,_("")); gtk_label_set_text (GTK_LABEL (status_label_fname), str); } static void new_obj_2edit (GFigObj *obj) { GFigObj *old_current = current_obj; /* Clear undo levels */ /* redraw the preview */ /* Set up options as define in the selected object */ clear_undo (); /* Point at this one */ current_obj = obj; /* Show all objects to start with */ obj_show_single = -1; /* Change options */ update_options (old_current); /* If have old object and NOT scaleing currently then force * back to saved coord type. */ gfig_update_stat_labels (); /* redraw with new */ update_draw_area (gfig_preview, NULL); /* And preview */ list_button_update (current_obj); if (obj->obj_status & GFIG_READONLY) { g_message (_("Editing read-only object - " "you will not be able to save it")); gtk_widget_set_sensitive (save_button, FALSE); } else { gtk_widget_set_sensitive (save_button, TRUE); } } static void edit_button_callback (GtkWidget *widget, gpointer data) { GList *sellist; GFigObj *sel_obj; GtkWidget *list = (GtkWidget *) data; #ifdef DEBUG printf ("Edit button pressed\n"); #endif /* DEBUG */ /* Must update which object we are editing */ /* Get the list and which item is selected */ /* Only allow single selections */ sellist = GTK_LIST (list)->selection; sel_obj = (GFigObj *) gtk_object_get_user_data (GTK_OBJECT (sellist->data)); if (sel_obj) new_obj_2edit (sel_obj); else g_warning ("Internal error - list item has null object!"); } static void merge_button_callback (GtkWidget *widget, gpointer data) { GList *sellist; GFigObj *sel_obj; DAllObjs *obj_copies; GtkWidget *list = (GtkWidget *) data; #ifdef DEBUG printf ("Merge button pressed\n"); #endif /* DEBUG */ /* Must update which object we are editing */ /* Get the list and which item is selected */ /* Only allow single selections */ sellist = GTK_LIST (list)->selection; sel_obj = (GFigObj *) gtk_object_get_user_data (GTK_OBJECT (sellist->data)); if (sel_obj && sel_obj->obj_list && sel_obj != current_obj) { /* Copy list tag onto current & redraw */ obj_copies = copy_all_objs (sel_obj->obj_list); prepend_to_all_obj (current_obj, obj_copies); /* redraw all */ update_draw_area (gfig_preview, NULL); /* And preview */ list_button_update (current_obj); } } static void gfig_save_menu_callback (GtkWidget *widget, gpointer data) { GFigObj * real_current = current_obj; /* Fiddle the current object and save it */ /* What happens if we get a redraw here ? */ current_obj = gfig_obj_for_menu; gfig_save (); /* Save current object */ current_obj = real_current; } static void gfig_edit_menu_callback (GtkWidget *widget, gpointer data) { new_obj_2edit (gfig_obj_for_menu); } static void gfig_rename_menu_callback (GtkWidget *widget, gpointer data) { create_file_selection (gfig_obj_for_menu, gfig_obj_for_menu->filename); } static void gfig_copy_menu_callback (GtkWidget *widget, gpointer data) { /* Create new entry with name + copy at end & copy object into it */ gchar *new_name = g_strdup_printf (_("%s copy"), gfig_obj_for_menu->draw_name); new_gfig_obj (new_name); g_free (new_name); /* Copy objs across */ current_obj->obj_list = copy_all_objs (gfig_obj_for_menu->obj_list); current_obj->opts = gfig_obj_for_menu->opts; /* Structure copy */ /* redraw all */ update_draw_area (gfig_preview, NULL); /* And preview */ list_button_update (current_obj); } static void gfig_op_menu_create (GtkWidget *window) { GtkWidget *menu_item; #if 0 GtkAcceleratorTable *accelerator_table; #endif /* 0 */ gfig_op_menu = gtk_menu_new (); #if 0 accelerator_table = gtk_accelerator_table_new (); gtk_menu_set_accelerator_table (GTK_MENU (gfig_op_menu), accelerator_table); gtk_window_add_accelerator_table (GTK_WINDOW (window), accelerator_table); #endif /* 0 */ save_menu_item = menu_item = gtk_menu_item_new_with_label (_("Save")); gtk_menu_append (GTK_MENU (gfig_op_menu), menu_item); gtk_widget_show (menu_item); gtk_signal_connect (GTK_OBJECT (menu_item), "activate", (GtkSignalFunc)gfig_save_menu_callback, NULL); #if 0 gtk_widget_install_accelerator (menu_item, accelerator_table, "activate", 'S', 0); #endif /* 0 */ menu_item = gtk_menu_item_new_with_label (_("Save as...")); gtk_menu_append (GTK_MENU (gfig_op_menu), menu_item); gtk_widget_show (menu_item); gtk_signal_connect (GTK_OBJECT (menu_item), "activate", (GtkSignalFunc)gfig_rename_menu_callback, NULL); #if 0 gtk_widget_install_accelerator (menu_item, accelerator_table, "activate", 'A', 0); #endif /* 0 */ menu_item = gtk_menu_item_new_with_label (_("Copy")); gtk_menu_append (GTK_MENU (gfig_op_menu), menu_item); gtk_widget_show (menu_item); gtk_signal_connect (GTK_OBJECT (menu_item), "activate", (GtkSignalFunc)gfig_copy_menu_callback, NULL); #if 0 gtk_widget_install_accelerator (menu_item, accelerator_table, "activate", 'C', 0); #endif /* 0 */ menu_item = gtk_menu_item_new_with_label (_("Edit")); gtk_menu_append (GTK_MENU (gfig_op_menu), menu_item); gtk_widget_show (menu_item); gtk_signal_connect (GTK_OBJECT (menu_item), "activate", (GtkSignalFunc)gfig_edit_menu_callback, NULL); #if 0 gtk_widget_install_accelerator (menu_item, accelerator_table, "activate", 'E', 0); #endif /* 0 */ } static void gfig_op_menu_popup (gint button, guint32 activate_time, GFigObj *obj) { gfig_obj_for_menu = obj; /* Static data again!*/ if (obj->obj_status & GFIG_READONLY) { gtk_widget_set_sensitive (save_menu_item, FALSE); } else { gtk_widget_set_sensitive (save_menu_item, TRUE); } gtk_menu_popup (GTK_MENU (gfig_op_menu), NULL, NULL, NULL, NULL, button, activate_time); } static gint list_button_press (GtkWidget *widget, GdkEventButton *event, gpointer data) { switch (event->type) { case GDK_BUTTON_PRESS: #ifdef DEBUG printf ("Single button press\n"); #endif /* DEBUG */ if (event->button == 3) { #ifdef DEBUG printf ("Popup on '%s'\n", ((GFigObj *)data)->draw_name); #endif /* DEBUG */ gfig_op_menu_popup (event->button, event->time, (GFigObj *) data); return FALSE; } list_button_update ((GFigObj *) data); break; case GDK_2BUTTON_PRESS: #ifdef DEBUG printf ("Two button press\n"); #endif /* DEBUG */ gfig_dialog_edit_list (widget, data, FALSE); break; default: g_warning ("gfig: unknown event.\n"); break; } return FALSE; } static void gfig_scale_update_scale (GtkAdjustment *adjustment, gdouble *value) { gimp_double_adjustment_update (adjustment, value); if (!selvals.scaletoimage) { scale_x_factor = (1 / (*value)) * org_scale_x_factor; scale_y_factor = (1 / (*value)) * org_scale_y_factor; update_draw_area (gfig_preview, NULL); } } /* Use to toggle the toggles */ static void gfig_scale2img_update (GtkWidget *widget, gpointer data) { gimp_toggle_button_update (widget, data); if (*((gint *) data)) { GtkObject *adj; adj = gtk_object_get_user_data (GTK_OBJECT (widget)); scale_x_factor = org_scale_x_factor; scale_y_factor = org_scale_y_factor; gtk_adjustment_set_value (GTK_ADJUSTMENT (adj), 1.0); update_draw_area (gfig_preview, NULL); } } /* Given a row then srink it down a bit */ static void do_gfig_preview (guchar *dest_row, guchar *src_row, gint width, gint dh, gint height, gint bpp) { memcpy (dest_row, src_row, width*bpp); } static void dialog_update_preview (void) { gint y; gint check, check_0, check_1; if (!selvals.showimage) { memset (preview_row, -1, preview_width*4); for (y = 0; y < preview_height; y++) { gtk_preview_draw_row (GTK_PREVIEW (gfig_preview), preview_row, 0, y, preview_width); } return; } if (!pv_cache) { refill_cache (); } for (y = 0; y < preview_height; y++) { if ((y / GIMP_CHECK_SIZE) & 1) { check_0 = GIMP_CHECK_DARK * 255; check_1 = GIMP_CHECK_LIGHT * 255; } else { check_0 = GIMP_CHECK_LIGHT * 255; check_1 = GIMP_CHECK_DARK * 255; } do_gfig_preview (preview_row, pv_cache+y*preview_width*img_bpp, preview_width, y, preview_height, img_bpp); if (img_bpp > 3) { int i, j; for (i = 0, j = 0 ; i < sizeof (preview_row); i += 4, j += 3) { gint alphaval; if (((i/4) / GIMP_CHECK_SIZE) & 1) check = check_0; else check = check_1; alphaval = preview_row[i + 3]; preview_row[j] = check + (((preview_row[i] - check)*alphaval)/255); preview_row[j + 1] = check + (((preview_row[i + 1] - check)*alphaval)/255); preview_row[j + 2] = check + (((preview_row[i + 2] - check)*alphaval)/255); } } gtk_preview_draw_row (GTK_PREVIEW (gfig_preview), preview_row, 0, y, preview_width); } } static void gfig_new_gc (void) { GdkColor fg, bg; /* create a new graphics context */ gfig_gc = gdk_gc_new (gfig_preview->window); gdk_gc_set_function (gfig_gc, GDK_INVERT); fg.pixel = 0xFFFFFFFF; bg.pixel = 0x00000000; gdk_gc_set_foreground (gfig_gc, &fg); gdk_gc_set_background (gfig_gc, &bg); gdk_gc_set_line_attributes (gfig_gc, 1, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER); } static gint get_num_radials (void) { gint gridsp = MAX_GRID + MIN_GRID; /* select number of radials to draw */ /* Either have 16 32 or 48 */ /* correspond to GRID_MAX, midway and GRID_MIN */ return (gridsp - selvals.opts.gridspacing); } #define SQ_SIZE 8 static gint inside_sqr (GdkPoint *cpnt, GdkPoint *testpnt) { /* Return TRUE if testpnt is near cpnt */ gint16 x = cpnt->x; gint16 y = cpnt->y; gint16 tx = testpnt->x; gint16 ty = testpnt->y; #ifdef DEBUG printf ("Testing if (%x,%x) is near (%x,%x)\n", tx, ty, x, y); #endif /* DEBUG */ return (abs (x - tx) <= SQ_SIZE && abs (y - ty) < SQ_SIZE); } /* find_grid_pos - Given an x, y point return the grid position of it */ /* return the new position in the passed point */ static void find_grid_pos (GdkPoint *p, GdkPoint *gp, guint is_butt3) { gint16 x = p->x; gint16 y = p->y; static GdkPoint cons_pnt; static gdouble cons_radius; static gdouble cons_ang; static gboolean cons_center; if (selvals.opts.gridtype == RECT_GRID) { if (p->x % selvals.opts.gridspacing > selvals.opts.gridspacing/2) x += selvals.opts.gridspacing; if (p->y % selvals.opts.gridspacing > selvals.opts.gridspacing/2) y += selvals.opts.gridspacing; gp->x = (x/selvals.opts.gridspacing)*selvals.opts.gridspacing; gp->y = (y/selvals.opts.gridspacing)*selvals.opts.gridspacing; if (is_butt3) { if (abs (gp->x - cons_pnt.x) < abs (gp->y - cons_pnt.y)) gp->x = cons_pnt.x; else gp->y = cons_pnt.y; } else { /* Store the point since might be used later */ cons_pnt = *gp; /* Structure copy */ } } else if (selvals.opts.gridtype == POLAR_GRID) { gdouble ang_grid; gdouble ang_radius; gdouble real_radius; gdouble real_angle; gdouble rounded_angle; gint rounded_radius; gint16 shift_x = x - preview_width/2; gint16 shift_y = -y + preview_height/2; real_radius = ang_radius = sqrt ((shift_y*shift_y) + (shift_x*shift_x)); /* round radius */ rounded_radius = (gint) (RINT (ang_radius/selvals.opts.gridspacing))*selvals.opts.gridspacing; if (rounded_radius <= 0 || real_radius <=0) { /* DEAD CENTER */ gp->x = preview_width/2; gp->y = preview_height/2; if (!is_butt3) cons_center = TRUE; #ifdef DEBUG printf ("Dead center\n"); #endif /* DEBUG */ return; } ang_grid = 2*G_PI/get_num_radials (); real_angle = atan2 (shift_y, shift_x); if (real_angle < 0) real_angle += 2*G_PI; rounded_angle = (RINT ((real_angle/ang_grid)))*ang_grid; #ifdef DEBUG printf ("real_ang = %f ang_gid = %f rounded_angle = %f rounded radius = %d\n", real_angle, ang_grid, rounded_angle, rounded_radius); printf ("preview_width = %d preview_height = %d\n", preview_width, preview_height); #endif /* DEBUG */ gp->x = (gint)RINT ((rounded_radius*cos (rounded_angle))) + preview_width/2; gp->y = -(gint)RINT ((rounded_radius*sin (rounded_angle))) + preview_height/2; if (is_butt3) { if (!cons_center) { if (fabs (rounded_angle - cons_ang) > ang_grid/2) { gp->x = (gint)RINT ((cons_radius*cos (rounded_angle))) + preview_width/2; gp->y = -(gint)RINT ((cons_radius*sin (rounded_angle))) + preview_height/2; } else { gp->x = (gint)RINT ((rounded_radius*cos (cons_ang))) + preview_width/2; gp->y = -(gint)RINT ((rounded_radius*sin (cons_ang))) + preview_height/2; } } } else { cons_radius = rounded_radius; cons_ang = rounded_angle; cons_center = FALSE; } } else if (selvals.opts.gridtype == ISO_GRID) { if (is_butt3) { static GdkPoint b_pnt; static GdkPoint i_pnt; static GdkPoint ii_pnt; gint d; gint dd; b_pnt.x = cons_pnt.x; b_pnt.y = cons_pnt.y + preview_width; d = calculate_point_to_line_distance (p, &cons_pnt, &b_pnt, &i_pnt); b_pnt.x = cons_pnt.x; b_pnt.y = cons_pnt.y - preview_width; dd = calculate_point_to_line_distance (p, &cons_pnt, &b_pnt, &ii_pnt); if (dd < d) { i_pnt.x = ii_pnt.x; i_pnt.y = ii_pnt.y; d = dd; } b_pnt.x = cons_pnt.x + preview_width; b_pnt.y = cons_pnt.y + preview_width/2; dd = calculate_point_to_line_distance (p, &cons_pnt, &b_pnt, &ii_pnt); if (dd < d) { i_pnt.x = ii_pnt.x; i_pnt.y = ii_pnt.y; d = dd; } b_pnt.x = cons_pnt.x + preview_width; b_pnt.y = cons_pnt.y - preview_width/2; dd = calculate_point_to_line_distance (p, &cons_pnt, &b_pnt, &ii_pnt); if (dd < d) { i_pnt.x = ii_pnt.x; i_pnt.y = ii_pnt.y; d = dd; } b_pnt.x = cons_pnt.x - preview_width; b_pnt.y = cons_pnt.y + preview_width/2; dd = calculate_point_to_line_distance (p, &cons_pnt, &b_pnt, &ii_pnt); if (dd < d) { i_pnt.x = ii_pnt.x; i_pnt.y = ii_pnt.y; d = dd; } b_pnt.x = cons_pnt.x - preview_width; b_pnt.y = cons_pnt.y - preview_width/2; dd = calculate_point_to_line_distance (p, &cons_pnt, &b_pnt, &ii_pnt); if (dd < d) { i_pnt.x = ii_pnt.x; i_pnt.y = ii_pnt.y; d = dd; } x = i_pnt.x; y = i_pnt.y; } if (x % selvals.opts.gridspacing > selvals.opts.gridspacing/2) x += selvals.opts.gridspacing; gp->x = (x/selvals.opts.gridspacing)*selvals.opts.gridspacing; if (((gp->x/selvals.opts.gridspacing) % 2) != 0) { y -= selvals.opts.gridspacing/2; if (y % selvals.opts.gridspacing > selvals.opts.gridspacing/2) y += selvals.opts.gridspacing; gp->y = (selvals.opts.gridspacing/2) + ((y/selvals.opts.gridspacing)*selvals.opts.gridspacing); } else { if (y % selvals.opts.gridspacing > selvals.opts.gridspacing/2) y += selvals.opts.gridspacing; gp->y = (y/selvals.opts.gridspacing)*selvals.opts.gridspacing; } if (!is_butt3) { /* Store the point since it might be used later */ cons_pnt = *gp; /* Structure copy */ } } } /* Calculate distance from a point to a line * Taken from the newsgroup comp.graphics.algorithms FAQ. */ static gint calculate_point_to_line_distance (GdkPoint *p, GdkPoint *A, GdkPoint *B, GdkPoint *I) { gint L2; gint L; L2 = ((B->x - A->x)*(B->x - A->x)) + ((B->y - A->y)*(B->y - A->y)); L = (gint) sqrt (L2); /* gint r; */ /* gint s; */ /* r = ((A->y - p->y)*(A->y - B->y) - (A->x - p->x)*(B->x - A->x))/L2; */ /* s = ((A->y - p->y)*(B->x - A->x) - (A->x - p->x)*(B->y - A->y))/L2; */ /* Let I be the point of perpendicular projection of C onto AB. */ I->x = A->x + (((A->y - p->y)*(A->y - B->y) - (A->x - p->x)*(B->x - A->x))*(B->x - A->x))/L2; I->y = A->y + (((A->y - p->y)*(A->y - B->y) - (A->x - p->x)*(B->x - A->x))*(B->y - A->y))/L2; return abs ((((A->y - p->y)*(B->x - A->x)) - ((A->x - p->x)*(B->y - A->y)))*L); } /* Given a point x, y draw a circle */ static void draw_circle (GdkPoint *p) { if (!selvals.opts.showcontrol || drawing_pic) return; gdk_draw_arc (gfig_preview->window, gfig_gc, 0, p->x - SQ_SIZE/2, p->y - SQ_SIZE/2, SQ_SIZE, SQ_SIZE, 0, 360*64); } /* Given a point x, y draw a square around it */ static void draw_sqr (GdkPoint *p) { if (!selvals.opts.showcontrol || drawing_pic) return; gdk_draw_rectangle (gfig_preview->window, gfig_gc, 0, gfig_scale_x ((gint)p->x) - SQ_SIZE/2, gfig_scale_y ((gint)p->y) - SQ_SIZE/2, (gint)SQ_SIZE, (gint)SQ_SIZE); } /* Draw the grid on the screen */ static void draw_grid_clear (GtkWidget *widget, gpointer data) { /* wipe slate and start again */ dialog_update_preview (); draw_grid (widget, data); draw_objects (current_obj->obj_list, TRUE); gtk_widget_draw (gfig_preview, NULL); gdk_flush (); } static void toggle_tooltips (GtkWidget *widget, gpointer data) { gimp_toggle_button_update (widget, data); if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) gimp_help_enable_tooltips (); else gimp_help_disable_tooltips (); } static void toggle_show_image (GtkWidget *widget, gpointer data) { /* wipe slate and start again */ draw_grid_clear (widget, data); } static void toggle_obj_type (GtkWidget *widget, gpointer data) { GdkCursorType ctype = GDK_LAST_CURSOR; static GdkCursor* p_cursors[DEL_OBJ + 1]; if (selvals.otype != (DobjType) data) { /* Mem leak */ obj_creating = NULL; tmp_line = NULL; tmp_bezier = NULL; if ((DobjType)data < MOVE_OBJ) { obj_show_single = -1; /* Cancel select preview */ } /* Update draw areas */ update_draw_area (gfig_preview, NULL); /* And preview */ list_button_update (current_obj); } selvals.otype = (DobjType) data; switch (selvals.otype) { case LINE: case CIRCLE: case ELLIPSE: case ARC: case POLY: case STAR: case SPIRAL: case BEZIER: default: ctype = GDK_CROSSHAIR; break; case MOVE_OBJ: case MOVE_POINT: case COPY_OBJ: case MOVE_COPY_OBJ: ctype = GDK_DIAMOND_CROSS; break; case DEL_OBJ: ctype = GDK_PIRATE; break; } if (!p_cursors[selvals.otype]) p_cursors[selvals.otype] = gdk_cursor_new (ctype); gdk_window_set_cursor (gfig_preview->window, p_cursors[selvals.otype]); } static void draw_grid_polar (GdkGC *drawgc) { gint step; gint loop; gint radius; gint max_rad; gdouble ang_grid; gdouble ang_loop; gdouble ang_radius; /* Pick center and draw concentric circles */ gint grid_x_center = preview_width/2; gint grid_y_center = preview_height/2; step = selvals.opts.gridspacing; max_rad = sqrt (preview_width * preview_width + preview_height * preview_height) / 2; for (loop = 0; loop < max_rad; loop += step) { radius = loop; gdk_draw_arc (gfig_preview->window, drawgc, 0, grid_x_center - radius, grid_y_center - radius, radius*2, radius*2, 0, 360 * 64); } /* Lines */ ang_grid = 2 * G_PI / get_num_radials (); ang_radius = sqrt ((preview_width * preview_width) + (preview_height * preview_height)) / 2; for (loop = 0; loop <= get_num_radials (); loop++) { gint lx, ly; ang_loop = loop * ang_grid; lx = (gint) RINT (ang_radius * cos (ang_loop)); ly = (gint) RINT (ang_radius * sin (ang_loop)); gdk_draw_line (gfig_preview->window, drawgc, (gint) lx + (preview_width) / 2, -(gint) ly + (preview_height) / 2, (gint) (preview_width) / 2, (gint) (preview_height) / 2); } } static void draw_grid_sq (GdkGC *drawgc) { gint step; gint loop; /* Draw the horizontal lines */ step = selvals.opts.gridspacing; for (loop = 0 ; loop < preview_height ; loop += step) { gdk_draw_line (gfig_preview->window, drawgc, (gint)0, (gint)loop, (gint)preview_width, (gint)loop); } /* Draw the vertical lines */ for (loop = 0 ; loop < preview_width ; loop += step) { gdk_draw_line (gfig_preview->window, drawgc, (gint)loop, (gint)0, (gint)loop, (gint)preview_height); } } static void draw_grid_iso (GdkGC *drawgc) { gint step; gint loop; gint diagonal_start; gint diagonal_end; gint diagonal_width; gint diagonal_height; step = selvals.opts.gridspacing; /* Draw the vertical lines */ for (loop = 0 ; loop < preview_width ; loop += step) { gdk_draw_line (gfig_preview->window, drawgc, (gint)loop, (gint)0, (gint)loop, (gint)preview_height); } diagonal_start = preview_width/2; diagonal_start = diagonal_start - (diagonal_start % step); diagonal_start = -diagonal_start; diagonal_end = preview_height + (preview_width/2); diagonal_end = diagonal_end - (diagonal_end % step); diagonal_width = preview_width; diagonal_height = diagonal_width/2; /* Draw diagonal lines */ for (loop = diagonal_start ; loop < diagonal_end ; loop += step) { gdk_draw_line (gfig_preview->window, drawgc, (gint)0, (gint)loop, (gint)diagonal_width, (gint)loop + diagonal_height); gdk_draw_line (gfig_preview->window, drawgc, (gint)0, (gint)loop, (gint)diagonal_width, (gint)loop - diagonal_height); } } static GdkGC * gfig_get_grid_gc (GtkWidget *w, gint gctype) { switch (gctype) { case GFIG_BLACK_GC: return (w->style->black_gc); case GFIG_WHITE_GC: return (w->style->white_gc); case GFIG_GREY_GC: return (grid_hightlight_drawgc); case GTK_STATE_NORMAL: return (w->style->bg_gc[GTK_STATE_NORMAL]); case GTK_STATE_ACTIVE: return (w->style->bg_gc[GTK_STATE_ACTIVE]); case GTK_STATE_PRELIGHT: return (w->style->bg_gc[GTK_STATE_PRELIGHT]); case GTK_STATE_SELECTED: return (w->style->bg_gc[GTK_STATE_SELECTED]); case GTK_STATE_INSENSITIVE: return (w->style->bg_gc[GTK_STATE_INSENSITIVE]); default: g_warning ("Unknown type for grid colouring\n"); return (w->style->bg_gc[GTK_STATE_PRELIGHT]); } } static void draw_grid (GtkWidget *widget, gpointer data) { GdkGC *drawgc; /* Get the size of the preview and calc where the lines go */ /* Draw in prelight to start with... */ /* Always start in the upper left corner for rect. */ if ((preview_width < selvals.opts.gridspacing && preview_height < selvals.opts.gridspacing) || drawing_pic) { /* Don't draw if they don't fit */ return; } if (selvals.opts.drawgrid) drawgc = gfig_get_grid_gc (gfig_preview, grid_gc_type); else return; if (selvals.opts.gridtype == RECT_GRID) draw_grid_sq (drawgc); else if (selvals.opts.gridtype == POLAR_GRID) draw_grid_polar (drawgc); else if (selvals.opts.gridtype == ISO_GRID) draw_grid_iso (drawgc); } static void do_gfig (void) { /* Not sure if requre post proc - leave stub in */ } /* This could belong in a separate file ... but makes it easier to lump into * one when compiling the plugin. */ /* 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. */ static void free_one_obj (Dobject *obj) { d_delete_dobjpoints (obj->points); g_free (obj); } static void free_all_objs (DAllObjs * objs) { /* Free all objects */ DAllObjs * next; while (objs) { free_one_obj (objs->obj); next = objs->next; g_free (objs); objs = next; } } static 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'; if (ferror (from)) { g_warning (_("Error reading file")); return (0); } #ifdef DEBUG printf ("Processing line '%s'\n", buf); #endif /* DEBUG */ return (ret); } static void gfig_clear_callback (GtkWidget *widget, gpointer data) { /* Make sure we can get back - if we have some objects to get back to */ if (!current_obj->obj_list) return; setup_undo (); /* Free all objects */ free_all_objs (current_obj->obj_list); current_obj->obj_list = NULL; obj_creating = NULL; tmp_line = NULL; tmp_bezier = NULL; update_draw_area (gfig_preview, NULL); /* And preview */ list_button_update (current_obj); } static void gfig_undo_callback (GtkWidget *widget, gpointer data) { if (undo_water_mark >= 0) { /* Free current objects an reinstate previous */ free_all_objs (current_obj->obj_list); current_obj->obj_list = NULL; tmp_bezier = tmp_line = obj_creating = NULL; current_obj->obj_list = undo_table[undo_water_mark]; undo_water_mark--; /* Update the screen */ update_draw_area (gfig_preview, NULL); /* And preview */ list_button_update (current_obj); gfig_obj_modified (current_obj, GFIG_MODIFIED); current_obj->obj_status |= GFIG_MODIFIED; } if (undo_water_mark < 0) gtk_widget_set_sensitive (widget, FALSE); } static void clear_undo (void) { int lv; for (lv = undo_water_mark; lv >= 0; lv--) { if (undo_table[lv]) free_all_objs (undo_table[lv]); undo_table[lv] = NULL; } undo_water_mark = -1; gtk_widget_set_sensitive (undo_widget, FALSE); } static void setup_undo (void) { /* Copy object list to undo buffer */ #if DEBUG printf ("setup undo level [%d]\n", undo_water_mark); #endif /*DEBUG*/ if (!current_obj) { /* If no current_obj must be loading -> no undo */ return; } if (undo_water_mark >= selvals.maxundo - 1) { int loop; /* the little one in the bed said "roll over".. */ if (undo_table[0]) free_one_obj (undo_table[0]->obj); for (loop = 0; loop < undo_water_mark; loop++) { undo_table[loop] = undo_table[loop + 1]; } } else { undo_water_mark++; } undo_table[undo_water_mark] = copy_all_objs (current_obj->obj_list); gtk_widget_set_sensitive (undo_widget, TRUE); gfig_obj_modified (current_obj, GFIG_MODIFIED); current_obj->obj_status |= GFIG_MODIFIED; } /* Given a number of float co-ords adjust for scaling back to org size */ /* Size is number of PAIRS of points */ /* FP + int varients */ static void scale_to_orginal_x (gdouble *list) { *list *= scale_x_factor; } static gint gfig_scale_x (gint x) { if (!selvals.scaletoimage) return (gint) (x * (1 / scale_x_factor)); else return x; } static gint gfig_invscale_x (gint x) { if (!selvals.scaletoimage) return (gint) (x * (scale_x_factor)); else return x; } static void scale_to_orginal_y (gdouble *list) { *list *= scale_y_factor; } static gint gfig_scale_y (gint y) { if (!selvals.scaletoimage) return (gint) (y * (1 / scale_y_factor)); else return y; } static gint gfig_invscale_y (gint y) { if (!selvals.scaletoimage) return (gint) (y*(scale_y_factor)); else return y; } /* Pairs x followed by y */ static void scale_to_original_xy (gdouble *list, gint size) { gint i; for (i = 0; i < size * 2; i += 2) { scale_to_orginal_x (&list[i]); scale_to_orginal_y (&list[i + 1]); } } /* Pairs x followed by y */ static void scale_to_xy (gdouble *list, gint size) { gint i; for (i = 0; i < size * 2; i += 2) { list[i] *= (org_scale_x_factor / scale_x_factor); list[i + 1] *= (org_scale_y_factor / scale_y_factor); } } /* Given an list of PAIRS of doubles reverse the list */ /* Size is number of pairs to swap */ static void reverse_pairs_list (gdouble *list, gint size) { gint i; struct cs { gdouble i1; gdouble i2; } copyit, *orglist; orglist = (struct cs *) list; /* Uses struct copies */ for (i = 0; i < size / 2; i++) { copyit = orglist[i]; orglist[i] = orglist[size - 1 - i]; orglist[size - 1 - i] = copyit; } } /* Delete a list of points */ static void d_delete_dobjpoints (DobjPoints * pnts) { DobjPoints * next; DobjPoints * pnt2del = pnts; while (pnt2del) { next = pnt2del->next; g_free (pnt2del); pnt2del = next; } } static DobjPoints * d_copy_dobjpoints (DobjPoints * pnts) { DobjPoints *ret = NULL; DobjPoints *head = NULL; DobjPoints *newpnt; DobjPoints *pnt2copy = pnts; while (pnt2copy) { newpnt = g_new0 (DobjPoints, 1); newpnt->pnt.x = pnt2copy->pnt.x; newpnt->pnt.y = pnt2copy->pnt.y; if (!ret) head = ret = newpnt; else { head->next = newpnt; head = newpnt; } pnt2copy = pnt2copy->next; } return ret; } static gint 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 Dobject * get_nearest_objs (GFigObj *obj, GdkPoint *pnt) { /* Nearest object to given point or NULL */ DAllObjs *all; Dobject *test_obj; gint count = 0; if (!obj) return NULL; all = obj->obj_list; while (all) { test_obj = all->obj; if (count == obj_show_single || obj_show_single == -1) if (scan_obj_points (test_obj->points, pnt)) { return test_obj; } all = all->next; count++; } return NULL; } 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; } } static void remove_obj_from_list (GFigObj *obj, Dobject *del_obj) { /* Nearest object to given point or NULL */ DAllObjs *all; DAllObjs *prev_all = NULL; g_assert (del_obj != NULL); all = obj->obj_list; while (all) { if (all->obj == del_obj) { /* Found the one to delete */ #ifdef DEBUG printf ("Found the one to delete\n"); #endif /* DEBUG */ if (prev_all) prev_all->next = all->next; else obj->obj_list = all->next; /* Draw obj (which will actually undraw it! */ del_obj->drawfunc (del_obj); free_one_obj (del_obj); g_free (all); if (obj_show_single != -1) { /* We've just deleted the only visible one */ draw_grid_clear (NULL, NULL); /*Args not used */ obj_show_single = -1; /* Show all again */ } return; } prev_all = all; all = all->next; } g_warning (_("Hey where has the object gone ?")); } static DobjPoints * get_diffs (Dobject *obj, gint16 *xdiff, gint16 *ydiff, GdkPoint *to_pnt) { DobjPoints *spnt; g_assert (obj != NULL); spnt = obj->points; if (!spnt) return (NULL); /* no-line */ /* Slow slow slowwwwww....*/ while (spnt) { if (spnt->found_me) { *xdiff = spnt->pnt.x - to_pnt->x; *ydiff = spnt->pnt.y - to_pnt->y; return (spnt); } spnt = spnt->next; } return (NULL); } static void update_pnts (Dobject *obj, gint16 xdiff, gint16 ydiff) { DobjPoints *spnt; g_assert (obj != NULL); /* Update all pnts */ spnt = obj->points; if (!spnt) return; /* no-line */ /* Go around all the points drawing a line from one to the next */ while (spnt) { spnt->pnt.x = spnt->pnt.x - xdiff; spnt->pnt.y = spnt->pnt.y - ydiff; spnt = spnt->next; } } static void do_move_all_obj (GdkPoint *to_pnt) { /* Move all objects in one go */ /* Undraw/then draw in new pos */ DAllObjs *all; Dobject *obj; gint16 xdiff = 0; gint16 ydiff = 0; xdiff = move_all_pnt->x - to_pnt->x; ydiff = move_all_pnt->y - to_pnt->y; if (!xdiff && !ydiff) return; all = current_obj->obj_list; while (all) { obj = all->obj; /* undraw ! */ draw_one_obj (obj); update_pnts (obj, xdiff, ydiff); /* Draw in new pos */ draw_one_obj (obj); all = all->next; } *move_all_pnt = *to_pnt; /* Structure copy */ } static void do_move_obj (Dobject *obj, GdkPoint *to_pnt) { /* Move the whole line - undraw the line to start with */ /* Then draw in new pos */ gint16 xdiff = 0; gint16 ydiff = 0; get_diffs (obj, &xdiff, &ydiff, to_pnt); if (!xdiff && !ydiff) return; /* undraw ! */ draw_one_obj (obj); update_pnts (obj, xdiff, ydiff); /* Draw in new pos */ draw_one_obj (obj); } static void do_move_obj_pnt (Dobject *obj, GdkPoint *to_pnt) { /* Move the whole line - undraw the line to start with */ /* Then draw in new pos */ DobjPoints *spnt; gint16 xdiff = 0; gint16 ydiff = 0; spnt = get_diffs (obj, &xdiff, &ydiff, to_pnt); if ((!xdiff && !ydiff) || !spnt) return; /* undraw ! */ draw_one_obj (obj); spnt->pnt.x = spnt->pnt.x - xdiff; spnt->pnt.y = spnt->pnt.y - ydiff; /* Draw in new pos */ draw_one_obj (obj); } /* Save a line away to the specified stream */ static void d_save_line (Dobject *obj, FILE *to) { DobjPoints * spnt; spnt = obj->points; if (!spnt) return; /* End-of-line */ fprintf (to, "\n"); while (spnt) { fprintf (to, "%d %d\n", (gint)spnt->pnt.x, (gint)spnt->pnt.y); spnt = spnt->next; } fprintf (to, "\n"); } /* Load a line from the specified stream */ static Dobject * d_load_line (FILE *from) { Dobject *new_obj = NULL; gint xpnt; gint ypnt; gchar buf[MAX_LOAD_LINE]; #ifdef DEBUG printf ("Load line called\n"); #endif /* DEBUG */ while (get_line (buf, MAX_LOAD_LINE, from, 0)) { if (sscanf (buf, "%d %d", &xpnt, &ypnt) != 2) { /* Must be the end */ if (strcmp ("", buf)) { g_warning ("[%d] Internal load error while loading line", line_no); return (NULL); } return (new_obj); } if (!new_obj) new_obj = d_new_line (xpnt, ypnt); else d_pnt_add_line (new_obj, xpnt, ypnt, -1); } return (new_obj); } static Dobject * d_copy_line (Dobject *obj) { Dobject *nl; if (!obj) return (NULL); g_assert (obj->type == LINE); nl = d_new_line (obj->points->pnt.x, obj->points->pnt.y); nl->points->next = d_copy_dobjpoints (obj->points->next); return (nl); } /* Draw the given line -- */ static void d_draw_line (Dobject *obj) { DobjPoints *spnt; DobjPoints *epnt; spnt = obj->points; if (!spnt) return; /* End-of-line */ epnt = spnt->next; while (spnt && epnt) { #if DEBUG printf ("Drawing line 0x%x (%x,%x) -> (%x,%x)\n", spnt, (gint)spnt->pnt.x, (gint)spnt->pnt.y, (gint)epnt->pnt.x, (gint)epnt->pnt.y); #endif /* DEBUG */ draw_sqr (&spnt->pnt); /* Go around all the points drawing a line from one to the next */ if (drawing_pic) { gdk_draw_line (pic_preview->window, pic_preview->style->black_gc, adjust_pic_coords ((gint)spnt->pnt.x, preview_width), adjust_pic_coords ((gint)spnt->pnt.y, preview_height), adjust_pic_coords ((gint)epnt->pnt.x, preview_width), adjust_pic_coords ((gint)epnt->pnt.y, preview_height)); } else { gdk_draw_line (gfig_preview->window, gfig_gc, gfig_scale_x ((gint)spnt->pnt.x), gfig_scale_y ((gint)spnt->pnt.y), gfig_scale_x ((gint)epnt->pnt.x), gfig_scale_y ((gint)epnt->pnt.y)); } spnt = epnt; epnt = epnt->next; } draw_sqr (&spnt->pnt); } static void d_paint_line (Dobject *obj) { DobjPoints * spnt; gdouble *line_pnts; gint seg_count = 0; gint i = 0; spnt = obj->points; /* count */ while (spnt) { seg_count++; spnt = spnt->next; } spnt = obj->points; if (!spnt || !seg_count) return; /* no-line */ line_pnts = g_new0 (gdouble, 2 * seg_count + 1); /* Go around all the points drawing a line from one to the next */ while (spnt) { line_pnts[i++] = spnt->pnt.x; line_pnts[i++] = spnt->pnt.y; spnt = spnt->next; } /* Reverse line if approp */ if (selvals.reverselines) reverse_pairs_list (&line_pnts[0], i/2); /* Scale before drawing */ if (selvals.scaletoimage) scale_to_original_xy (&line_pnts[0], i/2); else scale_to_xy (&line_pnts[0], i/2); /* One go */ if (selvals.painttype == PAINT_BRUSH_TYPE) { gfig_paint (selvals.brshtype, gfig_drawable, seg_count * 2, line_pnts); } else { gimp_free_select (gfig_image, seg_count * 2, line_pnts, selopt.type, selopt.antia, selopt.feather, selopt.feather_radius); } g_free (line_pnts); } /* Create a new line object. starting at the x, y point might add styles * later. */ static Dobject * d_new_line (gint x, gint y) { Dobject *nobj; DobjPoints *npnt; /* Get new object and starting point */ /* Start point */ npnt = g_new0 (DobjPoints, 1); #if DEBUG printf ("New line start at (%x,%x)\n", x, y); #endif /* DEBUG */ npnt->pnt.x = x; npnt->pnt.y = y; nobj = g_new0 (Dobject, 1); nobj->type = LINE; nobj->points = npnt; nobj->drawfunc = d_draw_line; nobj->loadfunc = d_load_line; nobj->savefunc = d_save_line; nobj->paintfunc = d_paint_line; nobj->copyfunc = d_copy_line; return (nobj); } /* You guessed it delete the object !*/ /* static void d_delete_line (Dobject *obj) { g_assert (obj != NULL); * First free the list of points - then the object itself * d_delete_dobjpoints (obj->points); g_free (obj); } */ /* Add a point to a line (given x, y) * pos = 0 = head * pos = -1 = tail * 0 < pos = nth position */ static void d_pnt_add_line (Dobject *obj, gint x, gint y, gint pos) { DobjPoints *npnts = g_new0 (DobjPoints, 1); g_assert (obj != NULL); npnts->pnt.x = x; npnts->pnt.y = y; 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; } } } } /* Update end point of line */ static void d_update_line (GdkPoint *pnt) { DobjPoints *spnt, *epnt; /* Get last but one segment and undraw it - * Then draw new segment in. * always dealing with the static object. */ /* Get start of segments */ spnt = obj_creating->points; if (!spnt) return; /* No points */ if ((epnt = spnt->next)) { /* undraw current */ /* Draw square on point */ draw_circle (&epnt->pnt); gdk_draw_line (gfig_preview->window, /*gfig_preview->style->bg_gc[GTK_STATE_NORMAL],*/ gfig_gc, (gint) spnt->pnt.x, (gint) spnt->pnt.y, (gint) epnt->pnt.x, (gint) epnt->pnt.y); g_free (epnt); } /* draw new */ /* Draw circle on point */ draw_circle (pnt); epnt = g_new0 (DobjPoints, 1); epnt->pnt.x = pnt->x; epnt->pnt.y = pnt->y; gdk_draw_line (gfig_preview->window, /*gfig_preview->style->bg_gc[GTK_STATE_NORMAL],*/ gfig_gc, (gint) spnt->pnt.x, (gint) spnt->pnt.y, (gint) epnt->pnt.x, (gint) epnt->pnt.y); spnt->next = epnt; } static void d_line_start (GdkPoint *pnt, gint shift_down) { if (!obj_creating || !shift_down) { /* Draw square on point */ /* Must delete obj_creating if we have one */ obj_creating = d_new_line (pnt->x, pnt->y); } else { /* Contniuation */ d_update_line (pnt); } } static void d_line_end (GdkPoint *pnt, gint shift_down) { /* Undraw the last circle */ draw_circle (pnt); if (shift_down) { if (tmp_line) { GdkPoint tmp_pnt = *pnt; if (need_to_scale) { tmp_pnt.x = (gint) (pnt->x * scale_x_factor); tmp_pnt.y = (gint) (pnt->y * scale_y_factor); } d_pnt_add_line (tmp_line, tmp_pnt.x, tmp_pnt.y, -1); free_one_obj (obj_creating); /* Must free obj_creating */ } else { tmp_line = obj_creating; add_to_all_obj (current_obj, obj_creating); } obj_creating = d_new_line (pnt->x, pnt->y); } else { if (tmp_line) { GdkPoint tmp_pnt = *pnt; if (need_to_scale) { tmp_pnt.x = (gint) (pnt->x * scale_x_factor); tmp_pnt.y = (gint) (pnt->y * scale_y_factor); } d_pnt_add_line (tmp_line, tmp_pnt.x, tmp_pnt.y, -1); free_one_obj (obj_creating); /* Must free obj_creating */ } else { add_to_all_obj (current_obj, obj_creating); } obj_creating = NULL; tmp_line = NULL; } /*update_draw_area (gfig_preview, NULL);*/ } /* Save a circle away to the specified stream */ static void d_save_circle (Dobject *obj, FILE *to) { DobjPoints *spnt; spnt = obj->points; if (!spnt) return; fprintf (to, "\n"); while (spnt) { fprintf (to, "%d %d\n", (gint) spnt->pnt.x, (gint) spnt->pnt.y); spnt = spnt->next; } fprintf (to, "\n"); } /* Load a circle from the specified stream */ static Dobject * d_load_circle (FILE *from) { Dobject *new_obj = NULL; gint xpnt; gint ypnt; gchar buf[MAX_LOAD_LINE]; #ifdef DEBUG printf ("Load circle called\n"); #endif /* DEBUG */ while (get_line (buf, MAX_LOAD_LINE, from, 0)) { if (sscanf (buf, "%d %d", &xpnt, &ypnt) != 2) { /* Must be the end */ if (strcmp ("", buf)) { g_warning ("[%d] Internal load error while loading circle", line_no); return NULL; } return new_obj; } if (!new_obj) new_obj = d_new_circle (xpnt, ypnt); else { DobjPoints *edge_pnt; /* Circles only have two points */ edge_pnt = g_new0 (DobjPoints, 1); edge_pnt->pnt.x = xpnt; edge_pnt->pnt.y = ypnt; new_obj->points->next = edge_pnt; } } g_warning ("[%d] Not enough points for circle", line_no); return NULL; } static void d_draw_circle (Dobject * obj) { DobjPoints *center_pnt; DobjPoints *edge_pnt; gdouble radius; center_pnt = obj->points; if (!center_pnt) return; /* End-of-line */ edge_pnt = center_pnt->next; if (!edge_pnt) { g_warning ("Internal error - circle no edge pnt"); } radius = sqrt (((center_pnt->pnt.x - edge_pnt->pnt.x) * (center_pnt->pnt.x - edge_pnt->pnt.x)) + ((center_pnt->pnt.y - edge_pnt->pnt.y) * (center_pnt->pnt.y - edge_pnt->pnt.y))); draw_sqr (¢er_pnt->pnt); draw_sqr (&edge_pnt->pnt); if (drawing_pic) { gdk_draw_arc (pic_preview->window, pic_preview->style->black_gc, 0, adjust_pic_coords (center_pnt->pnt.x - radius, preview_width), adjust_pic_coords (center_pnt->pnt.y - radius, preview_height), adjust_pic_coords (radius * 2, preview_width), adjust_pic_coords (radius * 2, preview_height), 0, 360 * 64); } else { gdk_draw_arc (gfig_preview->window, gfig_gc, 0, gfig_scale_x (center_pnt->pnt.x - (gint) RINT (radius)), gfig_scale_y (center_pnt->pnt.y - (gint) RINT (radius)), gfig_scale_x ((gint) RINT (radius) * 2), gfig_scale_y ((gint) RINT (radius) * 2), 0, 360 * 64); } } static void d_paint_circle (Dobject *obj) { DobjPoints * center_pnt; DobjPoints * edge_pnt; gint radius; gdouble dpnts[4]; g_assert (obj != NULL); if (selvals.approxcircles) { obj->type_data = (gpointer) 600; #ifdef DEBUG printf ("Painting circle as polygon\n"); #endif /* DEBUG */ d_paint_poly (obj); return; } /* Drawing circles is hard . * 1) select circle * 2) stroke it */ center_pnt = obj->points; if (!center_pnt) return; /* End-of-line */ edge_pnt = center_pnt->next; if (!edge_pnt) { g_error ("Internal error - circle no edge pnt"); } radius = (gint) sqrt (((center_pnt->pnt.x - edge_pnt->pnt.x) * (center_pnt->pnt.x - edge_pnt->pnt.x)) + ((center_pnt->pnt.y - edge_pnt->pnt.y) * (center_pnt->pnt.y - edge_pnt->pnt.y))); dpnts[0] = (gdouble) center_pnt->pnt.x - radius; dpnts[1] = (gdouble) center_pnt->pnt.y - radius; dpnts[3] = dpnts[2] = (gdouble) radius * 2; /* Scale before drawing */ if (selvals.scaletoimage) scale_to_original_xy (&dpnts[0], 2); else scale_to_xy (&dpnts[0], 2); gimp_ellipse_select (gfig_image, dpnts[0], dpnts[1], dpnts[2], dpnts[3], selopt.type, selopt.antia, selopt.feather, selopt.feather_radius); /* Is selection all we need ? */ if (selvals.painttype == PAINT_SELECTION_TYPE) return; gimp_edit_stroke (gfig_drawable); gimp_selection_clear (gfig_image); } static Dobject * d_copy_circle (Dobject * obj) { Dobject *nc; #if DEBUG printf ("Copy circle\n"); #endif /*DEBUG*/ if (!obj) return NULL; g_assert (obj->type == CIRCLE); nc = d_new_circle (obj->points->pnt.x, obj->points->pnt.y); nc->points->next = d_copy_dobjpoints (obj->points->next); #if DEBUG printf ("Circle (%x,%x) to (%x,%x)\n", nc->points->pnt.x, obj->points->pnt.y, nc->points->next->pnt.x, obj->points->next->pnt.y); printf ("Done copy\n"); #endif /*DEBUG*/ return nc; } static Dobject * d_new_circle (gint x, gint y) { Dobject *nobj; DobjPoints *npnt; /* Get new object and starting point */ /* Start point */ npnt = g_new0 (DobjPoints, 1); #if DEBUG printf ("New circle start at (%x,%x)\n", x, y); #endif /* DEBUG */ npnt->pnt.x = x; npnt->pnt.y = y; nobj = g_new0 (Dobject, 1); nobj->type = CIRCLE; nobj->points = npnt; nobj->drawfunc = d_draw_circle; nobj->loadfunc = d_load_circle; nobj->savefunc = d_save_circle; nobj->paintfunc = d_paint_circle; nobj->copyfunc = d_copy_circle; return nobj; } static void d_update_circle (GdkPoint *pnt) { DobjPoints *center_pnt, *edge_pnt; gdouble radius; /* Undraw last one then draw new one */ center_pnt = obj_creating->points; if (!center_pnt) return; /* No points */ if ((edge_pnt = center_pnt->next)) { /* Undraw current */ draw_circle (&edge_pnt->pnt); radius = sqrt (((center_pnt->pnt.x - edge_pnt->pnt.x) * (center_pnt->pnt.x - edge_pnt->pnt.x)) + ((center_pnt->pnt.y - edge_pnt->pnt.y) * (center_pnt->pnt.y - edge_pnt->pnt.y))); gdk_draw_arc (gfig_preview->window, gfig_gc, 0, center_pnt->pnt.x - (gint) RINT (radius), center_pnt->pnt.y - (gint) RINT (radius), (gint) RINT (radius) * 2, (gint) RINT (radius) * 2, 0, 360 * 64); } draw_circle (pnt); edge_pnt = g_new0 (DobjPoints, 1); edge_pnt->pnt.x = pnt->x; edge_pnt->pnt.y = pnt->y; radius = sqrt (((center_pnt->pnt.x - edge_pnt->pnt.x) * (center_pnt->pnt.x - edge_pnt->pnt.x)) + ((center_pnt->pnt.y - edge_pnt->pnt.y) * (center_pnt->pnt.y - edge_pnt->pnt.y))); gdk_draw_arc (gfig_preview->window, gfig_gc, 0, center_pnt->pnt.x - (gint) RINT (radius), center_pnt->pnt.y - (gint) RINT (radius), (gint) RINT (radius) * 2, (gint) RINT (radius) * 2, 0, 360 * 64); center_pnt->next = edge_pnt; } static void d_circle_start (GdkPoint *pnt, gint shift_down) { obj_creating = d_new_circle (pnt->x, pnt->y); } static void d_circle_end (GdkPoint *pnt, gint shift_down) { /* Under contrl point */ if (!obj_creating->points->next) { /* No circle created */ free_one_obj (obj_creating); } else { draw_circle (pnt); add_to_all_obj (current_obj, obj_creating); } obj_creating = NULL; } /* Save an ellipse away to the specified stream */ static void d_save_ellipse (Dobject *obj, FILE *to) { DobjPoints *spnt; spnt = obj->points; if (!spnt) return; fprintf (to, "\n"); while (spnt) { fprintf (to, "%d %d\n", (gint) spnt->pnt.x, (gint) spnt->pnt.y); spnt = spnt->next; } fprintf (to, "\n"); } /* Load a circle from the specified stream */ static Dobject * d_load_ellipse (FILE *from) { Dobject *new_obj = NULL; gint xpnt; gint ypnt; gchar buf[MAX_LOAD_LINE]; #ifdef DEBUG printf ("Load ellipse called\n"); #endif /* DEBUG */ while (get_line (buf, MAX_LOAD_LINE, from, 0)) { if (sscanf (buf, "%d %d", &xpnt, &ypnt) != 2) { /* Must be the end */ if (strcmp ("", buf)) { g_message ("[%d] Internal load error while loading ellipse", line_no); return NULL; } return new_obj; } if (!new_obj) new_obj = d_new_ellipse (xpnt, ypnt); else { DobjPoints *edge_pnt; /* Circles only have two points */ edge_pnt = g_new0 (DobjPoints, 1); edge_pnt->pnt.x = xpnt; edge_pnt->pnt.y = ypnt; new_obj->points->next = edge_pnt; } } g_message ("[%d] Not enough points for ellipse", line_no); return NULL; } static void d_draw_ellipse (Dobject * obj) { DobjPoints *center_pnt; DobjPoints *edge_pnt; gint bound_wx; gint bound_wy; gint top_x; gint top_y; center_pnt = obj->points; if (!center_pnt) return; /* End-of-line */ edge_pnt = center_pnt->next; if (!edge_pnt) { g_warning ("Internal error - ellipse no edge pnt"); } draw_sqr (¢er_pnt->pnt); draw_sqr (&edge_pnt->pnt); bound_wx = abs (center_pnt->pnt.x - edge_pnt->pnt.x) * 2; bound_wy = abs (center_pnt->pnt.y - edge_pnt->pnt.y) * 2; if (edge_pnt->pnt.x > center_pnt->pnt.x) top_x = 2 * center_pnt->pnt.x - edge_pnt->pnt.x; else top_x = edge_pnt->pnt.x; if (edge_pnt->pnt.y > center_pnt->pnt.y) top_y = 2 * center_pnt->pnt.y - edge_pnt->pnt.y; else top_y = edge_pnt->pnt.y; if (drawing_pic) { gdk_draw_arc (pic_preview->window, pic_preview->style->black_gc, 0, adjust_pic_coords (top_x, preview_width), adjust_pic_coords (top_y, preview_height), adjust_pic_coords (bound_wx, preview_width), adjust_pic_coords (bound_wy, preview_height), 0, 360 * 64); } else { gdk_draw_arc (gfig_preview->window, gfig_gc, 0, gfig_scale_x (top_x), gfig_scale_y (top_y), gfig_scale_x (bound_wx), gfig_scale_y (bound_wy), 0, 360 * 64); } } static void d_paint_approx_ellipse (Dobject *obj) { /* first point center */ /* Next point is radius */ gdouble *line_pnts; gint seg_count = 0; gint i = 0; DobjPoints * center_pnt; DobjPoints * radius_pnt; gdouble a_axis; gdouble b_axis; gdouble ang_grid; gdouble ang_loop; gdouble radius; gint loop; GdkPoint first_pnt, last_pnt; gint first = 1; g_assert (obj != NULL); /* count - add one to close polygon */ seg_count = 600; center_pnt = obj->points; if (!center_pnt || !seg_count) return; /* no-line */ line_pnts = g_new0 (gdouble, 2 * seg_count + 1); /* Go around all the points drawing a line from one to the next */ radius_pnt = center_pnt->next; /* this defines the vetices */ /* Have center and radius - get lines */ a_axis = ((gdouble) (radius_pnt->pnt.x - center_pnt->pnt.x)); b_axis = ((gdouble) (radius_pnt->pnt.y - center_pnt->pnt.y)); /* Lines */ ang_grid = 2 * G_PI / (gdouble) (gint) 600; for (loop = 0; loop < (gint) 600; loop++) { gdouble lx, ly; GdkPoint calc_pnt; ang_loop = (gdouble)loop * ang_grid; radius = (a_axis * b_axis / (sqrt (cos (ang_loop) * cos (ang_loop) * (b_axis * b_axis - a_axis * a_axis) + a_axis * a_axis))); lx = radius * cos (ang_loop); ly = radius * sin (ang_loop); calc_pnt.x = (gint) RINT (lx + center_pnt->pnt.x); calc_pnt.y = (gint) RINT (ly + center_pnt->pnt.y); /* Miss out duped pnts */ if (!first) { if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y) { continue; } } last_pnt.x = line_pnts[i++] = calc_pnt.x; last_pnt.y = line_pnts[i++] = calc_pnt.y; if (first) { first_pnt.x = calc_pnt.x; first_pnt.y = calc_pnt.y; first = 0; } } line_pnts[i++] = first_pnt.x; line_pnts[i++] = first_pnt.y; /* Reverse line if approp */ if (selvals.reverselines) reverse_pairs_list (&line_pnts[0], i / 2); /* Scale before drawing */ if (selvals.scaletoimage) scale_to_original_xy (&line_pnts[0], i / 2); else scale_to_xy (&line_pnts[0], i / 2); /* One go */ if (selvals.painttype == PAINT_BRUSH_TYPE) { gfig_paint (selvals.brshtype, gfig_drawable, i, line_pnts); } else { gimp_free_select (gfig_image, i, line_pnts, selopt.type, selopt.antia, selopt.feather, selopt.feather_radius); } g_free (line_pnts); } static void d_paint_ellipse (Dobject *obj) { DobjPoints * center_pnt; DobjPoints * edge_pnt; gint bound_wx; gint bound_wy; gint top_x; gint top_y; gdouble dpnts[4]; /* Drawing ellipse is hard . * 1) select circle * 2) stroke it */ g_assert (obj != NULL); if (selvals.approxcircles) { #ifdef DEBUG printf ("Painting ellipse as polygon\n"); #endif /* DEBUG */ d_paint_approx_ellipse (obj); return; } center_pnt = obj->points; if (!center_pnt) return; /* End-of-line */ edge_pnt = center_pnt->next; if (!edge_pnt) { g_error ("Internal error - ellipse no edge pnt"); } bound_wx = abs (center_pnt->pnt.x - edge_pnt->pnt.x)*2; bound_wy = abs (center_pnt->pnt.y - edge_pnt->pnt.y)*2; if (edge_pnt->pnt.x > center_pnt->pnt.x) top_x = 2*center_pnt->pnt.x - edge_pnt->pnt.x; else top_x = edge_pnt->pnt.x; if (edge_pnt->pnt.y > center_pnt->pnt.y) top_y = 2*center_pnt->pnt.y - edge_pnt->pnt.y; else top_y = edge_pnt->pnt.y; dpnts[0] = (gdouble)top_x; dpnts[1] = (gdouble)top_y; dpnts[2] = (gdouble)bound_wx; dpnts[3] = (gdouble)bound_wy; /* Scale before drawing */ if (selvals.scaletoimage) scale_to_original_xy (&dpnts[0], 2); else scale_to_xy (&dpnts[0], 2); gimp_ellipse_select (gfig_image, dpnts[0], dpnts[1], dpnts[2], dpnts[3], selopt.type, selopt.antia, selopt.feather, selopt.feather_radius); /* Is selection all we need ? */ if (selvals.painttype == PAINT_SELECTION_TYPE) return; gimp_edit_stroke (gfig_drawable); gimp_selection_clear (gfig_image); } static Dobject * d_copy_ellipse (Dobject * obj) { Dobject *nc; #if DEBUG printf ("Copy ellipse\n"); #endif /*DEBUG*/ if (!obj) return (NULL); g_assert (obj->type == ELLIPSE); nc = d_new_ellipse (obj->points->pnt.x, obj->points->pnt.y); nc->points->next = d_copy_dobjpoints (obj->points->next); #if DEBUG printf ("Ellipse (%x,%x) to (%x,%x)\n", nc->points->pnt.x, obj->points->pnt.y, nc->points->next->pnt.x, obj->points->next->pnt.y); printf ("Done copy\n"); #endif /*DEBUG*/ return (nc); } static Dobject * d_new_ellipse (gint x, gint y) { Dobject *nobj; DobjPoints *npnt; /* Get new object and starting point */ /* Start point */ npnt = g_new0 (DobjPoints, 1); #if DEBUG printf ("New ellipse start at (%x,%x)\n", x, y); #endif /* DEBUG */ npnt->pnt.x = x; npnt->pnt.y = y; nobj = g_new0 (Dobject, 1); nobj->type = ELLIPSE; nobj->points = npnt; nobj->drawfunc = d_draw_ellipse; nobj->loadfunc = d_load_ellipse; nobj->savefunc = d_save_ellipse; nobj->paintfunc = d_paint_ellipse; nobj->copyfunc = d_copy_ellipse; return (nobj); } static void d_update_ellipse (GdkPoint *pnt) { DobjPoints *center_pnt, *edge_pnt; gint bound_wx; gint bound_wy; gint top_x; gint top_y; /* Undraw last one then draw new one */ center_pnt = obj_creating->points; if (!center_pnt) return; /* No points */ if ((edge_pnt = center_pnt->next)) { /* Undraw current */ bound_wx = abs (center_pnt->pnt.x - edge_pnt->pnt.x)*2; bound_wy = abs (center_pnt->pnt.y - edge_pnt->pnt.y)*2; if (edge_pnt->pnt.x > center_pnt->pnt.x) top_x = 2*center_pnt->pnt.x - edge_pnt->pnt.x; else top_x = edge_pnt->pnt.x; if (edge_pnt->pnt.y > center_pnt->pnt.y) top_y = 2*center_pnt->pnt.y - edge_pnt->pnt.y; else top_y = edge_pnt->pnt.y; draw_circle (&edge_pnt->pnt); gdk_draw_arc (gfig_preview->window, gfig_gc, 0, top_x, top_y, bound_wx, bound_wy, 0, 360*64); } draw_circle (pnt); edge_pnt = g_new0 (DobjPoints, 1); edge_pnt->pnt.x = pnt->x; edge_pnt->pnt.y = pnt->y; bound_wx = abs (center_pnt->pnt.x - edge_pnt->pnt.x)*2; bound_wy = abs (center_pnt->pnt.y - edge_pnt->pnt.y)*2; if (edge_pnt->pnt.x > center_pnt->pnt.x) top_x = 2*center_pnt->pnt.x - edge_pnt->pnt.x; else top_x = edge_pnt->pnt.x; if (edge_pnt->pnt.y > center_pnt->pnt.y) top_y = 2* center_pnt->pnt.y - edge_pnt->pnt.y; else top_y = edge_pnt->pnt.y; gdk_draw_arc (gfig_preview->window, gfig_gc, 0, top_x, top_y, bound_wx, bound_wy, 0, 360*64); center_pnt->next = edge_pnt; } static void d_ellipse_start (GdkPoint *pnt, gint shift_down) { obj_creating = d_new_ellipse (pnt->x, pnt->y); } static void d_ellipse_end (GdkPoint *pnt, gint shift_down) { /* Under contrl point */ if (!obj_creating->points->next) { /* No circle created */ free_one_obj (obj_creating); } else { draw_circle (pnt); add_to_all_obj (current_obj, obj_creating); } obj_creating = NULL; } /* Normal polygon */ static void d_save_poly (Dobject * obj, FILE *to) { DobjPoints * spnt; spnt = obj->points; if (!spnt) return; /* End-of-line */ fprintf (to, "\n"); while (spnt) { fprintf (to, "%d %d\n", (gint)spnt->pnt.x, (gint)spnt->pnt.y); spnt = spnt->next; } fprintf (to, "\n"); fprintf (to, "%d\n\n", (gint)obj->type_data); fprintf (to, "\n"); } /* Load a circle from the specified stream */ static Dobject * d_load_poly (FILE *from) { Dobject *new_obj = NULL; gint xpnt; gint ypnt; gchar buf[MAX_LOAD_LINE]; #ifdef DEBUG printf ("Load poly called\n"); #endif /* DEBUG */ while (get_line (buf, MAX_LOAD_LINE, from, 0)) { if (sscanf (buf, "%d %d", &xpnt, &ypnt) != 2) { /* Must be the end */ if (!strcmp ("", buf)) { gint nsides = 3; /* Number of sides - data item */ if (!new_obj) { g_warning ("[%d] Internal load error while loading poly (extra area)", line_no); return (NULL); } get_line (buf, MAX_LOAD_LINE, from, 0); if (sscanf (buf, "%d", &nsides) != 1) { g_warning ("[%d] Internal load error while loading poly (extra area scanf)", line_no); return (NULL); } new_obj->type_data = (gpointer)nsides; get_line (buf, MAX_LOAD_LINE, from, 0); if (strcmp ("", buf)) { g_warning ("[%d] Internal load error while loading poly", line_no); return (NULL); } /* Go around and read the last line */ continue; } else if (strcmp ("", buf)) { g_warning ("[%d] Internal load error while loading poly", line_no); return (NULL); } return (new_obj); } if (!new_obj) new_obj = d_new_poly (xpnt, ypnt); else d_pnt_add_line (new_obj, xpnt, ypnt, -1); } return (new_obj); } static void d_draw_poly (Dobject *obj) { DobjPoints * center_pnt; DobjPoints * radius_pnt; gint16 shift_x; gint16 shift_y; gdouble ang_grid; gdouble ang_loop; gdouble radius; gdouble offset_angle; gint loop; GdkPoint start_pnt; GdkPoint first_pnt; gint do_line = 0; center_pnt = obj->points; if (!center_pnt) return; /* End-of-line */ /* First point is the center */ /* Just draw a control point around it */ draw_sqr (¢er_pnt->pnt); /* Next point defines the radius */ radius_pnt = center_pnt->next; /* this defines the vertices */ if (!radius_pnt) { #ifdef DEBUG g_warning ("Internal error in polygon - no vertice point \n"); #endif /* DEBUG */ return; } /* Other control point */ draw_sqr (&radius_pnt->pnt); /* Have center and radius - draw polygon */ shift_x = radius_pnt->pnt.x - center_pnt->pnt.x; shift_y = radius_pnt->pnt.y - center_pnt->pnt.y; radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y)); /* Lines */ ang_grid = 2*G_PI/(gdouble) (gint)obj->type_data; offset_angle = atan2 (shift_y, shift_x); for (loop = 0 ; loop < (gint)obj->type_data ; loop++) { gdouble lx, ly; GdkPoint calc_pnt; ang_loop = (gdouble)loop * ang_grid + offset_angle; lx = radius * cos (ang_loop); ly = radius * sin (ang_loop); calc_pnt.x = (gint)RINT (lx + center_pnt->pnt.x); calc_pnt.y = (gint)RINT (ly + center_pnt->pnt.y); if (do_line) { /* Miss out points that come to the same location */ if (calc_pnt.x == start_pnt.x && calc_pnt.y == start_pnt.y) continue; if (drawing_pic) { gdk_draw_line (pic_preview->window, pic_preview->style->black_gc, adjust_pic_coords (calc_pnt.x, preview_width), adjust_pic_coords (calc_pnt.y, preview_height), adjust_pic_coords (start_pnt.x, preview_width), adjust_pic_coords (start_pnt.y, preview_height)); } else { gdk_draw_line (gfig_preview->window, gfig_gc, gfig_scale_x (calc_pnt.x), gfig_scale_y (calc_pnt.y), gfig_scale_x (start_pnt.x), gfig_scale_y (start_pnt.y)); } } else { do_line = 1; first_pnt.x = calc_pnt.x; first_pnt.y = calc_pnt.y; } start_pnt.x = calc_pnt.x; start_pnt.y = calc_pnt.y; } /* Join up */ if (drawing_pic) { gdk_draw_line (pic_preview->window, pic_preview->style->black_gc, adjust_pic_coords (first_pnt.x, preview_width), adjust_pic_coords (first_pnt.y, preview_width), adjust_pic_coords (start_pnt.x, preview_width), adjust_pic_coords (start_pnt.y, preview_width)); } else { gdk_draw_line (gfig_preview->window, gfig_gc, gfig_scale_x (first_pnt.x), gfig_scale_y (first_pnt.y), gfig_scale_x (start_pnt.x), gfig_scale_y (start_pnt.y)); } } static void d_paint_poly (Dobject *obj) { /* first point center */ /* Next point is radius */ gdouble *line_pnts; gint seg_count = 0; gint i = 0; DobjPoints * center_pnt; DobjPoints * radius_pnt; gint16 shift_x; gint16 shift_y; gdouble ang_grid; gdouble ang_loop; gdouble radius; gdouble offset_angle; gint loop; GdkPoint first_pnt, last_pnt; gint first = 1; g_assert (obj != NULL); /* count - add one to close polygon */ seg_count = (gint)obj->type_data + 1; center_pnt = obj->points; if (!center_pnt || !seg_count || !center_pnt->next) return; /* no-line */ line_pnts = g_new0 (gdouble, 2 * seg_count + 1); /* Go around all the points drawing a line from one to the next */ radius_pnt = center_pnt->next; /* this defines the vetices */ /* Have center and radius - get lines */ shift_x = radius_pnt->pnt.x - center_pnt->pnt.x; shift_y = radius_pnt->pnt.y - center_pnt->pnt.y; radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y)); /* Lines */ ang_grid = 2*G_PI/(gdouble) (gint)obj->type_data; offset_angle = atan2 (shift_y, shift_x); for (loop = 0 ; loop < (gint)obj->type_data ; loop++) { gdouble lx, ly; GdkPoint calc_pnt; ang_loop = (gdouble)loop * ang_grid + offset_angle; lx = radius * cos (ang_loop); ly = radius * sin (ang_loop); calc_pnt.x = (gint)RINT (lx + center_pnt->pnt.x); calc_pnt.y = (gint)RINT (ly + center_pnt->pnt.y); /* Miss out duped pnts */ if (!first) { if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y) { continue; } } last_pnt.x = line_pnts[i++] = calc_pnt.x; last_pnt.y = line_pnts[i++] = calc_pnt.y; if (first) { first_pnt.x = calc_pnt.x; first_pnt.y = calc_pnt.y; first = 0; } } line_pnts[i++] = first_pnt.x; line_pnts[i++] = first_pnt.y; /* Reverse line if approp */ if (selvals.reverselines) reverse_pairs_list (&line_pnts[0], i/2); /* Scale before drawing */ if (selvals.scaletoimage) scale_to_original_xy (&line_pnts[0], i/2); else scale_to_xy (&line_pnts[0], i/2); /* One go */ if (selvals.painttype == PAINT_BRUSH_TYPE) { gfig_paint (selvals.brshtype, gfig_drawable, i, line_pnts); } else { gimp_free_select (gfig_image, i, line_pnts, selopt.type, selopt.antia, selopt.feather, selopt.feather_radius); } g_free (line_pnts); } static void d_poly2lines (Dobject *obj) { /* first point center */ /* Next point is radius */ gint seg_count = 0; DobjPoints * center_pnt; DobjPoints * radius_pnt; gint16 shift_x; gint16 shift_y; gdouble ang_grid; gdouble ang_loop; gdouble radius; gdouble offset_angle; gint loop; GdkPoint first_pnt, last_pnt; gint first = 1; g_assert (obj != NULL); #ifdef DEBUG printf ("d_poly2lines --- \n"); #endif /* DEBUG */ /* count - add one to close polygon */ seg_count = (gint)obj->type_data + 1; center_pnt = obj->points; if (!center_pnt) return; /* no-line */ /* Undraw it to start with - removes control points */ obj->drawfunc (obj); /* NULL out these points free later */ obj->points = NULL; /* Go around all the points creating line points */ radius_pnt = center_pnt->next; /* this defines the vertices */ /* Have center and radius - get lines */ shift_x = radius_pnt->pnt.x - center_pnt->pnt.x; shift_y = radius_pnt->pnt.y - center_pnt->pnt.y; radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y)); /* Lines */ ang_grid = 2*G_PI/(gdouble) (gint)obj->type_data; offset_angle = atan2 (shift_y, shift_x); for (loop = 0 ; loop < (gint)obj->type_data ; loop++) { gdouble lx, ly; GdkPoint calc_pnt; ang_loop = (gdouble)loop * ang_grid + offset_angle; lx = radius * cos (ang_loop); ly = radius * sin (ang_loop); calc_pnt.x = (gint)RINT (lx + center_pnt->pnt.x); calc_pnt.y = (gint)RINT (ly + center_pnt->pnt.y); if (!first) { if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y) { continue; } } d_pnt_add_line (obj, calc_pnt.x, calc_pnt.y, 0); last_pnt.x = calc_pnt.x; last_pnt.y = calc_pnt.y; if (first) { first_pnt.x = calc_pnt.x; first_pnt.y = calc_pnt.y; first = 0; } } d_pnt_add_line (obj, first_pnt.x, first_pnt.y, 0); /* Free old pnts */ d_delete_dobjpoints (center_pnt); /* hey we're a line now */ obj->type = LINE; obj->drawfunc = d_draw_line; obj->loadfunc = d_load_line; obj->savefunc = d_save_line; obj->paintfunc = d_paint_line; obj->copyfunc = d_copy_line; /* draw it + control pnts */ obj->drawfunc (obj); } static void d_star2lines (Dobject *obj) { /* first point center */ /* Next point is radius */ gint seg_count = 0; DobjPoints * center_pnt; DobjPoints * outer_radius_pnt; DobjPoints * inner_radius_pnt; gint16 shift_x; gint16 shift_y; gdouble ang_grid; gdouble ang_loop; gdouble outer_radius; gdouble inner_radius; gdouble offset_angle; gint loop; GdkPoint first_pnt, last_pnt; gint first = 1; g_assert (obj != NULL); #ifdef DEBUG printf ("d_star2lines --- \n"); #endif /* DEBUG */ /* count - add one to close polygon */ seg_count = 2*(gint)obj->type_data + 1; center_pnt = obj->points; if (!center_pnt) return; /* no-line */ /* Undraw it to start with - removes control points */ obj->drawfunc (obj); /* NULL out these points free later */ obj->points = NULL; /* Go around all the points creating line points */ /* Next point defines the radius */ outer_radius_pnt = center_pnt->next; /* this defines the vetices */ if (!outer_radius_pnt) { #ifdef DEBUG g_warning ("Internal error in star - no outer vertice point \n"); #endif /* DEBUG */ return; } inner_radius_pnt = outer_radius_pnt->next; /* this defines the vetices */ if (!inner_radius_pnt) { #ifdef DEBUG g_warning ("Internal error in star - no inner vertice point \n"); #endif /* DEBUG */ return; } shift_x = outer_radius_pnt->pnt.x - center_pnt->pnt.x; shift_y = outer_radius_pnt->pnt.y - center_pnt->pnt.y; outer_radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y)); /* Lines */ ang_grid = 2*G_PI/(2.0*(gdouble) (gint)obj->type_data); offset_angle = atan2 (shift_y, shift_x); shift_x = inner_radius_pnt->pnt.x - center_pnt->pnt.x; shift_y = inner_radius_pnt->pnt.y - center_pnt->pnt.y; inner_radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y)); for (loop = 0 ; loop < 2*(gint)obj->type_data ; loop++) { gdouble lx, ly; GdkPoint calc_pnt; ang_loop = (gdouble)loop * ang_grid + offset_angle; if (loop%2) { lx = inner_radius * cos (ang_loop); ly = inner_radius * sin (ang_loop); } else { lx = outer_radius * cos (ang_loop); ly = outer_radius * sin (ang_loop); } calc_pnt.x = (gint)RINT (lx + center_pnt->pnt.x); calc_pnt.y = (gint)RINT (ly + center_pnt->pnt.y); if (!first) { if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y) { continue; } } d_pnt_add_line (obj, calc_pnt.x, calc_pnt.y, 0); last_pnt.x = calc_pnt.x; last_pnt.y = calc_pnt.y; if (first) { first_pnt.x = calc_pnt.x; first_pnt.y = calc_pnt.y; first = 0; } } d_pnt_add_line (obj, first_pnt.x, first_pnt.y, 0); /* Free old pnts */ d_delete_dobjpoints (center_pnt); /* hey we're a line now */ obj->type = LINE; obj->drawfunc = d_draw_line; obj->loadfunc = d_load_line; obj->savefunc = d_save_line; obj->paintfunc = d_paint_line; obj->copyfunc = d_copy_line; /* draw it + control pnts */ obj->drawfunc (obj); } static Dobject * d_copy_poly (Dobject * obj) { Dobject *np; #if DEBUG printf ("Copy poly\n"); #endif /*DEBUG*/ if (!obj) return (NULL); g_assert (obj->type == POLY); np = d_new_poly (obj->points->pnt.x, obj->points->pnt.y); np->points->next = d_copy_dobjpoints (obj->points->next); np->type_data = obj->type_data; #if DEBUG printf ("Done poly copy\n"); #endif /*DEBUG*/ return (np); } static Dobject * d_new_poly (gint x, gint y) { Dobject *nobj; DobjPoints *npnt; /* Get new object and starting point */ /* Start point */ npnt = g_new0 (DobjPoints, 1); #if DEBUG printf ("New POLY start at (%x,%x)\n", x, y); #endif /* DEBUG */ npnt->pnt.x = x; npnt->pnt.y = y; nobj = g_new0 (Dobject, 1); nobj->type = POLY; nobj->type_data = (gpointer)3; /* Default to three sides */ nobj->points = npnt; nobj->drawfunc = d_draw_poly; nobj->loadfunc = d_load_poly; nobj->savefunc = d_save_poly; nobj->paintfunc = d_paint_poly; nobj->copyfunc = d_copy_poly; return (nobj); } static void d_update_poly (GdkPoint *pnt) { DobjPoints *center_pnt, *edge_pnt; gint saved_cnt_pnt = selvals.opts.showcontrol; /* Undraw last one then draw new one */ center_pnt = obj_creating->points; if (!center_pnt) return; /* No points */ /* Leave the first pnt alone - * Edge point defines "radius" * Only undraw if already have edge point. */ /* Hack - turn off cnt points in draw routine * Looking back over the other update routines I could * use this trick again and cut down on code size! */ if ((edge_pnt = center_pnt->next)) { /* Undraw */ draw_circle (&edge_pnt->pnt); selvals.opts.showcontrol = 0; d_draw_poly (obj_creating); edge_pnt->pnt.x = pnt->x; edge_pnt->pnt.y = pnt->y; } else { /* Radius is a few pixels away */ /* First edge point */ d_pnt_add_line (obj_creating, pnt->x, pnt->y, -1); edge_pnt = center_pnt->next; } /* draw it */ selvals.opts.showcontrol = 0; d_draw_poly (obj_creating); selvals.opts.showcontrol = saved_cnt_pnt; /* Realy draw the control points */ draw_circle (&edge_pnt->pnt); } /* first point is center * next defines the radius */ static void d_poly_start (GdkPoint *pnt, gint shift_down) { gint16 x, y; /* First is center point */ obj_creating = d_new_poly (x = pnt->x, y = pnt->y); obj_creating->type_data = (gpointer)poly_num_sides; } static void d_poly_end (GdkPoint *pnt, gint shift_down) { draw_circle (pnt); add_to_all_obj (current_obj, obj_creating); obj_creating = NULL; } /* ARC stuff */ /* Distance between two lines */ static gdouble dist (gdouble x1, gdouble y1, gdouble x2, gdouble y2) { double s1 = x1 - x2; double s2 = y1 - y2; return (sqrt ((s1*s1) + (s2*s2))); } /* Mid point of line returned */ static void mid_point (gdouble x1, gdouble y1, gdouble x2, gdouble y2, gdouble *mx, gdouble *my) { *mx = ((double) (x1 - x2))/2.0 + (double)x2; *my = ((double) (y1 - y2))/2.0 + (double)y2; } /* Careful about infinite grads */ static gdouble line_grad (gdouble x1, gdouble y1, gdouble x2, gdouble y2) { double dx, dy; dx = x1 - x2; dy = y1 - y2; if (dx == 0.0) return (0.0); /* Infinite ! */ return (dy/dx); } /* Constant of line that goes through x, y with grad lgrad */ static gdouble line_cons (gdouble x, gdouble y, gdouble lgrad) { return (y - lgrad*x); } /*Get grad & const for perpend. line to given points */ static void line_definition (gdouble x1, gdouble y1, gdouble x2, gdouble y2, gdouble *lgrad, gdouble *lconst) { double grad1; double midx, midy; grad1 = line_grad (x1, y1, x2, y2); if (grad1 == 0.0) { #ifdef DEBUG printf ("Infinite grad....\n"); #endif /* DEBUG */ return; } mid_point (x1, y1, x2, y2, &midx, &midy); /* Invert grad for perpen gradient */ *lgrad = -1.0/grad1; *lconst = line_cons (midx, midy,*lgrad); } /* Arch details * Given three points get arc radius and the co-ords * of center point. */ static void arc_details (GdkPoint *vert_a, GdkPoint *vert_b, GdkPoint *vert_c, GdkPoint *center_pnt, gdouble *radius) { /* Only vertices are in whole numbers - everything else is in doubles */ double ax, ay; double bx, by; double cx, cy; double len_a, len_b, len_c; double sum_sides2; double area; double circumcircle_R; double line1_grad, line1_const; double line2_grad, line2_const; double inter_x=0.0, inter_y=0.0; int got_x=0, got_y=0; ax = (double) (vert_a->x); ay = (double) (vert_a->y); bx = (double) (vert_b->x); by = (double) (vert_b->y); cx = (double) (vert_c->x); cy = (double) (vert_c->y); #ifdef DEBUG printf ("Vertices (%f,%f), (%f,%f), (%f,%f)\n", ax, ay, bx, by, cx, cy); #endif /* DEBUG */ len_a = dist (ax, ay, bx, by); len_b = dist (bx, by, cx, cy); len_c = dist (cx, cy, ax, ay); #ifdef DEBUG printf ("len_a = %f, len_b = %f, len_c = %f\n", len_a, len_b, len_c); #endif /* DEBUG */ sum_sides2 = (fabs (len_a) + fabs (len_b) + fabs (len_c))/2; #ifdef DEBUG printf ("Sum sides / 2 = %f\n", sum_sides2); #endif /* DEBUG */ /* Area */ area = sqrt (sum_sides2*(sum_sides2 - len_a)*(sum_sides2 - len_b)*(sum_sides2 - len_c)); #ifdef DEBUG printf ("Area of triangle = %f\n", area); #endif /* DEBUG */ /* Circumcircle */ circumcircle_R = len_a*len_b*len_c/(4*area); *radius = circumcircle_R; #ifdef DEBUG printf ("Circumcircle radius = %f\n", circumcircle_R); #endif /* DEBUG */ /* Deal with exceptions - I hate exceptions */ if (ax == bx || ax == cx || cx == bx) { /* vert line -> mid point gives inter_x */ if (ax == bx && bx == cx) { /* Straight line */ double miny = ay; double maxy = ay; if (by > maxy) maxy = by; if (by < miny) miny = by; if (cy > maxy) maxy = cy; if (cy < miny) miny = cy; inter_y = (maxy - miny)/2 + miny; } else if (ax == bx) { inter_y = (ay - by)/2 + by; } else if (bx == cx) { inter_y = (by - cy)/2 + cy; } else { inter_y = (cy - ay)/2 + ay; } got_y = 1; } if (ay == by || by == cy || ay == cy) { /* Horz line -> midpoint gives inter_y */ if (ax == bx && bx == cx) { /* Straight line */ double minx = ax; double maxx = ax; if (bx > maxx) maxx = bx; if (bx < minx) minx = bx; if (cx > maxx) maxx = cx; if (cx < minx) minx = cx; inter_x = (maxx - minx)/2 + minx; } else if (ay == by) { inter_x = (ax - bx)/2 + bx; } else if (by == cy) { inter_x = (bx - cx)/2 + cx; } else { inter_x = (cx - ax)/2 + ax; } got_x = 1; } if (!got_x || !got_y) { /* At least two of the lines are not parallel to the axis */ /*first line */ if (ax != bx && ay != by) line_definition (ax, ay, bx, by, &line1_grad, &line1_const); else line_definition (ax, ay, cx, cy, &line1_grad, &line1_const); /* second line */ if (bx != cx && by != cy) line_definition (bx, by, cx, cy, &line2_grad, &line2_const); else line_definition (ax, ay, cx, cy, &line2_grad, &line2_const); } /* Intersection point */ if (!got_x) inter_x = /*rint*/((line2_const - line1_const)/(line1_grad - line2_grad)); if (!got_y) inter_y = /*rint*/((line1_grad * inter_x + line1_const)); #ifdef DEBUG printf ("Intersection point is (%f,%f)\n", inter_x, inter_y); #endif /* DEBUG */ center_pnt->x = (gint16)inter_x; center_pnt->y = (gint16)inter_y; } static gdouble arc_angle (GdkPoint *pnt, GdkPoint *center) { /* Get angle (in degress) of point given origin of center */ gint16 shift_x; gint16 shift_y; gdouble offset_angle; shift_x = pnt->x - center->x; shift_y = -pnt->y + center->y; offset_angle = atan2 (shift_y, shift_x); #ifdef DEBUG printf ("offset_ang = %f\n", offset_angle); #endif /* DEBUG */ if (offset_angle < 0) offset_angle += 2*G_PI; return (offset_angle*360/(2*G_PI)); } static void d_save_arc (Dobject *obj, FILE *to) { DobjPoints * spnt; spnt = obj->points; if (!spnt) return; fprintf (to, "\n"); while (spnt) { fprintf (to, "%d %d\n", (gint)spnt->pnt.x, (gint)spnt->pnt.y); spnt = spnt->next; } fprintf (to, "\n"); } /* Load a circle from the specified stream */ static Dobject * d_load_arc (FILE *from) { Dobject *new_obj = NULL; gint xpnt; gint ypnt; gchar buf[MAX_LOAD_LINE]; gint num_pnts = 0; #ifdef DEBUG printf ("Load arc called\n"); #endif /* DEBUG */ while (get_line (buf, MAX_LOAD_LINE, from, 0)) { if (sscanf (buf, "%d %d", &xpnt, &ypnt) != 2) { /* Must be the end */ if (strcmp ("", buf) || num_pnts != 3) { g_warning ("[%d] Internal load error while loading arc", line_no); return (NULL); } return (new_obj); } num_pnts++; if (!new_obj) new_obj = d_new_arc (xpnt, ypnt); else { d_pnt_add_line (new_obj, xpnt, ypnt,-1); } } g_warning ("[%d] Not enough points for arc", line_no); return (NULL); } static void arc_drawing_details (Dobject *obj, gdouble *minang, GdkPoint *center_pnt, gdouble *arcang, gdouble *radius, gint draw_cnts, gint do_scale) { DobjPoints * pnt1 = NULL; DobjPoints * pnt2 = NULL; DobjPoints * pnt3 = NULL; DobjPoints dpnts[3]; gdouble ang1, ang2, ang3; gdouble maxang; pnt1 = obj->points; if (!pnt1) return; /* Not fully drawn */ pnt2 = pnt1->next; if (!pnt2) return; /* Not fully drawn */ pnt3 = pnt2->next; if (!pnt3) return; /* Still not fully drawn */ if (draw_cnts) { draw_sqr (&pnt1->pnt); draw_sqr (&pnt2->pnt); draw_sqr (&pnt3->pnt); } if (do_scale) { /* Adjust pnts for scaling */ /* Warning struct copies here! and casting to double <-> int */ /* Too complex fix me - to much hacking */ gdouble xy[2]; int j; dpnts[0] = *pnt1; dpnts[1] = *pnt2; dpnts[2] = *pnt3; pnt1 = &dpnts[0]; pnt2 = &dpnts[1]; pnt3 = &dpnts[2]; for (j = 0 ; j < 3; j++) { xy[0] = dpnts[j].pnt.x; xy[1] = dpnts[j].pnt.y; if (selvals.scaletoimage) scale_to_original_xy (&xy[0], 1); else scale_to_xy (&xy[0], 1); dpnts[j].pnt.x = xy[0]; dpnts[j].pnt.y = xy[1]; } } arc_details (&pnt1->pnt, &pnt2->pnt, &pnt3->pnt, center_pnt, radius); ang1 = arc_angle (&pnt1->pnt, center_pnt); ang2 = arc_angle (&pnt2->pnt, center_pnt); ang3 = arc_angle (&pnt3->pnt, center_pnt); /* Find min/max angle */ maxang = ang1; if (ang3 > maxang) maxang = ang3; *minang = ang1; if (ang3 < *minang) *minang = ang3; if (ang2 > *minang && ang2 < maxang) *arcang = maxang - *minang; else *arcang = maxang - *minang - 360; } static void d_draw_arc (Dobject * obj) { GdkPoint center_pnt; gdouble radius, minang, arcang; g_assert (obj != NULL); if (!obj) return; arc_drawing_details (obj, &minang, ¢er_pnt, &arcang, &radius, TRUE, FALSE); #ifdef DEBUG printf ("Min ang = %f Arc ang = %f\n", minang, arcang); #endif /* DEBUG */ if (drawing_pic) { gdk_draw_arc (pic_preview->window, pic_preview->style->black_gc, 0, adjust_pic_coords (center_pnt.x - (gint)radius, preview_width), adjust_pic_coords (center_pnt.y - (gint)radius, preview_height), adjust_pic_coords ((gint) (radius * 2), preview_width), adjust_pic_coords ((gint) (radius * 2), preview_height), (gint) (minang*64), (gint) (arcang*64)); } else { gdk_draw_arc (gfig_preview->window, gfig_gc, 0, gfig_scale_x (center_pnt.x - (gint)radius), gfig_scale_y (center_pnt.y - (gint)radius), gfig_scale_x ((gint) (radius * 2)), gfig_scale_y ((gint) (radius * 2)), (gint) (minang*64), (gint) (arcang*64)); } } static void d_paint_arc (Dobject *obj) { /* first point center */ /* Next point is radius */ gdouble *line_pnts; gint seg_count = 0; gint i = 0; gdouble ang_grid; gdouble ang_loop; gdouble radius; gint loop; GdkPoint first_pnt, last_pnt; gint first = 1; GdkPoint center_pnt; gdouble minang, arcang; g_assert (obj != NULL); if (!obj) return; /* No cnt pnts & must scale */ arc_drawing_details (obj, &minang, ¢er_pnt, &arcang, &radius, FALSE, TRUE); #ifdef DEBUG printf ("Paint Min ang = %f Arc ang = %f\n", minang, arcang); #endif /* DEBUG */ seg_count = 360; /* Should make a smoth-ish curve */ /* +3 because we MIGHT do pie selection */ line_pnts = g_new0 (gdouble, 2 * seg_count + 3); /* Lines */ ang_grid = 2*G_PI/(gdouble)360; if (arcang < 0.0) { /* Swap - since we always draw anti-clock wise */ minang += arcang; arcang = -arcang; } minang = minang * (2*G_PI/360); /* min ang is in degrees - need in rads*/ for (loop = 0 ; loop < abs ((gint)arcang) ; loop++) { gdouble lx, ly; GdkPoint calc_pnt; ang_loop = (gdouble)loop * ang_grid + minang; lx = radius * cos (ang_loop); ly = -radius * sin (ang_loop); /* y grows down screen and angs measured from x clockwise */ calc_pnt.x = (gint)RINT (lx + center_pnt.x); calc_pnt.y = (gint)RINT (ly + center_pnt.y); /* Miss out duped pnts */ if (!first) { if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y) { continue; } } last_pnt.x = line_pnts[i++] = calc_pnt.x; last_pnt.y = line_pnts[i++] = calc_pnt.y; if (first) { first_pnt.x = calc_pnt.x; first_pnt.y = calc_pnt.y; first = 0; } } /* Reverse line if approp */ if (selvals.reverselines) reverse_pairs_list (&line_pnts[0], i/2); /* One go */ if (selvals.painttype == PAINT_BRUSH_TYPE) { gfig_paint (selvals.brshtype, gfig_drawable, i, line_pnts); } else { if (selopt.as_pie) { /* Add center point - cause a pie like selection... */ line_pnts[i++] = center_pnt.x; line_pnts[i++] = center_pnt.y; } gimp_free_select (gfig_image, i, line_pnts, selopt.type, selopt.antia, selopt.feather, selopt.feather_radius); } g_free (line_pnts); } static Dobject * d_copy_arc (Dobject * obj) { Dobject *nc; #if DEBUG printf ("Copy ellipse\n"); #endif /*DEBUG*/ if (!obj) return (NULL); g_assert (obj->type == ARC); nc = d_new_arc (obj->points->pnt.x, obj->points->pnt.y); nc->points->next = d_copy_dobjpoints (obj->points->next); #if DEBUG printf ("Arc (%x,%x), (%x,%x), (%x,%x)\n", nc->points->pnt.x, obj->points->pnt.y, nc->points->next->pnt.x, obj->points->next->pnt.y, nc->points->next->next->pnt.x, obj->points->next->next->pnt.y); printf ("Done copy\n"); #endif /*DEBUG*/ return (nc); } static Dobject * d_new_arc (gint x, gint y) { Dobject *nobj; DobjPoints *npnt; /* Get new object and starting point */ /* Start point */ npnt = g_new0 (DobjPoints, 1); #if DEBUG printf ("New arc start at (%x,%x)\n", x, y); #endif /* DEBUG */ npnt->pnt.x = x; npnt->pnt.y = y; nobj = g_new0 (Dobject, 1); nobj->type = ARC; nobj->points = npnt; nobj->drawfunc = d_draw_arc; nobj->loadfunc = d_load_arc; nobj->savefunc = d_save_arc; nobj->paintfunc = d_paint_arc; nobj->copyfunc = d_copy_arc; return (nobj); } static void d_update_arc (GdkPoint *pnt) { DobjPoints * pnt1 = NULL; DobjPoints * pnt2 = NULL; DobjPoints * pnt3 = NULL; /* First two points as line only become arch when third * point is placed on canvas. */ pnt1 = obj_creating->points; if (!pnt1 || !(pnt2 = pnt1->next) || !(pnt3 = pnt2->next)) { d_update_line (pnt); return; /* Not fully drawn */ } /* Update a real curve */ /* Nothing to be done ... */ } static void d_arc_start (GdkPoint *pnt, gint shift_down) { /* Draw lines to start with -- then convert to an arc */ if (!tmp_line) draw_sqr (pnt); d_line_start (pnt, TRUE); /* TRUE means multiple pointed line */ } static void d_arc_end (GdkPoint *pnt, gint shift_down) { /* Under contrl point */ if (!tmp_line || !tmp_line->points || !tmp_line->points->next) { /* No arc created - yet */ /* Must have three points */ #ifdef DEBUG printf ("No arc created yet\n"); #endif /* DEBUG */ d_line_end (pnt, TRUE); } else { /* Complete arc */ /* Convert to an arc ... */ tmp_line->type = ARC; tmp_line->drawfunc = d_draw_arc; tmp_line->loadfunc = d_load_arc; tmp_line->savefunc = d_save_arc; tmp_line->paintfunc = d_paint_arc; tmp_line->copyfunc = d_copy_arc; d_line_end (pnt, FALSE); /*d_draw_line (newarc); Should undraw line */ if (need_to_scale) { selvals.scaletoimage = 0; } /*d_draw_arc (newarc);*/ update_draw_area (gfig_preview, NULL); if (need_to_scale) { selvals.scaletoimage = 1; } } } /*XXXXXXXXXXXXXXXXXXXXXXX*/ /* Star shape */ static void d_save_star (Dobject *obj, FILE *to) { DobjPoints * spnt; spnt = obj->points; if (!spnt) return; /* End-of-line */ fprintf (to, "\n"); while (spnt) { fprintf (to, "%d %d\n", (gint)spnt->pnt.x, (gint)spnt->pnt.y); spnt = spnt->next; } fprintf (to, "\n"); fprintf (to, "%d\n\n", (gint)obj->type_data); fprintf (to, "\n"); } /* Load a circle from the specified stream */ static Dobject * d_load_star (FILE *from) { Dobject *new_obj = NULL; gint xpnt; gint ypnt; gchar buf[MAX_LOAD_LINE]; #ifdef DEBUG printf ("Load star called\n"); #endif /* DEBUG */ while (get_line (buf, MAX_LOAD_LINE, from, 0)) { if (sscanf (buf, "%d %d", &xpnt, &ypnt) != 2) { /* Must be the end */ if (!strcmp ("", buf)) { gint nsides = 3; /* Number of sides - data item */ if (!new_obj) { g_warning ("[%d] Internal load error while loading star (extra area)", line_no); return (NULL); } get_line (buf, MAX_LOAD_LINE, from, 0); if (sscanf (buf, "%d", &nsides) != 1) { g_warning ("[%d] Internal load error while loading star (extra area scanf)", line_no); return (NULL); } new_obj->type_data = (gpointer)nsides; get_line (buf, MAX_LOAD_LINE, from, 0); if (strcmp ("", buf)) { g_warning ("[%d] Internal load error while loading star", line_no); return (NULL); } /* Go around and read the last line */ continue; } else if (strcmp ("", buf)) { g_warning ("[%d] Internal load error while loading star", line_no); return (NULL); } return (new_obj); } if (!new_obj) new_obj = d_new_star (xpnt, ypnt); else d_pnt_add_line (new_obj, xpnt, ypnt,-1); } return (new_obj); } static void d_draw_star (Dobject *obj) { DobjPoints * center_pnt; DobjPoints * outer_radius_pnt; DobjPoints * inner_radius_pnt; gint16 shift_x; gint16 shift_y; gdouble ang_grid; gdouble ang_loop; gdouble outer_radius; gdouble inner_radius; gdouble offset_angle; gint loop; GdkPoint start_pnt; GdkPoint first_pnt; gint do_line = 0; center_pnt = obj->points; if (!center_pnt) return; /* End-of-line */ /* First point is the center */ /* Just draw a control point around it */ draw_sqr (¢er_pnt->pnt); /* Next point defines the radius */ outer_radius_pnt = center_pnt->next; /* this defines the vetices */ if (!outer_radius_pnt) { #ifdef DEBUG g_warning ("Internal error in star - no outer vertice point \n"); #endif /* DEBUG */ return; } inner_radius_pnt = outer_radius_pnt->next; /* this defines the vetices */ if (!inner_radius_pnt) { #ifdef DEBUG g_warning ("Internal error in star - no inner vertice point \n"); #endif /* DEBUG */ return; } /* Other control points */ draw_sqr (&outer_radius_pnt->pnt); draw_sqr (&inner_radius_pnt->pnt); /* Have center and radius - draw star */ shift_x = outer_radius_pnt->pnt.x - center_pnt->pnt.x; shift_y = outer_radius_pnt->pnt.y - center_pnt->pnt.y; outer_radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y)); /* Lines */ ang_grid = 2*G_PI/(2.0*(gdouble) (gint)obj->type_data); offset_angle = atan2 (shift_y, shift_x); shift_x = inner_radius_pnt->pnt.x - center_pnt->pnt.x; shift_y = inner_radius_pnt->pnt.y - center_pnt->pnt.y; inner_radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y)); for (loop = 0 ; loop < 2*(gint)obj->type_data ; loop++) { gdouble lx, ly; GdkPoint calc_pnt; ang_loop = (gdouble)loop * ang_grid + offset_angle; if (loop%2) { lx = inner_radius * cos (ang_loop); ly = inner_radius * sin (ang_loop); } else { lx = outer_radius * cos (ang_loop); ly = outer_radius * sin (ang_loop); } calc_pnt.x = (gint)RINT (lx + center_pnt->pnt.x); calc_pnt.y = (gint)RINT (ly + center_pnt->pnt.y); if (do_line) { /* Miss out points that come to the same location */ if (calc_pnt.x == start_pnt.x && calc_pnt.y == start_pnt.y) continue; if (drawing_pic) { gdk_draw_line (pic_preview->window, pic_preview->style->black_gc, adjust_pic_coords (calc_pnt.x, preview_width), adjust_pic_coords (calc_pnt.y, preview_height), adjust_pic_coords (start_pnt.x, preview_width), adjust_pic_coords (start_pnt.y, preview_height)); } else { gdk_draw_line (gfig_preview->window, gfig_gc, gfig_scale_x (calc_pnt.x), gfig_scale_y (calc_pnt.y), gfig_scale_x (start_pnt.x), gfig_scale_y (start_pnt.y)); } } else { do_line = 1; first_pnt.x = calc_pnt.x; first_pnt.y = calc_pnt.y; } start_pnt.x = calc_pnt.x; start_pnt.y = calc_pnt.y; } /* Join up */ if (drawing_pic) { gdk_draw_line (pic_preview->window, pic_preview->style->black_gc, adjust_pic_coords (first_pnt.x, preview_width), adjust_pic_coords (first_pnt.y, preview_width), adjust_pic_coords (start_pnt.x, preview_width), adjust_pic_coords (start_pnt.y, preview_width)); } else { gdk_draw_line (gfig_preview->window, gfig_gc, gfig_scale_x (first_pnt.x), gfig_scale_y (first_pnt.y), gfig_scale_x (start_pnt.x), gfig_scale_y (start_pnt.y)); } } static void d_paint_star (Dobject *obj) { /* first point center */ /* Next point is radius */ gdouble *line_pnts; gint seg_count = 0; gint i = 0; DobjPoints * center_pnt; DobjPoints * outer_radius_pnt; DobjPoints * inner_radius_pnt; gint16 shift_x; gint16 shift_y; gdouble ang_grid; gdouble ang_loop; gdouble outer_radius; gdouble inner_radius; gdouble offset_angle; gint loop; GdkPoint first_pnt, last_pnt; gint first = 1; g_assert (obj != NULL); /* count - add one to close polygon */ seg_count = 2*(gint)obj->type_data + 1; center_pnt = obj->points; if (!center_pnt || !seg_count) return; /* no-line */ line_pnts = g_new0 (gdouble, 2 * seg_count + 1); /* Go around all the points drawing a line from one to the next */ /* Next point defines the radius */ outer_radius_pnt = center_pnt->next; /* this defines the vetices */ if (!outer_radius_pnt) { #ifdef DEBUG g_warning ("Internal error in star - no outer vertice point \n"); #endif /* DEBUG */ return; } inner_radius_pnt = outer_radius_pnt->next; /* this defines the vetices */ if (!inner_radius_pnt) { #ifdef DEBUG g_warning ("Internal error in star - no inner vertice point \n"); #endif /* DEBUG */ return; } shift_x = outer_radius_pnt->pnt.x - center_pnt->pnt.x; shift_y = outer_radius_pnt->pnt.y - center_pnt->pnt.y; outer_radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y)); /* Lines */ ang_grid = 2*G_PI/(2.0*(gdouble) (gint)obj->type_data); offset_angle = atan2 (shift_y, shift_x); shift_x = inner_radius_pnt->pnt.x - center_pnt->pnt.x; shift_y = inner_radius_pnt->pnt.y - center_pnt->pnt.y; inner_radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y)); for (loop = 0 ; loop < 2*(gint)obj->type_data ; loop++) { gdouble lx, ly; GdkPoint calc_pnt; ang_loop = (gdouble)loop * ang_grid + offset_angle; if (loop%2) { lx = inner_radius * cos (ang_loop); ly = inner_radius * sin (ang_loop); } else { lx = outer_radius * cos (ang_loop); ly = outer_radius * sin (ang_loop); } calc_pnt.x = (gint)RINT (lx + center_pnt->pnt.x); calc_pnt.y = (gint)RINT (ly + center_pnt->pnt.y); /* Miss out duped pnts */ if (!first) { if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y) { continue; } } last_pnt.x = line_pnts[i++] = calc_pnt.x; last_pnt.y = line_pnts[i++] = calc_pnt.y; if (first) { first_pnt.x = calc_pnt.x; first_pnt.y = calc_pnt.y; first = 0; } } line_pnts[i++] = first_pnt.x; line_pnts[i++] = first_pnt.y; /* Reverse line if approp */ if (selvals.reverselines) reverse_pairs_list (&line_pnts[0], i/2); /* Scale before drawing */ if (selvals.scaletoimage) scale_to_original_xy (&line_pnts[0], i/2); else scale_to_xy (&line_pnts[0], i/2); /* One go */ if (selvals.painttype == PAINT_BRUSH_TYPE) { gfig_paint (selvals.brshtype, gfig_drawable, i, line_pnts); } else { gimp_free_select (gfig_image, i, line_pnts, selopt.type, selopt.antia, selopt.feather, selopt.feather_radius); } g_free (line_pnts); } static Dobject * d_copy_star (Dobject * obj) { Dobject *np; #if DEBUG printf ("Copy star\n"); #endif /*DEBUG*/ if (!obj) return (NULL); g_assert (obj->type == STAR); np = d_new_star (obj->points->pnt.x, obj->points->pnt.y); np->points->next = d_copy_dobjpoints (obj->points->next); np->type_data = obj->type_data; #if DEBUG printf ("Done star copy\n"); #endif /*DEBUG*/ return (np); } static Dobject * d_new_star (gint x, gint y) { Dobject *nobj; DobjPoints *npnt; /* Get new object and starting point */ /* Start point */ npnt = g_new0 (DobjPoints, 1); #if DEBUG printf ("New STAR start at (%x,%x)\n", x, y); #endif /* DEBUG */ npnt->pnt.x = x; npnt->pnt.y = y; nobj = g_new0 (Dobject, 1); nobj->type = STAR; nobj->type_data = (gpointer)3; /* Default to three sides 6 points*/ nobj->points = npnt; nobj->drawfunc = d_draw_star; nobj->loadfunc = d_load_star; nobj->savefunc = d_save_star; nobj->paintfunc = d_paint_star; nobj->copyfunc = d_copy_star; return (nobj); } static void d_update_star (GdkPoint *pnt) { DobjPoints *center_pnt, *inner_pnt, *outer_pnt; gint saved_cnt_pnt = selvals.opts.showcontrol; /* Undraw last one then draw new one */ center_pnt = obj_creating->points; if (!center_pnt) return; /* No points */ /* Leave the first pnt alone - * Edge point defines "radius" * Only undraw if already have edge point. */ /* Hack - turn off cnt points in draw routine * Looking back over the other update routines I could * use this trick again and cut down on code size! */ if ((outer_pnt = center_pnt->next)) { /* Undraw */ inner_pnt = outer_pnt->next; draw_circle (&inner_pnt->pnt); draw_circle (&outer_pnt->pnt); selvals.opts.showcontrol = 0; d_draw_star (obj_creating); outer_pnt->pnt.x = pnt->x; outer_pnt->pnt.y = pnt->y; inner_pnt->pnt.x = pnt->x + (2*(center_pnt->pnt.x - pnt->x))/3; inner_pnt->pnt.y = pnt->y + (2*(center_pnt->pnt.y - pnt->y))/3; } else { /* Radius is a few pixels away */ /* First edge point */ d_pnt_add_line (obj_creating, pnt->x, pnt->y,-1); outer_pnt = center_pnt->next; /* Inner radius */ d_pnt_add_line (obj_creating, pnt->x + (2*(center_pnt->pnt.x - pnt->x))/3, pnt->y + (2*(center_pnt->pnt.y - pnt->y))/3, -1); inner_pnt = outer_pnt->next; } /* draw it */ selvals.opts.showcontrol = 0; d_draw_star (obj_creating); selvals.opts.showcontrol = saved_cnt_pnt; /* Realy draw the control points */ draw_circle (&outer_pnt->pnt); draw_circle (&inner_pnt->pnt); } /* first point is center * next defines the radius */ static void d_star_start (GdkPoint *pnt, gint shift_down) { gint16 x, y; /* First is center point */ obj_creating = d_new_star (x = pnt->x, y = pnt->y); obj_creating->type_data = (gpointer)star_num_sides; } static void d_star_end (GdkPoint *pnt, gint shift_down) { draw_circle (pnt); add_to_all_obj (current_obj, obj_creating); obj_creating = NULL; } /* Spiral */ static void d_save_spiral (Dobject *obj, FILE *to) { DobjPoints * spnt; spnt = obj->points; if (!spnt) return; /* End-of-line */ fprintf (to, "\n"); while (spnt) { fprintf (to, "%d %d\n", (gint)spnt->pnt.x, (gint)spnt->pnt.y); spnt = spnt->next; } fprintf (to, "\n"); fprintf (to, "%d\n\n", (gint)obj->type_data); fprintf (to, "\n"); } /* Load a spiral from the specified stream */ static Dobject * d_load_spiral (FILE *from) { Dobject *new_obj = NULL; gint xpnt; gint ypnt; gchar buf[MAX_LOAD_LINE]; #ifdef DEBUG printf ("Load spiral called\n"); #endif /* DEBUG */ while (get_line (buf, MAX_LOAD_LINE, from, 0)) { if (sscanf (buf, "%d %d", &xpnt, &ypnt) != 2) { /* Must be the end */ if (!strcmp ("", buf)) { gint nsides = 3; /* Number of sides - data item */ if (!new_obj) { g_warning ("[%d] Internal load error while loading spiral (extra area)", line_no); return (NULL); } get_line (buf, MAX_LOAD_LINE, from, 0); if (sscanf (buf, "%d", &nsides) != 1) { g_warning ("[%d] Internal load error while loading spiral (extra area scanf)", line_no); return (NULL); } new_obj->type_data = (gpointer)nsides; get_line (buf, MAX_LOAD_LINE, from, 0); if (strcmp ("", buf)) { g_warning ("[%d] Internal load error while loading spiral", line_no); return (NULL); } /* Go around and read the last line */ continue; } else if (strcmp ("", buf)) { g_warning ("[%d] Internal load error while loading spiral", line_no); return (NULL); } return (new_obj); } if (!new_obj) new_obj = d_new_spiral (xpnt, ypnt); else d_pnt_add_line (new_obj, xpnt, ypnt,-1); } return (new_obj); } static void d_draw_spiral (Dobject *obj) { DobjPoints * center_pnt; DobjPoints * radius_pnt; gint16 shift_x; gint16 shift_y; gdouble ang_grid; gdouble ang_loop; gdouble radius; gdouble offset_angle; gdouble sp_cons; gint loop; GdkPoint start_pnt; GdkPoint first_pnt; gint do_line = 0; gint clock_wise = 1; center_pnt = obj->points; if (!center_pnt) return; /* End-of-line */ /* First point is the center */ /* Just draw a control point around it */ draw_sqr (¢er_pnt->pnt); /* Next point defines the radius */ radius_pnt = center_pnt->next; /* this defines the vetices */ if (!radius_pnt) { #ifdef DEBUG g_warning ("Internal error in spiral - no vertice point \n"); #endif /* DEBUG */ return; } /* Other control point */ draw_sqr (&radius_pnt->pnt); /* Have center and radius - draw spiral */ shift_x = radius_pnt->pnt.x - center_pnt->pnt.x; shift_y = radius_pnt->pnt.y - center_pnt->pnt.y; radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y)); offset_angle = atan2 (shift_y, shift_x); clock_wise = ((gint)obj->type_data)/(abs ((gint) (obj->type_data))); if (offset_angle < 0) offset_angle += 2*G_PI; sp_cons = radius/((gint)obj->type_data * 2 * G_PI + offset_angle); /* Lines */ ang_grid = 2.0*G_PI/(gdouble)180; for (loop = 0 ; loop <= abs ((gint) (obj->type_data)*180) + clock_wise*(gint)RINT (offset_angle/ang_grid) ; loop++) { gdouble lx, ly; GdkPoint calc_pnt; ang_loop = (gdouble)loop * ang_grid; lx = sp_cons * ang_loop * cos (ang_loop)*clock_wise; ly = sp_cons * ang_loop * sin (ang_loop); calc_pnt.x = (gint)RINT (lx + center_pnt->pnt.x); calc_pnt.y = (gint)RINT (ly + center_pnt->pnt.y); if (do_line) { /* Miss out points that come to the same location */ if (calc_pnt.x == start_pnt.x && calc_pnt.y == start_pnt.y) continue; if (drawing_pic) { gdk_draw_line (pic_preview->window, pic_preview->style->black_gc, adjust_pic_coords (calc_pnt.x, preview_width), adjust_pic_coords (calc_pnt.y, preview_height), adjust_pic_coords (start_pnt.x, preview_width), adjust_pic_coords (start_pnt.y, preview_height)); } else { gdk_draw_line (gfig_preview->window, gfig_gc, gfig_scale_x (calc_pnt.x), gfig_scale_y (calc_pnt.y), gfig_scale_x (start_pnt.x), gfig_scale_y (start_pnt.y)); } } else { do_line = 1; first_pnt.x = calc_pnt.x; first_pnt.y = calc_pnt.y; } start_pnt.x = calc_pnt.x; start_pnt.y = calc_pnt.y; } } static void d_paint_spiral (Dobject *obj) { /* first point center */ /* Next point is radius */ gdouble *line_pnts; gint seg_count = 0; gint i = 0; DobjPoints * center_pnt; DobjPoints * radius_pnt; gint16 shift_x; gint16 shift_y; gdouble ang_grid; gdouble ang_loop; gdouble radius; gdouble offset_angle; gdouble sp_cons; gint loop; GdkPoint last_pnt; gint clock_wise = 1; g_assert (obj != NULL); center_pnt = obj->points; if (!center_pnt || !center_pnt->next) return; /* no-line */ /* Go around all the points drawing a line from one to the next */ radius_pnt = center_pnt->next; /* this defines the vetices */ /* Have center and radius - get lines */ shift_x = radius_pnt->pnt.x - center_pnt->pnt.x; shift_y = radius_pnt->pnt.y - center_pnt->pnt.y; radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y)); clock_wise = ((gint)obj->type_data)/(abs ((gint) (obj->type_data))); offset_angle = atan2 (shift_y, shift_x); if (offset_angle < 0) offset_angle += 2*G_PI; sp_cons = radius/((gint)obj->type_data * 2 * G_PI + offset_angle); /* Lines */ ang_grid = 2.0*G_PI/(gdouble)180; /* count - */ seg_count = abs ((gint) (obj->type_data)*180) + clock_wise*(gint)RINT (offset_angle/ang_grid); line_pnts = g_new0 (gdouble, 2 * seg_count + 3); for (loop = 0 ; loop <= seg_count; loop++) { gdouble lx, ly; GdkPoint calc_pnt; ang_loop = (gdouble)loop * ang_grid; lx = sp_cons * ang_loop * cos (ang_loop)*clock_wise; ly = sp_cons * ang_loop * sin (ang_loop); calc_pnt.x = (gint)RINT (lx + center_pnt->pnt.x); calc_pnt.y = (gint)RINT (ly + center_pnt->pnt.y); /* Miss out duped pnts */ if (!loop) { if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y) { continue; } } last_pnt.x = line_pnts[i++] = calc_pnt.x; last_pnt.y = line_pnts[i++] = calc_pnt.y; } /* Reverse line if approp */ if (selvals.reverselines) reverse_pairs_list (&line_pnts[0], i/2); /* Scale before drawing */ if (selvals.scaletoimage) scale_to_original_xy (&line_pnts[0], i/2); else scale_to_xy (&line_pnts[0], i/2); /* One go */ if (selvals.painttype == PAINT_BRUSH_TYPE) { gfig_paint (selvals.brshtype, gfig_drawable, i, line_pnts); } else { gimp_free_select (gfig_image, i, line_pnts, selopt.type, selopt.antia, selopt.feather, selopt.feather_radius); } g_free (line_pnts); } static Dobject * d_copy_spiral (Dobject * obj) { Dobject *np; #if DEBUG printf ("Copy spiral\n"); #endif /*DEBUG*/ if (!obj) return (NULL); g_assert (obj->type == SPIRAL); np = d_new_spiral (obj->points->pnt.x, obj->points->pnt.y); np->points->next = d_copy_dobjpoints (obj->points->next); np->type_data = obj->type_data; #if DEBUG printf ("Done spiral copy\n"); #endif /*DEBUG*/ return (np); } static Dobject * d_new_spiral (gint x, gint y) { Dobject *nobj; DobjPoints *npnt; /* Get new object and starting point */ /* Start point */ npnt = g_new0 (DobjPoints, 1); #if DEBUG printf ("New SPIRAL start at (%x,%x)\n", x, y); #endif /* DEBUG */ npnt->pnt.x = x; npnt->pnt.y = y; nobj = g_new0 (Dobject, 1); nobj->type = SPIRAL; nobj->type_data = (gpointer)4; /* Default to four turns */ nobj->points = npnt; nobj->drawfunc = d_draw_spiral; nobj->loadfunc = d_load_spiral; nobj->savefunc = d_save_spiral; nobj->paintfunc = d_paint_spiral; nobj->copyfunc = d_copy_spiral; return (nobj); } static void d_update_spiral (GdkPoint *pnt) { DobjPoints *center_pnt, *edge_pnt; gint saved_cnt_pnt = selvals.opts.showcontrol; /* Undraw last one then draw new one */ center_pnt = obj_creating->points; if (!center_pnt) return; /* No points */ /* Leave the first pnt alone - * Edge point defines "radius" * Only undraw if already have edge point. */ /* Hack - turn off cnt points in draw routine * Looking back over the other update routines I could * use this trick again and cut down on code size! */ if ((edge_pnt = center_pnt->next)) { /* Undraw */ draw_circle (&edge_pnt->pnt); selvals.opts.showcontrol = 0; d_draw_spiral (obj_creating); edge_pnt->pnt.x = pnt->x; edge_pnt->pnt.y = pnt->y; } else { /* Radius is a few pixels away */ /* First edge point */ d_pnt_add_line (obj_creating, pnt->x, pnt->y, -1); edge_pnt = center_pnt->next; } /* draw it */ selvals.opts.showcontrol = 0; d_draw_spiral (obj_creating); selvals.opts.showcontrol = saved_cnt_pnt; /* Realy draw the control points */ draw_circle (&edge_pnt->pnt); } /* first point is center * next defines the radius */ static void d_spiral_start (GdkPoint *pnt, gint shift_down) { gint16 x, y; /* First is center point */ obj_creating = d_new_spiral (x = pnt->x, y = pnt->y); obj_creating->type_data = (gpointer) (spiral_num_turns * ((spiral_toggle == 0) ? 1 : -1)); } static void d_spiral_end (GdkPoint *pnt, gint shift_down) { draw_circle (pnt); add_to_all_obj (current_obj, obj_creating); obj_creating = NULL; } /* Stuff for bezier curves... */ static void d_save_bezier (Dobject *obj, FILE *to) { DobjPoints *spnt; spnt = obj->points; if (!spnt) return; /* End-of-line */ fprintf (to, "\n"); while (spnt) { fprintf (to, "%d %d\n", (gint)spnt->pnt.x, (gint)spnt->pnt.y); spnt = spnt->next; } fprintf (to, "\n"); fprintf (to, "%d\n\n", (gint) obj->type_data); fprintf (to, "\n"); } /* Load a bezier from the specified stream */ static Dobject * d_load_bezier (FILE *from) { Dobject *new_obj = NULL; gint xpnt; gint ypnt; gchar buf[MAX_LOAD_LINE]; #ifdef DEBUG printf ("Load bezier called\n"); #endif /* DEBUG */ while (get_line (buf, MAX_LOAD_LINE, from, 0)) { if (sscanf (buf, "%d %d", &xpnt, &ypnt) != 2) { /* Must be the end */ if (!strcmp ("", buf)) { gint nsides = 3; /* Number of sides - data item */ if ( !new_obj) { g_message ("[%d] Internal load error while loading bezier " "(extra area)", line_no); return NULL; } get_line (buf, MAX_LOAD_LINE, from, 0); if (sscanf (buf, "%d", &nsides) != 1) { g_message ("[%d] Internal load error while loading bezier " "(extra area scanf)", line_no); return NULL; } new_obj->type_data = (gpointer) nsides; get_line (buf, MAX_LOAD_LINE, from, 0); if (strcmp ("", buf)) { g_message ("[%d] Internal load error while loading bezier", line_no); return NULL; } /* Go around and read the last line */ continue; } else if (strcmp ("", buf)) { g_message ("[%d] Internal load error while loading bezier", line_no); return NULL; } return new_obj; } if (!new_obj) new_obj = d_new_bezier (xpnt, ypnt); else d_pnt_add_line (new_obj, xpnt, ypnt, -1); } return new_obj; } #define FP_PNT_MAX 10 static int fp_pnt_cnt = 0; static int fp_pnt_chunk = 0; static gdouble *fp_pnt_pnts = NULL; static void fp_pnt_start (void) { fp_pnt_cnt = 0; } /* Add a line segment to collection array */ static void fp_pnt_add (gdouble p1, gdouble p2, gdouble p3, gdouble p4) { if (!fp_pnt_pnts) { fp_pnt_pnts = g_new0 (gdouble, FP_PNT_MAX); fp_pnt_chunk = 1; } if (((fp_pnt_cnt + 4) / FP_PNT_MAX) >= fp_pnt_chunk) { /* more space pls */ fp_pnt_chunk++; fp_pnt_pnts = (gdouble *) g_realloc (fp_pnt_pnts, sizeof (gdouble) * fp_pnt_chunk * FP_PNT_MAX); } fp_pnt_pnts[fp_pnt_cnt++] = p1; fp_pnt_pnts[fp_pnt_cnt++] = p2; fp_pnt_pnts[fp_pnt_cnt++] = p3; fp_pnt_pnts[fp_pnt_cnt++] = p4; } static gdouble * d_bz_get_array (gint *sz) { *sz = fp_pnt_cnt; return fp_pnt_pnts; } static void d_bz_line (void) { gint i, x0, y0, x1, y1; g_assert ((fp_pnt_cnt % 4) == 0); for (i = 0 ; i < fp_pnt_cnt; i += 4) { x0 = (gint) fp_pnt_pnts[i]; y0 = (gint) fp_pnt_pnts[i + 1]; x1 = (gint) fp_pnt_pnts[i + 2]; y1 = (gint) fp_pnt_pnts[i + 3]; if (drawing_pic) { gdk_draw_line (pic_preview->window, pic_preview->style->black_gc, adjust_pic_coords ((gint) x0, preview_width), adjust_pic_coords ((gint) y0, preview_height), adjust_pic_coords ((gint) x1, preview_width), adjust_pic_coords ((gint) y1, preview_height)); } else { gdk_draw_line (gfig_preview->window, gfig_gc, gfig_scale_x ((gint) x0), gfig_scale_y ((gint) y0), gfig_scale_x ((gint) x1), gfig_scale_y ((gint) y1)); } } } /* Return points to plot */ /* Terminate by point with DBL_MAX, DBL_MAX */ typedef gdouble (*fp_pnt)[2]; static void DrawBezier (gdouble (*points)[2], gint np, gdouble mid, gint depth) { gint i, j, x0 = 0, y0 = 0, x1, y1; fp_pnt left; fp_pnt right; if (depth == 0) /* draw polyline */ { for (i = 0; i < np; i++) { x1 = (int) points[i][0]; y1 = (int) points[i][1]; if (i > 0 && (x1 != x0 || y1 != y0)) { /* Add pnts up */ fp_pnt_add ((gdouble) x0, (gdouble) y0, (gdouble) x1, (gdouble) y1); } x0 = x1; y0 = y1; } } else /* subdivide control points at mid */ { left = (fp_pnt) g_new (gdouble, np * 2); right = (fp_pnt) g_new (gdouble, np * 2); for (i = 0; i < np; i++) { right[i][0] = points[i][0]; right[i][1] = points[i][1]; } left[0][0] = right[0][0]; left[0][1] = right[0][1]; for (j = np - 1; j >= 1; j--) { for (i = 0; i < j; i++) { right[i][0] = (1 - mid) * right[i][0] + mid * right[i + 1][0]; right[i][1] = (1 - mid) * right[i][1] + mid * right[i + 1][1]; } left[np - j][0] = right[0][0]; left[np - j][1] = right[0][1]; } if (depth > 0) { DrawBezier (left, np, mid, depth - 1); DrawBezier (right, np, mid, depth - 1); g_free (left); g_free (right); } } } static void d_draw_bezier (Dobject *obj) { DobjPoints * spnt; gint seg_count = 0; gint i = 0; gdouble (*line_pnts)[2]; spnt = obj->points; /* First count the number of points */ /* count */ while (spnt) { seg_count++; spnt = spnt->next; } spnt = obj->points; if (!spnt || !seg_count) return; /* no-line */ line_pnts = (fp_pnt) g_new0 (gdouble, 2 * seg_count + 1); /* Go around all the points drawing a line from one to the next */ while (spnt) { draw_sqr (&spnt->pnt); line_pnts[i][0] = spnt->pnt.x; line_pnts[i++][1] = spnt->pnt.y; spnt = spnt->next; } /* Generate an array of doubles which are the control points */ if (!drawing_pic && bezier_line_frame && tmp_bezier) { fp_pnt_start (); DrawBezier (line_pnts, seg_count, 0.5, 0); d_bz_line (); } fp_pnt_start (); DrawBezier (line_pnts, seg_count, 0.5, 3); d_bz_line (); /*bezier4 (line_pnts, seg_count, 20);*/ g_free (line_pnts); } static void d_paint_bezier (Dobject *obj) { gdouble *line_pnts; gdouble (*bz_line_pnts)[2]; DobjPoints *spnt; gint seg_count = 0; gint i = 0; spnt = obj->points; /* First count the number of points */ /* count */ while (spnt) { seg_count++; spnt = spnt->next; } spnt = obj->points; if (!spnt || !seg_count) return; /* no-line */ bz_line_pnts = (fp_pnt) g_new0 (gdouble, 2 * seg_count + 1); /* Go around all the points drawing a line from one to the next */ while (spnt) { bz_line_pnts[i][0] = spnt->pnt.x; bz_line_pnts[i++][1] = spnt->pnt.y; spnt = spnt->next; } fp_pnt_start (); DrawBezier (bz_line_pnts, seg_count, 0.5, 5); line_pnts = d_bz_get_array (&i); /* Reverse line if approp */ if (selvals.reverselines) reverse_pairs_list (&line_pnts[0], i / 2); /* Scale before drawing */ if (selvals.scaletoimage) scale_to_original_xy (&line_pnts[0], i / 2); else scale_to_xy (&line_pnts[0], i / 2); /* One go */ if (selvals.painttype == PAINT_BRUSH_TYPE) { gfig_paint (selvals.brshtype, gfig_drawable, i, line_pnts); } else { gimp_free_select (gfig_image, i, line_pnts, selopt.type, selopt.antia, selopt.feather, selopt.feather_radius); } g_free (bz_line_pnts); /* Don't free line_pnts - may need again */ } static Dobject * d_copy_bezier (Dobject * obj) { Dobject *np; #if DEBUG printf ("Copy bezier\n"); #endif /*DEBUG*/ if (!obj) return (NULL); g_assert (obj->type == BEZIER); np = d_new_bezier (obj->points->pnt.x, obj->points->pnt.y); np->points->next = d_copy_dobjpoints (obj->points->next); np->type_data = obj->type_data; #if DEBUG printf ("Done bezier copy\n"); #endif /*DEBUG*/ return np; } static Dobject * d_new_bezier (gint x, gint y) { Dobject *nobj; DobjPoints *npnt; /* Get new object and starting point */ /* Start point */ npnt = g_new0 (DobjPoints, 1); #if DEBUG printf ("New BEZIER start at (%x,%x)\n", x, y); #endif /* DEBUG */ npnt->pnt.x = x; npnt->pnt.y = y; nobj = g_new0 (Dobject, 1); nobj->type = BEZIER; nobj->type_data = (gpointer)4; /* Default to four turns */ nobj->points = npnt; nobj->drawfunc = d_draw_bezier; nobj->loadfunc = d_load_bezier; nobj->savefunc = d_save_bezier; nobj->paintfunc = d_paint_bezier; nobj->copyfunc = d_copy_bezier; return (nobj); } static void d_update_bezier (GdkPoint *pnt) { DobjPoints *s_pnt, *l_pnt; gint saved_cnt_pnt = selvals.opts.showcontrol; g_assert (tmp_bezier != NULL); /* Undraw last one then draw new one */ s_pnt = tmp_bezier->points; if (!s_pnt) return; /* No points */ /* Hack - turn off cnt points in draw routine */ if ((l_pnt = s_pnt->next)) { /* Undraw */ while (l_pnt->next) { l_pnt = l_pnt->next; } draw_circle (&l_pnt->pnt); selvals.opts.showcontrol = 0; d_draw_bezier (tmp_bezier); l_pnt->pnt.x = pnt->x; l_pnt->pnt.y = pnt->y; } else { /* Radius is a few pixels away */ /* First edge point */ d_pnt_add_line (tmp_bezier, pnt->x, pnt->y,-1); l_pnt = s_pnt->next; } /* draw it */ selvals.opts.showcontrol = 0; d_draw_bezier (tmp_bezier); selvals.opts.showcontrol = saved_cnt_pnt; /* Realy draw the control points */ draw_circle (&l_pnt->pnt); } /* first point is center * next defines the radius */ static void d_bezier_start (GdkPoint *pnt, gint shift_down) { gint16 x, y; /* First is center point */ if (!tmp_bezier) { /* New curve */ tmp_bezier = obj_creating = d_new_bezier (x = pnt->x, y = pnt->y); } } static void d_bezier_end (GdkPoint *pnt, gint shift_down) { DobjPoints *l_pnt; if (!tmp_bezier) { tmp_bezier = obj_creating; } l_pnt = tmp_bezier->points->next; if (!l_pnt) return; if (shift_down) { /* Undraw circle on last pnt */ while (l_pnt->next) { l_pnt = l_pnt->next; } if (l_pnt) { draw_circle (&l_pnt->pnt); draw_sqr (&l_pnt->pnt); if (bezier_closed) { gint tmp_frame = bezier_line_frame; /* if closed then add first point */ d_draw_bezier (tmp_bezier); d_pnt_add_line (tmp_bezier, tmp_bezier->points->pnt.x, tmp_bezier->points->pnt.y,-1); /* Final has no frame */ bezier_line_frame = 0; /* False */ d_draw_bezier (tmp_bezier); bezier_line_frame = tmp_frame; /* What is was */ } else if (bezier_line_frame) { d_draw_bezier (tmp_bezier); bezier_line_frame = 0; /* False */ d_draw_bezier (tmp_bezier); bezier_line_frame = 1; /* What is was */ } add_to_all_obj (current_obj, obj_creating); } /* small mem leak if !l_pnt ? */ tmp_bezier = NULL; obj_creating = NULL; } else { if (!tmp_bezier->points->next) { draw_circle (&tmp_bezier->points->pnt); draw_sqr (&tmp_bezier->points->pnt); } d_draw_bezier (tmp_bezier); d_pnt_add_line (tmp_bezier, pnt->x, pnt->y,-1); d_draw_bezier (tmp_bezier); } } /* copy objs */ static DAllObjs * copy_all_objs (DAllObjs *objs) { DAllObjs *nobj; DAllObjs *new_all_objs = NULL; DAllObjs *ret = NULL; while (objs) { nobj = g_new0 (DAllObjs, 1); if (!ret) { ret = new_all_objs = nobj; } else { new_all_objs->next = nobj; new_all_objs = nobj; } nobj->obj = (Dobject *) objs->obj->copyfunc (objs->obj); objs = objs->next; } return ret; } /* Screen refresh */ static void draw_one_obj (Dobject * obj) { obj->drawfunc (obj); } static void draw_objects (DAllObjs *objs, gint show_single) { /* Show_single - only one object to draw Unless shift * is down in whcih case show all. */ gint count = 0; while (objs) { if (!show_single || count == obj_show_single || obj_show_single == -1) draw_one_obj (objs->obj); objs = objs->next; count++; } } static void prepend_to_all_obj (GFigObj *fobj, DAllObjs *nobj) { DAllObjs *cobj; setup_undo (); /* Remember ME */ if (!fobj->obj_list) { fobj->obj_list = nobj; return; } cobj = fobj->obj_list; while (cobj->next) { cobj = cobj->next; } cobj->next = nobj; } static void add_to_all_obj (GFigObj *fobj, Dobject *obj) { DAllObjs *nobj; nobj = g_new0 (DAllObjs, 1); nobj->obj = obj; if (need_to_scale) scale_obj_points (obj->points, scale_x_factor, scale_y_factor); prepend_to_all_obj (fobj, nobj); } static void object_operation_start (GdkPoint *pnt, gint shift_down) { Dobject *new_obj; /* Find point in given object list */ operation_obj = get_nearest_objs (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_malloc0 (sizeof (*move_all_pnt)); *move_all_pnt = *pnt; /* Structure copy */ setup_undo (); return; } if (!operation_obj) return;/* None to work on */ setup_undo (); switch (selvals.otype) { case MOVE_OBJ: if (operation_obj->type == BEZIER) { d_draw_bezier (operation_obj); tmp_bezier = operation_obj; d_draw_bezier (operation_obj); } break; case MOVE_POINT: if (operation_obj->type == BEZIER) { d_draw_bezier (operation_obj); tmp_bezier = operation_obj; d_draw_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 lookin at */ scan_obj_points (operation_obj->points, pnt); } break; case COPY_OBJ: /* Copy the "operation object" */ /* Then bung us into "copy/move" mode */ #ifdef DEBUG printf ("In copy obj\n"); #endif /* DEBUG */ new_obj = (Dobject *) operation_obj->copyfunc (operation_obj); if (new_obj) { scan_obj_points (new_obj->points, pnt); add_to_all_obj (current_obj, new_obj); operation_obj = new_obj; selvals.otype = MOVE_COPY_OBJ; new_obj->drawfunc (new_obj); } break; case DEL_OBJ: remove_obj_from_list (current_obj, operation_obj); break; case MOVE_COPY_OBJ: /* Never when button down */ default: g_warning ("Internal error selvals.otype object operation start"); break; } } static void object_operation_end (GdkPoint *pnt, gint shift_down) { if (selvals.otype != DEL_OBJ && operation_obj && operation_obj->type == BEZIER) { d_draw_bezier (operation_obj); tmp_bezier = NULL; /* use as switch */ d_draw_bezier (operation_obj); } operation_obj = NULL; if (move_all_pnt) { g_free (move_all_pnt); move_all_pnt = 0; } /* 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 */ static void object_operation (GdkPoint *to_pnt, gint shift_down) { /* Must do diffent 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 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 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: break; case COPY_OBJ: /* Should have been changed to MOVE_COPY_OBJ */ default: g_warning ("Internal error selvals.otype"); break; } } /* First button press -- start drawing object */ static void object_start (GdkPoint *pnt, gint 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 */ if (!shift_down || !obj_creating) draw_sqr (pnt); d_line_start (pnt, shift_down); break; case CIRCLE: draw_sqr (pnt); d_circle_start (pnt, shift_down); break; case ELLIPSE: draw_sqr (pnt); d_ellipse_start (pnt, shift_down); break; case POLY: draw_sqr (pnt); d_poly_start (pnt, shift_down); break; case ARC: d_arc_start (pnt, shift_down); break; case STAR: draw_sqr (pnt); d_star_start (pnt, shift_down); break; case SPIRAL: draw_sqr (pnt); d_spiral_start (pnt, shift_down); break; case BEZIER: if (!tmp_bezier) draw_sqr (pnt); d_bezier_start (pnt, shift_down); break; default: /* Internal error */ break; } } /* Real object now !*/ static void object_end (GdkPoint *pnt, gint 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); draw_sqr (pnt); break; case CIRCLE: draw_sqr (pnt); d_circle_end (pnt, shift_down); break; case ELLIPSE: draw_sqr (pnt); d_ellipse_end (pnt, shift_down); break; case POLY: draw_sqr (pnt); d_poly_end (pnt, shift_down); break; case STAR: draw_sqr (pnt); d_star_end (pnt, shift_down); break; case ARC: draw_sqr (pnt); d_arc_end (pnt, shift_down); break; case SPIRAL: draw_sqr (pnt); 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; } } static void object_update (GdkPoint *pnt) { /* update for the current object */ /* New position xy */ switch (selvals.otype) { case LINE: d_update_line (pnt); break; case CIRCLE: d_update_circle (pnt); break; case ELLIPSE: d_update_ellipse (pnt); break; case POLY: d_update_poly (pnt); break; case STAR: d_update_star (pnt); break; case ARC: d_update_arc (pnt); break; case SPIRAL: d_update_spiral (pnt); break; case BEZIER: d_update_bezier (pnt); break; default: /* Internal error */ break; } }