/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * Vector tool
 * Copyright (C) 2003 Simon Budig  <simon@gimp.org>
 *
 * 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 <http://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <gegl.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

#include "libgimpbase/gimpbase.h"
#include "libgimpwidgets/gimpwidgets.h"

#include "tools-types.h"

#include "core/gimp.h"
#include "core/gimpcontext.h"
#include "core/gimpimage.h"
#include "core/gimpimage-undo.h"
#include "core/gimpimage-undo-push.h"
#include "core/gimplist.h"
#include "core/gimptoolinfo.h"
#include "core/gimpundostack.h"

#include "paint/gimppaintoptions.h" /* GIMP_PAINT_OPTIONS_CONTEXT_MASK */

#include "vectors/gimpanchor.h"
#include "vectors/gimpvectors.h"
#include "vectors/gimpbezierstroke.h"

#include "widgets/gimphelp-ids.h"
#include "widgets/gimpwidgets-utils.h"

#include "display/gimpcanvasitem.h"
#include "display/gimpdisplay.h"
#include "display/gimpdisplayshell.h"
#include "display/gimpdisplayshell-scale.h"

#include "gimptoolcontrol.h"
#include "gimpvectoroptions.h"
#include "gimpvectortool.h"

#include "dialogs/stroke-dialog.h"

#include "gimp-intl.h"


#define TOGGLE_MASK  GDK_SHIFT_MASK
#define MOVE_MASK    GDK_MOD1_MASK
#define INSDEL_MASK  gimp_get_toggle_behavior_mask ()


/*  local function prototypes  */

static void     gimp_vector_tool_control         (GimpTool              *tool,
                                                  GimpToolAction         action,
                                                  GimpDisplay           *display);
static void     gimp_vector_tool_button_press    (GimpTool              *tool,
                                                  const GimpCoords      *coords,
                                                  guint32                time,
                                                  GdkModifierType        state,
                                                  GimpButtonPressType    press_type,
                                                  GimpDisplay           *display);
static void     gimp_vector_tool_button_release  (GimpTool              *tool,
                                                  const GimpCoords      *coords,
                                                  guint32                time,
                                                  GdkModifierType        state,
                                                  GimpButtonReleaseType  release_type,
                                                  GimpDisplay           *display);
static void     gimp_vector_tool_motion          (GimpTool              *tool,
                                                  const GimpCoords      *coords,
                                                  guint32                time,
                                                  GdkModifierType        state,
                                                  GimpDisplay           *display);
static gboolean gimp_vector_tool_key_press       (GimpTool              *tool,
                                                  GdkEventKey           *kevent,
                                                  GimpDisplay           *display);
static void     gimp_vector_tool_modifier_key    (GimpTool              *tool,
                                                  GdkModifierType        key,
                                                  gboolean               press,
                                                  GdkModifierType        state,
                                                  GimpDisplay           *display);
static void     gimp_vector_tool_oper_update     (GimpTool              *tool,
                                                  const GimpCoords      *coords,
                                                  GdkModifierType        state,
                                                  gboolean               proximity,
                                                  GimpDisplay           *display);
static void     gimp_vector_tool_status_update   (GimpTool              *tool,
                                                  GimpDisplay           *display,
                                                  GdkModifierType        state,
                                                  gboolean               proximity);
static void     gimp_vector_tool_cursor_update   (GimpTool              *tool,
                                                  const GimpCoords      *coords,
                                                  GdkModifierType        state,
                                                  GimpDisplay           *display);

static void     gimp_vector_tool_draw            (GimpDrawTool          *draw_tool);

static void     gimp_vector_tool_vectors_changed (GimpImage             *image,
                                                  GimpVectorTool        *vector_tool);
static void     gimp_vector_tool_vectors_removed (GimpVectors           *vectors,
                                                  GimpVectorTool        *vector_tool);
static void     gimp_vector_tool_vectors_visible (GimpVectors           *vectors,
                                                  GimpVectorTool        *vector_tool);
static void     gimp_vector_tool_vectors_freeze  (GimpVectors           *vectors,
                                                  GimpVectorTool        *vector_tool);
static void     gimp_vector_tool_vectors_thaw    (GimpVectors           *vectors,
                                                  GimpVectorTool        *vector_tool);

static void     gimp_vector_tool_move_selected_anchors
                                                 (GimpVectorTool        *vector_tool,
                                                  gdouble                x,
                                                  gdouble                y);
static void     gimp_vector_tool_delete_selected_anchors
                                                 (GimpVectorTool        *vector_tool);
static void     gimp_vector_tool_verify_state    (GimpVectorTool        *vector_tool);
static void     gimp_vector_tool_undo_push       (GimpVectorTool        *vector_tool,
                                                  const gchar           *desc);

static void     gimp_vector_tool_to_selection    (GimpVectorTool        *vector_tool);
static void     gimp_vector_tool_to_selection_extended
                                                 (GimpVectorTool        *vector_tool,
                                                  gint                   state);
static void     gimp_vector_tool_stroke_vectors  (GimpVectorTool        *vector_tool,
                                                  GtkWidget             *button);


G_DEFINE_TYPE (GimpVectorTool, gimp_vector_tool, GIMP_TYPE_DRAW_TOOL)

#define parent_class gimp_vector_tool_parent_class


void
gimp_vector_tool_register (GimpToolRegisterCallback callback,
                           gpointer                 data)
{
  (* callback) (GIMP_TYPE_VECTOR_TOOL,
                GIMP_TYPE_VECTOR_OPTIONS,
                gimp_vector_options_gui,
                gimp_vector_options_gui_horizontal,
                GIMP_PAINT_OPTIONS_CONTEXT_MASK |
                GIMP_CONTEXT_PATTERN_MASK |
                GIMP_CONTEXT_GRADIENT_MASK, /* for stroking */
                "gimp-vector-tool",
                _("Paths"),
                _("Paths Tool: Create and edit paths"),
                N_("Pat_hs"), "b",
                NULL, GIMP_HELP_TOOL_PATH,
                GIMP_STOCK_TOOL_PATH,
                data);
}

static void
gimp_vector_tool_class_init (GimpVectorToolClass *klass)
{
  GimpToolClass     *tool_class      = GIMP_TOOL_CLASS (klass);
  GimpDrawToolClass *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass);

  tool_class->control        = gimp_vector_tool_control;
  tool_class->button_press   = gimp_vector_tool_button_press;
  tool_class->button_release = gimp_vector_tool_button_release;
  tool_class->motion         = gimp_vector_tool_motion;
  tool_class->key_press      = gimp_vector_tool_key_press;
  tool_class->modifier_key   = gimp_vector_tool_modifier_key;
  tool_class->oper_update    = gimp_vector_tool_oper_update;
  tool_class->cursor_update  = gimp_vector_tool_cursor_update;

  draw_tool_class->draw      = gimp_vector_tool_draw;
}

static void
gimp_vector_tool_init (GimpVectorTool *vector_tool)
{
  GimpTool *tool = GIMP_TOOL (vector_tool);

  gimp_tool_control_set_handle_empty_image (tool->control, TRUE);
  gimp_tool_control_set_motion_mode        (tool->control,
                                            GIMP_MOTION_MODE_COMPRESS);
  gimp_tool_control_set_precision          (tool->control,
                                            GIMP_CURSOR_PRECISION_SUBPIXEL);
  gimp_tool_control_set_tool_cursor        (tool->control,
                                            GIMP_TOOL_CURSOR_PATHS);

  vector_tool->function       = VECTORS_CREATE_VECTOR;
  vector_tool->restriction    = GIMP_ANCHOR_FEATURE_NONE;
  vector_tool->modifier_lock  = FALSE;
  vector_tool->last_x         = 0.0;
  vector_tool->last_y         = 0.0;
  vector_tool->undo_motion    = FALSE;
  vector_tool->have_undo      = FALSE;

  vector_tool->cur_anchor     = NULL;
  vector_tool->cur_anchor2    = NULL;
  vector_tool->cur_stroke     = NULL;
  vector_tool->cur_position   = 0.0;
  vector_tool->cur_vectors    = NULL;
  vector_tool->vectors        = NULL;

  vector_tool->sel_count      = 0;
  vector_tool->sel_anchor     = NULL;
  vector_tool->sel_stroke     = NULL;

  vector_tool->saved_mode     = GIMP_VECTOR_MODE_DESIGN;
}


