/*
 * 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.
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <string.h>

#include <gtk/gtk.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-spiral.h"
#include "gfig-star.h"
#include "gfig-stock.h"

#define BRUSH_PREVIEW_SZ 32
#define SEL_BUTTON_WIDTH 100
#define SEL_BUTTON_HEIGHT 20

#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)

#define GRID_TYPE_MENU   1
#define GRID_RENDER_MENU 2
#define GRID_IGNORE      0
#define GRID_HIGHTLIGHT  1
#define GRID_RESTORE     2

#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

/* 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           */
  },
  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             */
  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;
  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 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_combo_callback      (GtkWidget *widget,
                                              gpointer   data);
static void       gfig_grid_action_callback  (GtkAction *action,
                                              gpointer   data);
static void       gfig_prefs_action_callback (GtkAction *action,
                                              gpointer   data);
static gint       gfig_scale_x               (gint       x);
static gint       gfig_scale_y               (gint       y);
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 void     gfig_new_gc                  (void);

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;
  GtkWidget    *toggle;
  GtkWidget    *right_vbox;
  GtkWidget    *hbox;
  GtkUIManager *ui_manager;
  GtkWidget    *empty_label;

  gimp_ui_init ("gfig", TRUE);

  img_width  = gimp_drawable_width (gfig_context->drawable_id);
  img_height = gimp_drawable_height (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_drawable_parasite_find (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_TRANSPARENT_FILL);
      gfig_context->using_new_layer = FALSE;
      gimp_parasite_free (parasite);
    }
  else
    {
      newlayer = gimp_layer_new (gfig_context->image_id, "GFig", img_width, img_height,
                                 GIMP_RGBA_IMAGE, 100., GIMP_NORMAL_MODE);
      gimp_drawable_fill (newlayer, GIMP_TRANSPARENT_FILL);
      gimp_image_add_layer (gfig_context->image_id, newlayer, -1);
      gfig_context->drawable_id = newlayer;
      gfig_context->using_new_layer = TRUE;
    }

  gfig_drawable = gimp_drawable_get (gfig_context->drawable_id);

  gfig_stock_init ();

  gfig_path = gimp_gimprc_query ("gfig-path");

  if (! gfig_path)
    {
      gchar *gimprc = gimp_personal_rc_file ("gimprc");
      gchar *full_path;
      gchar *esc_path;

      full_path =
        g_strconcat ("${gimp_dir}", G_DIR_SEPARATOR_S, "gfig",
                     G_SEARCHPATH_SEPARATOR_S,
                     "${gimp_data_dir}", G_DIR_SEPARATOR_S, "gfig",
                     NULL);
      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"), "gfig",
                                   NULL, 0,
                                   gimp_standard_help_func, HELP_ID,

                                   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                   GTK_STOCK_CLOSE,  GTK_RESPONSE_OK,

                                   NULL);

  g_signal_connect (top_level_dlg, "response",
                    G_CALLBACK (gfig_response),
                    top_level_dlg);
  g_signal_connect (top_level_dlg, "destroy",
                    G_CALLBACK (gtk_main_quit),
                    NULL);

  /* 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 (top_level_dlg)->vbox),
                      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 (top_level_dlg)->vbox),
                      toolbar, FALSE, FALSE, 0);
  gtk_widget_show (toolbar);

  gfig_dialog_action_set_sensitive ("undo", undo_level >= 0);

  /* Main box */
  main_hbox = gtk_hbox_new (FALSE, 12);
  gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 12);
  gtk_box_pack_end (GTK_BOX (GTK_DIALOG (top_level_dlg)->vbox), 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_vbox_new (FALSE, 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_hbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (frame), hbox);
  gtk_widget_show (hbox);

  vbox = gtk_vbox_new (FALSE, 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_new (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_widget_new ("Brush",
                                    gfig_context->default_style.brush_name,
                                    -1, -1, -1,
                                    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_hbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (frame), hbox);
  gtk_widget_show (hbox);

  vbox = gtk_vbox_new (FALSE, 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,
                              _("Gradient fill"), FILL_GRADIENT,
                              NULL);
  gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), 0);
  g_signal_connect (combo, "changed",
                    G_CALLBACK (select_combo_callback),
                    GINT_TO_POINTER (SELECT_TYPE_MENU_FILL));
  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_new (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_widget_new ("Pattern", gfig_context->default_style.pattern,
                                      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_widget_new ("Gradient", gfig_context->default_style.gradient,
                                       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_vbox_new (FALSE, 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 (toggle, "toggled",
                    G_CALLBACK (gfig_preview_expose),
                    NULL);
  gtk_widget_show (toggle);

  /* "snap to grid" checkbutton at bottom of style frame */
  toggle = gtk_check_button_new_with_label (_("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_new_gc (); /* Need this for drawing */

  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_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);
}

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, 16, 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,

                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                     GTK_STOCK_OPEN,   GTK_RESPONSE_OK,

                                     NULL);

      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,

                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                     GTK_STOCK_SAVE,   GTK_RESPONSE_OK,

                                     NULL);

      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 ();
}


/* Given a point x, y draw a circle */
void
draw_circle (GdkPoint *p,
             gboolean  selected)
{
  if (!selvals.opts.showcontrol)
    return;

  gdk_draw_arc (gfig_context->preview->window,
                gfig_gc,
                selected,
                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 */
void
draw_sqr (GdkPoint *p,
          gboolean  selected)
{
  if (!selvals.opts.showcontrol)
    return;

  gdk_draw_rectangle (gfig_context->preview->window,
                      gfig_gc,
                      selected,
                      gfig_scale_x (p->x) - SQ_SIZE / 2,
                      gfig_scale_y (p->y) - SQ_SIZE / 2,
                      SQ_SIZE,
                      SQ_SIZE);
}

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_foreach (gfig_list, (GFunc) gfig_free, NULL);
  g_list_free (gfig_list);
  gfig_list = NULL;
}

static GtkUIManager *
create_ui_manager (GtkWidget *window)
{
  static GtkActionEntry actions[] =
  {
    { "gfig-menubar", NULL, "GFig Menu" },

    { "gfig-file-menu", NULL, "_File" },

    { "open", GTK_STOCK_OPEN,
      NULL, "<control>O", NULL,
      G_CALLBACK (gfig_load_action_callback) },

    { "save", GTK_STOCK_SAVE,
      NULL, "<control>S", NULL,
      G_CALLBACK (gfig_save_action_callback) },

    { "close", GTK_STOCK_CLOSE,
      NULL, "<control>C", NULL,
      G_CALLBACK (gfig_close_action_callback) },

    { "gfig-edit-menu", NULL, "_Edit" },

    { "undo", GTK_STOCK_UNDO,
      N_("_Undo"), "<control>Z", NULL,
      G_CALLBACK (gfig_undo_action_callback) },

    { "clear", GTK_STOCK_CLEAR,
      N_("_Clear"), NULL, NULL,
      G_CALLBACK (gfig_clear_action_callback) },

    { "grid", GIMP_STOCK_GRID,
      N_("_Grid"), "<control>G", NULL,
      G_CALLBACK (gfig_grid_action_callback) },

    { "prefs", GTK_STOCK_PREFERENCES,
      NULL, "<control>P", NULL,
      G_CALLBACK (gfig_prefs_action_callback) },

    { "raise", GTK_STOCK_GO_UP,
      NULL, "<control>U", N_("Raise selected object"),
      G_CALLBACK (raise_selected_obj) },

    { "lower", GTK_STOCK_GO_DOWN,
      NULL, "<control>D", N_("Lower selected object"),
      G_CALLBACK (lower_selected_obj) },

    { "top", GTK_STOCK_GOTO_TOP,
      NULL, "<control>T", N_("Raise selected object to top"),
      G_CALLBACK (raise_selected_obj_to_top) },

    { "bottom", GTK_STOCK_GOTO_BOTTOM,
      NULL, "<control>B", N_("Lower selected object to bottom"),
      G_CALLBACK (lower_selected_obj_to_bottom) },

    { "show_previous", GTK_STOCK_GO_BACK,
      NULL, "<control>H", N_("Show previous object"),
      G_CALLBACK (select_button_clicked_lt) },

    { "show_next", GTK_STOCK_GO_FORWARD,
      NULL, "<control>L", N_("Show next object"),
      G_CALLBACK (select_button_clicked_gt) },

    { "show_all", GFIG_STOCK_SHOW_ALL,
      NULL, "<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 },

    { "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=\"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);   /* 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_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);
  gtk_notebook_set_current_page (GTK_NOTEBOOK (fill_type_notebook), value);

  switch (mtype)
    {
    case SELECT_TYPE_MENU:
      selopt.type = (SelectionType) value;
      break;

    case SELECT_ARCTYPE_MENU:
      selopt.as_pie = (ArcType) value;
      break;

    case SELECT_TYPE_MENU_FILL:
      gfig_context_get_current_style ()->fill_type = (FillType) value;
      break;

    default:
      g_return_if_reached ();
      break;
    }

  gfig_paint_callback ();
}

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;
      GtkObject *scale_data;

      dialog = gimp_dialog_new (_("Options"), "gfig-options",
                                GTK_WIDGET (data), 0, NULL, NULL,

                                GTK_STOCK_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_vbox_new (FALSE, 0);
      gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
      gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
      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), 0);

      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 =
        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);
      g_signal_connect (scale_data, "value_changed",
                        G_CALLBACK (gfig_paint_callback),
                        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;

      dialog = gimp_dialog_new (_("Grid"), "gfig-grid",
                                GTK_WIDGET (data), 0, NULL, NULL,

                                GTK_STOCK_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_vbox_new (FALSE, 0);
      gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
      gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
      gtk_widget_show (main_vbox);

      hbox = gtk_hbox_new (FALSE, 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);

      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), 0);

      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"),    GTK_STATE_NORMAL,
                                      _("Black"),     GFIG_BLACK_GC,
                                      _("White"),     GFIG_WHITE_GC,
                                      _("Grey"),      GFIG_GREY_GC,
                                      _("Darker"),    GTK_STATE_ACTIVE,
                                      _("Lighter"),   GTK_STATE_PRELIGHT,
                                      _("Very dark"), GTK_STATE_SELECTED,
                                      NULL);
      gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), 0);

      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.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.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;
      GFigObj *real_current;

      filename = gtk_file_chooser_get_filename (chooser);

      obj->filename = filename;

      real_current = gfig_context->current_obj;
      gfig_context->current_obj = obj;
      gfig_save_callbk ();
      gfig_context->current_obj = gfig_context->current_obj;
    }

  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 (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_PATTERN_CLONE,
                  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_TYPE_MENU:
      selvals.painttype = (PaintType) value;

    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 (void)
{
  GimpBucketFillMode fill_mode;
  Style *current_style;

  current_style = gfig_context_get_current_style ();

  switch (current_style->fill_type)
    {
    case FILL_NONE:
      return;

    case FILL_COLOR:
      fill_mode = GIMP_BG_BUCKET_FILL;
      break;

    case FILL_PATTERN:
      fill_mode = GIMP_PATTERN_BUCKET_FILL;
      break;

    case FILL_GRADIENT:
      gimp_edit_blend (gfig_context->drawable_id,
                       GIMP_CUSTOM_MODE,
                       GIMP_NORMAL_MODE,
                       GIMP_GRADIENT_SHAPEBURST_DIMPLED,
                       100.0,             /* opacity            */
                       0.0,               /* offset             */
                       GIMP_REPEAT_NONE,
                       FALSE,             /* reverse            */
                       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;
    default:
      return;
    }

  gimp_edit_bucket_fill (gfig_context->drawable_id,
                         fill_mode,    /* Fill mode */
                         GIMP_NORMAL_MODE,
                         current_style->fill_opacity, /* Fill opacity */
                         0.0,                 /* threshold - ignored */
                         FALSE,               /* Sample merged - ignored */
                         0.0,                 /* x - ignored */
                         0.0);                /* y - ignored */
}

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_TRANSPARENT_FILL);

  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;

          gimp_selection_none (gfig_context->image_id);
        }

      objs = g_list_next (objs);

      ccount++;
    }

  gimp_displays_flush ();

  if (back_pixbuf)
    {
      g_object_unref (back_pixbuf);
      back_pixbuf = NULL;
    }

  gfig_preview_expose (gfig_context->preview, NULL);
}

/* 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 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 (gfig_context->preview->window, 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 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 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;
}

/* 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)
{
  gdk_draw_arc (gfig_context->preview->window,
                gfig_gc,
                FALSE,
                gfig_scale_x (x - width),
                gfig_scale_y (y - height),
                gfig_scale_x (2 * width),
                gfig_scale_y (2 * height),
                angle1 * 64,
                angle2 * 64);
}

void
gfig_draw_line (gint x0, gint y0, gint x1, gint y1)
{
  gdk_draw_line (gfig_context->preview->window,
                 gfig_gc,
                 gfig_scale_x (x0),
                 gfig_scale_y (y0),
                 gfig_scale_x (x1),
                 gfig_scale_y (y1));
}

static void
gfig_new_gc (void)
{
  GdkColor fg, bg;

  /*  create a new graphics context  */
  gfig_gc = gdk_gc_new (gfig_context->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);
}
