/* INSTALL: gimptool-2.0 --install isle.c
 */

/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * Selection remove isles filter
 * Copyright (C) 2006 Wolfgang Hofer
 * hof@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 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */



//#include "config.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

#include <gtk/gtk.h>

#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

//#include "libgimp/stdplugins-intl.h"


#define PLUG_IN_PROC        "plug_in_selection_subtract_isles"
#define PLUG_IN_BINARY      "isles"
#define PLUG_IN_VERSION     "2007.05.31, 1.0"

/* The gettext translation domain. */
#define GETTEXT_PACKAGE "gimp20-selection-isles"
#ifndef LOCALEDIR
#define LOCALEDIR "/usr/local/share/locale"
#endif

#include <libintl.h>

#define _(String) gettext (String)

#ifdef gettext_noop
#    define N_(String) gettext_noop (String)
#else
#    define N_(String) (String)
#endif

#ifndef HAVE_BIND_TEXTDOMAIN_CODESET
#    define bind_textdomain_codeset(Domain, Codeset) (Domain)
#endif

#define INIT_I18N()	G_STMT_START{                  \
  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);         \
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");  \
  textdomain (GETTEXT_PACKAGE);                        \
}G_STMT_END

#ifndef N_
/* for older gimp releases use dummys nls-macros */
#define N_(x)  x
#define _(x)   x
#endif


/***** Macro definitions  *****/

#define SEL_NONE           0
#define SEL_FULL         255
#define SEL_UNCHECKED    250
#define SEL_VISITED        1


/***** Magic numbers *****/

#define SCALE_WIDTH  200
#define SPIN_BUTTON_WIDTH 60
#define ISLE_RESPONSE_RESET 1

/***** Types *****/

typedef struct
{
  gdouble  pixel_diagonal;
  gint     pixel_limit;
  gint     threshold;
  gboolean connect_by_corner;
  gboolean affect_selected;
  gboolean affect_unselected;
  gboolean clear_below_threshold;
  gboolean keep_workchannels;
} isle_vals_t;


typedef struct
{
  gboolean          run_flag;
  isle_vals_t      *isleval_ptr;

  GtkWidget *dialog;
  GtkWidget *check_affect_selected;
  GtkWidget *check_affect_unselected;
  GtkWidget *check_clear_below_threshold;
  GtkWidget *check_connect_by_corner;
  GtkWidget *check_keep_workchannels;
  GtkObject *adj_pixel_diagonal;
  GtkObject *adj_pixel_limit;
  GtkObject *adj_threshold;
  
} dialog_widgets_t;


typedef struct AreaContext_t
{
  gint32 max_ix;
  gint32 max_iy;
  gint32 min_ix;
  gint32 min_iy;
  gint32 seed_ix;
  gint32 seed_iy;
  gint32 pixel_count;
  guchar marker;

  isle_vals_t      *isleval_ptr;
  GimpDrawable     *isle_drawable;
  GimpPixelFetcher *pft;
  
  /* the boundaries of the original  selection */
  gint              sel_x1;
  gint              sel_y1;
  gint              sel_x2;
  gint              sel_y2;
  gint              sel_width;
  gint              sel_height;
  
} AreaContext_t;


/***** Prototypes *****/

static void query (void);
static void run   (const gchar      *name,
                   gint              nparams,
                   const GimpParam  *param,
                   gint             *nreturn_vals,
                   GimpParam       **return_vals);

static void      p_init_default_values (isle_vals_t *isleval_ptr);
static void      p_init_islePr(AreaContext_t *acontext_ptr, GimpPixelRgn *islePR_ptr);

static void      p_exec_selection_processing  (gint32 image_id
                         , isle_vals_t *isleval_ptr
                         );
static void      p_subtract_selected_isles  (gint32 image_id
                         , AreaContext_t *acontext_ptr
                         );

static void      p_clear_selction_areas_by_size(AreaContext_t *acontext_ptr);
static void      p_reset_area_context(AreaContext_t *acontext_ptr
                         , gint32 ix, gint32 iy);
static gboolean  p_mark_area(AreaContext_t *acontext_ptr);

static void      p_check_and_mark_selected_pixel(AreaContext_t *acontext_ptr
                         , gint32 ix, gint32 iy);
static void      p_mark_pixel(AreaContext_t *acontext_ptr
                         , gint32 ix, gint32 iy);

static void      p_set_all_selected_pixels_unchecked(AreaContext_t *acontext_ptr);
static void      p_clear_all_not_full_selected_pixels(AreaContext_t *acontext_ptr);
static void      p_clear_selection_below_threshold(AreaContext_t *acontext_ptr
                         , GimpDrawable *selection_drawable);

static gdouble   p_compute_diagonal(AreaContext_t *acontext_ptr);
static void      p_debug_print_vals(AreaContext_t *acontext_ptr);

