/* 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 <stdio.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_DIRENT_H
#include <sys/types.h>
#include <dirent.h>
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include "appenv.h"
#include "color_area.h"
#include "color_notebook.h"
#include "datafiles.h"
#include "general.h"
#include "gimpcontext.h"
#include "gimpdnd.h"
#include "gimprc.h"
#include "gimpui.h"
#include "gradient_header.h"
#include "gradient_select.h"
#include "palette.h"
#include "paletteP.h"
#include "palette_entries.h"
#include "session.h"
#include "palette_select.h"
#include "dialog_handler.h"

#include "libgimp/gimpintl.h"

#include "pixmaps/zoom_in.xpm"
#include "pixmaps/zoom_out.xpm"

#define ENTRY_WIDTH  12
#define ENTRY_HEIGHT 10
#define SPACING       1
#define COLUMNS      16
#define ROWS         11

#define PREVIEW_WIDTH  ((ENTRY_WIDTH * COLUMNS) + (SPACING * (COLUMNS + 1)))
#define PREVIEW_HEIGHT ((ENTRY_HEIGHT * ROWS) + (SPACING * (ROWS + 1)))

#define PALETTE_EVENT_MASK GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | \
                           GDK_ENTER_NOTIFY_MASK

/* New palette code... */

#define IMPORT_PREVIEW_WIDTH  80
#define IMPORT_PREVIEW_HEIGHT 80
#define MAX_IMAGE_COLORS      (10000*2)

typedef enum
{
  GRAD_IMPORT = 0,
  IMAGE_IMPORT = 1,
  INDEXED_IMPORT = 2
} ImportType;

typedef struct _ImportDialog ImportDialog;

struct _ImportDialog
{
  GtkWidget     *dialog;
  GtkWidget     *preview;
  GtkWidget     *entry;
  GtkWidget     *select_area;
  GtkWidget     *select;
  GtkWidget     *image_list;
  GtkWidget     *image_menu_item_image;
  GtkWidget     *image_menu_item_indexed;
  GtkWidget     *image_menu_item_gradient;
  GtkWidget     *optionmenu1_menu;
  GtkWidget     *type_option;
  GtkWidget     *threshold_scale;
  GtkWidget     *threshold_text;
  GtkAdjustment *threshold;
  GtkAdjustment *sample;
  ImportType     import_type;
  GimpImage     *gimage;
};

typedef struct _PaletteDialog PaletteDialog;

struct _PaletteDialog
{
  GtkWidget      *shell;

  GtkWidget      *color_area;
  GtkWidget      *scrolled_window;
  GtkWidget      *color_name;
  GtkWidget      *clist;

  GtkWidget      *popup_menu;
  GtkWidget      *delete_menu_item;
  GtkWidget      *edit_menu_item;

  ColorNotebookP  color_notebook;
  gboolean        color_notebook_active;

  PaletteEntries *entries;
  PaletteEntry   *color;
  PaletteEntry   *dnd_color;

  GdkGC          *gc;
  guint           entry_sig_id;
  gfloat          zoom_factor;  /* range from 0.1 to 4.0 */
  gfloat          xzoom_factor;  
  gint            last_width;
  gint            columns;
  gboolean        freeze_update;
  gboolean        columns_valid;
};

/*  local function prototypes  */
static void  palette_entry_free    (PaletteEntry *);
static void  palette_entries_free  (PaletteEntries *);
static void  palette_entries_load  (gchar *);
static void  palette_entries_save  (PaletteEntries *, gchar *);
static void  palette_save_palettes (void);

static void  palette_entries_list_insert    (PaletteEntries *entries);

static void  palette_dialog_draw_entries    (PaletteDialog *palette,
					     gint           row_start,
					     gint           column_highlight);
static void  palette_dialog_redraw          (PaletteDialog *palette);
static void  palette_dialog_scroll_top_left (PaletteDialog *palette);

static PaletteDialog * palette_dialog_new        (gboolean vert);
static ImportDialog  * palette_import_dialog_new (PaletteDialog *palette);


GSList                *palette_entries_list    = NULL;
PaletteDialog         *top_level_edit_palette  = NULL;
PaletteDialog         *top_level_palette       = NULL;

static PaletteEntries *default_palette_entries = NULL;
static gint            num_palette_entries     = 0;
static ImportDialog   *import_dialog           = NULL;


/*  dnd stuff  */
static GtkTargetEntry color_palette_target_table[] =
{
  GIMP_TARGET_COLOR
};
static guint n_color_palette_targets = (sizeof (color_palette_target_table) /
					sizeof (color_palette_target_table[0]));

/*  public functions  ********************************************************/

void
palettes_init (gint no_data)
{
  if (!no_data)
    datafiles_read_directories (palette_path, palette_entries_load, 0);

  if (!default_palette_entries && palette_entries_list)
    default_palette_entries = palette_entries_list->data;
}

void
palette_init_palettes (gint no_data)
{
  palettes_init (no_data);
}

void
palettes_free (void)
{
  PaletteEntries *entries;
  GSList *list;

  for (list = palette_entries_list; list; list = g_slist_next (list))
    {
      entries = (PaletteEntries *) list->data;

      /*  If the palette has been changed, save it, if possible  */
      if (entries->changed)
	/*  save the palette  */
	palette_entries_save (entries, entries->filename);

      palette_entries_free (entries);
    }

  g_slist_free (palette_entries_list);

  num_palette_entries = 0;
  palette_entries_list = NULL;
}

void
palette_free_palettes (void)
{
  palettes_free ();
}

void 
palette_dialog_create (void)
{
  if (top_level_palette == NULL)
    {
      top_level_palette = palette_dialog_new (TRUE);
      session_set_window_geometry (top_level_palette->shell,
				   &palette_session_info, TRUE);
      dialog_register (top_level_palette->shell);

      gtk_widget_show (top_level_palette->shell);
    }
  else
    {
      if (! GTK_WIDGET_VISIBLE (top_level_palette->shell))
	{
	  gtk_widget_show (top_level_palette->shell);
	}
      else
	{
	  gdk_window_raise (top_level_palette->shell->window);
	}
    }
}

void
palette_dialog_free (void)
{
  if (top_level_edit_palette) 
    {
      if (import_dialog)
	{
	  gtk_widget_destroy (import_dialog->dialog);
	  g_free (import_dialog);
	  import_dialog = NULL;
	}

      gdk_gc_destroy (top_level_edit_palette->gc); 
      
      if (top_level_edit_palette->color_notebook) 
	color_notebook_free (top_level_edit_palette->color_notebook); 

      g_free (top_level_edit_palette); 
      top_level_edit_palette = NULL; 
    }

  if (top_level_palette)
    {
      gdk_gc_destroy (top_level_palette->gc); 
      session_get_window_info (top_level_palette->shell,
			       &palette_session_info);  

      if (top_level_palette->color_notebook) 
	color_notebook_free (top_level_palette->color_notebook); 

      g_free (top_level_palette);
      top_level_palette = NULL;
    }
}

/*  palette entries functions  ***********************************************/

static PaletteEntries *
palette_entries_new (gchar *palette_name)
{
  PaletteEntries *entries = NULL;
  gchar *home;
  gchar *local_path;
  gchar *first_token;
  gchar *token;
  gchar *path;

  if (palette_name)
    {
      entries = g_new (PaletteEntries, 1);
      if (palette_path)
	{
	  /*  Get the first path specified in the palette path list  */
	  home = g_get_home_dir ();
	  local_path = g_strdup (palette_path);
	  first_token = local_path;
	  token = xstrsep (&first_token, G_SEARCHPATH_SEPARATOR_S);

	  if (token)
	    {
	      if (*token == '~')
		{
		  if (home)
		    path = g_strdup_printf ("%s%s", home, token + 1);
		  else
		    /* Just ignore the ~ if no HOME ??? */
		    path = g_strdup (token + 1);
		}
	      else
		{
		  path = g_strdup (token);
		}

	      entries->filename = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s",
						   path, palette_name);

	      g_free (path);
	    }

	  g_free (local_path);
	}
      else
	entries->filename = NULL;

      entries->name = palette_name;  /*  don't need to copy because this memory is ours  */
      entries->colors = NULL;
      entries->n_colors = 0;
      entries->changed = TRUE;
      entries->pixmap = NULL;

      palette_entries_list_insert (entries);

      palette_save_palettes ();
    }

  return entries;
}

static void
palette_entries_free (PaletteEntries *entries)
{
  PaletteEntry *entry;
  GSList *list;

  for (list = entries->colors; list; list = g_slist_next (list))
    {
      entry = (PaletteEntry *) list->data;

      palette_entry_free (entry);
    }

  g_free (entries->name);

  if (entries->filename)
    g_free (entries->filename);

  g_free (entries);
}

static PaletteEntry *
palette_entries_add_entry (PaletteEntries *entries,
			   gchar          *name,
			   gint            r,
			   gint            g,
			   gint            b)
{
  PaletteEntry *entry;

  if (entries)
    {
      entry = g_new (PaletteEntry, 1);

      entry->color[0] = r;
      entry->color[1] = g;
      entry->color[2] = b;
      entry->name     = g_strdup (name ? name : _("Untitled"));
      entry->position = entries->n_colors;

      entries->colors    = g_slist_append (entries->colors, entry);
      entries->n_colors += 1;
      entries->changed   = TRUE;
      
      return entry;
    }

  return NULL;
}

static void
palette_entries_load (gchar *filename)
{
  PaletteEntries *entries;
  gchar  str[512];
  gchar *tok;
  FILE  *fp;
  gint   r, g, b;
  gint   linenum;

  r = g = b = 0;

  entries = g_new (PaletteEntries, 1);

  entries->filename = g_strdup (filename);
  entries->name     = g_strdup (g_basename (filename));
  entries->colors   = NULL;
  entries->n_colors = 0;
  entries->pixmap   = NULL;

  /*  Open the requested file  */
  if (! (fp = fopen (filename, "r")))
    {
      palette_entries_free (entries);
      g_warning ("failed to open palette file %s: can't happen?", filename);
      return;
    }

  linenum = 0;

  fread (str, 13, 1, fp);
  str[13] = '\0';
  linenum++;
  if (strcmp (str, "GIMP Palette\n"))
    {
      /* bad magic, but maybe it has \r\n at the end of lines? */
      if (!strcmp (str, "GIMP Palette\r"))
	g_message (_("Loading palette %s:\n"
		     "Corrupt palette:\n"
		     "missing magic header\n"
		     "Does this file need converting from DOS?"), filename);
      else
	g_message (_("Loading palette %s:\n"
		     "Corrupt palette: missing magic header"), filename);
      fclose (fp);
      palette_entries_free (entries);
      return;
    }

  while (!feof (fp))
    {
      if (!fgets (str, 512, fp))
	{
	  if (feof (fp))
	    break;
	  g_message (_("Loading palette %s (line %d):\nRead error"),
		     filename, linenum);
	  fclose (fp);
	  palette_entries_free (entries);
	  return;
	}

      linenum++;

      if (str[0] != '#')
	{
	  tok = strtok (str, " \t");
	  if (tok)
	    r = atoi (tok);
	  else
	    /* maybe we should just abort? */
	    g_message (_("Loading palette %s (line %d):\n"
			 "Missing RED component"), filename, linenum);

	  tok = strtok (NULL, " \t");
	  if (tok)
	    g = atoi (tok);
	  else
	    g_message (_("Loading palette %s (line %d):\n"
			 "Missing GREEN component"), filename, linenum);
	  
	  tok = strtok (NULL, " \t");
	  if (tok)
	    b = atoi (tok);
	  else
	    g_message (_("Loading palette %s (line %d):\n"
			 "Missing BLUE component"), filename, linenum);

	  /* optional name */
	  tok = strtok (NULL, "\n");

	  if (r < 0 || r > 255 ||
	      g < 0 || g > 255 ||
	      b < 0 || b > 255)
	    g_message (_("Loading palette %s (line %d):\n"
			 "RGB value out of range"), filename, linenum);

	  palette_entries_add_entry (entries, tok, r, g, b);
	}
    }

  /*  Clean up  */
  fclose (fp);
  entries->changed = FALSE;

  palette_entries_list_insert (entries);

  /*  Check if the current palette is the default one  */
  if (strcmp (default_palette, g_basename (filename)) == 0)
    default_palette_entries = entries;
}

