gimp/app/palette.c

3340 lines
85 KiB
C

/* 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;
}