/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "config.h"

#include <stdlib.h>
#include <string.h>

#include <gtk/gtk.h>

#include "libgimpwidgets/gimpwidgets.h"

#include "tools-types.h"

#include "base/pixel-region.h"
#include "base/temp-buf.h"

#include "paint-funcs/paint-funcs.h"

#include "core/gimp.h"
#include "core/gimpdrawable.h"
#include "core/gimpimage.h"
#include "core/gimpimage-mask.h"
#include "core/gimppattern.h"
#include "core/gimpcontext.h"
#include "core/gimpbrush.h"

#include "display/gimpdisplay.h"

#include "gimpclonetool.h"
#include "paint_options.h"
#include "tool_manager.h"
#include "tool_options.h"

#include "libgimp/gimpintl.h"


#define TARGET_HEIGHT  15
#define TARGET_WIDTH   15

/* default types */
#define CLONE_DEFAULT_TYPE     IMAGE_CLONE
#define CLONE_DEFAULT_ALIGNED  ALIGN_NO

/*  the clone structures  */

typedef enum
{
  ALIGN_NO,
  ALIGN_YES,
  ALIGN_REGISTERED
} AlignType;

typedef struct _CloneOptions CloneOptions;

struct _CloneOptions
{
  PaintOptions  paint_options;

  CloneType     type;
  CloneType     type_d;
  GtkWidget    *type_w[2];  /* 2 radio buttons */

  AlignType     aligned;
  AlignType     aligned_d;
  GtkWidget    *aligned_w[3];  /* 3 radio buttons */
};


/*  forward function declarations  */

static void       gimp_clone_tool_class_init   (GimpCloneToolClass *klass);
static void       gimp_clone_tool_init         (GimpCloneTool      *tool);

static void       gimp_clone_tool_paint        (GimpPaintTool   *paint_tool,
						GimpDrawable    *drawable,
						PaintState       state);
static void       gimp_clone_tool_draw         (GimpDrawTool    *draw_tool);
static void       gimp_clone_tool_cursor_update (GimpTool        *tool,
						 GdkEventMotion  *mevent,
						 GDisplay        *gdisp);
static void       gimp_clone_tool_motion       (GimpPaintTool   *paint_tool,
						GimpDrawable    *drawable,
						GimpDrawable    *src_drawable,
						PaintPressureOptions *pressure_options,
						CloneType        type,
						gint             offset_x,
						gint             offset_y);
static void       gimp_clone_tool_line_image   (GimpImage       *dest,
						GimpImage       *src,
						GimpDrawable    *d_drawable,
						GimpDrawable    *s_drawable,
						guchar          *s,
						guchar          *d,
						gint             has_alpha,
						gint             src_bytes,
						gint             dest_bytes,
						gint             width);
static void       gimp_clone_tool_line_pattern (GimpImage       *dest,
						GimpDrawable    *drawable,
						GimpPattern     *pattern,
						guchar          *d,
						gint             x,
						gint             y,
						gint             bytes,
						gint             width);

static CloneOptions * clone_options_new        (void);
static void           clone_options_reset      (GimpToolOptions *options);


/* The parent class */
static GimpPaintToolClass *parent_class;
 
/*  the clone tool options  */
static CloneOptions *clone_options = NULL;

/*  local variables  */
static gint          src_x         = 0;        /*                         */
static gint          src_y         = 0;        /*  position of clone src  */
static gint          dest_x        = 0;        /*                         */
static gint          dest_y        = 0;        /*  position of clone src  */
static gint          offset_x      = 0;        /*                         */
static gint          offset_y      = 0;        /*  offset for cloning     */
static gint          first         = TRUE;
static gint          trans_tx      = 0;        /*                         */
static gint          trans_ty      = 0;        /*  transformed target     */
static GDisplay     *the_src_gdisp = NULL;     /*  ID of source gdisplay  */
static GimpDrawable *src_drawable_ = NULL;     /*  source drawable        */

static GimpDrawable *non_gui_src_drawable;
static gint          non_gui_offset_x;
static gint          non_gui_offset_y;
static CloneType     non_gui_type;


/* global functions  */

void
gimp_clone_tool_register (Gimp *gimp)
{
  tool_manager_register_tool (gimp,
			      GIMP_TYPE_CLONE_TOOL,
			      TRUE,
  			      "gimp:clone_tool",
  			      _("Clone"),
  			      _("Paint using Patterns or Image Regions"),
      			      N_("/Tools/Paint Tools/Clone"), "C",
  			      NULL, "tools/clone.html",
			      GIMP_STOCK_TOOL_CLONE);
}