static void
palette_entry_free (PaletteEntry *entry)
{
  if (entry->name)
    g_free (entry->name);

  g_free (entry);
}

static void
palette_entries_delete (gchar *filename)
{
  if (filename)
    unlink (filename);
}

static void
palette_entries_save (PaletteEntries *palette,
		      gchar          *filename)
{
  PaletteEntry *entry;
  GSList *list;
  FILE *fp;

  if (! filename)
    return;

  /*  Open the requested file  */
  if (! (fp = fopen (filename, "w")))
    {
      g_message (_("can't save palette \"%s\"\n"), filename);
      return;
    }

  fprintf (fp, "GIMP Palette\n");
  fprintf (fp, "# %s -- GIMP Palette file\n", palette->name);

  for (list = palette->colors; list; list = g_slist_next (list))
    {
      entry = (PaletteEntry *) list->data;

      fprintf (fp, "%d %d %d\t%s\n",
	       entry->color[0], entry->color[1], entry->color[2], entry->name);
    }

  /*  Clean up  */
  fclose (fp);
}

static void
palette_save_palettes (void)
{
  PaletteEntries *entries;
  GSList *list;

  for (list = palette_entries_list; list; list = g_slist_next (list))
    {
      entries = (PaletteEntries *) list->data;

      /*  If the palette has been changed, save it, if possible  */
      if (entries->changed)
	/*  save the palette  */
	palette_entries_save (entries, entries->filename);
    }
}

static void 
palette_entries_update_small_preview (PaletteEntries *entries,
				      GdkGC          *gc)
{
  guchar rgb_buf[SM_PREVIEW_WIDTH * SM_PREVIEW_HEIGHT * 3];
  GSList *tmp_link;
  gint index;
  PaletteEntry *entry;

  memset (rgb_buf, 0x0, sizeof (rgb_buf));

  gdk_draw_rgb_image (entries->pixmap,
		      gc,
		      0,
		      0,
		      SM_PREVIEW_WIDTH,
		      SM_PREVIEW_HEIGHT,
		      GDK_RGB_DITHER_NORMAL,
		      rgb_buf,
		      SM_PREVIEW_WIDTH*3);

  index = 0;
  for (tmp_link = entries->colors; tmp_link; tmp_link = g_slist_next (tmp_link))
    {
      guchar cell[3*3*3];
      gint loop;

      entry = tmp_link->data;

      for (loop = 0; loop < 27 ; loop+=3)
	{
	  cell[0+loop] = entry->color[0];
	  cell[1+loop] = entry->color[1];
	  cell[2+loop] = entry->color[2];
	}

      gdk_draw_rgb_image (entries->pixmap,
			  gc,
			  1+(index%((SM_PREVIEW_WIDTH-2)/3))*3,
			  1+(index/((SM_PREVIEW_WIDTH-2)/3))*3,
			  3,
			  3,
			  GDK_RGB_DITHER_NORMAL,
			  cell,
			  3);

      index++;

      if (index >= (((SM_PREVIEW_WIDTH-2)*(SM_PREVIEW_HEIGHT-2))/9))
	break;
    }
}

static void
palette_entries_list_insert (PaletteEntries *entries)
{
  PaletteEntries *p_entries;
  GSList *list;
  gint    pos = 0;

  for (list = palette_entries_list; list; list = g_slist_next (list))
    {
      p_entries = (PaletteEntries *) list->data;
      
      /*  to make sure we get something!  */
      if (p_entries == NULL)
	p_entries = default_palette_entries;

      if (strcmp (p_entries->name, entries->name) > 0)
	break;

      pos++;
    }

  /*  add it to the list  */
  num_palette_entries++;
  palette_entries_list = g_slist_insert (palette_entries_list,
					 (gpointer) entries, pos);
}

/*  general palette clist update functions  **********************************/

void
palette_clist_init (GtkWidget *clist, 
		    GtkWidget *shell,
		    GdkGC     *gc)
{
  PaletteEntries *p_entries = NULL;
  GSList *list;
  gint pos;

  pos = 0;
  for (list = palette_entries_list; list; list = g_slist_next (list))
    {
      p_entries = (PaletteEntries *) list->data;

      /*  to make sure we get something!  */
      if (p_entries == NULL)
	p_entries = default_palette_entries;

      palette_clist_insert (clist, shell, gc, p_entries, pos);

      pos++;
    }
}

void
palette_clist_insert (GtkWidget      *clist, 
		      GtkWidget      *shell,
		      GdkGC          *gc,
		      PaletteEntries *entries,
		      gint            pos)
{
  gchar *string[3];

  string[0] = NULL;
  string[1] = g_strdup_printf ("%d", entries->n_colors);
  string[2] = entries->name;

  gtk_clist_insert (GTK_CLIST (clist), pos, string);

  g_free (string[1]);

  if (entries->pixmap == NULL)
    {
      entries->pixmap = gdk_pixmap_new (shell->window,
					SM_PREVIEW_WIDTH, 
					SM_PREVIEW_HEIGHT, 
					gtk_widget_get_visual (shell)->depth);
      palette_entries_update_small_preview (entries, gc);
    }

  gtk_clist_set_pixmap (GTK_CLIST (clist), pos, 0, entries->pixmap, NULL);
  gtk_clist_set_row_data (GTK_CLIST (clist), pos, (gpointer) entries);
}  

/*  palette dialog clist update functions  ***********************************/

static void
palette_dialog_clist_insert (PaletteDialog  *palette,
			     PaletteEntries *entries)
{
  PaletteEntries *chk_entries;
  GSList *list;
  gint pos;

  pos = 0;
  for (list = palette_entries_list; list; list = g_slist_next (list))
    {
      chk_entries = (PaletteEntries *) list->data;
      
      /*  to make sure we get something!  */
      if (chk_entries == NULL)
	return;

      if (strcmp (entries->name, chk_entries->name) == 0)
	break;

      pos++;
    }

  gtk_clist_freeze (GTK_CLIST (palette->clist));
  palette_clist_insert (palette->clist, palette->shell, palette->gc,
			entries, pos);
  gtk_clist_thaw (GTK_CLIST (palette->clist));
}

static void
palette_dialog_clist_set_text (PaletteDialog  *palette,
			       PaletteEntries *entries)
{
  PaletteEntries *chk_entries = NULL;
  GSList *list;
  gchar *num_buf;
  gint pos;

  pos = 0;
  for (list = palette_entries_list; list; list = g_slist_next (list))
    {
      chk_entries = (PaletteEntries *) list->data;
      
      if (entries == chk_entries)
	break;

      pos++;
    }

  if (chk_entries == NULL)
    return; /* This is actually and error */

  num_buf = g_strdup_printf ("%d", entries->n_colors);;

  gtk_clist_set_text (GTK_CLIST (palette->clist), pos, 1, num_buf);

  g_free (num_buf);
}

static void
palette_dialog_clist_refresh (PaletteDialog *palette)
{
  gtk_clist_freeze (GTK_CLIST (palette->clist));
  gtk_clist_clear (GTK_CLIST (palette->clist));
  palette_clist_init (palette->clist, palette->shell, palette->gc);
  gtk_clist_thaw (GTK_CLIST (palette->clist));

  palette->entries = palette_entries_list->data;
}

static void 
palette_dialog_clist_scroll_to_current (PaletteDialog *palette)
{
  PaletteEntries *p_entries;
  GSList *list;
  gint pos;

  if (palette && palette->entries)
    {
      pos = 0;
      for (list = palette_entries_list; list; list = g_slist_next (list))
	{
	  p_entries = (PaletteEntries *) list->data;
	  
	  if (p_entries == palette->entries)
	    break;

	  pos++;
	}

      gtk_clist_unselect_all (GTK_CLIST (palette->clist));
      gtk_clist_select_row (GTK_CLIST (palette->clist), pos, -1);
      gtk_clist_moveto (GTK_CLIST (palette->clist), pos, 0, 0.0, 0.0); 
    }
}

/*  update functions for all palette dialogs  ********************************/

static void
palette_insert_all (PaletteEntries *entries)
{
  PaletteDialog *palette;

  if ((palette = top_level_palette))
    {
      palette_dialog_clist_insert (palette, entries);

      if (palette->entries == NULL)
	{
	  palette->entries = entries;
	  palette_dialog_redraw (palette);
	}
    }

  if ((palette = top_level_edit_palette))
    {
      palette_dialog_clist_insert (palette, entries);

      palette->entries = entries;
      palette_dialog_redraw (palette);

      palette_dialog_clist_scroll_to_current (palette);
    }

  /*  Update other selectors on screen  */
  palette_select_clist_insert_all (entries);
}

static void
palette_update_all (PaletteEntries *entries)
{
  PaletteDialog *palette;
  GdkGC *gc = NULL;

  if (top_level_palette)
    gc = top_level_palette->gc;
  else if (top_level_edit_palette)
    gc = top_level_edit_palette->gc;

  if (gc)
    palette_entries_update_small_preview (entries, gc);

  if ((palette = top_level_palette))
    {
      if (palette->entries == entries)
	{
	  palette->columns_valid = FALSE;
	  palette_dialog_redraw (palette);
	}
      palette_dialog_clist_set_text (palette, entries);
    }

  if ((palette = top_level_edit_palette))
    {
      if (palette->entries == entries)
	{
	  palette->columns_valid = FALSE;
	  palette_dialog_redraw (palette);
	  palette_dialog_clist_scroll_to_current (palette);
	}
      palette_dialog_clist_set_text (palette, entries);
    }

  /*  Update other selectors on screen  */
  palette_select_set_text_all (entries);
}

static void
palette_draw_all (PaletteEntries *entries,
		  PaletteEntry   *color)
{
  PaletteDialog *palette;
  GdkGC *gc = NULL;

  if (top_level_palette)
    gc = top_level_palette->gc;
  else if (top_level_edit_palette)
    gc = top_level_edit_palette->gc;

  if (gc)
    palette_entries_update_small_preview (entries, gc);

  if ((palette = top_level_palette))
    {
      if (palette->entries == entries)
	{
	  palette_dialog_draw_entries (palette,
				       color->position / palette->columns,
				       color->position % palette->columns);
	}
    }

  if ((palette = top_level_edit_palette))
    {
      if (palette->entries == entries)
	{
	  palette_dialog_draw_entries (palette,
				       color->position / palette->columns,
				       color->position % palette->columns);
	}
    }
}

static void
palette_refresh_all (void)
{
  PaletteDialog *palette;

  default_palette_entries = NULL;

  palettes_free ();
  palettes_init (FALSE);

  if ((palette = top_level_palette))
    {
      palette_dialog_clist_refresh (palette);
      palette->columns_valid = FALSE;
      palette_dialog_redraw (palette);
      palette_dialog_clist_scroll_to_current (palette);
    }

  if ((palette = top_level_edit_palette))
    {
      palette_dialog_clist_refresh (palette);
      palette->columns_valid = FALSE;
      palette_dialog_redraw (palette);
      palette_dialog_clist_scroll_to_current (palette);
    }

  /*  Update other selectors on screen  */
  palette_select_refresh_all ();
}

/*  called from color_picker.h  *********************************************/

void
palette_set_active_color (gint r,
			  gint g,
			  gint b,
			  gint state)
{
  if (top_level_edit_palette && top_level_edit_palette->entries) 
    {
      switch (state) 
 	{ 
 	case COLOR_NEW: 
	  top_level_edit_palette->color =
	    palette_entries_add_entry (top_level_edit_palette->entries,
				       _("Untitled"), r, g, b); 
	  palette_update_all (top_level_edit_palette->entries);
 	  break;

 	case COLOR_UPDATE_NEW: 
 	  top_level_edit_palette->color->color[0] = r;
 	  top_level_edit_palette->color->color[1] = g;
 	  top_level_edit_palette->color->color[2] = b;
	  palette_draw_all (top_level_edit_palette->entries,
			    top_level_edit_palette->color);
 	  break; 
	  
 	default: 
 	  break; 
 	} 
    } 
  
  if (active_color == FOREGROUND)
    gimp_context_set_foreground (gimp_context_get_user (), r, g, b);
  else if (active_color == BACKGROUND)
    gimp_context_set_background (gimp_context_get_user (), r, g, b);
}