static void      p_set_default_values(dialog_widgets_t *gui_ptr);
static void      p_response_callback (GtkWidget *widget
                   ,gint       response_id
                   ,dialog_widgets_t *gui_ptr);
static gboolean  p_isle_dialog(GimpDrawable *drawable, isle_vals_t *isleval_ptr);


/***** Variables *****/

const GimpPlugInInfo PLUG_IN_INFO =
{
  NULL,   /* init_proc  */
  NULL,   /* quit_proc  */
  query,  /* query_proc */
  run     /* run_proc   */
};

gboolean gs_debug = FALSE;

/***** Functions *****/

MAIN()

/* ------------------
 * query
 * ------------------
 */
static void
query (void)
{
  static const GimpParamDef args[] =
  {
    { GIMP_PDB_INT32,    "run-mode",             "Interactive, non-interactive" },
    { GIMP_PDB_IMAGE,    "image",                "Input image"                  },
    { GIMP_PDB_DRAWABLE, "drawable",             "(ignored)"               },
    { GIMP_PDB_FLOAT,    "pixel_diagonal",       "maximum diagonal in pixels to consider as small isle"     },
    { GIMP_PDB_INT32,    "pixel_limit",          "maximum limit of pixels to consider as small isle"     },
    { GIMP_PDB_INT32,    "threshold",            "0..255 selection values below this threshold are treated as not selected"     },
    { GIMP_PDB_INT32,    "connect_by_corner",    "0: area touching only at corner are treated as seperate areas, !=0 are treated a one area" },
    { GIMP_PDB_INT32,    "affect_selected",      "0: dont affect selected isles, !=0 subtract selected isles from selection" },
    { GIMP_PDB_INT32,    "affect_unselected",    "0: dont affect unselected isles, !=0 add unselected isles to selection" },
    { GIMP_PDB_INT32,    "clear_below_threshold","0: clear selection below threshold (no matter if part of isle or not)" },
    { GIMP_PDB_INT32,    "keep_workchannels",    "0: remove work channles when finished, !=0 keep workchannels when finished" }
    
  };


  gimp_install_procedure (PLUG_IN_PROC,
                          N_("Subtract small isles from selection."),
                          "Subtract small isles from the current selection "
                          "and/or add small unselected isles to the current selection.  "
                          "The parameters pixel_diagonal and pixel_limit are used to "
                          "define area size that is considered as small isle. ",
                          "Wolfgang Hofer",
                          "Wolfgang Hofer",
                          PLUG_IN_VERSION,
                          N_("Subtract Small Selection Isles..."),
                          "RGB*, GRAY*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (args), 0,
                          args, NULL);

  gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Select");

}  /* end query */


/* ------------------
 * run
 * ------------------
 */
static void
run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
{
  static GimpParam values[1];
  static isle_vals_t gs_isle_vals;  


  GimpRunMode        run_mode;
  GimpPDBStatusType  status;
  GimpDrawable      *drawable;
  gint32             image_id;
  const gchar *l_env;

  status   = GIMP_PDB_SUCCESS;
  run_mode = param[0].data.d_int32;
  
  INIT_I18N ();

  l_env = g_getenv("ISLE_DEBUG");
  if(l_env != NULL)
    {
      if((*l_env != 'n') && (*l_env != 'N')) gs_debug = TRUE;
    }

  *nreturn_vals = 1;
  *return_vals  = values;

  values[0].type          = GIMP_PDB_STATUS;
  values[0].data.d_status = status;


  image_id = param[1].data.d_image;
  /* Get the active drawable info */
  drawable = gimp_drawable_get (param[2].data.d_drawable);

  /* Set the tile cache size */
  gimp_tile_cache_ntiles (2 * drawable->ntile_cols);
  
  /* start with harcoded default values
   * (maybe replaced by last values of the same session)
   */
  p_init_default_values(&gs_isle_vals);

  /* See how we will run */
  switch (run_mode)
    {
    case GIMP_RUN_INTERACTIVE:
      /* Possibly retrieve data */
      gimp_get_data (PLUG_IN_PROC, &gs_isle_vals);

      /* Get information from the dialog */
      if (!p_isle_dialog (drawable, &gs_isle_vals))
        return;

      break;

    case GIMP_RUN_NONINTERACTIVE:
      /* Make sure all the arguments are present */
      if (nparams != 11)
        {
          status = GIMP_PDB_CALLING_ERROR;
        }
      else
        {
          gs_isle_vals.pixel_diagonal        = param[3].data.d_float;
          gs_isle_vals.pixel_limit           = param[4].data.d_int32;
          gs_isle_vals.threshold             = param[5].data.d_int32;
          gs_isle_vals.connect_by_corner     = param[6].data.d_int32;
          gs_isle_vals.affect_selected       = param[7].data.d_int32;
          gs_isle_vals.affect_unselected     = param[8].data.d_int32;
          gs_isle_vals.clear_below_threshold = param[9].data.d_int32;
          gs_isle_vals.keep_workchannels     = param[10].data.d_int32;
        }

      break;

    case GIMP_RUN_WITH_LAST_VALS:
      /* Possibly retrieve data */
      gimp_get_data (PLUG_IN_PROC, &gs_isle_vals);
      break;

    default:
      break;
    }

  /* selection processing */
  if ((status == GIMP_PDB_SUCCESS)
  &&  (image_id >= 0)
  &&  (gimp_selection_is_empty(image_id) != TRUE))
    {

      /* Run! */
      gimp_image_undo_group_start (image_id);
      p_exec_selection_processing (image_id, &gs_isle_vals);
      gimp_image_undo_group_end (image_id);

      /* If run mode is interactive, flush displays */
      if (run_mode != GIMP_RUN_NONINTERACTIVE)
        {
          gimp_displays_flush ();
        }

      /* Store data */

      if (run_mode == GIMP_RUN_INTERACTIVE)
        {
          gimp_set_data (PLUG_IN_PROC, &gs_isle_vals, sizeof (isle_vals_t));
        }
    }
  else if (status == GIMP_PDB_SUCCESS)
    {
      status = GIMP_PDB_EXECUTION_ERROR;
    }

  values[0].data.d_status = status;

  gimp_drawable_detach (drawable);
}  /* end run */

