2003-08-31 Sven Neumann <sven@gimp.org> * plug-ins/gfig/gfig.c: applied an old patch from Carl Adams that was updated for 1.3 by Tino Schwarze. The patch fixes the isometric grid so that its mathematical properties can be exploited.
10989 lines
256 KiB
C
10989 lines
256 KiB
C
/*
|
|
* 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
|
|
*
|
|
* 1.4 Fixed isometric grid so that its mathematical properties can be
|
|
* exploited. Close wasn't good enough.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#ifdef __GNUC__
|
|
#warning GTK_DISABLE_DEPRECATED
|
|
#endif
|
|
#undef GTK_DISABLE_DEPRECATED
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#ifdef G_OS_WIN32
|
|
# include <io.h>
|
|
# ifndef W_OK
|
|
# define W_OK 2
|
|
# endif
|
|
#endif
|
|
|
|
#include <libgimp/gimp.h>
|
|
#include <libgimp/gimpui.h>
|
|
|
|
#include "libgimp/stdplugins-intl.h"
|
|
|
|
#include "gfig-stock.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"
|
|
|
|
/* For the isometric grid */
|
|
#define SQRT3 1.73205080756887729353 /* Square root of 3 */
|
|
#define SIN_1o6PI_RAD 0.5 /* Sine 1/6 Pi Radians */
|
|
#define COS_1o6PI_RAD SQRT3 / 2 /* Cosine 1/6 Pi Radians */
|
|
#define TAN_1o6PI_RAD 1 / SQRT3 /* Tangent 1/6 Pi Radians == SIN / COS */
|
|
#define RECIP_TAN_1o6PI_RAD SQRT3 /* Reciprocal of Tangent 1/6 Pi Radians */
|
|
|
|
#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 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 (const gchar *name,
|
|
gint nparams,
|
|
const 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 gboolean pic_preview_expose (GtkWidget *widget,
|
|
GdkEvent *event);
|
|
static void gfig_preview_realize (GtkWidget *widget);
|
|
static gboolean gfig_preview_expose (GtkWidget *widget,
|
|
GdkEvent *event);
|
|
static gboolean 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 (void);
|
|
static void draw_grid_clear (void);
|
|
static void toggle_show_image (void);
|
|
static void toggle_obj_type (GtkWidget *widget,
|
|
gpointer data);
|
|
|
|
static void gfig_new_gc (void);
|
|
static void find_grid_pos (GdkPoint *p,
|
|
GdkPoint *gp,
|
|
guint state);
|
|
|
|
|
|
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;
|
|
gboolean drawgrid;
|
|
gboolean snap2grid;
|
|
gboolean lockongrid;
|
|
gboolean 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;
|
|
gboolean showimage;
|
|
gint maxundo;
|
|
gboolean showpos;
|
|
gdouble brushfade;
|
|
gdouble brushgradient;
|
|
gdouble airbrushpressure;
|
|
DrawonLayers onlayers;
|
|
LayersBGType onlayerbg;
|
|
PaintType painttype;
|
|
gboolean reverselines;
|
|
gboolean scaletoimage;
|
|
gdouble scaletoimagefp;
|
|
gboolean 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 */
|
|
FALSE, /* drawgrid */
|
|
FALSE, /* snap2grid */
|
|
FALSE, /* lockongrid */
|
|
TRUE /* show control points */
|
|
},
|
|
FALSE, /* show image */
|
|
MIN_UNDO + (MAX_UNDO - MIN_UNDO)/2, /* Max level of undos */
|
|
TRUE, /* Show pos updates */
|
|
0.0, /* Brush fade */
|
|
0.0, /* Brush gradient */
|
|
20.0, /* Air bursh pressure */
|
|
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,
|
|
REPLACE,
|
|
INTERSECT
|
|
} SelectionType;
|
|
|
|
|
|
typedef enum
|
|
{
|
|
ARC_SEGMENT = 0,
|
|
ARC_SECTOR
|
|
} ArcType;
|
|
|
|
typedef enum
|
|
{
|
|
FILL_FOREGROUND = 0,
|
|
FILL_BACKGROUND,
|
|
FILL_PATTERN
|
|
} 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 gchar *gfig_path = 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? */
|
|
gint 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 *name;
|
|
gdouble opacity;
|
|
gint spacing;
|
|
GimpLayerModeEffects paint_mode;
|
|
gint width;
|
|
gint height;
|
|
guchar *pv_buf; /* Buffer where brush placed */
|
|
gint16 x_off;
|
|
gint16 y_off;
|
|
const gchar *popup;
|
|
} 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 *object_list; /* 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);
|
|
|
|
static void brush_list_button_callback (BrushDesc *bdesc);
|
|
|
|
|
|
/* 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_("<Image>/Filters/Render/_Gfig..."),
|
|
"RGB*, GRAY*",
|
|
GIMP_PLUGIN,
|
|
G_N_ELEMENTS (args), 0,
|
|
args, NULL);
|
|
}
|
|
|
|
static void
|
|
run (const gchar *name,
|
|
gint nparams,
|
|
const 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);*/
|
|
|
|
INIT_I18N ();
|
|
|
|
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 */
|
|
if (! gimp_selection_is_empty (gfig_image))
|
|
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);*/
|
|
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);
|
|
}
|
|
|
|
/*
|
|
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,
|
|
const gchar *src)
|
|
{
|
|
gint cnt = MAX_LOAD_LINE - 1;
|
|
guint 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;
|
|
}
|
|
|
|
/*
|
|
* Insert gfigs in alphabetical order
|
|
*/
|
|
|
|
static gint
|
|
gfig_list_insert (GFigObj *gfig)
|
|
{
|
|
gint n;
|
|
|
|
n = gfig_list_pos (gfig);
|
|
|
|
gfig_list = g_list_insert (gfig_list, gfig, n);
|
|
|
|
return n;
|
|
}
|
|
|
|
static void
|
|
gfig_free (GFigObj *gfig)
|
|
{
|
|
g_assert (gfig != NULL);
|
|
|
|
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)
|
|
{
|
|
remove (gfig->filename);
|
|
}
|
|
gfig_free (gfig);
|
|
}
|
|
|
|
static void
|
|
gfig_list_free_all (void)
|
|
{
|
|
g_list_foreach (gfig_list, (GFunc) gfig_free, NULL);
|
|
g_list_free (gfig_list);
|
|
gfig_list = NULL;
|
|
}
|
|
|
|
static void
|
|
gfig_list_load_one (const GimpDatafileData *file_data,
|
|
gpointer user_data)
|
|
{
|
|
GFigObj *gfig;
|
|
|
|
gfig = gfig_load (file_data->filename, file_data->basename);
|
|
|
|
if (gfig)
|
|
{
|
|
/* Read only ?*/
|
|
if (access (file_data->filename, W_OK))
|
|
gfig->obj_status |= GFIG_READONLY;
|
|
|
|
gfig_list_insert (gfig);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gfig_list_load_all (const gchar *path)
|
|
{
|
|
/* Make sure to clear any existing gfigs */
|
|
current_obj = pic_obj = NULL;
|
|
gfig_list_free_all ();
|
|
|
|
gimp_datafiles_read_directories (path, G_FILE_TEST_EXISTS,
|
|
gfig_list_load_one,
|
|
NULL);
|
|
|
|
if (! gfig_list)
|
|
{
|
|
GFigObj *gfig;
|
|
|
|
/* 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)
|
|
{
|
|
return g_new0 (GFigObj, 1);
|
|
}
|
|
|
|
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, "<LINE>"))
|
|
{
|
|
obj = d_load_line (fp);
|
|
}
|
|
else if (!strcmp (load_buf, "<CIRCLE>"))
|
|
{
|
|
obj = d_load_circle (fp);
|
|
}
|
|
else if (!strcmp (load_buf, "<ELLIPSE>"))
|
|
{
|
|
obj = d_load_ellipse (fp);
|
|
}
|
|
else if (!strcmp (load_buf, "<POLY>"))
|
|
{
|
|
obj = d_load_poly (fp);
|
|
}
|
|
else if (!strcmp (load_buf, "<STAR>"))
|
|
{
|
|
obj = d_load_star (fp);
|
|
}
|
|
else if (!strcmp (load_buf, "<SPIRAL>"))
|
|
{
|
|
obj = d_load_spiral (fp);
|
|
}
|
|
else if (!strcmp (load_buf, "<BEZIER>"))
|
|
{
|
|
obj = d_load_bezier (fp);
|
|
}
|
|
else if (!strcmp (load_buf, "<ARC>"))
|
|
{
|
|
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, "<OPTIONS>\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, "</OPTIONS>\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)))),
|
|
GINT_TO_POINTER (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, "<OPTIONS>"))
|
|
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, "</OPTIONS>"))
|
|
{
|
|
/* 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;
|
|
|
|
for (; objs; objs = objs->next)
|
|
count++;
|
|
|
|
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_strdup_printf (_("Error opening file '%s':\n%s"),
|
|
savename, g_strerror (errno));
|
|
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 = g_object_get_data (G_OBJECT (fs), "user_data");
|
|
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);
|
|
|
|
g_signal_connect (window, "destroy",
|
|
G_CALLBACK (gtk_widget_destroyed),
|
|
&window);
|
|
|
|
g_object_set_data (G_OBJECT (window), "user_data", obj);
|
|
g_signal_connect (GTK_FILE_SELECTION (window)->ok_button, "clicked",
|
|
G_CALLBACK (file_selection_ok),
|
|
window);
|
|
g_signal_connect_swapped (GTK_FILE_SELECTION (window)->cancel_button,
|
|
"clicked",
|
|
G_CALLBACK (gtk_widget_destroy),
|
|
window);
|
|
}
|
|
|
|
if (tpath)
|
|
{
|
|
gtk_file_selection_set_filename (GTK_FILE_SELECTION (window), tpath);
|
|
}
|
|
else if (gfig_path)
|
|
{
|
|
GList *list;
|
|
gchar *dir;
|
|
|
|
list = gimp_path_parse (gfig_path, 16, FALSE, 0);
|
|
dir = gimp_path_get_user_writable_dir (list);
|
|
gimp_path_free (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);
|
|
}
|
|
|
|
gtk_window_present (GTK_WINDOW (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 */
|
|
static void * xxx;
|
|
|
|
/* 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;
|
|
gint y, x;
|
|
guchar *src_rows;
|
|
guchar *p;
|
|
gint 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;
|
|
}
|
|
|
|
isgrey = gimp_drawable_is_gray (gfig_select_drawable->drawable_id);
|
|
|
|
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 */
|
|
gint 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, GINT_TO_POINTER (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 void
|
|
select_button_clicked (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
gint type = GPOINTER_TO_INT (data);
|
|
gint count = 0;
|
|
DAllObjs *objs;
|
|
|
|
if (current_obj)
|
|
{
|
|
for (objs = current_obj->obj_list; 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 ();
|
|
}
|
|
|
|
static GtkWidget *
|
|
obj_select_buttons (void)
|
|
{
|
|
GtkWidget *button;
|
|
GtkWidget *image;
|
|
GtkWidget *hbox, *vbox;
|
|
|
|
vbox = gtk_vbox_new (TRUE, 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 ();
|
|
gimp_help_set_help_data (button, _("Show previous object"), NULL);
|
|
gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (select_button_clicked),
|
|
GINT_TO_POINTER (OBJ_SELECT_LT));
|
|
gtk_widget_show (button);
|
|
|
|
image = gtk_image_new_from_stock (GTK_STOCK_GO_BACK,
|
|
GTK_ICON_SIZE_BUTTON);
|
|
gtk_container_add (GTK_CONTAINER (button), image);
|
|
gtk_widget_show (image);
|
|
|
|
button = gtk_button_new ();
|
|
gimp_help_set_help_data (button, _("Show next object"), NULL);
|
|
gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (select_button_clicked),
|
|
GINT_TO_POINTER (OBJ_SELECT_GT));
|
|
gtk_widget_show (button);
|
|
|
|
image = gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD,
|
|
GTK_ICON_SIZE_BUTTON);
|
|
gtk_container_add (GTK_CONTAINER (button), image);
|
|
gtk_widget_show (image);
|
|
|
|
button = gtk_button_new_with_label (_("All"));
|
|
gimp_help_set_help_data (button, _("Show all objects"), NULL);
|
|
gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (select_button_clicked),
|
|
GINT_TO_POINTER (OBJ_SELECT_EQ));
|
|
gtk_widget_show (button);
|
|
|
|
return vbox;
|
|
}
|
|
|
|
static GtkWidget *
|
|
but_with_pix (const gchar *stock_id,
|
|
GSList **group,
|
|
gint baction)
|
|
{
|
|
GtkWidget *button;
|
|
|
|
button = gtk_radio_button_new_with_label (*group, stock_id);
|
|
gtk_button_set_use_stock (GTK_BUTTON (button), TRUE);
|
|
gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE);
|
|
g_signal_connect (button, "toggled",
|
|
G_CALLBACK (toggle_obj_type),
|
|
GINT_TO_POINTER (baction));
|
|
gtk_widget_show (button);
|
|
|
|
*group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
|
|
|
|
return button;
|
|
}
|
|
|
|
static GtkWidget *
|
|
small_preview (void)
|
|
{
|
|
GtkWidget *frame;
|
|
GtkWidget *vbox;
|
|
gint y;
|
|
|
|
vbox = gtk_vbox_new (FALSE, 0);
|
|
gtk_widget_show (vbox);
|
|
|
|
frame = gtk_frame_new (NULL);
|
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
|
|
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
|
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, 255, SMALL_PREVIEW_SZ * 3);
|
|
gtk_preview_draw_row (GTK_PREVIEW (pic_preview), prow,
|
|
0, y, SMALL_PREVIEW_SZ);
|
|
}
|
|
|
|
g_signal_connect_after (pic_preview, "expose_event",
|
|
G_CALLBACK (pic_preview_expose),
|
|
NULL);
|
|
|
|
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);
|
|
g_signal_connect (size_data, "value_changed",
|
|
G_CALLBACK (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, GINT_TO_POINTER (*which_way),
|
|
|
|
_("Clockwise"), GINT_TO_POINTER (0), NULL,
|
|
_("Anti-Clockwise"), GINT_TO_POINTER (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)
|
|
{
|
|
gtk_window_present (GTK_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);
|
|
|
|
g_signal_connect (window, "destroy",
|
|
G_CALLBACK (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"));
|
|
g_signal_connect (toggle, "toggled",
|
|
G_CALLBACK (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"));
|
|
g_signal_connect (toggle, "toggled",
|
|
G_CALLBACK (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 *button;
|
|
GtkWidget *vbox;
|
|
GSList *group = NULL;
|
|
|
|
/* Create group */
|
|
vbox = gtk_vbox_new (FALSE, 0);
|
|
|
|
/* Put buttons in */
|
|
button = but_with_pix (GFIG_STOCK_LINE, &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 (GFIG_STOCK_CIRCLE, &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 (GFIG_STOCK_ELLIPSE, &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 (GFIG_STOCK_CURVE, &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 (GFIG_STOCK_POLYGON, &group, POLY);
|
|
gtk_container_add (GTK_CONTAINER (vbox), button);
|
|
gtk_widget_show (button);
|
|
|
|
g_signal_connect (button, "button_press_event",
|
|
G_CALLBACK (poly_button_press),
|
|
NULL);
|
|
gimp_help_set_help_data (button, _("Create reg polygon"), NULL);
|
|
|
|
button = but_with_pix (GFIG_STOCK_STAR, &group, STAR);
|
|
gtk_container_add (GTK_CONTAINER (vbox), button);
|
|
gtk_widget_show (button);
|
|
g_signal_connect (button, "button_press_event",
|
|
G_CALLBACK (star_button_press),
|
|
NULL);
|
|
gimp_help_set_help_data (button, _("Create star"), NULL);
|
|
|
|
button = but_with_pix (GFIG_STOCK_SPIRAL, &group, SPIRAL);
|
|
gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
|
|
gtk_widget_show (button);
|
|
|
|
g_signal_connect (button, "button_press_event",
|
|
G_CALLBACK (spiral_button_press),
|
|
NULL);
|
|
gimp_help_set_help_data (button, _("Create spiral"), NULL);
|
|
|
|
button = but_with_pix (GFIG_STOCK_BEZIER, &group, BEZIER);
|
|
gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
|
|
gtk_widget_show (button);
|
|
g_signal_connect (button, "button_press_event",
|
|
G_CALLBACK (bezier_button_press),
|
|
NULL);
|
|
|
|
gimp_help_set_help_data (button,
|
|
_("Create bezier curve. "
|
|
"Shift + Button ends object creation."), NULL);
|
|
|
|
button = but_with_pix (GFIG_STOCK_MOVE_OBJECT, &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 (GFIG_STOCK_MOVE_POINT, &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 (GFIG_STOCK_COPY_OBJECT, &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 (GFIG_STOCK_DELETE_OBJECT, &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);
|
|
|
|
gtk_widget_show (vbox);
|
|
|
|
return vbox;
|
|
}
|
|
|
|
/* 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_queue_draw (widget);
|
|
point.x = mevent->x;
|
|
point.y = mevent->y;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gfig_brush_update_preview (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
BrushDesc *bdesc = g_object_get_data (G_OBJECT (data), "brush-desc");
|
|
|
|
brush_list_button_callback (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);
|
|
g_signal_connect (*pv, "event",
|
|
G_CALLBACK (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, GINT_TO_POINTER (selvals.brshtype),
|
|
|
|
_("Brush"), GINT_TO_POINTER (BRUSH_BRUSH_TYPE), NULL,
|
|
_("Airbrush"), GINT_TO_POINTER (BRUSH_AIRBRUSH_TYPE), NULL,
|
|
_("Pencil"), GINT_TO_POINTER (BRUSH_PENCIL_TYPE), NULL,
|
|
_("Pattern"), GINT_TO_POINTER (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 *) g_object_get_data (G_OBJECT (pw),
|
|
"brush-desc");
|
|
|
|
/* 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 * 3
|
|
+ (bdesc->width
|
|
* 3 * (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;
|
|
|
|
g_free (bdesc->pv_buf); /* Free old area */
|
|
|
|
brushdrawable = gimp_drawable_get (layer_ID);
|
|
|
|
/* 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 * 3);
|
|
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 *name)
|
|
{
|
|
GimpParam *return_vals;
|
|
int nreturn_vals;
|
|
|
|
return_vals = gimp_run_procedure ("gimp_brushes_set_brush",
|
|
&nreturn_vals,
|
|
GIMP_PDB_STRING, name,
|
|
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;
|
|
gint nreturn_vals;
|
|
gchar *name = NULL;
|
|
|
|
return_vals = gimp_run_procedure ("gimp_brushes_get_brush",
|
|
&nreturn_vals,
|
|
GIMP_PDB_END);
|
|
|
|
if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
|
|
name = g_strdup (return_vals[1].data.d_string);
|
|
|
|
gimp_destroy_params (return_vals, nreturn_vals);
|
|
|
|
return name;
|
|
}
|
|
|
|
static void
|
|
mygimp_brush_info (gint32 *width,
|
|
gint32 *height)
|
|
{
|
|
GimpParam *return_vals;
|
|
gint 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_name;
|
|
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,
|
|
GIMP_RGB_IMAGE,
|
|
100.0, /* opacity */
|
|
GIMP_NORMAL_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_name = 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->name);
|
|
|
|
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, GIMP_BACKGROUND_FILL);
|
|
|
|
/* 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_name);
|
|
|
|
g_free (saved_name);
|
|
|
|
return layer_ID;
|
|
}
|
|
|
|
static void
|
|
brush_list_button_callback (BrushDesc *bdesc)
|
|
{
|
|
gint32 layer_ID;
|
|
|
|
if ((layer_ID = gfig_gen_brush_preview (bdesc)) != -1)
|
|
{
|
|
g_object_set_data (G_OBJECT (brush_page_pw), "brush-desc", bdesc);
|
|
gfig_brush_fill_preview (brush_page_pw, layer_ID, bdesc);
|
|
gtk_widget_queue_draw (brush_page_pw);
|
|
}
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
gboolean flag = GPOINTER_TO_INT (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)
|
|
{
|
|
gboolean flag = GPOINTER_TO_INT (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 = GPOINTER_TO_INT (data);
|
|
|
|
if (mtype == PAINT_LAYERS_MENU)
|
|
{
|
|
selvals.onlayers = (DrawonLayers)
|
|
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
|
|
"gimp-item-data"));
|
|
|
|
#ifdef DEBUG
|
|
printf ("layer type set to %s\n",
|
|
selvals.onlayers == SINGLE_LAYER ? "SINGLE_LAYER" : "MULTI_LAYER");
|
|
#endif /* DEBUG */
|
|
|
|
/* 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)
|
|
{
|
|
selvals.onlayerbg = (LayersBGType)
|
|
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
|
|
"gimp-item-data"));
|
|
#ifdef DEBUG
|
|
printf ("BG type = %d\n", selvals.onlayerbg);
|
|
#endif /* DEBUG */
|
|
}
|
|
else if (mtype == PAINT_TYPE_MENU)
|
|
{
|
|
selvals.painttype = (PaintType)
|
|
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
|
|
"gimp-item-data"));
|
|
|
|
#ifdef DEBUG
|
|
printf ("Got type menu = %d\n", selvals.painttype);
|
|
#endif /* DEBUG */
|
|
|
|
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),
|
|
GINT_TO_POINTER (PAINT_LAYERS_MENU), 0,
|
|
|
|
_("Original"), GINT_TO_POINTER (ORIGINAL_LAYER), NULL,
|
|
_("New"), GINT_TO_POINTER (SINGLE_LAYER), NULL,
|
|
_("Multiple"), GINT_TO_POINTER (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),
|
|
GINT_TO_POINTER (PAINT_TYPE_MENU), 0,
|
|
|
|
_("Brush"),
|
|
GINT_TO_POINTER (PAINT_BRUSH_TYPE), &item1,
|
|
_("Selection"),
|
|
GINT_TO_POINTER (PAINT_SELECTION_TYPE), &item2,
|
|
_("Selection+Fill"),
|
|
GINT_TO_POINTER (PAINT_SELECTION_FILL_TYPE), &item3,
|
|
|
|
NULL);
|
|
|
|
g_signal_connect (item1, "activate",
|
|
G_CALLBACK (gfig_brush_pane_activate),
|
|
GINT_TO_POINTER (TRUE));
|
|
g_signal_connect (item1, "activate",
|
|
G_CALLBACK (gfig_select_pane_activate),
|
|
GINT_TO_POINTER (FALSE));
|
|
|
|
g_signal_connect (item2, "activate",
|
|
G_CALLBACK (gfig_brush_pane_activate),
|
|
GINT_TO_POINTER (FALSE));
|
|
g_signal_connect (item2, "activate",
|
|
G_CALLBACK (gfig_select_pane_activate),
|
|
GINT_TO_POINTER (TRUE));
|
|
|
|
g_signal_connect (item3, "activate",
|
|
G_CALLBACK (gfig_brush_pane_activate),
|
|
GINT_TO_POINTER (FALSE));
|
|
g_signal_connect (item3, "activate",
|
|
G_CALLBACK (gfig_select_pane_activate),
|
|
GINT_TO_POINTER (TRUE));
|
|
|
|
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),
|
|
GINT_TO_POINTER (PAINT_BGS_MENU), 0,
|
|
|
|
_("Transparent"), GINT_TO_POINTER (LAYER_TRANS_BG), NULL,
|
|
_("Background"), GINT_TO_POINTER (LAYER_BG_BG), NULL,
|
|
_("Foreground"), GINT_TO_POINTER (LAYER_FG_BG), NULL,
|
|
_("White"), GINT_TO_POINTER (LAYER_WHITE_BG), NULL,
|
|
_("Copy"), GINT_TO_POINTER (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);
|
|
g_signal_connect (toggle, "toggled",
|
|
G_CALLBACK (gimp_toggle_button_update),
|
|
&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);
|
|
g_signal_connect (toggle, "toggled",
|
|
G_CALLBACK (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);
|
|
g_signal_connect (scale_scale_data, "value_changed",
|
|
G_CALLBACK (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);
|
|
g_object_set_data (G_OBJECT (toggle), "inverse_sensitive", scale_scale);
|
|
g_object_set_data (G_OBJECT (toggle), "user_data", 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);
|
|
g_signal_connect (toggle, "toggled",
|
|
G_CALLBACK (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 (const gchar *name,
|
|
gdouble opacity,
|
|
gint spacing,
|
|
GimpLayerModeEffects paint_mode,
|
|
gint width,
|
|
gint height,
|
|
const guchar *mask_data,
|
|
gboolean closing,
|
|
gpointer data)
|
|
{
|
|
BrushDesc *bdesc = (BrushDesc *) data;
|
|
|
|
g_free (bdesc->name);
|
|
|
|
bdesc->name = g_strdup (name);
|
|
bdesc->width = width;
|
|
bdesc->height = height;
|
|
bdesc->opacity = opacity;
|
|
bdesc->spacing = spacing;
|
|
bdesc->paint_mode = paint_mode;
|
|
|
|
brush_list_button_callback (bdesc);
|
|
|
|
if (closing)
|
|
bdesc->popup = NULL;
|
|
}
|
|
|
|
static void
|
|
select_brush_callback (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
BrushDesc *bdesc = (BrushDesc *) data;
|
|
|
|
if (bdesc->popup)
|
|
/* calling gimp_brushes_set_popup() raises the dialog */
|
|
gimp_brushes_set_popup (bdesc->popup,
|
|
bdesc->name,
|
|
bdesc->opacity,
|
|
bdesc->spacing,
|
|
bdesc->paint_mode);
|
|
else
|
|
bdesc->popup = gimp_brush_select_new (_("Gfig Brush Selection"),
|
|
bdesc->name,
|
|
100.0, /* opacity */
|
|
-1, /* spacing */
|
|
GIMP_NORMAL_MODE,
|
|
gfig_brush_invoker,
|
|
bdesc);
|
|
|
|
brush_list_button_callback (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);
|
|
g_signal_connect (fade_out_scale_data, "value_changed",
|
|
G_CALLBACK (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);
|
|
g_signal_connect (gradient_scale_data, "value_changed",
|
|
G_CALLBACK (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);
|
|
g_signal_connect (pressure_scale_data, "value_changed",
|
|
G_CALLBACK (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);
|
|
|
|
g_signal_connect (pressure_scale_data, "value_changed",
|
|
G_CALLBACK (gfig_brush_update_preview),
|
|
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);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (select_brush_callback),
|
|
bdesc);
|
|
gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
|
|
NULL, 0, 0,
|
|
button, 1, TRUE);
|
|
|
|
/* Setup initial brush settings */
|
|
bdesc->name = mygimp_brush_get ();
|
|
brush_list_button_callback (bdesc);
|
|
|
|
return vbox;
|
|
}
|
|
|
|
static void
|
|
select_menu_callback (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
gint mtype = GPOINTER_TO_INT (data);
|
|
|
|
if (mtype == SELECT_TYPE_MENU)
|
|
{
|
|
SelectionType type = (SelectionType)
|
|
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
|
|
"gimp-item-data"));
|
|
|
|
selopt.type = type;
|
|
}
|
|
else if (mtype == SELECT_ARCTYPE_MENU)
|
|
{
|
|
ArcType type = (ArcType)
|
|
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
|
|
"gimp-item-data"));
|
|
|
|
selopt.as_pie = type;
|
|
}
|
|
else if (mtype == SELECT_TYPE_MENU_FILL)
|
|
{
|
|
FillType type = (FillType)
|
|
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
|
|
"gimp-item-data"));
|
|
|
|
selopt.fill_type = type;
|
|
}
|
|
else if (mtype == SELECT_TYPE_MENU_WHEN)
|
|
{
|
|
FillWhen type = (FillWhen)
|
|
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
|
|
"gimp-item-data"));
|
|
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),
|
|
GINT_TO_POINTER (SELECT_TYPE_MENU), 0,
|
|
|
|
_("Add"), GINT_TO_POINTER (ADD), NULL,
|
|
_("Subtract"), GINT_TO_POINTER (SUBTRACT), NULL,
|
|
_("Replace"), GINT_TO_POINTER (REPLACE), NULL,
|
|
_("Intersect"), GINT_TO_POINTER (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);
|
|
g_signal_connect (toggle, "toggled",
|
|
G_CALLBACK (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);
|
|
g_signal_connect (toggle, "toggled",
|
|
G_CALLBACK (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);
|
|
g_signal_connect (scale_data, "value_changed",
|
|
G_CALLBACK (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),
|
|
GINT_TO_POINTER (SELECT_TYPE_MENU_FILL), 0,
|
|
|
|
_("Pattern"), GINT_TO_POINTER (FILL_PATTERN), NULL,
|
|
_("Foreground"), GINT_TO_POINTER (FILL_FOREGROUND), NULL,
|
|
_("Background"), GINT_TO_POINTER (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);
|
|
g_signal_connect (scale_data, "value_changed",
|
|
G_CALLBACK (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),
|
|
GINT_TO_POINTER (SELECT_TYPE_MENU_WHEN), 0,
|
|
|
|
_("Each Selection"), GINT_TO_POINTER (FILL_EACH), NULL,
|
|
_("All Selections"), GINT_TO_POINTER (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),
|
|
GINT_TO_POINTER (SELECT_ARCTYPE_MENU), 0,
|
|
|
|
_("Segment"), GINT_TO_POINTER (ARC_SEGMENT), NULL,
|
|
_("Sector"), GINT_TO_POINTER (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 = GPOINTER_TO_INT (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 =
|
|
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
|
|
"gimp-item-data"));
|
|
}
|
|
else
|
|
{
|
|
grid_gc_type =
|
|
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
|
|
"gimp-item-data"));
|
|
}
|
|
|
|
draw_grid_clear ();
|
|
}
|
|
|
|
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);
|
|
g_signal_connect (toggle, "toggled",
|
|
G_CALLBACK (gimp_toggle_button_update),
|
|
&selvals.showimage);
|
|
g_signal_connect (toggle, "toggled",
|
|
G_CALLBACK (toggle_show_image),
|
|
NULL);
|
|
gtk_widget_show (toggle);
|
|
|
|
button = gtk_button_new_with_label (_("Reload Image"));
|
|
gtk_misc_set_padding (GTK_MISC (GTK_BIN (button)->child), 2, 0);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (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),
|
|
GINT_TO_POINTER (GRID_TYPE_MENU), 0,
|
|
|
|
_("Rectangle"), GINT_TO_POINTER (RECT_GRID), NULL,
|
|
_("Polar"), GINT_TO_POINTER (POLAR_GRID), NULL,
|
|
_("Isometric"), GINT_TO_POINTER (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),
|
|
GINT_TO_POINTER (GRID_RENDER_MENU), 0,
|
|
|
|
_("Normal"), GINT_TO_POINTER (GTK_STATE_NORMAL), NULL,
|
|
_("Black"), GINT_TO_POINTER (GFIG_BLACK_GC), NULL,
|
|
_("White"), GINT_TO_POINTER (GFIG_WHITE_GC), NULL,
|
|
_("Grey"), GINT_TO_POINTER (GFIG_GREY_GC), NULL,
|
|
_("Darker"), GINT_TO_POINTER (GTK_STATE_ACTIVE), NULL,
|
|
_("Lighter"), GINT_TO_POINTER (GTK_STATE_PRELIGHT), NULL,
|
|
_("Very Dark"), GINT_TO_POINTER (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);
|
|
g_signal_connect (size_data, "value_changed",
|
|
G_CALLBACK (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);
|
|
g_signal_connect (toggle, "toggled",
|
|
G_CALLBACK (gimp_toggle_button_update),
|
|
&selvals.showpos);
|
|
g_signal_connect_after (toggle, "toggled",
|
|
G_CALLBACK (gfig_pos_enable),
|
|
NULL);
|
|
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);
|
|
g_signal_connect (toggle, "toggled",
|
|
G_CALLBACK (gimp_toggle_button_update),
|
|
&selvals.opts.showcontrol);
|
|
g_signal_connect (toggle, "toggled",
|
|
G_CALLBACK (toggle_show_image),
|
|
NULL);
|
|
gtk_widget_show (toggle);
|
|
gfig_opt_widget.showcontrol = 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);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (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);
|
|
g_signal_connect (toggle, "toggled",
|
|
G_CALLBACK (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);
|
|
g_signal_connect (toggle, "toggled",
|
|
G_CALLBACK (gimp_toggle_button_update),
|
|
&selvals.opts.drawgrid);
|
|
g_signal_connect (toggle, "toggled",
|
|
G_CALLBACK (draw_grid_clear),
|
|
NULL);
|
|
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);
|
|
g_signal_connect (toggle, "toggled",
|
|
G_CALLBACK (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);
|
|
g_signal_connect (size_data, "value_changed",
|
|
G_CALLBACK (gimp_int_adjustment_update),
|
|
&selvals.opts.gridspacing);
|
|
g_signal_connect (size_data, "value_changed",
|
|
G_CALLBACK (draw_grid_clear),
|
|
NULL);
|
|
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);
|
|
|
|
g_object_set_data (G_OBJECT (list_item), "user_data", g);
|
|
gtk_list_append_items (GTK_LIST (list), g_list_append (NULL, list_item));
|
|
|
|
g_signal_connect (list_item, "button_press_event",
|
|
G_CALLBACK (list_button_press),
|
|
g);
|
|
gtk_widget_show (list_item);
|
|
}
|
|
}
|
|
|
|
static GtkWidget *
|
|
add_objects_list (void)
|
|
{
|
|
GtkWidget *frame;
|
|
GtkWidget *vbox;
|
|
GtkWidget *hbox;
|
|
GtkWidget *scrolled_win;
|
|
GtkWidget *list;
|
|
GtkWidget *button;
|
|
|
|
frame = gtk_frame_new (_("Object"));
|
|
gtk_widget_show (frame);
|
|
|
|
hbox = gtk_hbox_new (FALSE, 2);
|
|
gtk_container_add (GTK_CONTAINER (frame), hbox);
|
|
gtk_widget_show (hbox);
|
|
|
|
gfig_gtk_list = list = gtk_list_new ();
|
|
gtk_list_set_selection_mode (GTK_LIST (list), GTK_SELECTION_BROWSE);
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), small_preview (), FALSE, FALSE, 0);
|
|
|
|
object_list = 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_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_win),
|
|
list);
|
|
gtk_box_pack_start (GTK_BOX (hbox), scrolled_win, TRUE, TRUE, 0);
|
|
gtk_widget_show (scrolled_win);
|
|
gtk_widget_show (list);
|
|
|
|
/* Load saved objects */
|
|
gfig_list_load_all (gfig_path);
|
|
|
|
/* Put list in */
|
|
build_list_items (list);
|
|
|
|
/* Put buttons in */
|
|
vbox = gtk_vbutton_box_new ();
|
|
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
|
|
gtk_widget_show (vbox);
|
|
|
|
button = gtk_button_new_from_stock (GTK_STOCK_NEW);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (new_button_callback),
|
|
NULL);
|
|
gimp_help_set_help_data (button, _("Create a new Gfig object collection "
|
|
"for editing"), NULL);
|
|
gtk_container_add (GTK_CONTAINER (vbox), button);
|
|
gtk_widget_show (button);
|
|
|
|
button = gtk_button_new_from_stock (GTK_STOCK_OPEN);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (load_button_callback),
|
|
list);
|
|
gimp_help_set_help_data (button,
|
|
_("Load a single Gfig object collection"), NULL);
|
|
gtk_container_add (GTK_CONTAINER (vbox), button);
|
|
gtk_widget_show (button);
|
|
|
|
button = gtk_button_new_from_stock (GIMP_STOCK_EDIT);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (edit_button_callback),
|
|
list);
|
|
gimp_help_set_help_data (button, _("Edit Gfig object collection"), NULL);
|
|
gtk_container_add (GTK_CONTAINER (vbox), button);
|
|
gtk_widget_show (button);
|
|
|
|
button = gtk_button_new_with_mnemonic (_("_Merge"));
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (merge_button_callback),
|
|
list);
|
|
gimp_help_set_help_data (button, _("Merge Gfig Object collection into the "
|
|
"current edit session"), NULL);
|
|
gtk_container_add (GTK_CONTAINER (vbox), button);
|
|
gtk_widget_show (button);
|
|
|
|
button = gtk_button_new_from_stock (GTK_STOCK_DELETE);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (gfig_delete_gfig_callback),
|
|
list);
|
|
gimp_help_set_help_data (button, _("Delete currently selected Gfig Object "
|
|
"collection"), NULL);
|
|
gtk_container_add (GTK_CONTAINER (vbox), button);
|
|
gtk_widget_show (button);
|
|
|
|
button = gtk_button_new_from_stock (GTK_STOCK_REFRESH);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (rescan_button_callback),
|
|
NULL);
|
|
gimp_help_set_help_data (button,
|
|
_("Select folder and rescan Gfig object collections"),
|
|
NULL);
|
|
gtk_container_add (GTK_CONTAINER (vbox), button);
|
|
gtk_widget_show (button);
|
|
|
|
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)
|
|
{
|
|
gboolean enable = selvals.showpos;
|
|
|
|
gtk_widget_set_sensitive (GTK_WIDGET (pos_label), enable);
|
|
}
|
|
|
|
static void
|
|
gfig_pos_update_labels (gpointer data)
|
|
{
|
|
static gchar buf[256];
|
|
|
|
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)
|
|
{
|
|
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;
|
|
|
|
hbox = gtk_hbox_new (TRUE, 6);
|
|
|
|
/* Position labels */
|
|
label = gtk_label_new (_("Size:"));
|
|
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
|
|
gtk_widget_show (label);
|
|
|
|
obj_size_label = gtk_label_new ("0");
|
|
gtk_misc_set_alignment (GTK_MISC (obj_size_label), 0.5, 0.5);
|
|
gtk_box_pack_start (GTK_BOX (hbox), obj_size_label, FALSE, FALSE, 0);
|
|
gtk_widget_show (obj_size_label);
|
|
|
|
gtk_widget_show (hbox);
|
|
|
|
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);
|
|
|
|
g_signal_connect (gfig_preview , "realize",
|
|
G_CALLBACK (gfig_preview_realize),
|
|
NULL);
|
|
|
|
g_signal_connect (gfig_preview , "event",
|
|
G_CALLBACK (gfig_preview_events),
|
|
NULL);
|
|
|
|
g_signal_connect_after (gfig_preview , "expose_event",
|
|
G_CALLBACK (gfig_preview_expose),
|
|
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);
|
|
g_signal_connect_swapped (gfig_preview, "motion_notify_event",
|
|
G_CALLBACK (GTK_WIDGET_CLASS (G_OBJECT_GET_CLASS (ruler))->motion_notify_event),
|
|
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);
|
|
g_signal_connect_swapped (gfig_preview, "motion_notify_event",
|
|
G_CALLBACK (GTK_WIDGET_CLASS (G_OBJECT_GET_CLASS (ruler))->motion_notify_event),
|
|
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;
|
|
}
|
|
|
|
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 *vbox;
|
|
GtkWidget *notebook;
|
|
GtkWidget *page;
|
|
GtkWidget *top_level_dlg;
|
|
|
|
gimp_ui_init ("gfig", TRUE);
|
|
gfig_stock_init ();
|
|
|
|
xxx = gdk_rgb_get_colormap ();
|
|
|
|
gfig_path = gimp_plug_in_get_path ("gfig-path", "gfig");
|
|
|
|
/*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, TRUE, FALSE,
|
|
|
|
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,
|
|
|
|
GTK_STOCK_CANCEL, gtk_widget_destroy,
|
|
NULL, 1, NULL, FALSE, TRUE,
|
|
|
|
GTK_STOCK_CLOSE, gfig_ok_callback,
|
|
NULL, NULL, NULL, TRUE, FALSE,
|
|
|
|
_("Paint"), gfig_paint_callback,
|
|
NULL, NULL, NULL, FALSE, FALSE,
|
|
|
|
NULL);
|
|
|
|
g_signal_connect (top_level_dlg, "destroy",
|
|
G_CALLBACK (gtk_main_quit),
|
|
NULL);
|
|
|
|
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 */
|
|
gtk_box_pack_start (GTK_BOX (main_hbox),
|
|
draw_buttons (top_level_dlg), FALSE, FALSE, 0);
|
|
|
|
/* Preview itself */
|
|
gtk_box_pack_start (GTK_BOX (main_hbox), make_preview (), FALSE, FALSE, 0);
|
|
|
|
gtk_widget_show (gfig_preview);
|
|
|
|
vbox = gtk_vbox_new (FALSE, 2);
|
|
gtk_box_pack_start (GTK_BOX (main_hbox), vbox, FALSE, FALSE, 0);
|
|
gtk_widget_show (vbox);
|
|
|
|
/* listbox + entry */
|
|
gtk_box_pack_start (GTK_BOX (vbox), add_objects_list (), TRUE, TRUE, 0);
|
|
|
|
/* Grid entry */
|
|
gtk_box_pack_start (GTK_BOX (vbox), grid_frame (), 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);
|
|
|
|
gtk_main ();
|
|
|
|
gimp_image_delete (brush_image_ID);
|
|
brush_image_ID = -1;
|
|
|
|
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.\n"
|
|
"Continue 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));
|
|
}
|
|
}
|
|
|
|
static void
|
|
gfig_preview_realize (GtkWidget *widget)
|
|
{
|
|
GdkCursor *preview_cursor;
|
|
|
|
preview_cursor = gdk_cursor_new (GDK_CROSSHAIR);
|
|
gdk_window_set_cursor (gfig_preview->window, preview_cursor);
|
|
}
|
|
|
|
static gboolean
|
|
gfig_preview_expose (GtkWidget *widget,
|
|
GdkEvent *event)
|
|
{
|
|
draw_grid ();
|
|
draw_objects (pic_obj->obj_list, TRUE);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
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 ();
|
|
}
|
|
break;
|
|
|
|
case GDK_KEY_RELEASE:
|
|
if (tmp_show_single != -1)
|
|
{
|
|
obj_show_single = tmp_show_single;
|
|
draw_grid_clear ();
|
|
}
|
|
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;
|
|
gboolean 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);
|
|
|
|
g_object_set_data (G_OBJECT (list_item), "user_data", 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);
|
|
|
|
g_signal_connect (list_item, "button_press_event",
|
|
G_CALLBACK (list_button_press),
|
|
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 */
|
|
|
|
g_free (options->obj->draw_name);
|
|
options->obj->draw_name =
|
|
g_strdup (gtk_entry_get_text (GTK_ENTRY (options->name_entry)));
|
|
|
|
/* 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);
|
|
|
|
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,
|
|
gboolean 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 Object 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;
|
|
|
|
gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
|
|
|
|
patheditor = GTK_WIDGET (g_object_get_data (G_OBJECT (data),
|
|
"patheditor"));
|
|
|
|
g_free (gfig_path);
|
|
gfig_path = gimp_path_editor_get_path (GIMP_PATH_EDITOR (patheditor));
|
|
|
|
if (gfig_path)
|
|
{
|
|
clear_list_items (GTK_LIST (gfig_gtk_list));
|
|
gfig_list_load_all (gfig_path);
|
|
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;
|
|
|
|
if (dlg)
|
|
{
|
|
gtk_window_present (GTK_WINDOW (dlg));
|
|
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);
|
|
|
|
g_signal_connect (dlg, "destroy",
|
|
G_CALLBACK (gtk_widget_destroyed),
|
|
&dlg);
|
|
|
|
patheditor = gimp_path_editor_new (_("Add Gfig Path"), gfig_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_object_set_data (G_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_queue_draw (pic_preview);
|
|
|
|
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 object collection"));
|
|
gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_MOUSE);
|
|
|
|
g_signal_connect (window, "destroy",
|
|
G_CALLBACK (gtk_widget_destroyed),
|
|
&window);
|
|
|
|
g_signal_connect (GTK_FILE_SELECTION (window)->ok_button, "clicked",
|
|
G_CALLBACK (gfig_load_file_selection_ok),
|
|
window);
|
|
|
|
g_signal_connect_swapped (GTK_FILE_SELECTION (window)->cancel_button,
|
|
"clicked",
|
|
G_CALLBACK (gtk_widget_destroy),
|
|
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)
|
|
{
|
|
GimpFillType fill_type;
|
|
GimpImageType type;
|
|
gint32 layer_id;
|
|
|
|
switch (gimp_drawable_type (gfig_select_drawable->drawable_id))
|
|
{
|
|
case GIMP_GRAYA_IMAGE:
|
|
case GIMP_GRAY_IMAGE:
|
|
type = GIMP_GRAYA_IMAGE;
|
|
break;
|
|
|
|
default:
|
|
type = GIMP_RGBA_IMAGE;
|
|
break;
|
|
}
|
|
|
|
if ((layer_id = gimp_layer_new (gfig_image,
|
|
new_name,
|
|
img_width,
|
|
img_height,
|
|
type,
|
|
100.0, /* opacity */
|
|
GIMP_NORMAL_MODE)) < 0)
|
|
{
|
|
g_warning ("Error in creating layer");
|
|
return;
|
|
}
|
|
|
|
gimp_image_add_layer (gfig_image, layer_id, -1);
|
|
|
|
gfig_drawable = layer_id;
|
|
|
|
switch (selvals.onlayerbg)
|
|
{
|
|
case LAYER_TRANS_BG:
|
|
fill_type = GIMP_TRANSPARENT_FILL;
|
|
break;
|
|
case LAYER_BG_BG:
|
|
fill_type = GIMP_BACKGROUND_FILL;
|
|
break;
|
|
case LAYER_FG_BG:
|
|
fill_type = GIMP_FOREGROUND_FILL;
|
|
break;
|
|
case LAYER_WHITE_BG:
|
|
fill_type = GIMP_WHITE_FILL;
|
|
break;
|
|
default:
|
|
fill_type = GIMP_BACKGROUND_FILL;
|
|
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 (void)
|
|
{
|
|
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
|
|
|
|
/* Set the brush up */
|
|
bdesc = g_object_get_data (G_OBJECT (brush_page_pw), "brush-desc");
|
|
|
|
if (bdesc)
|
|
mygimp_brush_set (bdesc->name);
|
|
|
|
gimp_undo_push_group_start (gfig_image);
|
|
|
|
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++;
|
|
}
|
|
|
|
/* Fill layer if required */
|
|
if (selvals.painttype == PAINT_SELECTION_FILL_TYPE
|
|
&& selopt.fill_when == FILL_AFTER)
|
|
paint_layer_fill ();
|
|
|
|
gimp_undo_push_group_end (gfig_image);
|
|
|
|
gimp_displays_flush ();
|
|
}
|
|
|
|
static void
|
|
reload_button_callback (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
refill_cache ();
|
|
draw_grid_clear ();
|
|
}
|
|
|
|
static void
|
|
about_button_callback (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
GtkWidget *window;
|
|
GtkWidget *label;
|
|
GtkWidget *hbox;
|
|
GtkWidget *vbox;
|
|
GtkWidget *image;
|
|
|
|
window = gimp_dialog_new (_("About Gfig"), "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);
|
|
|
|
image = gtk_image_new_from_stock (GFIG_STOCK_LOGO, GTK_ICON_SIZE_DIALOG);
|
|
gtk_widget_show (image);
|
|
|
|
hbox = gtk_hbox_new (FALSE, 4);
|
|
gtk_widget_show (hbox);
|
|
|
|
vbox = gtk_vbox_new (FALSE, 4);
|
|
gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
|
|
gtk_widget_show (vbox);
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), image, 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 (const gchar *name)
|
|
{
|
|
GFigObj *gfig;
|
|
GtkWidget *new_list_item;
|
|
/* Create a new entry */
|
|
|
|
gfig = gfig_new ();
|
|
|
|
if (!name)
|
|
name = _("New Gfig Object");
|
|
|
|
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 */
|
|
gtk_widget_queue_draw (gfig_preview);
|
|
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 (NULL);
|
|
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 (object_list, 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 *) g_object_get_data (G_OBJECT (sellist->data),
|
|
"user_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 (object_list, TRUE);
|
|
|
|
gtk_list_select_item (GTK_LIST (gfig_gtk_list), pos);
|
|
|
|
current_obj = g_list_nth (gfig_list, pos)->data;
|
|
|
|
gtk_widget_queue_draw (gfig_preview);
|
|
|
|
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 *) g_object_get_data (G_OBJECT (sellist->data),
|
|
"user_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);
|
|
|
|
g_signal_connect (delete_dialog, "destroy",
|
|
G_CALLBACK (gtk_widget_destroyed),
|
|
&delete_dialog);
|
|
|
|
gtk_widget_set_sensitive (GTK_WIDGET (object_list), 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,_("<NONE>"));
|
|
|
|
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,_("<NONE>"));
|
|
|
|
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 */
|
|
gtk_widget_queue_draw (gfig_preview);
|
|
/* 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 *) g_object_get_data (G_OBJECT (sellist->data),
|
|
"user_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 *) g_object_get_data (G_OBJECT (sellist->data),
|
|
"user_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 */
|
|
gtk_widget_queue_draw (gfig_preview);
|
|
/* 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 */
|
|
gtk_widget_queue_draw (gfig_preview);
|
|
/* 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_shell_append (GTK_MENU_SHELL (gfig_op_menu), menu_item);
|
|
gtk_widget_show (menu_item);
|
|
|
|
g_signal_connect (menu_item, "activate",
|
|
G_CALLBACK (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_shell_append (GTK_MENU_SHELL (gfig_op_menu), menu_item);
|
|
gtk_widget_show (menu_item);
|
|
g_signal_connect (menu_item, "activate",
|
|
G_CALLBACK (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_shell_append (GTK_MENU_SHELL (gfig_op_menu), menu_item);
|
|
gtk_widget_show (menu_item);
|
|
g_signal_connect (menu_item, "activate",
|
|
G_CALLBACK (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_shell_append (GTK_MENU_SHELL (gfig_op_menu), menu_item);
|
|
gtk_widget_show (menu_item);
|
|
g_signal_connect (menu_item, "activate",
|
|
G_CALLBACK (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;
|
|
gtk_widget_queue_draw (gfig_preview);
|
|
}
|
|
}
|
|
|
|
/* 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 = g_object_get_data (G_OBJECT (widget), "user_data");
|
|
|
|
scale_x_factor = org_scale_x_factor;
|
|
scale_y_factor = org_scale_y_factor;
|
|
gtk_adjustment_set_value (GTK_ADJUSTMENT (adj), 1.0);
|
|
|
|
gtk_widget_queue_draw (gfig_preview);
|
|
}
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
/*
|
|
* This really needs a picture to show the math...
|
|
*
|
|
* Consider an isometric grid with one of the sets of lines parallel to the
|
|
* y axis (vertical alignment). Further define that the origin of a Cartesian
|
|
* grid is at a isometric vertex. For simplicity consider the first quadrant only.
|
|
*
|
|
* - Let one line segment between vertices be r
|
|
* - Define the value of r as the grid spacing
|
|
* - Assign an integer n identifier to each vertical grid line along the x axis.
|
|
* with n=0 being the y axis. n can be any integer
|
|
* - Let m to be any integer
|
|
* - Let h be the spacing between vertical grid lines measured along the x axis.
|
|
* It follows from the isometric grid that h has a value of r * COS(1/6 Pi Rad)
|
|
*
|
|
* Consider a Vertex V at the Cartesian location [Xv, Yv]
|
|
*
|
|
* It follows that vertices belong to the set...
|
|
* V[Xv, Yv] = [ [ n * h ] ,
|
|
* [ m * r + ( 0.5 * r (n % 2) ) ] ]
|
|
* for all integers n and m
|
|
*
|
|
* Who cares? Me. It's useful in solving this problem:
|
|
* Consider an arbitrary point P[Xp,Yp], find the closest vertex in the set V.
|
|
*
|
|
* Restated this problem is "find values for m and n that are drive V closest to P"
|
|
*
|
|
* A Solution method (there may be a better one?):
|
|
*
|
|
* Step 1) bound n to the two closest values for Xp
|
|
* n_lo = (int) (Xp / h)
|
|
* n_hi = n_lo + 1
|
|
*
|
|
* Step 2) Consider the two closes vertices for each n_lo and n_hi. The further of
|
|
* the vertices in each pair can readily be discarded.
|
|
* m_lo_n_lo = (int) ( (Yp / r) - 0.5 (n_lo % 2) )
|
|
* m_hi_n_lo = m_lo_n_lo + 1
|
|
*
|
|
* m_lo_n_hi = (int) ( (Yp / r) - 0.5 (n_hi % 2) )
|
|
* m_hi_n_hi = m_hi_n_hi
|
|
*
|
|
* Step 3) compute the distance from P to V1 and V2. Snap to the closer point.
|
|
*/
|
|
gint n_lo;
|
|
gint n_hi;
|
|
gint m_lo_n_lo;
|
|
gint m_hi_n_lo;
|
|
gint m_lo_n_hi;
|
|
gint m_hi_n_hi;
|
|
gint m_n_lo;
|
|
gint m_n_hi;
|
|
gdouble r;
|
|
gdouble h;
|
|
gint x1;
|
|
gint x2;
|
|
gint y1;
|
|
gint y2;
|
|
|
|
r = selvals.opts.gridspacing;
|
|
h = COS_1o6PI_RAD * r;
|
|
|
|
n_lo = (gint) x / h;
|
|
n_hi = n_lo + 1;
|
|
|
|
/* evaluate m candidates for n_lo */
|
|
m_lo_n_lo = (gint) ( (y / r) - 0.5 * (n_lo % 2) );
|
|
m_hi_n_lo = m_lo_n_lo + 1;
|
|
/* figure out which is the better candidate */
|
|
if (abs((m_lo_n_lo * r + (0.5 * r * (n_lo % 2))) - y) <
|
|
abs((m_hi_n_lo * r + (0.5 * r * (n_lo % 2))) - y)) {
|
|
m_n_lo = m_lo_n_lo;
|
|
}
|
|
else {
|
|
m_n_lo = m_hi_n_lo;
|
|
}
|
|
|
|
/* evaluate m candidates for n_hi */
|
|
m_lo_n_hi = (gint) ( (y / r) - 0.5 * (n_hi % 2) );
|
|
m_hi_n_hi = m_lo_n_hi + 1;
|
|
/* figure out which is the better candidate */
|
|
if (abs((m_lo_n_hi * r + (0.5 * r * (n_hi % 2))) - y) <
|
|
abs((m_hi_n_hi * r + (0.5 * r * (n_hi % 2))) - y)) {
|
|
m_n_hi = m_lo_n_hi;
|
|
}
|
|
else {
|
|
m_n_hi = m_hi_n_hi;
|
|
}
|
|
|
|
/* Now, which is closer to [x,y]? we can use a somewhat abbreviated form of the
|
|
* distance formula since we only care about relative values. */
|
|
|
|
x1 = (gint) (n_lo * h);
|
|
y1 = (gint) (m_n_lo * r + (0.5 * r * (n_lo % 2)));
|
|
x2 = (gint) (n_hi * h);
|
|
y2 = (gint) (m_n_hi * r + (0.5 * r * (n_hi % 2)));
|
|
|
|
if (((x - x1) * (x - x1) + (y - y1) * (y - y1)) <
|
|
((x - x2) * (x - x2) + (y - y2) * (y - y2))) {
|
|
gp->x = x1;
|
|
gp->y = y1;
|
|
}
|
|
else {
|
|
gp->x = x2;
|
|
gp->y = y2;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/* 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 (p->x) - SQ_SIZE/2,
|
|
gfig_scale_y (p->y) - SQ_SIZE/2,
|
|
SQ_SIZE,
|
|
SQ_SIZE);
|
|
}
|
|
|
|
/* Draw the grid on the screen
|
|
*/
|
|
|
|
static void
|
|
draw_grid_clear ()
|
|
{
|
|
/* wipe slate and start again */
|
|
dialog_update_preview ();
|
|
gtk_widget_queue_draw (gfig_preview);
|
|
}
|
|
|
|
static void
|
|
toggle_show_image (void)
|
|
{
|
|
/* wipe slate and start again */
|
|
draw_grid_clear ();
|
|
}
|
|
|
|
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) GPOINTER_TO_INT (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 */
|
|
gtk_widget_queue_draw (gfig_preview);
|
|
/* And preview */
|
|
list_button_update (current_obj);
|
|
}
|
|
|
|
selvals.otype = (DobjType) GPOINTER_TO_INT (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 = RINT (ang_radius * cos (ang_loop));
|
|
ly = RINT (ang_radius * sin (ang_loop));
|
|
|
|
gdk_draw_line (gfig_preview->window,
|
|
drawgc,
|
|
lx + (preview_width) / 2,
|
|
- ly + (preview_height) / 2,
|
|
(preview_width) / 2,
|
|
(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,
|
|
0,
|
|
loop,
|
|
preview_width,
|
|
loop);
|
|
}
|
|
|
|
/* Draw the vertical lines */
|
|
|
|
for (loop = 0 ; loop < preview_width ; loop += step)
|
|
{
|
|
gdk_draw_line (gfig_preview->window,
|
|
drawgc,
|
|
loop,
|
|
0,
|
|
loop,
|
|
preview_height);
|
|
}
|
|
}
|
|
|
|
static void
|
|
draw_grid_iso (GdkGC *drawgc)
|
|
{
|
|
/* vstep is an int since it's defined from grid size */
|
|
gint vstep;
|
|
gdouble loop;
|
|
gdouble hstep;
|
|
|
|
gdouble diagonal_start;
|
|
gdouble diagonal_end;
|
|
gdouble diagonal_width;
|
|
gdouble diagonal_height;
|
|
|
|
vstep = selvals.opts.gridspacing;
|
|
hstep = selvals.opts.gridspacing * COS_1o6PI_RAD;
|
|
|
|
/* Draw the vertical lines - These are easy */
|
|
for (loop = 0 ; loop < preview_width ; loop += hstep){
|
|
gdk_draw_line (gfig_preview->window,
|
|
drawgc,
|
|
(gint)loop,
|
|
(gint)0,
|
|
(gint)loop,
|
|
(gint)preview_height);
|
|
}
|
|
|
|
/* draw diag lines at a Theta of +/- 1/6 Pi Rad */
|
|
|
|
diagonal_start = -(((int)preview_width * TAN_1o6PI_RAD) - (((int)(preview_width * TAN_1o6PI_RAD)) % vstep));
|
|
|
|
diagonal_end = preview_height + (preview_width * TAN_1o6PI_RAD);
|
|
diagonal_end -= ((int)diagonal_end) % vstep;
|
|
|
|
diagonal_width = preview_width;
|
|
diagonal_height = preview_width * TAN_1o6PI_RAD;
|
|
|
|
/* Draw diag lines */
|
|
for (loop = diagonal_start ; loop < diagonal_end ; loop += vstep)
|
|
{
|
|
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 (void)
|
|
{
|
|
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;
|
|
gtk_widget_queue_draw (gfig_preview);
|
|
/* 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 */
|
|
gtk_widget_queue_draw (gfig_preview);
|
|
/* 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--)
|
|
{
|
|
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 ();
|
|
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);
|
|
|
|
for (spnt = obj->points; spnt; spnt = spnt->next)
|
|
{
|
|
if (spnt->found_me)
|
|
{
|
|
*xdiff = spnt->pnt.x - to_pnt->x;
|
|
*ydiff = spnt->pnt.y - to_pnt->y;
|
|
return spnt;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
update_pnts (Dobject *obj,
|
|
gint16 xdiff,
|
|
gint16 ydiff)
|
|
{
|
|
DobjPoints *spnt;
|
|
|
|
g_assert (obj != NULL);
|
|
|
|
/* Update all pnts */
|
|
for (spnt = obj->points; spnt; spnt = spnt->next)
|
|
{
|
|
spnt->pnt.x -= xdiff;
|
|
spnt->pnt.y -= ydiff;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_move_all_obj (GdkPoint *to_pnt)
|
|
{
|
|
/* Move all objects in one go */
|
|
/* Undraw/then draw in new pos */
|
|
gint16 xdiff = move_all_pnt->x - to_pnt->x;
|
|
gint16 ydiff = move_all_pnt->y - to_pnt->y;
|
|
|
|
if (xdiff || ydiff)
|
|
{
|
|
DAllObjs *all;
|
|
|
|
for (all = current_obj->obj_list; all; all = all->next)
|
|
{
|
|
Dobject *obj = all->obj;
|
|
|
|
/* undraw ! */
|
|
draw_one_obj (obj);
|
|
|
|
update_pnts (obj, xdiff, ydiff);
|
|
|
|
/* Draw in new pos */
|
|
draw_one_obj (obj);
|
|
}
|
|
|
|
*move_all_pnt = *to_pnt;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
/* 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, "<LINE>\n");
|
|
|
|
while (spnt)
|
|
{
|
|
fprintf (to, "%d %d\n",
|
|
spnt->pnt.x,
|
|
spnt->pnt.y);
|
|
spnt = spnt->next;
|
|
}
|
|
|
|
fprintf (to, "</LINE>\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 ("</LINE>", 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,
|
|
spnt->pnt.x,
|
|
spnt->pnt.y,
|
|
epnt->pnt.x,
|
|
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 (spnt->pnt.x, preview_width),
|
|
adjust_pic_coords (spnt->pnt.y, preview_height),
|
|
adjust_pic_coords (epnt->pnt.x, preview_width),
|
|
adjust_pic_coords (epnt->pnt.y, preview_height));
|
|
}
|
|
else
|
|
{
|
|
gdk_draw_line (gfig_preview->window,
|
|
gfig_gc,
|
|
gfig_scale_x (spnt->pnt.x),
|
|
gfig_scale_y (spnt->pnt.y),
|
|
gfig_scale_x (epnt->pnt.x),
|
|
gfig_scale_y (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;
|
|
|
|
for (spnt = obj->points; spnt; spnt = spnt->next)
|
|
seg_count++;
|
|
|
|
if (!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 */
|
|
for (spnt = obj->points; spnt; spnt = spnt->next)
|
|
{
|
|
line_pnts[i++] = spnt->pnt.x;
|
|
line_pnts[i++] = spnt->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,
|
|
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,
|
|
spnt->pnt.x,
|
|
spnt->pnt.y,
|
|
epnt->pnt.x,
|
|
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,
|
|
spnt->pnt.x,
|
|
spnt->pnt.y,
|
|
epnt->pnt.x,
|
|
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 = (pnt->x * scale_x_factor);
|
|
tmp_pnt.y = (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 = (pnt->x * scale_x_factor);
|
|
tmp_pnt.y = (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;
|
|
}
|
|
/*gtk_widget_queue_draw (gfig_preview);*/
|
|
}
|
|
|
|
/* 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, "<CIRCLE>\n");
|
|
|
|
while (spnt)
|
|
{
|
|
fprintf (to, "%d %d\n",
|
|
spnt->pnt.x,
|
|
spnt->pnt.y);
|
|
spnt = spnt->next;
|
|
}
|
|
|
|
fprintf (to, "</CIRCLE>\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 ("</CIRCLE>", 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 = 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 (!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);
|
|
|
|
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);
|
|
|
|
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, "<ELLIPSE>\n");
|
|
|
|
for (; spnt; spnt = spnt->next)
|
|
fprintf (to, "%d %d\n", spnt->pnt.x, spnt->pnt.y);
|
|
|
|
fprintf (to, "</ELLIPSE>\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 ("</ELLIPSE>", 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) 600;
|
|
|
|
for (loop = 0; loop < 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 = RINT (lx + center_pnt->pnt.x);
|
|
calc_pnt.y = 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 = calc_pnt;
|
|
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 (!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);
|
|
|
|
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, "<POLY>\n");
|
|
|
|
while (spnt)
|
|
{
|
|
fprintf (to, "%d %d\n",
|
|
spnt->pnt.x,
|
|
spnt->pnt.y);
|
|
spnt = spnt->next;
|
|
}
|
|
|
|
fprintf (to, "<EXTRA>\n");
|
|
fprintf (to, "%d\n</EXTRA>\n", obj->type_data);
|
|
fprintf (to, "</POLY>\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 ("<EXTRA>", 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 = nsides;
|
|
get_line (buf, MAX_LOAD_LINE, from, 0);
|
|
if (strcmp ("</EXTRA>", 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 ("</POLY>", 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) obj->type_data;
|
|
offset_angle = atan2 (shift_y, shift_x);
|
|
|
|
for (loop = 0 ; loop < 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 = RINT (lx + center_pnt->pnt.x);
|
|
calc_pnt.y = 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 = 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) obj->type_data;
|
|
offset_angle = atan2 (shift_y, shift_x);
|
|
|
|
for (loop = 0 ; loop < 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 = RINT (lx + center_pnt->pnt.x);
|
|
calc_pnt.y = 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 = 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) obj->type_data;
|
|
offset_angle = atan2 (shift_y, shift_x);
|
|
|
|
for (loop = 0 ; loop < 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 = RINT (lx + center_pnt->pnt.x);
|
|
calc_pnt.y = 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*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) 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*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 = RINT (lx + center_pnt->pnt.x);
|
|
calc_pnt.y = 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 = 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 = 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;
|
|
|
|
return (dx == 0.0) ? 0.0 : 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, "<ARC>\n");
|
|
|
|
while (spnt)
|
|
{
|
|
fprintf (to, "%d %d\n",
|
|
spnt->pnt.x,
|
|
spnt->pnt.y);
|
|
spnt = spnt->next;
|
|
}
|
|
|
|
fprintf (to, "</ARC>\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 ("</ARC>", 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 = RINT (lx + center_pnt.x);
|
|
calc_pnt.y = 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 = calc_pnt;
|
|
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);*/
|
|
gtk_widget_queue_draw (gfig_preview);
|
|
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, "<STAR>\n");
|
|
|
|
while (spnt)
|
|
{
|
|
fprintf (to, "%d %d\n",
|
|
spnt->pnt.x,
|
|
spnt->pnt.y);
|
|
spnt = spnt->next;
|
|
}
|
|
|
|
fprintf (to, "<EXTRA>\n");
|
|
fprintf (to, "%d\n</EXTRA>\n", obj->type_data);
|
|
fprintf (to, "</STAR>\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 ("<EXTRA>", 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 = nsides;
|
|
get_line (buf, MAX_LOAD_LINE, from, 0);
|
|
if (strcmp ("</EXTRA>", 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 ("</STAR>", 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) 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 * 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 = RINT (lx + center_pnt->pnt.x);
|
|
calc_pnt.y = 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 * 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) 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*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 = RINT (lx + center_pnt->pnt.x);
|
|
calc_pnt.y = 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 = calc_pnt;
|
|
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 = 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 = 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, "<SPIRAL>\n");
|
|
|
|
while (spnt)
|
|
{
|
|
fprintf (to, "%d %d\n",
|
|
spnt->pnt.x,
|
|
spnt->pnt.y);
|
|
spnt = spnt->next;
|
|
}
|
|
|
|
fprintf (to, "<EXTRA>\n");
|
|
fprintf (to, "%d\n</EXTRA>\n", obj->type_data);
|
|
fprintf (to, "</SPIRAL>\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 ("<EXTRA>", 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 = nsides;
|
|
get_line (buf, MAX_LOAD_LINE, from, 0);
|
|
if (strcmp ("</EXTRA>", 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 ("</SPIRAL>", 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 = obj->type_data / abs (obj->type_data);
|
|
|
|
if (offset_angle < 0)
|
|
offset_angle += 2*G_PI;
|
|
|
|
sp_cons = radius/(obj->type_data * 2 * G_PI + offset_angle);
|
|
/* Lines */
|
|
ang_grid = 2.0*G_PI/(gdouble)180;
|
|
|
|
|
|
for (loop = 0 ; loop <= abs (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 = RINT (lx + center_pnt->pnt.x);
|
|
calc_pnt.y = 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 = obj->type_data / abs (obj->type_data);
|
|
|
|
offset_angle = atan2 (shift_y, shift_x);
|
|
|
|
if (offset_angle < 0)
|
|
offset_angle += 2*G_PI;
|
|
|
|
sp_cons = radius/(obj->type_data * 2 * G_PI + offset_angle);
|
|
/* Lines */
|
|
ang_grid = 2.0*G_PI/(gdouble)180;
|
|
|
|
|
|
/* count - */
|
|
seg_count = abs (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 = RINT (lx + center_pnt->pnt.x);
|
|
calc_pnt.y = 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 = 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 = 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, "<BEZIER>\n");
|
|
|
|
while (spnt)
|
|
{
|
|
fprintf (to, "%d %d\n",
|
|
spnt->pnt.x,
|
|
spnt->pnt.y);
|
|
spnt = spnt->next;
|
|
}
|
|
|
|
fprintf (to, "<EXTRA>\n");
|
|
fprintf (to, "%d\n</EXTRA>\n", obj->type_data);
|
|
fprintf (to, "</BEZIER>\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 ("<EXTRA>", 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 = nsides;
|
|
get_line (buf, MAX_LOAD_LINE, from, 0);
|
|
if (strcmp ("</EXTRA>", 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 ("</BEZIER>", 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 = fp_pnt_pnts[i];
|
|
y0 = fp_pnt_pnts[i + 1];
|
|
x1 = fp_pnt_pnts[i + 2];
|
|
y1 = fp_pnt_pnts[i + 3];
|
|
|
|
if (drawing_pic)
|
|
{
|
|
gdk_draw_line (pic_preview->window,
|
|
pic_preview->style->black_gc,
|
|
adjust_pic_coords (x0, preview_width),
|
|
adjust_pic_coords (y0, preview_height),
|
|
adjust_pic_coords (x1, preview_width),
|
|
adjust_pic_coords (y1, preview_height));
|
|
}
|
|
else
|
|
{
|
|
gdk_draw_line (gfig_preview->window,
|
|
gfig_gc,
|
|
gfig_scale_x (x0),
|
|
gfig_scale_y (y0),
|
|
gfig_scale_x (x1),
|
|
gfig_scale_y (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 */
|
|
for (spnt = obj->points; spnt; spnt = spnt->next)
|
|
seg_count++;
|
|
|
|
if (!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 */
|
|
for (spnt = obj->points; spnt; spnt = spnt->next)
|
|
{
|
|
draw_sqr (&spnt->pnt);
|
|
line_pnts[i][0] = spnt->pnt.x;
|
|
line_pnts[i++][1] = spnt->pnt.y;
|
|
}
|
|
|
|
/* 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;
|
|
|
|
/* First count the number of points */
|
|
for (spnt = obj->points; spnt; spnt = spnt->next)
|
|
seg_count++;
|
|
|
|
if (!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 */
|
|
for (spnt = obj->points; spnt; spnt = spnt->next)
|
|
{
|
|
bz_line_pnts[i][0] = spnt->pnt.x;
|
|
bz_line_pnts[i++][1] = spnt->pnt.y;
|
|
}
|
|
|
|
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 = 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 which 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;
|
|
}
|
|
}
|