/*  called from palette_select.c  ********************************************/

void 
palette_select_palette_init (void)
{
  if (top_level_edit_palette == NULL)
    {
      top_level_edit_palette = palette_dialog_new (FALSE);
      dialog_register (top_level_edit_palette->shell);
    }
}

void 
palette_create_edit (PaletteEntries *entries)
{
  if (top_level_edit_palette == NULL)
    {
      top_level_edit_palette = palette_dialog_new (FALSE);
      dialog_register (top_level_edit_palette->shell);

      gtk_widget_show (top_level_edit_palette->shell);
      palette_dialog_draw_entries (top_level_edit_palette, -1, -1);
    }
  else
    {
      if (! GTK_WIDGET_VISIBLE (top_level_edit_palette->shell))
	{
	  gtk_widget_show (top_level_edit_palette->shell);
	  palette_dialog_draw_entries (top_level_edit_palette, -1, -1);
	}
      else
	{
	  gdk_window_raise (top_level_edit_palette->shell->window);
	}
    }

  if (entries != NULL)
    {
      top_level_edit_palette->entries = entries;
      palette_dialog_clist_scroll_to_current (top_level_edit_palette);
    }
}

static void
palette_select_callback (gint                r,
			 gint                g,
			 gint                b,
			 ColorNotebookState  state,
			 void               *data)
{
  PaletteDialog *palette;
  guchar *color;
  
  palette = data;

  if (palette && palette->entries)
    {
      switch (state)
	{
	case COLOR_NOTEBOOK_UPDATE:
	  break;

	case COLOR_NOTEBOOK_OK:
	  if (palette->color)
	    {
	      color = palette->color->color;

	      color[0] = r;
	      color[1] = g;
	      color[2] = b;

	      /*  Update either foreground or background colors  */
	      if (active_color == FOREGROUND)
		gimp_context_set_foreground (gimp_context_get_user (), r, g, b);
	      else if (active_color == BACKGROUND)
		gimp_context_set_background (gimp_context_get_user (), r, g, b);

	      palette_draw_all (palette->entries, palette->color);
	    }

	  /* Fallthrough */
	case COLOR_NOTEBOOK_CANCEL:
	  if (palette->color_notebook_active)
	    {
	      color_notebook_hide (palette->color_notebook);
	      palette->color_notebook_active = FALSE;
	    }
	}
    }
}

/*  the palette dialog popup menu & callbacks  *******************************/

static void
palette_dialog_new_entry_callback (GtkWidget *widget,
				   gpointer   data)
{
  PaletteDialog *palette;

  palette = data;

  if (palette && palette->entries)
    {
      guchar col[3];

      if (active_color == FOREGROUND)
	gimp_context_get_foreground (gimp_context_get_user (),
				     &col[0], &col[1], &col[2]);
      else if (active_color == BACKGROUND)
	gimp_context_get_background (gimp_context_get_user (),
				     &col[0], &col[1], &col[2]);

      palette->color = palette_entries_add_entry (palette->entries,
						  _("Untitled"),
						  col[0], col[1], col[2]);

      palette_update_all (palette->entries);
    }
}

static void
palette_dialog_edit_entry_callback (GtkWidget *widget,
				    gpointer   data)
{
  PaletteDialog *palette;
  guchar *color;

  palette = data;
  if (palette && palette->entries && palette->color)
    {
      color = palette->color->color;

      if (!palette->color_notebook)
	{
	  palette->color_notebook =
	    color_notebook_new (color[0], color[1], color[2],
				palette_select_callback, palette,
				FALSE);
	  palette->color_notebook_active = TRUE;
	}
      else
	{
	  if (!palette->color_notebook_active)
	    {
	      color_notebook_show (palette->color_notebook);
	      palette->color_notebook_active = TRUE;
	    }

	  color_notebook_set_color (palette->color_notebook,
				    color[0], color[1], color[2], 1);
	}
    }
}

static void
palette_dialog_delete_entry_callback (GtkWidget *widget,
				      gpointer   data)
{
  PaletteEntry *entry;
  PaletteDialog *palette;
  GSList *tmp_link;
  gint pos = 0;

  palette = data;

  if (palette && palette->entries && palette->color)
    {
      entry = palette->color;
      palette->entries->colors = g_slist_remove (palette->entries->colors, entry);
      palette->entries->n_colors--;
      palette->entries->changed = TRUE;

      pos = entry->position;
      palette_entry_free (entry);

      tmp_link = g_slist_nth (palette->entries->colors, pos);

      if (tmp_link)
	{
	  palette->color = tmp_link->data;

	  while (tmp_link)
	    {
	      entry = tmp_link->data;
	      tmp_link = tmp_link->next;
	      entry->position = pos++;
	    }
	}
      else
	{
	  tmp_link = g_slist_nth (palette->entries->colors, pos - 1);
	  if (tmp_link)
	    palette->color = tmp_link->data;
	}

      if (palette->entries->n_colors == 0)
	palette->color =
	  palette_entries_add_entry (palette->entries,
				     _("Black"), 0, 0, 0);

      palette_update_all (palette->entries);
    }
}

static void
palette_dialog_create_popup_menu (PaletteDialog *palette)
{
  GtkWidget *menu;
  GtkWidget *menu_item;

  palette->popup_menu = menu = gtk_menu_new ();

  menu_item = gtk_menu_item_new_with_label (_("New"));
  gtk_menu_append (GTK_MENU (menu), menu_item);
  gtk_signal_connect (GTK_OBJECT (menu_item), "activate", 
		      GTK_SIGNAL_FUNC (palette_dialog_new_entry_callback),
		      (gpointer) palette);
  gtk_widget_show (menu_item);

  menu_item = gtk_menu_item_new_with_label (_("Edit"));
  gtk_menu_append (GTK_MENU (menu), menu_item);

  gtk_signal_connect (GTK_OBJECT (menu_item), "activate", 
		      GTK_SIGNAL_FUNC (palette_dialog_edit_entry_callback),
		      (gpointer) palette);
  gtk_widget_show (menu_item);

  palette->edit_menu_item = menu_item;

  menu_item = gtk_menu_item_new_with_label (_("Delete"));
  gtk_signal_connect (GTK_OBJECT (menu_item), "activate", 
		      GTK_SIGNAL_FUNC (palette_dialog_delete_entry_callback),
		      (gpointer) palette);
  gtk_menu_append (GTK_MENU (menu), menu_item);
  gtk_widget_show (menu_item);

  palette->delete_menu_item = menu_item;
}

/*  the color area event callbacks  ******************************************/

static gint
palette_dialog_eventbox_button_press (GtkWidget      *widget,
				      GdkEventButton *bevent,
				      PaletteDialog  *palette)
{
  if (gtk_get_event_widget ((GdkEvent *) bevent) == palette->color_area)
    return FALSE;

  if (bevent->button == 3)
    {
      if (GTK_WIDGET_SENSITIVE (palette->edit_menu_item))
	{
	  gtk_widget_set_sensitive (palette->edit_menu_item, FALSE);
	  gtk_widget_set_sensitive (palette->delete_menu_item, FALSE);
	}

      gtk_menu_popup (GTK_MENU (palette->popup_menu), NULL, NULL, 
		      NULL, NULL, 3,
		      bevent->time);
    }

  return TRUE;
}

static gint
palette_dialog_color_area_events (GtkWidget     *widget,
				  GdkEvent      *event,
				  PaletteDialog *palette)
{
  GdkEventButton *bevent;
  GSList *tmp_link;
  gint r, g, b;
  gint entry_width;
  gint entry_height;
  gint row, col;
  gint pos;

  switch (event->type)
    {
    case GDK_EXPOSE:
      palette_dialog_redraw (palette);
      break;

    case GDK_BUTTON_PRESS:
      bevent = (GdkEventButton *) event;
      entry_width  = (ENTRY_WIDTH  * palette->xzoom_factor) + SPACING;
      entry_height = (ENTRY_HEIGHT * palette->zoom_factor) +  SPACING;
      col = (bevent->x - 1) / entry_width;
      row = (bevent->y - 1) / entry_height;
      pos = row * palette->columns + col;
      
      tmp_link = g_slist_nth (palette->entries->colors, pos);

      if (tmp_link)
	palette->dnd_color = tmp_link->data;
      else
	palette->dnd_color = NULL;

      if ((bevent->button == 1 || bevent->button == 3) && palette->entries)
	{
	  if (tmp_link)
	    {
	      if (palette->color)
		{
		  palette->freeze_update = TRUE;
 		  palette_dialog_draw_entries (palette, -1, -1);
		  palette->freeze_update = FALSE;
		}
	      palette->color = tmp_link->data;

	      /*  Update either foreground or background colors  */
	      r = palette->color->color[0];
	      g = palette->color->color[1];
	      b = palette->color->color[2];
	      if (active_color == FOREGROUND)
		{
		  if (bevent->state & GDK_CONTROL_MASK)
		    gimp_context_set_background (gimp_context_get_user (),
						 r, g, b);
		  else
		    gimp_context_set_foreground (gimp_context_get_user (),
						 r, g, b);
		}
	      else if (active_color == BACKGROUND)
		{
		  if (bevent->state & GDK_CONTROL_MASK)
		    gimp_context_set_foreground (gimp_context_get_user (),
						 r, g, b);
		  else
		    gimp_context_set_background (gimp_context_get_user (),
						 r, g, b);
		}

	      palette_dialog_draw_entries (palette, row, col);
	      /*  Update the active color name  */
	      gtk_entry_set_text (GTK_ENTRY (palette->color_name),
				  palette->color->name);
	      gtk_widget_set_sensitive (palette->color_name, TRUE);
	      /* palette_update_current_entry (palette); */

	      if (bevent->button == 3)
		{
		  if (! GTK_WIDGET_SENSITIVE (palette->edit_menu_item))
		    {
		      gtk_widget_set_sensitive (palette->edit_menu_item, TRUE);
		      gtk_widget_set_sensitive (palette->delete_menu_item, TRUE);
		    }

		  gtk_menu_popup (GTK_MENU (palette->popup_menu), NULL, NULL, 
				  NULL, NULL, 3,
				  bevent->time);
		}
	    }
	  else
	    {
	      if (bevent->button == 3)
		{
		  if (GTK_WIDGET_SENSITIVE (palette->edit_menu_item))
		    {
		      gtk_widget_set_sensitive (palette->edit_menu_item, FALSE);
		      gtk_widget_set_sensitive (palette->delete_menu_item, FALSE);
		    }

		  gtk_menu_popup (GTK_MENU (palette->popup_menu), NULL, NULL, 
				  NULL, NULL, 3,
				  bevent->time);
		}
	    }
	}
      break;

    default:
      break;
    }

  return FALSE;
}

/*  functions for drawing & updating the palette dialog color area  **********/

