/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * color_notebook module (C) 1998 Austin Donnelly * * 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 #include #include #include #include #include "libgimpcolor/gimpcolor.h" #include "libgimpwidgets/gimpwidgets.h" #include "gui-types.h" #include "widgets/gimpdialogfactory.h" #include "widgets/gimpviewabledialog.h" #include "color-history.h" #include "color-notebook.h" #include "gimp-intl.h" #define COLOR_AREA_SIZE 20 typedef enum { UPDATE_NOTEBOOK = 1 << 0, UPDATE_CHANNEL = 1 << 1, UPDATE_SCALES = 1 << 2, UPDATE_ORIG_COLOR = 1 << 3, UPDATE_NEW_COLOR = 1 << 4, UPDATE_CALLER = 1 << 5 } ColorNotebookUpdateType; struct _ColorNotebook { GtkWidget *shell; GtkWidget *notebook; GtkWidget *scales; GtkWidget *new_color; GtkWidget *orig_color; GtkWidget *history[COLOR_HISTORY_SIZE]; GimpHSV hsv; GimpRGB rgb; GimpRGB orig_rgb; GimpColorSelectorChannel active_channel; ColorNotebookCallback callback; gpointer client_data; gboolean wants_updates; gboolean show_alpha; }; static ColorNotebook * color_notebook_new_internal (GimpViewable *viewable, const gchar *title, const gchar *wmclass_name, const gchar *stock_id, const gchar *desc, GimpDialogFactory *dialog_factory, const gchar *dialog_identifier, const GimpRGB *color, ColorNotebookCallback callback, gpointer client_data, gboolean wants_updates, gboolean show_alpha); static void color_notebook_help_func (const gchar *help_data); static void color_notebook_ok_callback (GtkWidget *widget, ColorNotebook *cnp); static void color_notebook_cancel_callback (GtkWidget *widget, ColorNotebook *cnp); static void color_notebook_update (ColorNotebook *cnp, ColorNotebookUpdateType update); static void color_notebook_reset_clicked (ColorNotebook *cnp); static void color_notebook_new_color_changed (GtkWidget *widget, ColorNotebook *cnp); static void color_notebook_notebook_changed (GimpColorSelector *selector, const GimpRGB *rgb, const GimpHSV *hsv, ColorNotebook *cnp); static void color_notebook_switch_page (GtkWidget *widget, GtkNotebookPage *page, guint page_num, ColorNotebook *cnp); static void color_notebook_channel_changed (GimpColorSelector *selector, GimpColorSelectorChannel channel, ColorNotebook *cnp); static void color_notebook_scales_changed (GimpColorSelector *selector, const GimpRGB *rgb, const GimpHSV *hsv, ColorNotebook *cnp); static void color_history_color_clicked (GtkWidget *widget, ColorNotebook *cnp); static void color_history_color_changed (GtkWidget *widget, gpointer data); static void color_history_add_clicked (GtkWidget *widget, ColorNotebook *cnp); static GList *color_notebooks = NULL; /* public functions */ ColorNotebook * color_notebook_new (const gchar *title, GimpDialogFactory *dialog_factory, const gchar *dialog_identifier, const GimpRGB *color, ColorNotebookCallback callback, gpointer client_data, gboolean wants_updates, gboolean show_alpha) { return color_notebook_new_internal (NULL, title, "color_selection", NULL, NULL, dialog_factory, dialog_identifier, color, callback, client_data, wants_updates, show_alpha); } ColorNotebook * color_notebook_viewable_new (GimpViewable *viewable, const gchar *title, const gchar *stock_id, const gchar *desc, GimpDialogFactory *dialog_factory, const gchar *dialog_identifier, const GimpRGB *color, ColorNotebookCallback callback, gpointer client_data, gboolean wants_updates, gboolean show_alpha) { return color_notebook_new_internal (viewable, title, "color_selection", stock_id, desc, dialog_factory, dialog_identifier, color, callback, client_data, wants_updates, show_alpha); } void color_notebook_free (ColorNotebook *cnp) { g_return_if_fail (cnp != NULL); color_notebooks = g_list_remove (color_notebooks, cnp); /* may be already destroyed by dialog factory */ if (cnp->shell) { g_object_remove_weak_pointer (G_OBJECT (cnp->shell), (gpointer *) &cnp->shell); gtk_widget_destroy (cnp->shell); } g_free (cnp); } void color_notebook_set_viewable (ColorNotebook *cnb, GimpViewable *viewable) { g_return_if_fail (cnb != NULL); if (GIMP_IS_VIEWABLE_DIALOG (cnb->shell)) gimp_viewable_dialog_set_viewable (GIMP_VIEWABLE_DIALOG (cnb->shell), viewable); } void color_notebook_set_title (ColorNotebook *cnb, const gchar *title) { g_return_if_fail (cnb != NULL); g_return_if_fail (title != NULL); gtk_window_set_title (GTK_WINDOW (cnb->shell), title); } void color_notebook_set_color (ColorNotebook *cnp, const GimpRGB *color) { g_return_if_fail (cnp != NULL); g_return_if_fail (color != NULL); cnp->rgb = *color; cnp->orig_rgb = *color; gimp_rgb_to_hsv (&cnp->rgb, &cnp->hsv); color_notebook_update (cnp, UPDATE_NOTEBOOK | UPDATE_SCALES | UPDATE_ORIG_COLOR | UPDATE_NEW_COLOR); } void color_notebook_get_color (ColorNotebook *cnp, GimpRGB *color) { g_return_if_fail (cnp != NULL); g_return_if_fail (color != NULL); *color = cnp->rgb; } void color_notebook_show (ColorNotebook *cnp) { g_return_if_fail (cnp != NULL); gtk_window_present (GTK_WINDOW (cnp->shell)); } void color_notebook_hide (ColorNotebook *cnp) { g_return_if_fail (cnp != NULL); gtk_widget_hide (cnp->shell); } /* private functions */ static ColorNotebook * color_notebook_new_internal (GimpViewable *viewable, const gchar *title, const gchar *wmclass_name, const gchar *stock_id, const gchar *desc, GimpDialogFactory *dialog_factory, const gchar *dialog_identifier, const GimpRGB *color, ColorNotebookCallback callback, gpointer client_data, gboolean wants_updates, gboolean show_alpha) { ColorNotebook *cnp; GtkWidget *main_vbox; GtkWidget *main_hbox; GtkWidget *left_vbox; GtkWidget *right_vbox; GtkWidget *color_frame; GtkWidget *table; GtkWidget *button; GtkWidget *image; GtkWidget *arrow; gint i; g_return_val_if_fail (dialog_factory == NULL || GIMP_IS_DIALOG_FACTORY (dialog_factory), NULL); g_return_val_if_fail (dialog_factory == NULL || dialog_identifier != NULL, NULL); g_return_val_if_fail (color != NULL, NULL); cnp = g_new0 (ColorNotebook, 1); cnp->callback = callback; cnp->client_data = client_data; cnp->wants_updates = wants_updates; cnp->show_alpha = show_alpha; cnp->rgb = *color; cnp->orig_rgb = *color; gimp_rgb_to_hsv (&cnp->rgb, &cnp->hsv); if (desc) { cnp->shell = gimp_viewable_dialog_new (viewable, title, wmclass_name, stock_id, desc, color_notebook_help_func, (const gchar *) cnp, NULL); gtk_window_set_resizable (GTK_WINDOW (cnp->shell), FALSE); } else { cnp->shell = gimp_dialog_new (title, wmclass_name, color_notebook_help_func, (const gchar *) cnp, GTK_WIN_POS_NONE, FALSE, TRUE, TRUE, NULL); } gimp_dialog_create_action_area (GIMP_DIALOG (cnp->shell), GTK_STOCK_CANCEL, color_notebook_cancel_callback, cnp, NULL, NULL, FALSE, TRUE, GTK_STOCK_OK, color_notebook_ok_callback, cnp, NULL, NULL, TRUE, FALSE, NULL); g_object_add_weak_pointer (G_OBJECT (cnp->shell), (gpointer *) &cnp->shell); gimp_dialog_factory_add_foreign (dialog_factory, dialog_identifier, cnp->shell); main_vbox = gtk_vbox_new (FALSE, 4); gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 4); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (cnp->shell)->vbox), main_vbox); gtk_widget_show (main_vbox); main_hbox = gtk_hbox_new (FALSE, 4); gtk_container_add (GTK_CONTAINER (main_vbox), main_hbox); gtk_widget_show (main_hbox); /* The left vbox with the notebook */ left_vbox = gtk_vbox_new (FALSE, 4); gtk_box_pack_start (GTK_BOX (main_hbox), left_vbox, FALSE, FALSE, 0); gtk_widget_show (left_vbox); /* The right vbox with color areas and color space sliders */ right_vbox = gtk_vbox_new (FALSE, 4); gtk_box_pack_start (GTK_BOX (main_hbox), right_vbox, TRUE, TRUE, 0); gtk_widget_show (right_vbox); cnp->notebook = gimp_color_selector_new (GIMP_TYPE_COLOR_NOTEBOOK, &cnp->rgb, &cnp->hsv, cnp->active_channel); gimp_color_selector_set_toggles_visible (GIMP_COLOR_SELECTOR (cnp->notebook), FALSE); gtk_box_pack_start (GTK_BOX (left_vbox), cnp->notebook, TRUE, TRUE, 0); gtk_widget_show (cnp->notebook); g_signal_connect (cnp->notebook, "color_changed", G_CALLBACK (color_notebook_notebook_changed), cnp); g_signal_connect (GIMP_COLOR_NOTEBOOK (cnp->notebook)->notebook, "switch_page", G_CALLBACK (color_notebook_switch_page), cnp); /* The table for the color_areas */ table = gtk_table_new (2, 3, FALSE); gtk_table_set_row_spacings (GTK_TABLE (table), 1); gtk_table_set_col_spacings (GTK_TABLE (table), 1); gtk_table_set_col_spacing (GTK_TABLE (table), 0, 3); gtk_box_pack_start (GTK_BOX (left_vbox), table, FALSE, FALSE, 0); gtk_widget_show (table); /* The new color area */ color_frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (color_frame), GTK_SHADOW_IN); gtk_widget_set_size_request (color_frame, -1, COLOR_AREA_SIZE); gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, _("Current:"), 1.0, 0.5, color_frame, 1, FALSE); cnp->new_color = gimp_color_area_new (&cnp->rgb, show_alpha ? GIMP_COLOR_AREA_SMALL_CHECKS : GIMP_COLOR_AREA_FLAT, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK); gtk_container_add (GTK_CONTAINER (color_frame), cnp->new_color); gtk_widget_show (cnp->new_color); g_signal_connect (cnp->new_color, "color_changed", G_CALLBACK (color_notebook_new_color_changed), cnp); /* The old color area */ color_frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (color_frame), GTK_SHADOW_IN); gtk_widget_set_size_request (color_frame, -1, COLOR_AREA_SIZE); gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, _("Old:"), 1.0, 0.5, color_frame, 1, FALSE); cnp->orig_color = gimp_color_area_new (&cnp->orig_rgb, show_alpha ? GIMP_COLOR_AREA_SMALL_CHECKS : GIMP_COLOR_AREA_FLAT, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK); gtk_drag_dest_unset (cnp->orig_color); gtk_container_add (GTK_CONTAINER (color_frame), cnp->orig_color); gtk_widget_show (cnp->orig_color); button = gtk_button_new (); gtk_table_attach (GTK_TABLE (table), button, 2, 3, 0, 2, GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); gtk_widget_show (button); image = gtk_image_new_from_stock (GIMP_STOCK_RESET, GTK_ICON_SIZE_MENU); gtk_container_add (GTK_CONTAINER (button), image); gtk_widget_show (image); gimp_help_set_help_data (button, _("Revert to old color"), NULL); g_signal_connect_swapped (button, "clicked", G_CALLBACK (color_notebook_reset_clicked), cnp); /* The color space sliders, toggle buttons and entries */ cnp->scales = gimp_color_selector_new (GIMP_TYPE_COLOR_SCALES, &cnp->rgb, &cnp->hsv, cnp->active_channel); gimp_color_selector_set_toggles_visible (GIMP_COLOR_SELECTOR (cnp->scales), TRUE); gimp_color_selector_set_show_alpha (GIMP_COLOR_SELECTOR (cnp->scales), cnp->show_alpha); gtk_box_pack_start (GTK_BOX (right_vbox), cnp->scales, TRUE, TRUE, 0); gtk_widget_show (cnp->scales); g_signal_connect (cnp->scales, "channel_changed", G_CALLBACK (color_notebook_channel_changed), cnp); g_signal_connect (cnp->scales, "color_changed", G_CALLBACK (color_notebook_scales_changed), cnp); /* The color history */ table = gtk_table_new (2, 9, TRUE); gtk_table_set_row_spacings (GTK_TABLE (table), 1); gtk_table_set_col_spacings (GTK_TABLE (table), 1); gtk_table_set_col_spacing (GTK_TABLE (table), 0, 3); gtk_box_pack_end (GTK_BOX (right_vbox), table, FALSE, FALSE, 0); gtk_widget_show (table); button = gtk_button_new (); gtk_widget_set_size_request (button, COLOR_AREA_SIZE, COLOR_AREA_SIZE); gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 1, 0, 1); gimp_help_set_help_data (button, _("Add the current color to the color history"), NULL); gtk_widget_show (button); g_signal_connect (button, "clicked", G_CALLBACK (color_history_add_clicked), cnp); arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_OUT); gtk_container_add (GTK_CONTAINER (button), arrow); gtk_widget_show (arrow); for (i = 0; i < COLOR_HISTORY_SIZE; i++) { GimpRGB history_color; color_history_get (i, &history_color); button = gtk_button_new (); gtk_widget_set_size_request (button, COLOR_AREA_SIZE, COLOR_AREA_SIZE); gtk_table_attach_defaults (GTK_TABLE (table), button, (i > 7 ? i - 8 : i) + 1, (i > 7 ? i - 8 : i) + 2, i > 7 ? 1 : 0, i > 7 ? 2 : 1); cnp->history[i] = gimp_color_area_new (&history_color, GIMP_COLOR_AREA_SMALL_CHECKS, GDK_BUTTON2_MASK); gtk_container_add (GTK_CONTAINER (button), cnp->history[i]); gtk_widget_show (cnp->history[i]); gtk_widget_show (button); g_signal_connect (button, "clicked", G_CALLBACK (color_history_color_clicked), cnp); g_signal_connect (cnp->history[i], "color_changed", G_CALLBACK (color_history_color_changed), GINT_TO_POINTER (i)); } gtk_widget_show (cnp->shell); color_notebooks = g_list_prepend (color_notebooks, cnp); return cnp; } static void color_notebook_help_func (const gchar *data) { ColorNotebook *cnp; GimpColorNotebook *notebook; gchar *help_path; cnp = (ColorNotebook *) data; notebook = GIMP_COLOR_NOTEBOOK (cnp->notebook); help_path = g_strconcat ("dialogs/color_selectors/", GIMP_COLOR_SELECTOR_GET_CLASS (notebook->cur_page)->help_page, NULL); gimp_standard_help_func (help_path); g_free (help_path); } static void color_notebook_ok_callback (GtkWidget *widget, ColorNotebook *cnp) { color_history_add_clicked (NULL, cnp); if (cnp->callback) cnp->callback (cnp, &cnp->rgb, COLOR_NOTEBOOK_OK, cnp->client_data); } static void color_notebook_cancel_callback (GtkWidget *widget, ColorNotebook *cnp) { if (cnp->callback) cnp->callback (cnp, &cnp->orig_rgb, COLOR_NOTEBOOK_CANCEL, cnp->client_data); } static void color_notebook_update (ColorNotebook *cnp, ColorNotebookUpdateType update) { if (!cnp) return; if (update & UPDATE_NOTEBOOK) { g_signal_handlers_block_by_func (cnp->notebook, color_notebook_notebook_changed, cnp); gimp_color_selector_set_color (GIMP_COLOR_SELECTOR (cnp->notebook), &cnp->rgb, &cnp->hsv); g_signal_handlers_unblock_by_func (cnp->notebook, color_notebook_notebook_changed, cnp); } if (update & UPDATE_CHANNEL) gimp_color_selector_set_channel (GIMP_COLOR_SELECTOR (cnp->notebook), cnp->active_channel); if (update & UPDATE_SCALES) { g_signal_handlers_block_by_func (cnp->scales, color_notebook_scales_changed, cnp); gimp_color_selector_set_color (GIMP_COLOR_SELECTOR (cnp->scales), &cnp->rgb, &cnp->hsv); g_signal_handlers_unblock_by_func (cnp->scales, color_notebook_scales_changed, cnp); } if (update & UPDATE_ORIG_COLOR) gimp_color_area_set_color (GIMP_COLOR_AREA (cnp->orig_color), &cnp->orig_rgb); if (update & UPDATE_NEW_COLOR) { g_signal_handlers_block_by_func (cnp->new_color, color_notebook_new_color_changed, cnp); gimp_color_area_set_color (GIMP_COLOR_AREA (cnp->new_color), &cnp->rgb); g_signal_handlers_unblock_by_func (cnp->new_color, color_notebook_new_color_changed, cnp); } if (update & UPDATE_CALLER) { if (cnp->wants_updates && cnp->callback) cnp->callback (cnp, &cnp->rgb, COLOR_NOTEBOOK_UPDATE, cnp->client_data); } } static void color_notebook_reset_clicked (ColorNotebook *cnp) { cnp->rgb = cnp->orig_rgb; gimp_rgb_to_hsv (&cnp->rgb, &cnp->hsv); color_notebook_update (cnp, UPDATE_NOTEBOOK | UPDATE_SCALES | UPDATE_NEW_COLOR | UPDATE_CALLER); } static void color_notebook_new_color_changed (GtkWidget *widget, ColorNotebook *cnp) { gimp_color_area_get_color (GIMP_COLOR_AREA (widget), &cnp->rgb); gimp_rgb_to_hsv (&cnp->rgb, &cnp->hsv); color_notebook_update (cnp, UPDATE_NOTEBOOK | UPDATE_SCALES | UPDATE_NEW_COLOR | UPDATE_CALLER); } /* color notebook callbacks */ static void color_notebook_notebook_changed (GimpColorSelector *selector, const GimpRGB *rgb, const GimpHSV *hsv, ColorNotebook *cnp) { cnp->hsv = *hsv; cnp->rgb = *rgb; color_notebook_update (cnp, UPDATE_SCALES | UPDATE_NEW_COLOR | UPDATE_CALLER); } static void color_notebook_switch_page (GtkWidget *widget, GtkNotebookPage *page, guint page_num, ColorNotebook *cnp) { GimpColorNotebook *notebook; gboolean set_channel; notebook = GIMP_COLOR_NOTEBOOK (cnp->notebook); set_channel = (GIMP_COLOR_SELECTOR_GET_CLASS (notebook->cur_page)->set_channel != NULL); gimp_color_selector_set_toggles_sensitive (GIMP_COLOR_SELECTOR (cnp->scales), set_channel); } /* color scales callbacks */ static void color_notebook_channel_changed (GimpColorSelector *selector, GimpColorSelectorChannel channel, ColorNotebook *cnp) { cnp->active_channel = channel; color_notebook_update (cnp, UPDATE_CHANNEL); } static void color_notebook_scales_changed (GimpColorSelector *selector, const GimpRGB *rgb, const GimpHSV *hsv, ColorNotebook *cnp) { cnp->rgb = *rgb; cnp->hsv = *hsv; color_notebook_update (cnp, UPDATE_NOTEBOOK | UPDATE_NEW_COLOR | UPDATE_CALLER); } /* color history callbacks */ static void color_history_color_clicked (GtkWidget *widget, ColorNotebook *cnp) { GimpColorArea *color_area; color_area = GIMP_COLOR_AREA (GTK_BIN (widget)->child); gimp_color_area_get_color (color_area, &cnp->rgb); gimp_rgb_to_hsv (&cnp->rgb, &cnp->hsv); color_notebook_update (cnp, UPDATE_NOTEBOOK | UPDATE_SCALES | UPDATE_ORIG_COLOR | UPDATE_NEW_COLOR | UPDATE_CALLER); } static void color_history_color_changed (GtkWidget *widget, gpointer data) { GimpRGB changed_color; gint color_index; GList *list; ColorNotebook *notebook; color_index = GPOINTER_TO_INT (data); gimp_color_area_get_color (GIMP_COLOR_AREA (widget), &changed_color); color_history_set (color_index, &changed_color); for (list = color_notebooks; list; list = g_list_next (list)) { notebook = (ColorNotebook *) list->data; if (notebook->history[color_index] == widget) continue; g_signal_handlers_block_by_func (notebook->history[color_index], color_history_color_changed, data); gimp_color_area_set_color (GIMP_COLOR_AREA (notebook->history[color_index]), &changed_color); g_signal_handlers_unblock_by_func (notebook->history[color_index], color_history_color_changed, data); } } static void color_history_add_clicked (GtkWidget *widget, ColorNotebook *cnp) { gint shift_begin; gint i; shift_begin = color_history_add (&cnp->rgb); for (i = shift_begin; i >= 0; i--) { GimpRGB color; color_history_get (i, &color); gimp_color_area_set_color (GIMP_COLOR_AREA (cnp->history[i]), &color); } }