GType
gimp_clone_tool_get_type (void)
{
  static GType tool_type = 0;

  if (! tool_type)
    {
      static const GTypeInfo tool_info =
      {
        sizeof (GimpCloneToolClass),
	(GBaseInitFunc) NULL,
	(GBaseFinalizeFunc) NULL,
	(GClassInitFunc) gimp_clone_tool_class_init,
	NULL,           /* class_finalize */
	NULL,           /* class_data     */
	sizeof (GimpCloneTool),
	0,              /* n_preallocs    */
	(GInstanceInitFunc) gimp_clone_tool_init,
      };

      tool_type = g_type_register_static (GIMP_TYPE_PAINT_TOOL,
					  "GimpCloneTool",
                                          &tool_info, 0);
    }

  return tool_type;
}

/* static functions  */

static void
gimp_clone_tool_class_init (GimpCloneToolClass *klass)
{
  GimpToolClass      *tool_class;
  GimpDrawToolClass  *draw_tool_class;
  GimpPaintToolClass *paint_tool_class;

  tool_class       = GIMP_TOOL_CLASS (klass);
  draw_tool_class  = GIMP_DRAW_TOOL_CLASS (klass);
  paint_tool_class = GIMP_PAINT_TOOL_CLASS (klass);

  parent_class = g_type_class_peek_parent (klass);

  tool_class->cursor_update = gimp_clone_tool_cursor_update;

  draw_tool_class->draw     = gimp_clone_tool_draw;

  paint_tool_class->paint   = gimp_clone_tool_paint;
}

static void
gimp_clone_tool_init (GimpCloneTool *clone)
{
  GimpTool      *tool;
  GimpPaintTool *paint_tool;

  tool       = GIMP_TOOL (clone);
  paint_tool = GIMP_PAINT_TOOL (clone);

  if (! clone_options)
    {
      clone_options = clone_options_new ();

      tool_manager_register_tool_options (GIMP_TYPE_CLONE_TOOL,
                                          (GimpToolOptions *) clone_options);
    }

  tool->tool_cursor = GIMP_CLONE_TOOL_CURSOR;

  paint_tool->pick_colors =  TRUE;
  paint_tool->flags       |= TOOL_CAN_HANDLE_CHANGING_BRUSH;
  paint_tool->flags       |= TOOL_TRACES_ON_WINDOW;
}

static void
clone_src_drawable_disconnect_cb (GimpDrawable  *drawable,
				  GimpDrawable **src_drawable)
{
  if (drawable == *src_drawable)
    {
      *src_drawable = NULL;
      the_src_gdisp = NULL;
    }
}

static void
clone_set_src_drawable (GimpDrawable *drawable)
{
  if (src_drawable_ == drawable)
    return;

  if (src_drawable_)
    g_signal_handlers_disconnect_by_func (G_OBJECT (src_drawable_),
                                          G_CALLBACK (clone_src_drawable_disconnect_cb), 
                                          &src_drawable_);

  src_drawable_ = drawable;
  if (drawable)
    {
      g_signal_connect (G_OBJECT (drawable), "disconnect",
                        G_CALLBACK (clone_src_drawable_disconnect_cb),
                        &src_drawable_);
    }
}