static int
palette_dialog_draw_color_row (guchar        **colors,
			       gint            ncolors,
			       gint            y,
			       gint            column_highlight,
			       guchar         *buffer,
			       PaletteDialog  *palette)
{
  guchar *p;
  guchar bcolor;
  gint width, height;
  gint entry_width;
  gint entry_height;
  gint vsize;
  gint vspacing;
  gint i, j;
  GtkWidget *preview;

  if (! palette)
    return -1;

  preview = palette->color_area;

  bcolor = 0;

  width = preview->requisition.width;
  height = preview->requisition.height;
  entry_width = (ENTRY_WIDTH * palette->xzoom_factor);
  entry_height = (ENTRY_HEIGHT * palette->zoom_factor);

  if ((y >= 0) && ((y + SPACING) < height))
    vspacing = SPACING;
  else if (y < 0)
    vspacing = SPACING + y;
  else
    vspacing = height - y;

  if (vspacing > 0)
    {
      if (y < 0)
	y += SPACING - vspacing;

      for (i = SPACING - vspacing; i < SPACING; i++, y++)
	{
	  p = buffer;
	  for (j = 0; j < width; j++)
	    {
	      *p++ = bcolor;
	      *p++ = bcolor;
	      *p++ = bcolor;
	    }
	  
	  if (column_highlight >= 0)
	    {
	      guchar *ph = &buffer[3*column_highlight*(entry_width+SPACING)];
	      for (j = 0 ; j <= entry_width + SPACING; j++)
		{
		  *ph++ = ~bcolor;
		  *ph++ = ~bcolor;
		  *ph++ = ~bcolor;
		}
 	      gtk_preview_draw_row (GTK_PREVIEW (preview), buffer, 0,
				    y + entry_height + 1, width); 
	    }

	  gtk_preview_draw_row (GTK_PREVIEW (preview), buffer, 0, y, width);
	}

      if (y > SPACING)
	y += SPACING - vspacing;
    }
  else
    y += SPACING;

  vsize = (y >= 0) ? (entry_height) : (entry_height + y);

  if ((y >= 0) && ((y + entry_height) < height))
    vsize = entry_height;
  else if (y < 0)
    vsize = entry_height + y;
  else
    vsize = height - y;

  if (vsize > 0)
    {
      p = buffer;
      for (i = 0; i < ncolors; i++)
	{
	  for (j = 0; j < SPACING; j++)
	    {
	      *p++ = bcolor;
	      *p++ = bcolor;
	      *p++ = bcolor;
	    }

	  for (j = 0; j < entry_width; j++)
	    {
	      *p++ = colors[i][0];
	      *p++ = colors[i][1];
	      *p++ = colors[i][2];
	    }
	}

      for (i = 0; i < (palette->columns - ncolors); i++)
	{
	  for (j = 0; j < (SPACING + entry_width); j++)
	    {
	      *p++ = 0;
	      *p++ = 0;
	      *p++ = 0;
	    }
	}

      for (j = 0; j < SPACING; j++)
	{
	  if (ncolors == column_highlight)
	    {
	      *p++ = ~bcolor;
	      *p++ = ~bcolor;
	      *p++ = ~bcolor;
	    }
	  else
	    {
	      *p++ = bcolor;
	      *p++ = bcolor;
	      *p++ = bcolor;
	    }
	}

      if (y < 0)
	y += entry_height - vsize;
      for (i = 0; i < vsize; i++, y++)
	{
	  if (column_highlight >= 0)
	    {
	      guchar *ph = &buffer[3*column_highlight*(entry_width+SPACING)];
	      *ph++ = ~bcolor;
	      *ph++ = ~bcolor;
	      *ph++ = ~bcolor;
	      ph += 3*(entry_width);
	      *ph++ = ~bcolor;
	      *ph++ = ~bcolor;
	      *ph++ = ~bcolor;
	    }
	  gtk_preview_draw_row (GTK_PREVIEW (preview), buffer, 0, y, width);
	}
      if (y > entry_height)
	y += entry_height - vsize;
    }
  else
    y += entry_height;

  return y;
}

static void
palette_dialog_draw_entries (PaletteDialog *palette,
			     gint           row_start,
			     gint           column_highlight)
{
  PaletteEntry *entry;
  guchar *buffer;
  guchar **colors;
  GSList *tmp_link;
  gint width, height;
  gint entry_width;
  gint entry_height;
  gint index, y;

  if (palette && palette->entries)
    {
      width  = palette->color_area->requisition.width;
      height = palette->color_area->requisition.height;

      entry_width  = (ENTRY_WIDTH  * palette->xzoom_factor);
      entry_height = (ENTRY_HEIGHT * palette->zoom_factor);

      colors = g_malloc (sizeof (guchar *) * palette->columns * 3);
      buffer = g_malloc (width * 3);

      if (row_start < 0)
	{
	  y = 0;
	  tmp_link = palette->entries->colors;
	  column_highlight = -1;
	}
      else
	{
	  y = (entry_height + SPACING) * row_start;
	  tmp_link = g_slist_nth (palette->entries->colors,
				  row_start * palette->columns);
	}
      index = 0;

      while (tmp_link)
	{
	  entry = tmp_link->data;
	  tmp_link = tmp_link->next;

	  colors[index] = entry->color;
	  index++;

	  if (index == palette->columns)
	    {
	      index = 0;
	      y = palette_dialog_draw_color_row (colors, palette->columns, y,
						 column_highlight, buffer,
						 palette);
	      if (y >= height || row_start >= 0)
		{
		  /* This row only */
		  gtk_widget_draw (palette->color_area, NULL);
		  g_free (buffer);
		  g_free (colors);
		  return;
		}
	    }
	}

      while (y < height)
	{
	  y = palette_dialog_draw_color_row (colors, index, y, column_highlight,
					     buffer, palette);
	  index = 0;
          if (row_start >= 0)
	    break;
	}

      g_free (buffer);
      g_free (colors);

      if (palette->freeze_update == FALSE)
	gtk_widget_draw (palette->color_area, NULL);
    }
}

static void
palette_dialog_scroll_top_left (PaletteDialog *palette)
{
  GtkAdjustment *hadj;
  GtkAdjustment *vadj;

  /*  scroll viewport to top left  */
  if (palette && palette->scrolled_window)
    {
      hadj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (palette->scrolled_window));
      vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (palette->scrolled_window));

      if (hadj)
	gtk_adjustment_set_value (hadj, 0.0);
      if (vadj)
	gtk_adjustment_set_value (vadj, 0.0);
    }
}

static void
palette_dialog_redraw (PaletteDialog *palette)
{
  GtkWidget *parent;
  gint vsize;
  gint nrows;
  gint n_entries;
  gint new_pre_width;
  guint width;
  gfloat new_xzoom;

  width = palette->color_area->parent->parent->parent->allocation.width;
  new_xzoom = (gfloat) ((gfloat) width /
			(gfloat) COLUMNS - SPACING) / (gfloat) ENTRY_WIDTH;

  if ((palette->columns_valid) && palette->last_width == width)
    return;

  palette->last_width = width;
  palette->columns_valid = TRUE;
  palette->xzoom_factor = new_xzoom;

  n_entries = palette->entries->n_colors;
  nrows = n_entries / palette->columns;
  if (n_entries % palette->columns)
    nrows += 1;

  vsize = nrows * (SPACING + (gint) (ENTRY_HEIGHT * palette->zoom_factor)) + SPACING;

  parent = palette->color_area->parent->parent;
  gtk_widget_ref (palette->color_area->parent);
  gtk_container_remove (GTK_CONTAINER (parent), palette->color_area->parent);

  new_pre_width = (gint) (ENTRY_WIDTH * palette->xzoom_factor);
  new_pre_width = (new_pre_width + SPACING) * palette->columns + SPACING;

  gtk_preview_size (GTK_PREVIEW (palette->color_area),
		    new_pre_width, /*PREVIEW_WIDTH,*/
		    vsize);

  gtk_container_add (GTK_CONTAINER (parent), palette->color_area->parent);
  gtk_widget_unref (palette->color_area->parent);

  palette_dialog_draw_entries (palette, -1, -1);
}

/*  the palette dialog clist "select_row" callback  **************************/

static void
palette_dialog_list_item_update (GtkWidget      *widget, 
				 gint            row,
				 gint            column,
				 GdkEventButton *event,
				 gpointer        data)
{
  PaletteDialog *palette;
  PaletteEntries *p_entries;

  palette = (PaletteDialog *) data;

  if (palette->color_notebook_active)
    {
      color_notebook_hide (palette->color_notebook);
      palette->color_notebook_active = FALSE;
    }

  if (palette->color_notebook)
    color_notebook_free (palette->color_notebook);
  palette->color_notebook = NULL;

  p_entries = 
    (PaletteEntries *) gtk_clist_get_row_data (GTK_CLIST (palette->clist), row);

  palette->entries = p_entries;
  palette->columns_valid = FALSE;

  palette_dialog_redraw (palette);
  palette_dialog_scroll_top_left (palette);

  /*  Stop errors in case no colors are selected  */ 
  gtk_signal_handler_block (GTK_OBJECT (palette->color_name),
			    palette->entry_sig_id);
  gtk_entry_set_text (GTK_ENTRY (palette->color_name), _("Undefined")); 
  gtk_widget_set_sensitive (palette->color_name, FALSE);
  gtk_signal_handler_unblock (GTK_OBJECT (palette->color_name),
			      palette->entry_sig_id);
}

/*  the color name entry callback  *******************************************/

static void
palette_dialog_color_name_entry_changed (GtkWidget *widget,
					 gpointer   data)
{
  PaletteDialog *palette;

  palette = data;

  if (palette->color->name)
    g_free (palette->color->name);
  palette->color->name = 
    g_strdup (gtk_entry_get_text (GTK_ENTRY (palette->color_name)));

  palette->entries->changed = TRUE;
}

/*  palette zoom functions & callbacks  **************************************/

static void
palette_dialog_redraw_zoom (PaletteDialog *palette)
{
  if (palette->zoom_factor > 4.0)
    {
      palette->zoom_factor = 4.0;
    }
  else if (palette->zoom_factor < 0.1)
    {
      palette->zoom_factor = 0.1;
    }
  
  palette->columns = COLUMNS;
  palette->columns_valid = FALSE;
  palette_dialog_redraw (palette); 

  palette_dialog_scroll_top_left (palette);
}

static void
palette_dialog_zoomin_callback (GtkWidget *widget,
				gpointer   data)
{
  PaletteDialog *palette = data;

  palette->zoom_factor += 0.1;
  palette_dialog_redraw_zoom (palette);
}

static void
palette_dialog_zoomout_callback (GtkWidget *widget,
				 gpointer   data)
{
  PaletteDialog *palette = data;

  palette->zoom_factor -= 0.1;
  palette_dialog_redraw_zoom (palette);
}

/*  the palette edit ops callbacks  ******************************************/

static void
palette_dialog_add_entries_callback (GtkWidget *widget,
				     gpointer   data,
				     gpointer   call_data)
{
  PaletteEntries *entries;

  entries = palette_entries_new ((gchar *) call_data);

  palette_insert_all (entries);
}

static void
palette_dialog_new_callback (GtkWidget *widget,
			     gpointer   data)
{
  GtkWidget *qbox;

  qbox = gimp_query_string_box (_("New Palette"),
				gimp_standard_help_func,
				"dialogs/palette_editor/new_palette.html",
				_("Enter a name for new palette"),
				NULL,
				NULL, NULL,
				palette_dialog_add_entries_callback, data);
  gtk_widget_show (qbox);
}

static void
palette_dialog_do_delete_callback (GtkWidget *widget,
				   gpointer   data)
{
  PaletteDialog  *palette;
  PaletteEntries *entries;

  palette = (PaletteDialog *) data;

  if (palette && palette->entries)
    {
      entries = palette->entries;
      if (entries && entries->filename)
	palette_entries_delete (entries->filename);

      palette_entries_list = g_slist_remove (palette_entries_list, entries);

      palette_refresh_all ();
    }

  gtk_widget_destroy (gtk_widget_get_toplevel (widget));
  gtk_widget_set_sensitive (palette->shell, TRUE);
}

static void
palette_dialog_cancel_delete_callback (GtkWidget *widget,
				       gpointer   data)
{
  PaletteDialog *palette;

  palette = (PaletteDialog *) data;

  gtk_widget_destroy (gtk_widget_get_toplevel (widget));
  gtk_widget_set_sensitive (palette->shell, TRUE);
}