/* --------------------------------
 * p_init_default_values
 * --------------------------------
 * init specified isle_vals with hardcoded default settings.
 */
static void
p_init_default_values (isle_vals_t *isleval_ptr)
{
  isleval_ptr->pixel_diagonal           = 5.0;
  isleval_ptr->pixel_limit              = 20;
  isleval_ptr->threshold                = 128;
  isleval_ptr->connect_by_corner        = FALSE;
  isleval_ptr->affect_selected          = TRUE;
  isleval_ptr->affect_unselected        = FALSE;
  isleval_ptr->clear_below_threshold    = TRUE;
  isleval_ptr->keep_workchannels        = FALSE;
}  /* end p_init_default_values */


/* --------------------------------
 * p_init_islePr
 * --------------------------------
 */
static void
p_init_islePr(AreaContext_t *acontext_ptr, GimpPixelRgn *islePR_ptr)
{
  gimp_pixel_rgn_init (islePR_ptr, acontext_ptr->isle_drawable
                      , acontext_ptr->sel_x1
                      , acontext_ptr->sel_y1
                      , acontext_ptr->sel_width
                      , acontext_ptr->sel_height
		      , TRUE      /* dirty */
		      , FALSE     /* shadow */
		       );
}  /* end p_init_islePr */


/* --------------------------------
 * p_exec_selection_processing
 * --------------------------------
 * this procedure performs the distortion
 * of the specified drawable, according
 * to paramters (isleval_ptr)
 */
static void
p_exec_selection_processing (gint32 image_id
   , isle_vals_t *isleval_ptr)
{
  AreaContext_t     acontext;
  AreaContext_t    *acontext_ptr;
  gboolean          non_empty;

  acontext_ptr = &acontext;
  
  acontext_ptr->isleval_ptr = isleval_ptr;
  gimp_selection_bounds (image_id
				     , &non_empty
				     , &acontext_ptr->sel_x1
				     , &acontext_ptr->sel_y1
				     , &acontext_ptr->sel_x2
				     , &acontext_ptr->sel_y2
				     );

  acontext_ptr->sel_width = acontext_ptr->sel_x2 - acontext_ptr->sel_x1;
  acontext_ptr->sel_height = acontext_ptr->sel_y2 - acontext_ptr->sel_y1;

  if (acontext_ptr->isleval_ptr->clear_below_threshold)
    {
      GimpDrawable *selection_drawable;
      selection_drawable = gimp_drawable_get(gimp_image_get_selection(image_id));
      p_clear_selection_below_threshold(acontext_ptr, selection_drawable);
    }

  if (acontext_ptr->isleval_ptr->affect_selected)
    {
      p_subtract_selected_isles(image_id, acontext_ptr);
    }
  if (acontext_ptr->isleval_ptr->affect_unselected)
    {
      gimp_selection_invert(image_id);
      gimp_selection_bounds (image_id
				     , &non_empty
				     , &acontext_ptr->sel_x1
				     , &acontext_ptr->sel_y1
				     , &acontext_ptr->sel_x2
				     , &acontext_ptr->sel_y2
				     );
      p_subtract_selected_isles(image_id, acontext_ptr);
      gimp_selection_invert(image_id);
    }

  if (gs_debug)
    {
      p_debug_print_vals(acontext_ptr);
    }
}  /* end p_exec_selection_processing */


/* --------------------------------
 * p_subtract_selected_isles
 * --------------------------------
 */