static void
gimp_vector_tool_control (GimpTool       *tool,
                          GimpToolAction  action,
                          GimpDisplay    *display)
{
  GimpVectorTool *vector_tool = GIMP_VECTOR_TOOL (tool);

  switch (action)
    {
    case GIMP_TOOL_ACTION_PAUSE:
    case GIMP_TOOL_ACTION_RESUME:
      break;

    case GIMP_TOOL_ACTION_HALT:
      gimp_vector_tool_set_vectors (vector_tool, NULL);
      break;
    }

  GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
}

static gboolean
gimp_vector_tool_check_writable (GimpVectorTool *vector_tool)
{
  if (gimp_item_is_content_locked (GIMP_ITEM (vector_tool->vectors)))
    {
      gimp_tool_message_literal (GIMP_TOOL (vector_tool),
                                 GIMP_TOOL (vector_tool)->display,
                                 _("The active path is locked."));
      vector_tool->function = VECTORS_FINISHED;

      return FALSE;
    }

  return TRUE;
}

static void
gimp_vector_tool_button_press (GimpTool            *tool,
                               const GimpCoords    *coords,
                               guint32              time,
                               GdkModifierType      state,
                               GimpButtonPressType  press_type,
                               GimpDisplay         *display)
{
  GimpDrawTool      *draw_tool   = GIMP_DRAW_TOOL (tool);
  GimpVectorTool    *vector_tool = GIMP_VECTOR_TOOL (tool);
  GimpVectorOptions *options     = GIMP_VECTOR_TOOL_GET_OPTIONS (tool);
  GimpImage         *image       = gimp_display_get_image (display);
  GimpVectors       *vectors;

  /* do nothing if we are an FINISHED state */
  if (vector_tool->function == VECTORS_FINISHED)
    return;

  g_return_if_fail (vector_tool->vectors  != NULL                  ||
                    vector_tool->function == VECTORS_SELECT_VECTOR ||
                    vector_tool->function == VECTORS_CREATE_VECTOR);

  vector_tool->undo_motion = FALSE;

  /* save the current modifier state */

  vector_tool->saved_state = state;

  gimp_draw_tool_pause (draw_tool);

  if (gimp_draw_tool_is_active (draw_tool) && draw_tool->display != display)
    {
      gimp_draw_tool_stop (draw_tool);
    }

  gimp_tool_control_activate (tool->control);
  tool->display = display;

  /* select a vectors object */

  if (vector_tool->function == VECTORS_SELECT_VECTOR)
    {
      if (gimp_draw_tool_on_vectors (draw_tool, display, coords,
                                     GIMP_TOOL_HANDLE_SIZE_CIRCLE,
                                     GIMP_TOOL_HANDLE_SIZE_CIRCLE,
                                     NULL, NULL, NULL, NULL, NULL, &vectors))
        {
          gimp_vector_tool_set_vectors (vector_tool, vectors);
          gimp_image_set_active_vectors (image, vectors);
        }

      vector_tool->function = VECTORS_FINISHED;
    }

  /* create a new vector from scratch */

  if (vector_tool->function == VECTORS_CREATE_VECTOR)
    {
      vectors = gimp_vectors_new (image, _("Unnamed"));

      /* Undo step gets added implicitely */
      vector_tool->have_undo = TRUE;

      vector_tool->undo_motion = TRUE;

      gimp_image_add_vectors (image, vectors,
                              GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
      gimp_image_flush (image);

      gimp_vector_tool_set_vectors (vector_tool, vectors);

      vector_tool->function = VECTORS_CREATE_STROKE;
    }

  gimp_vectors_freeze (vector_tool->vectors);

  /* create a new stroke */

  if (vector_tool->function == VECTORS_CREATE_STROKE &&
      gimp_vector_tool_check_writable (vector_tool))
    {
      gimp_vector_tool_undo_push (vector_tool, _("Add Stroke"));

      vector_tool->cur_stroke = gimp_bezier_stroke_new ();
      gimp_vectors_stroke_add (vector_tool->vectors, vector_tool->cur_stroke);
      g_object_unref (vector_tool->cur_stroke);

      vector_tool->undo_motion = TRUE;

      vector_tool->sel_stroke = vector_tool->cur_stroke;
      vector_tool->cur_anchor = NULL;
      vector_tool->sel_anchor = NULL;
      vector_tool->function = VECTORS_ADD_ANCHOR;
    }


  /* add an anchor to an existing stroke */

  if (vector_tool->function == VECTORS_ADD_ANCHOR &&
      gimp_vector_tool_check_writable (vector_tool))
    {
      GimpCoords position = GIMP_COORDS_DEFAULT_VALUES;

      position.x = coords->x;
      position.y = coords->y;

      gimp_vector_tool_undo_push (vector_tool, _("Add Anchor"));

      vector_tool->undo_motion = TRUE;

      vector_tool->cur_anchor =
                     gimp_bezier_stroke_extend (vector_tool->sel_stroke,
                                                &position,
                                                vector_tool->sel_anchor,
                                                EXTEND_EDITABLE);

      vector_tool->restriction = GIMP_ANCHOR_FEATURE_SYMMETRIC;

      if (! options->polygonal)
        vector_tool->function = VECTORS_MOVE_HANDLE;
      else
        vector_tool->function = VECTORS_MOVE_ANCHOR;

      vector_tool->cur_stroke = vector_tool->sel_stroke;
    }


  /* insertion of an anchor in a curve segment */

  if (vector_tool->function == VECTORS_INSERT_ANCHOR &&
      gimp_vector_tool_check_writable (vector_tool))
    {
      gimp_vector_tool_undo_push (vector_tool, _("Insert Anchor"));

      vector_tool->undo_motion = TRUE;

      vector_tool->cur_anchor =
                         gimp_stroke_anchor_insert (vector_tool->cur_stroke,
                                                    vector_tool->cur_anchor,
                                                    vector_tool->cur_position);
      if (vector_tool->cur_anchor)
        {
          if (options->polygonal)
            {
              gimp_stroke_anchor_convert (vector_tool->cur_stroke,
                                          vector_tool->cur_anchor,
                                          GIMP_ANCHOR_FEATURE_EDGE);
            }

          vector_tool->function = VECTORS_MOVE_ANCHOR;
        }
      else
        {
          vector_tool->function = VECTORS_FINISHED;
        }
    }


  /* move a handle */

  if (vector_tool->function == VECTORS_MOVE_HANDLE &&
      gimp_vector_tool_check_writable (vector_tool))
    {
      gimp_vector_tool_undo_push (vector_tool, _("Drag Handle"));

      if (vector_tool->cur_anchor->type == GIMP_ANCHOR_ANCHOR)
        {
          if (! vector_tool->cur_anchor->selected)
            {
              gimp_vectors_anchor_select (vector_tool->vectors,
                                          vector_tool->cur_stroke,
                                          vector_tool->cur_anchor,
                                          TRUE, TRUE);
              vector_tool->undo_motion = TRUE;
            }

          gimp_draw_tool_on_vectors_handle (GIMP_DRAW_TOOL (tool), display,
                                            vector_tool->vectors, coords,
                                            GIMP_TOOL_HANDLE_SIZE_CIRCLE,
                                            GIMP_TOOL_HANDLE_SIZE_CIRCLE,
                                            GIMP_ANCHOR_CONTROL, TRUE,
                                            &vector_tool->cur_anchor,
                                            &vector_tool->cur_stroke);
          if (! vector_tool->cur_anchor)
            vector_tool->function = VECTORS_FINISHED;
        }
    }


  /* move an anchor */

  if (vector_tool->function == VECTORS_MOVE_ANCHOR &&
      gimp_vector_tool_check_writable (vector_tool))
    {
      gimp_vector_tool_undo_push (vector_tool, _("Drag Anchor"));

      if (! vector_tool->cur_anchor->selected)
        {
          gimp_vectors_anchor_select (vector_tool->vectors,
                                      vector_tool->cur_stroke,
                                      vector_tool->cur_anchor,
                                      TRUE, TRUE);
          vector_tool->undo_motion = TRUE;
        }
    }


  /* move multiple anchors */

  if (vector_tool->function == VECTORS_MOVE_ANCHORSET &&
      gimp_vector_tool_check_writable (vector_tool))
    {
      gimp_vector_tool_undo_push (vector_tool, _("Drag Anchors"));

      if (state & TOGGLE_MASK)
        {
          gimp_vectors_anchor_select (vector_tool->vectors,
                                      vector_tool->cur_stroke,
                                      vector_tool->cur_anchor,
                                      !vector_tool->cur_anchor->selected,
                                      FALSE);

          vector_tool->undo_motion = TRUE;

          if (vector_tool->cur_anchor->selected == FALSE)
            vector_tool->function = VECTORS_FINISHED;
        }
    }


  /* move a curve segment directly */

  if (vector_tool->function == VECTORS_MOVE_CURVE &&
      gimp_vector_tool_check_writable (vector_tool))
    {
      gimp_vector_tool_undo_push (vector_tool, _("Drag Curve"));

      /* the magic numbers are taken from the "feel good" parameter
       * from gimp_bezier_stroke_point_move_relative in gimpbezierstroke.c. */
      if (vector_tool->cur_position < 5.0 / 6.0)
        {
          gimp_vectors_anchor_select (vector_tool->vectors,
                                      vector_tool->cur_stroke,
                                      vector_tool->cur_anchor, TRUE, TRUE);
          vector_tool->undo_motion = TRUE;
        }

      if (vector_tool->cur_position > 1.0 / 6.0)
        {
          gimp_vectors_anchor_select (vector_tool->vectors,
                                      vector_tool->cur_stroke,
                                      vector_tool->cur_anchor2, TRUE,
                                      (vector_tool->cur_position >= 5.0 / 6.0));
          vector_tool->undo_motion = TRUE;
        }

    }


  /* connect two strokes */

  if (vector_tool->function == VECTORS_CONNECT_STROKES &&
      gimp_vector_tool_check_writable (vector_tool))
    {
      gimp_vector_tool_undo_push (vector_tool, _("Connect Strokes"));

      gimp_stroke_connect_stroke (vector_tool->sel_stroke,
                                  vector_tool->sel_anchor,
                                  vector_tool->cur_stroke,
                                  vector_tool->cur_anchor);
      vector_tool->undo_motion = TRUE;

      if (vector_tool->cur_stroke != vector_tool->sel_stroke &&
          gimp_stroke_is_empty (vector_tool->cur_stroke))
        {
          gimp_vectors_stroke_remove (vector_tool->vectors,
                                      vector_tool->cur_stroke);
        }

      vector_tool->sel_anchor = vector_tool->cur_anchor;
      vector_tool->cur_stroke = vector_tool->sel_stroke;

      gimp_vectors_anchor_select (vector_tool->vectors,
                                  vector_tool->sel_stroke,
                                  vector_tool->sel_anchor, TRUE, TRUE);

      vector_tool->function = VECTORS_FINISHED;
    }


  /* move a stroke or all strokes of a vectors object */

  if ((vector_tool->function == VECTORS_MOVE_STROKE ||
       vector_tool->function == VECTORS_MOVE_VECTORS) &&
      gimp_vector_tool_check_writable (vector_tool))
    {
      gimp_vector_tool_undo_push (vector_tool, _("Drag Path"));

      /* Work is being done in gimp_vector_tool_motion ()... */
    }


  /* convert an anchor to something that looks like an edge */

  if (vector_tool->function == VECTORS_CONVERT_EDGE &&
      gimp_vector_tool_check_writable (vector_tool))
    {
      gimp_vector_tool_undo_push (vector_tool, _("Convert Edge"));

      gimp_stroke_anchor_convert (vector_tool->cur_stroke,
                                  vector_tool->cur_anchor,
                                  GIMP_ANCHOR_FEATURE_EDGE);
      vector_tool->undo_motion = TRUE;

      if (vector_tool->cur_anchor->type == GIMP_ANCHOR_ANCHOR)
        {
          gimp_vectors_anchor_select (vector_tool->vectors,
                                      vector_tool->cur_stroke,
                                      vector_tool->cur_anchor, TRUE, TRUE);

          vector_tool->function = VECTORS_MOVE_ANCHOR;
        }
      else
        {
          vector_tool->cur_stroke = NULL;
          vector_tool->cur_anchor = NULL;

          /* avoid doing anything stupid */
          vector_tool->function = VECTORS_FINISHED;
        }
    }


  /* removal of a node in a stroke */

  if (vector_tool->function == VECTORS_DELETE_ANCHOR &&
      gimp_vector_tool_check_writable (vector_tool))
    {
      gimp_vector_tool_undo_push (vector_tool, _("Delete Anchor"));

      gimp_stroke_anchor_delete (vector_tool->cur_stroke,
                                 vector_tool->cur_anchor);
      vector_tool->undo_motion = TRUE;

      if (gimp_stroke_is_empty (vector_tool->cur_stroke))
        gimp_vectors_stroke_remove (vector_tool->vectors,
                                    vector_tool->cur_stroke);

      vector_tool->cur_stroke = NULL;
      vector_tool->cur_anchor = NULL;
      vector_tool->function = VECTORS_FINISHED;
    }


  /* deleting a segment (opening up a stroke) */

  if (vector_tool->function == VECTORS_DELETE_SEGMENT &&
      gimp_vector_tool_check_writable (vector_tool))
    {
      GimpStroke *new_stroke;

      gimp_vector_tool_undo_push (vector_tool, _("Delete Segment"));

      new_stroke = gimp_stroke_open (vector_tool->cur_stroke,
                                     vector_tool->cur_anchor);
      if (new_stroke)
        {
          gimp_vectors_stroke_add (vector_tool->vectors, new_stroke);
          g_object_unref (new_stroke);
        }

      vector_tool->undo_motion = TRUE;
      vector_tool->cur_stroke = NULL;
      vector_tool->cur_anchor = NULL;
      vector_tool->function = VECTORS_FINISHED;
    }

  vector_tool->last_x = coords->x;
  vector_tool->last_y = coords->y;

  gimp_vectors_thaw (vector_tool->vectors);

  if (! gimp_draw_tool_is_active (draw_tool))
    {
      gimp_draw_tool_start (draw_tool, display);
    }

  gimp_draw_tool_resume (draw_tool);
}

static void
gimp_vector_tool_button_release (GimpTool              *tool,
                                 const GimpCoords      *coords,
                                 guint32                time,
                                 GdkModifierType        state,
                                 GimpButtonReleaseType  release_type,
                                 GimpDisplay           *display)
{
  GimpVectorTool *vector_tool = GIMP_VECTOR_TOOL (tool);
  GimpImage      *image       = gimp_display_get_image (display);

  vector_tool->function = VECTORS_FINISHED;

  if (vector_tool->have_undo &&
      (! vector_tool->undo_motion ||
       (release_type == GIMP_BUTTON_RELEASE_CANCEL)))
    {
      GimpUndo            *undo;
      GimpUndoAccumulator  accum = { 0, };

      undo = gimp_undo_stack_pop_undo (gimp_image_get_undo_stack (image),
                                       GIMP_UNDO_MODE_UNDO, &accum);

      gimp_image_undo_event (image, GIMP_UNDO_EVENT_UNDO_EXPIRED, undo);

      gimp_undo_free (undo, GIMP_UNDO_MODE_UNDO);
      g_object_unref (undo);
    }

  vector_tool->have_undo = FALSE;
  vector_tool->undo_motion = FALSE;

  gimp_tool_control_halt (tool->control);
  gimp_image_flush (image);
}

static void
gimp_vector_tool_motion (GimpTool         *tool,
                         const GimpCoords *coords,
                         guint32           time,
                         GdkModifierType   state,
                         GimpDisplay      *display)
{
  GimpVectorTool    *vector_tool = GIMP_VECTOR_TOOL (tool);
  GimpVectorOptions *options     = GIMP_VECTOR_TOOL_GET_OPTIONS (tool);
  GimpCoords         position    = GIMP_COORDS_DEFAULT_VALUES;
  GimpAnchor        *anchor;

  if (vector_tool->function == VECTORS_FINISHED)
    return;

  position.x = coords->x;
  position.y = coords->y;

  gimp_vectors_freeze (vector_tool->vectors);

  if ((vector_tool->saved_state & TOGGLE_MASK) != (state & TOGGLE_MASK))
    vector_tool->modifier_lock = FALSE;

  if (!vector_tool->modifier_lock)
    {
      if (state & TOGGLE_MASK)
        {
          vector_tool->restriction = GIMP_ANCHOR_FEATURE_SYMMETRIC;
        }
      else
        {
          vector_tool->restriction = GIMP_ANCHOR_FEATURE_NONE;
        }
    }

  switch (vector_tool->function)
    {
    case VECTORS_MOVE_ANCHOR:
    case VECTORS_MOVE_HANDLE:
      anchor = vector_tool->cur_anchor;

      if (anchor)
        {
          gimp_stroke_anchor_move_absolute (vector_tool->cur_stroke,
                                            vector_tool->cur_anchor,
                                            &position,
                                            vector_tool->restriction);
          vector_tool->undo_motion = TRUE;
        }
      break;

    case VECTORS_MOVE_CURVE:
      if (options->polygonal)
        {
          gimp_vector_tool_move_selected_anchors (vector_tool,
                                                  coords->x - vector_tool->last_x,
                                                  coords->y - vector_tool->last_y);
          vector_tool->undo_motion = TRUE;
        }
      else
        {
          gimp_stroke_point_move_absolute (vector_tool->cur_stroke,
                                           vector_tool->cur_anchor,
                                           vector_tool->cur_position,
                                           &position,
                                           vector_tool->restriction);
          vector_tool->undo_motion = TRUE;
        }
      break;

    case VECTORS_MOVE_ANCHORSET:
      gimp_vector_tool_move_selected_anchors (vector_tool,
                                              coords->x - vector_tool->last_x,
                                              coords->y - vector_tool->last_y);
      vector_tool->undo_motion = TRUE;
      break;

    case VECTORS_MOVE_STROKE:
      if (vector_tool->cur_stroke)
        {
          gimp_stroke_translate (vector_tool->cur_stroke,
                                 coords->x - vector_tool->last_x,
                                 coords->y - vector_tool->last_y);
          vector_tool->undo_motion = TRUE;
        }
      else if (vector_tool->sel_stroke)
        {
          gimp_stroke_translate (vector_tool->sel_stroke,
                                 coords->x - vector_tool->last_x,
                                 coords->y - vector_tool->last_y);
          vector_tool->undo_motion = TRUE;
        }
      break;

    case VECTORS_MOVE_VECTORS:
      gimp_item_translate (GIMP_ITEM (vector_tool->vectors),
                           coords->x - vector_tool->last_x,
                           coords->y - vector_tool->last_y, FALSE);
      vector_tool->undo_motion = TRUE;
      break;

    default:
      break;
    }

  vector_tool->last_x = coords->x;
  vector_tool->last_y = coords->y;

  gimp_vectors_thaw (vector_tool->vectors);
}

static gboolean
gimp_vector_tool_key_press (GimpTool     *tool,
                            GdkEventKey  *kevent,
                            GimpDisplay  *display)
{
  GimpVectorTool    *vector_tool = GIMP_VECTOR_TOOL (tool);
  GimpDrawTool      *draw_tool   = GIMP_DRAW_TOOL (tool);
  GimpVectorOptions *options     = GIMP_VECTOR_TOOL_GET_OPTIONS (tool);
  GimpDisplayShell  *shell;
  gdouble            xdist, ydist;
  gdouble            pixels = 1.0;

  if (! vector_tool->vectors)
    return FALSE;

  if (display != draw_tool->display)
    return FALSE;

  shell = gimp_display_get_shell (draw_tool->display);

  if (kevent->state & GDK_SHIFT_MASK)
    pixels = 10.0;

  if (kevent->state & gimp_get_toggle_behavior_mask ())
    pixels = 50.0;

  switch (kevent->keyval)
    {
    case GDK_KEY_Return:
    case GDK_KEY_KP_Enter:
    case GDK_KEY_ISO_Enter:
      gimp_vector_tool_to_selection_extended (vector_tool, kevent->state);
      break;

    case GDK_KEY_BackSpace:
    case GDK_KEY_Delete:
      gimp_vector_tool_delete_selected_anchors (vector_tool);
      break;

    case GDK_KEY_Left:
    case GDK_KEY_Right:
    case GDK_KEY_Up:
    case GDK_KEY_Down:
      xdist = FUNSCALEX (shell, pixels);
      ydist = FUNSCALEY (shell, pixels);

      gimp_vector_tool_undo_push (vector_tool, _("Move Anchors"));

      gimp_vectors_freeze (vector_tool->vectors);

      switch (kevent->keyval)
        {
        case GDK_KEY_Left:
          gimp_vector_tool_move_selected_anchors (vector_tool, -xdist, 0);
          break;

        case GDK_KEY_Right:
          gimp_vector_tool_move_selected_anchors (vector_tool, xdist, 0);
          break;

        case GDK_KEY_Up:
          gimp_vector_tool_move_selected_anchors (vector_tool, 0, -ydist);
          break;

        case GDK_KEY_Down:
          gimp_vector_tool_move_selected_anchors (vector_tool, 0, ydist);
          break;

        default:
          break;
        }

      gimp_vectors_thaw (vector_tool->vectors);
      vector_tool->have_undo = FALSE;
      break;

    case GDK_KEY_Escape:
      if (options->edit_mode != GIMP_VECTOR_MODE_DESIGN)
        g_object_set (options, "vectors-edit-mode",
                      GIMP_VECTOR_MODE_DESIGN, NULL);
      break;

    default:
      return FALSE;
    }

  gimp_image_flush (gimp_display_get_image (display));

  return TRUE;
}

static void
gimp_vector_tool_modifier_key (GimpTool        *tool,
                               GdkModifierType  key,
                               gboolean         press,
                               GdkModifierType  state,
                               GimpDisplay     *display)
{
  GimpVectorTool    *vector_tool = GIMP_VECTOR_TOOL (tool);
  GimpVectorOptions *options     = GIMP_VECTOR_TOOL_GET_OPTIONS (tool);

  if (key == TOGGLE_MASK)
    return;

  if (key == INSDEL_MASK || key == MOVE_MASK)
    {
      GimpVectorMode button_mode = options->edit_mode;

      if (press)
        {
          if (key == (state & (INSDEL_MASK | MOVE_MASK)))
            {
              /*  first modifier pressed  */

              vector_tool->saved_mode = options->edit_mode;
            }
        }
      else
        {
          if (! (state & (INSDEL_MASK | MOVE_MASK)))
            {
              /*  last modifier released  */

              button_mode = vector_tool->saved_mode;
            }
        }

      if (state & MOVE_MASK)
        {
          button_mode = GIMP_VECTOR_MODE_MOVE;
        }
      else if (state & INSDEL_MASK)
        {
          button_mode = GIMP_VECTOR_MODE_EDIT;
        }

      if (button_mode != options->edit_mode)
        {
          g_object_set (options, "vectors-edit-mode", button_mode, NULL);
        }
    }
}

static void
gimp_vector_tool_oper_update (GimpTool         *tool,
                              const GimpCoords *coords,
                              GdkModifierType   state,
                              gboolean          proximity,
                              GimpDisplay      *display)
{
  GimpVectorTool    *vector_tool = GIMP_VECTOR_TOOL (tool);
  GimpDrawTool      *draw_tool   = GIMP_DRAW_TOOL (tool);
  GimpVectorOptions *options     = GIMP_VECTOR_TOOL_GET_OPTIONS (tool);
  GimpAnchor        *anchor      = NULL;
  GimpAnchor        *anchor2     = NULL;
  GimpStroke        *stroke      = NULL;
  gdouble            position    = -1;
  gboolean           on_handle   = FALSE;
  gboolean           on_curve    = FALSE;
  gboolean           on_vectors  = FALSE;

  vector_tool->modifier_lock = FALSE;

  /* are we hovering the current vectors on the current display? */
  if (vector_tool->vectors && GIMP_DRAW_TOOL (tool)->display == display)
    {
      on_handle = gimp_draw_tool_on_vectors_handle (GIMP_DRAW_TOOL (tool),
                                                    display,
                                                    vector_tool->vectors,
                                                    coords,
                                                    GIMP_TOOL_HANDLE_SIZE_CIRCLE,
                                                    GIMP_TOOL_HANDLE_SIZE_CIRCLE,
                                                    GIMP_ANCHOR_ANCHOR,
                                                    vector_tool->sel_count > 2,
                                                    &anchor, &stroke);

      if (! on_handle)
        on_curve = gimp_draw_tool_on_vectors_curve (GIMP_DRAW_TOOL (tool),
                                                    display,
                                                    vector_tool->vectors,
                                                    coords,
                                                    GIMP_TOOL_HANDLE_SIZE_CIRCLE,
                                                    GIMP_TOOL_HANDLE_SIZE_CIRCLE,
                                                    NULL,
                                                    &position, &anchor,
                                                    &anchor2, &stroke);
    }

  if (on_handle || on_curve)
    {
      vector_tool->cur_vectors = NULL;
    }
  else
    {
      on_vectors = gimp_draw_tool_on_vectors (draw_tool, display, coords,
                                              GIMP_TOOL_HANDLE_SIZE_CIRCLE,
                                              GIMP_TOOL_HANDLE_SIZE_CIRCLE,
                                              NULL, NULL, NULL, NULL, NULL,
                                              &(vector_tool->cur_vectors));
    }

  vector_tool->cur_position   = position;
  vector_tool->cur_anchor     = anchor;
  vector_tool->cur_anchor2    = anchor2;
  vector_tool->cur_stroke     = stroke;

  switch (options->edit_mode)
    {
    case GIMP_VECTOR_MODE_DESIGN:
      if (! vector_tool->vectors || GIMP_DRAW_TOOL (tool)->display != display)
        {
          if (on_vectors)
            {
              vector_tool->function = VECTORS_SELECT_VECTOR;
            }
          else
            {
              vector_tool->function = VECTORS_CREATE_VECTOR;
              vector_tool->restriction = GIMP_ANCHOR_FEATURE_SYMMETRIC;
              vector_tool->modifier_lock = TRUE;
            }
        }
      else if (on_handle)
        {
          if (anchor->type == GIMP_ANCHOR_ANCHOR)
            {
              if (state & TOGGLE_MASK)
                {
                  vector_tool->function = VECTORS_MOVE_ANCHORSET;
                }
              else
                {
                  if (vector_tool->sel_count >= 2 && anchor->selected)
                    vector_tool->function = VECTORS_MOVE_ANCHORSET;
                  else
                    vector_tool->function = VECTORS_MOVE_ANCHOR;
                }
            }
          else
            {
              vector_tool->function = VECTORS_MOVE_HANDLE;

              if (state & TOGGLE_MASK)
                vector_tool->restriction = GIMP_ANCHOR_FEATURE_SYMMETRIC;
              else
                vector_tool->restriction = GIMP_ANCHOR_FEATURE_NONE;
            }
        }
      else if (on_curve)
        {
          if (gimp_stroke_point_is_movable (stroke, anchor, position))
            {
              vector_tool->function = VECTORS_MOVE_CURVE;

              if (state & TOGGLE_MASK)
                vector_tool->restriction = GIMP_ANCHOR_FEATURE_SYMMETRIC;
              else
                vector_tool->restriction = GIMP_ANCHOR_FEATURE_NONE;
            }
          else
            {
              vector_tool->function = VECTORS_FINISHED;
            }
        }
      else
        {
          if (vector_tool->sel_stroke && vector_tool->sel_anchor &&
              gimp_stroke_is_extendable (vector_tool->sel_stroke,
                                         vector_tool->sel_anchor) &&
              !(state & TOGGLE_MASK))
            vector_tool->function = VECTORS_ADD_ANCHOR;
          else
            vector_tool->function = VECTORS_CREATE_STROKE;

          vector_tool->restriction = GIMP_ANCHOR_FEATURE_SYMMETRIC;
          vector_tool->modifier_lock = TRUE;
        }

      break;

    case GIMP_VECTOR_MODE_EDIT:
      if (! vector_tool->vectors || GIMP_DRAW_TOOL (tool)->display != display)
        {
          if (on_vectors)
            {
              vector_tool->function = VECTORS_SELECT_VECTOR;
            }
          else
            {
              vector_tool->function = VECTORS_FINISHED;
            }
        }
      else if (on_handle)
        {
          if (anchor->type == GIMP_ANCHOR_ANCHOR)
            {
              if (!(state & TOGGLE_MASK) && vector_tool->sel_anchor &&
                  vector_tool->sel_anchor != anchor &&
                  gimp_stroke_is_extendable (vector_tool->sel_stroke,
                                             vector_tool->sel_anchor) &&
                  gimp_stroke_is_extendable (stroke, anchor))
                {
                  vector_tool->function = VECTORS_CONNECT_STROKES;
                }
              else
                {
                  if (state & TOGGLE_MASK)
                    {
                      vector_tool->function = VECTORS_DELETE_ANCHOR;
                    }
                  else
                    {
                      if (options->polygonal)
                        vector_tool->function = VECTORS_MOVE_ANCHOR;
                      else
                        vector_tool->function = VECTORS_MOVE_HANDLE;
                    }
                }
            }
          else
            {
              if (state & TOGGLE_MASK)
                vector_tool->function = VECTORS_CONVERT_EDGE;
              else
                vector_tool->function = VECTORS_MOVE_HANDLE;
            }
        }
      else if (on_curve)
        {
          if (state & TOGGLE_MASK)
            {
              vector_tool->function = VECTORS_DELETE_SEGMENT;
            }
          else if (gimp_stroke_anchor_is_insertable (stroke, anchor, position))
            {
              vector_tool->function = VECTORS_INSERT_ANCHOR;
            }
          else
            {
              vector_tool->function = VECTORS_FINISHED;
            }
        }
      else
        {
          vector_tool->function = VECTORS_FINISHED;
        }

      break;

    case GIMP_VECTOR_MODE_MOVE:
      if (! vector_tool->vectors || GIMP_DRAW_TOOL (tool)->display != display)
        {
          if (on_vectors)
            {
              vector_tool->function = VECTORS_SELECT_VECTOR;
            }
          else
            {
              vector_tool->function = VECTORS_FINISHED;
            }
        }
      else if (on_handle || on_curve)
        {
          if (state & TOGGLE_MASK)
            {
              vector_tool->function = VECTORS_MOVE_VECTORS;
            }
          else
            {
              vector_tool->function = VECTORS_MOVE_STROKE;
            }
        }
      else
        {
          if (on_vectors)
            {
              vector_tool->function = VECTORS_SELECT_VECTOR;
            }
          else
            {
              vector_tool->function = VECTORS_MOVE_VECTORS;
            }
        }
      break;
    }

  gimp_vector_tool_status_update (tool, display, state, proximity);
}


static void
gimp_vector_tool_status_update (GimpTool        *tool,
                                GimpDisplay     *display,
                                GdkModifierType  state,
                                gboolean         proximity)
{
  GimpVectorTool    *vector_tool = GIMP_VECTOR_TOOL (tool);
  GimpVectorOptions *options     = GIMP_VECTOR_TOOL_GET_OPTIONS (tool);

  gimp_tool_pop_status (tool, display);

  if (proximity)
    {
      const gchar *status      = NULL;
      gboolean     free_status = FALSE;

      switch (vector_tool->function)
        {
        case VECTORS_SELECT_VECTOR:
          status = _("Click to pick path to edit");
          break;

        case VECTORS_CREATE_VECTOR:
          status = _("Click to create a new path");
          break;

        case VECTORS_CREATE_STROKE:
          status = _("Click to create a new component of the path");
          break;

        case VECTORS_ADD_ANCHOR:
          status = gimp_suggest_modifiers (_("Click or Click-Drag to create "
                                             "a new anchor"),
                                           GDK_SHIFT_MASK & ~state,
                                           NULL, NULL, NULL);
          free_status = TRUE;
          break;

        case VECTORS_MOVE_ANCHOR:
          if (options->edit_mode != GIMP_VECTOR_MODE_EDIT)
            {
              GdkModifierType toggle_mask = gimp_get_toggle_behavior_mask ();

              status = gimp_suggest_modifiers (_("Click-Drag to move the "
                                                 "anchor around"),
                                               toggle_mask & ~state,
                                               NULL, NULL, NULL);
              free_status = TRUE;
            }
          else
            status = _("Click-Drag to move the anchor around");
          break;

        case VECTORS_MOVE_ANCHORSET:
          status = _("Click-Drag to move the anchors around");
          break;

        case VECTORS_MOVE_HANDLE:
          if (vector_tool->restriction != GIMP_ANCHOR_FEATURE_SYMMETRIC)
            {
              status = gimp_suggest_modifiers (_("Click-Drag to move the "
                                                 "handle around"),
                                               GDK_SHIFT_MASK & ~state,
                                               NULL, NULL, NULL);
            }
          else
            {
              status = gimp_suggest_modifiers (_("Click-Drag to move the "
                                                 "handles around symmetrically"),
                                               GDK_SHIFT_MASK & ~state,
                                               NULL, NULL, NULL);
            }
          free_status = TRUE;
          break;

        case VECTORS_MOVE_CURVE:
          if (GIMP_VECTOR_TOOL_GET_OPTIONS (tool)->polygonal)
            status = gimp_suggest_modifiers (_("Click-Drag to move the "
                                               "anchors around"),
                                             GDK_SHIFT_MASK & ~state,
                                             NULL, NULL, NULL);
          else
            status = gimp_suggest_modifiers (_("Click-Drag to change the "
                                               "shape of the curve"),
                                             GDK_SHIFT_MASK & ~state,
                                             _("%s: symmetrical"), NULL, NULL);
          free_status = TRUE;
          break;

        case VECTORS_MOVE_STROKE:
          status = gimp_suggest_modifiers (_("Click-Drag to move the "
                                             "component around"),
                                           GDK_SHIFT_MASK & ~state,
                                           NULL, NULL, NULL);
          free_status = TRUE;
          break;

        case VECTORS_MOVE_VECTORS:
          status = _("Click-Drag to move the path around");
          break;

        case VECTORS_INSERT_ANCHOR:
          status = gimp_suggest_modifiers (_("Click-Drag to insert an anchor "
                                             "on the path"),
                                           GDK_SHIFT_MASK & ~state,
                                           NULL, NULL, NULL);
          free_status = TRUE;
          break;

        case VECTORS_DELETE_ANCHOR:
          status = _("Click to delete this anchor");
          break;

        case VECTORS_CONNECT_STROKES:
          status = _("Click to connect this anchor "
                     "with the selected endpoint");
          break;

        case VECTORS_DELETE_SEGMENT:
          status = _("Click to open up the path");
          break;

        case VECTORS_CONVERT_EDGE:
          status = _("Click to make this node angular");
          break;

        case VECTORS_FINISHED:
          status = NULL;
          break;
      }

      if (status)
        gimp_tool_push_status (tool, display, "%s", status);

      if (free_status)
        g_free ((gchar *) status);
    }
}

static void
gimp_vector_tool_cursor_update (GimpTool         *tool,
                                const GimpCoords *coords,
                                GdkModifierType   state,
                                GimpDisplay      *display)
{
  GimpVectorTool     *vector_tool = GIMP_VECTOR_TOOL (tool);
  GimpToolCursorType  tool_cursor = GIMP_TOOL_CURSOR_PATHS;
  GimpCursorModifier  modifier    = GIMP_CURSOR_MODIFIER_NONE;

  switch (vector_tool->function)
    {
    case VECTORS_SELECT_VECTOR:
      tool_cursor = GIMP_TOOL_CURSOR_HAND;
      break;

    case VECTORS_CREATE_VECTOR:
    case VECTORS_CREATE_STROKE:
      modifier = GIMP_CURSOR_MODIFIER_CONTROL;
      break;

    case VECTORS_ADD_ANCHOR:
    case VECTORS_INSERT_ANCHOR:
      tool_cursor = GIMP_TOOL_CURSOR_PATHS_ANCHOR;
      modifier    = GIMP_CURSOR_MODIFIER_PLUS;
      break;

    case VECTORS_DELETE_ANCHOR:
      tool_cursor = GIMP_TOOL_CURSOR_PATHS_ANCHOR;
      modifier    = GIMP_CURSOR_MODIFIER_MINUS;
      break;

    case VECTORS_DELETE_SEGMENT:
      tool_cursor = GIMP_TOOL_CURSOR_PATHS_SEGMENT;
      modifier    = GIMP_CURSOR_MODIFIER_MINUS;
      break;

    case VECTORS_MOVE_HANDLE:
      tool_cursor = GIMP_TOOL_CURSOR_PATHS_CONTROL;
      modifier    = GIMP_CURSOR_MODIFIER_MOVE;
      break;

    case VECTORS_CONVERT_EDGE:
      tool_cursor = GIMP_TOOL_CURSOR_PATHS_CONTROL;
      modifier    = GIMP_CURSOR_MODIFIER_MINUS;
      break;

    case VECTORS_MOVE_ANCHOR:
      tool_cursor = GIMP_TOOL_CURSOR_PATHS_ANCHOR;
      modifier    = GIMP_CURSOR_MODIFIER_MOVE;
      break;

    case VECTORS_MOVE_CURVE:
      tool_cursor = GIMP_TOOL_CURSOR_PATHS_SEGMENT;
      modifier    = GIMP_CURSOR_MODIFIER_MOVE;
      break;

    case VECTORS_MOVE_STROKE:
    case VECTORS_MOVE_VECTORS:
      modifier = GIMP_CURSOR_MODIFIER_MOVE;
      break;

    case VECTORS_MOVE_ANCHORSET:
      tool_cursor = GIMP_TOOL_CURSOR_PATHS_ANCHOR;
      modifier    = GIMP_CURSOR_MODIFIER_MOVE;
      break;

    case VECTORS_CONNECT_STROKES:
      tool_cursor = GIMP_TOOL_CURSOR_PATHS_SEGMENT;
      modifier    = GIMP_CURSOR_MODIFIER_JOIN;
      break;

    default:
      modifier = GIMP_CURSOR_MODIFIER_BAD;
      break;
    }

  gimp_tool_control_set_tool_cursor     (tool->control, tool_cursor);
  gimp_tool_control_set_cursor_modifier (tool->control, modifier);

  GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
}

static void
gimp_vector_tool_draw (GimpDrawTool *draw_tool)
{
  GimpVectorTool *vector_tool = GIMP_VECTOR_TOOL (draw_tool);
  GimpStroke     *cur_stroke;
  GimpVectors    *vectors;

  vectors = vector_tool->vectors;

  if (! vectors || ! gimp_vectors_get_bezier (vectors))
    return;

  /* the stroke itself */
  if (! gimp_item_get_visible (GIMP_ITEM (vectors)))
    gimp_draw_tool_add_path (draw_tool, gimp_vectors_get_bezier (vectors), 0, 0);

  for (cur_stroke = gimp_vectors_stroke_get_next (vectors, NULL);
       cur_stroke;
       cur_stroke = gimp_vectors_stroke_get_next (vectors, cur_stroke))
    {
      GArray *coords;
      GList  *draw_anchors;
      GList  *list;

      /* anchor handles */
      draw_anchors = gimp_stroke_get_draw_anchors (cur_stroke);

      for (list = draw_anchors; list; list = g_list_next (list))
        {
          GimpAnchor *cur_anchor = GIMP_ANCHOR (list->data);

          if (cur_anchor->type == GIMP_ANCHOR_ANCHOR)
            {
              gimp_draw_tool_add_handle (draw_tool,
                                         cur_anchor->selected ?
                                         GIMP_HANDLE_CIRCLE :
                                         GIMP_HANDLE_FILLED_CIRCLE,
                                         cur_anchor->position.x,
                                         cur_anchor->position.y,
                                         GIMP_TOOL_HANDLE_SIZE_CIRCLE,
                                         GIMP_TOOL_HANDLE_SIZE_CIRCLE,
                                         GIMP_HANDLE_ANCHOR_CENTER);
            }
        }

      g_list_free (draw_anchors);

      if (vector_tool->sel_count <= 2)
        {
          /* the lines to the control handles */
          coords = gimp_stroke_get_draw_lines (cur_stroke);

          if (coords)
            {
              if (coords->len % 2 == 0)
                {
                  gint i;

                  for (i = 0; i < coords->len; i += 2)
                    {
                      GimpCanvasItem *item;

                      item = gimp_draw_tool_add_line
                        (draw_tool,
                         g_array_index (coords, GimpCoords, i).x,
                         g_array_index (coords, GimpCoords, i).y,
                         g_array_index (coords, GimpCoords, i + 1).x,
                         g_array_index (coords, GimpCoords, i + 1).y);

                      gimp_canvas_item_set_highlight (item, TRUE);
                    }
                }

              g_array_free (coords, TRUE);
            }

          /* control handles */
          draw_anchors = gimp_stroke_get_draw_controls (cur_stroke);

          for (list = draw_anchors; list; list = g_list_next (list))
            {
              GimpAnchor *cur_anchor = GIMP_ANCHOR (list->data);

              gimp_draw_tool_add_handle (draw_tool,
                                         GIMP_HANDLE_SQUARE,
                                         cur_anchor->position.x,
                                         cur_anchor->position.y,
                                         GIMP_TOOL_HANDLE_SIZE_CIRCLE - 3,
                                         GIMP_TOOL_HANDLE_SIZE_CIRCLE - 3,
                                         GIMP_HANDLE_ANCHOR_CENTER);
            }

          g_list_free (draw_anchors);
        }
    }
}

static void
gimp_vector_tool_vectors_changed (GimpImage      *image,
                                  GimpVectorTool *vector_tool)
{
  gimp_vector_tool_set_vectors (vector_tool,
                                gimp_image_get_active_vectors (image));
}

static void
gimp_vector_tool_vectors_removed (GimpVectors    *vectors,
                                  GimpVectorTool *vector_tool)
{
  gimp_vector_tool_set_vectors (vector_tool, NULL);
}

static void
gimp_vector_tool_vectors_visible (GimpVectors    *vectors,
                                  GimpVectorTool *vector_tool)
{
  GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (vector_tool);

  if (gimp_draw_tool_is_active (draw_tool) && draw_tool->paused_count == 0)
    {
      GimpStroke *stroke = NULL;

      while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke)))
        {
          GArray   *coords;
          gboolean  closed;

          coords = gimp_stroke_interpolate (stroke, 1.0, &closed);

          if (coords)
            {
              if (coords->len)
                gimp_draw_tool_add_strokes (draw_tool,
                                            &g_array_index (coords,
                                                            GimpCoords, 0),
                                            coords->len, FALSE);

              g_array_free (coords, TRUE);
            }
        }
    }
}