static void
palette_dialog_delete_callback (GtkWidget *widget,
				gpointer   data)
{
  PaletteDialog *palette;

  GtkWidget *dialog;
  GtkWidget *vbox;
  GtkWidget *label;
  gchar     *str;

  palette = data;

  if (!palette || !palette->entries)
    return;

  dialog = gimp_dialog_new (_("Delete Palette"), "delete_palette",
                            gimp_standard_help_func,
                            "dialogs/palette_editor/delete_palette.html",
                            GTK_WIN_POS_MOUSE,
                            FALSE, FALSE, FALSE,

                            _("Delete"), palette_dialog_do_delete_callback,
                            palette, NULL, NULL, FALSE, FALSE,
                            _("Cancel"), palette_dialog_cancel_delete_callback,
                            palette, NULL, NULL, TRUE, TRUE,

                            NULL);

  /*  The main vbox  */
  vbox = gtk_vbox_new (FALSE, 2);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox);
  gtk_widget_show (vbox);

  str = g_strdup_printf (_("Are you sure you want to delete\n"
                           "\"%s\" from the list and from disk?"),
                         palette->entries->name);

  label = gtk_label_new (str);
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

  g_free (str);

  gtk_widget_show (dialog);
  gtk_widget_set_sensitive (palette->shell, FALSE);
}

static void
palette_dialog_import_callback (GtkWidget *widget,
				gpointer   data)
{
  if (!import_dialog)
    {
      import_dialog = palette_import_dialog_new ((PaletteDialog *) data);
      gtk_widget_show (import_dialog->dialog);
    }
  else
    {
      gdk_window_raise (import_dialog->dialog->window);
    }
}

static void
palette_dialog_merge_entries_callback (GtkWidget *widget,
				       gpointer   data,
				       gpointer   call_data)
{
  PaletteDialog *palette;
  PaletteEntries *p_entries;
  PaletteEntries *new_entries;
  GList *sel_list;

  new_entries = palette_entries_new ((gchar *) call_data);

  palette = (PaletteDialog *) data;

  sel_list = GTK_CLIST (palette->clist)->selection;

  while (sel_list)
    {
      gint row;
      GSList *cols;

      row = GPOINTER_TO_INT (sel_list->data);
      p_entries = 
	(PaletteEntries *) gtk_clist_get_row_data (GTK_CLIST (palette->clist), row);

      /* Go through each palette and merge the colors */
      cols = p_entries->colors;
      while (cols)
	{
	  PaletteEntry *entry = cols->data;
	  palette_entries_add_entry (new_entries,
				     entry->name,
				     entry->color[0],
				     entry->color[1],
				     entry->color[2]);
	  cols = cols->next;
	}
      sel_list = sel_list->next;
    }

  palette_insert_all (new_entries);
}

static void
palette_dialog_merge_callback (GtkWidget *widget,
			       gpointer   data)
{
  GtkWidget *qbox;

  qbox = gimp_query_string_box (_("Merge Palette"),
				gimp_standard_help_func,
				"dialogs/palette_editor/merge_palette.html",
				_("Enter a name for merged palette"),
				NULL,
				NULL, NULL,
				palette_dialog_merge_entries_callback,
				data);
  gtk_widget_show (qbox);
}

/*  the palette & palette edit action area callbacks  ************************/

static void
palette_dialog_save_callback (GtkWidget *widget,
			      gpointer   data)
{
  palette_save_palettes ();
}

static void
palette_dialog_refresh_callback (GtkWidget *widget,
				 gpointer   data)
{
  palette_refresh_all ();
}

static void
palette_dialog_edit_callback (GtkWidget *widget,
			      gpointer   data)
{
  PaletteEntries *p_entries = NULL;
  PaletteDialog *palette;
  GList *sel_list;

  palette = (PaletteDialog *) data;
  sel_list = GTK_CLIST (palette->clist)->selection;

  while (sel_list)
    {
      gint row;

      row = GPOINTER_TO_INT (sel_list->data);

      p_entries = 
	(PaletteEntries *) gtk_clist_get_row_data (GTK_CLIST (palette->clist),
						   row);

      palette_create_edit (p_entries);

      /* One only */
      return;
    }
}

static void
palette_dialog_close_callback (GtkWidget *widget,
			       gpointer   data)
{
  PaletteDialog *palette;

  palette = data;

  if (palette)
    {
      if (palette->color_notebook_active)
	{
	  color_notebook_hide (palette->color_notebook);
	  palette->color_notebook_active = FALSE;
	}

      if (palette == top_level_edit_palette && import_dialog)
	{
	  gtk_widget_destroy (import_dialog->dialog);
	  g_free (import_dialog);
	  import_dialog = NULL;
	}
      
      if (GTK_WIDGET_VISIBLE (palette->shell))
	gtk_widget_hide (palette->shell);
    }
}

/*  the palette dialog color dnd callbacks  **********************************/

static void
palette_dialog_drag_color (GtkWidget *widget,
			   guchar    *r,
			   guchar    *g,
			   guchar    *b,
			   gpointer   data)
{
  PaletteDialog *palette;

  palette = (PaletteDialog *) data;

  if (palette && palette->entries && palette->dnd_color)
    {
      *r = (guchar) palette->dnd_color->color[0];
      *g = (guchar) palette->dnd_color->color[1];
      *b = (guchar) palette->dnd_color->color[2];
    }
  else
    {
      *r = *g = *b = 0;
    }
}

static void
palette_dialog_drop_color (GtkWidget *widget,
			   guchar      r,
			   guchar      g,
			   guchar      b,
			   gpointer    data)
{
  PaletteDialog *palette;

  palette = (PaletteDialog *) data;

  if (palette && palette->entries)
    {
      palette->color =
	palette_entries_add_entry (palette->entries, _("Untitled"), r, g, b);

      palette_update_all (palette->entries);
    }
}

/*  the palette & palette edit dialog constructor  ***************************/

PaletteDialog *
palette_dialog_new (gint vert)
{
  PaletteDialog *palette;
  GtkWidget *hbox;
  GtkWidget *hbox2;
  GtkWidget *vbox;
  GtkWidget *scrolledwindow;
  GtkWidget *palette_region;
  GtkWidget *entry;
  GtkWidget *eventbox;
  GtkWidget *alignment;
  GtkWidget *frame;
  GtkWidget *button;
  GtkWidget *pixmapwid;
  GdkPixmap *pixmap;
  GdkBitmap *mask;
  GtkStyle  *style;
  gchar     *titles[3];

  palette = g_new (PaletteDialog, 1);
  palette->entries               = default_palette_entries;
  palette->color                 = NULL;
  palette->dnd_color             = NULL;
  palette->color_notebook        = NULL;
  palette->color_notebook_active = FALSE;
  palette->zoom_factor           = 1.0;
  palette->xzoom_factor          = 1.0;
  palette->last_width            = 0;
  palette->columns               = COLUMNS;
  palette->columns_valid         = TRUE;
  palette->freeze_update         = FALSE;

  if (!vert)
    {
      palette->shell =
	gimp_dialog_new (_("Color Palette Edit"), "color_palette_edit",
			 gimp_standard_help_func,
			 "dialogs/palette_editor/palette_editor.html",
			 GTK_WIN_POS_NONE,
			 FALSE, TRUE, FALSE,

			 _("Save"), palette_dialog_save_callback,
			 palette, NULL, NULL, FALSE, FALSE,
			 _("Refresh"), palette_dialog_refresh_callback,
			 palette, NULL, NULL, FALSE, FALSE,
			 _("Close"), palette_dialog_close_callback,
			 palette, NULL, NULL, TRUE, TRUE,

			 NULL);
    }
  else
    {
      palette->shell =
	gimp_dialog_new (_("Color Palette"), "color_palette",
			 gimp_standard_help_func,
			 "dialogs/palette_selection.html",
			 GTK_WIN_POS_NONE,
			 FALSE, TRUE, FALSE,

			 _("Edit"), palette_dialog_edit_callback,
			 palette, NULL, NULL, FALSE, FALSE,
			 _("Close"), palette_dialog_close_callback,
			 palette, NULL, NULL, TRUE, TRUE,

			 NULL);
    }

  /*  The main container widget  */
  if (vert)
    {
      hbox = gtk_notebook_new ();
      gtk_container_set_border_width (GTK_CONTAINER (hbox), 1);
    }
  else
    {
      hbox = gtk_hbox_new (FALSE, 4);
      gtk_container_set_border_width (GTK_CONTAINER (hbox), 4);
    }
  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (palette->shell)->vbox), hbox);
  gtk_widget_show (hbox);

  vbox = gtk_vbox_new (FALSE, 2);
  gtk_widget_show (vbox);

  palette->scrolled_window =
    scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
				  GTK_POLICY_AUTOMATIC,
				  GTK_POLICY_ALWAYS);
  gtk_box_pack_start (GTK_BOX (vbox), scrolledwindow, TRUE, TRUE, 0);
  gtk_widget_show (scrolledwindow);

  eventbox = gtk_event_box_new ();
  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolledwindow),
					 eventbox);
  gtk_signal_connect (GTK_OBJECT (eventbox), "button_press_event",
		      GTK_SIGNAL_FUNC (palette_dialog_eventbox_button_press),
		      palette);
  gtk_widget_show (eventbox);

  alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); 
  gtk_container_add (GTK_CONTAINER (eventbox), alignment);
  gtk_widget_show (alignment);

  palette->color_area = palette_region = gtk_preview_new (GTK_PREVIEW_COLOR);
  gtk_preview_set_dither (GTK_PREVIEW (palette->color_area),
			  GDK_RGB_DITHER_MAX);
  gtk_preview_size (GTK_PREVIEW (palette_region), PREVIEW_WIDTH, PREVIEW_HEIGHT);
  
  gtk_widget_set_events (palette_region, PALETTE_EVENT_MASK);
  gtk_signal_connect (GTK_OBJECT (palette->color_area), "event",
		      GTK_SIGNAL_FUNC (palette_dialog_color_area_events),
		      palette);

  gtk_container_add (GTK_CONTAINER (alignment), palette_region);
  gtk_widget_show (palette_region);

  /*  dnd stuff  */
  gtk_drag_source_set (palette_region,
                       GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
                       color_palette_target_table, n_color_palette_targets,
                       GDK_ACTION_COPY | GDK_ACTION_MOVE);
  gimp_dnd_color_source_set (palette_region, palette_dialog_drag_color, palette);

  gtk_drag_dest_set (alignment,
                     GTK_DEST_DEFAULT_HIGHLIGHT |
                     GTK_DEST_DEFAULT_MOTION |
                     GTK_DEST_DEFAULT_DROP,
                     color_palette_target_table, n_color_palette_targets,
                     GDK_ACTION_COPY);
  gimp_dnd_color_dest_set (alignment, palette_dialog_drop_color, palette);

  /*  The color name entry  */
  hbox2 = gtk_hbox_new (FALSE, 2);
  gtk_box_pack_start (GTK_BOX (vbox), hbox2, FALSE, FALSE, 0);
  gtk_widget_show (hbox2);

  entry = palette->color_name = gtk_entry_new ();
  gtk_widget_show (entry);
  gtk_box_pack_start (GTK_BOX (hbox2), entry, TRUE, TRUE, 0);
  gtk_entry_set_text (GTK_ENTRY (entry), _("Undefined"));
  palette->entry_sig_id =
    gtk_signal_connect (GTK_OBJECT (entry), "changed",
			GTK_SIGNAL_FUNC (palette_dialog_color_name_entry_changed),
			palette);

  /*  + and - buttons  */
  if (! GTK_WIDGET_REALIZED (palette->shell))
    gtk_widget_realize (palette->shell);
  style = gtk_widget_get_style (palette->shell);

  button = gtk_button_new ();
  gtk_box_pack_start (GTK_BOX (hbox2), button, FALSE, FALSE, 0);
  pixmap = gdk_pixmap_create_from_xpm_d (palette->shell->window, &mask,
					 &style->bg[GTK_STATE_NORMAL], 
					 zoom_in_xpm);
  pixmapwid = gtk_pixmap_new (pixmap, mask);
  gdk_pixmap_unref (pixmap);
  gtk_container_add (GTK_CONTAINER (button), pixmapwid);
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
		      GTK_SIGNAL_FUNC (palette_dialog_zoomin_callback),
		      (gpointer) palette);
  gtk_widget_show (pixmapwid);
  gtk_widget_show (button);

  button = gtk_button_new ();
  gtk_box_pack_start (GTK_BOX (hbox2), button, FALSE, FALSE, 0);
  pixmap = gdk_pixmap_create_from_xpm_d (palette->shell->window, &mask,
					 &style->bg[GTK_STATE_NORMAL], 
					 zoom_out_xpm);
  pixmapwid = gtk_pixmap_new (pixmap, mask);
  gdk_pixmap_unref (pixmap);
  gtk_container_add (GTK_CONTAINER (button), pixmapwid);
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
		      GTK_SIGNAL_FUNC (palette_dialog_zoomout_callback),
		      (gpointer) palette);
  gtk_widget_show (pixmapwid);
  gtk_widget_show (button);
  
  /*  clist preview of palettes  */
  scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
				  GTK_POLICY_AUTOMATIC,
				  GTK_POLICY_ALWAYS);

  if (vert)
    {
      gtk_notebook_append_page (GTK_NOTEBOOK (hbox), vbox,
				gtk_label_new (_("Palette")));
      gtk_notebook_append_page (GTK_NOTEBOOK (hbox), scrolledwindow,
				gtk_label_new (_("Select")));
    }
  else
    {
      gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
      gtk_box_pack_start (GTK_BOX (hbox), scrolledwindow, TRUE, TRUE, 0);
    }

  gtk_widget_show (scrolledwindow);

  titles[0] = _("Palette");
  titles[1] = _("Ncols");
  titles[2] = _("Name");
  palette->clist = gtk_clist_new_with_titles (3, titles);
  gtk_widget_set_usize (palette->clist, 203, 203);
  gtk_clist_set_row_height (GTK_CLIST (palette->clist), SM_PREVIEW_HEIGHT + 2);
  gtk_clist_set_column_width (GTK_CLIST (palette->clist), 0, SM_PREVIEW_WIDTH+2);
  gtk_clist_column_titles_passive (GTK_CLIST (palette->clist));
  gtk_container_add (GTK_CONTAINER (scrolledwindow), palette->clist);

  if (!vert)
    gtk_clist_set_selection_mode (GTK_CLIST (palette->clist),
				  GTK_SELECTION_EXTENDED);

  gtk_signal_connect (GTK_OBJECT (palette->clist), "select_row",
		      GTK_SIGNAL_FUNC (palette_dialog_list_item_update),
		      (gpointer) palette);
  gtk_widget_show (palette->clist);
  
  if (!vert) 
    {
      frame = gtk_frame_new (_("Palette Ops"));
      gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
      gtk_widget_show (frame); 

      vbox = gtk_vbox_new (FALSE, 2);
      gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
      gtk_container_add (GTK_CONTAINER (frame), vbox);
      gtk_widget_show (vbox);
      
      button = gtk_button_new_with_label (_("New"));
      GTK_WIDGET_UNSET_FLAGS (button, GTK_RECEIVES_DEFAULT);
      gtk_misc_set_padding (GTK_MISC (GTK_BIN (button)->child), 2, 0);
      gtk_signal_connect (GTK_OBJECT (button), "clicked",
			  GTK_SIGNAL_FUNC (palette_dialog_new_callback),
			  (gpointer) palette);
      gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
      gimp_help_set_help_data (button, NULL,
			       "dialogs/palette_editor/new_palette.html");
      gtk_widget_show (button);

      button = gtk_button_new_with_label (_("Delete"));
      GTK_WIDGET_UNSET_FLAGS (button, GTK_RECEIVES_DEFAULT);
      gtk_misc_set_padding (GTK_MISC (GTK_BIN (button)->child), 2, 0);
      gtk_signal_connect (GTK_OBJECT (button), "clicked",
			  GTK_SIGNAL_FUNC (palette_dialog_delete_callback),
			  (gpointer) palette);
      gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
      gimp_help_set_help_data (button, NULL,
			       "dialogs/palette_editor/delete_palette.html");
      gtk_widget_show (button);
      
      button = gtk_button_new_with_label (_("Import"));
      GTK_WIDGET_UNSET_FLAGS (button, GTK_RECEIVES_DEFAULT);
      gtk_misc_set_padding (GTK_MISC (GTK_BIN (button)->child), 2, 0);
      gtk_signal_connect (GTK_OBJECT (button), "clicked",
			  GTK_SIGNAL_FUNC (palette_dialog_import_callback),
			  (gpointer) palette);
      gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
      gimp_help_set_help_data (button, NULL,
			       "dialogs/palette_editor/import_palette.html");
      gtk_widget_show (button);

      button = gtk_button_new_with_label (_("Merge"));
      GTK_WIDGET_UNSET_FLAGS (button, GTK_RECEIVES_DEFAULT);
      gtk_misc_set_padding (GTK_MISC (GTK_BIN (button)->child), 2, 0);
      gtk_signal_connect (GTK_OBJECT (button), "clicked",
			  GTK_SIGNAL_FUNC (palette_dialog_merge_callback),
			  (gpointer) palette);
      gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
      gimp_help_set_help_data (button, NULL,
			       "dialogs/palette_editor/merge_palette.html");
      gtk_widget_show (button);
    }

  palette->gc = gdk_gc_new (palette->shell->window);

  /*  fill the clist  */
  palette_clist_init (palette->clist, palette->shell, palette->gc);
  palette_dialog_clist_scroll_to_current (palette);

  palette_dialog_create_popup_menu (palette);

  return palette;
}