static void
p_subtract_selected_isles(gint32 image_id, AreaContext_t *acontext_ptr)
{
  gint32            isle_id;

  /* save original selection to a channel (working copy for the isle detection) */
  isle_id = gimp_selection_save(image_id);
  acontext_ptr->isle_drawable = gimp_drawable_get(isle_id);

  if (gimp_drawable_bpp(isle_id) != 1)
    {
      printf("selection with bpp %d not supported (bpp must be 1)\n"
        , (int)gimp_drawable_bpp(isle_id)
        );
      return;
    }

  p_set_all_selected_pixels_unchecked(acontext_ptr);
  
  p_clear_selction_areas_by_size(acontext_ptr);

  gimp_selection_combine(isle_id, GIMP_CHANNEL_OP_SUBTRACT);
  
  if (!acontext_ptr->isleval_ptr->keep_workchannels)
    {
      gimp_image_remove_channel(image_id, isle_id);
      gimp_drawable_delete(isle_id);
    }
}  /* end p_subtract_selected_isles */


/* --------------------------------
 * p_clear_selction_areas_by_size
 * --------------------------------
 */
static void
p_clear_selction_areas_by_size(AreaContext_t *acontext_ptr)
{
  guchar          pixel[4];
  gboolean        small_area;
  gint            row;
  gint            col;

  acontext_ptr->pft = gimp_pixel_fetcher_new(acontext_ptr->isle_drawable
                                             , FALSE   /* shadow */
                                             );

  for (row = acontext_ptr->sel_y1; row <= ((acontext_ptr->sel_y1 + acontext_ptr->sel_y2)); row++)
    {
      for (col = acontext_ptr->sel_x1; col < acontext_ptr->sel_x2; col++)
        {
          gimp_pixel_fetcher_get_pixel (acontext_ptr->pft, col, row, &pixel[0]);
          
          if ((gs_debug) && (pixel[0] != SEL_NONE))
            {
              printf("SEL_ANY  x:%4d y:%4d value:%d", col, row, (int)pixel[0]);
            }
       
          
          if (pixel[0] == SEL_UNCHECKED)
            {
              if (gs_debug)
                {
                  printf(" UNCHECKED");
                }
              p_reset_area_context(acontext_ptr, col, row);
              small_area = p_mark_area(acontext_ptr);
              if (small_area)
                {
                  acontext_ptr->marker = SEL_FULL;
                  p_mark_area(acontext_ptr);
                  if (gs_debug)
                    {
                      printf(" SMALL");
                    }
                }
            }

          if ((gs_debug) && (pixel[0] != SEL_NONE))
            {
              printf("\n");
            }
            
            
            
        }
    }


  /* the isle channel now contains small area isles marked as SEL_FULL
   * and big areas makrked as SEL_VISTED.
   * (and unselected areas as SEL_NONE)
   *
   * clear all pixels that are not part of a small area isle
   * alternative option:
   * clear all pixels that are not part of a big area.
   *
   * Note that the isle channel will be subtracted from the original
   * selection.
   */

  p_clear_all_not_full_selected_pixels(acontext_ptr);

  gimp_drawable_flush (acontext_ptr->isle_drawable);

  
}  /* p_clear_selction_areas_by_size */

/* --------------------------------
 * p_mark_area
 * --------------------------------
 * find and mark all selected pixels
 * belonging to the same area as the seed pixel of the area context.
 * return TRUE if the marked are is considered as small isle 
 *                (according to current isle param value settings)
 *        FALSE if the marked area is greater or the seed pixel
 *                is not marked at all.
 */
static gboolean
p_mark_area(AreaContext_t *acontext_ptr)
{
  p_check_and_mark_selected_pixel(acontext_ptr
                       ,acontext_ptr->seed_ix
                       ,acontext_ptr->seed_iy
                       );
  if (acontext_ptr->pixel_count <= acontext_ptr->isleval_ptr->pixel_limit)
    {
      gdouble diagonal;
      diagonal = p_compute_diagonal(acontext_ptr);
      if (diagonal <= acontext_ptr->isleval_ptr->pixel_diagonal)
        {
          return (TRUE);
        }
    }
  return (FALSE);
}  /* p_mark_area */



/* --------------------------------
 * p_check_and_mark_selected_pixel
 * --------------------------------
 * mark a selected but not yet marked pixel
 * with current area context marker value.
 * Also recursivley do this for all neighnour pixels.
 *
 * Note: the marker value must be different to SEL_UNCHECKED.
 *
 * Further update the number of pixels and min/max boundaries
 * in the area context.
 *
 * This procedure is typically called with the coords of a seed pixel
 * and returns if the full area of selected pixels surrounding the seed pixel 
 * is marked and analyzed.
 */