static void
gimp_vector_tool_vectors_freeze (GimpVectors    *vectors,
                                 GimpVectorTool *vector_tool)
{
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (vector_tool));
}

static void
gimp_vector_tool_vectors_thaw (GimpVectors    *vectors,
                               GimpVectorTool *vector_tool)
{
  /*  Ok, the vector might have changed externally (e.g. Undo) we need
   *  to validate our internal state.
   */
  gimp_vector_tool_verify_state (vector_tool);

  gimp_draw_tool_resume (GIMP_DRAW_TOOL (vector_tool));
}

void
gimp_vector_tool_set_vectors (GimpVectorTool *vector_tool,
                              GimpVectors    *vectors)
{
  GimpDrawTool      *draw_tool;
  GimpTool          *tool;
  GimpItem          *item = NULL;
  GimpVectorOptions *options;

  g_return_if_fail (GIMP_IS_VECTOR_TOOL (vector_tool));
  g_return_if_fail (vectors == NULL || GIMP_IS_VECTORS (vectors));

  draw_tool = GIMP_DRAW_TOOL (vector_tool);
  tool      = GIMP_TOOL (vector_tool);
  options   = GIMP_VECTOR_TOOL_GET_OPTIONS (vector_tool);

  if (vectors)
    item = GIMP_ITEM (vectors);

  if (vectors == vector_tool->vectors)
    return;

  gimp_draw_tool_pause (draw_tool);

  if (gimp_draw_tool_is_active (draw_tool) &&
      (! vectors ||
       gimp_display_get_image (draw_tool->display) != gimp_item_get_image (item)))
    {
      gimp_draw_tool_stop (draw_tool);
    }

  if (vector_tool->vectors)
    {
      GimpImage *old_image;

      old_image = gimp_item_get_image (GIMP_ITEM (vector_tool->vectors));

      g_signal_handlers_disconnect_by_func (old_image,
                                            gimp_vector_tool_vectors_changed,
                                            vector_tool);
      g_signal_handlers_disconnect_by_func (vector_tool->vectors,
                                            gimp_vector_tool_vectors_removed,
                                            vector_tool);
      g_signal_handlers_disconnect_by_func (vector_tool->vectors,
                                            gimp_vector_tool_vectors_visible,
                                            vector_tool);
      g_signal_handlers_disconnect_by_func (vector_tool->vectors,
                                            gimp_vector_tool_vectors_freeze,
                                            vector_tool);
      g_signal_handlers_disconnect_by_func (vector_tool->vectors,
                                            gimp_vector_tool_vectors_thaw,
                                            vector_tool);
      g_object_unref (vector_tool->vectors);

      if (options->to_selection_button)
        {
          gtk_widget_set_sensitive (options->to_selection_button, FALSE);
          g_signal_handlers_disconnect_by_func (options->to_selection_button,
                                                gimp_vector_tool_to_selection,
                                                tool);
          g_signal_handlers_disconnect_by_func (options->to_selection_button,
                                                gimp_vector_tool_to_selection_extended,
                                                tool);
        }

      if (options->stroke_button)
        {
          gtk_widget_set_sensitive (options->stroke_button, FALSE);
          g_signal_handlers_disconnect_by_func (options->stroke_button,
                                                gimp_vector_tool_stroke_vectors,
                                                tool);
        }
    }

  vector_tool->vectors  = vectors;
  vector_tool->function = VECTORS_FINISHED;
  gimp_vector_tool_verify_state (vector_tool);

  if (! vector_tool->vectors)
    {
      tool->display = NULL;

      /* leave draw_tool->paused_count in a consistent state */
      gimp_draw_tool_resume (draw_tool);

      vector_tool->function = VECTORS_CREATE_VECTOR;

      return;
    }

  g_object_ref (vectors);

  g_signal_connect_object (gimp_item_get_image (item), "active-vectors-changed",
                           G_CALLBACK (gimp_vector_tool_vectors_changed),
                           vector_tool, 0);
  g_signal_connect_object (vectors, "removed",
                           G_CALLBACK (gimp_vector_tool_vectors_removed),
                           vector_tool, 0);
  g_signal_connect_object (vectors, "visibility-changed",
                           G_CALLBACK (gimp_vector_tool_vectors_visible),
                           vector_tool, 0);
  g_signal_connect_object (vectors, "freeze",
                           G_CALLBACK (gimp_vector_tool_vectors_freeze),
                           vector_tool, 0);
  g_signal_connect_object (vectors, "thaw",
                           G_CALLBACK (gimp_vector_tool_vectors_thaw),
                           vector_tool, 0);

  if (options->to_selection_button)
    {
      g_signal_connect_swapped (options->to_selection_button, "clicked",
                                G_CALLBACK (gimp_vector_tool_to_selection),
                                tool);
      g_signal_connect_swapped (options->to_selection_button, "extended-clicked",
                                G_CALLBACK (gimp_vector_tool_to_selection_extended),
                                tool);
      gtk_widget_set_sensitive (options->to_selection_button, TRUE);
    }

  if (options->stroke_button)
    {
      g_signal_connect_swapped (options->stroke_button, "clicked",
                                G_CALLBACK (gimp_vector_tool_stroke_vectors),
                                tool);
      gtk_widget_set_sensitive (options->stroke_button, TRUE);
    }

  if (! gimp_draw_tool_is_active (draw_tool))
    {
      if (tool->display &&
          gimp_display_get_image (tool->display) == gimp_item_get_image (item))
        {
          gimp_draw_tool_start (draw_tool, tool->display);
        }
      else
        {
          GimpContext *context;
          GimpDisplay *display;

          context = gimp_get_user_context (tool->tool_info->gimp);
          display = gimp_context_get_display (context);

          if (! display ||
              gimp_display_get_image (display) != gimp_item_get_image (item))
            {
              GList *list;

              display = NULL;

              for (list = gimp_get_display_iter (gimp_item_get_image (item)->gimp);
                   list;
                   list = g_list_next (list))
                {
                  display = list->data;

                  if (gimp_display_get_image (display) == gimp_item_get_image (item))
                    {
                      gimp_context_set_display (context, display);
                      break;
                    }

                  display = NULL;
                }
            }

          tool->display = display;

          if (tool->display)
            gimp_draw_tool_start (draw_tool, tool->display);
        }
    }

  gimp_draw_tool_resume (draw_tool);

  if (options->edit_mode != GIMP_VECTOR_MODE_DESIGN)
    g_object_set (options, "vectors-edit-mode",
                  GIMP_VECTOR_MODE_DESIGN, NULL);
}