/*****************************************************************************/
/*  palette import dialog functions  *****************************************/

/*  functions to create & update the import dialog's gradient selection  *****/

static void
palette_import_select_grad_callback (GtkWidget *widget,
				     gpointer   data)
{
  /*  Popup grad edit box ....  */
  gradient_dialog_create ();
}

static void
palette_import_fill_grad_preview (GtkWidget  *preview,
				  gradient_t *gradient)
{
  guchar buffer[3*IMPORT_PREVIEW_WIDTH];
  gint loop;
  guchar *p = buffer;
  gdouble  dx, cur_x;
  gdouble  r, g, b, a;

  dx    = 1.0/ (IMPORT_PREVIEW_WIDTH - 1);
  cur_x = 0;

  for (loop = 0 ; loop < IMPORT_PREVIEW_WIDTH; loop++)
    {
      gradient_get_color_at (gradient, cur_x, &r, &g, &b, &a);
      *p++ = r * 255.0;
      *p++ = g * 255.0;
      *p++ = b * 255.0;
      cur_x += dx;
    }

  for (loop = 0 ; loop < IMPORT_PREVIEW_HEIGHT; loop++)
    {
      gtk_preview_draw_row (GTK_PREVIEW (preview), buffer, 0, loop,
			    IMPORT_PREVIEW_WIDTH);
    }

  gtk_widget_draw (preview, NULL);
}

static void
palette_import_gradient_update (GimpContext *context,
				gradient_t  *gradient,
				gpointer     data)
{
  if (import_dialog && import_dialog->import_type == GRAD_IMPORT)
    {
      /* redraw gradient */
      palette_import_fill_grad_preview (import_dialog->preview, gradient);
      gtk_entry_set_text (GTK_ENTRY (import_dialog->entry), gradient->name);
    }
}

/*  functions to create & update the import dialog's image selection  ********/

static void
palette_import_gimlist_cb (gpointer im,
			   gpointer data)
{
  GSList** l;

  l = (GSList**) data;
  *l = g_slist_prepend (*l, im);
}

static void
palette_import_gimlist_indexed_cb (gpointer im,
				   gpointer data)
{
  GimpImage *gimage = GIMP_IMAGE (im);
  GSList** l;

  if (gimage_base_type (gimage) == INDEXED)
    {
      l = (GSList**) data;
      *l = g_slist_prepend (*l, im);
    }
}

static void
palette_import_update_image_preview (GimpImage *gimage)
{
  TempBuf * preview_buf;
  gchar *src, *buf;
  gint x,y,has_alpha;
  gint sel_width, sel_height;
  gint pwidth, pheight;

  import_dialog->gimage = gimage;

  /* Calculate preview size */

  sel_width = gimage->width;
  sel_height = gimage->height;

  if (sel_width > sel_height) {
    pwidth  = MIN(sel_width, IMPORT_PREVIEW_WIDTH);
    pheight = sel_height * pwidth / sel_width;
  } else {
    pheight = MIN(sel_height, IMPORT_PREVIEW_HEIGHT);
    pwidth  = sel_width * pheight / sel_height;
  }
  
  /* Min size is 2 */
  preview_buf = gimp_image_construct_composite_preview (gimage,
							MAX (pwidth, 2),
							MAX (pheight, 2));

  gtk_preview_size (GTK_PREVIEW (import_dialog->preview),
		    preview_buf->width,
		    preview_buf->height);

  buf = g_new (gchar,  IMPORT_PREVIEW_WIDTH * 3);
  src = (gchar *) temp_buf_data (preview_buf);
  has_alpha = (preview_buf->bytes == 2 || preview_buf->bytes == 4);
  for (y = 0; y <preview_buf->height ; y++)
    {
      if (preview_buf->bytes == (1+has_alpha))
	for (x = 0; x < preview_buf->width; x++)
	  {
	    buf[x*3+0] = src[x];
	    buf[x*3+1] = src[x];
	    buf[x*3+2] = src[x];
	  }
      else
	for (x = 0; x < preview_buf->width; x++)
	  {
	    gint stride = 3 + has_alpha;
	    buf[x*3+0] = src[x*stride+0];
	    buf[x*3+1] = src[x*stride+1];
	    buf[x*3+2] = src[x*stride+2];
	  }
      gtk_preview_draw_row (GTK_PREVIEW (import_dialog->preview),
			    (guchar *)buf, 0, y, preview_buf->width);
      src += preview_buf->width * preview_buf->bytes;
    }

  g_free (buf);
  temp_buf_free (preview_buf);

  gtk_widget_hide (import_dialog->preview);
  gtk_widget_draw (import_dialog->preview, NULL); 
  gtk_widget_show (import_dialog->preview);
}

static void
palette_import_image_sel_callback (GtkWidget *widget,
				   gpointer   data)
{
  GimpImage *gimage;
  gchar *lab;

  gimage = GIMP_IMAGE (data);
  palette_import_update_image_preview (gimage);

  lab = g_strdup_printf ("%s-%d",
			 g_basename (gimage_filename (import_dialog->gimage)),
			 pdb_image_to_id (import_dialog->gimage));

  gtk_entry_set_text (GTK_ENTRY (import_dialog->entry), lab);
}

static void
palette_import_image_menu_add (GimpImage *gimage)
{
  GtkWidget *menuitem;
  gchar *lab = g_strdup_printf ("%s-%d",
				g_basename (gimage_filename (gimage)),
				pdb_image_to_id (gimage));
  menuitem = gtk_menu_item_new_with_label (lab);
  gtk_widget_show (menuitem);
  gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
		      GTK_SIGNAL_FUNC (palette_import_image_sel_callback),
		      gimage);
  gtk_menu_append (GTK_MENU (import_dialog->optionmenu1_menu), menuitem);
}