static void
p_check_and_mark_selected_pixel(AreaContext_t *acontext_ptr, gint32 ix, gint32 iy)
{
  guchar          pixel[4];

  gimp_pixel_fetcher_get_pixel (acontext_ptr->pft, ix, iy, &pixel[0]);

  if ((pixel[0] == SEL_NONE) || (pixel[0] == acontext_ptr->marker))
    {
       return;
    }

  p_mark_pixel(acontext_ptr, ix, iy);
  acontext_ptr->pixel_count++;
  acontext_ptr->max_ix = MAX(acontext_ptr->max_ix, ix);
  acontext_ptr->max_iy = MAX(acontext_ptr->max_iy, iy);
  acontext_ptr->min_ix = MIN(acontext_ptr->min_ix, ix);
  acontext_ptr->min_iy = MIN(acontext_ptr->min_iy, iy);
  
  /* check neighbours */
  if (ix > acontext_ptr->sel_x1)
    {
      p_check_and_mark_selected_pixel(acontext_ptr, ix-1, iy);
    }

  if (ix < acontext_ptr->sel_x2)
    {
      p_check_and_mark_selected_pixel(acontext_ptr, ix+1, iy);
    }

  if (iy > acontext_ptr->sel_y1)
    {
      p_check_and_mark_selected_pixel(acontext_ptr, ix, iy-1);
    }

  if (iy < acontext_ptr->sel_y2)
    {
      p_check_and_mark_selected_pixel(acontext_ptr, ix, iy+1);
    }



  if (acontext_ptr->isleval_ptr->connect_by_corner != TRUE)
    {
       return;
    }

  /* check diagonal neighbours */
  if ((ix > acontext_ptr->sel_x1) && (iy > acontext_ptr->sel_y1))
    {
      p_check_and_mark_selected_pixel(acontext_ptr, ix-1, iy-1);
    }

  if ((ix < acontext_ptr->sel_x2) && (iy < acontext_ptr->sel_y2))
    {
      p_check_and_mark_selected_pixel(acontext_ptr, ix+1, iy+1);
    }

  if ((iy > acontext_ptr->sel_y1) && (ix < acontext_ptr->sel_x2))
    {
      p_check_and_mark_selected_pixel(acontext_ptr, ix+1, iy-1);
    }

  if ((iy < acontext_ptr->sel_y2) && (ix > acontext_ptr->sel_x1))
    {
      p_check_and_mark_selected_pixel(acontext_ptr, ix-1, iy+1);
    }
  
}  /* end p_check_and_mark_selected_pixel */



/* --------------------------------
 * p_mark_pixel
 * --------------------------------
 * mark the pixel at position ix/iy
 * with the current marker byte of the area context.
 */
static void
p_mark_pixel(AreaContext_t *acontext_ptr, gint32 ix, gint32 iy)
{
  guchar          pixel[4];
  
  pixel[0] = acontext_ptr->marker;
  gimp_pixel_fetcher_put_pixel(acontext_ptr->pft, ix, iy, &pixel[0]);
  
  if (gs_debug)
    {
      printf("  mark x:%4d y:%4d [%d]\n"
       ,ix
       ,iy
       ,pixel[0]
       );                                           
    }
}  /* end p_mark_pixel */


/* --------------------------------
 * p_reset_area_context
 * --------------------------------
 */
static void
p_reset_area_context(AreaContext_t *acontext_ptr, gint32 ix, gint32 iy)
{
  acontext_ptr->seed_ix = ix;
  acontext_ptr->seed_iy = iy;
  acontext_ptr->max_ix = ix;
  acontext_ptr->min_ix = ix;
  acontext_ptr->max_iy = iy;
  acontext_ptr->min_iy = iy;
  acontext_ptr->pixel_count = 0;
  acontext_ptr->marker = SEL_VISITED;
  
}  /* end p_reset_area_context */


/* -----------------------------------
 * p_set_all_selected_pixels_unchecked
 * -----------------------------------
 */
static void
p_set_all_selected_pixels_unchecked(AreaContext_t *acontext_ptr)
{
  GimpPixelRgn      islePR;
  gpointer  pr;
  
  p_init_islePr(acontext_ptr, &islePR);

  for (pr = gimp_pixel_rgns_register (1, &islePR);
       pr != NULL;
       pr = gimp_pixel_rgns_process (pr))
    {
        guint    row;
        guint    col;
        guchar  *dest;

        dest = islePR.data;
        for (row = 0; row < islePR.h; row++)
          {
            guchar* dest_ptr = dest;

            for(col = 0; col < islePR.w; col++)
              {
                if((*dest_ptr != SEL_NONE)
                && (*dest_ptr >= acontext_ptr->isleval_ptr->threshold))
                  {
                    *dest_ptr = SEL_UNCHECKED;
                  }
                else
                  {
                    *dest_ptr = SEL_NONE;
                  }
                dest_ptr += islePR.bpp;
              }

            dest += islePR.rowstride;
          }
    }

  
}  /* end p_set_all_selected_pixels_unchecked */


