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

#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 =
{
  GIMP_CHANNEL_OP_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
{
  GtkWidget *gridspacing;
  GtkWidget *grid_sectors_desired;
  GtkWidget *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 void       shape_change_state         (GSimpleAction *action,
                                              GVariant      *new_state,
                                              gpointer       user_data);

static void       gfig_response              (GtkWidget *widget,
                                              gint       response_id,
                                              gpointer   data);
static void       gfig_load_action           (GSimpleAction *action,
                                              GVariant      *parameter,
                                              gpointer       user_data);
static void       gfig_save_action           (GSimpleAction *action,
                                              GVariant      *parameter,
                                              gpointer       user_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_close_action          (GSimpleAction *action,
                                              GVariant      *parameter,
                                              gpointer       user_data);
static void       gfig_undo_action           (GSimpleAction *action,
                                              GVariant      *parameter,
                                              gpointer       user_data);
static void       gfig_clear_action          (GSimpleAction *action,
                                              GVariant      *parameter,
                                              gpointer       user_data);
static void       gfig_grid_action           (GSimpleAction *action,
                                              GVariant      *parameter,
                                              gpointer       user_data);
static void       gfig_prefs_action          (GSimpleAction *action,
                                              GVariant      *parameter,
                                              gpointer       user_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     (GSimpleAction *action,
                                              GVariant      *parameter,
                                              gpointer       user_data);
static void     select_button_clicked_gt     (GSimpleAction *action,
                                              GVariant      *parameter,
                                              gpointer       user_data);
static void     select_button_clicked_eq     (GSimpleAction *action,
                                              GVariant      *parameter,
                                              gpointer       user_data);
static void     raise_selected_obj_to_top    (GSimpleAction *action,
                                              GVariant      *parameter,
                                              gpointer       user_data);
static void     lower_selected_obj_to_bottom (GSimpleAction *action,
                                              GVariant      *parameter,
                                              gpointer       user_data);
static void     raise_selected_obj           (GSimpleAction *action,
                                              GVariant      *parameter,
                                              gpointer       user_data);
static void     lower_selected_obj           (GSimpleAction *action,
                                              GVariant      *parameter,
                                              gpointer       user_data);

static void     toggle_obj_type              (DobjType   new_type);

/* GAction helper methods */
static GtkWidget * add_tool_button            (GtkWidget     *toolbar,
                                               const char    *action,
                                               const char    *icon,
                                               const char    *label,
                                               const char    *tooltip);
static GtkWidget * add_toggle_button          (GtkWidget     *toolbar,
                                               const char    *action,
                                               const char    *icon,
                                               const char    *label,
                                               const char    *tooltip);
static void        add_tool_separator         (GtkWidget     *toolbar,
                                               gboolean       expand);

static const GActionEntry ACTIONS[] =
{
  /* Sub-menu options */
  { "open", gfig_load_action },
  { "save", gfig_save_action },
  { "close", gfig_close_action },
  { "undo", gfig_undo_action },
  { "clear", gfig_clear_action },
  { "grid", gfig_grid_action },
  { "preferences", gfig_prefs_action },

  /* Toolbar buttons */
  { "raise", raise_selected_obj },
  { "lower", lower_selected_obj },
  { "top", raise_selected_obj_to_top },
  { "bottom", lower_selected_obj_to_bottom },
  { "show-prev", select_button_clicked_lt },
  { "show-next", select_button_clicked_gt },
  { "show-all", select_button_clicked_eq },

  /* RadioButtons - only the default state is shown here. */
  { "shape", shape_change_state, "s", "'line'", NULL },
};

gboolean
gfig_dialog (GimpGfig *gfig)
{
  GAction      *action;
  GtkWidget    *main_hbox;
  GtkWidget    *vbox;
  GFigObj      *gfig_obj;
  GimpParasite *parasite;
  GimpLayer    *newlayer;
  GtkWidget    *menubar;
  GMenuModel   *model;
  GtkWidget    *toolbar;
  GtkWidget    *combo;
  GtkWidget    *frame;
  gint          img_width;
  gint          img_height;
  GimpImageType img_type;
  GtkWidget    *toggle;
  GtkWidget    *right_vbox;
  GtkWidget    *hbox;
  GtkWidget    *empty_label;
  gchar        *path;

  gimp_ui_init (PLUG_IN_BINARY);

  img_width  = gimp_drawable_get_width (gfig_context->drawable);
  img_height = gimp_drawable_get_height (gfig_context->drawable);
  img_type   = gimp_drawable_type_with_alpha (gfig_context->drawable);

  /*
   * 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 (GIMP_ITEM (gfig_context->drawable), "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, GIMP_FILL_TRANSPARENT);
      gfig_context->using_new_layer = FALSE;
      gimp_parasite_free (parasite);
    }
  else
    {
      newlayer = gimp_layer_new (gfig_context->image, "GFig",
                                 img_width, img_height,
                                 img_type,
                                 100.0,
                                 gimp_image_get_default_new_layer_mode (gfig_context->image));
      gimp_drawable_fill (GIMP_DRAWABLE (newlayer), GIMP_FILL_TRANSPARENT);
      gimp_image_insert_layer (gfig_context->image, newlayer, NULL, -1);
      gfig_context->drawable = GIMP_DRAWABLE (newlayer);
      gfig_context->using_new_layer = TRUE;
    }

  gfig_icons_init ();

  path = gimp_gimprc_query ("gfig-path");

  if (path)
    {
      gfig_path = g_filename_from_utf8 (path, -1, NULL, NULL, NULL);
      g_free (path);
    }
  else
    {
      GFile *gimprc    = gimp_directory_file ("gimprc", NULL);
      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_file_get_utf8_name (gimprc));

      g_object_unref (gimprc);
      g_free (esc_path);
    }

  /* Start building the dialog up */
  gfig->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_window_set_application (GTK_WINDOW (gfig->top_level_dlg), gfig->app);
  gimp_window_set_transient (GTK_WINDOW (gfig->top_level_dlg));

  gimp_dialog_set_alternative_button_order (GTK_DIALOG (gfig->top_level_dlg),
                                           GTK_RESPONSE_OK,
                                           GTK_RESPONSE_CANCEL,
                                           -1);

  g_signal_connect (gfig->top_level_dlg, "response",
                    G_CALLBACK (gfig_response),
                    gfig);

  /* build the menu */
  g_action_map_add_action_entries (G_ACTION_MAP (gfig->app),
                                   ACTIONS, G_N_ELEMENTS (ACTIONS),
                                   gfig);

  model = G_MENU_MODEL (gtk_builder_get_object (gfig->builder, "gfig-menubar"));
  menubar = gtk_menu_bar_new_from_model (model);

  gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (gfig->top_level_dlg))),
                      menubar, FALSE, FALSE, 0);
  gtk_widget_show (menubar);

  toolbar = gtk_toolbar_new ();
  add_toggle_button (toolbar, "app.shape::line", GFIG_ICON_LINE,
                     _("Line"), _("Create line"));
  add_toggle_button (toolbar, "app.shape::rectangle", GFIG_ICON_RECTANGLE,
                   _("Rectangle"), _("Create rectangle"));
  add_toggle_button (toolbar, "app.shape::circle", GFIG_ICON_CIRCLE,
                     _("Circle"), _("Create circle"));
  add_toggle_button (toolbar, "app.shape::ellipse", GFIG_ICON_ELLIPSE,
                     _("Ellipse"), _("Create ellipse"));
  add_toggle_button (toolbar, "app.shape::arc", GFIG_ICON_CURVE,
                     _("Arc"), _("Create arc"));
  add_toggle_button (toolbar, "app.shape::polygon", GFIG_ICON_POLYGON,
                     _("Polygon"), _("Create reg polygon"));
  add_toggle_button (toolbar, "app.shape::star", GFIG_ICON_STAR,
                     _("Star"), _("Create star"));
  add_toggle_button (toolbar, "app.shape::spiral", GFIG_ICON_SPIRAL,
                     _("Spiral"), _("Create spiral"));
  add_toggle_button (toolbar, "app.shape::bezier", GFIG_ICON_BEZIER,
                     _("Bezier"), _("Create bezier curve. "
                                    "Shift + Button ends object creation."));
  add_toggle_button (toolbar, "app.shape::move-obj", GFIG_ICON_MOVE_OBJECT,
                     _("Move Object"), _("Move an object"));
  add_toggle_button (toolbar, "app.shape::move-point", GFIG_ICON_MOVE_POINT,
                     _("Move Point"), _("Move a single point"));
  add_toggle_button (toolbar, "app.shape::copy", GFIG_ICON_COPY_OBJECT,
                     _("Copy"), _("Copy an object"));
  add_toggle_button (toolbar, "app.shape::delete", GFIG_ICON_DELETE_OBJECT,
                     _("Delete"), _("Delete an object"));
  add_toggle_button (toolbar, "app.shape::select", GIMP_ICON_CURSOR,
                     _("Select"), _("Select an object"));
  add_tool_separator (toolbar, FALSE);
  add_tool_button (toolbar, "app.raise", GIMP_ICON_GO_UP,
                   _("Raise"), _("Raise selected object"));
  add_tool_button (toolbar, "app.lower", GIMP_ICON_GO_DOWN,
                   _("Lower"), _("Lower selected object"));
  add_tool_button (toolbar, "app.raise", GIMP_ICON_GO_TOP,
                   _("To Top"), _("Raise selected object to top"));
  add_tool_button (toolbar, "app.lower", GIMP_ICON_GO_BOTTOM,
                   _("To Bottom"), _("Lower selected object to bottom"));
  add_tool_separator (toolbar, FALSE);
  add_tool_button (toolbar, "app.show-prev", GIMP_ICON_GO_PREVIOUS,
                   _("Show Previous"), _("Show previous object"));
  add_tool_button (toolbar, "app.show-next", GIMP_ICON_GO_NEXT,
                   _("Show Next"), _("Show next object"));
  add_tool_button (toolbar, "app.show-all", GFIG_ICON_SHOW_ALL,
                   _("Show All"), _("Show all objects"));

  gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (gfig->top_level_dlg))),
                      toolbar, FALSE, FALSE, 0);
  gtk_widget_show (toolbar);

  action = g_action_map_lookup_action (G_ACTION_MAP (gfig->app), "undo");
  g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
                               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 (gfig->top_level_dlg))),
                    main_hbox, TRUE, TRUE, 0);

  /* Preview itself */
  gtk_box_pack_start (GTK_BOX (main_hbox), make_preview (gfig), FALSE, FALSE, 0);

  gtk_widget_show (gfig_context->preview);

  right_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
  gtk_widget_set_hexpand (right_vbox, FALSE);
  gtk_widget_set_halign (right_vbox, GTK_ALIGN_START);
  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 = gegl_color_duplicate (gfig_context->default_style.foreground);
  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);
  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_chooser_new (_("Brush"), _("Brush"),
                              gfig_context->default_style.brush);
  g_signal_connect (gfig_context->brush_select, "resource-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 = gegl_color_duplicate (gfig_context->default_style.background);
  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);
  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_chooser_new (_("Pattern"), _("Pattern"),
                                gfig_context->default_style.pattern);
  g_signal_connect (gfig_context->pattern_select, "resource-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_chooser_new (_("Gradient"), _("Gradient"),
                                 gfig_context->default_style.gradient);
  g_signal_connect (gfig_context->gradient_select, "resource-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 */
  if (gimp_context_get_brush ())
    set_context_bdesc (gimp_context_get_brush ());

  gtk_widget_show (main_hbox);

  gtk_widget_show (gfig->top_level_dlg);

  gfig_obj = gfig_load_from_parasite (gfig);
  if (gfig_obj)
    {
      gfig_list_insert (gfig_obj);
      new_obj_2edit (gfig, gfig_obj);
      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 ();

  /* FIXME */
  return TRUE;
}

static void shape_change_state (GSimpleAction *action,
                                GVariant      *new_state,
                                gpointer       user_data)
{
  gchar    *str;
  DobjType  new_type = LINE;

  str = g_strdup_printf ("%s",  g_variant_get_string (new_state, NULL));

  if (! strcmp (str, "line"))
    new_type = LINE;
  else if (! strcmp (str, "rectangle"))
    new_type = RECTANGLE;
  else if (! strcmp (str, "circle"))
    new_type = CIRCLE;
  else if (! strcmp (str, "ellipse"))
    new_type = ELLIPSE;
  else if (! strcmp (str, "arc"))
    new_type = ARC;
  else if (! strcmp (str, "polygon"))
    new_type = POLY;
  else if (! strcmp (str, "star"))
    new_type = STAR;
  else if (! strcmp (str, "spiral"))
    new_type = SPIRAL;
  else if (! strcmp (str, "bezier"))
    new_type = BEZIER;
  else if (! strcmp (str, "move-obj"))
    new_type = MOVE_OBJ;
  else if (! strcmp (str, "move-point"))
    new_type = MOVE_POINT;
  else if (! strcmp (str, "copy"))
    new_type = COPY_OBJ;
  else if (! strcmp (str, "delete"))
    new_type = DEL_OBJ;
  else if (! strcmp (str, "select"))
    new_type = SELECT_OBJ;

  g_free (str);

  g_simple_action_set_state (action, new_state);
  toggle_obj_type (new_type);
}

static void
gfig_response (GtkWidget *widget,
               gint       response_id,
               gpointer   data)
{
  GFigObj *gfig_obj;

  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,
                                   GIMP_LAYER (gfig_context->drawable));
        }
      else /* revert back to the original figure */
        {
          free_all_objs (gfig_context->current_obj->obj_list);
          gfig_context->current_obj->obj_list = NULL;
          gfig_obj = gfig_load_from_parasite (GIMP_GFIG (data));
          if (gfig_obj)
            {
              gfig_list_insert (gfig_obj);
              new_obj_2edit (GIMP_GFIG (data), gfig_obj);
            }
          gfig_context->enable_repaint = TRUE;
          gfig_paint_callback ();
        }
      break;

    case GTK_RESPONSE_OK:  /* Close button */
      gfig_save_as_parasite ();
      break;

    default:
      break;
    }

  gtk_application_remove_window ((GIMP_GFIG (data))->app,
                                 GTK_WINDOW ((GIMP_GFIG (data))->top_level_dlg));
}

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 (GSimpleAction *action,
                  GVariant      *parameter,
                  gpointer       user_data)
{
  static GtkWidget *dialog = NULL;

  if (! dialog)
    {
      gchar *dir;

      dialog =
        gtk_file_chooser_dialog_new (_("Load Gfig Object Collection"),
                                     NULL,
                                     GTK_FILE_CHOOSER_ACTION_OPEN,

                                     _("_Cancel"), GTK_RESPONSE_CANCEL,
                                     _("_Open"),   GTK_RESPONSE_OK,

                                     NULL);

      gimp_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),
                        user_data);

      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 (GSimpleAction *action,
                  GVariant      *parameter,
                  gpointer       user_data)
{
  static GtkWidget *dialog = NULL;

  if (!dialog)
    {
      gchar *dir;

      dialog =
        gtk_file_chooser_dialog_new (_("Save Gfig Drawing"),
                                     NULL,
                                     GTK_FILE_CHOOSER_ACTION_SAVE,

                                     _("_Cancel"), GTK_RESPONSE_CANCEL,
                                     _("_Save"),   GTK_RESPONSE_OK,

                                     NULL);

      gimp_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 (GSimpleAction *action,
                   GVariant      *parameter,
                   gpointer       user_data)
{
  GimpGfig *gfig = GIMP_GFIG (user_data);

  gtk_dialog_response (GTK_DIALOG (gfig->top_level_dlg), GTK_RESPONSE_OK);
}

static void
gfig_undo_action (GSimpleAction *action,
                  GVariant      *parameter,
                  gpointer       user_data)
{
  GAction  *action_undo;
  GimpGfig *gfig = GIMP_GFIG (user_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;
    }

  action_undo = g_action_map_lookup_action (G_ACTION_MAP (gfig->app), "undo");
  g_simple_action_set_enabled (G_SIMPLE_ACTION (action_undo),
                               undo_level >= 0);

  gfig_paint_callback ();
}

static void
gfig_clear_action (GSimpleAction *action,
                   GVariant      *parameter,
                   gpointer       user_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 (GIMP_GFIG (user_data));
  /* 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 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 (GSimpleAction *action,
                           GVariant      *parameter,
                           gpointer       user_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 (GSimpleAction *action,
                              GVariant      *parameter,
                              gpointer       user_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 (GSimpleAction *action,
                    GVariant      *parameter,
                    gpointer       user_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 (GSimpleAction *action,
                    GVariant      *parameter,
                    gpointer       user_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 (GSimpleAction *action,
                   GVariant      *parameter,
                   gpointer       user_data)
{
  static GtkWidget *dialog = NULL;

  if (!dialog)
    {
      GtkWidget     *main_vbox;
      GtkWidget     *grid;
      GtkWidget     *toggle;
      GtkWidget     *undo_scale;
      GtkWidget     *scale;
      GtkAdjustment *scale_data;

      dialog = gimp_dialog_new (_("Options"), "gimp-gfig-options",
                                NULL, 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);

      grid = gtk_grid_new ();
      gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
      gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
      gtk_box_pack_start (GTK_BOX (main_vbox), grid, FALSE, FALSE, 6);
      gtk_widget_show (grid);

      scale_data = gtk_adjustment_new (selvals.maxundo, MIN_UNDO, MAX_UNDO, 1.0, 10.0, 0.0);
      undo_scale = gimp_spin_scale_new (scale_data, _("Max undo"), 1);
      g_signal_connect (scale_data, "value-changed",
                        G_CALLBACK (gimp_int_adjustment_update),
                        &selvals.maxundo);
      gtk_grid_attach (GTK_GRID (grid), undo_scale, 0, 0, 3, 1);
      gtk_widget_show (undo_scale);

      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_grid_attach_aligned (GTK_GRID (grid), 0, 1,
                                _("Background:"), 0.0, 0.5,
                                page_menu_bg, 2);

      toggle = gtk_check_button_new_with_label (_("Feather"));
      gtk_grid_attach (GTK_GRID (grid), toggle, 2, 2, 1, 1);
      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 = gimp_spin_scale_new (scale_data, _("Radius"), 1);

      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);
      gtk_grid_attach (GTK_GRID (grid), scale, 0, 2, 2, 1);
      gtk_widget_show (scale);

      gtk_widget_show (dialog);
    }
  else
    {
      gtk_window_present (GTK_WINDOW (dialog));
    }
}

static void
gfig_grid_action (GSimpleAction *action,
                  GVariant      *parameter,
                  gpointer       user_data)
{
  static GtkWidget *dialog = NULL;

  if (!dialog)
    {
      GtkAdjustment *scale_data;
      GtkWidget     *main_vbox;
      GtkWidget     *hbox;
      GtkWidget     *grid;
      GtkWidget     *combo;
      GtkWidget     *grid_scale;
      GtkWidget     *sectors_scale;
      GtkWidget     *radius_scale;

      dialog = gimp_dialog_new (_("Grid"), "gimp-gfig-grid",
                                NULL, 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);

      grid = gtk_grid_new ();
      gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
      gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
      gtk_box_pack_start (GTK_BOX (main_vbox), grid, FALSE, FALSE, 0);
      gtk_widget_show (grid);

      scale_data = gtk_adjustment_new (selvals.opts.gridspacing, MIN_GRID, MAX_GRID, 1.0, 10.0, 0.0);
      grid_scale = gimp_spin_scale_new (scale_data, _("Grid spacing"), 1);
      g_signal_connect (scale_data, "value-changed",
                        G_CALLBACK (gimp_int_adjustment_update),
                        &selvals.opts.gridspacing);
      g_signal_connect (scale_data, "value-changed",
                        G_CALLBACK (draw_grid_clear),
                        NULL);
      gtk_grid_attach (GTK_GRID (grid), grid_scale, 0, 0, 3, 1);
      gtk_widget_show (grid_scale);

      gfig_opt_widget.gridspacing = grid_scale;
      g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.gridspacing),
                                 (gpointer) &gfig_opt_widget.gridspacing);

      scale_data = gtk_adjustment_new (selvals.opts.grid_sectors_desired, 5, 360, 1.0, 10.0, 0.0);
      sectors_scale = gimp_spin_scale_new (scale_data, _("Polar grid sectors desired"), 1);
      g_signal_connect (scale_data, "value-changed",
                        G_CALLBACK (gimp_int_adjustment_update),
                        &selvals.opts.grid_sectors_desired);
      g_signal_connect (scale_data, "value-changed",
                        G_CALLBACK (draw_grid_clear),
                        NULL);
      gtk_grid_attach (GTK_GRID (grid), sectors_scale, 0, 3, 3, 1);
      gtk_widget_show (sectors_scale);


      gfig_opt_widget.grid_sectors_desired = sectors_scale;
      g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.grid_sectors_desired),
                                 (gpointer) &gfig_opt_widget.grid_sectors_desired);

      scale_data = gtk_adjustment_new (selvals.opts.grid_radius_interval, 5, 360, 1.0, 10.0, 0.0);
      radius_scale = gimp_spin_scale_new (scale_data, _("Polar grid radius interval"), 1);
      g_signal_connect (scale_data, "value-changed",
                        G_CALLBACK (gimp_double_adjustment_update),
                        &selvals.opts.grid_radius_interval);
      g_signal_connect (scale_data, "value-changed",
                        G_CALLBACK (draw_grid_clear),
                        NULL);
      gtk_grid_attach (GTK_GRID (grid), radius_scale, 0, 4, 3, 1);
      gtk_widget_show (radius_scale);

      gfig_opt_widget.grid_radius_interval = radius_scale;
      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_grid_attach_aligned (GTK_GRID (grid), 0, 1,
                                _("Grid type:"), 0.0, 0.5,
                                combo, 2);

      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_grid_attach_aligned (GTK_GRID (grid), 0, 2,
                                _("Grid color:"), 0.0, 0.5,
                                combo, 2);

      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_spin_button_set_value (GTK_SPIN_BUTTON (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_spin_button_set_value (GTK_SPIN_BUTTON (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_spin_button_set_value (GTK_SPIN_BUTTON (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 (GSimpleAction *action,
                          GVariant      *parameter,
                          gpointer       user_data)
{
  select_button_clicked (OBJ_SELECT_LT);
}

static void
select_button_clicked_gt (GSimpleAction *action,
                          GVariant      *parameter,
                          gpointer       user_data)
{
  select_button_clicked (OBJ_SELECT_GT);
}

static void
select_button_clicked_eq (GSimpleAction *action,
                          GVariant      *parameter,
                          gpointer       user_data)
{
  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)
{
  GtkAdjustment *scale_data;
  GtkWidget     *grid;
  GtkWidget     *scale;

  grid = gtk_grid_new ();
  gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
  gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
  gtk_container_set_border_width (GTK_CONTAINER (grid), 12);
  gtk_widget_show (grid);

  scale_data = gtk_adjustment_new (*num_sides, adj_min, adj_max, 1.0, 10.0, 0.0);
  scale = gimp_spin_scale_new (scale_data, _("Sides"), 1);
  g_signal_connect (scale_data, "value-changed",
                    G_CALLBACK (gimp_int_adjustment_update),
                    num_sides);
  gtk_grid_attach (GTK_GRID (grid), scale, 0, 0, 3, 1);
  gtk_widget_show (scale);

  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_grid_attach_aligned (GTK_GRID (grid), 0, 1,
                                _("Orientation:"), 0.0, 0.5,
                                combo, 1);
    }
  return grid;
}

void
gfig_paint (BrushType     brush_type,
            GimpDrawable *drawable,
            gint          seg_count,
            gdouble       line_pnts[])
{
  switch (brush_type)
    {
    case BRUSH_BRUSH_TYPE:
      gimp_paintbrush (drawable,
                       selvals.brushfade,
                       seg_count, line_pnts,
                       GIMP_PAINT_CONSTANT,
                       selvals.brushgradient);
      break;

    case BRUSH_PENCIL_TYPE:
      gimp_pencil (drawable,
                   seg_count, line_pnts);
      break;

    case BRUSH_AIRBRUSH_TYPE:
      gimp_airbrush (drawable,
                     selvals.airbrushpressure,
                     seg_count, line_pnts);
      break;

    case BRUSH_PATTERN_TYPE:
      gimp_clone (drawable,
                  drawable,
                  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_obj;
      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_obj = gfig_load (GIMP_GFIG (data), filename, filename);
          gfig_context->current_obj = current_saved;

          if (gfig_obj)
            {
              /* Read only ?*/
              if (access (filename, W_OK))
                gfig_obj->obj_status |= GFIG_READONLY;

              gfig_list_insert (gfig_obj);
              new_obj_2edit (GIMP_GFIG (data), gfig_obj);
            }
        }

      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,
                                        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,
                                        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,
                                        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,
                           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, 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 (DobjType new_type)
{
  /* cache of cursors.
   * Must be larger than DobjType values, i.e. NULL_OPER.
   * Test by clicking the "select object" icon.
   * C ensures is initialized to NULL.
   */
  static GdkCursor *p_cursors[NULL_OPER];

  GdkCursorType     ctype = GDK_LAST_CURSOR;

  if (selvals.otype != new_type)
    {
      /* Mem leak */
      obj_creating = NULL;
      tmp_line = NULL;
      tmp_bezier = NULL;

      if (new_type < MOVE_OBJ) /* Eeeeek */
        {
          g_debug ("%s new_type < MOVE_OBJ", G_STRFUNC);
          obj_show_single = -1; /* Cancel select preview */
        }
      /* Update draw areas */
      gtk_widget_queue_draw (gfig_context->preview);
    }

  g_debug ("%s: old and new obj type %d %d", G_STRFUNC, selvals.otype, new_type);

  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:
      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;
    case OBJ_TYPE_NONE:
      ctype = GDK_CROSSHAIR;
      break;
    default:
      g_debug ("%s: default cursor for object type %d.", G_STRFUNC, selvals.otype);
      ctype = GDK_CROSSHAIR;
      break;
    }

  /* Get cached cursor. */
  if (!p_cursors[selvals.otype])
    {
      GdkCursor *cursor;

      GdkDisplay *display = gtk_widget_get_display (gfig_context->preview);
      cursor = gdk_cursor_new_for_display (display, ctype);
      p_cursors[selvals.otype] = cursor;
    }
  /* Require cursor (possibly from cache) is-a cursor. */
  g_assert (GDK_IS_CURSOR (p_cursors[selvals.otype]));

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

static GtkWidget *
add_tool_button (GtkWidget  *toolbar,
                 const char *action,
                 const char *icon,
                 const char *label,
                 const char *tooltip)
{
  GtkWidget   *tool_icon;
  GtkToolItem *tool_button;

  tool_icon = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_BUTTON);
  gtk_widget_show (GTK_WIDGET (tool_icon));
  tool_button = gtk_tool_button_new (tool_icon, label);
  gtk_widget_show (GTK_WIDGET (tool_button));
  gtk_tool_item_set_tooltip_text (tool_button, tooltip);
  gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (tool_button), action);

  gtk_toolbar_insert (GTK_TOOLBAR (toolbar), tool_button, -1);

  return GTK_WIDGET (tool_button);
}

GtkWidget *
add_toggle_button (GtkWidget  *toolbar,
                   const char *action,
                   const char *icon,
                   const char *label,
                   const char *tooltip)
{
  GtkWidget   *tool_icon;
  GtkToolItem *toggle_tool_button;

  tool_icon = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_BUTTON);
  gtk_widget_show (GTK_WIDGET (tool_icon));

  toggle_tool_button = gtk_toggle_tool_button_new ();
  gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (toggle_tool_button),
                                   tool_icon);
  gtk_tool_button_set_label (GTK_TOOL_BUTTON (toggle_tool_button), label);
  gtk_widget_show (GTK_WIDGET (toggle_tool_button));
  gtk_tool_item_set_tooltip_text (toggle_tool_button, tooltip);
  gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (toggle_tool_button), action);

  gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toggle_tool_button, -1);

  return GTK_WIDGET (toggle_tool_button);
}

static void
add_tool_separator (GtkWidget *toolbar,
                    gboolean   expand)
{
  GtkToolItem *item;

  item = gtk_separator_tool_item_new ();
  gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
  gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (item), FALSE);
  gtk_tool_item_set_expand (item, expand);
  gtk_widget_show (GTK_WIDGET (item));
}
