gimp/app/widgets/gimppaletteeditor.c

1800 lines
43 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "appenv.h"
#include "actionarea.h"
#include "buildmenu.h"
#include "colormaps.h"
#include "color_area.h"
#include "color_select.h"
#include "datafiles.h"
#include "devices.h"
#include "errors.h"
#include "general.h"
#include "gimprc.h"
#include "interface.h"
#include "palette.h"
#include "session.h"
#define ENTRY_WIDTH 14
#define ENTRY_HEIGHT 10
#define SPACING 1
#define COLUMNS 16
#define ROWS 16
#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
typedef struct _Palette _Palette, *PaletteP;
struct _Palette {
GtkWidget *shell;
GtkWidget *vbox;
GtkWidget *frame;
GtkWidget *menu;
GtkWidget *option_menu;
GtkWidget *color_area;
GtkWidget *color_name;
GtkWidget *palette_ops;
GdkGC *gc;
GtkAdjustment *sbar_data;
PaletteEntriesP entries;
PaletteEntryP color;
ColorSelectP color_select;
int color_select_active;
int scroll_offset;
int updating;
};
static void palette_create_palette_menu (PaletteP, PaletteEntriesP);
static PaletteEntryP palette_add_entry (PaletteEntriesP, char *, int, int, int);
static void palette_delete_entry (PaletteP);
static void palette_calc_scrollbar (PaletteP);
static void palette_entries_load (char *);
static GSList * palette_entries_insert_list (GSList *, PaletteEntriesP);
static void palette_entries_delete (char *);
static void palette_entries_save (PaletteEntriesP, char *);
static void palette_entries_free (PaletteEntriesP);
static void palette_entry_free (PaletteEntryP);
static void palette_entries_set_callback (GtkWidget *, gpointer);
static void palette_change_color (int, int, int, int);
static gint palette_color_area_expose (GtkWidget *, GdkEventExpose *, PaletteP);
static gint palette_color_area_events (GtkWidget *, GdkEvent *, PaletteP);
static void palette_scroll_update (GtkAdjustment *, gpointer);
static void palette_new_callback (GtkWidget *, gpointer);
static void palette_delete_callback (GtkWidget *, gpointer);
static void palette_refresh_callback (GtkWidget *, gpointer);
static void palette_edit_callback (GtkWidget *, gpointer);
static void palette_close_callback (GtkWidget *, gpointer);
static gint palette_dialog_delete_callback (GtkWidget *, GdkEvent *, gpointer);
static void palette_new_entries_callback (GtkWidget *, gpointer);
static void palette_add_entries_callback (GtkWidget *, gpointer, gpointer);
/* static void palette_merge_entries_callback (GtkWidget *, gpointer); */
static void palette_delete_entries_callback (GtkWidget *, gpointer);
static void palette_select_callback (int, int, int, ColorSelectState, void *);
static void palette_draw_entries (PaletteP);
static void palette_draw_current_entry (PaletteP);
static void palette_update_current_entry (PaletteP);
GSList *palette_entries_list = NULL;
static PaletteP palette = NULL;
static PaletteEntriesP default_palette_entries = NULL;
static int num_palette_entries = 0;
static unsigned char foreground[3] = { 0, 0, 0 };
static unsigned char background[3] = { 255, 255, 255 };
/* Color select dialog */
/* static ColorSelectP color_select = NULL;
static int color_select_active = 0; */
static ActionAreaItem action_items[] =
{
{ "New", palette_new_callback, NULL, NULL },
{ "Edit", palette_edit_callback, NULL, NULL },
{ "Delete", palette_delete_callback, NULL, NULL },
{ "Close", palette_close_callback, NULL, NULL },
};
static MenuItem palette_ops[] =
{
{ "New Palette", 0, 0, palette_new_entries_callback, NULL, NULL, NULL },
{ "Delete Palette", 0, 0, palette_delete_entries_callback, NULL, NULL, NULL },
{ "Refresh Palettes", 0, 0, palette_refresh_callback, NULL, NULL, NULL },
{ "Close", 0, 0, palette_close_callback, NULL, NULL, NULL },
{ NULL, 0, 0, NULL, NULL, NULL, NULL },
};
void
palettes_init (int no_data)
{
palette_init_palettes (no_data);
}
void
palettes_free ()
{
palette_free_palettes ();
}
void
palette_create ()
{
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *sbar;
GtkWidget *frame;
GtkWidget *options_box;
GtkWidget *arrow_hbox;
GtkWidget *label;
GtkWidget *arrow;
GtkWidget *menu_bar;
GtkWidget *menu_bar_item;
int i;
if (!palette)
{
palette = g_malloc (sizeof (_Palette));
palette->entries = default_palette_entries;
palette->color = NULL;
palette->color_select = NULL;
palette->color_select_active = 0;
palette->scroll_offset = 0;
palette->gc = NULL;
palette->updating = FALSE;
/* The shell and main vbox */
palette->shell = gtk_dialog_new ();
gtk_window_set_wmclass (GTK_WINDOW (palette->shell), "color_palette", "Gimp");
session_set_window_geometry (palette->shell, &palette_session_info, FALSE);
gtk_window_set_policy (GTK_WINDOW (palette->shell), FALSE, FALSE, FALSE);
gtk_window_set_title (GTK_WINDOW (palette->shell), "Color Palette");
vbox = gtk_vbox_new (FALSE, 1);
gtk_container_border_width (GTK_CONTAINER (vbox), 1);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (palette->shell)->vbox), vbox, TRUE, TRUE, 0);
/* handle the wm close event */
gtk_signal_connect (GTK_OBJECT (palette->shell), "delete_event",
GTK_SIGNAL_FUNC (palette_dialog_delete_callback),
palette);
/* The palette options box */
options_box = gtk_hbox_new (FALSE, 1);
gtk_box_pack_start (GTK_BOX (vbox), options_box, FALSE, FALSE, 0);
/* The popup menu -- palette_ops */
for (i = 0; palette_ops[i].label; i++)
palette_ops[i].user_data = palette;
palette->palette_ops = build_menu (palette_ops, NULL);
/* The palette commands pulldown menu */
menu_bar = gtk_menu_bar_new ();
gtk_box_pack_start (GTK_BOX (options_box), menu_bar, FALSE, FALSE, 0);
menu_bar_item = gtk_menu_item_new ();
gtk_container_add (GTK_CONTAINER (menu_bar), menu_bar_item);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_bar_item), palette->palette_ops);
arrow_hbox = gtk_hbox_new (FALSE, 1);
gtk_container_add (GTK_CONTAINER (menu_bar_item), arrow_hbox);
label = gtk_label_new ("Ops");
arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
gtk_box_pack_start (GTK_BOX (arrow_hbox), arrow, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (arrow_hbox), label, FALSE, FALSE, 4);
gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
gtk_misc_set_alignment (GTK_MISC (arrow), 0.5, 0.5);
gtk_widget_show (arrow);
gtk_widget_show (label);
gtk_widget_show (arrow_hbox);
gtk_widget_show (menu_bar_item);
gtk_widget_show (menu_bar);
/* The option menu */
palette->option_menu = gtk_option_menu_new ();
gtk_box_pack_start (GTK_BOX (options_box), palette->option_menu, TRUE, TRUE, 0);
gtk_widget_show (palette->option_menu);
gtk_widget_show (options_box);
/* The active color name */
palette->color_name = gtk_entry_new ();
gtk_entry_set_text (GTK_ENTRY (palette->color_name), "Active Color Name");
gtk_box_pack_start (GTK_BOX (vbox), palette->color_name, FALSE, FALSE, 0);
gtk_widget_show (palette->color_name);
/* The horizontal box containing preview & scrollbar */
hbox = gtk_hbox_new (FALSE, 1);
gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
palette->sbar_data = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, PREVIEW_HEIGHT, 1, 1, PREVIEW_HEIGHT));
gtk_signal_connect (GTK_OBJECT (palette->sbar_data), "value_changed",
(GtkSignalFunc) palette_scroll_update,
palette);
sbar = gtk_vscrollbar_new (palette->sbar_data);
gtk_box_pack_start (GTK_BOX (hbox), sbar, FALSE, FALSE, 0);
/* Create the color area window and the underlying image */
palette->color_area = gtk_preview_new (GTK_PREVIEW_COLOR);
gtk_preview_size (GTK_PREVIEW (palette->color_area), PREVIEW_WIDTH, PREVIEW_HEIGHT);
gtk_widget_set_events (palette->color_area, PALETTE_EVENT_MASK);
gtk_signal_connect_after (GTK_OBJECT (palette->color_area), "expose_event",
(GtkSignalFunc) palette_color_area_expose,
palette);
gtk_signal_connect (GTK_OBJECT (palette->color_area), "event",
(GtkSignalFunc) palette_color_area_events,
palette);
gtk_container_add (GTK_CONTAINER (frame), palette->color_area);
gtk_widget_show (palette->color_area);
gtk_widget_show (sbar);
gtk_widget_show (frame);
gtk_widget_show (hbox);
if(no_data)
palettes_init(FALSE);
/* The action area */
action_items[0].user_data = palette;
action_items[1].user_data = palette;
action_items[2].user_data = palette;
action_items[3].user_data = palette;
build_action_area (GTK_DIALOG (palette->shell), action_items, 4, 0);
gtk_widget_show (vbox);
gtk_widget_show (palette->shell);
palette_create_palette_menu (palette, default_palette_entries);
palette_calc_scrollbar (palette);
}
else
{
if (!GTK_WIDGET_VISIBLE (palette->shell))
{
gtk_widget_show (palette->shell);
}
else
{
gdk_window_raise(palette->shell->window);
}
}
}
void
palette_free ()
{
if (palette)
{
gdk_gc_destroy (palette->gc);
if (palette->color_select)
color_select_free (palette->color_select);
session_get_window_info (palette->shell, &palette_session_info);
g_free (palette);
palette = NULL;
}
}
void
palette_get_foreground (unsigned char *r,
unsigned char *g,
unsigned char *b)
{
*r = foreground[0];
*g = foreground[1];
*b = foreground[2];
}
void
palette_get_background (unsigned char *r,
unsigned char *g,
unsigned char *b)
{
*r = background[0];
*g = background[1];
*b = background[2];
}
void
palette_set_foreground (int r,
int g,
int b)
{
unsigned char rr, gg, bb;
/* Foreground */
foreground[0] = r;
foreground[1] = g;
foreground[2] = b;
palette_get_foreground (&rr, &gg, &bb);
if (no_interface == FALSE)
{
store_color (&foreground_pixel, rr, gg, bb);
color_area_update ();
device_status_update (current_device);
}
}
void
palette_set_background (int r,
int g,
int b)
{
unsigned char rr, gg, bb;
/* Background */
background[0] = r;
background[1] = g;
background[2] = b;
palette_get_background (&rr, &gg, &bb);
if (no_interface == FALSE)
{
store_color (&background_pixel, rr, gg, bb);
color_area_update ();
}
}
void
palette_set_default_colors (void)
{
palette_set_foreground (0, 0, 0);
palette_set_background (255, 255, 255);
}
void
palette_swap_colors (void)
{
unsigned char fg_r, fg_g, fg_b;
unsigned char bg_r, bg_g, bg_b;
palette_get_foreground (&fg_r, &fg_g, &fg_b);
palette_get_background (&bg_r, &bg_g, &bg_b);
palette_set_foreground (bg_r, bg_g, bg_b);
palette_set_background (fg_r, fg_g, fg_b);
}
void
palette_init_palettes (int no_data)
{
if(!no_data)
datafiles_read_directories (palette_path, palette_entries_load, 0);
}
void
palette_free_palettes (void)
{
GSList *list;
PaletteEntriesP entries;
list = palette_entries_list;
while (list)
{
entries = (PaletteEntriesP) 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);
list = g_slist_next (list);
}
g_slist_free (palette_entries_list);
if (palette)
{
palette->entries = NULL;
palette->color = NULL;
palette->color_select = NULL;
palette->color_select_active = 0;
palette->scroll_offset = 0;
}
num_palette_entries = 0;
palette_entries_list = NULL;
}
/*****************************************/
/* Local functions */
/*****************************************/
static void
palette_create_palette_menu (PaletteP palette,
PaletteEntriesP default_entries)
{
GtkWidget *menu_item;
GSList *list;
PaletteEntriesP p_entries = NULL;
PaletteEntriesP found_entries = NULL;
int i = 0;
int default_index = -1;
palette->menu = gtk_menu_new ();
list = palette_entries_list;
while (list)
{
p_entries = (PaletteEntriesP) list->data;
list = g_slist_next (list);
/* to make sure we get something! */
if (p_entries == NULL)
{
found_entries = p_entries;
default_index = i;
}
if (p_entries == default_entries)
{
found_entries = default_entries;
default_index = i;
}
menu_item = gtk_menu_item_new_with_label (p_entries->name);
gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
(GtkSignalFunc) palette_entries_set_callback,
(gpointer) p_entries);
gtk_container_add (GTK_CONTAINER (palette->menu), menu_item);
gtk_widget_show (menu_item);
i++;
}
if (i == 0)
{
menu_item = gtk_menu_item_new_with_label ("none");
gtk_container_add (GTK_CONTAINER (palette->menu), menu_item);
gtk_widget_show (menu_item);
/* Set the action area and option menus to insensitive */
gtk_widget_set_sensitive (palette->option_menu, FALSE);
gtk_widget_set_sensitive (GTK_DIALOG (palette->shell)->action_area, FALSE);
/* Clear the color area */
gdk_window_clear (palette->color_area->window);
}
else
{
/* Make sure the action area and option menus are sensitive */
gtk_widget_set_sensitive (palette->option_menu, TRUE);
gtk_widget_set_sensitive (GTK_DIALOG (palette->shell)->action_area, TRUE);
/* Clear the color area */
gdk_window_clear (palette->color_area->window);
}
gtk_option_menu_set_menu (GTK_OPTION_MENU (palette->option_menu), palette->menu);
/* Set the current item of the option menu to reflect
* the default palette. Need to refresh here too
*/
if (default_index != -1)
{
gtk_option_menu_set_history (GTK_OPTION_MENU (palette->option_menu), default_index);
palette_entries_set_callback (NULL, found_entries);
}
}
static void
palette_entries_load (char *filename)
{
PaletteEntriesP entries;
char str[512];
char *tok;
FILE *fp;
int r, g, b;
r = g = b = 0;
entries = (PaletteEntriesP) g_malloc (sizeof (_PaletteEntries));
entries->filename = g_strdup (filename);
entries->name = g_strdup (prune_filename (filename));
entries->colors = NULL;
entries->n_colors = 0;
/* Open the requested file */
if (!(fp = fopen (filename, "r")))
{
palette_entries_free (entries);
return;
}
fread (str, 13, 1, fp);
str[13] = '\0';
if (strcmp (str, "GIMP Palette\n"))
{
fclose (fp);
return;
}
while (!feof (fp))
{
if (!fgets (str, 512, fp))
continue;
if (str[0] != '#')
{
tok = strtok (str, " \t");
if (tok)
r = atoi (tok);
tok = strtok (NULL, " \t");
if (tok)
g = atoi (tok);
tok = strtok (NULL, " \t");
if (tok)
b = atoi (tok);
tok = strtok (NULL, "\n");
palette_add_entry (entries, tok, r, g, b);
} /* if */
} /* while */
/* Clean up */
fclose (fp);
entries->changed = 0;
palette_entries_list = palette_entries_insert_list(palette_entries_list, entries);
/* Check if the current palette is the default one */
if (strcmp(default_palette, prune_filename(filename)) == 0)
default_palette_entries = entries;
}
static void
palette_entries_delete (char *filename)
{
if (filename)
unlink (filename);
}
static GSList *
palette_entries_insert_list (GSList * list,
PaletteEntriesP entries)
{
/* add it to the list */
num_palette_entries++;
return g_slist_append (list, (void *) entries);
}
static void
palette_entries_save (PaletteEntriesP palette,
char *filename)
{
FILE * fp;
GSList * list;
PaletteEntryP entry;
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);
list = palette->colors;
while (list)
{
entry = (PaletteEntryP) list->data;
fprintf (fp, "%d %d %d\t%s\n", entry->color[0], entry->color[1],
entry->color[2], entry->name);
list = g_slist_next (list);
}
/* Clean up */
fclose (fp);
}
static void
palette_entries_free (PaletteEntriesP entries)
{
PaletteEntryP entry;
GSList * list;
list = entries->colors;
while (list)
{
entry = (PaletteEntryP) list->data;
palette_entry_free (entry);
list = list->next;
}
g_free (entries->name);
if (entries->filename)
g_free (entries->filename);
g_free (entries);
}
static void
palette_entry_free (PaletteEntryP entry)
{
if (entry->name)
g_free (entry->name);
g_free (entry);
}
static void
palette_entries_set_callback (GtkWidget *w,
gpointer client_data)
{
PaletteEntriesP pal;
if (palette)
{
pal = (PaletteEntriesP) client_data;
palette->entries = pal;
palette->color = NULL;
if (palette->color_select_active)
{
palette->color_select_active = 0;
color_select_hide (palette->color_select);
}
palette->color_select = NULL;
palette->scroll_offset = 0;
palette_calc_scrollbar (palette);
palette_draw_entries (palette);
palette_draw_current_entry (palette);
}
}
static void
palette_change_color (int r,
int g,
int b,
int state)
{
if (palette && palette->entries)
{
switch (state)
{
case COLOR_NEW:
palette->color = palette_add_entry (palette->entries, "Untitled", r, g, b);
palette_calc_scrollbar (palette);
palette_draw_entries (palette);
palette_draw_current_entry (palette);
break;
case COLOR_UPDATE_NEW:
palette->color->color[0] = r;
palette->color->color[1] = g;
palette->color->color[2] = b;
palette_draw_entries (palette);
palette_draw_current_entry (palette);
break;
default:
break;
}
}
if (active_color == FOREGROUND)
palette_set_foreground (r, g, b);
else if (active_color == BACKGROUND)
palette_set_background (r, g, b);
}
void
palette_set_active_color (int r,
int g,
int b,
int state)
{
palette_change_color (r, g, b, state);
}
static gint
palette_color_area_expose (GtkWidget *widget,
GdkEventExpose *event,
PaletteP palette)
{
if (!palette->gc)
palette->gc = gdk_gc_new (widget->window);
palette_draw_current_entry (palette);
return FALSE;
}
static gint
palette_color_area_events (GtkWidget *widget,
GdkEvent *event,
PaletteP palette)
{
GdkEventButton *bevent;
GSList *tmp_link;
int r, g, b;
int width, height;
int entry_width;
int entry_height;
int row, col;
int pos;
switch (event->type)
{
case GDK_BUTTON_PRESS:
bevent = (GdkEventButton *) event;
width = palette->color_area->requisition.width;
height = palette->color_area->requisition.height;
entry_width = ((width - (SPACING * (COLUMNS + 1))) / COLUMNS) + SPACING;
entry_height = ((height - (SPACING * (ROWS + 1))) / ROWS) + SPACING;
col = (bevent->x - 1) / entry_width;
row = (palette->scroll_offset + bevent->y - 1) / entry_height;
pos = row * COLUMNS + col;
if (bevent->button == 1 && palette->entries)
{
tmp_link = g_slist_nth (palette->entries->colors, pos);
if (tmp_link)
{
palette_draw_current_entry (palette);
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)
palette_set_foreground (r, g, b);
else if (active_color == BACKGROUND)
palette_set_background (r, g, b);
palette_update_current_entry (palette);
}
}
break;
default:
break;
}
return FALSE;
}
static void
palette_scroll_update (GtkAdjustment *adjustment,
gpointer data)
{
PaletteP palette;
palette = (PaletteP) data;
if (palette)
{
palette->scroll_offset = adjustment->value;
palette_draw_entries (palette);
palette_draw_current_entry (palette);
}
}
static void
palette_new_callback (GtkWidget *w,
gpointer client_data)
{
PaletteP palette;
palette = client_data;
if (palette && palette->entries)
{
if (active_color == FOREGROUND)
palette->color =
palette_add_entry (palette->entries, "Untitled",
foreground[0], foreground[1], foreground[2]);
else if (active_color == BACKGROUND)
palette->color =
palette_add_entry (palette->entries, "Untitled",
background[0], background[1], background[2]);
palette_calc_scrollbar (palette);
palette_draw_entries (palette);
palette_draw_current_entry (palette);
}
}
static void
palette_delete_callback (GtkWidget *w,
gpointer client_data)
{
PaletteP palette;
palette = client_data;
if (palette)
palette_delete_entry (palette);
}
static void
palette_refresh_callback (GtkWidget *w,
gpointer client_data)
{
PaletteP palette;
palette = client_data;
if(palette)
{
palette_free_palettes ();
palette_init_palettes(FALSE);
palette_create_palette_menu (palette, default_palette_entries);
palette_calc_scrollbar (palette);
palette_draw_entries (palette);
palette_draw_current_entry (palette);
}
else
{
palette_free_palettes ();
palette_init_palettes(FALSE);
}
}
static void
palette_edit_callback (GtkWidget *w,
gpointer client_data)
{
PaletteP palette;
unsigned char *color;
palette = client_data;
if (palette && palette->entries && palette->color)
{
color = palette->color->color;
if (!palette->color_select)
{
palette->color_select = color_select_new (color[0], color[1], color[2],
palette_select_callback, NULL,
FALSE);
palette->color_select_active = 1;
}
else
{
if (!palette->color_select_active)
{
color_select_show (palette->color_select);
palette->color_select_active = 1;
}
color_select_set_color (palette->color_select, color[0], color[1], color[2], 1);
}
}
}
static gint
palette_dialog_delete_callback (GtkWidget *w,
GdkEvent *e,
gpointer client_data)
{
palette_close_callback (w, client_data);
return TRUE;
}
static void
palette_close_callback (GtkWidget *w,
gpointer client_data)
{
PaletteP palette;
palette = client_data;
if (palette)
{
if (palette->color_select_active)
{
palette->color_select_active = 0;
color_select_hide (palette->color_select);
}
if (GTK_WIDGET_VISIBLE (palette->shell))
gtk_widget_hide (palette->shell);
}
}
static void
palette_new_entries_callback (GtkWidget *w,
gpointer client_data)
{
query_string_box ("New Palette", "Enter a name for new palette", NULL,
palette_add_entries_callback, NULL);
}
static void
palette_add_entries_callback (GtkWidget *w,
gpointer client_data,
gpointer call_data)
{
char *home;
char *palette_name;
char *local_path;
char *first_token;
char *token;
char *path;
PaletteEntriesP entries;
palette_name = (char *) call_data;
if (palette && palette_name)
{
entries = g_malloc (sizeof (_PaletteEntries));
if (palette_path)
{
/* Get the first path specified in the palette path list */
home = getenv("HOME");
local_path = g_strdup (palette_path);
first_token = local_path;
token = xstrsep(&first_token, ":");
if (token)
{
if (*token == '~')
{
path = g_malloc(strlen(home) + strlen(token) + 1);
sprintf(path, "%s%s", home, token + 1);
}
else
{
path = g_malloc(strlen(token) + 1);
strcpy(path, token);
}
entries->filename = g_malloc (strlen (path) + strlen (palette_name) + 2);
sprintf (entries->filename, "%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 = 1;
palette_entries_list = palette_entries_insert_list (palette_entries_list, entries);
gtk_option_menu_remove_menu (GTK_OPTION_MENU (palette->option_menu));
palette_create_palette_menu (palette, entries);
}
}
/* static void
palette_merge_entries_callback (GtkWidget *w,
gpointer client_data)
{
} */
static void
palette_delete_entries_callback (GtkWidget *w,
gpointer client_data)
{
PaletteP palette;
PaletteEntriesP entries;
palette = client_data;
if (palette && palette->entries)
{
/* If a color selection dialog is up, hide it */
if (palette->color_select_active)
{
palette->color_select_active = 0;
color_select_hide (palette->color_select);
}
gtk_option_menu_remove_menu (GTK_OPTION_MENU (palette->option_menu));
entries = palette->entries;
if (entries && entries->filename)
palette_entries_delete (entries->filename);
if (default_palette_entries == entries)
default_palette_entries = NULL;
palette->entries = NULL;
palette_free_palettes (); /* free palettes, don't save any modified versions */
palette_init_palettes (FALSE); /* load in brand new palettes */
palette_create_palette_menu (palette, default_palette_entries);
}
}
static void
palette_select_callback (int r,
int g,
int b,
ColorSelectState state,
void *client_data)
{
unsigned char * color;
if (palette && palette->entries)
{
switch (state) {
case COLOR_SELECT_UPDATE:
break;
case COLOR_SELECT_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)
palette_set_foreground (r, g, b);
else if (active_color == BACKGROUND)
palette_set_background (r, g, b);
palette_calc_scrollbar (palette);
palette_draw_entries (palette);
palette_draw_current_entry (palette);
}
/* Fallthrough */
case COLOR_SELECT_CANCEL:
color_select_hide (palette->color_select);
palette->color_select_active = 0;
}
}
}
static int
palette_draw_color_row (unsigned char **colors,
int ncolors,
int y,
unsigned char *buffer,
GtkWidget *preview)
{
unsigned char *p;
unsigned char bcolor;
int width, height;
int entry_width;
int entry_height;
int vsize;
int vspacing;
int i, j;
bcolor = 0;
width = preview->requisition.width;
height = preview->requisition.height;
entry_width = (width - (SPACING * (COLUMNS + 1))) / COLUMNS;
entry_height = (height - (SPACING * (ROWS + 1))) / ROWS;
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;
}
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 < (COLUMNS - ncolors); i++)
{
for (j = 0; j < (SPACING + entry_width); j++)
{
*p++ = 0;
*p++ = 0;
*p++ = 0;
}
}
for (j = 0; j < SPACING; j++)
{
*p++ = bcolor;
*p++ = bcolor;
*p++ = bcolor;
}
if (y < 0)
y += entry_height - vsize;
for (i = 0; i < vsize; i++, y++)
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_draw_entries (PaletteP palette)
{
PaletteEntryP entry;
unsigned char *buffer;
unsigned char *colors[COLUMNS];
GSList *tmp_link;
int width, height;
int entry_width;
int entry_height;
int row_vsize;
int index, y;
if (palette && palette->entries && !palette->updating)
{
width = palette->color_area->requisition.width;
height = palette->color_area->requisition.height;
entry_width = (width - (SPACING * (COLUMNS + 1))) / COLUMNS;
entry_height = (height - (SPACING * (ROWS + 1))) / ROWS;
buffer = g_malloc (width * 3);
y = -palette->scroll_offset;
row_vsize = SPACING + entry_height;
tmp_link = palette->entries->colors;
index = 0;
while ((tmp_link) && (y < -row_vsize))
{
tmp_link = tmp_link->next;
if (++index == COLUMNS)
{
index = 0;
y += row_vsize;
}
}
index = 0;
while (tmp_link)
{
entry = tmp_link->data;
tmp_link = tmp_link->next;
colors[index] = entry->color;
index++;
if (index == COLUMNS)
{
index = 0;
y = palette_draw_color_row (colors, COLUMNS, y, buffer, palette->color_area);
if (y >= height)
break;
}
}
while (y < height)
{
y = palette_draw_color_row (colors, index, y, buffer, palette->color_area);
index = 0;
}
gtk_widget_draw (palette->color_area, NULL);
g_free (buffer);
}
}
static void
palette_draw_current_entry (PaletteP palette)
{
PaletteEntryP entry;
int width, height;
int entry_width;
int entry_height;
int row, col;
int x, y;
if (palette && palette->entries && !palette->updating && palette->color)
{
gdk_gc_set_function (palette->gc, GDK_INVERT);
entry = palette->color;
row = entry->position / COLUMNS;
col = entry->position % COLUMNS;
entry_width = (palette->color_area->requisition.width -
(SPACING * (COLUMNS + 1))) / COLUMNS;
entry_height = (palette->color_area->requisition.height -
(SPACING * (ROWS + 1))) / ROWS;
x = col * (entry_width + SPACING);
y = row * (entry_height + SPACING);
y -= palette->scroll_offset;
width = entry_width + SPACING;
height = entry_height + SPACING;
gdk_draw_rectangle (palette->color_area->window, palette->gc,
0, x, y, width, height);
gdk_gc_set_function (palette->gc, GDK_COPY);
}
}
static void
palette_update_current_entry (PaletteP palette)
{
if (palette && palette->entries)
{
/* Draw the current entry */
palette_draw_current_entry (palette);
/* Update the active color name */
gtk_entry_set_text (GTK_ENTRY (palette->color_name), palette->color->name);
}
}
static PaletteEntryP
palette_add_entry (PaletteEntriesP entries,
char *name,
int r,
int g,
int b)
{
PaletteEntryP entry;
if (entries)
{
entry = g_malloc (sizeof (_PaletteEntry));
entry->color[0] = r;
entry->color[1] = g;
entry->color[2] = b;
if (name)
entry->name = g_strdup (name);
else
entry->name = g_strdup ("Untitled");
entry->position = entries->n_colors;
entries->colors = g_slist_append (entries->colors, entry);
entries->n_colors += 1;
entries->changed = 1;
return entry;
}
return NULL;
}
static void
palette_delete_entry (PaletteP palette)
{
PaletteEntryP entry;
GSList *tmp_link;
int pos;
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 = 1;
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_add_entry (palette->entries, "Black", 0, 0, 0);
palette_calc_scrollbar (palette);
palette_draw_entries (palette);
palette_draw_current_entry (palette);
}
}
static void
palette_calc_scrollbar (PaletteP palette)
{
int n_entries;
int cur_entry_row;
int nrows;
int row_vsize;
int vsize;
int page_size;
int new_offset;
if (palette && palette->entries)
{
n_entries = palette->entries->n_colors;
nrows = n_entries / COLUMNS;
if (n_entries % COLUMNS)
nrows += 1;
row_vsize = SPACING + ((palette->color_area->requisition.height -
(SPACING * (ROWS + 1))) / ROWS);
vsize = row_vsize * nrows;
page_size = row_vsize * ROWS;
if (palette->color)
cur_entry_row = palette->color->position / COLUMNS;
else
cur_entry_row = 0;
new_offset = cur_entry_row * row_vsize;
/* scroll only if necessary */
if (new_offset < palette->scroll_offset)
{
palette->scroll_offset = new_offset;
}
else if (new_offset > palette->scroll_offset)
{
/* only scroll the minimum amount to bring the current color into view */
if ((palette->scroll_offset + page_size - row_vsize) < new_offset)
palette->scroll_offset = new_offset - (page_size - row_vsize);
}
/* sanity check to make sure the scrollbar offset is valid */
if (vsize > page_size)
if (palette->scroll_offset > (vsize - page_size))
palette->scroll_offset = vsize - page_size;
palette->sbar_data->value = palette->scroll_offset;
palette->sbar_data->upper = vsize;
palette->sbar_data->page_size = (page_size < vsize) ? page_size : vsize;
palette->sbar_data->page_increment = page_size;
palette->sbar_data->step_increment = row_vsize;
gtk_signal_emit_by_name (GTK_OBJECT (palette->sbar_data), "changed");
}
}
/* Procedural database entries */
/****************************/
/* PALETTE_GET_FOREGROUND */
static Argument *
palette_refresh_invoker (Argument *args)
{
/* FIXME: I've hardcoded success to be 1, because brushes_init() is a
* void function right now. It'd be nice if it returned a value at
* some future date, so we could tell if things blew up when reparsing
* the list (for whatever reason).
* - Seth "Yes, this is a kludge" Burgess
* <sjburges@ou.edu>
* -and shaemlessly stolen by Adrian Likins for use here...
*/
int success = TRUE ;
palette_free_palettes();
palette_init_palettes(FALSE);
return procedural_db_return_args (&palette_refresh_proc, success);
}
ProcRecord palette_refresh_proc =
{
"gimp_palette_refresh",
"Refreshes current palettes",
"This procedure incorporates all palettes currently in the users palette path. ",
"Adrian Likins <adrain@gimp.org>",
"Adrian Likins",
"1998",
PDB_INTERNAL,
/* input aarguments */
0,
NULL,
/* Output arguments */
0,
NULL,
/* Exec mehtos */
{ { palette_refresh_invoker } },
};
static Argument *
palette_get_foreground_invoker (Argument *args)
{
Argument *return_args;
unsigned char r, g, b;
unsigned char *col;
palette_get_foreground (&r, &g, &b);
col = (unsigned char *) g_malloc (3);
col[RED_PIX] = r;
col[GREEN_PIX] = g;
col[BLUE_PIX] = b;
return_args = procedural_db_return_args (&palette_get_foreground_proc, TRUE);
return_args[1].value.pdb_pointer = col;
return return_args;
}
/* The procedure definition */
ProcArg palette_get_foreground_args[] =
{
{ PDB_COLOR,
"foreground",
"the foreground color"
}
};
ProcRecord palette_get_foreground_proc =
{
"gimp_palette_get_foreground",
"Get the current GIMP foreground color",
"This procedure retrieves the current GIMP foreground color. The foreground color is used in a variety of tools such as paint tools, blending, and bucket fill.",
"Spencer Kimball & Peter Mattis",
"Spencer Kimball & Peter Mattis",
"1995-1996",
PDB_INTERNAL,
/* Input arguments */
0,
NULL,
/* Output arguments */
1,
palette_get_foreground_args,
/* Exec method */
{ { palette_get_foreground_invoker } },
};
/****************************/
/* PALETTE_GET_BACKGROUND */
static Argument *
palette_get_background_invoker (Argument *args)
{
Argument *return_args;
unsigned char r, g, b;
unsigned char *col;
palette_get_background (&r, &g, &b);
col = (unsigned char *) g_malloc (3);
col[RED_PIX] = r;
col[GREEN_PIX] = g;
col[BLUE_PIX] = b;
return_args = procedural_db_return_args (&palette_get_background_proc, TRUE);
return_args[1].value.pdb_pointer = col;
return return_args;
}
/* The procedure definition */
ProcArg palette_get_background_args[] =
{
{ PDB_COLOR,
"background",
"the background color"
}
};
ProcRecord palette_get_background_proc =
{
"gimp_palette_get_background",
"Get the current GIMP background color",
"This procedure retrieves the current GIMP background color. The background color is used in a variety of tools such as blending, erasing (with non-apha images), and image filling.",
"Spencer Kimball & Peter Mattis",
"Spencer Kimball & Peter Mattis",
"1995-1996",
PDB_INTERNAL,
/* Input arguments */
0,
NULL,
/* Output arguments */
1,
palette_get_background_args,
/* Exec method */
{ { palette_get_background_invoker } },
};
/****************************/
/* PALETTE_SET_FOREGROUND */
static Argument *
palette_set_foreground_invoker (Argument *args)
{
unsigned char *color;
int success;
success = TRUE;
if (success)
color = (unsigned char *) args[0].value.pdb_pointer;
if (success)
palette_set_foreground (color[RED_PIX], color[GREEN_PIX], color[BLUE_PIX]);
return procedural_db_return_args (&palette_set_foreground_proc, success);
}
/* The procedure definition */
ProcArg palette_set_foreground_args[] =
{
{ PDB_COLOR,
"foreground",
"the foreground color"
}
};
ProcRecord palette_set_foreground_proc =
{
"gimp_palette_set_foreground",
"Set the current GIMP foreground color",
"This procedure sets the current GIMP foreground color. After this is set, operations which use foreground such as paint tools, blending, and bucket fill will use the new value.",
"Spencer Kimball & Peter Mattis",
"Spencer Kimball & Peter Mattis",
"1995-1996",
PDB_INTERNAL,
/* Input arguments */
1,
palette_set_foreground_args,
/* Output arguments */
0,
NULL,
/* Exec method */
{ { palette_set_foreground_invoker } },
};
/****************************/
/* PALETTE_SET_BACKGROUND */
static Argument *
palette_set_background_invoker (Argument *args)
{
unsigned char *color;
int success;
success = TRUE;
if (success)
color = (unsigned char *) args[0].value.pdb_pointer;
if (success)
palette_set_background (color[RED_PIX], color[GREEN_PIX], color[BLUE_PIX]);
return procedural_db_return_args (&palette_set_background_proc, success);
}
/* The procedure definition */
ProcArg palette_set_background_args[] =
{
{ PDB_COLOR,
"background",
"the background color"
}
};
ProcRecord palette_set_background_proc =
{
"gimp_palette_set_background",
"Set the current GIMP background color",
"This procedure sets the current GIMP background color. After this is set, operations which use background such as blending, filling images, clearing, and erasing (in non-alpha images) will use the new value.",
"Spencer Kimball & Peter Mattis",
"Spencer Kimball & Peter Mattis",
"1995-1996",
PDB_INTERNAL,
/* Input arguments */
1,
palette_set_background_args,
/* Output arguments */
0,
NULL,
/* Exec method */
{ { palette_set_background_invoker } },
};
/*******************************/
/* PALETTE_SET_DEFAULT_COLORS */
static Argument *
palette_set_default_colors_invoker (Argument *args)
{
int success;
success = TRUE;
if (success)
palette_set_default_colors ();
return procedural_db_return_args (&palette_set_background_proc, success);
}
/* The procedure definition */
ProcRecord palette_set_default_colors_proc =
{
"gimp_palette_set_default_colors",
"Set the current GIMP foreground and background colors to black and white",
"This procedure sets the current GIMP foreground and background colors to their initial default values, black and white.",
"Spencer Kimball & Peter Mattis",
"Spencer Kimball & Peter Mattis",
"1995-1996",
PDB_INTERNAL,
/* Input arguments */
0,
NULL,
/* Output arguments */
0,
NULL,
/* Exec method */
{ { palette_set_default_colors_invoker } },
};
/************************/
/* PALETTE_SWAP_COLORS */
static Argument *
palette_swap_colors_invoker (Argument *args)
{
int success;
success = TRUE;
if (success)
palette_swap_colors ();
return procedural_db_return_args (&palette_swap_colors_proc, success);
}
/* The procedure definition */
ProcRecord palette_swap_colors_proc =
{
"gimp_palette_swap_colors",
"Swap the current GIMP foreground and background colors",
"This procedure swaps the current GIMP foreground and background colors, so that the new foreground color becomes the old background color and vice versa.",
"Spencer Kimball & Peter Mattis",
"Spencer Kimball & Peter Mattis",
"1995-1996",
PDB_INTERNAL,
/* Input arguments */
0,
NULL,
/* Output arguments */
0,
NULL,
/* Exec method */
{ { palette_swap_colors_invoker } },
};