/* ------------------------------------
 * p_clear_all_not_full_selected_pixels
 * ------------------------------------
 */
static void
p_clear_all_not_full_selected_pixels(AreaContext_t *acontext_ptr)
{
  GimpPixelRgn      islePR;
  gpointer  pr;

  p_init_islePr(acontext_ptr, &islePR);

  for (pr = gimp_pixel_rgns_register (1, &islePR);
       pr != NULL;
       pr = gimp_pixel_rgns_process (pr))
    {
        guint    row;
        guint    col;
        guchar  *dest;

        dest = islePR.data;
        for (row = 0; row < islePR.h; row++)
          {
            guchar* dest_ptr = dest;

            for(col = 0; col < islePR.w; col++)
              {
                if (*dest_ptr != SEL_FULL)
                  {
                    *dest_ptr = SEL_NONE;
                  }
                dest_ptr += islePR.bpp;
              }

            dest += islePR.rowstride;
          }
    }

  
}  /* end p_clear_all_not_full_selected_pixels */


/* --------------------------------
 * p_clear_selection_below_threshold
 * --------------------------------
 */
static void
p_clear_selection_below_threshold(AreaContext_t *acontext_ptr, GimpDrawable *selection_drawable)
{
  GimpPixelRgn      selectionPR;
  gpointer  pr;

  gimp_pixel_rgn_init (&selectionPR, selection_drawable
                      , acontext_ptr->sel_x1
                      , acontext_ptr->sel_y1
                      , acontext_ptr->sel_width
                      , acontext_ptr->sel_height
		      , TRUE      /* dirty */
		      , FALSE     /* shadow */
		       );

  for (pr = gimp_pixel_rgns_register (1, &selectionPR);
       pr != NULL;
       pr = gimp_pixel_rgns_process (pr))
    {
        guint    row;
        guint    col;
        guchar  *dest;

        dest = selectionPR.data;
        for (row = 0; row < selectionPR.h; row++)
          {
            guchar* dest_ptr = dest;

            for(col = 0; col < selectionPR.w; col++)
              {
                if (*dest_ptr <  acontext_ptr->isleval_ptr->threshold)
                  {
                    *dest_ptr = SEL_NONE;
                  }
                dest_ptr += selectionPR.bpp;
              }

            dest += selectionPR.rowstride;
          }
    }
}  /* end p_clear_selection_below_threshold */


/* --------------------------------
 * p_compute_diagonal
 * --------------------------------
 */
static gdouble
p_compute_diagonal(AreaContext_t *acontext_ptr)
{
  gdouble dx;
  gdouble dy;
  gdouble diagonal;
  
  dx = acontext_ptr->max_ix - acontext_ptr->min_ix;
  dy = acontext_ptr->max_iy - acontext_ptr->min_iy;
  
  diagonal = sqrt((dx * dx) + (dy * dy));
  return (diagonal);
  
}  /* end p_compute_diagonal */


static void
p_debug_print_vals(AreaContext_t *acontext_ptr)
{
  printf("\nVALS:\n");
  printf("  pixel_diagonal:%.4f\n", (float)acontext_ptr->isleval_ptr->pixel_diagonal);
  printf("  pixel_limit:%d\n", (int)acontext_ptr->isleval_ptr->pixel_limit);
  printf("  threshold:%d\n", (int)acontext_ptr->isleval_ptr->threshold);
  printf("  connect_by_corner:%d\n", (int)acontext_ptr->isleval_ptr->connect_by_corner);
  printf("  affect_selected:%d\n", (int)acontext_ptr->isleval_ptr->affect_selected);
  printf("  affect_unselected:%d\n", (int)acontext_ptr->isleval_ptr->affect_unselected);
  printf("  clear_below_threshold:%d\n", (int)acontext_ptr->isleval_ptr->clear_below_threshold);
  printf("  keep_workchannels:%d\n", (int)acontext_ptr->isleval_ptr->keep_workchannels);
  printf("\nSELCTION:\n");
  printf("  sel_x1:%d\n", (int)acontext_ptr->sel_x1);
  printf("  sel_y1:%d\n", (int)acontext_ptr->sel_y1);
  printf("  sel_x2:%d\n", (int)acontext_ptr->sel_x2);
  printf("  sel_y2:%d\n", (int)acontext_ptr->sel_y2);
  printf("  sel_width:%d\n", (int)acontext_ptr->sel_width);
  printf("  sel_height:%d\n", (int)acontext_ptr->sel_height);
}

/* -----------------------------------------------------------  */
/* ------- D I A L O G --- GUI Procedures --------------------  */
/* -----------------------------------------------------------  */

/* --------------------------------
 * p_set_default_values
 * --------------------------------
 * reset all values to default (by entering default values in
 * the correspnding widgets via program control)
 */