/* Last Param gives us control over what goes in the menu on a delete oper */
static void
palette_import_image_menu_activate (gint        redo,
				    ImportType  type,
				    GimpImage  *del_image)
{
  GSList *list=NULL;
  gint num_images;
  GimpImage *last_img = NULL;
  GimpImage *first_img = NULL;
  gint act_num = -1;
  gint count = 0;
  gchar *lab;

  if (!import_dialog)
    return;

  if (import_dialog->import_type == type && !redo)
    return;

  /* Destroy existing widget if necessary */
  if (import_dialog->image_list)
    {
      if (redo) /* Preserve settings in this case */
        last_img = import_dialog->gimage;
      gtk_widget_hide (import_dialog->image_list);
      gtk_widget_destroy (import_dialog->image_list);
      import_dialog->image_list = NULL;
    }

  import_dialog->import_type= type;

  /* Get list of images */
  if (import_dialog->import_type == INDEXED_IMPORT)
    {
      gimage_foreach (palette_import_gimlist_indexed_cb, &list);
    }
  else
    {
      gimage_foreach (palette_import_gimlist_cb, &list);
    }

  num_images = g_slist_length (list);
      
  if (num_images)
    {
      gint i;
      GtkWidget *optionmenu1;
      GtkWidget *optionmenu1_menu;

      import_dialog->image_list = optionmenu1 = gtk_option_menu_new ();
      gtk_widget_set_usize (optionmenu1, IMPORT_PREVIEW_WIDTH, -1);
      import_dialog->optionmenu1_menu = optionmenu1_menu = gtk_menu_new ();

      for (i = 0; i < num_images; i++, list = g_slist_next (list))
        {
          if (GIMP_IMAGE (list->data) != del_image)
            {
	      if (first_img == NULL)
	        first_img = GIMP_IMAGE (list->data);
	      palette_import_image_menu_add (GIMP_IMAGE (list->data));
	      if (last_img == GIMP_IMAGE (list->data))
	        act_num = count;
	      else
	        count++;
	    }
	}

      gtk_option_menu_set_menu (GTK_OPTION_MENU (optionmenu1),
				optionmenu1_menu);
      gtk_widget_hide (import_dialog->select);
      gtk_box_pack_start (GTK_BOX (import_dialog->select_area),
            		  optionmenu1, FALSE, FALSE, 0);

      if(last_img != NULL && last_img != del_image)
        palette_import_update_image_preview (last_img);
      else if (first_img != NULL)
	palette_import_update_image_preview (first_img);

      gtk_widget_show (optionmenu1);

      /* reset to last one */
      if (redo && act_num >= 0)
        {
           gchar *lab = g_strdup_printf ("%s-%d",
			 g_basename (gimage_filename (import_dialog->gimage)),
			 pdb_image_to_id (import_dialog->gimage));

           gtk_option_menu_set_history (GTK_OPTION_MENU (optionmenu1), act_num);
           gtk_entry_set_text (GTK_ENTRY (import_dialog->entry), lab);
	}
    }
  g_slist_free (list);

  lab = g_strdup_printf ("%s-%d",
			 g_basename (gimage_filename (import_dialog->gimage)),
			 pdb_image_to_id (import_dialog->gimage));

  gtk_entry_set_text (GTK_ENTRY (import_dialog->entry), lab);
}

/*  the import source menu item callbacks  ***********************************/

static void
palette_import_grad_callback (GtkWidget *widget,
			      gpointer   data)
{
  if (import_dialog)
    {
      gradient_t *gradient;

      gradient = gimp_context_get_gradient (gimp_context_get_user ());

      import_dialog->import_type = GRAD_IMPORT;
      if (import_dialog->image_list)
	{
	  gtk_widget_hide (import_dialog->image_list);
	  gtk_widget_destroy (import_dialog->image_list);
	  import_dialog->image_list = NULL;
	}
      gtk_widget_show (import_dialog->select);
      palette_import_fill_grad_preview (import_dialog->preview, gradient);

      gtk_entry_set_text (GTK_ENTRY (import_dialog->entry), gradient->name);
      gtk_widget_set_sensitive (import_dialog->threshold_scale, FALSE);
      gtk_widget_set_sensitive (import_dialog->threshold_text, FALSE);
    }
}

static void
palette_import_image_callback (GtkWidget *widget,
			       gpointer   data)
{
  palette_import_image_menu_activate (FALSE, IMAGE_IMPORT, NULL);
  gtk_widget_set_sensitive (import_dialog->threshold_scale, TRUE);
  gtk_widget_set_sensitive (import_dialog->threshold_text, TRUE);
}

static void
palette_import_indexed_callback (GtkWidget *widget,
				 gpointer   data)
{
  palette_import_image_menu_activate (FALSE, INDEXED_IMPORT, NULL);
  gtk_widget_set_sensitive (import_dialog->threshold_scale, FALSE);
  gtk_widget_set_sensitive (import_dialog->threshold_text, FALSE);
}

/*  functions & callbacks to keep the import dialog uptodate  ****************/

static gint
palette_import_image_count (ImportType type)
{
  GSList *list=NULL;
  gint num_images = 0;

  if (type == INDEXED_IMPORT)
    {
      gimage_foreach (palette_import_gimlist_indexed_cb, &list);
    }
  else
    {
      gimage_foreach (palette_import_gimlist_cb, &list);
    }

  num_images = g_slist_length (list);

  g_slist_free (list);

  return num_images;
}

static void
palette_import_image_new (GimpSet   *set,
			  GimpImage *gimage,
			  gpointer   data)
{
  if (!import_dialog)
    return;

  if (!GTK_WIDGET_IS_SENSITIVE (import_dialog->image_menu_item_image))
    {
      gtk_widget_set_sensitive (import_dialog->image_menu_item_image, TRUE);
      return;
    }

  if (!GTK_WIDGET_IS_SENSITIVE (import_dialog->image_menu_item_indexed) &&
      gimage_base_type(gimage) == INDEXED)
    {
      gtk_widget_set_sensitive (import_dialog->image_menu_item_indexed, TRUE);
      return;
    }

  /* Now fill in the names if image menu shown */
  if (import_dialog->import_type == IMAGE_IMPORT ||
      import_dialog->import_type == INDEXED_IMPORT)
    {
      palette_import_image_menu_activate (TRUE, import_dialog->import_type,
					  NULL);
    }
}

static void
palette_import_image_destroyed (GimpSet   *set,
				GimpImage *gimage,
				gpointer   data)
{
  if (!import_dialog)
    return;

  if (palette_import_image_count (import_dialog->import_type) <= 1)
    {
      /* Back to gradient type */
      gtk_option_menu_set_history (GTK_OPTION_MENU (import_dialog->type_option), 0);
      palette_import_grad_callback (NULL, NULL);
      if (import_dialog->image_menu_item_image)
	gtk_widget_set_sensitive (import_dialog->image_menu_item_image, FALSE);
      return;
    }

  if (import_dialog->import_type == IMAGE_IMPORT ||
      import_dialog->import_type == INDEXED_IMPORT)
    {
      palette_import_image_menu_activate (TRUE, import_dialog->import_type,
					  gimage);
    }
}

void
palette_import_image_renamed (GimpImage* gimage)
{
  /* Now fill in the names if image menu shown */
  if (import_dialog && (import_dialog->import_type == IMAGE_IMPORT ||
      import_dialog->import_type == INDEXED_IMPORT))
    {
      palette_import_image_menu_activate (TRUE, import_dialog->import_type,
					  NULL);
    }
}

/*  create a palette from a gradient  ****************************************/

static void
palette_import_create_from_grad (gchar *name)
{
  PaletteEntries *entries;
  gradient_t *gradient;

  gradient = gimp_context_get_gradient (gimp_context_get_user ());

  if (gradient)
    {
      /* Add names to entry */
      gdouble  dx, cur_x;
      gdouble  r, g, b, a;

      gint sample_sz;
      gint loop;

      entries = palette_entries_new (name);
      sample_sz = (gint) import_dialog->sample->value;  

      dx    = 1.0 / (sample_sz - 1);
      cur_x = 0;
      
      for (loop = 0; loop < sample_sz; loop++)
	{
	  gradient_get_color_at (gradient, cur_x, &r, &g, &b, &a);
	  r = r * 255.0;
	  g = g * 255.0;
	  b = b * 255.0;
	  cur_x += dx;
	  palette_entries_add_entry (entries, _("Untitled"),
				     (gint) r, (gint) g, (gint) b);
	}

      palette_insert_all (entries);
    }
}

/*  create a palette from a non-indexed image  *******************************/

typedef struct _ImgColors ImgColors;

struct _ImgColors
{
  guint count;
  guint r_adj;
  guint g_adj;
  guint b_adj;
  guchar r;
  guchar g;
  guchar b;
};

static gint count_color_entries = 0;

static GHashTable *
palette_import_store_colors (GHashTable *h_array, 
			     guchar     *colors,
			     guchar     *colors_real, 
			     gint        sample_sz)
{
  gpointer found_color = NULL;
  ImgColors *new_color;
  guint key_colors = colors[0]*256*256 + colors[1]*256 + colors[2];

  if(h_array == NULL)
    {
      h_array = g_hash_table_new (g_direct_hash, g_direct_equal);
      count_color_entries = 0;
    }
  else
    {
      found_color = g_hash_table_lookup (h_array, (gpointer) key_colors);
    }

  if (found_color == NULL)
    {
      if (count_color_entries > MAX_IMAGE_COLORS)
	{
	  /* Don't add any more new ones */
	  return h_array;
	}

      count_color_entries++;

      new_color = g_new (ImgColors, 1);

      new_color->count = 1;
      new_color->r_adj = 0;
      new_color->g_adj = 0;
      new_color->b_adj = 0;
      new_color->r = colors[0];
      new_color->g = colors[1];
      new_color->b = colors[2];

      g_hash_table_insert (h_array, (gpointer) key_colors, new_color);
    }
  else
    {
      new_color = (ImgColors *) found_color;
      if(new_color->count < (G_MAXINT - 1))
	new_color->count++;
      
      /* Now do the adjustments ...*/
      new_color->r_adj += (colors_real[0] - colors[0]);
      new_color->g_adj += (colors_real[1] - colors[1]);
      new_color->b_adj += (colors_real[2] - colors[2]);

      /* Boundary conditions */
      if(new_color->r_adj > (G_MAXINT - 255))
	new_color->r_adj /= new_color->count;

      if(new_color->g_adj > (G_MAXINT - 255))
	new_color->g_adj /= new_color->count;

      if(new_color->b_adj > (G_MAXINT - 255))
	new_color->b_adj /= new_color->count;
    }

  return h_array;
}

static void
palette_import_create_sorted_list (gpointer key,
				   gpointer value,
				   gpointer user_data)
{
  GSList    **sorted_list = (GSList**) user_data;
  ImgColors  *color_tab  = (ImgColors *) value;

  *sorted_list = g_slist_prepend (*sorted_list, color_tab);
}

static gint
palette_import_sort_colors (gconstpointer a,
			    gconstpointer b)
{
  ImgColors *s1 = (ImgColors *) a;
  ImgColors *s2 = (ImgColors *) b;

  if(s1->count > s2->count)
    return -1;
  if(s1->count < s2->count)
    return 1;

  return 0;
}

static void
palette_import_create_image_palette (gpointer data,
				     gpointer user_data)
{
  PaletteEntries *entries = (PaletteEntries *) user_data;
  ImgColors *color_tab = (ImgColors *) data;
  gint sample_sz;
  gchar *lab;

  sample_sz = (gint) import_dialog->sample->value;  

  if (entries->n_colors >= sample_sz)
    return;

  lab = g_strdup_printf ("%s (occurs %u)", _("Untitled"), color_tab->count);

  /* Adjust the colors to the mean of the the sample */
  palette_entries_add_entry
    (entries, lab, 
     (gint) color_tab->r + (color_tab->r_adj / color_tab->count), 
     (gint) color_tab->g + (color_tab->g_adj / color_tab->count), 
     (gint) color_tab->b + (color_tab->b_adj / color_tab->count));
}

static gboolean
palette_import_color_print_remove (gpointer key,
				   gpointer value,
				   gpointer user_data)
{
  g_free (value);

  return TRUE;
}

static void
palette_import_image_make_palette (GHashTable *h_array,
				   guchar     *name)
{
  PaletteEntries *entries;
  GSList *sorted_list = NULL;

  g_hash_table_foreach (h_array, palette_import_create_sorted_list,
			&sorted_list);
  sorted_list = g_slist_sort (sorted_list, palette_import_sort_colors);

  entries = palette_entries_new (name);
  g_slist_foreach (sorted_list, palette_import_create_image_palette, entries);

  /*  Free up used memory
   *  Note the same structure is on both the hash list and the sorted
   *  list. So only delete it once.
   */
  g_hash_table_freeze (h_array);
  g_hash_table_foreach_remove (h_array,
			       palette_import_color_print_remove, NULL);
  g_hash_table_thaw (h_array);
  g_hash_table_destroy (h_array);
  g_slist_free (sorted_list);

  palette_insert_all (entries);
}