static void
gimp_clone_tool_paint (GimpPaintTool *paint_tool,
		       GimpDrawable  *drawable,
		       PaintState     state)
{
  GDisplay     *gdisp;
  GDisplay     *src_gdisp;
  gint          x1, y1, x2, y2;
  static gint   orig_src_x, orig_src_y;
  GimpDrawTool *draw_tool;
  GimpContext  *context;

  gdisp = (GDisplay *) tool_manager_get_active (drawable->gimage->gimp)->gdisp;

  draw_tool = GIMP_DRAW_TOOL (paint_tool);

  context = gimp_get_current_context (gdisp->gimage->gimp);

  switch (state)
    {
    case PRETRACE_PAINT:
      gimp_draw_tool_pause (draw_tool);
      break;

    case MOTION_PAINT:
      x1 = paint_tool->curx;
      y1 = paint_tool->cury;
      x2 = paint_tool->lastx;
      y2 = paint_tool->lasty;

      /*  If the control key is down, move the src target and return */
      if (paint_tool->state & GDK_CONTROL_MASK)
	{
	  src_x = x1;
	  src_y = y1;
	  first = TRUE;
	}
      /*  otherwise, update the target  */
      else
	{
	  dest_x = x1;
	  dest_y = y1;

          if (clone_options->aligned == ALIGN_REGISTERED)
            {
	      offset_x = 0;
	      offset_y = 0;
            }
          else if (first)
	    {
	      offset_x = src_x - dest_x;
	      offset_y = src_y - dest_y;
	      first = FALSE;
	    }

	  src_x = dest_x + offset_x;
	  src_y = dest_y + offset_y;

	  gimp_clone_tool_motion (paint_tool, drawable, src_drawable_, 
				  clone_options->paint_options.pressure_options, 
				  clone_options->type, offset_x, offset_y);
	}

      break;

    case INIT_PAINT:
      if (paint_tool->state & GDK_CONTROL_MASK)
	{
	  the_src_gdisp = gdisp;
	  clone_set_src_drawable(drawable);
	  src_x = paint_tool->curx;
	  src_y = paint_tool->cury;
	  first = TRUE;
	}
      else if (clone_options->aligned == ALIGN_NO)
	{
	  first = TRUE;
	  orig_src_x = src_x;
	  orig_src_y = src_y;
	}
      if (clone_options->type == PATTERN_CLONE)
	if (! gimp_context_get_pattern (context))
	  g_message (_("No patterns available for this operation."));
      break;

    case FINISH_PAINT:
      gimp_draw_tool_stop (draw_tool);
      if (clone_options->aligned == ALIGN_NO && !first)
	{
	  src_x = orig_src_x;
	  src_y = orig_src_y;
	}
      return;
      break;

    default:
      break;
    }

  /*  Calculate the coordinates of the target  */
  src_gdisp = the_src_gdisp;
  if (!src_gdisp)
    {
      the_src_gdisp = gdisp;
      src_gdisp = the_src_gdisp;
    }

  if (state == INIT_PAINT)
    /*  Initialize the tool drawing core  */
    gimp_draw_tool_start (draw_tool,
			  src_gdisp->canvas->window);
  else if (state == POSTTRACE_PAINT)
    {
      /*  Find the target cursor's location onscreen  */
      gdisplay_transform_coords (src_gdisp, src_x, src_y,
				 &trans_tx, &trans_ty, 1);
      gimp_draw_tool_resume (draw_tool);      
    }
}

void
gimp_clone_tool_cursor_update (GimpTool       *tool,
			       GdkEventMotion *mevent,
			       GDisplay       *gdisp)
{
  GimpLayer     *layer;
  GdkCursorType  ctype = GDK_TOP_LEFT_ARROW;
  gint           x, y;

  gdisplay_untransform_coords (gdisp, (double) mevent->x, (double) mevent->y,
			       &x, &y, TRUE, FALSE);
 
  if ((layer = gimp_image_get_active_layer (gdisp->gimage))) 
    {
      int off_x, off_y;

      gimp_drawable_offsets (GIMP_DRAWABLE (layer), &off_x, &off_y);

      if (x >= off_x && y >= off_y &&
	  x < (off_x + gimp_drawable_width (GIMP_DRAWABLE (layer))) &&
	  y < (off_y + gimp_drawable_height (GIMP_DRAWABLE (layer))))
	{
	  /*  One more test--is there a selected region?
	   *  if so, is cursor inside?
	   */
	  if (gimage_mask_is_empty (gdisp->gimage))
	    ctype = GIMP_MOUSE_CURSOR;
	  else if (gimage_mask_value (gdisp->gimage, x, y))
	    ctype = GIMP_MOUSE_CURSOR;
	}
    }
  
  if (clone_options->type == IMAGE_CLONE)
    {
      if (mevent->state & GDK_CONTROL_MASK)
	ctype = GIMP_CROSSHAIR_SMALL_CURSOR;
      else if (!src_drawable_)
	ctype = GIMP_BAD_CURSOR;
    }

  gdisplay_install_tool_cursor (gdisp,
				ctype,
				ctype == GIMP_CROSSHAIR_SMALL_CURSOR ?
				GIMP_TOOL_CURSOR_NONE :
				GIMP_CLONE_TOOL_CURSOR,
				GIMP_CURSOR_MODIFIER_NONE);
}