static void
p_set_default_values(dialog_widgets_t *gui_ptr)
{
  isle_vals_t default_isle_vals;
  isle_vals_t *isleval_ptr;
  
  if(gui_ptr==NULL)
    {
      return;
    }

  isleval_ptr = &default_isle_vals;
  p_init_default_values(isleval_ptr);


  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gui_ptr->check_affect_selected)
                              , isleval_ptr->affect_selected);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gui_ptr->check_affect_unselected)
                              , isleval_ptr->affect_unselected);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gui_ptr->check_clear_below_threshold)
                              , isleval_ptr->clear_below_threshold);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gui_ptr->check_connect_by_corner)
                              , isleval_ptr->connect_by_corner);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gui_ptr->check_keep_workchannels)
                              , isleval_ptr->keep_workchannels);

  gtk_adjustment_set_value (GTK_ADJUSTMENT (gui_ptr->adj_pixel_diagonal)
                           , (gfloat)isleval_ptr->pixel_diagonal);
  gtk_adjustment_set_value (GTK_ADJUSTMENT (gui_ptr->adj_pixel_limit)
                           , (gfloat)isleval_ptr->pixel_limit);
  gtk_adjustment_set_value (GTK_ADJUSTMENT (gui_ptr->adj_threshold)
                           , (gfloat)isleval_ptr->threshold);
  
}  /* end p_set_default_values */


/* ---------------------------------
 * p_response_callback
 * ---------------------------------
 * handle response.
 */
static void
p_response_callback (GtkWidget *widget
                 ,gint       response_id
                 ,dialog_widgets_t *gui_ptr)
{
  GtkWidget *dialog;
  
  switch (response_id)
    {
    case ISLE_RESPONSE_RESET:
      p_set_default_values(gui_ptr);
      break;

    case GTK_RESPONSE_OK:
      gui_ptr->run_flag = TRUE;
      /* fall through */

    default:
      dialog = gui_ptr->dialog;
      gui_ptr->dialog = NULL;
      if (dialog != NULL)
        {
          gtk_widget_hide (dialog);
          gtk_widget_destroy (dialog);
          gtk_main_quit();
        }
      break;
    }
}  /* end p_response_callback */


/* --------------------------------
 * p_isle_dialog
 * --------------------------------
 * gtk dialog windows with controls to adjust parmeters
 */
