PIKApp/app/widgets/pikacoloreditor.c

737 lines
25 KiB
C

/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikacoloreditor.c
* Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikacolor/pikacolor.h"
#include "libpikawidgets/pikawidgets.h"
#include "widgets-types.h"
#include "config/pikacoreconfig.h"
#include "core/pika.h"
#include "core/pikacontext.h"
#include "pikacoloreditor.h"
#include "pikacolorhistory.h"
#include "pikadocked.h"
#include "pikafgbgeditor.h"
#include "pikafgbgview.h"
#include "pikasessioninfo-aux.h"
#include "pika-intl.h"
enum
{
PROP_0,
PROP_CONTEXT
};
static void pika_color_editor_docked_iface_init (PikaDockedInterface *iface);
static void pika_color_editor_constructed (GObject *object);
static void pika_color_editor_dispose (GObject *object);
static void pika_color_editor_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_color_editor_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_color_editor_style_updated (GtkWidget *widget);
static void pika_color_editor_set_aux_info (PikaDocked *docked,
GList *aux_info);
static GList *pika_color_editor_get_aux_info (PikaDocked *docked);
static GtkWidget *pika_color_editor_get_preview (PikaDocked *docked,
PikaContext *context,
GtkIconSize size);
static void pika_color_editor_set_context (PikaDocked *docked,
PikaContext *context);
static void pika_color_editor_fg_changed (PikaContext *context,
const PikaRGB *rgb,
PikaColorEditor *editor);
static void pika_color_editor_bg_changed (PikaContext *context,
const PikaRGB *rgb,
PikaColorEditor *editor);
static void pika_color_editor_color_changed (PikaColorSelector *selector,
const PikaRGB *rgb,
const PikaHSV *hsv,
PikaColorEditor *editor);
static void pika_color_editor_tab_toggled (GtkWidget *widget,
PikaColorEditor *editor);
static void pika_color_editor_fg_bg_notify (GtkWidget *widget,
GParamSpec *pspec,
PikaColorEditor *editor);
static void pika_color_editor_color_picked (GtkWidget *widget,
const PikaRGB *rgb,
PikaColorEditor *editor);
static void pika_color_editor_entry_changed (PikaColorHexEntry *entry,
PikaColorEditor *editor);
static void pika_color_editor_history_selected (PikaColorHistory *history,
const PikaRGB *rgb,
PikaColorEditor *editor);
G_DEFINE_TYPE_WITH_CODE (PikaColorEditor, pika_color_editor, PIKA_TYPE_EDITOR,
G_IMPLEMENT_INTERFACE (PIKA_TYPE_DOCKED,
pika_color_editor_docked_iface_init))
#define parent_class pika_color_editor_parent_class
static PikaDockedInterface *parent_docked_iface = NULL;
static void
pika_color_editor_class_init (PikaColorEditorClass* klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->constructed = pika_color_editor_constructed;
object_class->dispose = pika_color_editor_dispose;
object_class->set_property = pika_color_editor_set_property;
object_class->get_property = pika_color_editor_get_property;
widget_class->style_updated = pika_color_editor_style_updated;
g_object_class_install_property (object_class, PROP_CONTEXT,
g_param_spec_object ("context",
NULL, NULL,
PIKA_TYPE_CONTEXT,
G_PARAM_CONSTRUCT |
PIKA_PARAM_READWRITE));
}
static void
pika_color_editor_docked_iface_init (PikaDockedInterface *iface)
{
parent_docked_iface = g_type_interface_peek_parent (iface);
if (! parent_docked_iface)
parent_docked_iface = g_type_default_interface_peek (PIKA_TYPE_DOCKED);
iface->get_preview = pika_color_editor_get_preview;
iface->set_aux_info = pika_color_editor_set_aux_info;
iface->get_aux_info = pika_color_editor_get_aux_info;
iface->set_context = pika_color_editor_set_context;
}
static void
pika_color_editor_init (PikaColorEditor *editor)
{
GtkWidget *notebook;
GtkWidget *hbox;
GtkWidget *button;
gint content_spacing;
gint button_spacing;
GtkIconSize button_icon_size;
PikaRGB rgb;
PikaHSV hsv;
GList *list;
GSList *group;
gint icon_width = 40;
gint icon_height = 38;
editor->context = NULL;
editor->edit_bg = FALSE;
pika_rgba_set (&rgb, 0.0, 0.0, 0.0, 1.0);
pika_rgb_to_hsv (&rgb, &hsv);
gtk_widget_style_get (GTK_WIDGET (editor),
"content-spacing", &content_spacing,
"button-spacing", &button_spacing,
"button-icon-size", &button_icon_size,
NULL);
editor->hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, button_spacing);
gtk_box_set_homogeneous (GTK_BOX (editor->hbox), TRUE);
gtk_box_pack_start (GTK_BOX (editor), editor->hbox, FALSE, FALSE, 0);
gtk_widget_show (editor->hbox);
editor->notebook = pika_color_selector_new (PIKA_TYPE_COLOR_NOTEBOOK,
&rgb, &hsv,
PIKA_COLOR_SELECTOR_RED);
pika_color_selector_set_show_alpha (PIKA_COLOR_SELECTOR (editor->notebook),
FALSE);
gtk_box_pack_start (GTK_BOX (editor), editor->notebook,
TRUE, TRUE, content_spacing);
gtk_widget_show (editor->notebook);
g_signal_connect (editor->notebook, "color-changed",
G_CALLBACK (pika_color_editor_color_changed),
editor);
notebook = pika_color_notebook_get_notebook (PIKA_COLOR_NOTEBOOK (editor->notebook));
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE);
gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE);
pika_color_notebook_set_has_page (PIKA_COLOR_NOTEBOOK (editor->notebook),
PIKA_TYPE_COLOR_SCALES, TRUE);
group = NULL;
for (list = pika_color_notebook_get_selectors (PIKA_COLOR_NOTEBOOK (editor->notebook));
list;
list = g_list_next (list))
{
PikaColorSelector *selector;
PikaColorSelectorClass *selector_class;
GtkWidget *button;
GtkWidget *image;
selector = PIKA_COLOR_SELECTOR (list->data);
selector_class = PIKA_COLOR_SELECTOR_GET_CLASS (selector);
button = gtk_radio_button_new (group);
group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE);
gtk_box_pack_start (GTK_BOX (editor->hbox), button, TRUE, TRUE, 0);
gtk_widget_show (button);
image = gtk_image_new_from_icon_name (selector_class->icon_name,
GTK_ICON_SIZE_MENU);
gtk_container_add (GTK_CONTAINER (button), image);
gtk_widget_show (image);
pika_help_set_help_data (button,
selector_class->name, selector_class->help_id);
g_object_set_data (G_OBJECT (button), "selector", selector);
g_object_set_data (G_OBJECT (selector), "button", button);
g_signal_connect (button, "toggled",
G_CALLBACK (pika_color_editor_tab_toggled),
editor);
}
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
gtk_box_pack_start (GTK_BOX (editor), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
/* FG/BG editor */
editor->fg_bg = pika_fg_bg_editor_new (NULL);
gtk_icon_size_lookup (button_icon_size, &icon_width, &icon_height);
gtk_widget_set_size_request (editor->fg_bg,
(gint) (icon_width * 1.75),
(gint) (icon_height * 1.75));
gtk_box_pack_start (GTK_BOX (hbox), editor->fg_bg, FALSE, FALSE, 0);
gtk_widget_show (editor->fg_bg);
g_signal_connect (editor->fg_bg, "notify::active-color",
G_CALLBACK (pika_color_editor_fg_bg_notify),
editor);
/* The color picker */
button = pika_pick_button_new ();
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
g_signal_connect (button, "color-picked",
G_CALLBACK (pika_color_editor_color_picked),
editor);
/* The hex triplet entry */
editor->hex_entry = pika_color_hex_entry_new ();
gtk_box_pack_end (GTK_BOX (hbox), editor->hex_entry, TRUE, TRUE, 0);
gtk_widget_show (editor->hex_entry);
g_signal_connect (editor->hex_entry, "color-changed",
G_CALLBACK (pika_color_editor_entry_changed),
editor);
}
static void
pika_color_editor_constructed (GObject *object)
{
PikaColorEditor *editor = PIKA_COLOR_EDITOR (object);
GtkWidget *history;
G_OBJECT_CLASS (parent_class)->constructed (object);
/* The color history */
history = pika_color_history_new (editor->context, 12);
gtk_box_pack_end (GTK_BOX (editor), history, FALSE, FALSE, 0);
gtk_widget_show (history);
g_signal_connect (history, "color-selected",
G_CALLBACK (pika_color_editor_history_selected),
editor);
}
static void
pika_color_editor_dispose (GObject *object)
{
PikaColorEditor *editor = PIKA_COLOR_EDITOR (object);
if (editor->context)
pika_docked_set_context (PIKA_DOCKED (editor), NULL);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
pika_color_editor_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
case PROP_CONTEXT:
pika_docked_set_context (PIKA_DOCKED (object),
g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_color_editor_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaColorEditor *editor = PIKA_COLOR_EDITOR (object);
switch (property_id)
{
case PROP_CONTEXT:
g_value_set_object (value, editor->context);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static GtkWidget *
pika_color_editor_get_preview (PikaDocked *docked,
PikaContext *context,
GtkIconSize size)
{
GtkWidget *preview;
gint width;
gint height;
preview = pika_fg_bg_view_new (context);
if (gtk_icon_size_lookup (size, &width, &height))
gtk_widget_set_size_request (preview, width, height);
return preview;
}
#define AUX_INFO_CURRENT_PAGE "current-page"
static void
pika_color_editor_set_aux_info (PikaDocked *docked,
GList *aux_info)
{
PikaColorEditor *editor = PIKA_COLOR_EDITOR (docked);
GtkWidget *notebook;
GList *list;
notebook = pika_color_notebook_get_notebook (PIKA_COLOR_NOTEBOOK (editor->notebook));
parent_docked_iface->set_aux_info (docked, aux_info);
for (list = aux_info; list; list = g_list_next (list))
{
PikaSessionInfoAux *aux = list->data;
if (! strcmp (aux->name, AUX_INFO_CURRENT_PAGE))
{
GList *children;
GList *child;
children = gtk_container_get_children (GTK_CONTAINER (notebook));
for (child = children; child; child = g_list_next (child))
{
if (! strcmp (G_OBJECT_TYPE_NAME (child->data), aux->value))
{
GtkWidget *button;
button = g_object_get_data (G_OBJECT (child->data), "button");
if (button)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
TRUE);
break;
}
}
g_list_free (children);
}
}
}
static GList *
pika_color_editor_get_aux_info (PikaDocked *docked)
{
PikaColorEditor *editor = PIKA_COLOR_EDITOR (docked);
PikaColorNotebook *notebook = PIKA_COLOR_NOTEBOOK (editor->notebook);
PikaColorSelector *current;
GList *aux_info;
aux_info = parent_docked_iface->get_aux_info (docked);
current = pika_color_notebook_get_current_selector (notebook);
if (current)
{
PikaSessionInfoAux *aux;
aux = pika_session_info_aux_new (AUX_INFO_CURRENT_PAGE,
G_OBJECT_TYPE_NAME (current));
aux_info = g_list_append (aux_info, aux);
}
return aux_info;
}
static void
pika_color_editor_set_context (PikaDocked *docked,
PikaContext *context)
{
PikaColorEditor *editor = PIKA_COLOR_EDITOR (docked);
if (context == editor->context)
return;
if (editor->context)
{
g_signal_handlers_disconnect_by_func (editor->context,
pika_color_editor_fg_changed,
editor);
g_signal_handlers_disconnect_by_func (editor->context,
pika_color_editor_bg_changed,
editor);
g_signal_handlers_disconnect_by_func (editor->context->pika->config,
G_CALLBACK (pika_color_editor_style_updated),
editor);
g_object_unref (editor->context);
}
editor->context = context;
if (editor->context)
{
PikaRGB rgb;
g_object_ref (editor->context);
g_signal_connect (editor->context, "foreground-changed",
G_CALLBACK (pika_color_editor_fg_changed),
editor);
g_signal_connect (editor->context, "background-changed",
G_CALLBACK (pika_color_editor_bg_changed),
editor);
g_signal_connect_object (editor->context->pika->config,
"notify::theme",
G_CALLBACK (pika_color_editor_style_updated),
editor, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
g_signal_connect_object (context->pika->config,
"notify::override-theme-icon-size",
G_CALLBACK (pika_color_editor_style_updated),
editor, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
g_signal_connect_object (context->pika->config,
"notify::custom-icon-size",
G_CALLBACK (pika_color_editor_style_updated),
editor, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
if (editor->edit_bg)
{
pika_context_get_background (editor->context, &rgb);
pika_color_editor_bg_changed (editor->context, &rgb, editor);
}
else
{
pika_context_get_foreground (editor->context, &rgb);
pika_color_editor_fg_changed (editor->context, &rgb, editor);
}
g_object_set_data (G_OBJECT (context->pika->config->color_management),
"pika-context", editor->context);
pika_color_selector_set_config (PIKA_COLOR_SELECTOR (editor->notebook),
context->pika->config->color_management);
g_object_set_data (G_OBJECT (context->pika->config->color_management),
"pika-context", NULL);
}
pika_fg_bg_editor_set_context (PIKA_FG_BG_EDITOR (editor->fg_bg), context);
}
GtkWidget *
pika_color_editor_new (PikaContext *context)
{
return g_object_new (PIKA_TYPE_COLOR_EDITOR,
"context", context,
NULL);
}
static void
pika_color_editor_style_updated (GtkWidget *widget)
{
PikaColorEditor *editor = PIKA_COLOR_EDITOR (widget);
GtkIconSize button_icon_size;
gint icon_width = 40;
gint icon_height = 38;
GTK_WIDGET_CLASS (parent_class)->style_updated (widget);
if (editor->hbox)
pika_editor_set_box_style (PIKA_EDITOR (editor), GTK_BOX (editor->hbox));
gtk_widget_style_get (GTK_WIDGET (editor),
"button-icon-size", &button_icon_size,
NULL);
gtk_icon_size_lookup (button_icon_size, &icon_width, &icon_height);
gtk_widget_set_size_request (editor->fg_bg,
(gint) (icon_width * 1.75),
(gint) (icon_height * 1.75));
}
static void
pika_color_editor_set_color (PikaColorEditor *editor,
const PikaRGB *rgb)
{
PikaHSV hsv;
pika_rgb_to_hsv (rgb, &hsv);
g_signal_handlers_block_by_func (editor->notebook,
pika_color_editor_color_changed,
editor);
pika_color_selector_set_color (PIKA_COLOR_SELECTOR (editor->notebook),
rgb, &hsv);
g_signal_handlers_unblock_by_func (editor->notebook,
pika_color_editor_color_changed,
editor);
g_signal_handlers_block_by_func (editor->hex_entry,
pika_color_editor_entry_changed,
editor);
pika_color_hex_entry_set_color (PIKA_COLOR_HEX_ENTRY (editor->hex_entry),
rgb);
g_signal_handlers_unblock_by_func (editor->hex_entry,
pika_color_editor_entry_changed,
editor);
}
static void
pika_color_editor_fg_changed (PikaContext *context,
const PikaRGB *rgb,
PikaColorEditor *editor)
{
if (! editor->edit_bg)
pika_color_editor_set_color (editor, rgb);
}
static void
pika_color_editor_bg_changed (PikaContext *context,
const PikaRGB *rgb,
PikaColorEditor *editor)
{
if (editor->edit_bg)
pika_color_editor_set_color (editor, rgb);
}
static void
pika_color_editor_color_changed (PikaColorSelector *selector,
const PikaRGB *rgb,
const PikaHSV *hsv,
PikaColorEditor *editor)
{
if (editor->context)
{
if (editor->edit_bg)
{
g_signal_handlers_block_by_func (editor->context,
pika_color_editor_bg_changed,
editor);
pika_context_set_background (editor->context, rgb);
g_signal_handlers_unblock_by_func (editor->context,
pika_color_editor_bg_changed,
editor);
}
else
{
g_signal_handlers_block_by_func (editor->context,
pika_color_editor_fg_changed,
editor);
pika_context_set_foreground (editor->context, rgb);
g_signal_handlers_unblock_by_func (editor->context,
pika_color_editor_fg_changed,
editor);
}
}
g_signal_handlers_block_by_func (editor->hex_entry,
pika_color_editor_entry_changed,
editor);
pika_color_hex_entry_set_color (PIKA_COLOR_HEX_ENTRY (editor->hex_entry),
rgb);
g_signal_handlers_unblock_by_func (editor->hex_entry,
pika_color_editor_entry_changed,
editor);
}
static void
pika_color_editor_tab_toggled (GtkWidget *widget,
PikaColorEditor *editor)
{
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
{
GtkWidget *selector;
selector = g_object_get_data (G_OBJECT (widget), "selector");
if (selector)
{
GtkWidget *notebook;
gint page_num;
notebook = pika_color_notebook_get_notebook (PIKA_COLOR_NOTEBOOK (editor->notebook));
page_num = gtk_notebook_page_num (GTK_NOTEBOOK (notebook), selector);
if (page_num >= 0)
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), page_num);
}
}
}
static void
pika_color_editor_fg_bg_notify (GtkWidget *widget,
GParamSpec *pspec,
PikaColorEditor *editor)
{
gboolean edit_bg;
edit_bg = (PIKA_FG_BG_EDITOR (widget)->active_color ==
PIKA_ACTIVE_COLOR_BACKGROUND);
if (edit_bg != editor->edit_bg)
{
editor->edit_bg = edit_bg;
if (editor->context)
{
PikaRGB rgb;
if (edit_bg)
{
pika_context_get_background (editor->context, &rgb);
pika_color_editor_bg_changed (editor->context, &rgb, editor);
}
else
{
pika_context_get_foreground (editor->context, &rgb);
pika_color_editor_fg_changed (editor->context, &rgb, editor);
}
}
}
}
static void
pika_color_editor_color_picked (GtkWidget *widget,
const PikaRGB *rgb,
PikaColorEditor *editor)
{
if (editor->context)
{
if (editor->edit_bg)
pika_context_set_background (editor->context, rgb);
else
pika_context_set_foreground (editor->context, rgb);
}
}
static void
pika_color_editor_entry_changed (PikaColorHexEntry *entry,
PikaColorEditor *editor)
{
PikaRGB rgb;
pika_color_hex_entry_get_color (entry, &rgb);
if (editor->context)
{
if (editor->edit_bg)
pika_context_set_background (editor->context, &rgb);
else
pika_context_set_foreground (editor->context, &rgb);
}
}
static void
pika_color_editor_history_selected (PikaColorHistory *history,
const PikaRGB *rgb,
PikaColorEditor *editor)
{
if (editor->context)
{
if (editor->edit_bg)
pika_context_set_background (editor->context, rgb);
else
pika_context_set_foreground (editor->context, rgb);
}
}