static void
gimp_clone_tool_draw (GimpDrawTool *draw_tool)
{
  if (draw_tool->gc != NULL && clone_options->type == IMAGE_CLONE)
    {
      gdk_draw_line (draw_tool->win, draw_tool->gc,
		     trans_tx - (TARGET_WIDTH >> 1), trans_ty,
		     trans_tx + (TARGET_WIDTH >> 1), trans_ty);
      gdk_draw_line (draw_tool->win, draw_tool->gc,
		     trans_tx, trans_ty - (TARGET_HEIGHT >> 1),
		     trans_tx, trans_ty + (TARGET_HEIGHT >> 1));
    }
}

static void
gimp_clone_tool_motion (GimpPaintTool        *paint_tool,
			GimpDrawable         *drawable,
			GimpDrawable         *src_drawable,
			PaintPressureOptions *pressure_options,
			CloneType             type,
			int                   offset_x,
			int                   offset_y)
{
  GimpImage   *gimage;
  GimpImage   *src_gimage = NULL;
  GimpContext *context;
  guchar      *s;
  guchar      *d;
  TempBuf     *orig;
  TempBuf     *area;
  gpointer     pr;
  gint         y;
  gint         x1, y1, x2, y2;
  gint         has_alpha = -1;
  PixelRegion  srcPR, destPR;
  GimpPattern *pattern;
  gint         opacity;
  gdouble      scale;

  pr      = NULL;
  pattern = NULL;

  /*  Make sure we still have a source if we are doing image cloning */
  if (type == IMAGE_CLONE)
    {
      if (!src_drawable)
	return;
      if (! (src_gimage = gimp_drawable_gimage (src_drawable)))
	return;
      /*  Determine whether the source image has an alpha channel  */
      has_alpha = gimp_drawable_has_alpha (src_drawable);
    }

  /*  We always need a destination image */
  if (! (gimage = gimp_drawable_gimage (drawable)))
    return;

  context = gimp_get_current_context (gimage->gimp);

  if (pressure_options->size)
    scale = paint_tool->curpressure;
  else
    scale = 1.0;

  /*  Get a region which can be used to paint to  */
  if (! (area = gimp_paint_tool_get_paint_area (paint_tool, drawable, scale)))
    return;

  switch (type)
    {
    case IMAGE_CLONE:
      /*  Set the paint area to transparent  */
      temp_buf_data_clear (area);

      /*  If the source gimage is different from the destination,
       *  then we should copy straight from the destination image
       *  to the canvas.
       *  Otherwise, we need a call to get_orig_image to make sure
       *  we get a copy of the unblemished (offset) image
       */
      if (src_drawable != drawable)
	{
	  x1 = CLAMP (area->x + offset_x, 0, gimp_drawable_width (src_drawable));
	  y1 = CLAMP (area->y + offset_y, 0, gimp_drawable_height (src_drawable));
	  x2 = CLAMP (area->x + offset_x + area->width,
		      0, gimp_drawable_width (src_drawable));
	  y2 = CLAMP (area->y + offset_y + area->height,
		      0, gimp_drawable_height (src_drawable));

	  if (!(x2 - x1) || !(y2 - y1))
	    return;

	  pixel_region_init (&srcPR, gimp_drawable_data (src_drawable),
			     x1, y1, (x2 - x1), (y2 - y1), FALSE);
	}
      else
	{
	  x1 = CLAMP (area->x + offset_x, 0, gimp_drawable_width (drawable));
	  y1 = CLAMP (area->y + offset_y, 0, gimp_drawable_height (drawable));
	  x2 = CLAMP (area->x + offset_x + area->width,
		      0, gimp_drawable_width (drawable));
	  y2 = CLAMP (area->y + offset_y + area->height,
		      0, gimp_drawable_height (drawable));

	  if (!(x2 - x1) || !(y2 - y1))
	    return;

	  /*  get the original image  */
	  orig = gimp_paint_tool_get_orig_image (paint_tool, drawable, x1, y1, x2, y2);

	  srcPR.bytes = orig->bytes;
	  srcPR.x = 0; srcPR.y = 0;
	  srcPR.w = x2 - x1;
	  srcPR.h = y2 - y1;
	  srcPR.rowstride = srcPR.bytes * orig->width;
	  srcPR.data = temp_buf_data (orig);
	}

      offset_x = x1 - (area->x + offset_x);
      offset_y = y1 - (area->y + offset_y);

      /*  configure the destination  */
      destPR.bytes = area->bytes;
      destPR.x = 0; destPR.y = 0;
      destPR.w = srcPR.w;
      destPR.h = srcPR.h;
      destPR.rowstride = destPR.bytes * area->width;
      destPR.data = temp_buf_data (area) + offset_y * destPR.rowstride +
	offset_x * destPR.bytes;

      pr = pixel_regions_register (2, &srcPR, &destPR);
      break;

    case PATTERN_CLONE:
      pattern = gimp_context_get_pattern (context);

      if (!pattern)
	return;

      destPR.bytes = area->bytes;
      destPR.x = 0; destPR.y = 0;
      destPR.w = area->width;
      destPR.h = area->height;
      destPR.rowstride = destPR.bytes * area->width;
      destPR.data = temp_buf_data (area);

      pr = pixel_regions_register (1, &destPR);
      break;
    }

  for (; pr != NULL; pr = pixel_regions_process (pr))
    {
      s = srcPR.data;
      d = destPR.data;
      for (y = 0; y < destPR.h; y++)
	{
	  switch (type)
	    {
	    case IMAGE_CLONE:
	      gimp_clone_tool_line_image (gimage, src_gimage, drawable, 
					  src_drawable, s, d, has_alpha, 
					  srcPR.bytes, destPR.bytes, destPR.w);
	      s += srcPR.rowstride;
	      break;
	    case PATTERN_CLONE:
	      gimp_clone_tool_line_pattern (gimage, drawable, pattern, d,
					    area->x + offset_x, 
					    area->y + y + offset_y,
					    destPR.bytes, destPR.w);
	      break;
	    }

	  d += destPR.rowstride;
	}
    }

  opacity = 255.0 * gimp_context_get_opacity (context);
  if (pressure_options->opacity)
    opacity = opacity * 2.0 * paint_tool->curpressure;

  /*  paste the newly painted canvas to the gimage which is being worked on  */
  gimp_paint_tool_paste_canvas (paint_tool, drawable, 
				MIN (opacity, 255),
				(gint) (gimp_context_get_opacity (context) * 255),
				gimp_context_get_paint_mode (context),
				pressure_options->pressure ? PRESSURE : SOFT, 
				scale, CONSTANT);
}


