/* 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 #include #include "appenv.h" #include "actionarea.h" #include "color_select.h" #include "colormaps.h" #include "errors.h" #include "gimprc.h" #include "session.h" #define XY_DEF_WIDTH 240 #define XY_DEF_HEIGHT 240 #define Z_DEF_WIDTH 15 #define Z_DEF_HEIGHT 240 #define COLOR_AREA_WIDTH 74 #define COLOR_AREA_HEIGHT 20 #define COLOR_AREA_MASK GDK_EXPOSURE_MASK | \ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | \ GDK_BUTTON1_MOTION_MASK | GDK_ENTER_NOTIFY_MASK typedef enum { HUE = 0, SATURATION, VALUE, RED, GREEN, BLUE, HUE_SATURATION, HUE_VALUE, SATURATION_VALUE, RED_GREEN, RED_BLUE, GREEN_BLUE } ColorSelectFillType; typedef enum { UPDATE_VALUES = 1 << 0, UPDATE_POS = 1 << 1, UPDATE_XY_COLOR = 1 << 2, UPDATE_Z_COLOR = 1 << 3, UPDATE_NEW_COLOR = 1 << 4, UPDATE_ORIG_COLOR = 1 << 5, UPDATE_CALLER = 1 << 6 } ColorSelectUpdateType; typedef struct _ColorSelectFill ColorSelectFill; typedef void (*ColorSelectFillUpdateProc) (ColorSelectFill *); struct _ColorSelectFill { unsigned char *buffer; int y; int width; int height; int *values; ColorSelectFillUpdateProc update; }; static void color_select_update (ColorSelectP, ColorSelectUpdateType); static void color_select_update_caller (ColorSelectP); static void color_select_update_values (ColorSelectP); static void color_select_update_rgb_values (ColorSelectP); static void color_select_update_hsv_values (ColorSelectP); static void color_select_update_pos (ColorSelectP); static void color_select_update_sliders (ColorSelectP, int); static void color_select_update_entries (ColorSelectP, int); static void color_select_update_colors (ColorSelectP, int); static void color_select_ok_callback (GtkWidget *, gpointer); static void color_select_cancel_callback (GtkWidget *, gpointer); static gint color_select_delete_callback (GtkWidget *, GdkEvent *, gpointer); static gint color_select_xy_expose (GtkWidget *, GdkEventExpose *, ColorSelectP); static gint color_select_xy_events (GtkWidget *, GdkEvent *, ColorSelectP); static gint color_select_z_expose (GtkWidget *, GdkEventExpose *, ColorSelectP); static gint color_select_z_events (GtkWidget *, GdkEvent *, ColorSelectP); static gint color_select_color_events (GtkWidget *, GdkEvent *); static void color_select_slider_update (GtkAdjustment *, gpointer); static void color_select_entry_update (GtkWidget *, gpointer); static void color_select_toggle_update (GtkWidget *, gpointer); static gint color_select_hex_entry_leave (GtkWidget *, GdkEvent *, gpointer); static void color_select_image_fill (GtkWidget *, ColorSelectFillType, int *); static void color_select_draw_z_marker (ColorSelectP, int); static void color_select_draw_xy_marker (ColorSelectP, int); static void color_select_update_red (ColorSelectFill *); static void color_select_update_green (ColorSelectFill *); static void color_select_update_blue (ColorSelectFill *); static void color_select_update_hue (ColorSelectFill *); static void color_select_update_saturation (ColorSelectFill *); static void color_select_update_value (ColorSelectFill *); static void color_select_update_red_green (ColorSelectFill *); static void color_select_update_red_blue (ColorSelectFill *); static void color_select_update_green_blue (ColorSelectFill *); static void color_select_update_hue_saturation (ColorSelectFill *); static void color_select_update_hue_value (ColorSelectFill *); static void color_select_update_saturation_value (ColorSelectFill *); static ColorSelectFillUpdateProc update_procs[] = { color_select_update_hue, color_select_update_saturation, color_select_update_value, color_select_update_red, color_select_update_green, color_select_update_blue, color_select_update_hue_saturation, color_select_update_hue_value, color_select_update_saturation_value, color_select_update_red_green, color_select_update_red_blue, color_select_update_green_blue, }; static ActionAreaItem action_items[2] = { { "OK", color_select_ok_callback, NULL, NULL }, { "Cancel", color_select_cancel_callback, NULL, NULL }, }; ColorSelectP color_select_new (int r, int g, int b, ColorSelectCallback callback, void *client_data, int wants_updates) { /* static char *toggle_titles[6] = { "Hue", "Saturation", "Value", "Red", "Green", "Blue" }; */ static char *toggle_titles[6] = { "H", "S", "V", "R", "G", "B" }; static gfloat slider_max_vals[6] = { 360, 100, 100, 255, 255, 255 }; static gfloat slider_incs[6] = { 0.1, 0.1, 0.1, 1.0, 1.0, 1.0 }; ColorSelectP csp; GtkWidget *main_vbox; GtkWidget *main_hbox; GtkWidget *xy_frame; GtkWidget *z_frame; GtkWidget *colors_frame; GtkWidget *colors_hbox; GtkWidget *right_vbox; GtkWidget *table; GtkWidget *slider; GtkWidget *hex_hbox; GtkWidget *label; GSList *group; char buffer[16]; int i; csp = g_malloc (sizeof (_ColorSelect)); csp->callback = callback; csp->client_data = client_data; csp->z_color_fill = HUE; csp->xy_color_fill = SATURATION_VALUE; csp->gc = NULL; csp->wants_updates = wants_updates; csp->values[RED] = csp->orig_values[0] = r; csp->values[GREEN] = csp->orig_values[1] = g; csp->values[BLUE] = csp->orig_values[2] = b; color_select_update_hsv_values (csp); color_select_update_pos (csp); csp->shell = gtk_dialog_new (); gtk_window_set_wmclass (GTK_WINDOW (csp->shell), "color_selection", "Gimp"); gtk_window_set_title (GTK_WINDOW (csp->shell), "Color Selection"); gtk_window_set_policy (GTK_WINDOW (csp->shell), FALSE, FALSE, FALSE); /* handle the wm close signal */ gtk_signal_connect (GTK_OBJECT (csp->shell), "delete_event", (GtkSignalFunc) color_select_delete_callback, csp); main_vbox = gtk_vbox_new (FALSE, 2); gtk_container_border_width (GTK_CONTAINER (main_vbox), 2); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (csp->shell)->vbox), main_vbox, TRUE, TRUE, 0); gtk_widget_show (main_vbox); main_hbox = gtk_hbox_new (FALSE, 2); gtk_container_border_width (GTK_CONTAINER (main_hbox), 0); gtk_box_pack_start (GTK_BOX (main_vbox), main_hbox, TRUE, TRUE, 2); gtk_widget_show (main_hbox); xy_frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (xy_frame), GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX (main_hbox), xy_frame, FALSE, FALSE, 2); gtk_widget_show (xy_frame); csp->xy_color = gtk_preview_new (GTK_PREVIEW_COLOR); gtk_preview_size (GTK_PREVIEW (csp->xy_color), XY_DEF_WIDTH, XY_DEF_HEIGHT); gtk_widget_set_events (csp->xy_color, COLOR_AREA_MASK); gtk_signal_connect_after (GTK_OBJECT (csp->xy_color), "expose_event", (GtkSignalFunc) color_select_xy_expose, csp); gtk_signal_connect (GTK_OBJECT (csp->xy_color), "event", (GtkSignalFunc) color_select_xy_events, csp); gtk_container_add (GTK_CONTAINER (xy_frame), csp->xy_color); gtk_widget_show (csp->xy_color); z_frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (z_frame), GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX (main_hbox), z_frame, FALSE, FALSE, 2); gtk_widget_show (z_frame); csp->z_color = gtk_preview_new (GTK_PREVIEW_COLOR); gtk_preview_size (GTK_PREVIEW (csp->z_color), Z_DEF_WIDTH, Z_DEF_HEIGHT); gtk_widget_set_events (csp->z_color, COLOR_AREA_MASK); gtk_signal_connect_after (GTK_OBJECT (csp->z_color), "expose_event", (GtkSignalFunc) color_select_z_expose, csp); gtk_signal_connect (GTK_OBJECT (csp->z_color), "event", (GtkSignalFunc) color_select_z_events, csp); gtk_container_add (GTK_CONTAINER (z_frame), csp->z_color); gtk_widget_show (csp->z_color); /* The right vertical box with old/new color area and color space sliders */ right_vbox = gtk_vbox_new (FALSE, 2); gtk_container_border_width (GTK_CONTAINER (right_vbox), 0); gtk_box_pack_start (GTK_BOX (main_hbox), right_vbox, TRUE, TRUE, 0); gtk_widget_show (right_vbox); /* The old/new color area */ colors_frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (colors_frame), GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX (right_vbox), colors_frame, FALSE, FALSE, 0); gtk_widget_show (colors_frame); colors_hbox = gtk_hbox_new (TRUE, 2); gtk_container_add (GTK_CONTAINER (colors_frame), colors_hbox); gtk_widget_show (colors_hbox); csp->new_color = gtk_drawing_area_new (); gtk_drawing_area_size (GTK_DRAWING_AREA (csp->new_color), COLOR_AREA_WIDTH, COLOR_AREA_HEIGHT); gtk_widget_set_events (csp->new_color, GDK_EXPOSURE_MASK); gtk_signal_connect (GTK_OBJECT (csp->new_color), "event", (GtkSignalFunc) color_select_color_events, csp); gtk_object_set_user_data (GTK_OBJECT (csp->new_color), csp); gtk_box_pack_start (GTK_BOX (colors_hbox), csp->new_color, TRUE, TRUE, 0); gtk_widget_show (csp->new_color); csp->orig_color = gtk_drawing_area_new (); gtk_drawing_area_size (GTK_DRAWING_AREA (csp->orig_color), COLOR_AREA_WIDTH, COLOR_AREA_HEIGHT); gtk_widget_set_events (csp->orig_color, GDK_EXPOSURE_MASK); gtk_signal_connect (GTK_OBJECT (csp->orig_color), "event", (GtkSignalFunc) color_select_color_events, csp); gtk_object_set_user_data (GTK_OBJECT (csp->orig_color), csp); gtk_box_pack_start (GTK_BOX (colors_hbox), csp->orig_color, TRUE, TRUE, 0); gtk_widget_show (csp->orig_color); /* The color space sliders, toggle buttons and entries */ table = gtk_table_new (6, 3, FALSE); gtk_table_set_row_spacings (GTK_TABLE (table), 3); gtk_table_set_col_spacings (GTK_TABLE (table), 3); gtk_container_border_width (GTK_CONTAINER (table), 2); gtk_box_pack_start (GTK_BOX (right_vbox), table, TRUE, TRUE, 0); gtk_widget_show (table); group = NULL; for (i = 0; i < 6; i++) { csp->toggles[i] = gtk_radio_button_new_with_label (group, toggle_titles[i]); group = gtk_radio_button_group (GTK_RADIO_BUTTON (csp->toggles[i])); gtk_table_attach (GTK_TABLE (table), csp->toggles[i], 0, 1, i, i+1, GTK_FILL, GTK_EXPAND, 0, 0); gtk_signal_connect (GTK_OBJECT (csp->toggles[i]), "toggled", (GtkSignalFunc) color_select_toggle_update, csp); gtk_widget_show (csp->toggles[i]); csp->slider_data[i] = GTK_ADJUSTMENT (gtk_adjustment_new (csp->values[i], 0.0, slider_max_vals[i], slider_incs[i], 1.0, 0.0)); slider = gtk_hscale_new (csp->slider_data[i]); gtk_table_attach (GTK_TABLE (table), slider, 1, 2, i, i+1, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0); gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP); gtk_scale_set_draw_value (GTK_SCALE (slider), FALSE); gtk_signal_connect (GTK_OBJECT (csp->slider_data[i]), "value_changed", (GtkSignalFunc) color_select_slider_update, csp); gtk_widget_show (slider); csp->entries[i] = gtk_entry_new (); sprintf (buffer, "%d", csp->values[i]); gtk_entry_set_text (GTK_ENTRY (csp->entries[i]), buffer); gtk_widget_set_usize (GTK_WIDGET (csp->entries[i]), 40, 0); gtk_table_attach (GTK_TABLE (table), csp->entries[i], 2, 3, i, i+1, GTK_FILL, GTK_EXPAND, 0, 0); gtk_signal_connect (GTK_OBJECT (csp->entries[i]), "changed", (GtkSignalFunc) color_select_entry_update, csp); gtk_widget_show (csp->entries[i]); } /* The hex triplet entry */ hex_hbox = gtk_hbox_new (FALSE, 3); gtk_box_pack_start (GTK_BOX (right_vbox), hex_hbox, FALSE, FALSE, 0); gtk_widget_show (hex_hbox); csp->hex_entry = gtk_entry_new (); sprintf(buffer, "#%.2x%.2x%.2x", r, g, b); gtk_entry_set_text (GTK_ENTRY (csp->hex_entry), buffer); gtk_widget_set_usize (GTK_WIDGET (csp->hex_entry), 55, 0); gtk_box_pack_end (GTK_BOX (hex_hbox), csp->hex_entry, FALSE, FALSE, 2); gtk_signal_connect (GTK_OBJECT (csp->hex_entry), "focus_out_event", (GtkSignalFunc) color_select_hex_entry_leave, csp); gtk_widget_show (csp->hex_entry); label = gtk_label_new ("Hex Triplet:"); gtk_box_pack_end (GTK_BOX (hex_hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); /* The action area */ action_items[0].user_data = csp; action_items[1].user_data = csp; if (csp->wants_updates) { action_items[0].label = "Close"; action_items[1].label = "Revert to Old Color"; } else { action_items[0].label = "OK"; action_items[1].label = "Cancel"; } build_action_area (GTK_DIALOG (csp->shell), action_items, 2, 0); color_select_image_fill (csp->z_color, csp->z_color_fill, csp->values); color_select_image_fill (csp->xy_color, csp->xy_color_fill, csp->values); gtk_widget_show (csp->shell); return csp; } void color_select_show (ColorSelectP csp) { if (csp) gtk_widget_show (csp->shell); } void color_select_hide (ColorSelectP csp) { if (csp) gtk_widget_hide (csp->shell); } void color_select_free (ColorSelectP csp) { if (csp) { gtk_widget_destroy (csp->shell); gdk_gc_destroy (csp->gc); g_free (csp); } } void color_select_set_color (ColorSelectP csp, int r, int g, int b, int set_current) { if (csp) { csp->orig_values[0] = r; csp->orig_values[1] = g; csp->orig_values[2] = b; color_select_update_colors (csp, 1); if (set_current) { csp->values[RED] = r; csp->values[GREEN] = g; csp->values[BLUE] = b; color_select_update_hsv_values (csp); color_select_update_pos (csp); color_select_update_sliders (csp, -1); color_select_update_entries (csp, -1); color_select_update_colors (csp, 0); color_select_update (csp, UPDATE_Z_COLOR); color_select_update (csp, UPDATE_XY_COLOR); } } } static void color_select_update (ColorSelectP csp, ColorSelectUpdateType update) { if (csp) { if (update & UPDATE_POS) color_select_update_pos (csp); if (update & UPDATE_VALUES) { color_select_update_values (csp); color_select_update_sliders (csp, -1); color_select_update_entries (csp, -1); if (!(update & UPDATE_NEW_COLOR)) color_select_update_colors (csp, 0); } if (update & UPDATE_XY_COLOR) { color_select_image_fill (csp->xy_color, csp->xy_color_fill, csp->values); gtk_widget_draw (csp->xy_color, NULL); } if (update & UPDATE_Z_COLOR) { color_select_image_fill (csp->z_color, csp->z_color_fill, csp->values); gtk_widget_draw (csp->z_color, NULL); } if (update & UPDATE_NEW_COLOR) color_select_update_colors (csp, 0); if (update & UPDATE_ORIG_COLOR) color_select_update_colors (csp, 1); /*if (update & UPDATE_CALLER)*/ color_select_update_caller (csp); } } static void color_select_update_caller (ColorSelectP csp) { if (csp && csp->wants_updates && csp->callback) { (* csp->callback) (csp->values[RED], csp->values[GREEN], csp->values[BLUE], COLOR_SELECT_UPDATE, csp->client_data); } } static void color_select_update_values (ColorSelectP csp) { if (csp) { switch (csp->z_color_fill) { case RED: csp->values[BLUE] = csp->pos[0]; csp->values[GREEN] = csp->pos[1]; csp->values[RED] = csp->pos[2]; break; case GREEN: csp->values[BLUE] = csp->pos[0]; csp->values[RED] = csp->pos[1]; csp->values[GREEN] = csp->pos[2]; break; case BLUE: csp->values[GREEN] = csp->pos[0]; csp->values[RED] = csp->pos[1]; csp->values[BLUE] = csp->pos[2]; break; case HUE: csp->values[VALUE] = csp->pos[0] * 100 / 255; csp->values[SATURATION] = csp->pos[1] * 100 / 255; csp->values[HUE] = csp->pos[2] * 360 / 255; break; case SATURATION: csp->values[VALUE] = csp->pos[0] * 100 / 255; csp->values[HUE] = csp->pos[1] * 360 / 255; csp->values[SATURATION] = csp->pos[2] * 100 / 255; break; case VALUE: csp->values[SATURATION] = csp->pos[0] * 100 / 255; csp->values[HUE] = csp->pos[1] * 360 / 255; csp->values[VALUE] = csp->pos[2] * 100 / 255; break; } switch (csp->z_color_fill) { case RED: case GREEN: case BLUE: color_select_update_hsv_values (csp); break; case HUE: case SATURATION: case VALUE: color_select_update_rgb_values (csp); break; } } } static void color_select_update_rgb_values (ColorSelectP csp) { float h, s, v; float f, p, q, t; if (csp) { h = csp->values[HUE]; s = csp->values[SATURATION] / 100.0; v = csp->values[VALUE] / 100.0; if (s == 0) { csp->values[RED] = v * 255; csp->values[GREEN] = v * 255; csp->values[BLUE] = v * 255; } else { if (h == 360) h = 0; h /= 60; f = h - (int) h; p = v * (1 - s); q = v * (1 - (s * f)); t = v * (1 - (s * (1 - f))); switch ((int) h) { case 0: csp->values[RED] = v * 255; csp->values[GREEN] = t * 255; csp->values[BLUE] = p * 255; break; case 1: csp->values[RED] = q * 255; csp->values[GREEN] = v * 255; csp->values[BLUE] = p * 255; break; case 2: csp->values[RED] = p * 255; csp->values[GREEN] = v * 255; csp->values[BLUE] = t * 255; break; case 3: csp->values[RED] = p * 255; csp->values[GREEN] = q * 255; csp->values[BLUE] = v * 255; break; case 4: csp->values[RED] = t * 255; csp->values[GREEN] = p * 255; csp->values[BLUE] = v * 255; break; case 5: csp->values[RED] = v * 255; csp->values[GREEN] = p * 255; csp->values[BLUE] = q * 255; break; } } } } static void color_select_update_hsv_values (ColorSelectP csp) { int r, g, b; float h, s, v; int min, max; int delta; if (csp) { r = csp->values[RED]; g = csp->values[GREEN]; b = csp->values[BLUE]; if (r > g) { if (r > b) max = r; else max = b; if (g < b) min = g; else min = b; } else { if (g > b) max = g; else max = b; if (r < b) min = r; else min = b; } v = max; if (max != 0) s = (max - min) / (float) max; else s = 0; if (s == 0) h = 0; else { h = 0; delta = max - min; if (r == max) h = (g - b) / (float) delta; else if (g == max) h = 2 + (b - r) / (float) delta; else if (b == max) h = 4 + (r - g) / (float) delta; h *= 60; if (h < 0) h += 360; } csp->values[HUE] = h; csp->values[SATURATION] = s * 100; csp->values[VALUE] = v * 100 / 255; } } static void color_select_update_pos (ColorSelectP csp) { if (csp) { switch (csp->z_color_fill) { case RED: csp->pos[0] = csp->values[BLUE]; csp->pos[1] = csp->values[GREEN]; csp->pos[2] = csp->values[RED]; break; case GREEN: csp->pos[0] = csp->values[BLUE]; csp->pos[1] = csp->values[RED]; csp->pos[2] = csp->values[GREEN]; break; case BLUE: csp->pos[0] = csp->values[GREEN]; csp->pos[1] = csp->values[RED]; csp->pos[2] = csp->values[BLUE]; break; case HUE: csp->pos[0] = csp->values[VALUE] * 255 / 100; csp->pos[1] = csp->values[SATURATION] * 255 / 100; csp->pos[2] = csp->values[HUE] * 255 / 360; break; case SATURATION: csp->pos[0] = csp->values[VALUE] * 255 / 100; csp->pos[1] = csp->values[HUE] * 255 / 360; csp->pos[2] = csp->values[SATURATION] * 255 / 100; break; case VALUE: csp->pos[0] = csp->values[SATURATION] * 255 / 100; csp->pos[1] = csp->values[HUE] * 255 / 360; csp->pos[2] = csp->values[VALUE] * 255 / 100; break; } } } static void color_select_update_sliders (ColorSelectP csp, int skip) { int i; if (csp) { for (i = 0; i < 6; i++) if (i != skip) { csp->slider_data[i]->value = (gfloat) csp->values[i]; gtk_signal_handler_block_by_data (GTK_OBJECT (csp->slider_data[i]), csp); gtk_signal_emit_by_name (GTK_OBJECT (csp->slider_data[i]), "value_changed"); gtk_signal_handler_unblock_by_data (GTK_OBJECT (csp->slider_data[i]), csp); } } } static void color_select_update_entries (ColorSelectP csp, int skip) { char buffer[16]; int i; if (csp) { for (i = 0; i < 6; i++) if (i != skip) { sprintf (buffer, "%d", csp->values[i]); gtk_signal_handler_block_by_data (GTK_OBJECT (csp->entries[i]), csp); gtk_entry_set_text (GTK_ENTRY (csp->entries[i]), buffer); gtk_signal_handler_unblock_by_data (GTK_OBJECT (csp->entries[i]), csp); } sprintf(buffer, "#%.2x%.2x%.2x", csp->values[RED], csp->values[GREEN], csp->values[BLUE]); gtk_entry_set_text (GTK_ENTRY (csp->hex_entry), buffer); } } static void color_select_update_colors (ColorSelectP csp, int which) { GdkWindow *window; GdkColor color; int red, green, blue; int width, height; if (csp) { if (which) { window = csp->orig_color->window; color.pixel = old_color_pixel; red = csp->orig_values[0]; green = csp->orig_values[1]; blue = csp->orig_values[2]; } else { window = csp->new_color->window; color.pixel = new_color_pixel; red = csp->values[RED]; green = csp->values[GREEN]; blue = csp->values[BLUE]; } gdk_window_get_size (window, &width, &height); store_color (&color.pixel, red, green, blue); if (csp->gc) { gdk_gc_set_foreground (csp->gc, &color); gdk_draw_rectangle (window, csp->gc, 1, 0, 0, width, height); } } } static void color_select_ok_callback (GtkWidget *w, gpointer client_data) { ColorSelectP csp; csp = (ColorSelectP) client_data; if (csp) { if (csp->callback) (* csp->callback) (csp->values[RED], csp->values[GREEN], csp->values[BLUE], COLOR_SELECT_OK, csp->client_data); } } static gint color_select_delete_callback (GtkWidget *w, GdkEvent *e, gpointer client_data) { color_select_cancel_callback (w, client_data); return TRUE; } static void color_select_cancel_callback (GtkWidget *w, gpointer client_data) { ColorSelectP csp; csp = (ColorSelectP) client_data; if (csp) { if (csp->callback) (* csp->callback) (csp->orig_values[0], csp->orig_values[1], csp->orig_values[2], COLOR_SELECT_CANCEL, csp->client_data); } } static gint color_select_xy_expose (GtkWidget *widget, GdkEventExpose *event, ColorSelectP csp) { if (!csp->gc) csp->gc = gdk_gc_new (widget->window); color_select_draw_xy_marker (csp, 1); return FALSE; } static gint color_select_xy_events (GtkWidget *widget, GdkEvent *event, ColorSelectP csp) { GdkEventButton *bevent; GdkEventMotion *mevent; int tx, ty; switch (event->type) { case GDK_BUTTON_PRESS: bevent = (GdkEventButton *) event; color_select_draw_xy_marker (csp, 1); csp->pos[0] = (bevent->x * 255) / (XY_DEF_WIDTH - 1); csp->pos[1] = 255 - (bevent->y * 255) / (XY_DEF_HEIGHT - 1); if (csp->pos[0] < 0) csp->pos[0] = 0; if (csp->pos[0] > 255) csp->pos[0] = 255; if (csp->pos[1] < 0) csp->pos[1] = 0; if (csp->pos[1] > 255) csp->pos[1] = 255; gdk_pointer_grab (csp->xy_color->window, FALSE, GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, NULL, bevent->time); color_select_draw_xy_marker (csp, 1); color_select_update (csp, UPDATE_VALUES); break; case GDK_BUTTON_RELEASE: bevent = (GdkEventButton *) event; color_select_draw_xy_marker (csp, 1); csp->pos[0] = (bevent->x * 255) / (XY_DEF_WIDTH - 1); csp->pos[1] = 255 - (bevent->y * 255) / (XY_DEF_HEIGHT - 1); if (csp->pos[0] < 0) csp->pos[0] = 0; if (csp->pos[0] > 255) csp->pos[0] = 255; if (csp->pos[1] < 0) csp->pos[1] = 0; if (csp->pos[1] > 255) csp->pos[1] = 255; gdk_pointer_ungrab (bevent->time); color_select_draw_xy_marker (csp, 1); color_select_update (csp, UPDATE_VALUES); break; case GDK_MOTION_NOTIFY: mevent = (GdkEventMotion *) event; if (mevent->is_hint) { gdk_window_get_pointer (widget->window, &tx, &ty, NULL); mevent->x = tx; mevent->y = ty; } color_select_draw_xy_marker (csp, 1); csp->pos[0] = (mevent->x * 255) / (XY_DEF_WIDTH - 1); csp->pos[1] = 255 - (mevent->y * 255) / (XY_DEF_HEIGHT - 1); if (csp->pos[0] < 0) csp->pos[0] = 0; if (csp->pos[0] > 255) csp->pos[0] = 255; if (csp->pos[1] < 0) csp->pos[1] = 0; if (csp->pos[1] > 255) csp->pos[1] = 255; color_select_draw_xy_marker (csp, 1); color_select_update (csp, UPDATE_VALUES); break; default: break; } return FALSE; } static gint color_select_z_expose (GtkWidget *widget, GdkEventExpose *event, ColorSelectP csp) { if (!csp->gc) csp->gc = gdk_gc_new (widget->window); color_select_draw_z_marker (csp, 1); return FALSE; } static gint color_select_z_events (GtkWidget *widget, GdkEvent *event, ColorSelectP csp) { GdkEventButton *bevent; GdkEventMotion *mevent; int tx, ty; switch (event->type) { case GDK_BUTTON_PRESS: bevent = (GdkEventButton *) event; color_select_draw_z_marker (csp, 1); csp->pos[2] = 255 - (bevent->y * 255) / (Z_DEF_HEIGHT - 1); if (csp->pos[2] < 0) csp->pos[2] = 0; if (csp->pos[2] > 255) csp->pos[2] = 255; gdk_pointer_grab (csp->z_color->window, FALSE, GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, NULL, bevent->time); color_select_draw_z_marker (csp, 1); color_select_update (csp, UPDATE_VALUES); break; case GDK_BUTTON_RELEASE: bevent = (GdkEventButton *) event; color_select_draw_z_marker (csp, 1); csp->pos[2] = 255 - (bevent->y * 255) / (Z_DEF_HEIGHT - 1); if (csp->pos[2] < 0) csp->pos[2] = 0; if (csp->pos[2] > 255) csp->pos[2] = 255; gdk_pointer_ungrab (bevent->time); color_select_draw_z_marker (csp, 1); color_select_update (csp, UPDATE_VALUES | UPDATE_XY_COLOR); break; case GDK_MOTION_NOTIFY: mevent = (GdkEventMotion *) event; if (mevent->is_hint) { gdk_window_get_pointer (widget->window, &tx, &ty, NULL); mevent->x = tx; mevent->y = ty; } color_select_draw_z_marker (csp, 1); csp->pos[2] = 255 - (mevent->y * 255) / (Z_DEF_HEIGHT - 1); if (csp->pos[2] < 0) csp->pos[2] = 0; if (csp->pos[2] > 255) csp->pos[2] = 255; color_select_draw_z_marker (csp, 1); color_select_update (csp, UPDATE_VALUES); break; default: break; } return FALSE; } static gint color_select_color_events (GtkWidget *widget, GdkEvent *event) { ColorSelectP csp; csp = (ColorSelectP) gtk_object_get_user_data (GTK_OBJECT (widget)); if (!csp) return FALSE; switch (event->type) { case GDK_EXPOSE: if (!csp->gc) csp->gc = gdk_gc_new (widget->window); if (widget == csp->new_color) color_select_update (csp, UPDATE_NEW_COLOR); else if (widget == csp->orig_color) color_select_update (csp, UPDATE_ORIG_COLOR); break; default: break; } return FALSE; } static void color_select_slider_update (GtkAdjustment *adjustment, gpointer data) { ColorSelectP csp; int old_values[6]; int update_z_marker; int update_xy_marker; int i, j; csp = (ColorSelectP) data; if (csp) { for (i = 0; i < 6; i++) if (csp->slider_data[i] == adjustment) break; for (j = 0; j < 6; j++) old_values[j] = csp->values[j]; csp->values[i] = (int) adjustment->value; if ((i >= HUE) && (i <= VALUE)) color_select_update_rgb_values (csp); else if ((i >= RED) && (i <= BLUE)) color_select_update_hsv_values (csp); color_select_update_sliders (csp, i); color_select_update_entries (csp, -1); update_z_marker = 0; update_xy_marker = 0; for (j = 0; j < 6; j++) { if (j == csp->z_color_fill) { if (old_values[j] != csp->values[j]) update_z_marker = 1; } else { if (old_values[j] != csp->values[j]) update_xy_marker = 1; } } if (update_z_marker) { color_select_draw_z_marker (csp, 1); color_select_update (csp, UPDATE_POS | UPDATE_XY_COLOR); color_select_draw_z_marker (csp, 1); } else { if (update_z_marker) color_select_draw_z_marker (csp, 1); if (update_xy_marker) color_select_draw_xy_marker (csp, 1); color_select_update (csp, UPDATE_POS); if (update_z_marker) color_select_draw_z_marker (csp, 1); if (update_xy_marker) color_select_draw_xy_marker (csp, 1); } color_select_update (csp, UPDATE_NEW_COLOR); } } static void color_select_entry_update (GtkWidget *w, gpointer data) { ColorSelectP csp; int old_values[6]; int update_z_marker; int update_xy_marker; int i, j; csp = (ColorSelectP) data; if (csp) { for (i = 0; i < 6; i++) if (csp->entries[i] == w) break; for (j = 0; j < 6; j++) old_values[j] = csp->values[j]; csp->values[i] = atoi (gtk_entry_get_text (GTK_ENTRY (csp->entries[i]))); if (csp->values[i] == old_values[i]) return; if ((i >= HUE) && (i <= VALUE)) color_select_update_rgb_values (csp); else if ((i >= RED) && (i <= BLUE)) color_select_update_hsv_values (csp); color_select_update_entries (csp, i); color_select_update_sliders (csp, -1); update_z_marker = 0; update_xy_marker = 0; for (j = 0; j < 6; j++) { if (j == csp->z_color_fill) { if (old_values[j] != csp->values[j]) update_z_marker = 1; } else { if (old_values[j] != csp->values[j]) update_xy_marker = 1; } } if (update_z_marker) { color_select_draw_z_marker (csp, 1); color_select_update (csp, UPDATE_POS | UPDATE_XY_COLOR); color_select_draw_z_marker (csp, 1); } else { if (update_z_marker) color_select_draw_z_marker (csp, 1); if (update_xy_marker) color_select_draw_xy_marker (csp, 1); color_select_update (csp, UPDATE_POS); if (update_z_marker) color_select_draw_z_marker (csp, 1); if (update_xy_marker) color_select_draw_xy_marker (csp, 1); } color_select_update (csp, UPDATE_NEW_COLOR); } } static void color_select_toggle_update (GtkWidget *w, gpointer data) { ColorSelectP csp; ColorSelectFillType type = HUE; int i; if (!GTK_TOGGLE_BUTTON (w)->active) return; csp = (ColorSelectP) data; if (csp) { for (i = 0; i < 6; i++) if (w == csp->toggles[i]) type = (ColorSelectFillType) i; switch (type) { case HUE: csp->z_color_fill = HUE; csp->xy_color_fill = SATURATION_VALUE; break; case SATURATION: csp->z_color_fill = SATURATION; csp->xy_color_fill = HUE_VALUE; break; case VALUE: csp->z_color_fill = VALUE; csp->xy_color_fill = HUE_SATURATION; break; case RED: csp->z_color_fill = RED; csp->xy_color_fill = GREEN_BLUE; break; case GREEN: csp->z_color_fill = GREEN; csp->xy_color_fill = RED_BLUE; break; case BLUE: csp->z_color_fill = BLUE; csp->xy_color_fill = RED_GREEN; break; default: break; } color_select_update (csp, UPDATE_POS); color_select_update (csp, UPDATE_Z_COLOR | UPDATE_XY_COLOR); } } static gint color_select_hex_entry_leave (GtkWidget *w, GdkEvent *event, gpointer data) { ColorSelectP csp; gchar buffer[8]; gchar *hex_color; guint hex_rgb; csp = (ColorSelectP) data; if (csp) { hex_color = g_strdup (gtk_entry_get_text (GTK_ENTRY (csp->hex_entry))); sprintf(buffer, "#%.2x%.2x%.2x", csp->values[RED], csp->values[GREEN], csp->values[BLUE]); if ((strlen (hex_color) == 7) && (g_strcasecmp (buffer, hex_color) != 0)) { if ((sscanf (hex_color, "#%x", &hex_rgb) == 1) && (hex_rgb < (1 << 24))) color_select_set_color (csp, (hex_rgb & 0xff0000) >> 16, (hex_rgb & 0x00ff00) >> 8, hex_rgb & 0x0000ff, TRUE); } g_free (hex_color); } return FALSE; } static void color_select_image_fill (GtkWidget *preview, ColorSelectFillType type, int *values) { ColorSelectFill csf; int height; csf.buffer = g_malloc (preview->requisition.width * 3); csf.update = update_procs[type]; csf.y = -1; csf.width = preview->requisition.width; csf.height = preview->requisition.height; csf.values = values; height = csf.height; if (height > 0) while (height--) { (* csf.update) (&csf); gtk_preview_draw_row (GTK_PREVIEW (preview), csf.buffer, 0, csf.y, csf.width); } g_free (csf.buffer); } static void color_select_draw_z_marker (ColorSelectP csp, int update) { int width; int y; if (csp->gc) { y = (Z_DEF_HEIGHT - 1) - ((Z_DEF_HEIGHT - 1) * csp->pos[2]) / 255; width = csp->z_color->requisition.width; if (width <= 0) return; gdk_gc_set_function (csp->gc, GDK_INVERT); gdk_draw_line (csp->z_color->window, csp->gc, 0, y, width, y); gdk_gc_set_function (csp->gc, GDK_COPY); } } static void color_select_draw_xy_marker (ColorSelectP csp, int update) { int width; int height; int x, y; if (csp->gc) { x = ((XY_DEF_WIDTH - 1) * csp->pos[0]) / 255; y = (XY_DEF_HEIGHT - 1) - ((XY_DEF_HEIGHT - 1) * csp->pos[1]) / 255; width = csp->xy_color->requisition.width; height = csp->xy_color->requisition.height; if ((width <= 0) || (height <= 0)) return; gdk_gc_set_function (csp->gc, GDK_INVERT); gdk_draw_line (csp->xy_color->window, csp->gc, 0, y, width, y); gdk_draw_line (csp->xy_color->window, csp->gc, x, 0, x, height); gdk_gc_set_function (csp->gc, GDK_COPY); } } static void color_select_update_red (ColorSelectFill *csf) { unsigned char *p; int i, r; p = csf->buffer; csf->y += 1; r = (csf->height - csf->y + 1) * 255 / csf->height; if (r < 0) r = 0; if (r > 255) r = 255; for (i = 0; i < csf->width; i++) { *p++ = r; *p++ = 0; *p++ = 0; } } static void color_select_update_green (ColorSelectFill *csf) { unsigned char *p; int i, g; p = csf->buffer; csf->y += 1; g = (csf->height - csf->y + 1) * 255 / csf->height; if (g < 0) g = 0; if (g > 255) g = 255; for (i = 0; i < csf->width; i++) { *p++ = 0; *p++ = g; *p++ = 0; } } static void color_select_update_blue (ColorSelectFill *csf) { unsigned char *p; int i, b; p = csf->buffer; csf->y += 1; b = (csf->height - csf->y + 1) * 255 / csf->height; if (b < 0) b = 0; if (b > 255) b = 255; for (i = 0; i < csf->width; i++) { *p++ = 0; *p++ = 0; *p++ = b; } } static void color_select_update_hue (ColorSelectFill *csf) { unsigned char *p; float h, f; int r, g, b; int i; p = csf->buffer; csf->y += 1; h = csf->y * 360 / csf->height; h = 360 - h; if (h < 0) h = 0; if (h >= 360) h = 0; h /= 60; f = (h - (int) h) * 255; r = g = b = 0; switch ((int) h) { case 0: r = 255; g = f; b = 0; break; case 1: r = 255 - f; g = 255; b = 0; break; case 2: r = 0; g = 255; b = f; break; case 3: r = 0; g = 255 - f; b = 255; break; case 4: r = f; g = 0; b = 255; break; case 5: r = 255; g = 0; b = 255 - f; break; } for (i = 0; i < csf->width; i++) { *p++ = r; *p++ = g; *p++ = b; } } static void color_select_update_saturation (ColorSelectFill *csf) { unsigned char *p; int s; int i; p = csf->buffer; csf->y += 1; s = csf->y * 255 / csf->height; if (s < 0) s = 0; if (s > 255) s = 255; s = 255 - s; for (i = 0; i < csf->width; i++) { *p++ = s; *p++ = s; *p++ = s; } } static void color_select_update_value (ColorSelectFill *csf) { unsigned char *p; int v; int i; p = csf->buffer; csf->y += 1; v = csf->y * 255 / csf->height; if (v < 0) v = 0; if (v > 255) v = 255; v = 255 - v; for (i = 0; i < csf->width; i++) { *p++ = v; *p++ = v; *p++ = v; } } static void color_select_update_red_green (ColorSelectFill *csf) { unsigned char *p; int i, r, b; float g, dg; p = csf->buffer; csf->y += 1; b = csf->values[BLUE]; r = (csf->height - csf->y + 1) * 255 / csf->height; if (r < 0) r = 0; if (r > 255) r = 255; g = 0; dg = 255.0 / csf->width; for (i = 0; i < csf->width; i++) { *p++ = r; *p++ = g; *p++ = b; g += dg; } } static void color_select_update_red_blue (ColorSelectFill *csf) { unsigned char *p; int i, r, g; float b, db; p = csf->buffer; csf->y += 1; g = csf->values[GREEN]; r = (csf->height - csf->y + 1) * 255 / csf->height; if (r < 0) r = 0; if (r > 255) r = 255; b = 0; db = 255.0 / csf->width; for (i = 0; i < csf->width; i++) { *p++ = r; *p++ = g; *p++ = b; b += db; } } static void color_select_update_green_blue (ColorSelectFill *csf) { unsigned char *p; int i, g, r; float b, db; p = csf->buffer; csf->y += 1; r = csf->values[RED]; g = (csf->height - csf->y + 1) * 255 / csf->height; if (g < 0) g = 0; if (g > 255) g = 255; b = 0; db = 255.0 / csf->width; for (i = 0; i < csf->width; i++) { *p++ = r; *p++ = g; *p++ = b; b += db; } } static void color_select_update_hue_saturation (ColorSelectFill *csf) { unsigned char *p; float h, v, s, ds; int f; int i; p = csf->buffer; csf->y += 1; h = 360 - (csf->y * 360 / csf->height); if (h < 0) h = 0; if (h > 359) h = 359; h /= 60; f = (h - (int) h) * 255; s = 0; ds = 1.0 / csf->width; v = csf->values[VALUE] / 100.0; switch ((int) h) { case 0: for (i = 0; i < csf->width; i++) { *p++ = v * 255; *p++ = v * (255 - (s * (255 - f))); *p++ = v * 255 * (1 - s); s += ds; } break; case 1: for (i = 0; i < csf->width; i++) { *p++ = v * (255 - s * f); *p++ = v * 255; *p++ = v * 255 * (1 - s); s += ds; } break; case 2: for (i = 0; i < csf->width; i++) { *p++ = v * 255 * (1 - s); *p++ = v *255; *p++ = v * (255 - (s * (255 - f))); s += ds; } break; case 3: for (i = 0; i < csf->width; i++) { *p++ = v * 255 * (1 - s); *p++ = v * (255 - s * f); *p++ = v * 255; s += ds; } break; case 4: for (i = 0; i < csf->width; i++) { *p++ = v * (255 - (s * (255 - f))); *p++ = v * (255 * (1 - s)); *p++ = v * 255; s += ds; } break; case 5: for (i = 0; i < csf->width; i++) { *p++ = v * 255; *p++ = v * 255 * (1 - s); *p++ = v * (255 - s * f); s += ds; } break; } } static void color_select_update_hue_value (ColorSelectFill *csf) { unsigned char *p; float h, v, dv, s; int f; int i; p = csf->buffer; csf->y += 1; h = 360 - (csf->y * 360 / csf->height); if (h < 0) h = 0; if (h > 359) h = 359; h /= 60; f = (h - (int) h) * 255; v = 0; dv = 1.0 / csf->width; s = csf->values[SATURATION] / 100.0; switch ((int) h) { case 0: for (i = 0; i < csf->width; i++) { *p++ = v * 255; *p++ = v * (255 - (s * (255 - f))); *p++ = v * 255 * (1 - s); v += dv; } break; case 1: for (i = 0; i < csf->width; i++) { *p++ = v * (255 - s * f); *p++ = v * 255; *p++ = v * 255 * (1 - s); v += dv; } break; case 2: for (i = 0; i < csf->width; i++) { *p++ = v * 255 * (1 - s); *p++ = v *255; *p++ = v * (255 - (s * (255 - f))); v += dv; } break; case 3: for (i = 0; i < csf->width; i++) { *p++ = v * 255 * (1 - s); *p++ = v * (255 - s * f); *p++ = v * 255; v += dv; } break; case 4: for (i = 0; i < csf->width; i++) { *p++ = v * (255 - (s * (255 - f))); *p++ = v * (255 * (1 - s)); *p++ = v * 255; v += dv; } break; case 5: for (i = 0; i < csf->width; i++) { *p++ = v * 255; *p++ = v * 255 * (1 - s); *p++ = v * (255 - s * f); v += dv; } break; } } static void color_select_update_saturation_value (ColorSelectFill *csf) { unsigned char *p; float h, v, dv, s; int f; int i; p = csf->buffer; csf->y += 1; s = (float) csf->y / csf->height; if (s < 0) s = 0; if (s > 1) s = 1; s = 1 - s; h = (float) csf->values[HUE]; if (h >= 360) h -= 360; h /= 60; f = (h - (int) h) * 255; v = 0; dv = 1.0 / csf->width; switch ((int) h) { case 0: for (i = 0; i < csf->width; i++) { *p++ = v * 255; *p++ = v * (255 - (s * (255 - f))); *p++ = v * 255 * (1 - s); v += dv; } break; case 1: for (i = 0; i < csf->width; i++) { *p++ = v * (255 - s * f); *p++ = v * 255; *p++ = v * 255 * (1 - s); v += dv; } break; case 2: for (i = 0; i < csf->width; i++) { *p++ = v * 255 * (1 - s); *p++ = v *255; *p++ = v * (255 - (s * (255 - f))); v += dv; } break; case 3: for (i = 0; i < csf->width; i++) { *p++ = v * 255 * (1 - s); *p++ = v * (255 - s * f); *p++ = v * 255; v += dv; } break; case 4: for (i = 0; i < csf->width; i++) { *p++ = v * (255 - (s * (255 - f))); *p++ = v * (255 * (1 - s)); *p++ = v * 255; v += dv; } break; case 5: for (i = 0; i < csf->width; i++) { *p++ = v * 255; *p++ = v * 255 * (1 - s); *p++ = v * (255 - s * f); v += dv; } break; } }