static void
palette_import_create_from_image (GImage *gimage,
				  gchar  *pname)
{
  PixelRegion imagePR;
  guchar *image_data;
  guchar *idata;
  guchar  rgb[MAX_CHANNELS];
  guchar  rgb_real[MAX_CHANNELS];
  gint has_alpha, indexed;
  gint width, height;
  gint bytes, alpha;
  gint i, j;
  void * pr;
  gint d_type;
  GHashTable *store_array = NULL;
  gint sample_sz;
  gint threshold = 1;

  sample_sz = (gint) import_dialog->sample->value;  

  if (gimage == NULL)
    return;

  /*  Get the image information  */
  bytes = gimage_composite_bytes (gimage);
  d_type = gimage_composite_type (gimage);
  has_alpha = (d_type == RGBA_GIMAGE ||
	       d_type == GRAYA_GIMAGE ||
	       d_type == INDEXEDA_GIMAGE);
  indexed = d_type == INDEXEDA_GIMAGE || d_type == INDEXED_GIMAGE;
  width = gimage->width;
  height = gimage->height;
  pixel_region_init (&imagePR, gimage_composite (gimage), 0, 0,
		     width, height, FALSE);

  alpha = bytes - 1;

  threshold = (gint) import_dialog->threshold->value;

  if(threshold < 1)
    threshold = 1;

  /*  iterate over the entire image  */
  for (pr = pixel_regions_register (1, &imagePR);
       pr != NULL;
       pr = pixel_regions_process (pr))
    {
      image_data = imagePR.data;

      for (i = 0; i < imagePR.h; i++)
	{
	  idata = image_data;

	  for (j = 0; j < imagePR.w; j++)
	    {
	      /*  Get the rgb values for the color  */
	      gimage_get_color (gimage, d_type, rgb, idata);
	      memcpy (rgb_real, rgb, MAX_CHANNELS); /* Structure copy */

	      rgb[0] = (rgb[0] / threshold) * threshold;
	      rgb[1] = (rgb[1] / threshold) * threshold;
	      rgb[2] = (rgb[2] / threshold) * threshold;

	      store_array =
		palette_import_store_colors (store_array, rgb, rgb_real,
					     sample_sz);

	      idata += bytes;
	    }

	  image_data += imagePR.rowstride;
	}
    }

  /*  Make palette from the store_array  */
  palette_import_image_make_palette (store_array, pname);
}

/*  create a palette from an indexed image  **********************************/

static void
palette_import_create_from_indexed (GImage *gimage,
				    gchar  *pname)
{
  PaletteEntries *entries;
  gint samples, count;

  samples = (gint) import_dialog->sample->value;  

  if (gimage == NULL)
    return;

  if (gimage_base_type (gimage) != INDEXED)
    return;

  entries = palette_entries_new (pname);

  for (count= 0; count < samples && count < gimage->num_cols; ++count)
    {
      palette_entries_add_entry (entries, NULL,
				 gimage->cmap[count*3],
				 gimage->cmap[count*3+1],
				 gimage->cmap[count*3+2]);
    }

  palette_insert_all (entries);
}

/*  the palette import action area callbacks  ********************************/

static void
palette_import_close_callback (GtkWidget *widget,
			      gpointer   data)
{
  gtk_widget_destroy (import_dialog->dialog);
  g_free (import_dialog);
  import_dialog = NULL;
}

static void
palette_import_import_callback (GtkWidget *widget,
				gpointer   data)
{
  PaletteDialog *palette;

  palette = data;

  if (import_dialog)
    {
      gchar *pname;

      pname = gtk_entry_get_text (GTK_ENTRY (import_dialog->entry));
      if (!pname || !strlen (pname))
	pname = g_strdup ("tmp");
      else
	pname = g_strdup (pname);

      switch (import_dialog->import_type)
	{
	case GRAD_IMPORT:
	  palette_import_create_from_grad (pname);
	  break;
	case IMAGE_IMPORT:
	  palette_import_create_from_image (import_dialog->gimage, pname);
	  break;
	case INDEXED_IMPORT:
	  palette_import_create_from_indexed (import_dialog->gimage, pname);
	  break;
	default:
	  break;
	}
      palette_import_close_callback (NULL, NULL);
    }
}

/*  the palette import dialog constructor  ***********************************/

static ImportDialog *
palette_import_dialog_new (PaletteDialog *palette)
{
  GtkWidget *dialog;
  GtkWidget *hbox;
  GtkWidget *frame;
  GtkWidget *vbox;
  GtkWidget *table;
  GtkWidget *label;
  GtkWidget *spinbutton;
  GtkWidget *button;
  GtkWidget *entry;
  GtkWidget *optionmenu;
  GtkWidget *optionmenu_menu;
  GtkWidget *menuitem;
  GtkWidget *image;
  GtkWidget *hscale;

  import_dialog = g_new (ImportDialog, 1);
  import_dialog->image_list = NULL;
  import_dialog->gimage     = NULL;

  import_dialog->dialog = dialog =
    gimp_dialog_new (_("Import Palette"), "import_palette",
		     gimp_standard_help_func,
		     "dialogs/palette_editor/import_palette.html",
		     GTK_WIN_POS_NONE,
		     FALSE, TRUE, FALSE,

		     _("Import"), palette_import_import_callback,
		     palette, NULL, NULL, FALSE, FALSE,
		     _("Close"), palette_import_close_callback,
		     palette, NULL, NULL, TRUE, TRUE,

		     NULL);

  /*  The main hbox  */
  hbox = gtk_hbox_new (FALSE, 4);
  gtk_container_set_border_width (GTK_CONTAINER (hbox), 4);
  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hbox);
  gtk_widget_show (hbox);

  /*  The "Import" frame  */
  frame = gtk_frame_new (_("Import"));
  gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
  gtk_widget_show (frame);

  vbox = gtk_vbox_new (FALSE, 2);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
  gtk_container_add (GTK_CONTAINER (frame), vbox);
  gtk_widget_show (vbox);

  table = gtk_table_new (4, 2, FALSE);
  gtk_table_set_col_spacing (GTK_TABLE (table), 0, 4);
  gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
  gtk_widget_show (table);

  /*  The source's name  */
  label = gtk_label_new (_("Name:"));
  gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
                    GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0);
  gtk_widget_show (label);

  entry = import_dialog->entry = gtk_entry_new ();
  gtk_table_attach_defaults (GTK_TABLE (table), entry, 1, 2, 0, 1);
  {
    gradient_t* gradient;

    gradient = gimp_context_get_gradient (gimp_context_get_current ());
    gtk_entry_set_text (GTK_ENTRY (entry),
			gradient ? gradient->name : _("new_import"));
  }
  gtk_widget_show (entry);

  /*  The source type  */
  label = gtk_label_new (_("Source:"));
  gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
                    GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0);
  gtk_widget_show (label);

  optionmenu = import_dialog->type_option = gtk_option_menu_new ();
  optionmenu_menu = gtk_menu_new ();
  gtk_table_attach_defaults (GTK_TABLE (table), optionmenu, 1, 2, 1, 2);
  menuitem = import_dialog->image_menu_item_gradient = 
    gtk_menu_item_new_with_label (_("Gradient"));
  gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
		      GTK_SIGNAL_FUNC (palette_import_grad_callback),
		      NULL);
  gtk_menu_append (GTK_MENU (optionmenu_menu), menuitem);
  gtk_widget_show (menuitem);

  menuitem = import_dialog->image_menu_item_image =
    gtk_menu_item_new_with_label (_("Image"));
  gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
		      GTK_SIGNAL_FUNC (palette_import_image_callback),
		      (gpointer) import_dialog);
  gtk_menu_append (GTK_MENU (optionmenu_menu), menuitem);
  gtk_widget_show (menuitem);
  gtk_widget_set_sensitive (menuitem,
			    palette_import_image_count (IMAGE_IMPORT) > 0);

  menuitem = import_dialog->image_menu_item_indexed =
    gtk_menu_item_new_with_label (_("Indexed Palette"));
  gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
                      GTK_SIGNAL_FUNC (palette_import_indexed_callback),
                      (gpointer) import_dialog);
  gtk_menu_append (GTK_MENU (optionmenu_menu), menuitem);
  gtk_widget_show (menuitem);
  gtk_widget_set_sensitive (menuitem,
			    palette_import_image_count (INDEXED_IMPORT) > 0);

  gtk_option_menu_set_menu (GTK_OPTION_MENU (optionmenu), optionmenu_menu);
  gtk_widget_show (optionmenu);

  /*  The sample size  */
  label = gtk_label_new (_("Sample Size:"));
  gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3,
                    GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0);
  gtk_widget_show (label);

  import_dialog->sample =
    GTK_ADJUSTMENT(gtk_adjustment_new (256, 2, 10000, 1, 10, 10));
  spinbutton = gtk_spin_button_new (import_dialog->sample, 1, 0);
  gtk_table_attach_defaults (GTK_TABLE (table), spinbutton, 1, 2, 2, 3);
  gtk_widget_show (spinbutton);

  /*  The interval  */
  label = import_dialog->threshold_text = gtk_label_new (_("Interval:"));
  gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0);
  gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4,
                    GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
  gtk_widget_set_sensitive(label, FALSE);
  gtk_widget_show (label);

  import_dialog->threshold = 
    GTK_ADJUSTMENT (gtk_adjustment_new (1, 1, 128, 1, 1, 1));
  hscale = import_dialog->threshold_scale = 
    gtk_hscale_new (import_dialog->threshold);
  gtk_scale_set_value_pos (GTK_SCALE (hscale), GTK_POS_TOP);
  gtk_scale_set_digits (GTK_SCALE (hscale), 0);
  gtk_table_attach_defaults (GTK_TABLE (table), hscale, 1, 2, 3, 4);
  gtk_widget_set_sensitive (hscale, FALSE);
  gtk_widget_show (hscale);

  /*  The preview frame  */
  frame = gtk_frame_new (_("Preview"));
  gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
  gtk_widget_show (frame);

  vbox = import_dialog->select_area = gtk_vbox_new (FALSE, 2);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
  gtk_container_add (GTK_CONTAINER (frame), vbox);
  gtk_widget_show (vbox);

  image = import_dialog->preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  gtk_preview_set_dither (GTK_PREVIEW (image), GDK_RGB_DITHER_MAX);
  gtk_preview_size (GTK_PREVIEW (image),
		    IMPORT_PREVIEW_WIDTH, IMPORT_PREVIEW_HEIGHT);
  gtk_widget_set_usize (image, IMPORT_PREVIEW_WIDTH, IMPORT_PREVIEW_HEIGHT);
  gtk_box_pack_start (GTK_BOX (vbox), image, FALSE, FALSE, 0);
  gtk_widget_show (image);

  button = import_dialog->select = gtk_button_new_with_label (_("Select"));
  GTK_WIDGET_UNSET_FLAGS (button, GTK_RECEIVES_DEFAULT);
  gtk_signal_connect (GTK_OBJECT (button), "clicked", 
		      GTK_SIGNAL_FUNC (palette_import_select_grad_callback),
		      (gpointer) image);
  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
  gtk_widget_show (button);

  /*  Fill with the selected gradient  */
  palette_import_fill_grad_preview
    (image, gimp_context_get_gradient (gimp_context_get_user ()));
  import_dialog->import_type = GRAD_IMPORT;
  gtk_signal_connect (GTK_OBJECT (gimp_context_get_user ()), "gradient_changed",
		      GTK_SIGNAL_FUNC (palette_import_gradient_update),
		      NULL);

  /*  keep the dialog up-to-date  */
  gtk_signal_connect (GTK_OBJECT (image_context), "add",
		      GTK_SIGNAL_FUNC (palette_import_image_new),
		      NULL);
  gtk_signal_connect (GTK_OBJECT (image_context), "remove",
		      GTK_SIGNAL_FUNC (palette_import_image_destroyed),
		      NULL);

  return import_dialog;
}