static void
gimp_clone_tool_line_image (GimpImage    *dest,
			    GimpImage    *src,
			    GimpDrawable *d_drawable,
			    GimpDrawable *s_drawable,
			    guchar       *s,
			    guchar       *d,
			    gint          has_alpha,
			    gint          src_bytes,
			    gint          dest_bytes,
			    gint          width)
{
  guchar rgb[3];
  gint   src_alpha, dest_alpha;

  src_alpha  = src_bytes - 1;
  dest_alpha = dest_bytes - 1;

  while (width--)
    {
      gimp_image_get_color (src, gimp_drawable_type (s_drawable), rgb, s);
      gimp_image_transform_color (dest, d_drawable, rgb, d, RGB);

      if (has_alpha)
	d[dest_alpha] = s[src_alpha];
      else
	d[dest_alpha] = OPAQUE_OPACITY;

      s += src_bytes;
      d += dest_bytes;
    }
}

static void
gimp_clone_tool_line_pattern (GimpImage    *dest,
			      GimpDrawable *drawable,
			      GimpPattern  *pattern,
			      guchar       *d,
			      gint          x,
			      gint          y,
			      gint          bytes,
			      gint          width)
{
  guchar *pat, *p;
  gint    color, alpha;
  gint    i;

  /*  Make sure x, y are positive  */
  while (x < 0)
    x += pattern->mask->width;
  while (y < 0)
    y += pattern->mask->height;

  /*  Get a pointer to the appropriate scanline of the pattern buffer  */
  pat = temp_buf_data (pattern->mask) +
    (y % pattern->mask->height) * pattern->mask->width * pattern->mask->bytes;
  color = (pattern->mask->bytes == 3) ? RGB : GRAY;

  alpha = bytes - 1;

  for (i = 0; i < width; i++)
    {
      p = pat + ((i + x) % pattern->mask->width) * pattern->mask->bytes;

      gimp_image_transform_color (dest, drawable, p, d, color);

      d[alpha] = OPAQUE_OPACITY;

      d += bytes;
    }
}

#if 0 /* leave these to the stub functions. */

static gpointer
gimp_clone_tool_non_gui_paint_func (GimpPaintTool    *paint_tool,
				    GimpDrawable     *drawable,
				    PaintState        state)
{
  gimp_clone_tool_motion (paint_tool, drawable, non_gui_src_drawable,
			  &non_gui_pressure_options,
			  non_gui_type, non_gui_offset_x, non_gui_offset_y);

  return NULL;
}

