diff options
Diffstat (limited to '')
-rw-r--r-- | plug-ins/gfig/gfig-dialog.c | 2193 |
1 files changed, 2193 insertions, 0 deletions
diff --git a/plug-ins/gfig/gfig-dialog.c b/plug-ins/gfig/gfig-dialog.c new file mode 100644 index 0000000..9f72b97 --- /dev/null +++ b/plug-ins/gfig/gfig-dialog.c @@ -0,0 +1,2193 @@ +/* + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This is a plug-in for GIMP. + * + * Generates images containing vector type drawings. + * + * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <glib.h> + +#ifdef G_OS_WIN32 +#include <libgimpbase/gimpwin32-io.h> +#endif + +#include <libgimp/gimp.h> +#include <libgimp/gimpui.h> + +#include "libgimp/stdplugins-intl.h" + +#include "gfig.h" +#include "gfig-style.h" +#include "gfig-dialog.h" +#include "gfig-arc.h" +#include "gfig-bezier.h" +#include "gfig-circle.h" +#include "gfig-dobject.h" +#include "gfig-ellipse.h" +#include "gfig-grid.h" +#include "gfig-line.h" +#include "gfig-poly.h" +#include "gfig-preview.h" +#include "gfig-rectangle.h" +#include "gfig-spiral.h" +#include "gfig-star.h" +#include "gfig-stock.h" + +#define SEL_BUTTON_WIDTH 100 +#define SEL_BUTTON_HEIGHT 20 + +#define GRID_TYPE_MENU 1 +#define GRID_RENDER_MENU 2 + +#define PAINT_BGS_MENU 2 +#define PAINT_TYPE_MENU 3 + +#define OBJ_SELECT_GT 1 +#define OBJ_SELECT_LT 2 +#define OBJ_SELECT_EQ 4 + +#define UPDATE_DELAY 300 /* From GtkRange in GTK+ 2.22 */ + +/* Globals */ +gint undo_level; /* Last slot filled in -1 = no undo */ +GList *undo_table[MAX_UNDO]; + +/* Values when first invoked */ +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 */ + 0.0, /* grid_radius_min */ + 10.0, /* grid_radius_interval */ + 0.0, /* grid_rotation */ + 5.0, /* grid_granularity */ + 120 /* grid_sectors_desired */ + }, + 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 brush 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 */ + BRUSH_BRUSH_TYPE, /* Default to use a brush */ + LINE /* Initial object type */ +}; + +selection_option selopt = +{ + ADD, /* type */ + FALSE, /* Antia */ + FALSE, /* Feather */ + 10.0, /* feather radius */ + ARC_SEGMENT, /* Arc as a segment */ + FILL_PATTERN, /* Fill as pattern */ + 100.0, /* Max opacity */ +}; + +/* Should be kept in sync with GfigOpts */ +typedef struct +{ + GtkAdjustment *gridspacing; + GtkAdjustment *grid_sectors_desired; + GtkAdjustment *grid_radius_interval; + GtkWidget *gridtypemenu; + GtkWidget *drawgrid; + GtkWidget *snap2grid; + GtkWidget *lockongrid; + GtkWidget *showcontrol; +} GfigOptWidgets; + +static GfigOptWidgets gfig_opt_widget = { NULL, NULL, NULL, NULL, NULL, NULL }; +static gchar *gfig_path = NULL; +static GtkWidget *page_menu_bg; +static GtkWidget *tool_options_notebook; +static GtkWidget *fill_type_notebook; +static guint paint_timeout = 0; + +static GtkActionGroup *gfig_actions = NULL; + + +static void gfig_response (GtkWidget *widget, + gint response_id, + gpointer data); +static void gfig_load_action_callback (GtkAction *action, + gpointer data); +static void gfig_save_action_callback (GtkAction *action, + gpointer data); +static void gfig_list_load_all (const gchar *path); +static void gfig_list_free_all (void); +static void create_notebook_pages (GtkWidget *notebook); +static void select_filltype_callback (GtkWidget *widget); +static void gfig_grid_action_callback (GtkAction *action, + gpointer data); +static void gfig_prefs_action_callback (GtkAction *action, + gpointer data); +static void toggle_show_image (void); +static void gridtype_combo_callback (GtkWidget *widget, + gpointer data); + +static void load_file_chooser_response (GtkFileChooser *chooser, + gint response_id, + gpointer data); +static void save_file_chooser_response (GtkFileChooser *chooser, + gint response_id, + GFigObj *obj); +static void paint_combo_callback (GtkWidget *widget, + gpointer data); + +static void select_button_clicked (gint type); +static void select_button_clicked_lt (void); +static void select_button_clicked_gt (void); +static void select_button_clicked_eq (void); +static void raise_selected_obj_to_top (GtkWidget *widget, + gpointer data); +static void lower_selected_obj_to_bottom (GtkWidget *widget, + gpointer data); +static void raise_selected_obj (GtkWidget *widget, + gpointer data); +static void lower_selected_obj (GtkWidget *widget, + gpointer data); + +static void toggle_obj_type (GtkRadioAction *action, + GtkRadioAction *current, + gpointer data); + +static GtkUIManager *create_ui_manager (GtkWidget *window); + + +gboolean +gfig_dialog (void) +{ + GtkWidget *main_hbox; + GtkWidget *vbox; + GFigObj *gfig; + GimpParasite *parasite; + gint newlayer; + GtkWidget *menubar; + GtkWidget *toolbar; + GtkWidget *combo; + GtkWidget *frame; + gint img_width; + gint img_height; + GimpImageType img_type; + GtkWidget *toggle; + GtkWidget *right_vbox; + GtkWidget *hbox; + GtkUIManager *ui_manager; + GtkWidget *empty_label; + gchar *path; + + gimp_ui_init (PLUG_IN_BINARY, TRUE); + + img_width = gimp_drawable_width (gfig_context->drawable_id); + img_height = gimp_drawable_height (gfig_context->drawable_id); + img_type = gimp_drawable_type_with_alpha (gfig_context->drawable_id); + + /* + * See if there is a "gfig" parasite. If so, this is a gfig layer, + * and we start by clearing it to transparent. + * If not, we create a new transparent layer. + */ + gfig_list = NULL; + undo_level = -1; + parasite = gimp_item_get_parasite (gfig_context->drawable_id, "gfig"); + gfig_context->enable_repaint = FALSE; + + /* debug */ + gfig_context->debug_styles = FALSE; + + /* initial default style */ + gfig_read_gimp_style (&gfig_context->default_style, "Base"); + gfig_context->default_style.paint_type = selvals.painttype; + + if (parasite) + { + gimp_drawable_fill (gfig_context->drawable_id, GIMP_FILL_TRANSPARENT); + gfig_context->using_new_layer = FALSE; + gimp_parasite_free (parasite); + } + else + { + newlayer = gimp_layer_new (gfig_context->image_id, "GFig", + img_width, img_height, + img_type, + 100.0, + gimp_image_get_default_new_layer_mode (gfig_context->image_id)); + gimp_drawable_fill (newlayer, GIMP_FILL_TRANSPARENT); + gimp_image_insert_layer (gfig_context->image_id, newlayer, -1, -1); + gfig_context->drawable_id = newlayer; + gfig_context->using_new_layer = TRUE; + } + + gfig_stock_init (); + + path = gimp_gimprc_query ("gfig-path"); + + if (path) + { + gfig_path = g_filename_from_utf8 (path, -1, NULL, NULL, NULL); + g_free (path); + } + else + { + gchar *gimprc = gimp_personal_rc_file ("gimprc"); + gchar *full_path = gimp_config_build_data_path ("gfig"); + gchar *esc_path = g_strescape (full_path, NULL); + g_free (full_path); + + g_message (_("No %s in gimprc:\n" + "You need to add an entry like\n" + "(%s \"%s\")\n" + "to your %s file."), + "gfig-path", "gfig-path", esc_path, + gimp_filename_to_utf8 (gimprc)); + + g_free (gimprc); + g_free (esc_path); + } + + /* Start building the dialog up */ + top_level_dlg = gimp_dialog_new (_("Gfig"), PLUG_IN_ROLE, + NULL, 0, + gimp_standard_help_func, PLUG_IN_PROC, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Close"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (top_level_dlg), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + g_signal_connect (top_level_dlg, "response", + G_CALLBACK (gfig_response), + top_level_dlg); + + /* build the menu */ + ui_manager = create_ui_manager (top_level_dlg); + menubar = gtk_ui_manager_get_widget (ui_manager, "/ui/gfig-menubar"); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (top_level_dlg))), + menubar, FALSE, FALSE, 0); + gtk_widget_show (menubar); + toolbar = gtk_ui_manager_get_widget (ui_manager, "/ui/gfig-toolbar"); + gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (top_level_dlg))), + toolbar, FALSE, FALSE, 0); + gtk_widget_show (toolbar); + + gfig_dialog_action_set_sensitive ("undo", undo_level >= 0); + + /* Main box */ + main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); + gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 12); + gtk_box_pack_end (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (top_level_dlg))), + main_hbox, TRUE, TRUE, 0); + + /* Preview itself */ + gtk_box_pack_start (GTK_BOX (main_hbox), make_preview (), FALSE, FALSE, 0); + + gtk_widget_show (gfig_context->preview); + + right_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + gtk_box_pack_start (GTK_BOX (main_hbox), right_vbox, FALSE, FALSE, 0); + gtk_widget_show (right_vbox); + + /* Tool options notebook */ + frame = gimp_frame_new ( _("Tool Options")); + gtk_box_pack_start (GTK_BOX (right_vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + tool_options_notebook = gtk_notebook_new (); + gtk_container_add (GTK_CONTAINER (frame), tool_options_notebook); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (tool_options_notebook), FALSE); + gtk_notebook_set_show_border (GTK_NOTEBOOK (tool_options_notebook), FALSE); + gtk_widget_show (tool_options_notebook); + create_notebook_pages (tool_options_notebook); + + /* Stroke frame on right side */ + frame = gimp_frame_new (NULL); + gtk_box_pack_start (GTK_BOX (right_vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + gfig_context->paint_type_toggle = + toggle = gtk_check_button_new_with_mnemonic (_("_Stroke")); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), selvals.painttype); + gtk_frame_set_label_widget (GTK_FRAME (frame), toggle); + gtk_widget_show (toggle); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_container_add (GTK_CONTAINER (frame), hbox); + gtk_widget_show (hbox); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0); + gtk_widget_show (vbox); + + gtk_widget_set_sensitive (vbox, selvals.painttype); + g_signal_connect (toggle, "toggled", + G_CALLBACK (set_paint_type_callback), + vbox); + + /* foreground color button in Stroke frame*/ + gfig_context->fg_color = g_new0 (GimpRGB, 1); + gfig_context->fg_color_button = gimp_color_button_new ("Foreground", + SEL_BUTTON_WIDTH, + SEL_BUTTON_HEIGHT, + gfig_context->fg_color, + GIMP_COLOR_AREA_SMALL_CHECKS); + g_signal_connect (gfig_context->fg_color_button, "color-changed", + G_CALLBACK (set_foreground_callback), + gfig_context->fg_color); + gimp_color_button_set_color (GIMP_COLOR_BUTTON (gfig_context->fg_color_button), + &gfig_context->default_style.foreground); + gtk_box_pack_start (GTK_BOX (vbox), gfig_context->fg_color_button, + FALSE, FALSE, 0); + gtk_widget_show (gfig_context->fg_color_button); + + /* brush selector in Stroke frame */ + gfig_context->brush_select + = gimp_brush_select_button_new ("Brush", + gfig_context->default_style.brush_name, + -1.0, -1, -1); + g_signal_connect (gfig_context->brush_select, "brush-set", + G_CALLBACK (gfig_brush_changed_callback), NULL); + gtk_box_pack_start (GTK_BOX (vbox), gfig_context->brush_select, + FALSE, FALSE, 0); + gtk_widget_show (gfig_context->brush_select); + + /* Fill frame on right side */ + frame = gimp_frame_new (_("Fill")); + gtk_box_pack_start (GTK_BOX (right_vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_container_add (GTK_CONTAINER (frame), hbox); + gtk_widget_show (hbox); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0); + gtk_widget_show (vbox); + + /* fill style combo box in Style frame */ + gfig_context->fillstyle_combo = combo + = gimp_int_combo_box_new (_("No fill"), FILL_NONE, + _("Color fill"), FILL_COLOR, + _("Pattern fill"), FILL_PATTERN, + _("Shape gradient"), FILL_GRADIENT, + _("Vertical gradient"), FILL_VERTICAL, + _("Horizontal gradient"), FILL_HORIZONTAL, + NULL); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), 0); + g_signal_connect (combo, "changed", + G_CALLBACK (select_filltype_callback), + NULL); + gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0); + gtk_widget_show (combo); + + fill_type_notebook = gtk_notebook_new (); + gtk_box_pack_start (GTK_BOX (vbox), fill_type_notebook, FALSE, FALSE, 0); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (fill_type_notebook), FALSE); + gtk_notebook_set_show_border (GTK_NOTEBOOK (fill_type_notebook), FALSE); + gtk_widget_show (fill_type_notebook); + + /* An empty page for "No fill" */ + empty_label = gtk_label_new (""); + gtk_widget_show (empty_label); + gtk_notebook_append_page (GTK_NOTEBOOK (fill_type_notebook), + empty_label, NULL); + + /* A page for the fill color button */ + gfig_context->bg_color = g_new0 (GimpRGB, 1); + gfig_context->bg_color_button = gimp_color_button_new ("Background", + SEL_BUTTON_WIDTH, SEL_BUTTON_HEIGHT, + gfig_context->bg_color, + GIMP_COLOR_AREA_SMALL_CHECKS); + g_signal_connect (gfig_context->bg_color_button, "color-changed", + G_CALLBACK (set_background_callback), + gfig_context->bg_color); + gimp_color_button_set_color (GIMP_COLOR_BUTTON (gfig_context->bg_color_button), + &gfig_context->default_style.background); + gtk_widget_show (gfig_context->bg_color_button); + gtk_notebook_append_page (GTK_NOTEBOOK (fill_type_notebook), + gfig_context->bg_color_button, NULL); + + /* A page for the pattern selector */ + gfig_context->pattern_select + = gimp_pattern_select_button_new ("Pattern", gfig_context->default_style.pattern); + g_signal_connect (gfig_context->pattern_select, "pattern-set", + G_CALLBACK (gfig_pattern_changed_callback), NULL); + gtk_widget_show (gfig_context->pattern_select); + gtk_notebook_append_page (GTK_NOTEBOOK (fill_type_notebook), + gfig_context->pattern_select, NULL); + + /* A page for the gradient selector */ + gfig_context->gradient_select + = gimp_gradient_select_button_new ("Gradient", gfig_context->default_style.gradient); + g_signal_connect (gfig_context->gradient_select, "gradient-set", + G_CALLBACK (gfig_gradient_changed_callback), NULL); + gtk_widget_show (gfig_context->gradient_select); + gtk_notebook_append_page (GTK_NOTEBOOK (fill_type_notebook), + gfig_context->gradient_select, NULL); + + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_box_pack_start (GTK_BOX (right_vbox), vbox, FALSE, FALSE, 0); + gtk_widget_show (vbox); + + /* "show image" checkbutton at bottom of style frame */ + toggle = gtk_check_button_new_with_label (_("Show image")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + gfig_context->show_background); + gtk_box_pack_end (GTK_BOX (vbox), toggle, FALSE, FALSE, 0); + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &gfig_context->show_background); + g_signal_connect_swapped (toggle, "toggled", + G_CALLBACK (gtk_widget_queue_draw), + gfig_context->preview); + gtk_widget_show (toggle); + + /* "snap to grid" checkbutton at bottom of style frame */ + toggle = gtk_check_button_new_with_label (C_("checkbutton", "Snap to grid")); + gtk_box_pack_end (GTK_BOX (vbox), 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; + + /* "show grid" checkbutton at bottom of style frame */ + toggle = gtk_check_button_new_with_label (_("Show grid")); + gtk_box_pack_end (GTK_BOX (vbox), 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; + + /* Load saved objects */ + gfig_list_load_all (gfig_path); + + /* Setup initial brush settings */ + gfig_context->bdesc.name = gimp_context_get_brush (); + mygimp_brush_info (&gfig_context->bdesc.width, &gfig_context->bdesc.height); + + gtk_widget_show (main_hbox); + + gtk_widget_show (top_level_dlg); + + gfig = gfig_load_from_parasite (); + if (gfig) + { + gfig_list_insert (gfig); + new_obj_2edit (gfig); + gfig_style_set_context_from_style (&gfig_context->default_style); + gfig_style_apply (&gfig_context->default_style); + } + + gfig_context->enable_repaint = TRUE; + gfig_paint_callback (); + + gtk_main (); + + /* FIXME */ + return TRUE; +} + +static void +gfig_response (GtkWidget *widget, + gint response_id, + gpointer data) +{ + GFigObj *gfig; + + switch (response_id) + { + case GTK_RESPONSE_DELETE_EVENT: + case GTK_RESPONSE_CANCEL: + /* if we created a new layer, delete it */ + if (gfig_context->using_new_layer) + { + gimp_image_remove_layer (gfig_context->image_id, + gfig_context->drawable_id); + } + else /* revert back to the original figure */ + { + free_all_objs (gfig_context->current_obj->obj_list); + gfig_context->current_obj->obj_list = NULL; + gfig = gfig_load_from_parasite (); + if (gfig) + { + gfig_list_insert (gfig); + new_obj_2edit (gfig); + } + gfig_context->enable_repaint = TRUE; + gfig_paint_callback (); + } + break; + + case GTK_RESPONSE_OK: /* Close button */ + gfig_save_as_parasite (); + break; + + default: + break; + } + + gtk_widget_destroy (widget); + gtk_main_quit (); +} + +void +gfig_dialog_action_set_sensitive (const gchar *name, + gboolean sensitive) +{ + g_return_if_fail (name != NULL); + + if (gfig_actions) + { + GtkAction *action = gtk_action_group_get_action (gfig_actions, name); + + if (! action) + { + g_warning ("%s: Unable to set sensitivity of action " + "which doesn't exist: %s", + G_STRFUNC, name); + return; + } + + g_object_set (action, "sensitive", sensitive ? TRUE : FALSE, NULL); + } +} + +static gchar * +gfig_get_user_writable_dir (void) +{ + if (gfig_path) + { + GList *list; + gchar *dir; + + list = gimp_path_parse (gfig_path, 256, FALSE, NULL); + dir = gimp_path_get_user_writable_dir (list); + gimp_path_free (list); + + return dir; + } + + return g_strdup (gimp_directory ()); +} + +static void +gfig_load_action_callback (GtkAction *action, + gpointer data) +{ + static GtkWidget *dialog = NULL; + + if (! dialog) + { + gchar *dir; + + dialog = + gtk_file_chooser_dialog_new (_("Load Gfig Object Collection"), + GTK_WINDOW (data), + GTK_FILE_CHOOSER_ACTION_OPEN, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Open"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + g_object_add_weak_pointer (G_OBJECT (dialog), (gpointer) &dialog); + + g_signal_connect (dialog, "response", + G_CALLBACK (load_file_chooser_response), + NULL); + + dir = gfig_get_user_writable_dir (); + if (dir) + { + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), + dir); + g_free (dir); + } + + gtk_widget_show (dialog); + } + else + { + gtk_window_present (GTK_WINDOW (dialog)); + } +} + +static void +gfig_save_action_callback (GtkAction *action, + gpointer data) +{ + static GtkWidget *dialog = NULL; + + if (!dialog) + { + gchar *dir; + + dialog = + gtk_file_chooser_dialog_new (_("Save Gfig Drawing"), + GTK_WINDOW (data), + GTK_FILE_CHOOSER_ACTION_SAVE, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Save"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), + TRUE); + + g_object_add_weak_pointer (G_OBJECT (dialog), (gpointer) &dialog); + + /* FIXME: GFigObj should be a GObject and g_signal_connect_object() + * should be used here. + */ + g_signal_connect (dialog, "response", + G_CALLBACK (save_file_chooser_response), + gfig_context->current_obj); + + dir = gfig_get_user_writable_dir (); + if (dir) + { + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), dir); + g_free (dir); + } + + gtk_widget_show (dialog); + } + else + { + gtk_window_present (GTK_WINDOW (dialog)); + } +} + +static void +gfig_close_action_callback (GtkAction *action, + gpointer data) +{ + gtk_dialog_response (GTK_DIALOG (data), GTK_RESPONSE_OK); +} + +static void +gfig_undo_action_callback (GtkAction *action, + gpointer data) +{ + if (undo_level >= 0) + { + /* Free current objects an reinstate previous */ + free_all_objs (gfig_context->current_obj->obj_list); + gfig_context->current_obj->obj_list = NULL; + tmp_bezier = tmp_line = obj_creating = NULL; + gfig_context->current_obj->obj_list = undo_table[undo_level]; + undo_level--; + /* Update the screen */ + gtk_widget_queue_draw (gfig_context->preview); + /* And preview */ + gfig_context->current_obj->obj_status |= GFIG_MODIFIED; + if (gfig_context->current_obj->obj_list) + gfig_context->selected_obj = gfig_context->current_obj->obj_list->data; + else + gfig_context->selected_obj = NULL; + } + + gfig_dialog_action_set_sensitive ("undo", undo_level >= 0); + gfig_paint_callback (); +} + +static void +gfig_clear_action_callback (GtkWidget *widget, + gpointer data) +{ + /* Make sure we can get back - if we have some objects to get back to */ + if (!gfig_context->current_obj->obj_list) + return; + + setup_undo (); + /* Free all objects */ + free_all_objs (gfig_context->current_obj->obj_list); + gfig_context->current_obj->obj_list = NULL; + gfig_context->selected_obj = NULL; + obj_creating = NULL; + tmp_line = NULL; + tmp_bezier = NULL; + gtk_widget_queue_draw (gfig_context->preview); + gfig_paint_callback (); +} + +void +draw_item (cairo_t *cr, gboolean fill) +{ + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0); + + if (fill) + { + cairo_fill_preserve (cr); + } + else + { + cairo_set_line_width (cr, 3.0); + cairo_stroke_preserve (cr); + } + + cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0); + cairo_set_line_width (cr, 1.0); + cairo_stroke (cr); +} + +/* Given a point x, y draw a circle */ +void +draw_circle (GdkPoint *p, + gboolean selected, + cairo_t *cr) +{ + if (!selvals.opts.showcontrol) + return; + + cairo_arc (cr, + gfig_scale_x (p->x) + .5, + gfig_scale_y (p->y) + .5, + SQ_SIZE / 2, + 0, 2 * G_PI); + + draw_item (cr, selected); +} + +/* Given a point x, y draw a square around it */ +void +draw_sqr (GdkPoint *p, + gboolean selected, + cairo_t *cr) +{ + if (!selvals.opts.showcontrol) + return; + + cairo_rectangle (cr, + gfig_scale_x (p->x) + .5 - SQ_SIZE / 2, + gfig_scale_y (p->y) + .5 - SQ_SIZE / 2, + SQ_SIZE, + SQ_SIZE); + + draw_item (cr, selected); +} + +static void +gfig_list_load_all (const gchar *path) +{ + /* Make sure to clear any existing gfigs */ + gfig_context->current_obj = NULL; + gfig_list_free_all (); + + if (! gfig_list) + { + GFigObj *gfig; + + /* lets have at least one! */ + gfig = gfig_new (); + gfig->draw_name = g_strdup (_("First Gfig")); + gfig_list_insert (gfig); + } + + gfig_context->current_obj = gfig_list->data; /* set to first entry */ +} + +static void +gfig_list_free_all (void) +{ + g_list_free_full (gfig_list, (GDestroyNotify) gfig_free); + gfig_list = NULL; +} + +static GtkUIManager * +create_ui_manager (GtkWidget *window) +{ + static GtkActionEntry actions[] = + { + { "gfig-menubar", NULL, "GFig Menu" }, + + { "gfig-file-menu", NULL, "_File" }, + + { "open", GIMP_ICON_DOCUMENT_OPEN, + N_("_Open..."), "<control>O", NULL, + G_CALLBACK (gfig_load_action_callback) }, + + { "save", GIMP_ICON_DOCUMENT_SAVE, + N_("_Save..."), "<control>S", NULL, + G_CALLBACK (gfig_save_action_callback) }, + + { "close", GIMP_ICON_CLOSE, + N_("_Close"), "<control>C", NULL, + G_CALLBACK (gfig_close_action_callback) }, + + { "gfig-edit-menu", NULL, "_Edit" }, + + { "undo", GIMP_ICON_EDIT_UNDO, + N_("_Undo"), "<control>Z", NULL, + G_CALLBACK (gfig_undo_action_callback) }, + + { "clear", GIMP_ICON_EDIT_CLEAR, + N_("_Clear"), NULL, NULL, + G_CALLBACK (gfig_clear_action_callback) }, + + { "grid", GIMP_ICON_GRID, + N_("_Grid"), "<control>G", NULL, + G_CALLBACK (gfig_grid_action_callback) }, + + { "prefs", GIMP_ICON_PREFERENCES_SYSTEM, + N_("_Preferences..."), "<control>P", NULL, + G_CALLBACK (gfig_prefs_action_callback) }, + + { "raise", GIMP_ICON_GO_UP, + N_("_Raise"), "<control>U", N_("Raise selected object"), + G_CALLBACK (raise_selected_obj) }, + + { "lower", GIMP_ICON_GO_DOWN, + N_("_Lower"), "<control>D", N_("Lower selected object"), + G_CALLBACK (lower_selected_obj) }, + + { "top", GIMP_ICON_GO_TOP, + N_("Raise to _top"), "<control>T", N_("Raise selected object to top"), + G_CALLBACK (raise_selected_obj_to_top) }, + + { "bottom", GIMP_ICON_GO_BOTTOM, + N_("Lower to _bottom"), "<control>B", N_("Lower selected object to bottom"), + G_CALLBACK (lower_selected_obj_to_bottom) }, + + { "show_previous", GIMP_ICON_GO_PREVIOUS, + N_("_Previous"), "<control>H", N_("Show previous object"), + G_CALLBACK (select_button_clicked_lt) }, + + { "show_next", GIMP_ICON_GO_NEXT, + N_("_Next"), "<control>L", N_("Show next object"), + G_CALLBACK (select_button_clicked_gt) }, + + { "show_all", GFIG_STOCK_SHOW_ALL, + N_("Show _all"), "<control>A", N_("Show all objects"), + G_CALLBACK (select_button_clicked_eq) } + }; + static GtkRadioActionEntry radio_actions[] = + { + { "line", GFIG_STOCK_LINE, + NULL, "L", N_("Create line"), LINE }, + + { "rectangle", GFIG_STOCK_RECTANGLE, + NULL, "R", N_("Create rectangle"), RECTANGLE }, + + { "circle", GFIG_STOCK_CIRCLE, + NULL, "C", N_("Create circle"), CIRCLE }, + + { "ellipse", GFIG_STOCK_ELLIPSE, + NULL, "E", N_("Create ellipse"), ELLIPSE }, + + { "arc", GFIG_STOCK_CURVE, + NULL, "A", N_("Create arc"), ARC }, + + { "polygon", GFIG_STOCK_POLYGON, + NULL, "P", N_("Create reg polygon"), POLY }, + + { "star", GFIG_STOCK_STAR, + NULL, "S", N_("Create star"), STAR }, + + { "spiral", GFIG_STOCK_SPIRAL, + NULL, "I", N_("Create spiral"), SPIRAL }, + + { "bezier", GFIG_STOCK_BEZIER, + NULL, "B", N_("Create bezier curve. " + "Shift + Button ends object creation."), BEZIER }, + + { "move_obj", GFIG_STOCK_MOVE_OBJECT, + NULL, "M", N_("Move an object"), MOVE_OBJ }, + + { "move_point", GFIG_STOCK_MOVE_POINT, + NULL, "V", N_("Move a single point"), MOVE_POINT }, + + { "copy", GFIG_STOCK_COPY_OBJECT, + NULL, "Y", N_("Copy an object"), COPY_OBJ }, + + { "delete", GFIG_STOCK_DELETE_OBJECT, + NULL, "D", N_("Delete an object"), DEL_OBJ }, + + { "select", GFIG_STOCK_SELECT_OBJECT, + NULL, "A", N_("Select an object"), SELECT_OBJ } + }; + + GtkUIManager *ui_manager = gtk_ui_manager_new (); + + gfig_actions = gtk_action_group_new ("Actions"); + + gtk_action_group_set_translation_domain (gfig_actions, NULL); + + gtk_action_group_add_actions (gfig_actions, + actions, + G_N_ELEMENTS (actions), + window); + gtk_action_group_add_radio_actions (gfig_actions, + radio_actions, + G_N_ELEMENTS (radio_actions), + LINE, + G_CALLBACK (toggle_obj_type), + window); + + gtk_window_add_accel_group (GTK_WINDOW (window), + gtk_ui_manager_get_accel_group (ui_manager)); + gtk_accel_group_lock (gtk_ui_manager_get_accel_group (ui_manager)); + + gtk_ui_manager_insert_action_group (ui_manager, gfig_actions, -1); + g_object_unref (gfig_actions); + + gtk_ui_manager_add_ui_from_string (ui_manager, + "<ui>" + " <menubar name=\"gfig-menubar\">" + " <menu name=\"File\" action=\"gfig-file-menu\">" + " <menuitem action=\"open\" />" + " <menuitem action=\"save\" />" + " <menuitem action=\"close\" />" + " </menu>" + " <menu name=\"Edit\" action=\"gfig-edit-menu\">" + " <menuitem action=\"undo\" />" + " <menuitem action=\"clear\" />" + " <menuitem action=\"grid\" />" + " <menuitem action=\"prefs\" />" + " </menu>" + " </menubar>" + "</ui>", + -1, NULL); + gtk_ui_manager_add_ui_from_string (ui_manager, + "<ui>" + " <toolbar name=\"gfig-toolbar\">" + " <toolitem action=\"line\" />" + " <toolitem action=\"rectangle\" />" + " <toolitem action=\"circle\" />" + " <toolitem action=\"ellipse\" />" + " <toolitem action=\"arc\" />" + " <toolitem action=\"polygon\" />" + " <toolitem action=\"star\" />" + " <toolitem action=\"spiral\" />" + " <toolitem action=\"bezier\" />" + " <toolitem action=\"move_obj\" />" + " <toolitem action=\"move_point\" />" + " <toolitem action=\"copy\" />" + " <toolitem action=\"delete\" />" + " <toolitem action=\"select\" />" + " <separator />" + " <toolitem action=\"raise\" />" + " <toolitem action=\"lower\" />" + " <toolitem action=\"top\" />" + " <toolitem action=\"bottom\" />" + " <separator />" + " <toolitem action=\"show_previous\" />" + " <toolitem action=\"show_next\" />" + " <toolitem action=\"show_all\" />" + " </toolbar>" + "</ui>", + -1, NULL); + + return ui_manager; +} + +static void +tool_option_no_option (GtkWidget *notebook) +{ + GtkWidget *label; + + label = gtk_label_new (_("This tool has no options")); + gtk_widget_show (label); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), label, NULL); +} + +static void +create_notebook_pages (GtkWidget *notebook) +{ + tool_option_no_option (notebook); /* Line */ + tool_option_no_option (notebook); /* Rectangle */ + tool_option_no_option (notebook); /* Circle */ + tool_option_no_option (notebook); /* Ellipse */ + tool_option_no_option (notebook); /* Arc */ + tool_options_poly (notebook); /* Polygon */ + tool_options_star (notebook); /* Star */ + tool_options_spiral (notebook); /* Spiral */ + tool_options_bezier (notebook); /* Bezier */ + tool_option_no_option (notebook); /* Dummy */ + tool_option_no_option (notebook); /* Move Object */ + tool_option_no_option (notebook); /* Move Point */ + tool_option_no_option (notebook); /* Copy Object */ + tool_option_no_option (notebook); /* Delete Object */ +} + +static void +raise_selected_obj_to_top (GtkWidget *widget, + gpointer data) +{ + if (!gfig_context->selected_obj) + return; + + if (g_list_find (gfig_context->current_obj->obj_list, + gfig_context->selected_obj)) + { + gfig_context->current_obj->obj_list = + g_list_remove (gfig_context->current_obj->obj_list, + gfig_context->selected_obj); + gfig_context->current_obj->obj_list = + g_list_append (gfig_context->current_obj->obj_list, + gfig_context->selected_obj); + } + else + { + g_message ("Trying to raise object that does not exist."); + return; + } + + gfig_paint_callback (); +} + +static void +lower_selected_obj_to_bottom (GtkWidget *widget, + gpointer data) +{ + if (!gfig_context->selected_obj) + return; + + if (g_list_find (gfig_context->current_obj->obj_list, + gfig_context->selected_obj)) + { + gfig_context->current_obj->obj_list = + g_list_remove (gfig_context->current_obj->obj_list, + gfig_context->selected_obj); + gfig_context->current_obj->obj_list = + g_list_prepend (gfig_context->current_obj->obj_list, + gfig_context->selected_obj); + } + else + { + g_message ("Trying to lower object that does not exist."); + return; + } + + gfig_paint_callback (); +} + +static void +raise_selected_obj (GtkWidget *widget, + gpointer data) +{ + if (!gfig_context->selected_obj) + return; + + if (g_list_find (gfig_context->current_obj->obj_list, + gfig_context->selected_obj)) + { + int position; + + position = g_list_index (gfig_context->current_obj->obj_list, + gfig_context->selected_obj); + gfig_context->current_obj->obj_list = + g_list_remove (gfig_context->current_obj->obj_list, + gfig_context->selected_obj); + gfig_context->current_obj->obj_list = + g_list_insert (gfig_context->current_obj->obj_list, + gfig_context->selected_obj, + position + 1); + } + else + { + g_message ("Trying to raise object that does not exist."); + return; + } + + gfig_paint_callback (); +} + + +static void +lower_selected_obj (GtkWidget *widget, + gpointer data) +{ + if (!gfig_context->selected_obj) + return; + + if (g_list_find (gfig_context->current_obj->obj_list, + gfig_context->selected_obj)) + { + int position; + + position = g_list_index (gfig_context->current_obj->obj_list, + gfig_context->selected_obj); + gfig_context->current_obj->obj_list = + g_list_remove (gfig_context->current_obj->obj_list, + gfig_context->selected_obj); + gfig_context->current_obj->obj_list = + g_list_insert (gfig_context->current_obj->obj_list, + gfig_context->selected_obj, + MAX (0, position - 1)); + } + else + { + g_message ("Trying to lower object that does not exist."); + return; + } + + gfig_paint_callback (); +} + + +static void +select_filltype_callback (GtkWidget *widget) +{ + gint value; + + gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value); + gtk_notebook_set_current_page (GTK_NOTEBOOK (fill_type_notebook), + MIN (value, FILL_GRADIENT)); + + gfig_context_get_current_style ()->fill_type = (FillType) value; + + gfig_paint_callback (); +} + +static gboolean +gfig_paint_timeout (gpointer data) +{ + gfig_paint_callback (); + + paint_timeout = 0; + + return FALSE; +} + +static void +gfig_paint_delayed (void) +{ + if (paint_timeout) + g_source_remove (paint_timeout); + + paint_timeout = + g_timeout_add (UPDATE_DELAY, gfig_paint_timeout, NULL); +} + +static void +gfig_prefs_action_callback (GtkAction *widget, + gpointer data) +{ + static GtkWidget *dialog = NULL; + + if (!dialog) + { + GtkWidget *main_vbox; + GtkWidget *table; + GtkWidget *toggle; + GtkObject *size_data; + GtkWidget *scale; + GtkAdjustment *scale_data; + + dialog = gimp_dialog_new (_("Options"), "gimp-gfig-options", + GTK_WIDGET (data), 0, NULL, NULL, + + _("_Close"), GTK_RESPONSE_CLOSE, + + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); + + g_object_add_weak_pointer (G_OBJECT (dialog), (gpointer) &dialog); + + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), + NULL); + + main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + main_vbox, TRUE, TRUE, 0); + gtk_widget_show (main_vbox); + + /* Put buttons in */ + toggle = gtk_check_button_new_with_label (_("Show position")); + gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 6); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + selvals.showpos); + 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 (_("Show control points")); + gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 6); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + selvals.opts.showcontrol); + 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; + g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.showcontrol), + (gpointer) &gfig_opt_widget.showcontrol); + + toggle = gtk_check_button_new_with_label (_("Antialiasing")); + gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 6); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), selopt.antia); + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &selopt.antia); + g_signal_connect (toggle, "toggled", + G_CALLBACK (gfig_paint_callback), + NULL); + gtk_widget_show (toggle); + + table = gtk_table_new (4, 4, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 6); + gtk_widget_show (table); + + size_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0, + _("Max undo:"), 100, 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); + + page_menu_bg = gimp_int_combo_box_new (_("Transparent"), LAYER_TRANS_BG, + _("Background"), LAYER_BG_BG, + _("Foreground"), LAYER_FG_BG, + _("White"), LAYER_WHITE_BG, + _("Copy"), LAYER_COPY_BG, + NULL); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (page_menu_bg), selvals.onlayerbg); + + g_signal_connect (page_menu_bg, "changed", + G_CALLBACK (paint_combo_callback), + GINT_TO_POINTER (PAINT_BGS_MENU)); + + gimp_help_set_help_data (page_menu_bg, + _("Layer background type. Copy causes the " + "previous layer to be copied before the " + "draw is performed."), + NULL); + + gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, + _("Background:"), 0.0, 0.5, + page_menu_bg, 2, FALSE); + + toggle = gtk_check_button_new_with_label (_("Feather")); + gtk_table_attach (GTK_TABLE (table), toggle, 2, 3, 2, 3, + GTK_FILL, GTK_FILL, 0, 0); + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &selopt.feather); + g_signal_connect (toggle, "toggled", + G_CALLBACK (gfig_paint_callback), + NULL); + gtk_widget_show (toggle); + + scale_data = (GtkAdjustment *) + gtk_adjustment_new (selopt.feather_radius, 0.0, 100.0, 1.0, 1.0, 0.0); + scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, scale_data); + gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP); + + g_signal_connect (scale_data, "value-changed", + G_CALLBACK (gimp_double_adjustment_update), + &selopt.feather_radius); + g_signal_connect (scale_data, "value-changed", + G_CALLBACK (gfig_paint_delayed), + NULL); + gimp_table_attach_aligned (GTK_TABLE (table), 0, 2, + _("Radius:"), 0.0, 1.0, scale, 1, FALSE); + + gtk_widget_show (dialog); + } + else + { + gtk_window_present (GTK_WINDOW (dialog)); + } +} + +static void +gfig_grid_action_callback (GtkAction *action, + gpointer data) +{ + static GtkWidget *dialog = NULL; + + if (!dialog) + { + GtkWidget *main_vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *combo; + GtkObject *size_data; + GtkObject *sectors_data; + GtkObject *radius_data; + + dialog = gimp_dialog_new (_("Grid"), "gimp-gfig-grid", + GTK_WIDGET (data), 0, NULL, NULL, + + _("_Close"), GTK_RESPONSE_CLOSE, + + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); + + g_object_add_weak_pointer (G_OBJECT (dialog), (gpointer) &dialog); + + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), + NULL); + + main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + main_vbox, TRUE, TRUE, 0); + gtk_widget_show (main_vbox); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + table = gtk_table_new (3, 3, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0); + gtk_widget_show (table); + + size_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0, + _("Grid spacing:"), 100, 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 = GTK_ADJUSTMENT (size_data); + g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.gridspacing), + (gpointer) &gfig_opt_widget.gridspacing); + + sectors_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 3, + _("Polar grid sectors desired:"), 1, 5, + selvals.opts.grid_sectors_desired, + 5, 360, 5, 1, 0, + TRUE, 0, 0, + NULL, NULL); + g_signal_connect (sectors_data, "value-changed", + G_CALLBACK (gimp_int_adjustment_update), + &selvals.opts.grid_sectors_desired); + g_signal_connect (sectors_data, "value-changed", + G_CALLBACK (draw_grid_clear), + NULL); + + gfig_opt_widget.grid_sectors_desired = GTK_ADJUSTMENT (sectors_data); + g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.grid_sectors_desired), + (gpointer) &gfig_opt_widget.grid_sectors_desired); + + + gfig_opt_widget.gridspacing = GTK_ADJUSTMENT (size_data); + g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.gridspacing), + (gpointer) &gfig_opt_widget.gridspacing); + + radius_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 4, + _("Polar grid radius interval:"), 1, 5, + selvals.opts.grid_radius_interval, + 5, 50, 5, 1, 0, + TRUE, 0, 0, + NULL, NULL); + g_signal_connect (radius_data, "value-changed", + G_CALLBACK (gimp_double_adjustment_update), + &selvals.opts.grid_radius_interval); + g_signal_connect (radius_data, "value-changed", + G_CALLBACK (draw_grid_clear), + NULL); + + gfig_opt_widget.grid_radius_interval = GTK_ADJUSTMENT (radius_data); + g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.grid_radius_interval), + (gpointer) &gfig_opt_widget.grid_radius_interval); + + combo = gimp_int_combo_box_new (_("Rectangle"), RECT_GRID, + _("Polar"), POLAR_GRID, + _("Isometric"), ISO_GRID, + NULL); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), selvals.opts.gridtype); + + g_signal_connect (combo, "changed", + G_CALLBACK (gridtype_combo_callback), + GINT_TO_POINTER (GRID_TYPE_MENU)); + + gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, + _("Grid type:"), 0.0, 0.5, + combo, 2, FALSE); + + gfig_opt_widget.gridtypemenu = combo; + g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.gridtypemenu), + (gpointer) &gfig_opt_widget.gridtypemenu); + + combo = gimp_int_combo_box_new (_("Normal"), GFIG_NORMAL_GC, + _("Black"), GFIG_BLACK_GC, + _("White"), GFIG_WHITE_GC, + _("Grey"), GFIG_GREY_GC, + _("Darker"), GFIG_DARKER_GC, + _("Lighter"), GFIG_LIGHTER_GC, + _("Very dark"), GFIG_VERY_DARK_GC, + NULL); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), grid_gc_type); + + g_signal_connect (combo, "changed", + G_CALLBACK (gridtype_combo_callback), + GINT_TO_POINTER (GRID_RENDER_MENU)); + + gimp_table_attach_aligned (GTK_TABLE (table), 0, 2, + _("Grid color:"), 0.0, 0.5, + combo, 2, FALSE); + + gtk_widget_show (dialog); + } + else + { + gtk_window_present (GTK_WINDOW (dialog)); + } +} + +void +options_update (GFigObj *old_obj) +{ + /* Save old vals */ + if (selvals.opts.gridspacing != old_obj->opts.gridspacing) + { + old_obj->opts.gridspacing = selvals.opts.gridspacing; + } + if (selvals.opts.grid_sectors_desired != old_obj->opts.grid_sectors_desired) + { + old_obj->opts.grid_sectors_desired = selvals.opts.grid_sectors_desired; + } + if (selvals.opts.grid_radius_interval != old_obj->opts.grid_radius_interval) + { + old_obj->opts.grid_radius_interval = selvals.opts.grid_radius_interval; + } + 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 != gfig_context->current_obj->opts.gridspacing) + { + if (gfig_opt_widget.gridspacing) + gtk_adjustment_set_value (gfig_opt_widget.gridspacing, + gfig_context->current_obj->opts.gridspacing); + } + if (selvals.opts.grid_sectors_desired != gfig_context->current_obj->opts.grid_sectors_desired) + { + if (gfig_opt_widget.grid_sectors_desired) + gtk_adjustment_set_value (gfig_opt_widget.grid_sectors_desired, + gfig_context->current_obj->opts.grid_sectors_desired); + } + if (selvals.opts.grid_radius_interval != gfig_context->current_obj->opts.grid_radius_interval) + { + if (gfig_opt_widget.grid_radius_interval) + gtk_adjustment_set_value (gfig_opt_widget.grid_radius_interval, + gfig_context->current_obj->opts.grid_radius_interval); + } + if (selvals.opts.gridtype != gfig_context->current_obj->opts.gridtype) + { + if (gfig_opt_widget.gridtypemenu) + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (gfig_opt_widget.gridtypemenu), + gfig_context->current_obj->opts.gridtype); + } + if (selvals.opts.drawgrid != gfig_context->current_obj->opts.drawgrid) + { + if (gfig_opt_widget.drawgrid) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_opt_widget.drawgrid), + gfig_context->current_obj->opts.drawgrid); + } + if (selvals.opts.snap2grid != gfig_context->current_obj->opts.snap2grid) + { + if (gfig_opt_widget.snap2grid) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_opt_widget.snap2grid), + gfig_context->current_obj->opts.snap2grid); + } + if (selvals.opts.lockongrid != gfig_context->current_obj->opts.lockongrid) + { + if (gfig_opt_widget.lockongrid) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_opt_widget.lockongrid), + gfig_context->current_obj->opts.lockongrid); + } + if (selvals.opts.showcontrol != gfig_context->current_obj->opts.showcontrol) + { + if (gfig_opt_widget.showcontrol) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_opt_widget.showcontrol), + gfig_context->current_obj->opts.showcontrol); + } +} + +static void +save_file_chooser_response (GtkFileChooser *chooser, + gint response_id, + GFigObj *obj) +{ + if (response_id == GTK_RESPONSE_OK) + { + gchar *filename; + + filename = gtk_file_chooser_get_filename (chooser); + + obj->filename = filename; + + gfig_context->current_obj = obj; + gfig_save_callbk (); + } + + gtk_widget_destroy (GTK_WIDGET (chooser)); +} + +static GfigObject * +gfig_select_obj_by_number (gint count) +{ + GList *objs; + GfigObject *object = NULL; + gint k; + + gfig_context->selected_obj = NULL; + + for (objs = gfig_context->current_obj->obj_list, k = 0; + objs; + objs = g_list_next (objs), k++) + { + if (k == obj_show_single) + { + object = objs->data; + gfig_context->selected_obj = object; + gfig_style_set_context_from_style (&object->style); + break; + } + } + + return object; +} + +static void +select_button_clicked (gint type) +{ + gint count = 0; + + if (gfig_context->current_obj) + { + count = g_list_length (gfig_context->current_obj->obj_list); + } + + 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; + } + + if (obj_show_single >= 0) + gfig_select_obj_by_number (obj_show_single); + + draw_grid_clear (); + gfig_paint_callback (); +} + +static void +select_button_clicked_lt (void) +{ + select_button_clicked (OBJ_SELECT_LT); +} + +static void +select_button_clicked_gt (void) +{ + select_button_clicked (OBJ_SELECT_GT); +} + +static void +select_button_clicked_eq (void) +{ + select_button_clicked (OBJ_SELECT_EQ); +} + +/* Special case for now - options on poly/star/spiral button */ + +GtkWidget * +num_sides_widget (const gchar *d_title, + gint *num_sides, + gint *which_way, + gint adj_min, + gint adj_max) +{ + GtkWidget *table; + GtkObject *size_data; + + table = gtk_table_new (which_way ? 2 : 1, 3, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_container_set_border_width (GTK_CONTAINER (table), 12); + gtk_widget_show (table); + + size_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0, + _("Sides:"), 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 *combo = gimp_int_combo_box_new (_("Right"), 0, + _("Left"), 1, + NULL); + + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), *which_way); + + g_signal_connect (combo, "changed", + G_CALLBACK (gimp_int_combo_box_get_active), + which_way); + + gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, + _("Orientation:"), 0.0, 0.5, + combo, 1, FALSE); + } + return table; +} + +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_CLONE_PATTERN, + 0.0, 0.0, + seg_count, line_pnts); + break; + } +} + + +static void +paint_combo_callback (GtkWidget *widget, + gpointer data) +{ + gint mtype = GPOINTER_TO_INT (data); + gint value; + + gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value); + + switch (mtype) + { + case PAINT_BGS_MENU: + selvals.onlayerbg = (LayersBGType) value; + break; + + default: + g_return_if_reached (); + break; + } + + gfig_paint_callback (); +} + + +static void +gridtype_combo_callback (GtkWidget *widget, + gpointer data) +{ + gint mtype = GPOINTER_TO_INT (data); + gint value; + + gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value); + + switch (mtype) + { + case GRID_TYPE_MENU: + selvals.opts.gridtype = value; + break; + + case GRID_RENDER_MENU: + grid_gc_type = value; + break; + + default: + g_return_if_reached (); + break; + } + + draw_grid_clear (); +} + +/* + * 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 void +load_file_chooser_response (GtkFileChooser *chooser, + gint response_id, + gpointer data) +{ + if (response_id == GTK_RESPONSE_OK) + { + gchar *filename; + GFigObj *gfig; + GFigObj *current_saved; + + filename = gtk_file_chooser_get_filename (chooser); + + 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 = gfig_context->current_obj; + gfig_context->current_obj = NULL; + gfig = gfig_load (filename, filename); + gfig_context->current_obj = current_saved; + + if (gfig) + { + /* Read only ?*/ + if (access (filename, W_OK)) + gfig->obj_status |= GFIG_READONLY; + + gfig_list_insert (gfig); + new_obj_2edit (gfig); + } + } + + g_free (filename); + } + + gtk_widget_destroy (GTK_WIDGET (chooser)); + gfig_paint_callback (); +} + +void +paint_layer_fill (gdouble x1, gdouble y1, gdouble x2, gdouble y2) +{ + GimpFillType fill_type = GIMP_FILL_FOREGROUND; + Style *current_style; + + current_style = gfig_context_get_current_style (); + + gimp_context_push (); + + gimp_context_set_paint_mode (GIMP_LAYER_MODE_NORMAL_LEGACY); + gimp_context_set_opacity (100.0); + gimp_context_set_gradient_repeat_mode (GIMP_REPEAT_NONE); + gimp_context_set_gradient_reverse (FALSE); + + switch (current_style->fill_type) + { + case FILL_NONE: + return; + + case FILL_COLOR: + fill_type = GIMP_FILL_BACKGROUND; + break; + + case FILL_PATTERN: + fill_type = GIMP_FILL_PATTERN; + break; + + case FILL_GRADIENT: + gimp_drawable_edit_gradient_fill (gfig_context->drawable_id, + GIMP_GRADIENT_SHAPEBURST_DIMPLED, + 0.0, /* offset */ + FALSE, /* supersampling */ + 0, /* max_depth */ + 0.0, /* threshold */ + FALSE, /* dither */ + 0.0, 0.0, /* (x1, y1) - ignored */ + 0.0, 0.0); /* (x2, y2) - ignored */ + return; + case FILL_VERTICAL: + gimp_drawable_edit_gradient_fill (gfig_context->drawable_id, + GIMP_GRADIENT_LINEAR, + 0.0, + FALSE, + 0, + 0.0, + FALSE, + x1, y1, + x1, y2); + return; + case FILL_HORIZONTAL: + gimp_drawable_edit_gradient_fill (gfig_context->drawable_id, + GIMP_GRADIENT_LINEAR, + 0.0, + FALSE, + 0, + 0.0, + FALSE, + x1, y1, + x2, y1); + return; + } + + gimp_context_set_opacity (current_style->fill_opacity); + + gimp_drawable_edit_fill (gfig_context->drawable_id, + fill_type); + + gimp_context_pop (); +} + +void +gfig_paint_callback (void) +{ + GList *objs; + gint ccount = 0; + GfigObject *object; + + if (!gfig_context->enable_repaint || !gfig_context->current_obj) + return; + + objs = gfig_context->current_obj->obj_list; + + gimp_drawable_fill (gfig_context->drawable_id, GIMP_FILL_TRANSPARENT); + + while (objs) + { + if (ccount == obj_show_single || obj_show_single == -1) + { + FillType saved_filltype; + + object = objs->data; + + gfig_style_apply (&object->style); + + saved_filltype = gfig_context_get_current_style ()->fill_type; + gfig_context_get_current_style ()->fill_type = object->style.fill_type; + object->class->paintfunc (object); + gfig_context_get_current_style ()->fill_type = saved_filltype; + } + + objs = g_list_next (objs); + + ccount++; + } + + gimp_displays_flush (); + + if (back_pixbuf) + { + g_object_unref (back_pixbuf); + back_pixbuf = NULL; + } + + gtk_widget_queue_draw (gfig_context->preview); +} + +/* Draw the grid on the screen + */ + +void +draw_grid_clear (void) +{ + /* wipe slate and start again */ + gtk_widget_queue_draw (gfig_context->preview); +} + +static void +toggle_show_image (void) +{ + /* wipe slate and start again */ + draw_grid_clear (); +} + +static void +toggle_obj_type (GtkRadioAction *action, + GtkRadioAction *current, + gpointer data) +{ + static GdkCursor *p_cursors[DEL_OBJ + 1]; + GdkCursorType ctype = GDK_LAST_CURSOR; + DobjType new_type; + + new_type = gtk_radio_action_get_current_value (action); + if (selvals.otype != new_type) + { + /* Mem leak */ + obj_creating = NULL; + tmp_line = NULL; + tmp_bezier = NULL; + + if (new_type < MOVE_OBJ) /* Eeeeek */ + { + obj_show_single = -1; /* Cancel select preview */ + } + /* Update draw areas */ + gtk_widget_queue_draw (gfig_context->preview); + } + + selvals.otype = new_type; + gtk_notebook_set_current_page (GTK_NOTEBOOK (tool_options_notebook), + new_type - 1); + + switch (selvals.otype) + { + case LINE: + case RECTANGLE: + 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]) + { + GdkDisplay *display = gtk_widget_get_display (gfig_context->preview); + + p_cursors[selvals.otype] = gdk_cursor_new_for_display (display, ctype); + } + + gdk_window_set_cursor (gtk_widget_get_window (gfig_context->preview), + p_cursors[selvals.otype]); +} + +/* This could belong in a separate file ... but makes it easier to lump into + * one when compiling the plugin. + */ + +/* Given a number of float co-ords adjust for scaling back to org size */ +/* Size is number of PAIRS of points */ +/* FP + int variants */ + +static void +scale_to_orginal_x (gdouble *list) +{ + *list *= scale_x_factor; +} + +gint +gfig_scale_x (gint x) +{ + if (!selvals.scaletoimage) + return (gint) (x * (1 / scale_x_factor)); + else + return x; +} + +static void +scale_to_orginal_y (gdouble *list) +{ + *list *= scale_y_factor; +} + +gint +gfig_scale_y (gint y) +{ + if (!selvals.scaletoimage) + return (gint) (y * (1 / scale_y_factor)); + else + return y; +} + +/* Pairs x followed by y */ +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 */ +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); + } +} + +void +gfig_draw_arc (gint x, gint y, gint width, gint height, gint angle1, + gint angle2, cairo_t *cr) +{ + cairo_save (cr); + + cairo_translate (cr, gfig_scale_x (x), gfig_scale_y (y)); + cairo_scale (cr, gfig_scale_x (width), gfig_scale_y (height)); + + if (angle1 < angle2) + cairo_arc (cr, 0., 0., 1., angle1 * G_PI / 180, angle2 * G_PI / 180); + else + cairo_arc_negative (cr, 0., 0., 1., angle1 * G_PI / 180, angle2 * G_PI / 180); + + cairo_restore (cr); + + draw_item (cr, FALSE); +} + +void +gfig_draw_line (gint x0, gint y0, gint x1, gint y1, cairo_t *cr) +{ + cairo_move_to (cr, gfig_scale_x (x0) + .5, gfig_scale_y (y0) + .5); + cairo_line_to (cr, gfig_scale_x (x1) + .5, gfig_scale_y (y1) + .5); + + draw_item (cr, FALSE); +} |