static void
gimp_vector_tool_move_selected_anchors (GimpVectorTool *vector_tool,
                                        gdouble         x,
                                        gdouble         y)
{
  GimpAnchor *cur_anchor;
  GimpStroke *cur_stroke = NULL;
  GList      *anchors;
  GList      *list;
  GimpCoords  offset = { 0.0, };

  offset.x = x;
  offset.y = y;

  while ((cur_stroke = gimp_vectors_stroke_get_next (vector_tool->vectors,
                                                     cur_stroke)))
    {
      /* anchors */
      anchors = gimp_stroke_get_draw_anchors (cur_stroke);

      for (list = anchors; list; list = g_list_next (list))
        {
          cur_anchor = GIMP_ANCHOR (list->data);

          if (cur_anchor->selected)
            gimp_stroke_anchor_move_relative (cur_stroke,
                                              cur_anchor,
                                              &offset,
                                              GIMP_ANCHOR_FEATURE_NONE);
        }

      g_list_free (anchors);
    }
}

static void
gimp_vector_tool_delete_selected_anchors (GimpVectorTool *vector_tool)
{
  GimpAnchor *cur_anchor;
  GimpStroke *cur_stroke = NULL;
  GList      *anchors;
  GList      *list;
  gboolean    have_undo = FALSE;

  gimp_draw_tool_pause (GIMP_DRAW_TOOL (vector_tool));
  gimp_vectors_freeze (vector_tool->vectors);

  while ((cur_stroke = gimp_vectors_stroke_get_next (vector_tool->vectors,
                                                     cur_stroke)))
    {
      /* anchors */
      anchors = gimp_stroke_get_draw_anchors (cur_stroke);

      for (list = anchors; list; list = g_list_next (list))
        {
          cur_anchor = GIMP_ANCHOR (list->data);

          if (cur_anchor->selected)
            {
              if (! have_undo)
                {
                  gimp_vector_tool_undo_push (vector_tool, _("Delete Anchors"));
                  have_undo = TRUE;
                }

              gimp_stroke_anchor_delete (cur_stroke, cur_anchor);

              if (gimp_stroke_is_empty (cur_stroke))
                {
                  gimp_vectors_stroke_remove (vector_tool->vectors, cur_stroke);
                  cur_stroke = NULL;
                }
            }
        }

      g_list_free (anchors);
    }

  gimp_vectors_thaw (vector_tool->vectors);
  gimp_draw_tool_resume (GIMP_DRAW_TOOL (vector_tool));
}