static gboolean
p_isle_dialog (GimpDrawable *drawable, isle_vals_t *isleval_ptr)
{
  GtkWidget *main_vbox;
  GtkWidget *table;
  GtkWidget *check;
  GtkObject *adj;
  gint       row;
  gint       col;

  dialog_widgets_t  dialog_widgets;
  dialog_widgets_t *gui_ptr;

  
  gui_ptr = &dialog_widgets;

  gui_ptr->run_flag = FALSE;

  gimp_ui_init (PLUG_IN_BINARY, TRUE);

  gui_ptr->dialog = gimp_dialog_new (_("Remove Selection Isles"), PLUG_IN_BINARY,
                            NULL, 0,
                            gimp_standard_help_func, PLUG_IN_PROC,

			    GIMP_STOCK_RESET, ISLE_RESPONSE_RESET,
                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                            GTK_STOCK_OK,     GTK_RESPONSE_OK,
                            NULL);

  gtk_dialog_set_alternative_button_order (GTK_DIALOG (gui_ptr->dialog),
                                           GTK_RESPONSE_OK,
                                           GTK_RESPONSE_CANCEL,
                                           -1);
  g_signal_connect (G_OBJECT (gui_ptr->dialog), "response",
                    G_CALLBACK (p_response_callback),
                    gui_ptr);


  main_vbox = gtk_vbox_new (FALSE, 12);
  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (gui_ptr->dialog)->vbox), main_vbox);
  gtk_widget_show (main_vbox);


  /* Controls */
  table = gtk_table_new (9, 3, FALSE);
  gtk_table_set_col_spacings (GTK_TABLE (table), 5);
  gtk_table_set_row_spacings (GTK_TABLE (table), 5);
  gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
  gtk_widget_show (table);

  /* check button affect_selected */
  row=0;
  check = gtk_check_button_new_with_label (_("Selected"));
  gui_ptr->check_affect_selected = check;
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), isleval_ptr->affect_selected);
  gtk_table_attach (GTK_TABLE (table), check, 1, 3, row, row + 1,
                    GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
  gimp_help_set_help_data (GTK_WIDGET (check),
			   _("Remove small selection isles from selection"), NULL);
  g_signal_connect (check, "toggled",
                    G_CALLBACK (gimp_toggle_button_update),
                    &isleval_ptr->affect_selected);
  gtk_widget_show (check);

  /* check button affect_unselected */
  row++;
  check = gtk_check_button_new_with_label (_("Unselected"));
  gui_ptr->check_affect_unselected = check;
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), isleval_ptr->affect_unselected);
  gtk_table_attach (GTK_TABLE (table), check, 1, 3, row, row + 1,
                    GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
  gimp_help_set_help_data (GTK_WIDGET (check),
			   _("Add small unselected isles to selection"), NULL);
  g_signal_connect (check, "toggled",
                    G_CALLBACK (gimp_toggle_button_update),
                    &isleval_ptr->affect_unselected);
  gtk_widget_show (check);

  /* check button clear_below_threshold */
  row++;
  check = gtk_check_button_new_with_label (_("Clear Weak"));
  gui_ptr->check_clear_below_threshold = check;
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), isleval_ptr->clear_below_threshold);
  gtk_table_attach (GTK_TABLE (table), check, 1, 3, row, row + 1,
                    GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
  gimp_help_set_help_data (GTK_WIDGET (check),
			   _("Clear weak selected pixels below threshold "
			   "(no matter if part of small isle or not)"), NULL);
  g_signal_connect (check, "toggled",
                    G_CALLBACK (gimp_toggle_button_update),
                    &isleval_ptr->clear_below_threshold);
  gtk_widget_show (check);

  /* check button connect_by_corner */
  row++;
  check = gtk_check_button_new_with_label (_("Connect By Corner"));
  gui_ptr->check_connect_by_corner = check;
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), isleval_ptr->connect_by_corner);
  gtk_table_attach (GTK_TABLE (table), check, 1, 3, row, row + 1,
                    GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
  gimp_help_set_help_data (GTK_WIDGET (check),
			   _("ON: selected pixels touching only at corners are"
                             "treated as part of the same area"), NULL);
  g_signal_connect (check, "toggled",
                    G_CALLBACK (gimp_toggle_button_update),
                    &isleval_ptr->connect_by_corner);
  gtk_widget_show (check);

  /* check button keep_workchannels */
  row++;
  check = gtk_check_button_new_with_label (_("KeepWorkchannels"));
  gui_ptr->check_keep_workchannels = check;
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), isleval_ptr->keep_workchannels);
  gtk_table_attach (GTK_TABLE (table), check, 1, 3, row, row + 1,
                    GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
  gimp_help_set_help_data (GTK_WIDGET (check),
			   _("Says whether or not keep working channels "), NULL);
  g_signal_connect (check, "toggled",
                    G_CALLBACK (gimp_toggle_button_update),
                    &isleval_ptr->keep_workchannels);
  if (gs_debug)
    {
      gtk_widget_show (check);
    }
  else
    {
      gtk_widget_hide (check);
    }

  /* the pixel_diagonal */
  row = 0;
  col = 3;
  adj = gimp_scale_entry_new (GTK_TABLE (table), col, row,
                  _("Area Diagonal"), SCALE_WIDTH, SPIN_BUTTON_WIDTH,
                  isleval_ptr->pixel_diagonal,
                  1.0, 1000.0, 1.0, 10.0, 2,
                  TRUE,
                  0, 0,
                  _("area diagonal in pixels. "
                    "selection areas with greater diagonal are NOT considered as small isles"),
                  NULL);
  gui_ptr->adj_pixel_diagonal = adj;
  g_signal_connect (adj, "value-changed",
                    G_CALLBACK (gimp_double_adjustment_update),
                    &isleval_ptr->pixel_diagonal);


  /* the  pixel_limit */
  row++;
  adj = gimp_scale_entry_new(GTK_TABLE(table), col, row, 
			     _("Pixel Limit"), 
			     SCALE_WIDTH, SPIN_BUTTON_WIDTH, 
			     isleval_ptr->pixel_limit,
			     1, 20000, 1,10,0, 
			     TRUE,
			     0, 0,
			     ("selection areas having more pixels are NOT considered as small isles"),
			     NULL);
  gui_ptr->adj_pixel_limit = adj;
  g_signal_connect (adj, "value_changed",
		     G_CALLBACK (gimp_int_adjustment_update),
		     &isleval_ptr->pixel_limit);


  /* the selection threshold */
  row++;
  adj = gimp_scale_entry_new(GTK_TABLE(table), col, row, 
			     _("Selection Threshold"), 
			     SCALE_WIDTH, SPIN_BUTTON_WIDTH, 
			     isleval_ptr->threshold,
			     0, 255, 1,10,0, 
			     TRUE,
			     0, 0,
			     _("selection threshold (weak selected pixels below threshold are "
			       "treated as unselected pixels at isle detection)"),
			     NULL);
  gui_ptr->adj_threshold = adj;
  g_signal_connect (adj, "value_changed",
		     G_CALLBACK (gimp_int_adjustment_update),
		     &isleval_ptr->threshold);



  /* Done */

  gtk_widget_show (gui_ptr->dialog);

  gtk_main ();
  gdk_flush ();


  return gui_ptr->run_flag;
}  /* end p_isle_dialog */