gboolean
gimp_clone_tool_non_gui_default (GimpDrawable *drawable,
				 gint          num_strokes,
				 gdouble      *stroke_array)
{
  GimpDrawable *src_drawable = NULL;
  CloneType     clone_type = CLONE_DEFAULT_TYPE;
  gdouble       local_src_x = 0.0;
  gdouble       local_src_y = 0.0;
  CloneOptions *options = clone_options;

  if (options)
    {
      clone_type = options->type;
      src_drawable = src_drawable_;
      local_src_x = src_x;
      local_src_y = src_y;
    }
  
  return gimp_clone_tool_non_gui (drawable,
				  src_drawable,
				  clone_type,
				  local_src_x,local_src_y,
				  num_strokes, stroke_array);
}

gboolean
gimp_clone_tool_non_gui (GimpDrawable *drawable,
			 GimpDrawable *src_drawable,
			 CloneType     clone_type,
			 gdouble       src_x,
			 gdouble       src_y,
			 gint          num_strokes,
			 gdouble      *stroke_array)
{
  gint i;

  if (gimp_paint_tool_start (&non_gui_paint_tool, drawable,
			     stroke_array[0], stroke_array[1]))
    {
      /* Set the paint core's paint func */
      non_gui_paint_core.paint_func = clone_non_gui_paint_func;
      
      non_gui_type = clone_type;

      non_gui_src_drawable = src_drawable;

      non_gui_paint_core.startx = non_gui_paint_core.lastx = stroke_array[0];
      non_gui_paint_core.starty = non_gui_paint_core.lasty = stroke_array[1];

      non_gui_offset_x = (int) (src_x - non_gui_paint_core.startx);
      non_gui_offset_y = (int) (src_y - non_gui_paint_core.starty);

      clone_non_gui_paint_func (&non_gui_paint_core, drawable, 0);

      for (i = 1; i < num_strokes; i++)
	{
	  non_gui_paint_core.curx = stroke_array[i * 2 + 0];
	  non_gui_paint_core.cury = stroke_array[i * 2 + 1];

	  paint_core_interpolate (&non_gui_paint_core, drawable);

	  non_gui_paint_core.lastx = non_gui_paint_core.curx;
	  non_gui_paint_core.lasty = non_gui_paint_core.cury;
	}

      /* Finish the painting */
      paint_core_finish (&non_gui_paint_core, drawable, -1);

      /* Cleanup */
      paint_core_cleanup ();
      return TRUE;
    }
  else
    return FALSE;
}

#endif /* 0 - non-gui functions */

static CloneOptions *
clone_options_new (void)
{
  CloneOptions *options;
  GtkWidget    *vbox;
  GtkWidget    *frame;

  /*  the new clone tool options structure  */
  options = g_new (CloneOptions, 1);
  paint_options_init ((PaintOptions *) options,
		      GIMP_TYPE_CLONE_TOOL,
		      clone_options_reset);
  options->type    = options->type_d    = CLONE_DEFAULT_TYPE;
  options->aligned = options->aligned_d = CLONE_DEFAULT_ALIGNED;

  /*  the main vbox  */
  vbox = ((GimpToolOptions *) options)->main_vbox;

  frame = gimp_radio_group_new2 (TRUE, _("Source"),
				 G_CALLBACK (gimp_radio_button_update),
				 &options->type, (gpointer) options->type,

				 _("Image Source"), (gpointer) IMAGE_CLONE,
				 &options->type_w[0],
				 _("Pattern Source"), (gpointer) PATTERN_CLONE,
				 &options->type_w[1],

				 NULL);

  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  gtk_widget_show (frame);

  frame = gimp_radio_group_new2 (TRUE, _("Alignment"),
				 G_CALLBACK (gimp_radio_button_update),
				 &options->aligned, (gpointer) options->aligned,

				 _("Non Aligned"), (gpointer) ALIGN_NO,
				 &options->aligned_w[0],
				 _("Aligned"), (gpointer) ALIGN_YES,
				 &options->aligned_w[1],
				 _("Registered"), (gpointer) ALIGN_REGISTERED,
				 &options->aligned_w[2],

				 NULL);

  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  gtk_widget_show (frame);
  
  return options;
}

static void
clone_options_reset (GimpToolOptions *tool_options)
{
  CloneOptions *options;

  options = (CloneOptions *) tool_options;

  paint_options_reset (tool_options);

  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->type_w[options->type_d]), TRUE);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->aligned_w[options->aligned_d]), TRUE);
}