static void
gimp_vector_tool_verify_state (GimpVectorTool *vector_tool)
{
  GimpStroke *cur_stroke       = NULL;
  gboolean    cur_anchor_valid = FALSE;
  gboolean    cur_stroke_valid = FALSE;

  vector_tool->sel_count  = 0;
  vector_tool->sel_anchor = NULL;
  vector_tool->sel_stroke = NULL;

  if (! vector_tool->vectors)
    {
      vector_tool->cur_position = -1;
      vector_tool->cur_anchor   = NULL;
      vector_tool->cur_stroke   = NULL;
      return;
    }

  while ((cur_stroke = gimp_vectors_stroke_get_next (vector_tool->vectors,
                                                     cur_stroke)))
    {
      GList *anchors;
      GList *list;

      /* anchor handles */
      anchors = gimp_stroke_get_draw_anchors (cur_stroke);

      if (cur_stroke == vector_tool->cur_stroke)
        cur_stroke_valid = TRUE;

      for (list = anchors; list; list = g_list_next (list))
        {
          GimpAnchor *cur_anchor = list->data;

          if (cur_anchor == vector_tool->cur_anchor)
            cur_anchor_valid = TRUE;

          if (cur_anchor->type == GIMP_ANCHOR_ANCHOR &&
              cur_anchor->selected)
            {
              vector_tool->sel_count++;
              if (vector_tool->sel_count == 1)
                {
                  vector_tool->sel_anchor = cur_anchor;
                  vector_tool->sel_stroke = cur_stroke;
                }
              else
                {
                  vector_tool->sel_anchor = NULL;
                  vector_tool->sel_stroke = NULL;
                }
            }
        }

      g_list_free (anchors);

      anchors = gimp_stroke_get_draw_controls (cur_stroke);

      for (list = anchors; list; list = g_list_next (list))
        {
          GimpAnchor *cur_anchor = list->data;

          if (cur_anchor == vector_tool->cur_anchor)
            cur_anchor_valid = TRUE;
        }

      g_list_free (anchors);
    }

  if (! cur_stroke_valid)
    vector_tool->cur_stroke = NULL;

  if (! cur_anchor_valid)
    vector_tool->cur_anchor = NULL;

}

static void
gimp_vector_tool_undo_push (GimpVectorTool *vector_tool,
                            const gchar    *desc)
{
  g_return_if_fail (vector_tool->vectors != NULL);

  /* don't push two undos */
  if (vector_tool->have_undo)
    return;

  gimp_image_undo_push_vectors_mod (gimp_item_get_image (GIMP_ITEM (vector_tool->vectors)),
                                    desc, vector_tool->vectors);
  vector_tool->have_undo = TRUE;
}


static void
gimp_vector_tool_to_selection (GimpVectorTool *vector_tool)
{
  gimp_vector_tool_to_selection_extended (vector_tool, 0);
}


static void
gimp_vector_tool_to_selection_extended (GimpVectorTool *vector_tool,
                                        gint            state)
{
  GimpImage *image;

  if (! vector_tool->vectors)
    return;

  image = gimp_item_get_image (GIMP_ITEM (vector_tool->vectors));

  gimp_item_to_selection (GIMP_ITEM (vector_tool->vectors),
                          gimp_modifiers_to_channel_op (state),
                          TRUE, FALSE, 0, 0);
  gimp_image_flush (image);
}


static void
gimp_vector_tool_stroke_vectors (GimpVectorTool *vector_tool,
                                 GtkWidget      *button)
{
  GimpImage    *image;
  GimpDrawable *active_drawable;
  GtkWidget    *dialog;

  if (! vector_tool->vectors)
    return;

  image = gimp_item_get_image (GIMP_ITEM (vector_tool->vectors));

  active_drawable = gimp_image_get_active_drawable (image);

  if (! active_drawable)
    {
      gimp_tool_message (GIMP_TOOL (vector_tool),
                         GIMP_TOOL (vector_tool)->display,
                         _("There is no active layer or channel to stroke to"));
      return;
    }

  dialog = stroke_dialog_new (GIMP_ITEM (vector_tool->vectors),
                              GIMP_CONTEXT (GIMP_TOOL_GET_OPTIONS (vector_tool)),
                              _("Stroke Path"),
                              GIMP_STOCK_PATH_STROKE,
                              GIMP_HELP_PATH_STROKE,
                              button);
  gtk_widget_show (dialog);
}
