1
0
Fork 0

Adding upstream version 3.0.4.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
This commit is contained in:
Daniel Baumann 2025-06-23 00:14:50 +02:00
parent 1c8b56a4f5
commit 554424e00a
Signed by: daniel.baumann
GPG key ID: BCC918A2ABD66424
6822 changed files with 5440542 additions and 0 deletions

View file

@ -0,0 +1,463 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpbrowser.c
* Copyright (C) 2005 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "gimpwidgetstypes.h"
#include "gimpwidgets.h"
#include "gimpwidgetsmarshal.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimpbrowser
* @title: GimpBrowser
* @short_description: A base class for a documentation browser.
*
* A base class for a documentation browser.
**/
enum
{
SEARCH,
LAST_SIGNAL
};
struct _GimpBrowser
{
GtkPaned parent_instance;
GtkWidget *left_vbox;
GtkWidget *search_entry;
guint search_timeout_id;
GtkWidget *search_type_combo;
gint search_type;
GtkWidget *count_label;
GtkWidget *right_vbox;
GtkWidget *right_widget;
};
static void gimp_browser_dispose (GObject *object);
static void gimp_browser_combo_changed (GtkComboBox *combo,
GimpBrowser *browser);
static void gimp_browser_entry_changed (GtkEntry *entry,
GimpBrowser *browser);
static void gimp_browser_entry_icon_press (GtkEntry *entry,
GtkEntryIconPosition icon_pos,
GdkEvent *event,
GimpBrowser *browser);
static gboolean gimp_browser_search_timeout (gpointer data);
G_DEFINE_TYPE (GimpBrowser, gimp_browser, GTK_TYPE_PANED)
#define parent_class gimp_browser_parent_class
static guint browser_signals[LAST_SIGNAL] = { 0 };
static void
gimp_browser_class_init (GimpBrowserClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
browser_signals[SEARCH] =
g_signal_new ("search",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
_gimp_widgets_marshal_VOID__STRING_INT,
G_TYPE_NONE, 2,
G_TYPE_STRING,
G_TYPE_INT);
object_class->dispose = gimp_browser_dispose;
}
static void
gimp_browser_init (GimpBrowser *browser)
{
GtkWidget *hbox;
GtkWidget *label;
GtkWidget *scrolled_window;
GtkWidget *viewport;
gtk_orientable_set_orientation (GTK_ORIENTABLE (browser),
GTK_ORIENTATION_HORIZONTAL);
browser->search_type = -1;
browser->left_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_paned_pack1 (GTK_PANED (browser), browser->left_vbox, TRUE, FALSE);
gtk_widget_show (browser->left_vbox);
/* search entry */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start (GTK_BOX (browser->left_vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
label = gtk_label_new_with_mnemonic (_("_Search:"));
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
browser->search_entry = gtk_entry_new ();
gtk_box_pack_start (GTK_BOX (hbox), browser->search_entry, TRUE, TRUE, 0);
gtk_widget_show (browser->search_entry);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), browser->search_entry);
g_signal_connect (browser->search_entry, "changed",
G_CALLBACK (gimp_browser_entry_changed),
browser);
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (browser->search_entry),
GTK_ENTRY_ICON_SECONDARY, "edit-clear");
gtk_entry_set_icon_activatable (GTK_ENTRY (browser->search_entry),
GTK_ENTRY_ICON_SECONDARY, TRUE);
gtk_entry_set_icon_sensitive (GTK_ENTRY (browser->search_entry),
GTK_ENTRY_ICON_SECONDARY, FALSE);
g_signal_connect (browser->search_entry, "icon-press",
G_CALLBACK (gimp_browser_entry_icon_press),
browser);
/* count label */
browser->count_label = gtk_label_new (_("No matches"));
gtk_label_set_xalign (GTK_LABEL (browser->count_label), 0.0);
gimp_label_set_attributes (GTK_LABEL (browser->count_label),
PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
-1);
gtk_box_pack_end (GTK_BOX (browser->left_vbox), browser->count_label,
FALSE, FALSE, 0);
gtk_widget_show (browser->count_label);
/* scrolled window */
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_paned_pack2 (GTK_PANED (browser), scrolled_window, TRUE, FALSE);
gtk_widget_show (scrolled_window);
viewport = gtk_viewport_new (NULL, NULL);
gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
gtk_widget_show (viewport);
browser->right_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_set_border_width (GTK_CONTAINER (browser->right_vbox), 12);
gtk_container_add (GTK_CONTAINER (viewport), browser->right_vbox);
gtk_widget_show (browser->right_vbox);
gtk_widget_grab_focus (browser->search_entry);
}
static void
gimp_browser_dispose (GObject *object)
{
GimpBrowser *browser = GIMP_BROWSER (object);
if (browser->search_timeout_id)
{
g_source_remove (browser->search_timeout_id);
browser->search_timeout_id = 0;
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
/* public functions */
/**
* gimp_browser_new:
*
* Create a new #GimpBrowser widget.
*
* Returns: a newly created #GimpBrowser.
*
* Since: 2.4
**/
GtkWidget *
gimp_browser_new (void)
{
return g_object_new (GIMP_TYPE_BROWSER, NULL);
}
/**
* gimp_browser_add_search_types: (skip)
* @browser: a #GimpBrowser widget
* @first_type_label: the label of the first search type
* @first_type_id: an integer that identifies the first search type
* @...: a %NULL-terminated list of more labels and ids.
*
* Populates the #GtkComboBox with search types.
*
* Since: 2.4
**/
void
gimp_browser_add_search_types (GimpBrowser *browser,
const gchar *first_type_label,
gint first_type_id,
...)
{
g_return_if_fail (GIMP_IS_BROWSER (browser));
g_return_if_fail (first_type_label != NULL);
if (! browser->search_type_combo)
{
GtkWidget *combo;
va_list args;
va_start (args, first_type_id);
combo = gimp_int_combo_box_new_valist (first_type_label,
first_type_id,
args);
va_end (args);
gtk_widget_set_focus_on_click (combo, FALSE);
browser->search_type_combo = combo;
browser->search_type = first_type_id;
gtk_box_pack_end (GTK_BOX (gtk_widget_get_parent (browser->search_entry)),
combo, FALSE, FALSE, 0);
gtk_widget_show (combo);
gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
browser->search_type,
G_CALLBACK (gimp_int_combo_box_get_active),
&browser->search_type, NULL);
g_signal_connect (combo, "changed",
G_CALLBACK (gimp_browser_combo_changed),
browser);
}
else
{
gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (browser->search_type_combo),
first_type_label, first_type_id,
NULL);
}
}
/**
* gimp_browser_get_left_vbox:
* @browser: a #GimpBrowser widget
*
* Returns: (transfer none) (type GtkBox): The left vbox.
*
* Since: 3.0
**/
GtkWidget *
gimp_browser_get_left_vbox (GimpBrowser *browser)
{
g_return_val_if_fail (GIMP_IS_BROWSER (browser), NULL);
return browser->left_vbox;
}
/**
* gimp_browser_get_right_vbox:
* @browser: a #GimpBrowser widget
*
* Returns: (transfer none) (type GtkBox): The right vbox.
*
* Since: 3.0
**/
GtkWidget *
gimp_browser_get_right_vbox (GimpBrowser *browser)
{
g_return_val_if_fail (GIMP_IS_BROWSER (browser), NULL);
return browser->right_vbox;
}
/**
* gimp_browser_set_search_summary:
* @browser: a #GimpBrowser widget
* @summary: a string describing the search result
*
* Sets the search summary text.
*
* Since: 3.0
**/
void
gimp_browser_set_search_summary (GimpBrowser *browser,
const gchar *summary)
{
g_return_if_fail (GIMP_IS_BROWSER (browser));
g_return_if_fail (summary != NULL);
gtk_label_set_text (GTK_LABEL (browser->count_label), summary);
}
/**
* gimp_browser_set_widget:
* @browser: a #GimpBrowser widget
* @widget: a #GtkWidget
*
* Sets the widget to appear on the right side of the @browser.
*
* Since: 2.4
**/
void
gimp_browser_set_widget (GimpBrowser *browser,
GtkWidget *widget)
{
g_return_if_fail (GIMP_IS_BROWSER (browser));
g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
if (widget == browser->right_widget)
return;
if (browser->right_widget)
gtk_container_remove (GTK_CONTAINER (browser->right_vbox),
browser->right_widget);
browser->right_widget = widget;
if (widget)
{
gtk_box_pack_start (GTK_BOX (browser->right_vbox), widget,
FALSE, FALSE, 0);
gtk_widget_show (widget);
}
}
/**
* gimp_browser_show_message:
* @browser: a #GimpBrowser widget
* @message: text message
*
* Displays @message in the right side of the @browser. Unless the right
* side already contains a #GtkLabel, the widget previously added with
* gimp_browser_set_widget() is removed and replaced by a #GtkLabel.
*
* Since: 2.4
**/
void
gimp_browser_show_message (GimpBrowser *browser,
const gchar *message)
{
g_return_if_fail (GIMP_IS_BROWSER (browser));
g_return_if_fail (message != NULL);
if (GTK_IS_LABEL (browser->right_widget))
{
gtk_label_set_text (GTK_LABEL (browser->right_widget), message);
}
else
{
GtkWidget *label = gtk_label_new (message);
gimp_label_set_attributes (GTK_LABEL (label),
PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
-1);
gimp_browser_set_widget (browser, label);
}
while (gtk_events_pending ())
gtk_main_iteration ();
}
/* private functions */
static void
gimp_browser_queue_search (GimpBrowser *browser)
{
if (browser->search_timeout_id)
g_source_remove (browser->search_timeout_id);
browser->search_timeout_id =
g_timeout_add (100, gimp_browser_search_timeout, browser);
}
static void
gimp_browser_combo_changed (GtkComboBox *combo,
GimpBrowser *browser)
{
gimp_browser_queue_search (browser);
}
static void
gimp_browser_entry_changed (GtkEntry *entry,
GimpBrowser *browser)
{
gimp_browser_queue_search (browser);
gtk_entry_set_icon_sensitive (entry,
GTK_ENTRY_ICON_SECONDARY,
gtk_entry_get_text_length (entry) > 0);
}
static void
gimp_browser_entry_icon_press (GtkEntry *entry,
GtkEntryIconPosition icon_pos,
GdkEvent *event,
GimpBrowser *browser)
{
GdkEventButton *bevent = (GdkEventButton *) event;
if (icon_pos == GTK_ENTRY_ICON_SECONDARY && bevent->button == 1)
{
gtk_entry_set_text (entry, "");
}
}
static gboolean
gimp_browser_search_timeout (gpointer data)
{
GimpBrowser *browser = GIMP_BROWSER (data);
const gchar *search_string;
search_string = gtk_entry_get_text (GTK_ENTRY (browser->search_entry));
if (! search_string)
search_string = "";
g_signal_emit (data, browser_signals[SEARCH], 0,
search_string, browser->search_type);
browser->search_timeout_id = 0;
return FALSE;
}

View file

@ -0,0 +1,58 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpbrowser.h
* Copyright (C) 2005 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_BROWSER_H__
#define __GIMP_BROWSER_H__
G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
#define GIMP_TYPE_BROWSER (gimp_browser_get_type ())
G_DECLARE_FINAL_TYPE (GimpBrowser, gimp_browser, GIMP, BROWSER, GtkPaned)
GtkWidget * gimp_browser_new (void);
void gimp_browser_add_search_types (GimpBrowser *browser,
const gchar *first_type_label,
gint first_type_id,
...) G_GNUC_NULL_TERMINATED;
GtkWidget * gimp_browser_get_left_vbox (GimpBrowser *browser);
GtkWidget * gimp_browser_get_right_vbox (GimpBrowser *browser);
void gimp_browser_set_search_summary (GimpBrowser *browser,
const gchar *summary);
void gimp_browser_set_widget (GimpBrowser *browser,
GtkWidget *widget);
void gimp_browser_show_message (GimpBrowser *browser,
const gchar *message);
G_END_DECLS
#endif /* __GIMP_BROWSER_H__ */

View file

@ -0,0 +1,232 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpbusybox.c
* Copyright (C) 2018 Ell
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "gimpwidgetstypes.h"
#include "gimpbusybox.h"
#include "gimpwidgetsutils.h"
/**
* SECTION: gimpbusybox
* @title: GimpBusyBox
* @short_description: A widget indicating an ongoing operation
*
* #GimpBusyBox displays a styled message, providing indication of
* an ongoing operation.
**/
enum
{
PROP_0,
PROP_MESSAGE
};
struct _GimpBusyBox
{
GtkBox parent_instance;
GtkLabel *label;
};
/* local function prototypes */
static void gimp_busy_box_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_busy_box_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
G_DEFINE_TYPE (GimpBusyBox, gimp_busy_box, GTK_TYPE_BOX)
#define parent_class gimp_busy_box_parent_class
/* private functions */
static void
gimp_busy_box_class_init (GimpBusyBoxClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = gimp_busy_box_set_property;
object_class->get_property = gimp_busy_box_get_property;
/**
* GimpBusyBox:message:
*
* Specifies the displayed message.
*
* Since: 2.10.4
**/
g_object_class_install_property (object_class, PROP_MESSAGE,
g_param_spec_string ("message",
"Message",
"The message to display",
NULL,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
}
static void
gimp_busy_box_init (GimpBusyBox *box)
{
GtkWidget *spinner;
GtkWidget *label;
gtk_widget_set_halign (GTK_WIDGET (box), GTK_ALIGN_CENTER);
gtk_widget_set_valign (GTK_WIDGET (box), GTK_ALIGN_CENTER);
gtk_box_set_spacing (GTK_BOX (box), 8);
/* the spinner */
spinner = gtk_spinner_new ();
gtk_spinner_start (GTK_SPINNER (spinner));
gtk_box_pack_start (GTK_BOX (box), spinner, FALSE, FALSE, 0);
gtk_widget_show (spinner);
/* the label */
label = gtk_label_new (NULL);
box->label = GTK_LABEL (label);
gimp_label_set_attributes (GTK_LABEL (label),
PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
-1);
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
gtk_widget_show (label);
}
static void
gimp_busy_box_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpBusyBox *box = GIMP_BUSY_BOX (object);
switch (property_id)
{
case PROP_MESSAGE:
gtk_label_set_text (box->label, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_busy_box_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpBusyBox *box = GIMP_BUSY_BOX (object);
switch (property_id)
{
case PROP_MESSAGE:
g_value_set_string (value, gtk_label_get_text (box->label));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/* public functions */
/**
* gimp_busy_box_new:
* @message: (allow-none): the displayed message, or %NULL
*
* Creates a new #GimpBusyBox widget.
*
* Returns: A pointer to the new #GimpBusyBox widget.
*
* Since: 2.10.4
**/
GtkWidget *
gimp_busy_box_new (const gchar *message)
{
if (message == NULL)
message = "";
return g_object_new (GIMP_TYPE_BUSY_BOX,
"message", message,
NULL);
}
/**
* gimp_busy_box_set_message:
* @box: a #GimpBusyBox
* @message: the displayed message
*
* Sets the displayed message og @box to @message.
*
* Since: 2.10.4
**/
void
gimp_busy_box_set_message (GimpBusyBox *box,
const gchar *message)
{
g_return_if_fail (GIMP_IS_BUSY_BOX (box));
g_return_if_fail (message != NULL);
g_object_set (box,
"message", message,
NULL);
}
/**
* gimp_busy_box_get_message:
* @box: a #GimpBusyBox
*
* Returns the displayed message of @box.
*
* Returns: The displayed message.
*
* Since: 2.10.4
**/
const gchar *
gimp_busy_box_get_message (GimpBusyBox *box)
{
g_return_val_if_fail (GIMP_IS_BUSY_BOX (box), NULL);
return gtk_label_get_text (box->label);
}

View file

@ -0,0 +1,44 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpbusybox.h
* Copyright (C) 2018 Ell
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_BUSY_BOX_H__
#define __GIMP_BUSY_BOX_H__
G_BEGIN_DECLS
#define GIMP_TYPE_BUSY_BOX (gimp_busy_box_get_type ())
G_DECLARE_FINAL_TYPE (GimpBusyBox, gimp_busy_box, GIMP, BUSY_BOX, GtkBox)
GtkWidget * gimp_busy_box_new (const gchar *message);
void gimp_busy_box_set_message (GimpBusyBox *box,
const gchar *message);
const gchar * gimp_busy_box_get_message (GimpBusyBox *box);
G_END_DECLS
#endif /* __GIMP_BUSY_BOX_H__ */

172
libgimpwidgets/gimpbutton.c Normal file
View file

@ -0,0 +1,172 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpbutton.c
* Copyright (C) 2000-2008 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gtk/gtk.h>
#include "gimpwidgetstypes.h"
#include "gimpbutton.h"
/**
* SECTION: gimpbutton
* @title: GimpButton
* @short_description: A #GtkButton with a little extra functionality.
*
* #GimpButton adds an extra signal to the #GtkButton widget that
* allows the callback to distinguish a normal click from a click that
* was performed with modifier keys pressed.
**/
enum
{
EXTENDED_CLICKED,
LAST_SIGNAL
};
typedef struct _GimpButtonPrivate
{
GdkModifierType press_state;
} GimpButtonPrivate;
static gboolean gimp_button_button_press (GtkWidget *widget,
GdkEventButton *event);
static void gimp_button_clicked (GtkButton *button);
G_DEFINE_TYPE_WITH_PRIVATE (GimpButton, gimp_button, GTK_TYPE_BUTTON)
#define parent_class gimp_button_parent_class
static guint button_signals[LAST_SIGNAL] = { 0 };
static void
gimp_button_class_init (GimpButtonClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass);
/**
* GimpButton::extended-clicked:
* @gimpbutton: the object that received the signal.
* @arg1: the state of modifier keys when the button was clicked
*
* This signal is emitted when the button is clicked with a modifier
* key pressed.
**/
button_signals[EXTENDED_CLICKED] =
g_signal_new ("extended-clicked",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpButtonClass, extended_clicked),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
GDK_TYPE_MODIFIER_TYPE);
widget_class->button_press_event = gimp_button_button_press;
button_class->clicked = gimp_button_clicked;
}
static void
gimp_button_init (GimpButton *button)
{
}
/**
* gimp_button_new:
*
* Creates a new #GimpButton widget.
*
* Returns: A pointer to the new #GimpButton widget.
**/
GtkWidget *
gimp_button_new (void)
{
return g_object_new (GIMP_TYPE_BUTTON, NULL);
}
/**
* gimp_button_extended_clicked:
* @button: a #GimpButton.
* @modifier_state: a state as found in #GdkEventButton->state,
* e.g. #GDK_SHIFT_MASK.
*
* Emits the button's "extended_clicked" signal.
**/
void
gimp_button_extended_clicked (GimpButton *button,
GdkModifierType modifier_state)
{
g_return_if_fail (GIMP_IS_BUTTON (button));
g_signal_emit (button, button_signals[EXTENDED_CLICKED], 0, modifier_state);
}
static gboolean
gimp_button_button_press (GtkWidget *widget,
GdkEventButton *bevent)
{
GimpButton *button = GIMP_BUTTON (widget);
GimpButtonPrivate *private = gimp_button_get_instance_private (button);
if (bevent->button == 1)
{
private->press_state = bevent->state;
}
else
{
private->press_state = 0;
}
return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, bevent);
}
static void
gimp_button_clicked (GtkButton *button)
{
GimpButton *gimp_button = GIMP_BUTTON (button);
GimpButtonPrivate *private = gimp_button_get_instance_private (gimp_button);
if (private->press_state &
(GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK |
gtk_widget_get_modifier_mask (GTK_WIDGET (button),
GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR) |
gtk_widget_get_modifier_mask (GTK_WIDGET (button),
GDK_MODIFIER_INTENT_EXTEND_SELECTION) |
gtk_widget_get_modifier_mask (GTK_WIDGET (button),
GDK_MODIFIER_INTENT_MODIFY_SELECTION)))
{
g_signal_stop_emission_by_name (button, "clicked");
gimp_button_extended_clicked (GIMP_BUTTON (button), private->press_state);
}
else if (GTK_BUTTON_CLASS (parent_class)->clicked)
{
GTK_BUTTON_CLASS (parent_class)->clicked (button);
}
}

View file

@ -0,0 +1,66 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpbutton.h
* Copyright (C) 2001 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_BUTTON_H__
#define __GIMP_BUTTON_H__
G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
#define GIMP_TYPE_BUTTON (gimp_button_get_type ())
G_DECLARE_DERIVABLE_TYPE (GimpButton, gimp_button, GIMP, BUTTON, GtkButton)
struct _GimpButtonClass
{
GtkButtonClass parent_class;
void (* extended_clicked) (GimpButton *button,
GdkModifierType modifier_state);
/* Padding for future expansion */
void (* _gimp_reserved0) (void);
void (* _gimp_reserved1) (void);
void (* _gimp_reserved2) (void);
void (* _gimp_reserved3) (void);
void (* _gimp_reserved4) (void);
void (* _gimp_reserved5) (void);
void (* _gimp_reserved6) (void);
void (* _gimp_reserved7) (void);
void (* _gimp_reserved8) (void);
void (* _gimp_reserved9) (void);
};
GtkWidget * gimp_button_new (void);
void gimp_button_extended_clicked (GimpButton *button,
GdkModifierType modifier_state);
G_END_DECLS
#endif /* __GIMP_BUTTON_H__ */

View file

@ -0,0 +1,242 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcairo-utils.c
* Copyright (C) 2007 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpcolor/gimpcolor.h"
#include "libgimpconfig/gimpconfig.h"
#include "gimpcairo-utils.h"
#include "gimpwidgetsutils.h"
/**
* SECTION: gimpcairo-utils
* @title: GimpCairo-utils
* @short_description: Utility functions for cairo
*
* Utility functions that make cairo easier to use with common
* GIMP data types.
**/
/**
* gimp_cairo_set_focus_line_pattern:
* @cr: Cairo context
* @widget: widget to draw the focus indicator on
*
* Sets color and dash pattern for stroking a focus line on the given
* @cr. The line pattern is taken from @widget.
*
* Returns: %TRUE if the widget style has a focus line pattern,
* %FALSE otherwise
*
* Since: 2.6
**/
gboolean
gimp_cairo_set_focus_line_pattern (cairo_t *cr,
GtkWidget *widget)
{
gint8 *dash_list;
gboolean retval = FALSE;
g_return_val_if_fail (cr != NULL, FALSE);
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
gtk_widget_style_get (widget,
"focus-line-pattern", (gchar *) &dash_list,
NULL);
if (dash_list[0])
{
/* Taken straight from gtk_default_draw_focus()
*/
gint n_dashes = strlen ((const gchar *) dash_list);
gdouble *dashes = g_new (gdouble, n_dashes);
gint i;
for (i = 0; i < n_dashes; i++)
dashes[i] = dash_list[i];
cairo_set_dash (cr, dashes, n_dashes, 0.5);
g_free (dashes);
retval = TRUE;
}
g_free (dash_list);
return retval;
}
/**
* gimp_cairo_surface_create_from_pixbuf:
* @pixbuf: a #GdkPixbuf
*
* Create a Cairo image surface from a GdkPixbuf.
*
* You should avoid calling this function as there are probably more
* efficient ways of achieving the result you are looking for.
*
* Returns: a #cairo_surface_t.
*
* Since: 2.6
**/
cairo_surface_t *
gimp_cairo_surface_create_from_pixbuf (GdkPixbuf *pixbuf)
{
cairo_surface_t *surface;
cairo_format_t format;
guchar *dest;
const guchar *src;
gint width;
gint height;
gint src_stride;
gint dest_stride;
gint y;
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
width = gdk_pixbuf_get_width (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
switch (gdk_pixbuf_get_n_channels (pixbuf))
{
case 3:
format = CAIRO_FORMAT_RGB24;
break;
case 4:
format = CAIRO_FORMAT_ARGB32;
break;
default:
g_assert_not_reached ();
break;
}
surface = cairo_image_surface_create (format, width, height);
cairo_surface_flush (surface);
src = gdk_pixbuf_get_pixels (pixbuf);
src_stride = gdk_pixbuf_get_rowstride (pixbuf);
dest = cairo_image_surface_get_data (surface);
dest_stride = cairo_image_surface_get_stride (surface);
switch (format)
{
case CAIRO_FORMAT_RGB24:
for (y = 0; y < height; y++)
{
const guchar *s = src;
guchar *d = dest;
gint w = width;
while (w--)
{
GIMP_CAIRO_RGB24_SET_PIXEL (d, s[0], s[1], s[2]);
s += 3;
d += 4;
}
src += src_stride;
dest += dest_stride;
}
break;
case CAIRO_FORMAT_ARGB32:
for (y = 0; y < height; y++)
{
const guchar *s = src;
guchar *d = dest;
gint w = width;
while (w--)
{
GIMP_CAIRO_ARGB32_SET_PIXEL (d, s[0], s[1], s[2], s[3]);
s += 4;
d += 4;
}
src += src_stride;
dest += dest_stride;
}
break;
default:
break;
}
cairo_surface_mark_dirty (surface);
return surface;
}
/**
* gimp_cairo_set_source_color:
* @cr: Cairo context.
* @color: the [class@Gegl.Color] to use as source pattern within @cr.
* @config: the color management settings.
* @softproof: whether the color must also be soft-proofed.
* @widget: (nullable): [class@Gtk.Widget] to draw the focus indicator on.
*
* Sets @color as the source pattern within @cr, taking into account the profile
* of the [class@Gdk.Monitor] which @widget is displayed on.
*
* If @config is set, the color configuration as set by the user will be used,
* in particular using any custom monitor profile set in preferences (overriding
* system-set profile). If no such custom profile is set, it will use the
* profile of the monitor @widget is displayed on and will default to sRGB if
* @widget is %NULL.
*
* Use [func@Gimp.get_color_configuration] to retrieve the user
* [class@Gimp.ColorConfig].
*
* TODO: @softproof is currently unused.
*
* Since: 3.0
**/
void
gimp_cairo_set_source_color (cairo_t *cr,
GeglColor *color,
GimpColorConfig *config,
gboolean softproof,
GtkWidget *widget)
{
const Babl *space;
gdouble rgba[4];
g_return_if_fail (GEGL_IS_COLOR (color));
g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
space = gimp_widget_get_render_space (widget, config);
gegl_color_get_pixel (color, babl_format_with_space ("R'G'B'A double", space), rgba);
cairo_set_source_rgba (cr, rgba[0], rgba[1], rgba[2], rgba[3]);
}

View file

@ -0,0 +1,42 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcairo-utils.h
* Copyright (C) 2007 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_CAIRO_UTILS_H__
#define __GIMP_CAIRO_UTILS_H__
gboolean gimp_cairo_set_focus_line_pattern (cairo_t *cr,
GtkWidget *widget);
cairo_surface_t * gimp_cairo_surface_create_from_pixbuf (GdkPixbuf *pixbuf);
void gimp_cairo_set_source_color (cairo_t *cr,
GeglColor *color,
GimpColorConfig *config,
gboolean softproof,
GtkWidget *widget);
#endif /* __GIMP_CAIRO_UTILS_H__ */

View file

@ -0,0 +1,345 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcellrenderercolor.c
* Copyright (C) 2004,2007 Sven Neuman <sven1@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpcolor/gimpcolor.h"
#include "gimpwidgetstypes.h"
#include "gimpcairo-utils.h"
#include "gimpcellrenderercolor.h"
/**
* SECTION: gimpcellrenderercolor
* @title: GimpCellRendererColor
* @short_description: A #GtkCellRenderer to display a #GeglColor color.
*
* A #GtkCellRenderer to display a #GeglColor color.
**/
#define DEFAULT_ICON_SIZE GTK_ICON_SIZE_MENU
enum
{
PROP_0,
PROP_COLOR,
PROP_OPAQUE,
PROP_SIZE
};
struct _GimpCellRendererColor
{
GtkCellRenderer parent_instance;
GeglColor *color;
gboolean opaque;
GtkIconSize size;
gint border;
};
static void gimp_cell_renderer_color_finalize (GObject *object);
static void gimp_cell_renderer_color_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec);
static void gimp_cell_renderer_color_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_cell_renderer_color_get_size (GtkCellRenderer *cell,
GtkWidget *widget,
const GdkRectangle *rectangle,
gint *x_offset,
gint *y_offset,
gint *width,
gint *height);
static void gimp_cell_renderer_color_render (GtkCellRenderer *cell,
cairo_t *cr,
GtkWidget *widget,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags);
G_DEFINE_TYPE (GimpCellRendererColor, gimp_cell_renderer_color, GTK_TYPE_CELL_RENDERER)
#define parent_class gimp_cell_renderer_color_parent_class
static void
gimp_cell_renderer_color_class_init (GimpCellRendererColorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
object_class->finalize = gimp_cell_renderer_color_finalize;
object_class->get_property = gimp_cell_renderer_color_get_property;
object_class->set_property = gimp_cell_renderer_color_set_property;
cell_class->get_size = gimp_cell_renderer_color_get_size;
cell_class->render = gimp_cell_renderer_color_render;
g_object_class_install_property (object_class, PROP_COLOR,
gimp_param_spec_color ("color",
"Color",
"The displayed color",
TRUE, NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_OPAQUE,
g_param_spec_boolean ("opaque",
"Opaque",
"Whether to show transparency",
TRUE,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_SIZE,
g_param_spec_int ("icon-size",
"Icon Size",
"The cell's size",
0, G_MAXINT,
DEFAULT_ICON_SIZE,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
}
static void
gimp_cell_renderer_color_init (GimpCellRendererColor *cell)
{
cell->color = gegl_color_new ("black");
}
static void
gimp_cell_renderer_color_finalize (GObject *object)
{
GimpCellRendererColor *renderer = GIMP_CELL_RENDERER_COLOR (object);
g_clear_object (&renderer->color);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_cell_renderer_color_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec)
{
GimpCellRendererColor *renderer = GIMP_CELL_RENDERER_COLOR (object);
switch (param_id)
{
case PROP_COLOR:
g_clear_object (&renderer->color);
renderer->color = gegl_color_duplicate (g_value_get_object (value));
break;
case PROP_OPAQUE:
g_value_set_boolean (value, renderer->opaque);
break;
case PROP_SIZE:
g_value_set_int (value, renderer->size);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
gimp_cell_renderer_color_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec)
{
GimpCellRendererColor *renderer = GIMP_CELL_RENDERER_COLOR (object);
switch (param_id)
{
case PROP_COLOR:
g_set_object (&renderer->color, g_value_get_object (value));
break;
case PROP_OPAQUE:
renderer->opaque = g_value_get_boolean (value);
break;
case PROP_SIZE:
renderer->size = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
gimp_cell_renderer_color_get_size (GtkCellRenderer *cell,
GtkWidget *widget,
const GdkRectangle *cell_area,
gint *x_offset,
gint *y_offset,
gint *width,
gint *height)
{
GimpCellRendererColor *renderer = GIMP_CELL_RENDERER_COLOR (cell);
gint calc_width;
gint calc_height;
gfloat xalign;
gfloat yalign;
gint xpad;
gint ypad;
gtk_icon_size_lookup (renderer->size, &calc_width, &calc_height);
gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
if (cell_area && calc_width > 0 && calc_height > 0)
{
if (x_offset)
{
*x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
1.0 - xalign : xalign) *
(cell_area->width - calc_width));
*x_offset = MAX (*x_offset, 0) + xpad;
}
if (y_offset)
{
*y_offset = (yalign * (cell_area->height - calc_height));
*y_offset = MAX (*y_offset, 0) + ypad;
}
}
else
{
if (x_offset)
*x_offset = 0;
if (y_offset)
*y_offset = 0;
}
if (width)
*width = calc_width + 2 * xpad;
if (height)
*height = calc_height + 2 * ypad;
}
static void
gimp_cell_renderer_color_render (GtkCellRenderer *cell,
cairo_t *cr,
GtkWidget *widget,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags)
{
GimpCellRendererColor *renderer = GIMP_CELL_RENDERER_COLOR (cell);
GdkRectangle rect;
gint xpad;
gint ypad;
gimp_cell_renderer_color_get_size (cell, widget, cell_area,
&rect.x,
&rect.y,
&rect.width,
&rect.height);
gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
rect.x += cell_area->x + xpad;
rect.y += cell_area->y + ypad;
rect.width -= 2 * xpad;
rect.height -= 2 * ypad;
if (rect.width > 2 && rect.height > 2)
{
GtkStyleContext *context = gtk_widget_get_style_context (widget);
GtkStateFlags state;
GdkRGBA color;
gdouble rgba[4];
cairo_rectangle (cr,
rect.x + 1, rect.y + 1,
rect.width - 2, rect.height - 2);
gimp_cairo_set_source_color (cr, renderer->color, NULL, FALSE,
widget);
cairo_fill (cr);
gegl_color_get_pixel (renderer->color, babl_format ("R'G'B'A double"), rgba);
if (! renderer->opaque && rgba[3] < 1.0)
{
cairo_pattern_t *pattern;
cairo_move_to (cr, rect.x + 1, rect.y + rect.height - 1);
cairo_line_to (cr, rect.x + rect.width - 1, rect.y + rect.height - 1);
cairo_line_to (cr, rect.x + rect.width - 1, rect.y + 1);
cairo_close_path (cr);
pattern = gimp_cairo_checkerboard_create (cr,
GIMP_CHECK_SIZE_SM,
NULL, NULL);
cairo_set_source (cr, pattern);
cairo_pattern_destroy (pattern);
cairo_fill_preserve (cr);
gimp_cairo_set_source_color (cr, renderer->color, NULL, FALSE,
widget);
cairo_fill (cr);
}
/* draw border */
cairo_rectangle (cr,
rect.x + 0.5, rect.y + 0.5,
rect.width - 1, rect.height - 1);
state = gtk_cell_renderer_get_state (cell, widget, flags);
cairo_set_line_width (cr, 1);
gtk_style_context_get_color (context, state, &color);
gdk_cairo_set_source_rgba (cr, &color);
cairo_stroke (cr);
}
}
/**
* gimp_cell_renderer_color_new:
*
* Creates a #GtkCellRenderer that displays a color.
*
* Returns: a new #GimpCellRendererColor
*
* Since: 2.2
**/
GtkCellRenderer *
gimp_cell_renderer_color_new (void)
{
return g_object_new (GIMP_TYPE_CELL_RENDERER_COLOR, NULL);
}

View file

@ -0,0 +1,41 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcellrenderercolor.h
* Copyright (C) 2004 Sven Neuman <sven1@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_CELL_RENDERER_COLOR_H__
#define __GIMP_CELL_RENDERER_COLOR_H__
G_BEGIN_DECLS
#define GIMP_TYPE_CELL_RENDERER_COLOR (gimp_cell_renderer_color_get_type ())
G_DECLARE_FINAL_TYPE (GimpCellRendererColor, gimp_cell_renderer_color, GIMP, CELL_RENDERER_COLOR, GtkCellRenderer)
GtkCellRenderer * gimp_cell_renderer_color_new (void);
G_END_DECLS
#endif /* __GIMP_CELL_RENDERER_COLOR_H__ */

View file

@ -0,0 +1,571 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcellrenderertoggle.c
* Copyright (C) 2003-2004 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "gimpwidgetstypes.h"
#include "gimpwidgetsmarshal.h"
#include "gimpcellrenderertoggle.h"
/**
* SECTION: gimpcellrenderertoggle
* @title: GimpCellRendererToggle
* @short_description: A #GtkCellRendererToggle that displays icons instead
* of a checkbox.
*
* A #GtkCellRendererToggle that displays icons instead of a checkbox.
**/
#define DEFAULT_ICON_SIZE 16
enum
{
CLICKED,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_ICON_NAME,
PROP_ICON_SIZE,
PROP_OVERRIDE_BACKGROUND
};
struct _GimpCellRendererToggle
{
GtkCellRendererToggle parent_instance;
gchar *icon_name;
gint icon_size;
gboolean override_background;
GdkPixbuf *pixbuf;
};
static void gimp_cell_renderer_toggle_finalize (GObject *object);
static void gimp_cell_renderer_toggle_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec);
static void gimp_cell_renderer_toggle_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_cell_renderer_toggle_get_size (GtkCellRenderer *cell,
GtkWidget *widget,
const GdkRectangle *rectangle,
gint *x_offset,
gint *y_offset,
gint *width,
gint *height);
static void gimp_cell_renderer_toggle_render (GtkCellRenderer *cell,
cairo_t *cr,
GtkWidget *widget,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags);
static gboolean gimp_cell_renderer_toggle_activate (GtkCellRenderer *cell,
GdkEvent *event,
GtkWidget *widget,
const gchar *path,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags);
static void gimp_cell_renderer_toggle_create_pixbuf (GimpCellRendererToggle *toggle,
GtkWidget *widget);
G_DEFINE_TYPE (GimpCellRendererToggle, gimp_cell_renderer_toggle, GTK_TYPE_CELL_RENDERER_TOGGLE)
#define parent_class gimp_cell_renderer_toggle_parent_class
static guint toggle_cell_signals[LAST_SIGNAL] = { 0 };
static void
gimp_cell_renderer_toggle_class_init (GimpCellRendererToggleClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
toggle_cell_signals[CLICKED] =
g_signal_new ("clicked",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
_gimp_widgets_marshal_VOID__STRING_FLAGS,
G_TYPE_NONE, 2,
G_TYPE_STRING,
GDK_TYPE_MODIFIER_TYPE);
object_class->finalize = gimp_cell_renderer_toggle_finalize;
object_class->get_property = gimp_cell_renderer_toggle_get_property;
object_class->set_property = gimp_cell_renderer_toggle_set_property;
cell_class->get_size = gimp_cell_renderer_toggle_get_size;
cell_class->render = gimp_cell_renderer_toggle_render;
cell_class->activate = gimp_cell_renderer_toggle_activate;
g_object_class_install_property (object_class, PROP_ICON_NAME,
g_param_spec_string ("icon-name",
"Icon Name",
"The icon to display",
NULL,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_ICON_SIZE,
g_param_spec_int ("icon-size",
"Icon Size",
"The desired icon size to use in pixel (before applying scaling factor)",
0, G_MAXINT,
DEFAULT_ICON_SIZE,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_OVERRIDE_BACKGROUND,
g_param_spec_boolean ("override-background",
"Override Background",
"Draw the background if the row is selected",
FALSE,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
}
static void
gimp_cell_renderer_toggle_init (GimpCellRendererToggle *toggle)
{
}
static void
gimp_cell_renderer_toggle_finalize (GObject *object)
{
GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (object);
g_clear_pointer (&toggle->icon_name, g_free);
g_clear_object (&toggle->pixbuf);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_cell_renderer_toggle_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec)
{
GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (object);
switch (param_id)
{
case PROP_ICON_NAME:
g_value_set_string (value, toggle->icon_name);
break;
case PROP_ICON_SIZE:
g_value_set_int (value, toggle->icon_size);
break;
case PROP_OVERRIDE_BACKGROUND:
g_value_set_boolean (value, toggle->override_background);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
gimp_cell_renderer_toggle_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec)
{
GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (object);
switch (param_id)
{
case PROP_ICON_NAME:
if (toggle->icon_name)
g_free (toggle->icon_name);
toggle->icon_name = g_value_dup_string (value);
break;
case PROP_ICON_SIZE:
toggle->icon_size = g_value_get_int (value);
break;
case PROP_OVERRIDE_BACKGROUND:
toggle->override_background = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
g_clear_object (&toggle->pixbuf);
}
static void
gimp_cell_renderer_toggle_get_size (GtkCellRenderer *cell,
GtkWidget *widget,
const GdkRectangle *cell_area,
gint *x_offset,
gint *y_offset,
gint *width,
gint *height)
{
GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (cell);
GtkStyleContext *context = gtk_widget_get_style_context (widget);
GtkBorder border;
gint scale_factor;
gint calc_width;
gint calc_height;
gint pixbuf_width;
gint pixbuf_height;
gfloat xalign;
gfloat yalign;
gint xpad;
gint ypad;
scale_factor = gtk_widget_get_scale_factor (widget);
if (! toggle->icon_name)
{
GTK_CELL_RENDERER_CLASS (parent_class)->get_size (cell,
widget,
cell_area,
x_offset, y_offset,
width, height);
return;
}
gtk_style_context_save (context);
gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
if (! toggle->pixbuf)
gimp_cell_renderer_toggle_create_pixbuf (toggle, widget);
pixbuf_width = gdk_pixbuf_get_width (toggle->pixbuf);
pixbuf_height = gdk_pixbuf_get_height (toggle->pixbuf);
/* The pixbuf size may be bigger than the logical size. */
calc_width = pixbuf_width / scale_factor + (gint) xpad * 2;
calc_height = pixbuf_height / scale_factor + (gint) ypad * 2;
gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
gtk_style_context_get_border (context, 0, &border);
calc_width += border.left + border.right;
calc_height += border.top + border.bottom;
if (width)
*width = calc_width;
if (height)
*height = calc_height;
if (cell_area)
{
if (x_offset)
{
*x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
(1.0 - xalign) : xalign) *
(cell_area->width - calc_width));
*x_offset = MAX (*x_offset, 0);
}
if (y_offset)
{
*y_offset = yalign * (cell_area->height - calc_height);
*y_offset = MAX (*y_offset, 0);
}
}
gtk_style_context_restore (context);
}
static void
gimp_cell_renderer_toggle_render (GtkCellRenderer *cell,
cairo_t *cr,
GtkWidget *widget,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags)
{
GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (cell);
GtkStyleContext *context = gtk_widget_get_style_context (widget);
GdkRectangle toggle_rect;
GtkStateFlags state;
gboolean active;
gint scale_factor;
gint xpad;
gint ypad;
scale_factor = gtk_widget_get_scale_factor (widget);
if (! toggle->icon_name)
{
GTK_CELL_RENDERER_CLASS (parent_class)->render (cell, cr, widget,
background_area,
cell_area,
flags);
return;
}
if ((flags & GTK_CELL_RENDERER_SELECTED) && toggle->override_background)
{
gboolean background_set;
g_object_get (cell,
"cell-background-set", &background_set,
NULL);
if (background_set)
{
GdkRGBA *color;
g_object_get (cell,
"cell-background-rgba", &color,
NULL);
gdk_cairo_rectangle (cr, background_area);
gdk_cairo_set_source_rgba (cr, color);
cairo_fill (cr);
gdk_rgba_free (color);
}
}
gimp_cell_renderer_toggle_get_size (cell, widget, cell_area,
&toggle_rect.x,
&toggle_rect.y,
&toggle_rect.width,
&toggle_rect.height);
gtk_style_context_save (context);
gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
gtk_style_context_add_class (context, "toggle-icon");
gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
toggle_rect.x += cell_area->x + xpad;
toggle_rect.y += cell_area->y + ypad;
toggle_rect.width -= xpad * 2;
toggle_rect.height -= ypad * 2;
if (toggle_rect.width <= 0 || toggle_rect.height <= 0)
return;
state = gtk_cell_renderer_get_state (cell, widget, flags);
active =
gtk_cell_renderer_toggle_get_active (GTK_CELL_RENDERER_TOGGLE (cell));
if (active)
{
gtk_style_context_add_class (context, "visible");
state |= GTK_STATE_FLAG_ACTIVE;
}
if (! gtk_cell_renderer_toggle_get_activatable (GTK_CELL_RENDERER_TOGGLE (cell)))
state |= GTK_STATE_FLAG_INSENSITIVE;
gtk_style_context_set_state (context, state);
if (state & GTK_STATE_FLAG_PRELIGHT)
gtk_render_frame (context, cr,
toggle_rect.x, toggle_rect.y,
toggle_rect.width, toggle_rect.height);
if (active)
{
GtkBorder border;
gboolean inconsistent;
gtk_style_context_get_border (context, 0, &border);
toggle_rect.x += border.left;
toggle_rect.x *= scale_factor;
toggle_rect.y += border.top;
toggle_rect.y *= scale_factor;
toggle_rect.width -= border.left + border.right;
toggle_rect.height -= border.top + border.bottom;
/* For high DPI displays, pixbuf size is bigger than logical size. */
cairo_scale (cr, (gdouble) 1.0 / scale_factor, (gdouble) 1.0 / scale_factor);
gdk_cairo_set_source_pixbuf (cr, toggle->pixbuf,
toggle_rect.x, toggle_rect.y);
cairo_paint (cr);
g_object_get (cell,
"inconsistent", &inconsistent,
NULL);
if (inconsistent)
{
GdkRGBA color;
gtk_style_context_get_color (context, state, &color);
gdk_cairo_set_source_rgba (cr, &color);
cairo_set_line_width (cr, scale_factor * 1.5);
cairo_move_to (cr,
toggle_rect.x + scale_factor * (toggle_rect.width - 1),
toggle_rect.y + scale_factor);
cairo_line_to (cr,
toggle_rect.x + scale_factor,
toggle_rect.y + scale_factor * (toggle_rect.height - 1));
cairo_stroke (cr);
}
}
gtk_style_context_restore (context);
}
static gboolean
gimp_cell_renderer_toggle_activate (GtkCellRenderer *cell,
GdkEvent *event,
GtkWidget *widget,
const gchar *path,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags)
{
GtkCellRendererToggle *toggle = GTK_CELL_RENDERER_TOGGLE (cell);
if (gtk_cell_renderer_toggle_get_activatable (toggle))
{
GdkModifierType state = 0;
if (event && ((GdkEventAny *) event)->type == GDK_BUTTON_PRESS)
state = ((GdkEventButton *) event)->state;
gimp_cell_renderer_toggle_clicked (GIMP_CELL_RENDERER_TOGGLE (cell),
path, state);
return TRUE;
}
return FALSE;
}
static void
gimp_cell_renderer_toggle_create_pixbuf (GimpCellRendererToggle *toggle,
GtkWidget *widget)
{
g_clear_object (&toggle->pixbuf);
if (toggle->icon_name)
{
GdkPixbuf *pixbuf;
GdkScreen *screen;
GtkIconTheme *icon_theme;
GtkIconInfo *icon_info;
gchar *icon_name;
gint scale_factor;
scale_factor = gtk_widget_get_scale_factor (widget);
screen = gtk_widget_get_screen (widget);
icon_theme = gtk_icon_theme_get_for_screen (screen);
/* Look for symbolic and fallback to color icon. */
icon_name = g_strdup_printf ("%s-symbolic", toggle->icon_name);
icon_info = gtk_icon_theme_lookup_icon_for_scale (icon_theme, icon_name,
toggle->icon_size, scale_factor,
GTK_ICON_LOOKUP_GENERIC_FALLBACK);
g_free (icon_name);
if (! icon_info)
{
icon_info = gtk_icon_theme_lookup_icon_for_scale (icon_theme, "image-missing",
toggle->icon_size, scale_factor,
GTK_ICON_LOOKUP_GENERIC_FALLBACK |
GTK_ICON_LOOKUP_USE_BUILTIN);
}
g_return_if_fail (icon_info != NULL);
pixbuf = gtk_icon_info_load_symbolic_for_context (icon_info,
gtk_widget_get_style_context (widget),
NULL, NULL);
toggle->pixbuf = pixbuf;
g_object_unref (icon_info);
}
}
/**
* gimp_cell_renderer_toggle_new:
* @icon_name: the icon name of the icon to use for the active state
*
* Creates a custom version of the #GtkCellRendererToggle. Instead of
* showing the standard toggle button, it shows a named icon if the
* cell is active and no icon otherwise. This cell renderer is for
* example used in the Layers treeview to indicate and control the
* layer's visibility by showing %GIMP_STOCK_VISIBLE.
*
* Returns: a new #GimpCellRendererToggle
*
* Since: 2.2
**/
GtkCellRenderer *
gimp_cell_renderer_toggle_new (const gchar *icon_name)
{
return g_object_new (GIMP_TYPE_CELL_RENDERER_TOGGLE,
"icon-name", icon_name,
NULL);
}
/**
* gimp_cell_renderer_toggle_clicked:
* @cell: a #GimpCellRendererToggle
* @path: the path to the clicked row
* @state: the modifier state
*
* Emits the "clicked" signal from a #GimpCellRendererToggle.
*
* Since: 2.2
**/
void
gimp_cell_renderer_toggle_clicked (GimpCellRendererToggle *cell,
const gchar *path,
GdkModifierType state)
{
g_return_if_fail (GIMP_IS_CELL_RENDERER_TOGGLE (cell));
g_return_if_fail (path != NULL);
g_signal_emit (cell, toggle_cell_signals[CLICKED], 0, path, state);
}

View file

@ -0,0 +1,45 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcellrenderertoggle.h
* Copyright (C) 2003-2004 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_CELL_RENDERER_TOGGLE_H__
#define __GIMP_CELL_RENDERER_TOGGLE_H__
G_BEGIN_DECLS
#define GIMP_TYPE_CELL_RENDERER_TOGGLE (gimp_cell_renderer_toggle_get_type ())
G_DECLARE_FINAL_TYPE (GimpCellRendererToggle, gimp_cell_renderer_toggle, GIMP, CELL_RENDERER_TOGGLE, GtkCellRendererToggle)
GtkCellRenderer * gimp_cell_renderer_toggle_new (const gchar *icon_name);
void gimp_cell_renderer_toggle_clicked (GimpCellRendererToggle *cell,
const gchar *path,
GdkModifierType state);
G_END_DECLS
#endif /* __GIMP_CELL_RENDERER_TOGGLE_H__ */

View file

@ -0,0 +1,602 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpchainbutton.c
* Copyright (C) 1999-2000 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "gimpwidgetstypes.h"
#include "gimpchainbutton.h"
#include "gimpicons.h"
/**
* SECTION: gimpchainbutton
* @title: GimpChainButton
* @short_description: Widget to visually connect two entry widgets.
* @see_also: You may want to use the convenience function
* gimp_coordinates_new() to set up two GimpSizeEntries
* (see #GimpSizeEntry) linked with a #GimpChainButton.
*
* This widget provides a button showing either a linked or a broken
* chain that can be used to link two entries, spinbuttons, colors or
* other GUI elements and show that they may be locked. Use it for
* example to connect X and Y ratios to provide the possibility of a
* constrained aspect ratio.
*
* The #GimpChainButton only gives visual feedback, it does not really
* connect widgets. You have to take care of locking the values
* yourself by checking the state of the #GimpChainButton whenever a
* value changes in one of the connected widgets and adjusting the
* other value if necessary.
**/
enum
{
PROP_0,
PROP_POSITION,
PROP_ICON_SIZE,
PROP_ACTIVE
};
enum
{
TOGGLED,
LAST_SIGNAL
};
struct _GimpChainButton
{
GtkGrid parent_instance;
GimpChainPosition position;
gboolean active;
GtkWidget *button;
GtkWidget *line1;
GtkWidget *line2;
GtkWidget *image;
};
static void gimp_chain_button_constructed (GObject *object);
static void gimp_chain_button_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_chain_button_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_chain_button_compute_expand (GtkWidget *widget,
gboolean *hexpand_p,
gboolean *vexpand_p);
static void gimp_chain_button_clicked_callback (GtkWidget *widget,
GimpChainButton *button);
static void gimp_chain_button_update_image (GimpChainButton *button);
static GtkWidget * gimp_chain_line_new (GimpChainPosition position,
gint which);
G_DEFINE_TYPE (GimpChainButton, gimp_chain_button, GTK_TYPE_GRID)
#define parent_class gimp_chain_button_parent_class
static guint gimp_chain_button_signals[LAST_SIGNAL] = { 0 };
static const gchar * const gimp_chain_icon_names[] =
{
GIMP_ICON_CHAIN_HORIZONTAL,
GIMP_ICON_CHAIN_HORIZONTAL_BROKEN,
GIMP_ICON_CHAIN_VERTICAL,
GIMP_ICON_CHAIN_VERTICAL_BROKEN
};
static void
gimp_chain_button_class_init (GimpChainButtonClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->constructed = gimp_chain_button_constructed;
object_class->set_property = gimp_chain_button_set_property;
object_class->get_property = gimp_chain_button_get_property;
widget_class->compute_expand = gimp_chain_button_compute_expand;
gimp_chain_button_signals[TOGGLED] =
g_signal_new ("toggled",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
/**
* GimpChainButton:position:
*
* The position in which the chain button will be used.
*
* Since: 2.4
*/
g_object_class_install_property (object_class, PROP_POSITION,
g_param_spec_enum ("position",
"Position",
"The chain's position",
GIMP_TYPE_CHAIN_POSITION,
GIMP_CHAIN_TOP,
G_PARAM_CONSTRUCT_ONLY |
GIMP_PARAM_READWRITE));
/**
* GimpChainButton:icon-size:
*
* The chain button icon size.
*
* Since: 2.10.10
*/
g_object_class_install_property (object_class, PROP_ICON_SIZE,
g_param_spec_enum ("icon-size",
"Icon Size",
"The chain's icon size",
GTK_TYPE_ICON_SIZE,
GTK_ICON_SIZE_BUTTON,
G_PARAM_CONSTRUCT |
GIMP_PARAM_READWRITE));
/**
* GimpChainButton:active:
*
* The toggled state of the chain button.
*
* Since: 2.10.10
*/
g_object_class_install_property (object_class, PROP_ACTIVE,
g_param_spec_boolean ("active",
"Active",
"The chain's toggled state",
FALSE,
G_PARAM_CONSTRUCT |
GIMP_PARAM_READWRITE));
}
static void
gimp_chain_button_init (GimpChainButton *button)
{
button->position = GIMP_CHAIN_TOP;
button->active = FALSE;
button->image = gtk_image_new ();
button->button = gtk_button_new ();
gtk_button_set_relief (GTK_BUTTON (button->button), GTK_RELIEF_NONE);
gtk_container_add (GTK_CONTAINER (button->button), button->image);
gtk_widget_show (button->image);
g_signal_connect (button->button, "clicked",
G_CALLBACK (gimp_chain_button_clicked_callback),
button);
}
static void
gimp_chain_button_constructed (GObject *object)
{
GimpChainButton *button = GIMP_CHAIN_BUTTON (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
button->line1 = gimp_chain_line_new (button->position, 1);
button->line2 = gimp_chain_line_new (button->position, -1);
gimp_chain_button_update_image (button);
if (button->position & GIMP_CHAIN_LEFT) /* are we a vertical chainbutton? */
{
gtk_widget_set_vexpand (button->line1, TRUE);
gtk_widget_set_vexpand (button->line2, TRUE);
gtk_grid_attach (GTK_GRID (button), button->line1, 0, 0, 1, 1);
gtk_grid_attach (GTK_GRID (button), button->button, 0, 1, 1, 1);
gtk_grid_attach (GTK_GRID (button), button->line2, 0, 2, 1, 1);
}
else
{
gtk_widget_set_hexpand (button->line1, TRUE);
gtk_widget_set_hexpand (button->line2, TRUE);
gtk_grid_attach (GTK_GRID (button), button->line1, 0, 0, 1, 1);
gtk_grid_attach (GTK_GRID (button), button->button, 1, 0, 1, 1);
gtk_grid_attach (GTK_GRID (button), button->line2, 2, 0, 1, 1);
}
gtk_widget_show (button->button);
gtk_widget_show (button->line1);
gtk_widget_show (button->line2);
}
static void
gimp_chain_button_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpChainButton *button = GIMP_CHAIN_BUTTON (object);
switch (property_id)
{
case PROP_POSITION:
button->position = g_value_get_enum (value);
break;
case PROP_ICON_SIZE:
g_object_set_property (G_OBJECT (button->image), "icon-size", value);
break;
case PROP_ACTIVE:
gimp_chain_button_set_active (button, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_chain_button_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpChainButton *button = GIMP_CHAIN_BUTTON (object);
switch (property_id)
{
case PROP_POSITION:
g_value_set_enum (value, button->position);
break;
case PROP_ICON_SIZE:
g_object_get_property (G_OBJECT (button->image), "icon-size", value);
break;
case PROP_ACTIVE:
g_value_set_boolean (value, gimp_chain_button_get_active (button));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_chain_button_compute_expand (GtkWidget *widget,
gboolean *hexpand_p,
gboolean *vexpand_p)
{
/* don't inherit [hv]expand from the chain lines. see issue #3876. */
*hexpand_p = FALSE;
*vexpand_p = FALSE;
}
/**
* gimp_chain_button_new:
* @position: The position you are going to use for the button
* with respect to the widgets you want to chain.
*
* Creates a new #GimpChainButton widget.
*
* This returns a button showing either a broken or a linked chain and
* small clamps attached to both sides that visually group the two
* widgets you want to connect. This widget looks best when attached
* to a grid taking up two columns (or rows respectively) next to the
* widgets that it is supposed to connect. It may work for more than
* two widgets, but the look is optimized for two.
*
* Returns: Pointer to the new #GimpChainButton, which is inactive
* by default. Use gimp_chain_button_set_active() to
* change its state.
*/
GtkWidget *
gimp_chain_button_new (GimpChainPosition position)
{
return g_object_new (GIMP_TYPE_CHAIN_BUTTON,
"position", position,
NULL);
}
/**
* gimp_chain_button_set_icon_size:
* @button: Pointer to a #GimpChainButton.
* @size: The new icon size.
*
* Sets the icon size of the #GimpChainButton.
*
* Since: 2.10.10
*/
void
gimp_chain_button_set_icon_size (GimpChainButton *button,
GtkIconSize size)
{
g_return_if_fail (GIMP_IS_CHAIN_BUTTON (button));
g_object_set (button,
"icon-size", size,
NULL);
}
/**
* gimp_chain_button_get_icon_size:
* @button: Pointer to a #GimpChainButton.
*
* Gets the icon size of the #GimpChainButton.
*
* Returns: The icon size.
*
* Since: 2.10.10
*/
GtkIconSize
gimp_chain_button_get_icon_size (GimpChainButton *button)
{
GtkIconSize size;
g_return_val_if_fail (GIMP_IS_CHAIN_BUTTON (button), GTK_ICON_SIZE_BUTTON);
g_object_get (button,
"icon-size", &size,
NULL);
return size;
}
/**
* gimp_chain_button_set_active:
* @button: Pointer to a #GimpChainButton.
* @active: The new state.
*
* Sets the state of the #GimpChainButton to be either locked (%TRUE) or
* unlocked (%FALSE) and changes the showed pixmap to reflect the new state.
*/
void
gimp_chain_button_set_active (GimpChainButton *button,
gboolean active)
{
g_return_if_fail (GIMP_IS_CHAIN_BUTTON (button));
button = gimp_chain_button_get_instance_private (button);
if (button->active != active)
{
button->active = active ? TRUE : FALSE;
gimp_chain_button_update_image (button);
g_signal_emit (button, gimp_chain_button_signals[TOGGLED], 0);
g_object_notify (G_OBJECT (button), "active");
}
}
/**
* gimp_chain_button_get_active
* @button: Pointer to a #GimpChainButton.
*
* Checks the state of the #GimpChainButton.
*
* Returns: %TRUE if the #GimpChainButton is active (locked).
*/
gboolean
gimp_chain_button_get_active (GimpChainButton *button)
{
g_return_val_if_fail (GIMP_IS_CHAIN_BUTTON (button), FALSE);
button = gimp_chain_button_get_instance_private (button);
return button->active;
}
/**
* gimp_chain_button_get_button
* @button: A #GimpChainButton.
*
* Returns: (transfer none) (type GtkButton): The #GimpChainButton's button.
*
* Since: 3.0
*/
GtkWidget *
gimp_chain_button_get_button (GimpChainButton *button)
{
g_return_val_if_fail (GIMP_IS_CHAIN_BUTTON (button), FALSE);
button = gimp_chain_button_get_instance_private (button);
return button->button;
}
/* private functions */
static void
gimp_chain_button_clicked_callback (GtkWidget *widget,
GimpChainButton *button)
{
gimp_chain_button_set_active (button, ! button->active);
}
static void
gimp_chain_button_update_image (GimpChainButton *button)
{
guint i;
i = ((button->position & GIMP_CHAIN_LEFT) << 1) + (button->active ? 0 : 1);
gtk_image_set_from_icon_name (GTK_IMAGE (button->image),
gimp_chain_icon_names[i],
gimp_chain_button_get_icon_size (button));
}
/* GimpChainLine is a simple no-window widget for drawing the lines.
*
* Originally this used to be a GtkDrawingArea but this turned out to
* be a bad idea. We don't need an extra window to draw on and we also
* don't need any input events.
*/
static GType gimp_chain_line_get_type (void) G_GNUC_CONST;
static gboolean gimp_chain_line_draw (GtkWidget *widget,
cairo_t *cr);
struct _GimpChainLine
{
GtkWidget parent_instance;
GimpChainPosition position;
gint which;
};
typedef struct _GimpChainLine GimpChainLine;
typedef GtkWidgetClass GimpChainLineClass;
G_DEFINE_TYPE (GimpChainLine, gimp_chain_line, GTK_TYPE_WIDGET)
static void
gimp_chain_line_class_init (GimpChainLineClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
widget_class->draw = gimp_chain_line_draw;
}
static void
gimp_chain_line_init (GimpChainLine *line)
{
gtk_widget_set_has_window (GTK_WIDGET (line), FALSE);
}
static GtkWidget *
gimp_chain_line_new (GimpChainPosition position,
gint which)
{
GimpChainLine *line = g_object_new (gimp_chain_line_get_type (), NULL);
line->position = position;
line->which = which;
return GTK_WIDGET (line);
}
static gboolean
gimp_chain_line_draw (GtkWidget *widget,
cairo_t *cr)
{
GtkStyleContext *context = gtk_widget_get_style_context (widget);
GimpChainLine *line = ((GimpChainLine *) widget);
GtkAllocation allocation;
GdkPoint points[3];
GimpChainPosition position;
GdkRGBA color;
gtk_widget_get_allocation (widget, &allocation);
#define SHORT_LINE 4
points[0].x = allocation.width / 2;
points[0].y = allocation.height / 2;
position = line->position;
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
{
switch (position)
{
case GIMP_CHAIN_TOP:
case GIMP_CHAIN_BOTTOM:
break;
case GIMP_CHAIN_LEFT:
position = GIMP_CHAIN_RIGHT;
break;
case GIMP_CHAIN_RIGHT:
position = GIMP_CHAIN_LEFT;
break;
}
}
switch (position)
{
case GIMP_CHAIN_LEFT:
points[0].x += SHORT_LINE;
points[1].x = points[0].x - SHORT_LINE;
points[1].y = points[0].y;
points[2].x = points[1].x;
points[2].y = (line->which == 1 ? allocation.height - 1 : 0);
break;
case GIMP_CHAIN_RIGHT:
points[0].x -= SHORT_LINE;
points[1].x = points[0].x + SHORT_LINE;
points[1].y = points[0].y;
points[2].x = points[1].x;
points[2].y = (line->which == 1 ? allocation.height - 1 : 0);
break;
case GIMP_CHAIN_TOP:
points[0].y += SHORT_LINE;
points[1].x = points[0].x;
points[1].y = points[0].y - SHORT_LINE;
points[2].x = (line->which == 1 ? allocation.width - 1 : 0);
points[2].y = points[1].y;
break;
case GIMP_CHAIN_BOTTOM:
points[0].y -= SHORT_LINE;
points[1].x = points[0].x;
points[1].y = points[0].y + SHORT_LINE;
points[2].x = (line->which == 1 ? allocation.width - 1 : 0);
points[2].y = points[1].y;
break;
default:
return FALSE;
}
cairo_move_to (cr, points[0].x, points[0].y);
cairo_line_to (cr, points[1].x, points[1].y);
cairo_line_to (cr, points[2].x, points[2].y);
cairo_set_line_width (cr, 2.0);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
gtk_style_context_get_color (context, gtk_widget_get_state_flags (widget), &color);
gdk_cairo_set_source_rgba (cr, &color);
cairo_stroke (cr);
return TRUE;
}

View file

@ -0,0 +1,59 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpchainbutton.h
* Copyright (C) 1999-2000 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
/*
* This implements a widget derived from GtkGrid that visualizes
* it's state with two different pixmaps showing a closed and a
* broken chain. It's intended to be used with the GimpSizeEntry
* widget. The usage is quite similar to the one the GtkToggleButton
* provides.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_CHAIN_BUTTON_H__
#define __GIMP_CHAIN_BUTTON_H__
G_BEGIN_DECLS
#define GIMP_TYPE_CHAIN_BUTTON (gimp_chain_button_get_type ())
G_DECLARE_FINAL_TYPE (GimpChainButton, gimp_chain_button, GIMP, CHAIN_BUTTON, GtkGrid)
GtkWidget * gimp_chain_button_new (GimpChainPosition position);
void gimp_chain_button_set_icon_size (GimpChainButton *button,
GtkIconSize size);
GtkIconSize gimp_chain_button_get_icon_size (GimpChainButton *button);
void gimp_chain_button_set_active (GimpChainButton *button,
gboolean active);
gboolean gimp_chain_button_get_active (GimpChainButton *button);
GtkWidget * gimp_chain_button_get_button (GimpChainButton *button);
G_END_DECLS
#endif /* __GIMP_CHAIN_BUTTON_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,65 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorarea.h
* Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
/* This provides a color preview area. The preview
* can handle transparency by showing the checkerboard and
* handles drag'n'drop.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_COLOR_AREA_H__
#define __GIMP_COLOR_AREA_H__
G_BEGIN_DECLS
#define GIMP_TYPE_COLOR_AREA (gimp_color_area_get_type ())
G_DECLARE_FINAL_TYPE (GimpColorArea, gimp_color_area, GIMP, COLOR_AREA, GtkDrawingArea)
GtkWidget * gimp_color_area_new (GeglColor *color,
GimpColorAreaType type,
GdkModifierType drag_mask);
void gimp_color_area_set_color (GimpColorArea *area,
GeglColor *color);
GeglColor * gimp_color_area_get_color (GimpColorArea *area);
gboolean gimp_color_area_has_alpha (GimpColorArea *area);
void gimp_color_area_set_type (GimpColorArea *area,
GimpColorAreaType type);
void gimp_color_area_enable_drag (GimpColorArea *area,
GdkModifierType drag_mask);
void gimp_color_area_set_draw_border (GimpColorArea *area,
gboolean draw_border);
void gimp_color_area_set_out_of_gamut (GimpColorArea *area,
gboolean out_of_gamut);
void gimp_color_area_set_color_config (GimpColorArea *area,
GimpColorConfig *config);
G_END_DECLS
#endif /* __GIMP_COLOR_AREA_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,97 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorbutton.h
* Copyright (C) 1999-2001 Sven Neumann
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
/* This provides a button with a color preview. The preview
* can handle transparency by showing the checkerboard.
* On click, a color selector is opened, which is already
* fully functional wired to the preview button.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_COLOR_BUTTON_H__
#define __GIMP_COLOR_BUTTON_H__
#include <libgimpwidgets/gimpbutton.h>
G_BEGIN_DECLS
#define GIMP_TYPE_COLOR_BUTTON (gimp_color_button_get_type ())
G_DECLARE_DERIVABLE_TYPE (GimpColorButton, gimp_color_button, GIMP, COLOR_BUTTON, GimpButton)
struct _GimpColorButtonClass
{
GimpButtonClass parent_class;
/* signals */
void (* color_changed) (GimpColorButton *button);
/* virtual functions */
GType (* get_action_type) (GimpColorButton *button);
/* Padding for future expansion */
void (* _gimp_reserved0) (void);
void (* _gimp_reserved1) (void);
void (* _gimp_reserved2) (void);
void (* _gimp_reserved3) (void);
void (* _gimp_reserved4) (void);
void (* _gimp_reserved5) (void);
void (* _gimp_reserved6) (void);
void (* _gimp_reserved7) (void);
void (* _gimp_reserved8) (void);
void (* _gimp_reserved9) (void);
};
GtkWidget * gimp_color_button_new (const gchar *title,
gint width,
gint height,
GeglColor *color,
GimpColorAreaType type);
void gimp_color_button_set_title (GimpColorButton *button,
const gchar *title);
const gchar * gimp_color_button_get_title (GimpColorButton *button);
void gimp_color_button_set_color (GimpColorButton *button,
GeglColor *color);
GeglColor * gimp_color_button_get_color (GimpColorButton *button);
gboolean gimp_color_button_has_alpha (GimpColorButton *button);
void gimp_color_button_set_type (GimpColorButton *button,
GimpColorAreaType type);
gboolean gimp_color_button_get_update (GimpColorButton *button);
void gimp_color_button_set_update (GimpColorButton *button,
gboolean continuous);
void gimp_color_button_set_color_config (GimpColorButton *button,
GimpColorConfig *config);
GSimpleActionGroup * gimp_color_button_get_action_group (GimpColorButton *button);
G_END_DECLS
#endif /* __GIMP_COLOR_BUTTON_H__ */

View file

@ -0,0 +1,478 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolordisplay.c
* Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpcolor/gimpcolor.h"
#include "libgimpconfig/gimpconfig.h"
#include "gimpwidgetstypes.h"
#include "gimpcolordisplay.h"
#include "gimpicons.h"
/**
* SECTION: gimpcolordisplay
* @title: GimpColorDisplay
* @short_description: Pluggable GIMP display color correction modules.
* @see_also: #GModule, #GTypeModule, #GimpModule
*
* Functions and definitions for creating pluggable GIMP
* display color correction modules.
**/
enum
{
PROP_0,
PROP_ENABLED,
PROP_COLOR_CONFIG,
PROP_COLOR_MANAGED
};
enum
{
CHANGED,
LAST_SIGNAL
};
typedef struct _GimpColorDisplayPrivate
{
gboolean enabled;
GimpColorConfig *config;
GimpColorManaged *managed;
} GimpColorDisplayPrivate;
#define GET_PRIVATE(obj) ((GimpColorDisplayPrivate *) gimp_color_display_get_instance_private ((GimpColorDisplay *) (obj)))
static void gimp_color_display_constructed (GObject *object);
static void gimp_color_display_dispose (GObject *object);
static void gimp_color_display_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_color_display_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_color_display_set_color_config (GimpColorDisplay *display,
GimpColorConfig *config);
static void gimp_color_display_set_color_managed (GimpColorDisplay *display,
GimpColorManaged *managed);
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GimpColorDisplay, gimp_color_display, G_TYPE_OBJECT,
G_ADD_PRIVATE (GimpColorDisplay)
G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL))
#define parent_class gimp_color_display_parent_class
static guint display_signals[LAST_SIGNAL] = { 0 };
static void
gimp_color_display_class_init (GimpColorDisplayClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = gimp_color_display_constructed;
object_class->dispose = gimp_color_display_dispose;
object_class->set_property = gimp_color_display_set_property;
object_class->get_property = gimp_color_display_get_property;
g_object_class_install_property (object_class, PROP_ENABLED,
g_param_spec_boolean ("enabled",
"Enabled",
"Whether this display filter is enabled",
TRUE,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_COLOR_CONFIG,
g_param_spec_object ("color-config",
"Color Config",
"The color config used for this filter",
GIMP_TYPE_COLOR_CONFIG,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_COLOR_MANAGED,
g_param_spec_object ("color-managed",
"Color Managed",
"The color managed pixel source that is filtered",
GIMP_TYPE_COLOR_MANAGED,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
display_signals[CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpColorDisplayClass, changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
klass->name = "Unnamed";
klass->help_id = NULL;
klass->icon_name = GIMP_ICON_DISPLAY_FILTER;
klass->convert_buffer = NULL;
klass->configure = NULL;
klass->changed = NULL;
}
static void
gimp_color_display_init (GimpColorDisplay *display)
{
}
static void
gimp_color_display_constructed (GObject *object)
{
G_OBJECT_CLASS (parent_class)->constructed (object);
/* emit an initial "changed" signal after all construct properties are set */
gimp_color_display_changed (GIMP_COLOR_DISPLAY (object));
}
static void
gimp_color_display_dispose (GObject *object)
{
GimpColorDisplayPrivate *private = GET_PRIVATE (object);
if (private->config)
{
g_signal_handlers_disconnect_by_func (private->config,
gimp_color_display_changed,
object);
g_object_unref (private->config);
private->config = NULL;
}
if (private->managed)
{
g_signal_handlers_disconnect_by_func (private->managed,
gimp_color_display_changed,
object);
g_object_unref (private->managed);
private->managed = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gimp_color_display_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpColorDisplay *display = GIMP_COLOR_DISPLAY (object);
GimpColorDisplayPrivate *private = GET_PRIVATE (object);
switch (property_id)
{
case PROP_ENABLED:
private->enabled = g_value_get_boolean (value);
break;
case PROP_COLOR_CONFIG:
gimp_color_display_set_color_config (display,
g_value_get_object (value));
break;
case PROP_COLOR_MANAGED:
gimp_color_display_set_color_managed (display,
g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_color_display_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpColorDisplayPrivate *private = GET_PRIVATE (object);
switch (property_id)
{
case PROP_ENABLED:
g_value_set_boolean (value, private->enabled);
break;
case PROP_COLOR_CONFIG:
g_value_set_object (value, private->config);
break;
case PROP_COLOR_MANAGED:
g_value_set_object (value, private->managed);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_color_display_set_color_config (GimpColorDisplay *display,
GimpColorConfig *config)
{
GimpColorDisplayPrivate *private = GET_PRIVATE (display);
g_return_if_fail (private->config == NULL);
if (config)
{
private->config = g_object_ref (config);
g_signal_connect_swapped (private->config, "notify",
G_CALLBACK (gimp_color_display_changed),
display);
}
}
static void
gimp_color_display_set_color_managed (GimpColorDisplay *display,
GimpColorManaged *managed)
{
GimpColorDisplayPrivate *private = GET_PRIVATE (display);
g_return_if_fail (private->managed == NULL);
if (managed)
{
private->managed = g_object_ref (managed);
g_signal_connect_swapped (private->managed, "profile-changed",
G_CALLBACK (gimp_color_display_changed),
display);
}
}
/**
* gimp_color_display_clone:
* @display: a #GimpColorDisplay
*
* Creates a copy of @display.
*
* Returns: (transfer full): a duplicate of @display.
*
* Since: 2.0
**/
GimpColorDisplay *
gimp_color_display_clone (GimpColorDisplay *display)
{
g_return_val_if_fail (GIMP_IS_COLOR_DISPLAY (display), NULL);
return GIMP_COLOR_DISPLAY (gimp_config_duplicate (GIMP_CONFIG (display)));
}
/**
* gimp_color_display_convert_buffer:
* @display: a #GimpColorDisplay
* @buffer: a #GeglBuffer
* @area: area in @buffer to convert
*
* Converts all pixels in @area of @buffer.
*
* Since: 2.10
**/
void
gimp_color_display_convert_buffer (GimpColorDisplay *display,
GeglBuffer *buffer,
GeglRectangle *area)
{
GimpColorDisplayPrivate *private;
g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
g_return_if_fail (GEGL_IS_BUFFER (buffer));
private = GET_PRIVATE (display);
if (private->enabled &&
GIMP_COLOR_DISPLAY_GET_CLASS (display)->convert_buffer)
{
GIMP_COLOR_DISPLAY_GET_CLASS (display)->convert_buffer (display, buffer,
area);
}
}
/**
* gimp_color_display_load_state:
* @display: a #GimpColorDisplay
* @state: a #GimpParasite
*
* Configures @display from the contents of the parasite @state.
* @state must be a properly serialized configuration for a
* #GimpColorDisplay, such as saved by gimp_color_display_save_state().
*
* Since: 2.0
**/
void
gimp_color_display_load_state (GimpColorDisplay *display,
GimpParasite *state)
{
g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
g_return_if_fail (state != NULL);
gimp_config_deserialize_parasite (GIMP_CONFIG (display),
state,
NULL, NULL);
}
/**
* gimp_color_display_save_state:
* @display: a #GimpColorDisplay
*
* Saves the configuration state of @display as a new parasite.
*
* Returns: (transfer full): a #GimpParasite
*
* Since: 2.0
**/
GimpParasite *
gimp_color_display_save_state (GimpColorDisplay *display)
{
g_return_val_if_fail (GIMP_IS_COLOR_DISPLAY (display), NULL);
return gimp_config_serialize_to_parasite (GIMP_CONFIG (display),
"Display/Proof",
GIMP_PARASITE_PERSISTENT,
NULL);
}
/**
* gimp_color_display_configure:
* @display: a #GimpColorDisplay
*
* Creates a configuration widget for @display which can be added to a
* container widget.
*
* Returns: (transfer full): a new configuration widget for @display, or
* %NULL if no specific widget exists.
*
* Since: 2.0
**/
GtkWidget *
gimp_color_display_configure (GimpColorDisplay *display)
{
g_return_val_if_fail (GIMP_IS_COLOR_DISPLAY (display), NULL);
if (GIMP_COLOR_DISPLAY_GET_CLASS (display)->configure)
return GIMP_COLOR_DISPLAY_GET_CLASS (display)->configure (display);
return NULL;
}
void
gimp_color_display_configure_reset (GimpColorDisplay *display)
{
g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
gimp_config_reset (GIMP_CONFIG (display));
}
void
gimp_color_display_changed (GimpColorDisplay *display)
{
g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
g_signal_emit (display, display_signals[CHANGED], 0);
}
void
gimp_color_display_set_enabled (GimpColorDisplay *display,
gboolean enabled)
{
GimpColorDisplayPrivate *private;
g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
private = GET_PRIVATE (display);
if (enabled != private->enabled)
{
g_object_set (display,
"enabled", enabled,
NULL);
}
}
gboolean
gimp_color_display_get_enabled (GimpColorDisplay *display)
{
g_return_val_if_fail (GIMP_IS_COLOR_DISPLAY (display), FALSE);
return GET_PRIVATE (display)->enabled;
}
/**
* gimp_color_display_get_config:
* @display:
*
* Returns: (transfer none): a pointer to the #GimpColorConfig
* object or %NULL.
*
* Since: 2.4
**/
GimpColorConfig *
gimp_color_display_get_config (GimpColorDisplay *display)
{
g_return_val_if_fail (GIMP_IS_COLOR_DISPLAY (display), NULL);
return GET_PRIVATE (display)->config;
}
/**
* gimp_color_display_get_managed:
* @display:
*
* Returns: (transfer none): a pointer to the #GimpColorManaged
* object or %NULL.
*
* Since: 2.4
**/
GimpColorManaged *
gimp_color_display_get_managed (GimpColorDisplay *display)
{
g_return_val_if_fail (GIMP_IS_COLOR_DISPLAY (display), NULL);
return GET_PRIVATE (display)->managed;
}

View file

@ -0,0 +1,92 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolordisplay.c
* Copyright (C) 1999 Manish Singh <yosh@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_COLOR_DISPLAY_H__
#define __GIMP_COLOR_DISPLAY_H__
G_BEGIN_DECLS
/* For information look at the html documentation */
#define GIMP_TYPE_COLOR_DISPLAY (gimp_color_display_get_type ())
G_DECLARE_DERIVABLE_TYPE (GimpColorDisplay, gimp_color_display, GIMP, COLOR_DISPLAY, GObject)
struct _GimpColorDisplayClass
{
GObjectClass parent_class;
const gchar *name;
const gchar *help_id;
const gchar *icon_name;
/* virtual functions */
void (* convert_buffer) (GimpColorDisplay *display,
GeglBuffer *buffer,
GeglRectangle *area);
GtkWidget * (* configure) (GimpColorDisplay *display);
/* signals */
void (* changed) (GimpColorDisplay *display);
/* Padding for future expansion */
void (* _gimp_reserved0) (void);
void (* _gimp_reserved1) (void);
void (* _gimp_reserved2) (void);
void (* _gimp_reserved3) (void);
void (* _gimp_reserved4) (void);
void (* _gimp_reserved5) (void);
void (* _gimp_reserved6) (void);
void (* _gimp_reserved7) (void);
void (* _gimp_reserved8) (void);
void (* _gimp_reserved9) (void);
};
GimpColorDisplay * gimp_color_display_clone (GimpColorDisplay *display);
void gimp_color_display_convert_buffer (GimpColorDisplay *display,
GeglBuffer *buffer,
GeglRectangle *area);
void gimp_color_display_load_state (GimpColorDisplay *display,
GimpParasite *state);
GimpParasite * gimp_color_display_save_state (GimpColorDisplay *display);
GtkWidget * gimp_color_display_configure (GimpColorDisplay *display);
void gimp_color_display_configure_reset (GimpColorDisplay *display);
void gimp_color_display_changed (GimpColorDisplay *display);
void gimp_color_display_set_enabled (GimpColorDisplay *display,
gboolean enabled);
gboolean gimp_color_display_get_enabled (GimpColorDisplay *display);
GimpColorConfig * gimp_color_display_get_config (GimpColorDisplay *display);
GimpColorManaged * gimp_color_display_get_managed (GimpColorDisplay *display);
G_END_DECLS
#endif /* __GIMP_COLOR_DISPLAY_H__ */

View file

@ -0,0 +1,432 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolordisplaystack.c
* Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpcolor/gimpcolor.h"
#include "gimpwidgetstypes.h"
#include "gimpcolordisplay.h"
#include "gimpcolordisplaystack.h"
#include "gimpwidgetsmarshal.h"
/**
* SECTION: gimpcolordisplaystack
* @title: GimpColorDisplayStack
* @short_description: A stack of color correction modules.
* @see_also: #GimpColorDisplay
*
* A stack of color correction modules.
**/
enum
{
CHANGED,
ADDED,
REMOVED,
REORDERED,
LAST_SIGNAL
};
struct _GimpColorDisplayStack
{
GObject parent;
GList *filters;
};
static void gimp_color_display_stack_dispose (GObject *object);
static void gimp_color_display_stack_display_changed (GimpColorDisplay *display,
GimpColorDisplayStack *stack);
static void gimp_color_display_stack_display_enabled (GimpColorDisplay *display,
GParamSpec *pspec,
GimpColorDisplayStack *stack);
static void gimp_color_display_stack_disconnect (GimpColorDisplayStack *stack,
GimpColorDisplay *display);
G_DEFINE_TYPE (GimpColorDisplayStack, gimp_color_display_stack, G_TYPE_OBJECT)
#define parent_class gimp_color_display_stack_parent_class
static guint stack_signals[LAST_SIGNAL] = { 0 };
static void
gimp_color_display_stack_class_init (GimpColorDisplayStackClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
stack_signals[CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
stack_signals[ADDED] =
g_signal_new ("added",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
_gimp_widgets_marshal_VOID__OBJECT_INT,
G_TYPE_NONE, 2,
GIMP_TYPE_COLOR_DISPLAY,
G_TYPE_INT);
stack_signals[REMOVED] =
g_signal_new ("removed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 1,
GIMP_TYPE_COLOR_DISPLAY);
stack_signals[REORDERED] =
g_signal_new ("reordered",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
_gimp_widgets_marshal_VOID__OBJECT_INT,
G_TYPE_NONE, 2,
GIMP_TYPE_COLOR_DISPLAY,
G_TYPE_INT);
object_class->dispose = gimp_color_display_stack_dispose;
}
static void
gimp_color_display_stack_init (GimpColorDisplayStack *stack)
{
}
static void
gimp_color_display_stack_dispose (GObject *object)
{
GimpColorDisplayStack *stack = GIMP_COLOR_DISPLAY_STACK (object);
if (stack->filters)
{
GList *list;
for (list = stack->filters; list; list = g_list_next (list))
{
GimpColorDisplay *display = list->data;
gimp_color_display_stack_disconnect (stack, display);
g_object_unref (display);
}
g_list_free (stack->filters);
stack->filters = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
/* public functions */
/**
* gimp_color_display_stack_new:
*
* Creates a new stack of color correction modules.
*
* Returns: (transfer full): a newly allocated #GimpColorDisplayStack.
*
* Since: 2.0
**/
GimpColorDisplayStack *
gimp_color_display_stack_new (void)
{
return g_object_new (GIMP_TYPE_COLOR_DISPLAY_STACK, NULL);
}
/**
* gimp_color_display_stack_clone:
* @stack: a #GimpColorDisplayStack
*
* Creates a copy of @stack with all its display color modules also
* duplicated.
*
* Returns: (transfer full): a duplicate of @stack.
*
* Since: 2.0
**/
GimpColorDisplayStack *
gimp_color_display_stack_clone (GimpColorDisplayStack *stack)
{
GimpColorDisplayStack *clone;
GList *list;
g_return_val_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack), NULL);
clone = g_object_new (GIMP_TYPE_COLOR_DISPLAY_STACK, NULL);
for (list = stack->filters; list; list = g_list_next (list))
{
GimpColorDisplay *display;
display = gimp_color_display_clone (list->data);
gimp_color_display_stack_add (clone, display);
g_object_unref (display);
}
return clone;
}
/**
* gimp_color_display_stack_changed:
* @stack: a #GimpColorDisplayStack
*
* Emit the "changed" signal of @stack.
*
* Since: 2.0
**/
void
gimp_color_display_stack_changed (GimpColorDisplayStack *stack)
{
g_return_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack));
g_signal_emit (stack, stack_signals[CHANGED], 0);
}
/**
* gimp_color_display_stack_get_filters:
* @stack: a #GimpColorDisplayStack
*
* Gets the list of added color modules.
*
* Returns: (transfer none) (element-type GimpColorDisplay):
the list of @stack's display color modules.
*
* Since: 3.0
**/
GList *
gimp_color_display_stack_get_filters (GimpColorDisplayStack *stack)
{
g_return_val_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack), NULL);
return stack->filters;
}
/**
* gimp_color_display_stack_add:
* @stack: a #GimpColorDisplayStack
* @display: (transfer none): a #GimpColorDisplay
*
* Add the color module @display to @stack.
*
* Since: 2.0
**/
void
gimp_color_display_stack_add (GimpColorDisplayStack *stack,
GimpColorDisplay *display)
{
g_return_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack));
g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
g_return_if_fail (g_list_find (stack->filters, display) == NULL);
stack->filters = g_list_append (stack->filters, g_object_ref (display));
g_signal_connect (display, "changed",
G_CALLBACK (gimp_color_display_stack_display_changed),
G_OBJECT (stack));
g_signal_connect (display, "notify::enabled",
G_CALLBACK (gimp_color_display_stack_display_enabled),
G_OBJECT (stack));
g_signal_emit (stack, stack_signals[ADDED], 0,
display, g_list_length (stack->filters) - 1);
gimp_color_display_stack_changed (stack);
}
/**
* gimp_color_display_stack_remove:
* @stack: a #GimpColorDisplayStack
* @display: (transfer none): a #GimpColorDisplay
*
* Remove the color module @display from @stack.
*
* Since: 2.0
**/
void
gimp_color_display_stack_remove (GimpColorDisplayStack *stack,
GimpColorDisplay *display)
{
g_return_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack));
g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
g_return_if_fail (g_list_find (stack->filters, display) != NULL);
gimp_color_display_stack_disconnect (stack, display);
stack->filters = g_list_remove (stack->filters, display);
g_signal_emit (stack, stack_signals[REMOVED], 0, display);
gimp_color_display_stack_changed (stack);
g_object_unref (display);
}
/**
* gimp_color_display_stack_reorder_up:
* @stack: a #GimpColorDisplayStack
* @display: a #GimpColorDisplay
*
* Move the color module @display up in the filter list of @stack.
*
* Since: 2.0
**/
void
gimp_color_display_stack_reorder_up (GimpColorDisplayStack *stack,
GimpColorDisplay *display)
{
GList *list;
g_return_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack));
g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
list = g_list_find (stack->filters, display);
g_return_if_fail (list != NULL);
if (list->prev)
{
list->data = list->prev->data;
list->prev->data = display;
g_signal_emit (stack, stack_signals[REORDERED], 0,
display, g_list_position (stack->filters, list->prev));
gimp_color_display_stack_changed (stack);
}
}
/**
* gimp_color_display_stack_reorder_down:
* @stack: a #GimpColorDisplayStack
* @display: a #GimpColorDisplay
*
* Move the color module @display down in the filter list of @stack.
*
* Since: 2.0
**/
void
gimp_color_display_stack_reorder_down (GimpColorDisplayStack *stack,
GimpColorDisplay *display)
{
GList *list;
g_return_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack));
g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
list = g_list_find (stack->filters, display);
g_return_if_fail (list != NULL);
if (list->next)
{
list->data = list->next->data;
list->next->data = display;
g_signal_emit (stack, stack_signals[REORDERED], 0,
display, g_list_position (stack->filters, list->next));
gimp_color_display_stack_changed (stack);
}
}
/**
* gimp_color_display_stack_convert_buffer:
* @stack: a #GimpColorDisplayStack
* @buffer: a #GeglBuffer
* @area: area of @buffer to convert
*
* Runs all the stack's filters on all pixels in @area of @buffer.
*
* Since: 2.10
**/
void
gimp_color_display_stack_convert_buffer (GimpColorDisplayStack *stack,
GeglBuffer *buffer,
GeglRectangle *area)
{
GList *list;
g_return_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack));
g_return_if_fail (GEGL_IS_BUFFER (buffer));
for (list = stack->filters; list; list = g_list_next (list))
{
GimpColorDisplay *display = list->data;
gimp_color_display_convert_buffer (display, buffer, area);
}
}
/* private functions */
static void
gimp_color_display_stack_display_changed (GimpColorDisplay *display,
GimpColorDisplayStack *stack)
{
if (gimp_color_display_get_enabled (display))
gimp_color_display_stack_changed (stack);
}
static void
gimp_color_display_stack_display_enabled (GimpColorDisplay *display,
GParamSpec *pspec,
GimpColorDisplayStack *stack)
{
gimp_color_display_stack_changed (stack);
}
static void
gimp_color_display_stack_disconnect (GimpColorDisplayStack *stack,
GimpColorDisplay *display)
{
g_signal_handlers_disconnect_by_func (display,
gimp_color_display_stack_display_changed,
stack);
g_signal_handlers_disconnect_by_func (display,
gimp_color_display_stack_display_enabled,
stack);
}

View file

@ -0,0 +1,60 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolordisplaystack.h
* Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_COLOR_DISPLAY_STACK_H__
#define __GIMP_COLOR_DISPLAY_STACK_H__
G_BEGIN_DECLS
/* For information look at the html documentation */
#define GIMP_TYPE_COLOR_DISPLAY_STACK (gimp_color_display_stack_get_type ())
G_DECLARE_FINAL_TYPE (GimpColorDisplayStack, gimp_color_display_stack, GIMP, COLOR_DISPLAY_STACK, GObject)
GimpColorDisplayStack * gimp_color_display_stack_new (void);
GimpColorDisplayStack * gimp_color_display_stack_clone (GimpColorDisplayStack *stack);
void gimp_color_display_stack_changed (GimpColorDisplayStack *stack);
GList * gimp_color_display_stack_get_filters (GimpColorDisplayStack *stack);
void gimp_color_display_stack_add (GimpColorDisplayStack *stack,
GimpColorDisplay *display);
void gimp_color_display_stack_remove (GimpColorDisplayStack *stack,
GimpColorDisplay *display);
void gimp_color_display_stack_reorder_up (GimpColorDisplayStack *stack,
GimpColorDisplay *display);
void gimp_color_display_stack_reorder_down (GimpColorDisplayStack *stack,
GimpColorDisplay *display);
void gimp_color_display_stack_convert_buffer (GimpColorDisplayStack *stack,
GeglBuffer *buffer,
GeglRectangle *area);
G_END_DECLS
#endif /* __GIMP_COLOR_DISPLAY_STACK_H__ */

View file

@ -0,0 +1,340 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorhexentry.c
* Copyright (C) 2004 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "libgimpcolor/gimpcolor.h"
#include "gimpwidgetstypes.h"
#include "gimpcellrenderercolor.h"
#include "gimpcolorhexentry.h"
#include "gimphelpui.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimpcolorhexentry
* @title: GimpColorHexEntry
* @short_description: Widget for entering a color's hex triplet.
*
* Widget for entering a color's hex triplet. The syntax follows CSS and
* SVG specifications, which means that only sRGB colors are supported.
**/
enum
{
COLOR_CHANGED,
LAST_SIGNAL
};
enum
{
COLUMN_NAME,
COLUMN_COLOR,
NUM_COLUMNS
};
struct _GimpColorHexEntry
{
GtkEntry parent_instance;
GeglColor *color;
};
static void gimp_color_hex_entry_constructed (GObject *object);
static void gimp_color_hex_entry_finalize (GObject *object);
static gboolean gimp_color_hex_entry_events (GtkWidget *widget,
GdkEvent *event);
static gboolean gimp_color_hex_entry_matched (GtkEntryCompletion *completion,
GtkTreeModel *model,
GtkTreeIter *iter,
GimpColorHexEntry *entry);
G_DEFINE_TYPE (GimpColorHexEntry, gimp_color_hex_entry, GTK_TYPE_ENTRY)
#define parent_class gimp_color_hex_entry_parent_class
static guint entry_signals[LAST_SIGNAL] = { 0 };
static void
gimp_color_hex_entry_class_init (GimpColorHexEntryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
entry_signals[COLOR_CHANGED] =
g_signal_new ("color-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->constructed = gimp_color_hex_entry_constructed;
object_class->finalize = gimp_color_hex_entry_finalize;
}
static void
gimp_color_hex_entry_init (GimpColorHexEntry *entry)
{
GtkEntryCompletion *completion;
GtkCellRenderer *cell;
GtkListStore *store;
GeglColor **colors;
const gchar **names;
gint i;
/* GtkEntry's minimum size is way too large, set a reasonable one
* for our use case
*/
gtk_entry_set_width_chars (GTK_ENTRY (entry), 8);
gimp_help_set_help_data (GTK_WIDGET (entry),
_("Hexadecimal color notation as used in HTML and "
"CSS. This entry also accepts CSS color names."),
NULL);
entry->color = gegl_color_new ("black");
store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, GEGL_TYPE_COLOR);
names = gimp_color_list_names (&colors);
for (i = 0; names[i] != NULL; i++)
{
GtkTreeIter iter;
GeglColor *named_color = colors[i];
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
COLUMN_NAME, names[i],
COLUMN_COLOR, named_color,
-1);
}
gimp_color_array_free (colors);
g_free (names);
completion = g_object_new (GTK_TYPE_ENTRY_COMPLETION,
"model", store,
NULL);
g_object_unref (store);
cell = gimp_cell_renderer_color_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion), cell, FALSE);
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (completion), cell,
"color", COLUMN_COLOR,
NULL);
gtk_entry_completion_set_text_column (completion, COLUMN_NAME);
gtk_entry_set_completion (GTK_ENTRY (entry), completion);
g_object_unref (completion);
g_signal_connect (entry, "focus-out-event",
G_CALLBACK (gimp_color_hex_entry_events),
NULL);
g_signal_connect (entry, "key-press-event",
G_CALLBACK (gimp_color_hex_entry_events),
NULL);
g_signal_connect (completion, "match-selected",
G_CALLBACK (gimp_color_hex_entry_matched),
entry);
}
static void
gimp_color_hex_entry_constructed (GObject *object)
{
G_OBJECT_CLASS (parent_class)->constructed (object);
gtk_entry_set_text (GTK_ENTRY (object), "000000");
}
static void
gimp_color_hex_entry_finalize (GObject *object)
{
GimpColorHexEntry *entry = GIMP_COLOR_HEX_ENTRY (object);
g_object_unref (entry->color);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
/**
* gimp_color_hex_entry_new:
*
* Returns: a new #GimpColorHexEntry widget
*
* Since: 2.2
**/
GtkWidget *
gimp_color_hex_entry_new (void)
{
return g_object_new (GIMP_TYPE_COLOR_HEX_ENTRY, NULL);
}
/**
* gimp_color_hex_entry_set_color:
* @entry: a #GimpColorHexEntry widget
* @color: the color to set.
*
* Sets the color displayed by a #GimpColorHexEntry. If the new color
* is different to the previously set color, the "color-changed"
* signal is emitted.
*
* Since: 2.2
**/
void
gimp_color_hex_entry_set_color (GimpColorHexEntry *entry,
GeglColor *color)
{
gchar buffer[8];
guchar rgb[3];
g_return_if_fail (GIMP_IS_COLOR_HEX_ENTRY (entry));
g_return_if_fail (GEGL_IS_COLOR (color));
g_object_unref (entry->color);
entry->color = gegl_color_duplicate (color);
gegl_color_get_pixel (color, babl_format ("R'G'B' u8"), rgb);
g_snprintf (buffer, sizeof (buffer), "%.2x%.2x%.2x", rgb[0], rgb[1], rgb[2]);
gtk_entry_set_text (GTK_ENTRY (entry), buffer);
/* move cursor to the end */
gtk_editable_set_position (GTK_EDITABLE (entry), -1);
g_signal_emit (entry, entry_signals[COLOR_CHANGED], 0);
}
/**
* gimp_color_hex_entry_get_color:
* @entry: a #GimpColorHexEntry widget
*
* Retrieves the color value displayed by a #GimpColorHexEntry.
*
* Returns: (transfer full): the color stored in @entry.
*
* Since: 2.2
**/
GeglColor *
gimp_color_hex_entry_get_color (GimpColorHexEntry *entry)
{
g_return_val_if_fail (GIMP_IS_COLOR_HEX_ENTRY (entry), NULL);
return gegl_color_duplicate (entry->color);
}
static gboolean
gimp_color_hex_entry_events (GtkWidget *widget,
GdkEvent *event)
{
GimpColorHexEntry *entry = GIMP_COLOR_HEX_ENTRY (widget);
switch (event->type)
{
case GDK_KEY_PRESS:
{
GdkEventKey *kevent = (GdkEventKey *) event;
if (kevent->keyval != GDK_KEY_Return &&
kevent->keyval != GDK_KEY_KP_Enter &&
kevent->keyval != GDK_KEY_ISO_Enter)
break;
/* else fall through */
}
case GDK_FOCUS_CHANGE:
{
const gchar *text;
gchar buffer[8];
guchar rgb[3];
text = gtk_entry_get_text (GTK_ENTRY (widget));
gegl_color_get_pixel (entry->color, babl_format ("R'G'B' u8"), rgb);
g_snprintf (buffer, sizeof (buffer), "%.2x%.2x%.2x", rgb[0], rgb[1], rgb[2]);
if (g_ascii_strcasecmp (buffer, text) != 0)
{
GeglColor *color = NULL;
gsize len = strlen (text);
if (len > 0 &&
((color = gimp_color_parse_hex_substring (text, len)) ||
(color = gimp_color_parse_name (text))))
{
gimp_color_hex_entry_set_color (entry, color);
g_object_unref (color);
}
else
{
gtk_entry_set_text (GTK_ENTRY (entry), buffer);
}
}
}
break;
default:
/* do nothing */
break;
}
return FALSE;
}
static gboolean
gimp_color_hex_entry_matched (GtkEntryCompletion *completion,
GtkTreeModel *model,
GtkTreeIter *iter,
GimpColorHexEntry *entry)
{
gchar *name = NULL;
GeglColor *color = NULL;
gtk_tree_model_get (model, iter,
COLUMN_NAME, &name,
-1);
if ((color = gimp_color_parse_name (name)))
gimp_color_hex_entry_set_color (entry, color);
g_free (name);
g_clear_object (&color);
return TRUE;
}

View file

@ -0,0 +1,45 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorhexentry.h
* Copyright (C) 2004 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_COLOR_HEX_ENTRY_H__
#define __GIMP_COLOR_HEX_ENTRY_H__
G_BEGIN_DECLS
#define GIMP_TYPE_COLOR_HEX_ENTRY (gimp_color_hex_entry_get_type ())
G_DECLARE_FINAL_TYPE (GimpColorHexEntry, gimp_color_hex_entry, GIMP, COLOR_HEX_ENTRY, GtkEntry)
GtkWidget * gimp_color_hex_entry_new (void);
void gimp_color_hex_entry_set_color (GimpColorHexEntry *entry,
GeglColor *color);
GeglColor * gimp_color_hex_entry_get_color (GimpColorHexEntry *entry);
G_END_DECLS
#endif /* __GIMP_COLOR_HEX_ENTRY_H__ */

View file

@ -0,0 +1,657 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolornotebook.c
* Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
*
* based on color_notebook module
* Copyright (C) 1998 Austin Donnelly <austin@greenend.org.uk>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpcolor/gimpcolor.h"
#include "gimpwidgetstypes.h"
#include "gimpcolornotebook.h"
#include "gimpcolorscales.h"
#include "gimphelpui.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimpcolornotebook
* @title: GimpColorNotebook
* @short_description: A #GimpColorSelector implementation.
*
* The #GimpColorNotebook widget is an implementation of a
* #GimpColorSelector. It serves as a container for
* #GimpColorSelectors.
**/
#define DEFAULT_TAB_ICON_SIZE GTK_ICON_SIZE_BUTTON
struct _GimpColorNotebook
{
GimpColorSelector parent_instance;
GtkWidget *notebook;
GList *selectors;
GimpColorSelector *cur_page;
};
static void gimp_color_notebook_style_updated (GtkWidget *widget);
static void gimp_color_notebook_togg_visible (GimpColorSelector *selector,
gboolean visible);
static void gimp_color_notebook_togg_sensitive (GimpColorSelector *selector,
gboolean sensitive);
static void gimp_color_notebook_set_show_alpha (GimpColorSelector *selector,
gboolean show_alpha);
static void gimp_color_notebook_set_color (GimpColorSelector *selector,
GeglColor *color);
static void gimp_color_notebook_set_channel (GimpColorSelector *selector,
GimpColorSelectorChannel channel);
static void gimp_color_notebook_set_model_visible
(GimpColorSelector *selector,
GimpColorSelectorModel model,
gboolean gboolean);
static void gimp_color_notebook_set_config (GimpColorSelector *selector,
GimpColorConfig *config);
static void gimp_color_notebook_switch_page (GtkNotebook *gtk_notebook,
gpointer page,
guint page_num,
GimpColorNotebook *notebook);
static void gimp_color_notebook_color_changed (GimpColorSelector *page,
GeglColor *color,
GimpColorNotebook *notebook);
static void gimp_color_notebook_channel_changed (GimpColorSelector *page,
GimpColorSelectorChannel channel,
GimpColorNotebook *notebook);
static void gimp_color_notebook_model_visible_changed
(GimpColorSelector *page,
GimpColorSelectorModel model,
gboolean visible,
GimpColorNotebook *notebook);
static GtkWidget * gimp_color_notebook_add_page (GimpColorNotebook *notebook,
GType page_type);
static void gimp_color_notebook_remove_selector (GtkContainer *container,
GtkWidget *widget,
GimpColorNotebook *notebook);
G_DEFINE_TYPE (GimpColorNotebook, gimp_color_notebook, GIMP_TYPE_COLOR_SELECTOR)
#define parent_class gimp_color_notebook_parent_class
static void
gimp_color_notebook_class_init (GimpColorNotebookClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GimpColorSelectorClass *selector_class = GIMP_COLOR_SELECTOR_CLASS (klass);
widget_class->style_updated = gimp_color_notebook_style_updated;
selector_class->name = "Notebook";
selector_class->help_id = "gimp-colorselector-notebook";
selector_class->set_toggles_visible = gimp_color_notebook_togg_visible;
selector_class->set_toggles_sensitive = gimp_color_notebook_togg_sensitive;
selector_class->set_show_alpha = gimp_color_notebook_set_show_alpha;
selector_class->set_color = gimp_color_notebook_set_color;
selector_class->set_channel = gimp_color_notebook_set_channel;
selector_class->set_model_visible = gimp_color_notebook_set_model_visible;
selector_class->set_config = gimp_color_notebook_set_config;
gtk_widget_class_install_style_property (widget_class,
g_param_spec_enum ("tab-icon-size",
NULL,
"Size for icons displayed in the tab",
GTK_TYPE_ICON_SIZE,
DEFAULT_TAB_ICON_SIZE,
G_PARAM_READABLE));
gtk_widget_class_set_css_name (widget_class, "GimpColorNotebook");
}
static void
gimp_color_notebook_init (GimpColorNotebook *notebook)
{
GType *selector_types;
guint n_selector_types;
guint i;
notebook->notebook = gtk_notebook_new ();
gtk_notebook_popup_enable (GTK_NOTEBOOK (notebook->notebook));
gtk_box_pack_start (GTK_BOX (notebook), notebook->notebook, TRUE, TRUE, 0);
gtk_widget_show (notebook->notebook);
g_signal_connect (notebook->notebook, "switch-page",
G_CALLBACK (gimp_color_notebook_switch_page),
notebook);
g_signal_connect (notebook->notebook, "remove",
G_CALLBACK (gimp_color_notebook_remove_selector),
notebook);
selector_types = g_type_children (GIMP_TYPE_COLOR_SELECTOR,
&n_selector_types);
if (n_selector_types == 2)
{
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook->notebook), FALSE);
gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook->notebook), FALSE);
}
for (i = 0; i < n_selector_types; i++)
{
/* skip ourselves */
if (g_type_is_a (selector_types[i], GIMP_TYPE_COLOR_NOTEBOOK))
continue;
/* skip the "Scales" color selector */
if (g_type_is_a (selector_types[i], GIMP_TYPE_COLOR_SCALES))
continue;
gimp_color_notebook_add_page (notebook, selector_types[i]);
}
g_free (selector_types);
}
static void
gimp_color_notebook_style_updated (GtkWidget *widget)
{
GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (widget);
GList *list;
GtkIconSize icon_size;
GTK_WIDGET_CLASS (parent_class)->style_updated (widget);
gtk_widget_style_get (widget,
"tab-icon-size", &icon_size,
NULL);
for (list = notebook->selectors; list; list = g_list_next (list))
{
GimpColorSelectorClass *selector_class;
GtkWidget *image;
selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (list->data);
image = gtk_image_new_from_icon_name (selector_class->icon_name,
icon_size);
gimp_help_set_help_data (image, gettext (selector_class->name), NULL);
gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook->notebook),
GTK_WIDGET (list->data),
image);
}
}
static void
gimp_color_notebook_togg_visible (GimpColorSelector *selector,
gboolean visible)
{
GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (selector);
GList *list;
for (list = notebook->selectors; list; list = g_list_next (list))
{
GimpColorSelector *child = list->data;
gimp_color_selector_set_toggles_visible (child, visible);
}
}
static void
gimp_color_notebook_togg_sensitive (GimpColorSelector *selector,
gboolean sensitive)
{
GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (selector);
GList *list;
for (list = notebook->selectors; list; list = g_list_next (list))
{
GimpColorSelector *child = list->data;
gimp_color_selector_set_toggles_sensitive (child, sensitive);
}
}
static void
gimp_color_notebook_set_show_alpha (GimpColorSelector *selector,
gboolean show_alpha)
{
GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (selector);
GList *list;
for (list = notebook->selectors; list; list = g_list_next (list))
{
GimpColorSelector *child = list->data;
gimp_color_selector_set_show_alpha (child, show_alpha);
}
}
static void
gimp_color_notebook_set_color (GimpColorSelector *selector,
GeglColor *color)
{
GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (selector);
g_signal_handlers_block_by_func (notebook->cur_page,
gimp_color_notebook_color_changed,
selector);
gimp_color_selector_set_color (notebook->cur_page, color);
g_signal_handlers_unblock_by_func (notebook->cur_page,
gimp_color_notebook_color_changed,
selector);
}
static void
gimp_color_notebook_set_channel (GimpColorSelector *selector,
GimpColorSelectorChannel channel)
{
GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (selector);
g_signal_handlers_block_by_func (notebook->cur_page,
gimp_color_notebook_channel_changed,
selector);
gimp_color_selector_set_channel (notebook->cur_page, channel);
g_signal_handlers_unblock_by_func (notebook->cur_page,
gimp_color_notebook_channel_changed,
selector);
}
static void
gimp_color_notebook_set_model_visible (GimpColorSelector *selector,
GimpColorSelectorModel model,
gboolean visible)
{
GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (selector);
g_signal_handlers_block_by_func (notebook->cur_page,
gimp_color_notebook_model_visible_changed,
selector);
gimp_color_selector_set_model_visible (notebook->cur_page, model, visible);
g_signal_handlers_unblock_by_func (notebook->cur_page,
gimp_color_notebook_model_visible_changed,
selector);
}
static void
gimp_color_notebook_set_config (GimpColorSelector *selector,
GimpColorConfig *config)
{
GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (selector);
GList *list;
for (list = notebook->selectors; list; list = g_list_next (list))
{
GimpColorSelector *child = list->data;
gimp_color_selector_set_config (child, config);
}
}
static void
gimp_color_notebook_switch_page (GtkNotebook *gtk_notebook,
gpointer page,
guint page_num,
GimpColorNotebook *notebook)
{
GimpColorSelector *selector = GIMP_COLOR_SELECTOR (notebook);
GtkWidget *page_widget;
GeglColor *color;
GimpColorSelectorModel model;
page_widget = gtk_notebook_get_nth_page (gtk_notebook, page_num);
notebook->cur_page = GIMP_COLOR_SELECTOR (page_widget);
g_signal_handlers_block_by_func (notebook->cur_page,
gimp_color_notebook_color_changed,
notebook);
g_signal_handlers_block_by_func (notebook->cur_page,
gimp_color_notebook_channel_changed,
notebook);
g_signal_handlers_block_by_func (notebook->cur_page,
gimp_color_notebook_model_visible_changed,
notebook);
color = gimp_color_selector_get_color (selector);
gimp_color_selector_set_color (notebook->cur_page, color);
g_object_unref (color);
gimp_color_selector_set_channel (notebook->cur_page,
gimp_color_selector_get_channel (selector));
for (model = GIMP_COLOR_SELECTOR_MODEL_RGB;
model <= GIMP_COLOR_SELECTOR_MODEL_HSV;
model++)
{
gboolean visible = gimp_color_selector_get_model_visible (selector, model);
gimp_color_selector_set_model_visible (notebook->cur_page, model,
visible);
}
g_signal_handlers_unblock_by_func (notebook->cur_page,
gimp_color_notebook_color_changed,
notebook);
g_signal_handlers_unblock_by_func (notebook->cur_page,
gimp_color_notebook_channel_changed,
notebook);
g_signal_handlers_unblock_by_func (notebook->cur_page,
gimp_color_notebook_model_visible_changed,
notebook);
}
static void
gimp_color_notebook_color_changed (GimpColorSelector *page,
GeglColor *color,
GimpColorNotebook *notebook)
{
GimpColorSelector *selector = GIMP_COLOR_SELECTOR (notebook);
gimp_color_selector_set_color (selector, color);
}
static void
gimp_color_notebook_channel_changed (GimpColorSelector *page,
GimpColorSelectorChannel channel,
GimpColorNotebook *notebook)
{
GimpColorSelector *selector = GIMP_COLOR_SELECTOR (notebook);
gimp_color_selector_set_channel (selector, channel);
}
static void
gimp_color_notebook_model_visible_changed (GimpColorSelector *page,
GimpColorSelectorModel model,
gboolean visible,
GimpColorNotebook *notebook)
{
GimpColorSelector *selector = GIMP_COLOR_SELECTOR (notebook);
gimp_color_selector_set_model_visible (selector, model, visible);
}
static GtkWidget *
gimp_color_notebook_add_page (GimpColorNotebook *notebook,
GType page_type)
{
GimpColorSelector *selector = GIMP_COLOR_SELECTOR (notebook);
GimpColorSelectorClass *selector_class;
GtkWidget *page;
GtkWidget *menu_widget;
GtkWidget *image;
GtkWidget *label;
GeglColor *color;
gboolean show_alpha;
color = gimp_color_selector_get_color (selector);
page = gimp_color_selector_new (page_type, color,
gimp_color_selector_get_channel (selector));
g_object_unref (color);
if (! page)
return NULL;
selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (page);
show_alpha = gimp_color_selector_get_show_alpha (GIMP_COLOR_SELECTOR (notebook));
gimp_color_selector_set_show_alpha (GIMP_COLOR_SELECTOR (page), show_alpha);
menu_widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
image = gtk_image_new_from_icon_name (selector_class->icon_name,
GTK_ICON_SIZE_MENU);
gtk_box_pack_start (GTK_BOX (menu_widget), image, FALSE, FALSE, 0);
gtk_widget_show (image);
label = gtk_label_new (gettext (selector_class->name));
gtk_box_pack_start (GTK_BOX (menu_widget), label, FALSE, FALSE, 0);
gtk_widget_show (label);
image = gtk_image_new_from_icon_name (selector_class->icon_name,
DEFAULT_TAB_ICON_SIZE);
gimp_help_set_help_data (image, gettext (selector_class->name), NULL);
gtk_notebook_append_page_menu (GTK_NOTEBOOK (notebook->notebook),
page, image, menu_widget);
if (! notebook->cur_page)
notebook->cur_page = GIMP_COLOR_SELECTOR (page);
notebook->selectors = g_list_append (notebook->selectors, page);
gtk_widget_show (page);
g_signal_connect (page, "color-changed",
G_CALLBACK (gimp_color_notebook_color_changed),
notebook);
g_signal_connect (page, "channel-changed",
G_CALLBACK (gimp_color_notebook_channel_changed),
notebook);
g_signal_connect (page, "model-visible-changed",
G_CALLBACK (gimp_color_notebook_model_visible_changed),
notebook);
return page;
}
static void
gimp_color_notebook_remove_selector (GtkContainer *container,
GtkWidget *widget,
GimpColorNotebook *notebook)
{
notebook->selectors = g_list_remove (notebook->selectors, widget);
if (! notebook->selectors)
notebook->cur_page = NULL;
}
/**
* gimp_color_notebook_set_has_page:
* @notebook: A #GimpColorNotebook widget.
* @page_type: The #GType of the notebook page to add or remove.
* @has_page: Whether the page should be added or removed.
*
* This function adds and removed pages to / from a #GimpColorNotebook.
* The @page_type passed must be a #GimpColorSelector subtype.
*
* Returns: (transfer none): The new page widget, if @has_page was
* %TRUE, or %NULL if @has_page was %FALSE.
**/
GtkWidget *
gimp_color_notebook_set_has_page (GimpColorNotebook *notebook,
GType page_type,
gboolean has_page)
{
GList *list;
g_return_val_if_fail (GIMP_IS_COLOR_NOTEBOOK (notebook), NULL);
g_return_val_if_fail (g_type_is_a (page_type, GIMP_TYPE_COLOR_SELECTOR),
NULL);
g_return_val_if_fail (! g_type_is_a (page_type, GIMP_TYPE_COLOR_NOTEBOOK),
NULL);
for (list = notebook->selectors; list; list = g_list_next (list))
{
GimpColorSelector *page = list->data;
if (G_TYPE_FROM_INSTANCE (page) == page_type)
{
if (has_page)
return GTK_WIDGET (page);
gtk_container_remove (GTK_CONTAINER (notebook->notebook),
GTK_WIDGET (page));
return NULL;
}
}
if (! has_page)
return NULL;
return gimp_color_notebook_add_page (notebook, page_type);
}
/**
* gimp_color_notebook_get_notebook:
* @notebook: A #GimpColorNotebook widget.
*
* Returns: (transfer none) (type GtkNotebook): The #GtkNotebook inside.
*
* Since: 3.0
**/
GtkWidget *
gimp_color_notebook_get_notebook (GimpColorNotebook *notebook)
{
g_return_val_if_fail (GIMP_IS_COLOR_NOTEBOOK (notebook), NULL);
return notebook->notebook;
}
/**
* gimp_color_notebook_get_selectors:
* @notebook: A #GimpColorNotebook widget.
*
* Returns: (element-type GimpColorSelector) (transfer none): The
* notebook's list of #GimpColorSelector's.
*
* Since: 3.0
**/
GList *
gimp_color_notebook_get_selectors (GimpColorNotebook *notebook)
{
g_return_val_if_fail (GIMP_IS_COLOR_NOTEBOOK (notebook), NULL);
return notebook->selectors;
}
/**
* gimp_color_notebook_get_current_selector:
* @notebook: A #GimpColorNotebook widget.
*
* Returns: (transfer none): The active page's #GimpColorSelector.
*
* Since: 3.0
**/
GimpColorSelector *
gimp_color_notebook_get_current_selector (GimpColorNotebook *notebook)
{
g_return_val_if_fail (GIMP_IS_COLOR_NOTEBOOK (notebook), NULL);
return notebook->cur_page;
}
/**
* gimp_color_notebook_set_format:
* @notebook: A #GimpColorNotebook widget.
* @format: A Babl format, with space.
*
* Updates all selectors with the current format.
*
* Since: 3.0
**/
void
gimp_color_notebook_set_format (GimpColorNotebook *notebook,
const Babl *format)
{
GList *list;
g_return_if_fail (GIMP_IS_COLOR_NOTEBOOK (notebook));
for (list = notebook->selectors; list; list = g_list_next (list))
{
GimpColorSelector *selector = list->data;
if (selector)
gimp_color_selector_set_format (selector, format);
}
}
/**
* gimp_color_notebook_set_simulation:
* @notebook: A #GimpColorNotebook widget.
* @profile: A #GimpColorProfile object.
* @intent: A #GimpColorRenderingIntent enum.
* @bpc: A gboolean.
*
* Updates all selectors with the current simulation settings.
*
* Since: 3.0
**/
void
gimp_color_notebook_set_simulation (GimpColorNotebook *notebook,
GimpColorProfile *profile,
GimpColorRenderingIntent intent,
gboolean bpc)
{
GList *list;
g_return_if_fail (GIMP_IS_COLOR_NOTEBOOK (notebook));
g_return_if_fail (profile == NULL || GIMP_IS_COLOR_PROFILE (profile));
for (list = notebook->selectors; list; list = g_list_next (list))
{
GimpColorSelector *selector = list->data;
if (selector)
gimp_color_selector_set_simulation (selector, profile, intent, bpc);
}
}
void
gimp_color_notebook_enable_simulation (GimpColorNotebook *notebook,
gboolean enabled)
{
GList *list;
g_return_if_fail (GIMP_IS_COLOR_NOTEBOOK (notebook));
for (list = notebook->selectors; list; list = g_list_next (list))
{
GimpColorSelector *selector = list->data;
if (selector)
gimp_color_selector_enable_simulation (selector, enabled);
}
}

View file

@ -0,0 +1,60 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolornotebook.h
* Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
*
* based on color_notebook module
* Copyright (C) 1998 Austin Donnelly <austin@greenend.org.uk>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_COLOR_NOTEBOOK_H__
#define __GIMP_COLOR_NOTEBOOK_H__
#include <libgimpwidgets/gimpcolorselector.h>
G_BEGIN_DECLS
#define GIMP_TYPE_COLOR_NOTEBOOK (gimp_color_notebook_get_type ())
G_DECLARE_FINAL_TYPE (GimpColorNotebook, gimp_color_notebook, GIMP, COLOR_NOTEBOOK, GimpColorSelector)
GtkWidget * gimp_color_notebook_set_has_page (GimpColorNotebook *notebook,
GType page_type,
gboolean has_page);
GtkWidget * gimp_color_notebook_get_notebook (GimpColorNotebook *notebook);
GList * gimp_color_notebook_get_selectors (GimpColorNotebook *notebook);
GimpColorSelector * gimp_color_notebook_get_current_selector (GimpColorNotebook *notebook);
void gimp_color_notebook_set_format (GimpColorNotebook *notebook,
const Babl *format);
void gimp_color_notebook_set_simulation (GimpColorNotebook *notebook,
GimpColorProfile *profile,
GimpColorRenderingIntent intent,
gboolean bpc);
void gimp_color_notebook_enable_simulation (GimpColorNotebook *notebook,
gboolean enabled);
G_END_DECLS
#endif /* __GIMP_COLOR_NOTEBOOK_H__ */

View file

@ -0,0 +1,357 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* GimpColorProfileChooserDialog
* Copyright (C) 2006-2014 Sven Neumann <sven@gimp.org>
* 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>
#ifdef PLATFORM_OSX
#include <AppKit/AppKit.h>
#endif
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpcolor/gimpcolor.h"
#include "gimpwidgetstypes.h"
#include "gimpcolorprofilechooserdialog.h"
#include "gimpcolorprofileview.h"
#include "gimpdialog.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimpcolorprofilechooserdialog
* @title: GimpColorProfileChooserDialog
* @short_description: A file chooser for selecting color profiles.
*
* A #GtkFileChooser subclass for selecting color profiles.
**/
struct _GimpColorProfileChooserDialog
{
GtkFileChooserDialog parent_instance;
GimpColorProfileView *profile_view;
};
static void gimp_color_profile_chooser_dialog_constructed (GObject *object);
static gboolean gimp_color_profile_chooser_dialog_delete_event (GtkWidget *widget,
GdkEventAny *event);
static void gimp_color_profile_chooser_dialog_add_shortcut (GimpColorProfileChooserDialog *dialog);
static void gimp_color_profile_chooser_dialog_update_preview (GimpColorProfileChooserDialog *dialog);
G_DEFINE_TYPE (GimpColorProfileChooserDialog, gimp_color_profile_chooser_dialog,
GTK_TYPE_FILE_CHOOSER_DIALOG)
#define parent_class gimp_color_profile_chooser_dialog_parent_class
static void
gimp_color_profile_chooser_dialog_class_init (GimpColorProfileChooserDialogClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->constructed = gimp_color_profile_chooser_dialog_constructed;
widget_class->delete_event = gimp_color_profile_chooser_dialog_delete_event;
}
static void
gimp_color_profile_chooser_dialog_init (GimpColorProfileChooserDialog *dialog)
{
}
static void
gimp_color_profile_chooser_dialog_constructed (GObject *object)
{
GimpColorProfileChooserDialog *dialog;
GtkFileFilter *filter;
GtkWidget *scrolled_window;
GtkWidget *profile_view;
dialog = GIMP_COLOR_PROFILE_CHOOSER_DIALOG (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
gtk_window_set_role (GTK_WINDOW (dialog), "gimp-profile-chooser-dialog");
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("All files (*.*)"));
gtk_file_filter_add_pattern (filter, "*");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("ICC color profile (*.icc, *.icm)"));
gtk_file_filter_add_pattern (filter, "*.[Ii][Cc][Cc]");
gtk_file_filter_add_pattern (filter, "*.[Ii][Cc][Mm]");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
/* the preview widget */
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_set_size_request (scrolled_window, 300, -1);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_SHADOW_IN);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
profile_view = gimp_color_profile_view_new ();
gtk_container_add (GTK_CONTAINER (scrolled_window), profile_view);
gtk_widget_show (profile_view);
dialog->profile_view = GIMP_COLOR_PROFILE_VIEW (profile_view);
gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (dialog),
scrolled_window);
g_signal_connect (dialog, "update-preview",
G_CALLBACK (gimp_color_profile_chooser_dialog_update_preview),
NULL);
}
static gboolean
gimp_color_profile_chooser_dialog_delete_event (GtkWidget *widget,
GdkEventAny *event)
{
return TRUE;
}
GtkWidget *
gimp_color_profile_chooser_dialog_new (const gchar *title,
GtkWindow *parent,
GtkFileChooserAction action)
{
GtkWidget *dialog;
g_return_val_if_fail (title != NULL, NULL);
g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL);
dialog = g_object_new (GIMP_TYPE_COLOR_PROFILE_CHOOSER_DIALOG,
"title", title,
"action", action,
NULL);
if (parent)
gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
if (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)) ==
GTK_FILE_CHOOSER_ACTION_SAVE)
{
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Save"), GTK_RESPONSE_ACCEPT,
NULL);
gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
TRUE);
}
else
{
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Open"), GTK_RESPONSE_ACCEPT,
NULL);
}
gimp_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_ACCEPT,
GTK_RESPONSE_CANCEL,
-1);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
gimp_color_profile_chooser_dialog_add_shortcut (GIMP_COLOR_PROFILE_CHOOSER_DIALOG (dialog));
return dialog;
}
/* Add shortcuts for default ICC profile locations */
static gboolean
add_shortcut (GimpColorProfileChooserDialog *dialog,
const gchar *folder)
{
return (g_file_test (folder, G_FILE_TEST_IS_DIR) &&
gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (dialog),
folder, NULL));
}
static void
gimp_color_profile_chooser_dialog_add_shortcut (GimpColorProfileChooserDialog *dialog)
{
#ifndef G_OS_WIN32
gboolean save = (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)) ==
GTK_FILE_CHOOSER_ACTION_SAVE);
#endif
#ifdef G_OS_WIN32
{
const gchar *prefix = g_getenv ("SystemRoot");
gchar *folder;
if (! prefix)
prefix = "c:\\windows";
folder = g_strconcat (prefix, "\\system32\\spool\\drivers\\color", NULL);
add_shortcut (dialog, folder);
g_free (folder);
}
#elif defined(PLATFORM_OSX)
{
NSAutoreleasePool *pool;
NSArray *path;
NSString *library_dir;
gchar *folder;
gboolean folder_set = FALSE;
pool = [[NSAutoreleasePool alloc] init];
if (save)
{
path = NSSearchPathForDirectoriesInDomains (NSLibraryDirectory,
NSUserDomainMask, YES);
library_dir = [path objectAtIndex:0];
folder = g_build_filename ([library_dir UTF8String],
"ColorSync", "Profiles", NULL);
folder_set = add_shortcut (dialog, folder);
g_free (folder);
}
if (! folder_set)
{
path = NSSearchPathForDirectoriesInDomains (NSLibraryDirectory,
NSSystemDomainMask, YES);
library_dir = [path objectAtIndex:0];
folder = g_build_filename ([library_dir UTF8String],
"ColorSync", "Profiles", NULL);
add_shortcut (dialog, folder);
g_free (folder);
}
[pool drain];
}
#else
{
gboolean folder_set = FALSE;
if (save)
{
gchar *folder = g_build_filename (g_get_user_data_dir (),
"color", "icc", NULL);
folder_set = add_shortcut (dialog, folder);
if (! folder_set)
{
g_free (folder);
/* Some software, like GNOME color, will save profiles in
* $XDG_DATA_HOME/icc/
*/
folder = g_build_filename (g_get_user_data_dir (),
"icc", NULL);
folder_set = add_shortcut (dialog, folder);
}
if (! folder_set)
{
g_free (folder);
folder = g_build_filename (g_get_home_dir (),
".color", "icc", NULL);
folder_set = add_shortcut (dialog, folder);
}
g_free (folder);
}
if (! folder_set)
add_shortcut (dialog, COLOR_PROFILE_DIRECTORY);
}
#endif
}
static void
gimp_color_profile_chooser_dialog_update_preview (GimpColorProfileChooserDialog *dialog)
{
GimpColorProfile *profile;
GFile *file;
GError *error = NULL;
file = gtk_file_chooser_get_preview_file (GTK_FILE_CHOOSER (dialog));
if (! file)
{
gimp_color_profile_view_set_profile (dialog->profile_view, NULL);
return;
}
switch (g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL))
{
case G_FILE_TYPE_REGULAR:
profile = gimp_color_profile_new_from_file (file, &error);
if (! profile)
{
gimp_color_profile_view_set_error (dialog->profile_view,
error->message);
g_clear_error (&error);
}
else
{
gimp_color_profile_view_set_profile (dialog->profile_view,
profile);
g_object_unref (profile);
}
break;
case G_FILE_TYPE_DIRECTORY:
gimp_color_profile_view_set_error (dialog->profile_view,
_("Folder"));
break;
default:
gimp_color_profile_view_set_error (dialog->profile_view,
_("Not a regular file."));
break;
}
g_object_unref (file);
}

View file

@ -0,0 +1,38 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* GimpColorProfileChooserDialog
* Copyright (C) 2006-2014 Sven Neumann <sven@gimp.org>
* 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/>.
*/
#ifndef __GIMP_COLOR_PROFILE_CHOOSER_DIALOG_H__
#define __GIMP_COLOR_PROFILE_CHOOSER_DIALOG_H__
G_BEGIN_DECLS
#define GIMP_TYPE_COLOR_PROFILE_CHOOSER_DIALOG (gimp_color_profile_chooser_dialog_get_type ())
G_DECLARE_FINAL_TYPE (GimpColorProfileChooserDialog, gimp_color_profile_chooser_dialog, GIMP, COLOR_PROFILE_CHOOSER_DIALOG, GtkFileChooserDialog)
GtkWidget * gimp_color_profile_chooser_dialog_new (const gchar *title,
GtkWindow *parent,
GtkFileChooserAction action);
G_END_DECLS
#endif /* __GIMP_COLOR_PROFILE_CHOOSER_DIALOG_H__ */

View file

@ -0,0 +1,554 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorprofilecombobox.c
* Copyright (C) 2007 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpcolor/gimpcolor.h"
#include "gimpwidgetstypes.h"
#include "gimpcolorprofilechooserdialog.h"
#include "gimpcolorprofilecombobox.h"
#include "gimpcolorprofilestore.h"
#include "gimpcolorprofilestore-private.h"
/**
* SECTION: gimpcolorprofilecombobox
* @title: GimpColorProfileComboBox
* @short_description: A combo box for selecting color profiles.
*
* A combo box for selecting color profiles.
**/
enum
{
PROP_0,
PROP_DIALOG,
PROP_MODEL
};
struct _GimpColorProfileComboBox
{
GtkComboBox parent_instance;
GtkWidget *dialog;
GtkTreePath *last_path;
};
static void gimp_color_profile_combo_box_finalize (GObject *object);
static void gimp_color_profile_combo_box_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_color_profile_combo_box_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_color_profile_combo_box_changed (GtkComboBox *combo);
static gboolean gimp_color_profile_row_separator_func (GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data);
static void gimp_color_profile_combo_dialog_response (GimpColorProfileChooserDialog *dialog,
gint response,
GimpColorProfileComboBox *combo);
G_DEFINE_TYPE (GimpColorProfileComboBox, gimp_color_profile_combo_box, GTK_TYPE_COMBO_BOX)
#define parent_class gimp_color_profile_combo_box_parent_class
static void
gimp_color_profile_combo_box_class_init (GimpColorProfileComboBoxClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkComboBoxClass *combo_class = GTK_COMBO_BOX_CLASS (klass);
object_class->set_property = gimp_color_profile_combo_box_set_property;
object_class->get_property = gimp_color_profile_combo_box_get_property;
object_class->finalize = gimp_color_profile_combo_box_finalize;
combo_class->changed = gimp_color_profile_combo_box_changed;
/**
* GimpColorProfileComboBox:dialog:
*
* #GtkDialog to present when the user selects the
* "Select color profile from disk..." item.
*
* Since: 2.4
*/
g_object_class_install_property (object_class,
PROP_DIALOG,
g_param_spec_object ("dialog",
"Dialog",
"The dialog to present when selecting profiles from disk",
GTK_TYPE_DIALOG,
G_PARAM_CONSTRUCT_ONLY |
GIMP_PARAM_READWRITE));
/**
* GimpColorProfileComboBox:model:
*
* Overrides the "model" property of the #GtkComboBox class.
* #GimpColorProfileComboBox requires the model to be a
* #GimpColorProfileStore.
*
* Since: 2.4
*/
g_object_class_install_property (object_class,
PROP_MODEL,
g_param_spec_object ("model",
"Model",
"The profile store used for this combo box",
GIMP_TYPE_COLOR_PROFILE_STORE,
GIMP_PARAM_READWRITE));
}
static void
gimp_color_profile_combo_box_init (GimpColorProfileComboBox *combo_box)
{
GtkCellRenderer *cell;
cell = gtk_cell_renderer_text_new ();
g_object_set (cell,
"width-chars", 42,
"ellipsize", PANGO_ELLIPSIZE_END,
NULL);
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
"text", GIMP_COLOR_PROFILE_STORE_LABEL,
NULL);
gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo_box),
gimp_color_profile_row_separator_func,
NULL, NULL);
}
static void
gimp_color_profile_combo_box_finalize (GObject *object)
{
GimpColorProfileComboBox *combo = GIMP_COLOR_PROFILE_COMBO_BOX (object);
if (combo->dialog)
{
if (GIMP_IS_COLOR_PROFILE_CHOOSER_DIALOG (combo->dialog))
gtk_widget_destroy (combo->dialog);
g_object_unref (combo->dialog);
combo->dialog = NULL;
}
g_clear_pointer (&combo->last_path, gtk_tree_path_free);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_color_profile_combo_box_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpColorProfileComboBox *combo = GIMP_COLOR_PROFILE_COMBO_BOX (object);
switch (property_id)
{
case PROP_DIALOG:
g_return_if_fail (combo->dialog == NULL);
combo->dialog = g_value_dup_object (value);
if (GIMP_IS_COLOR_PROFILE_CHOOSER_DIALOG (combo->dialog))
g_signal_connect (combo->dialog, "response",
G_CALLBACK (gimp_color_profile_combo_dialog_response),
object);
break;
case PROP_MODEL:
gtk_combo_box_set_model (GTK_COMBO_BOX (object),
g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_color_profile_combo_box_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpColorProfileComboBox *combo = GIMP_COLOR_PROFILE_COMBO_BOX (object);
switch (property_id)
{
case PROP_DIALOG:
g_value_set_object (value, combo->dialog);
break;
case PROP_MODEL:
g_value_set_object (value,
gtk_combo_box_get_model (GTK_COMBO_BOX (object)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_color_profile_combo_box_changed (GtkComboBox *combo)
{
GimpColorProfileComboBox *color_combo = GIMP_COLOR_PROFILE_COMBO_BOX (combo);
GtkTreeModel *model = gtk_combo_box_get_model (combo);
GtkTreeIter iter;
gint type;
if (! gtk_combo_box_get_active_iter (combo, &iter))
return;
gtk_tree_model_get (model, &iter,
GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
-1);
switch (type)
{
case GIMP_COLOR_PROFILE_STORE_ITEM_DIALOG:
{
GtkWidget *parent = gtk_widget_get_toplevel (GTK_WIDGET (combo));
if (GTK_IS_WINDOW (parent))
gtk_window_set_transient_for (GTK_WINDOW (color_combo->dialog),
GTK_WINDOW (parent));
gtk_window_present (GTK_WINDOW (color_combo->dialog));
if (color_combo->last_path &&
gtk_tree_model_get_iter (model, &iter, color_combo->last_path))
{
gtk_combo_box_set_active_iter (combo, &iter);
}
}
break;
case GIMP_COLOR_PROFILE_STORE_ITEM_FILE:
if (color_combo->last_path)
gtk_tree_path_free (color_combo->last_path);
color_combo->last_path = gtk_tree_model_get_path (model, &iter);
_gimp_color_profile_store_history_reorder (GIMP_COLOR_PROFILE_STORE (model),
&iter);
break;
default:
break;
}
}
/**
* gimp_color_profile_combo_box_new:
* @dialog: a #GtkDialog to present when the user selects the
* "Select color profile from disk..." item
* @history: #GFile of the profilerc (or %NULL for no history)
*
* Create a combo-box widget for selecting color profiles. The combo-box
* is populated from the file specified as @history. This filename is
* typically created using the following code snippet:
* <informalexample><programlisting>
* gchar *history = gimp_personal_rc_file ("profilerc");
* </programlisting></informalexample>
*
* The recommended @dialog type to use is a #GimpColorProfileChooserDialog.
* If a #GimpColorProfileChooserDialog is passed, #GimpColorProfileComboBox
* will take complete control over the dialog, which means connecting
* a GtkDialog::response() callback by itself, and take care of destroying
* the dialog when the combo box is destroyed.
*
* If another type of @dialog is passed, this has to be implemented
* separately.
*
* See also gimp_color_profile_combo_box_new_with_model().
*
* Returns: a new #GimpColorProfileComboBox.
*
* Since: 2.4
**/
GtkWidget *
gimp_color_profile_combo_box_new (GtkWidget *dialog,
GFile *history)
{
GtkWidget *combo;
GtkListStore *store;
g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
g_return_val_if_fail (history == NULL || G_IS_FILE (history), NULL);
store = gimp_color_profile_store_new (history);
combo = gimp_color_profile_combo_box_new_with_model (dialog,
GTK_TREE_MODEL (store));
g_object_unref (store);
return combo;
}
/**
* gimp_color_profile_combo_box_new_with_model:
* @dialog: a #GtkDialog to present when the user selects the
* "Select color profile from disk..." item
* @model: a #GimpColorProfileStore object
*
* This constructor is useful when you want to create several
* combo-boxes for profile selection that all share the same
* #GimpColorProfileStore. This is for example done in the
* GIMP Preferences dialog.
*
* See also gimp_color_profile_combo_box_new().
*
* Returns: a new #GimpColorProfileComboBox.
*
* Since: 2.4
**/
GtkWidget *
gimp_color_profile_combo_box_new_with_model (GtkWidget *dialog,
GtkTreeModel *model)
{
g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
g_return_val_if_fail (GIMP_IS_COLOR_PROFILE_STORE (model), NULL);
return g_object_new (GIMP_TYPE_COLOR_PROFILE_COMBO_BOX,
"dialog", dialog,
"model", model,
NULL);
}
/**
* gimp_color_profile_combo_box_add_file:
* @combo: a #GimpColorProfileComboBox
* @file: file of the profile to add (or %NULL)
* @label: label to use for the profile
* (may only be %NULL if @file is %NULL)
*
* This function delegates to the underlying
* #GimpColorProfileStore. Please refer to the documentation of
* gimp_color_profile_store_add_file() for details.
*
* Since: 2.10
**/
void
gimp_color_profile_combo_box_add_file (GimpColorProfileComboBox *combo,
GFile *file,
const gchar *label)
{
GtkTreeModel *model;
g_return_if_fail (GIMP_IS_COLOR_PROFILE_COMBO_BOX (combo));
g_return_if_fail (label != NULL || file == NULL);
g_return_if_fail (file == NULL || G_IS_FILE (file));
model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
gimp_color_profile_store_add_file (GIMP_COLOR_PROFILE_STORE (model),
file, label);
}
/**
* gimp_color_profile_combo_box_set_active_file:
* @combo: a #GimpColorProfileComboBox
* @file: file of the profile to select
* @label: label to use when adding a new entry (can be %NULL)
*
* Selects a color profile from the @combo and makes it the active
* item. If the profile is not listed in the @combo, then it is added
* with the given @label (or @file in case that @label is %NULL).
*
* Since: 2.10
**/
void
gimp_color_profile_combo_box_set_active_file (GimpColorProfileComboBox *combo,
GFile *file,
const gchar *label)
{
GimpColorProfile *profile = NULL;
GtkTreeModel *model;
GtkTreeIter iter;
g_return_if_fail (GIMP_IS_COLOR_PROFILE_COMBO_BOX (combo));
g_return_if_fail (file == NULL || G_IS_FILE (file));
model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
if (file && ! (label && *label))
{
GError *error = NULL;
profile = gimp_color_profile_new_from_file (file, &error);
if (! profile)
{
g_message ("%s", error->message);
g_clear_error (&error);
}
else
{
label = gimp_color_profile_get_label (profile);
}
}
if (_gimp_color_profile_store_history_add (GIMP_COLOR_PROFILE_STORE (model),
file, label, &iter))
{
gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
}
if (profile)
g_object_unref (profile);
}
/**
* gimp_color_profile_combo_box_set_active_profile:
* @combo: a #GimpColorProfileComboBox
* @profile: a #GimpColorProfile to set
*
* Selects a color profile from the @combo and makes it the active
* item.
*
* Since: 3.0
**/
void
gimp_color_profile_combo_box_set_active_profile (GimpColorProfileComboBox *combo,
GimpColorProfile *profile)
{
GtkTreeModel *model;
GtkTreeIter iter;
g_return_if_fail (GIMP_IS_COLOR_PROFILE_COMBO_BOX (combo));
g_return_if_fail (profile == NULL || GIMP_IS_COLOR_PROFILE (profile));
model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
if (_gimp_color_profile_store_history_find_profile (GIMP_COLOR_PROFILE_STORE (model),
profile, &iter))
{
gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
}
}
/**
* gimp_color_profile_combo_box_get_active_file:
* @combo: a #GimpColorProfileComboBox
*
* Returns: (transfer none): The file of the currently selected
* color profile, release using g_object_unref() when it
* is not any longer needed.
*
* Since: 2.10
**/
GFile *
gimp_color_profile_combo_box_get_active_file (GimpColorProfileComboBox *combo)
{
GtkTreeModel *model;
GtkTreeIter iter;
g_return_val_if_fail (GIMP_IS_COLOR_PROFILE_COMBO_BOX (combo), NULL);
model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
{
GFile *file;
gint type;
gtk_tree_model_get (model, &iter,
GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
GIMP_COLOR_PROFILE_STORE_FILE, &file,
-1);
if (type == GIMP_COLOR_PROFILE_STORE_ITEM_FILE)
return file;
if (file)
g_object_unref (file);
}
return NULL;
}
static gboolean
gimp_color_profile_row_separator_func (GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data)
{
gint type;
gtk_tree_model_get (model, iter,
GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
-1);
switch (type)
{
case GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_TOP:
case GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_BOTTOM:
return TRUE;
default:
return FALSE;
}
}
static void
gimp_color_profile_combo_dialog_response (GimpColorProfileChooserDialog *dialog,
gint response,
GimpColorProfileComboBox *combo)
{
if (response == GTK_RESPONSE_ACCEPT)
{
GFile *file;
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
if (file)
{
gimp_color_profile_combo_box_set_active_file (combo, file, NULL);
g_object_unref (file);
}
}
gtk_widget_hide (GTK_WIDGET (dialog));
}

View file

@ -0,0 +1,55 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorprofilecombobox.h
* Copyright (C) 2007 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_COLOR_PROFILE_COMBO_BOX_H__
#define __GIMP_COLOR_PROFILE_COMBO_BOX_H__
G_BEGIN_DECLS
#define GIMP_TYPE_COLOR_PROFILE_COMBO_BOX (gimp_color_profile_combo_box_get_type ())
G_DECLARE_FINAL_TYPE (GimpColorProfileComboBox, gimp_color_profile_combo_box, GIMP, COLOR_PROFILE_COMBO_BOX, GtkComboBox)
GtkWidget * gimp_color_profile_combo_box_new (GtkWidget *dialog,
GFile *history);
GtkWidget * gimp_color_profile_combo_box_new_with_model (GtkWidget *dialog,
GtkTreeModel *model);
void gimp_color_profile_combo_box_add_file (GimpColorProfileComboBox *combo,
GFile *file,
const gchar *label);
void gimp_color_profile_combo_box_set_active_file (GimpColorProfileComboBox *combo,
GFile *file,
const gchar *label);
void gimp_color_profile_combo_box_set_active_profile (GimpColorProfileComboBox *combo,
GimpColorProfile *profile);
GFile * gimp_color_profile_combo_box_get_active_file (GimpColorProfileComboBox *combo);
G_END_DECLS
#endif /* __GIMP_COLOR_PROFILE_COMBO_BOX_H__ */

View file

@ -0,0 +1,57 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpprofilestore-private.h
* Copyright (C) 2007 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#ifndef __GIMP_COLOR_PROFILE_STORE_PRIVATE_H__
#define __GIMP_COLOR_PROFILE_STORE_PRIVATE_H__
typedef enum
{
GIMP_COLOR_PROFILE_STORE_ITEM_FILE,
GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_TOP,
GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_BOTTOM,
GIMP_COLOR_PROFILE_STORE_ITEM_DIALOG
} GimpColorProfileStoreItemType;
typedef enum
{
GIMP_COLOR_PROFILE_STORE_ITEM_TYPE,
GIMP_COLOR_PROFILE_STORE_LABEL,
GIMP_COLOR_PROFILE_STORE_FILE,
GIMP_COLOR_PROFILE_STORE_INDEX
} GimpColorProfileStoreColumns;
G_GNUC_INTERNAL gboolean _gimp_color_profile_store_history_add (GimpColorProfileStore *store,
GFile *file,
const gchar *label,
GtkTreeIter *iter);
G_GNUC_INTERNAL gboolean _gimp_color_profile_store_history_find_profile
(GimpColorProfileStore *store,
GimpColorProfile *profile,
GtkTreeIter *iter);
G_GNUC_INTERNAL void _gimp_color_profile_store_history_reorder (GimpColorProfileStore *store,
GtkTreeIter *iter);
#endif /* __GIMP_COLOR_PROFILE_STORE_PRIVATE_H__ */

View file

@ -0,0 +1,840 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpprofilestore.c
* Copyright (C) 2004-2008 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gtk/gtk.h>
#include <gegl.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpcolor/gimpcolor.h"
#include "libgimpconfig/gimpconfig.h"
#include "gimpwidgetstypes.h"
#include "gimpcolorprofilestore.h"
#include "gimpcolorprofilestore-private.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimpcolorprofilestore
* @title: GimpColorProfileStore
* @short_description: A #GtkListStore subclass that keep color profiles.
*
* A #GtkListStore subclass that keep color profiles.
**/
#define HISTORY_SIZE 8
enum
{
PROP_0,
PROP_HISTORY
};
struct _GimpColorProfileStore
{
GtkListStore parent_instance;
GFile *history;
};
static void gimp_color_profile_store_constructed (GObject *object);
static void gimp_color_profile_store_dispose (GObject *object);
static void gimp_color_profile_store_finalize (GObject *object);
static void gimp_color_profile_store_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_color_profile_store_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static gboolean gimp_color_profile_store_history_insert (GimpColorProfileStore *store,
GtkTreeIter *iter,
GFile *file,
const gchar *label,
gint index);
static void gimp_color_profile_store_get_separator (GimpColorProfileStore *store,
GtkTreeIter *iter,
gboolean top);
static gboolean gimp_color_profile_store_save (GimpColorProfileStore *store,
GFile *file,
GError **error);
static gboolean gimp_color_profile_store_load (GimpColorProfileStore *store,
GFile *file,
GError **error);
G_DEFINE_TYPE (GimpColorProfileStore, gimp_color_profile_store, GTK_TYPE_LIST_STORE)
#define parent_class gimp_color_profile_store_parent_class
static void
gimp_color_profile_store_class_init (GimpColorProfileStoreClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = gimp_color_profile_store_constructed;
object_class->dispose = gimp_color_profile_store_dispose;
object_class->finalize = gimp_color_profile_store_finalize;
object_class->set_property = gimp_color_profile_store_set_property;
object_class->get_property = gimp_color_profile_store_get_property;
/**
* GimpColorProfileStore:history:
*
* #GFile of the color history used to populate the profile store.
*
* Since: 2.4
*/
g_object_class_install_property (object_class,
PROP_HISTORY,
g_param_spec_object ("history",
"History",
"Filen of the color history used to populate the profile store",
G_TYPE_FILE,
G_PARAM_CONSTRUCT_ONLY |
GIMP_PARAM_READWRITE));
}
static void
gimp_color_profile_store_init (GimpColorProfileStore *store)
{
GType types[] =
{
G_TYPE_INT, /* GIMP_COLOR_PROFILE_STORE_ITEM_TYPE */
G_TYPE_STRING, /* GIMP_COLOR_PROFILE_STORE_LABEL */
G_TYPE_FILE, /* GIMP_COLOR_PROFILE_STORE_FILE */
G_TYPE_INT /* GIMP_COLOR_PROFILE_STORE_INDEX */
};
gtk_list_store_set_column_types (GTK_LIST_STORE (store),
G_N_ELEMENTS (types), types);
}
static void
gimp_color_profile_store_constructed (GObject *object)
{
GimpColorProfileStore *store = GIMP_COLOR_PROFILE_STORE (object);
GtkTreeIter iter;
G_OBJECT_CLASS (parent_class)->constructed (object);
gtk_list_store_append (GTK_LIST_STORE (store), &iter);
gtk_list_store_set (GTK_LIST_STORE (store), &iter,
GIMP_COLOR_PROFILE_STORE_ITEM_TYPE,
GIMP_COLOR_PROFILE_STORE_ITEM_DIALOG,
GIMP_COLOR_PROFILE_STORE_LABEL,
_("Select color profile from disk..."),
-1);
if (store->history)
gimp_color_profile_store_load (store, store->history, NULL);
}
static void
gimp_color_profile_store_dispose (GObject *object)
{
GimpColorProfileStore *store = GIMP_COLOR_PROFILE_STORE (object);
if (store->history)
gimp_color_profile_store_save (store, store->history, NULL);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gimp_color_profile_store_finalize (GObject *object)
{
GimpColorProfileStore *store = GIMP_COLOR_PROFILE_STORE (object);
g_clear_object (&store->history);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_color_profile_store_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpColorProfileStore *store = GIMP_COLOR_PROFILE_STORE (object);
switch (property_id)
{
case PROP_HISTORY:
g_return_if_fail (store->history == NULL);
store->history = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_color_profile_store_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpColorProfileStore *store = GIMP_COLOR_PROFILE_STORE (object);
switch (property_id)
{
case PROP_HISTORY:
g_value_set_object (value, store->history);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* gimp_color_profile_store_new:
* @history: #GFile of the profilerc (or %NULL for no history)
*
* Creates a new #GimpColorProfileStore object and populates it with
* last used profiles read from the file @history. The updated history
* is written back to disk when the store is disposed.
*
* The #GFile passed as @history is typically created using the
* following code snippet:
* <informalexample><programlisting>
* gchar *history = gimp_personal_rc_file ("profilerc");
* </programlisting></informalexample>
*
* Returns: a new #GimpColorProfileStore
*
* Since: 2.4
**/
GtkListStore *
gimp_color_profile_store_new (GFile *history)
{
g_return_val_if_fail (history == NULL || G_IS_FILE (history), NULL);
return g_object_new (GIMP_TYPE_COLOR_PROFILE_STORE,
"history", history,
NULL);
}
/**
* gimp_color_profile_store_add_file:
* @store: a #GimpColorProfileStore
* @file: #GFile of the profile to add (or %NULL)
* @label: label to use for the profile
* (may only be %NULL if @file is %NULL)
*
* Adds a color profile item to the #GimpColorProfileStore. Items
* added with this function will be kept at the top, separated from
* the history of last used color profiles.
*
* This function is often used to add a selectable item for the %NULL
* file. If you pass %NULL for both @file and @label, the @label will
* be set to the string "None" for you (and translated for the user).
*
* Since: 2.10
**/
void
gimp_color_profile_store_add_file (GimpColorProfileStore *store,
GFile *file,
const gchar *label)
{
GtkTreeIter separator;
GtkTreeIter iter;
g_return_if_fail (GIMP_IS_COLOR_PROFILE_STORE (store));
g_return_if_fail (label != NULL || file == NULL);
g_return_if_fail (file == NULL || G_IS_FILE (file));
if (! file && ! label)
label = C_("profile", "None");
gimp_color_profile_store_get_separator (store, &separator, TRUE);
gtk_list_store_insert_before (GTK_LIST_STORE (store), &iter, &separator);
gtk_list_store_set (GTK_LIST_STORE (store), &iter,
GIMP_COLOR_PROFILE_STORE_ITEM_TYPE,
GIMP_COLOR_PROFILE_STORE_ITEM_FILE,
GIMP_COLOR_PROFILE_STORE_FILE, file,
GIMP_COLOR_PROFILE_STORE_LABEL, label,
GIMP_COLOR_PROFILE_STORE_INDEX, -1,
-1);
}
/**
* _gimp_color_profile_store_history_add:
* @store: a #GimpColorProfileStore
* @file: file of the profile to add (or %NULL)
* @label: label to use for the profile (or %NULL)
* @iter: a #GtkTreeIter
*
* Returns: %TRUE if the iter is valid and pointing to the item
*
* Since: 2.4
**/
gboolean
_gimp_color_profile_store_history_add (GimpColorProfileStore *store,
GFile *file,
const gchar *label,
GtkTreeIter *iter)
{
GtkTreeModel *model;
gboolean iter_valid;
gint max = -1;
g_return_val_if_fail (GIMP_IS_COLOR_PROFILE_STORE (store), FALSE);
g_return_val_if_fail (iter != NULL, FALSE);
model = GTK_TREE_MODEL (store);
for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, iter))
{
gint type;
gint index;
GFile *this;
gtk_tree_model_get (model, iter,
GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
GIMP_COLOR_PROFILE_STORE_INDEX, &index,
-1);
if (type != GIMP_COLOR_PROFILE_STORE_ITEM_FILE)
continue;
if (index > max)
max = index;
/* check if we found a filename match */
gtk_tree_model_get (model, iter,
GIMP_COLOR_PROFILE_STORE_FILE, &this,
-1);
if ((this && file && g_file_equal (this, file)) ||
(! this && ! file))
{
/* update the label */
if (label && *label)
gtk_list_store_set (GTK_LIST_STORE (store), iter,
GIMP_COLOR_PROFILE_STORE_LABEL, label,
-1);
if (this)
g_object_unref (this);
return TRUE;
}
if (this)
g_object_unref (this);
}
if (! file)
return FALSE;
if (label && *label)
{
iter_valid = gimp_color_profile_store_history_insert (store, iter,
file, label,
++max);
}
else
{
const gchar *utf8 = gimp_file_get_utf8_name (file);
gchar *basename = g_path_get_basename (utf8);
iter_valid = gimp_color_profile_store_history_insert (store, iter,
file, basename,
++max);
g_free (basename);
}
return iter_valid;
}
/**
* _gimp_color_profile_store_history_find_profile:
* @store: a #GimpColorProfileStore
* @profile: a #GimpColorProfile to find (or %NULL)
* @iter: a #GtkTreeIter
*
* Returns: %TRUE if the iter is valid and pointing to the item
*
* Since: 3.0
**/
gboolean
_gimp_color_profile_store_history_find_profile (GimpColorProfileStore *store,
GimpColorProfile *profile,
GtkTreeIter *iter)
{
GtkTreeModel *model;
gboolean iter_valid;
gint max = -1;
g_return_val_if_fail (GIMP_IS_COLOR_PROFILE_STORE (store), FALSE);
g_return_val_if_fail (iter != NULL, FALSE);
model = GTK_TREE_MODEL (store);
for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, iter))
{
gint type;
gint index;
GFile *file;
GimpColorProfile *combo_profile = NULL;
gtk_tree_model_get (model, iter,
GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
GIMP_COLOR_PROFILE_STORE_INDEX, &index,
-1);
if (type != GIMP_COLOR_PROFILE_STORE_ITEM_FILE)
continue;
if (index > max)
max = index;
/* check if we found a filename match */
gtk_tree_model_get (model, iter,
GIMP_COLOR_PROFILE_STORE_FILE, &file,
-1);
/* Convert file to GimpColorProfile */
if (file)
combo_profile = gimp_color_profile_new_from_file (file, NULL);
if ((combo_profile && profile &&
gimp_color_profile_is_equal (profile, combo_profile)) ||
(! file && ! profile))
{
if (file)
g_object_unref (file);
if (combo_profile)
g_object_unref (combo_profile);
return TRUE;
}
if (file)
g_object_unref (file);
if (combo_profile)
g_object_unref (combo_profile);
}
if (! profile)
return FALSE;
return iter_valid;
}
/**
* _gimp_color_profile_store_history_reorder
* @store: a #GimpColorProfileStore
* @iter: a #GtkTreeIter
*
* Moves the entry pointed to by @iter to the front of the MRU list.
*
* Since: 2.4
**/
void
_gimp_color_profile_store_history_reorder (GimpColorProfileStore *store,
GtkTreeIter *iter)
{
GtkTreeModel *model;
gint index;
gboolean iter_valid;
g_return_if_fail (GIMP_IS_COLOR_PROFILE_STORE (store));
g_return_if_fail (iter != NULL);
model = GTK_TREE_MODEL (store);
gtk_tree_model_get (model, iter,
GIMP_COLOR_PROFILE_STORE_INDEX, &index,
-1);
if (index == 0)
return; /* already at the top */
for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, iter))
{
gint type;
gint this_index;
gtk_tree_model_get (model, iter,
GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
GIMP_COLOR_PROFILE_STORE_INDEX, &this_index,
-1);
if (type == GIMP_COLOR_PROFILE_STORE_ITEM_FILE && this_index > -1)
{
if (this_index < index)
{
this_index++;
}
else if (this_index == index)
{
this_index = 0;
}
gtk_list_store_set (GTK_LIST_STORE (store), iter,
GIMP_COLOR_PROFILE_STORE_INDEX, this_index,
-1);
}
}
}
static gboolean
gimp_color_profile_store_history_insert (GimpColorProfileStore *store,
GtkTreeIter *iter,
GFile *file,
const gchar *label,
gint index)
{
GtkTreeModel *model = GTK_TREE_MODEL (store);
GtkTreeIter sibling;
gboolean iter_valid;
g_return_val_if_fail (G_IS_FILE (file), FALSE);
g_return_val_if_fail (label != NULL, FALSE);
g_return_val_if_fail (index > -1, FALSE);
gimp_color_profile_store_get_separator (store, iter, FALSE);
for (iter_valid = gtk_tree_model_get_iter_first (model, &sibling);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &sibling))
{
gint type;
gint this_index;
gtk_tree_model_get (model, &sibling,
GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
GIMP_COLOR_PROFILE_STORE_INDEX, &this_index,
-1);
if (type == GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_BOTTOM)
{
gtk_list_store_insert_before (GTK_LIST_STORE (store),
iter, &sibling);
break;
}
if (type == GIMP_COLOR_PROFILE_STORE_ITEM_FILE && this_index > -1)
{
gchar *this_label;
gtk_tree_model_get (model, &sibling,
GIMP_COLOR_PROFILE_STORE_LABEL, &this_label,
-1);
if (this_label && g_utf8_collate (label, this_label) < 0)
{
gtk_list_store_insert_before (GTK_LIST_STORE (store),
iter, &sibling);
g_free (this_label);
break;
}
g_free (this_label);
}
}
if (iter_valid)
gtk_list_store_set (GTK_LIST_STORE (store), iter,
GIMP_COLOR_PROFILE_STORE_ITEM_TYPE,
GIMP_COLOR_PROFILE_STORE_ITEM_FILE,
GIMP_COLOR_PROFILE_STORE_FILE, file,
GIMP_COLOR_PROFILE_STORE_LABEL, label,
GIMP_COLOR_PROFILE_STORE_INDEX, index,
-1);
return iter_valid;
}
static void
gimp_color_profile_store_create_separator (GimpColorProfileStore *store,
GtkTreeIter *iter,
gboolean top)
{
if (top)
{
gtk_list_store_prepend (GTK_LIST_STORE (store), iter);
}
else
{
GtkTreeModel *model = GTK_TREE_MODEL (store);
GtkTreeIter sibling;
gboolean iter_valid;
for (iter_valid = gtk_tree_model_get_iter_first (model, &sibling);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &sibling))
{
gint type;
gtk_tree_model_get (model, &sibling,
GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
-1);
if (type == GIMP_COLOR_PROFILE_STORE_ITEM_DIALOG)
break;
}
if (iter_valid)
gtk_list_store_insert_before (GTK_LIST_STORE (store), iter, &sibling);
}
gtk_list_store_set (GTK_LIST_STORE (store), iter,
GIMP_COLOR_PROFILE_STORE_ITEM_TYPE,
top ?
GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_TOP :
GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_BOTTOM,
GIMP_COLOR_PROFILE_STORE_INDEX, -1,
-1);
}
static void
gimp_color_profile_store_get_separator (GimpColorProfileStore *store,
GtkTreeIter *iter,
gboolean top)
{
GtkTreeModel *model = GTK_TREE_MODEL (store);
gboolean iter_valid;
gint needle;
needle = (top ?
GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_TOP :
GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_BOTTOM);
for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, iter))
{
gint type;
gtk_tree_model_get (model, iter,
GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
-1);
if (type == needle)
return;
}
gimp_color_profile_store_create_separator (store, iter, top);
}
static GTokenType
gimp_color_profile_store_load_profile (GimpColorProfileStore *store,
GScanner *scanner,
gint index)
{
GtkTreeIter iter;
gchar *label = NULL;
gchar *path = NULL;
if (gimp_scanner_parse_string (scanner, &label) &&
gimp_scanner_parse_string (scanner, &path))
{
GFile *file = NULL;
if (g_str_has_prefix (path, "file://"))
{
file = g_file_new_for_uri (path);
}
else
{
file = gimp_file_new_for_config_path (path, NULL);
}
if (file)
{
if (g_file_query_file_type (file, 0, NULL) == G_FILE_TYPE_REGULAR)
{
gimp_color_profile_store_history_insert (store, &iter,
file, label, index);
}
g_object_unref (file);
}
g_free (label);
g_free (path);
return G_TOKEN_RIGHT_PAREN;
}
g_free (label);
g_free (path);
return G_TOKEN_STRING;
}
static gboolean
gimp_color_profile_store_load (GimpColorProfileStore *store,
GFile *file,
GError **error)
{
GScanner *scanner;
GTokenType token;
gint i = 0;
scanner = gimp_scanner_new_file (file, error);
if (! scanner)
return FALSE;
g_scanner_scope_add_symbol (scanner, 0, "color-profile", NULL);
token = G_TOKEN_LEFT_PAREN;
while (g_scanner_peek_next_token (scanner) == token)
{
token = g_scanner_get_next_token (scanner);
switch (token)
{
case G_TOKEN_LEFT_PAREN:
token = G_TOKEN_SYMBOL;
break;
case G_TOKEN_SYMBOL:
token = gimp_color_profile_store_load_profile (store, scanner, i++);
break;
case G_TOKEN_RIGHT_PAREN:
token = G_TOKEN_LEFT_PAREN;
break;
default: /* do nothing */
break;
}
}
if (token != G_TOKEN_LEFT_PAREN)
{
g_scanner_get_next_token (scanner);
g_scanner_unexp_token (scanner, token, NULL, NULL, NULL,
_("fatal parse error"), TRUE);
}
gimp_scanner_unref (scanner);
return TRUE;
}
static gboolean
gimp_color_profile_store_save (GimpColorProfileStore *store,
GFile *file,
GError **error)
{
GimpConfigWriter *writer;
GtkTreeModel *model;
GtkTreeIter iter;
gchar *labels[HISTORY_SIZE] = { NULL, };
GFile *files[HISTORY_SIZE] = { NULL, };
gboolean iter_valid;
gint i;
writer = gimp_config_writer_new_from_file (file,
TRUE,
"GIMP color profile history",
error);
if (! writer)
return FALSE;
model = GTK_TREE_MODEL (store);
for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &iter))
{
gint type;
gint index;
gtk_tree_model_get (model, &iter,
GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
GIMP_COLOR_PROFILE_STORE_INDEX, &index,
-1);
if (type == GIMP_COLOR_PROFILE_STORE_ITEM_FILE &&
index >= 0 &&
index < HISTORY_SIZE)
{
if (labels[index] || files[index])
g_warning ("%s: double index %d", G_STRFUNC, index);
gtk_tree_model_get (model, &iter,
GIMP_COLOR_PROFILE_STORE_LABEL,
&labels[index],
GIMP_COLOR_PROFILE_STORE_FILE,
&files[index],
-1);
}
}
for (i = 0; i < HISTORY_SIZE; i++)
{
if (files[i] && labels[i])
{
gchar *path = gimp_file_get_config_path (files[i], NULL);
if (path)
{
gimp_config_writer_open (writer, "color-profile");
gimp_config_writer_string (writer, labels[i]);
gimp_config_writer_string (writer, path);
gimp_config_writer_close (writer);
g_free (path);
}
}
if (files[i])
g_object_unref (files[i]);
g_free (labels[i]);
}
return gimp_config_writer_finish (writer,
"end of color profile history", error);
}

View file

@ -0,0 +1,45 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpprofilestore.h
* Copyright (C) 2007 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_COLOR_PROFILE_STORE_H__
#define __GIMP_COLOR_PROFILE_STORE_H__
G_BEGIN_DECLS
#define GIMP_TYPE_COLOR_PROFILE_STORE (gimp_color_profile_store_get_type ())
G_DECLARE_FINAL_TYPE (GimpColorProfileStore, gimp_color_profile_store, GIMP, COLOR_PROFILE_STORE, GtkListStore)
GtkListStore * gimp_color_profile_store_new (GFile *history);
void gimp_color_profile_store_add_file (GimpColorProfileStore *store,
GFile *file,
const gchar *label);
G_END_DECLS
#endif /* __GIMP_COLOR_PROFILE_STORE_H__ */

View file

@ -0,0 +1,209 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* GimpColorProfileView
* Copyright (C) 2014 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 "libgimpcolor/gimpcolor.h"
#include "gimpwidgetstypes.h"
#include "gimpcolorprofileview.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimpcolorprofileview
* @title: GimpColorProfileView
* @short_description: A widget for viewing color profile properties
*
* A widget for viewing the properties of a #GimpColorProfile.
**/
struct _GimpColorProfileView
{
GtkTextView parent_instance;
GimpColorProfile *profile;
};
static void gimp_color_profile_view_constructed (GObject *object);
static void gimp_color_profile_view_finalize (GObject *object);
G_DEFINE_TYPE (GimpColorProfileView, gimp_color_profile_view, GTK_TYPE_TEXT_VIEW)
#define parent_class gimp_color_profile_view_parent_class
static void
gimp_color_profile_view_class_init (GimpColorProfileViewClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = gimp_color_profile_view_constructed;
object_class->finalize = gimp_color_profile_view_finalize;
}
static void
gimp_color_profile_view_init (GimpColorProfileView *view)
{
}
static void
gimp_color_profile_view_constructed (GObject *object)
{
GtkTextBuffer *buffer;
G_OBJECT_CLASS (parent_class)->constructed (object);
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (object));
gtk_text_buffer_create_tag (buffer, "text",
NULL);
gtk_text_buffer_create_tag (buffer, "title",
"weight", PANGO_WEIGHT_BOLD,
"scale", PANGO_SCALE_LARGE,
NULL);
gtk_text_buffer_create_tag (buffer, "header",
"weight", PANGO_WEIGHT_BOLD,
NULL);
gtk_text_buffer_create_tag (buffer, "error",
"style", PANGO_STYLE_OBLIQUE,
NULL);
gtk_text_view_set_editable (GTK_TEXT_VIEW (object), FALSE);
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (object), GTK_WRAP_WORD);
gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (object), 6);
gtk_text_view_set_left_margin (GTK_TEXT_VIEW (object), 6);
gtk_text_view_set_right_margin (GTK_TEXT_VIEW (object), 6);
}
static void
gimp_color_profile_view_finalize (GObject *object)
{
GimpColorProfileView *view = GIMP_COLOR_PROFILE_VIEW (object);
g_clear_object (&view->profile);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
GtkWidget *
gimp_color_profile_view_new (void)
{
return g_object_new (GIMP_TYPE_COLOR_PROFILE_VIEW, NULL);
}
void
gimp_color_profile_view_set_profile (GimpColorProfileView *view,
GimpColorProfile *profile)
{
GtkTextBuffer *buffer;
g_return_if_fail (GIMP_IS_COLOR_PROFILE_VIEW (view));
g_return_if_fail (profile == NULL || GIMP_IS_COLOR_PROFILE (profile));
if (profile == view->profile)
return;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
gtk_text_buffer_set_text (buffer, "", 0);
if (g_set_object (&view->profile, profile) && profile)
{
GtkTextIter iter;
const gchar *text;
gtk_text_buffer_get_start_iter (buffer, &iter);
text = gimp_color_profile_get_label (profile);
if (text && strlen (text))
{
gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
text, -1,
"title", NULL);
gtk_text_buffer_insert (buffer, &iter, "\n", 1);
}
text = gimp_color_profile_get_model (profile);
if (text && strlen (text))
{
gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
text, -1,
"text", NULL);
gtk_text_buffer_insert (buffer, &iter, "\n", 1);
}
text = gimp_color_profile_get_manufacturer (profile);
if (text && strlen (text))
{
gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
_("Manufacturer: "), -1,
"header", NULL);
gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
text, -1,
"text", NULL);
gtk_text_buffer_insert (buffer, &iter, "\n", 1);
}
text = gimp_color_profile_get_copyright (profile);
if (text && strlen (text))
{
gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
_("Copyright: "), -1,
"header", NULL);
gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
text, -1,
"text", NULL);
gtk_text_buffer_insert (buffer, &iter, "\n", 1);
}
}
}
void
gimp_color_profile_view_set_error (GimpColorProfileView *view,
const gchar *message)
{
GtkTextBuffer *buffer;
GtkTextIter iter;
g_return_if_fail (GIMP_IS_COLOR_PROFILE_VIEW (view));
g_return_if_fail (message != NULL);
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
gtk_text_buffer_set_text (buffer, "", 0);
gtk_text_buffer_get_start_iter (buffer, &iter);
gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
message, -1,
"error", NULL);
}

View file

@ -0,0 +1,40 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* GimpColorProfileView
* Copyright (C) 2014 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/>.
*/
#ifndef __GIMP_COLOR_PROFILE_VIEW_H__
#define __GIMP_COLOR_PROFILE_VIEW_H__
G_BEGIN_DECLS
#define GIMP_TYPE_COLOR_PROFILE_VIEW (gimp_color_profile_view_get_type ())
G_DECLARE_FINAL_TYPE (GimpColorProfileView, gimp_color_profile_view, GIMP, COLOR_PROFILE_VIEW, GtkTextView)
GtkWidget * gimp_color_profile_view_new (void);
void gimp_color_profile_view_set_profile (GimpColorProfileView *view,
GimpColorProfile *profile);
void gimp_color_profile_view_set_error (GimpColorProfileView *view,
const gchar *message);
G_END_DECLS
#endif /* __GIMP_COLOR_PROFILE_VIEW_H__ */

View file

@ -0,0 +1,923 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorscale.c
* Copyright (C) 2002-2010 Sven Neumann <sven@gimp.org>
* Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpconfig/gimpconfig.h"
#include "libgimpcolor/gimpcolor.h"
#include "gimpwidgetstypes.h"
#include "gimpcairo-utils.h"
#include "gimpcolorscale.h"
#include "gimpwidgetsutils.h"
/**
* SECTION: gimpcolorscale
* @title: GimpColorScale
* @short_description: Fancy colored sliders.
*
* Fancy colored sliders.
**/
enum
{
PROP_0,
PROP_CHANNEL
};
struct _GimpColorScale
{
GtkScale parent_instance;
GimpColorConfig *config;
guchar oog_color[3];
const Babl *format;
GimpColorSelectorChannel channel;
GeglColor *color;
guchar *buf;
guint width;
guint height;
guint rowstride;
gboolean needs_render;
};
static void gimp_color_scale_dispose (GObject *object);
static void gimp_color_scale_finalize (GObject *object);
static void gimp_color_scale_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_color_scale_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_color_scale_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static gboolean gimp_color_scale_draw (GtkWidget *widget,
cairo_t *cr);
static void gimp_color_scale_render (GimpColorScale *scale);
static void gimp_color_scale_render_alpha (GimpColorScale *scale);
static void gimp_color_scale_notify_config (GimpColorConfig *config,
const GParamSpec *pspec,
GimpColorScale *scale);
G_DEFINE_TYPE (GimpColorScale, gimp_color_scale, GTK_TYPE_SCALE)
#define parent_class gimp_color_scale_parent_class
static const Babl *fish_lch_to_rgb = NULL;
static const Babl *fish_hsv_to_rgb = NULL;
static const Babl *fish_rgb_to_cairo = NULL;
static void
gimp_color_scale_class_init (GimpColorScaleClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = gimp_color_scale_dispose;
object_class->finalize = gimp_color_scale_finalize;
object_class->get_property = gimp_color_scale_get_property;
object_class->set_property = gimp_color_scale_set_property;
widget_class->size_allocate = gimp_color_scale_size_allocate;
widget_class->draw = gimp_color_scale_draw;
/**
* GimpColorScale:channel:
*
* The channel which is edited by the color scale.
*
* Since: 2.8
*/
g_object_class_install_property (object_class, PROP_CHANNEL,
g_param_spec_enum ("channel",
"Channel",
"The channel which is edited by the color scale",
GIMP_TYPE_COLOR_SELECTOR_CHANNEL,
GIMP_COLOR_SELECTOR_VALUE,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
gtk_widget_class_set_css_name (widget_class, "GimpColorScale");
fish_lch_to_rgb = babl_fish (babl_format ("CIE LCH(ab) float"),
babl_format ("R'G'B' double"));
fish_hsv_to_rgb = babl_fish (babl_format ("HSV float"),
babl_format ("R'G'B' double"));
fish_rgb_to_cairo = babl_fish (babl_format ("R'G'B' u8"),
babl_format ("cairo-RGB24"));
}
static void
gimp_color_scale_init (GimpColorScale *scale)
{
GtkRange *range = GTK_RANGE (scale);
GtkCssProvider *css;
gtk_widget_set_can_focus (GTK_WIDGET (scale), TRUE);
gtk_range_set_slider_size_fixed (range, TRUE);
gtk_range_set_flippable (GTK_RANGE (scale), TRUE);
gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
scale->channel = GIMP_COLOR_SELECTOR_VALUE;
scale->needs_render = TRUE;
gtk_orientable_set_orientation (GTK_ORIENTABLE (range),
GTK_ORIENTATION_HORIZONTAL);
scale->color = gegl_color_new ("black");
css = gtk_css_provider_new ();
gtk_css_provider_load_from_data (css,
"GimpColorScale {"
" padding: 2px 12px 2px 12px;"
" min-width: 24px;"
" min-height: 24px;"
"}\n"
"GimpColorScale contents trough {"
" min-width: 20px;"
" min-height: 20px;"
"}\n"
"GimpColorScale contents trough slider {"
" min-width: 12px;"
" min-height: 12px;"
" margin: -6px -6px -6px -6px;"
"}",
-1, NULL);
gtk_style_context_add_provider (gtk_widget_get_style_context (GTK_WIDGET (scale)),
GTK_STYLE_PROVIDER (css),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_unref (css);
}
static void
gimp_color_scale_dispose (GObject *object)
{
GimpColorScale *scale = GIMP_COLOR_SCALE (object);
gimp_color_scale_set_color_config (scale, NULL);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gimp_color_scale_finalize (GObject *object)
{
GimpColorScale *scale = GIMP_COLOR_SCALE (object);
g_clear_pointer (&scale->buf, g_free);
scale->width = 0;
scale->height = 0;
scale->rowstride = 0;
g_object_unref (scale->color);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_color_scale_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpColorScale *scale = GIMP_COLOR_SCALE (object);
switch (property_id)
{
case PROP_CHANNEL:
g_value_set_enum (value, scale->channel);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_color_scale_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpColorScale *scale = GIMP_COLOR_SCALE (object);
switch (property_id)
{
case PROP_CHANNEL:
gimp_color_scale_set_channel (scale, g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_color_scale_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
GimpColorScale *scale = GIMP_COLOR_SCALE (widget);
GtkRange *range = GTK_RANGE (widget);
GdkRectangle range_rect;
GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
gtk_range_get_range_rect (range, &range_rect);
if (range_rect.width != scale->width ||
range_rect.height != scale->height)
{
scale->width = range_rect.width;
scale->height = range_rect.height;
/* TODO: we should move to CAIRO_FORMAT_RGBA128F. */
scale->rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, scale->width);
g_free (scale->buf);
scale->buf = g_new (guchar, 3 * scale->width * scale->height);
scale->needs_render = TRUE;
}
}
static gboolean
gimp_color_scale_draw (GtkWidget *widget,
cairo_t *cr)
{
GimpColorScale *scale = GIMP_COLOR_SCALE (widget);
GtkRange *range = GTK_RANGE (widget);
GtkStyleContext *context = gtk_widget_get_style_context (widget);
GdkRectangle range_rect;
GdkRectangle area = { 0, };
cairo_surface_t *buffer;
guchar *buf = NULL;
guchar *src;
guchar *dest;
gint slider_start;
gint slider_end;
gint slider_mid;
gint slider_size;
const Babl *render_space;
if (! scale->buf)
return FALSE;
gtk_range_get_range_rect (range, &range_rect);
gtk_range_get_slider_range (range, &slider_start, &slider_end);
slider_mid = slider_start + (slider_end - slider_start) / 2;
slider_size = 6;
if (scale->needs_render)
{
gimp_color_scale_render (scale);
scale->needs_render = FALSE;
}
render_space = gimp_widget_get_render_space (widget, scale->config);
fish_rgb_to_cairo = babl_fish (babl_format_with_space ("R'G'B' u8", scale->format),
babl_format_with_space ("cairo-RGB24", render_space)),
src = scale->buf;
buf = g_new (guchar, scale->rowstride * scale->height);
dest = buf;
/* We convert per line because the cairo rowstride may be bigger than the
* real contents.
*/
for (gint i = 0; i < scale->height; i++)
{
babl_process (fish_rgb_to_cairo, src, dest, scale->width);
src += 3 * scale->width;
dest += scale->rowstride;
}
buffer = cairo_image_surface_create_for_data (buf,
CAIRO_FORMAT_RGB24,
scale->width,
scale->height,
scale->rowstride);
cairo_surface_set_user_data (buffer, NULL, buf, (cairo_destroy_func_t) g_free);
switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)))
{
case GTK_ORIENTATION_HORIZONTAL:
cairo_set_source_surface (cr, buffer,
range_rect.x, range_rect.y);
break;
case GTK_ORIENTATION_VERTICAL:
cairo_set_source_surface (cr, buffer,
range_rect.x, range_rect.y);
break;
}
cairo_surface_destroy (buffer);
if (! gtk_widget_is_sensitive (widget))
{
static cairo_pattern_t *pattern = NULL;
if (! pattern)
{
static const guchar stipple[] = { 0, 255, 0, 0,
255, 0, 0, 0 };
cairo_surface_t *surface;
gint stride;
stride = cairo_format_stride_for_width (CAIRO_FORMAT_A8, 2);
surface = cairo_image_surface_create_for_data ((guchar *) stipple,
CAIRO_FORMAT_A8,
2, 2, stride);
pattern = cairo_pattern_create_for_surface (surface);
cairo_surface_destroy (surface);
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
}
cairo_mask (cr, pattern);
}
else
{
cairo_paint (cr);
}
if (gtk_widget_has_focus (widget))
gtk_render_focus (context, cr,
0, 0,
gtk_widget_get_allocated_width (widget),
gtk_widget_get_allocated_height (widget));
switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)))
{
case GTK_ORIENTATION_HORIZONTAL:
area.x = slider_mid - slider_size;
area.y = range_rect.y;
area.width = 2 * slider_size;
area.height = range_rect.height;
break;
case GTK_ORIENTATION_VERTICAL:
area.x = range_rect.x;
area.y = slider_mid - slider_size;
area.width = range_rect.width;
area.height = 2 * slider_size;
break;
}
if (gtk_widget_is_sensitive (widget))
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
else
cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)))
{
case GTK_ORIENTATION_HORIZONTAL:
cairo_move_to (cr, area.x, area.y);
cairo_line_to (cr, area.x + area.width, area.y);
cairo_line_to (cr,
area.x + area.width / 2 + 0.5,
area.y + slider_size);
break;
case GTK_ORIENTATION_VERTICAL:
cairo_move_to (cr, area.x, area.y);
cairo_line_to (cr, area.x, area.y + area.height);
cairo_line_to (cr,
area.x + slider_size,
area.y + area.height / 2 + 0.5);
break;
}
cairo_close_path (cr);
cairo_fill (cr);
if (gtk_widget_is_sensitive (widget))
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
else
cairo_set_source_rgb (cr, 0.8, 0.8, 0.8);
switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)))
{
case GTK_ORIENTATION_HORIZONTAL:
cairo_move_to (cr, area.x, area.y + area.height);
cairo_line_to (cr, area.x + area.width, area.y + area.height);
cairo_line_to (cr,
area.x + area.width / 2 + 0.5,
area.y + area.height - slider_size);
break;
case GTK_ORIENTATION_VERTICAL:
cairo_move_to (cr, area.x + area.width, area.y);
cairo_line_to (cr, area.x + area.width, area.y + area.height);
cairo_line_to (cr,
area.x + area.width - slider_size,
area.y + area.height / 2 + 0.5);
break;
}
cairo_close_path (cr);
cairo_fill (cr);
return FALSE;
}
/**
* gimp_color_scale_new:
* @orientation: the scale's orientation (horizontal or vertical)
* @channel: the scale's color channel
*
* Creates a new #GimpColorScale widget.
*
* Returns: a new #GimpColorScale widget
**/
GtkWidget *
gimp_color_scale_new (GtkOrientation orientation,
GimpColorSelectorChannel channel)
{
GimpColorScale *scale = g_object_new (GIMP_TYPE_COLOR_SCALE,
"orientation", orientation,
"channel", channel,
NULL);
gtk_range_set_flippable (GTK_RANGE (scale),
orientation == GTK_ORIENTATION_HORIZONTAL);
return GTK_WIDGET (scale);
}
/**
* gimp_color_scale_set_format:
* @scale: a #GimpColorScale widget
* @format: the Babl format represented by @scale.
*
* Changes the color format displayed by the @scale.
**/
void
gimp_color_scale_set_format (GimpColorScale *scale,
const Babl *format)
{
if (scale->format != format)
{
scale->format = format;
fish_lch_to_rgb = babl_fish (babl_format ("CIE LCH(ab) float"),
babl_format_with_space ("R'G'B' double", format));
fish_hsv_to_rgb = babl_fish (babl_format_with_space ("HSV float", format),
babl_format_with_space ("R'G'B' double", format));
scale->needs_render = TRUE;
gtk_widget_queue_draw (GTK_WIDGET (scale));
}
}
/**
* gimp_color_scale_set_channel:
* @scale: a #GimpColorScale widget
* @channel: the new color channel
*
* Changes the color channel displayed by the @scale.
**/
void
gimp_color_scale_set_channel (GimpColorScale *scale,
GimpColorSelectorChannel channel)
{
g_return_if_fail (GIMP_IS_COLOR_SCALE (scale));
if (channel != scale->channel)
{
scale->channel = channel;
scale->needs_render = TRUE;
gtk_widget_queue_draw (GTK_WIDGET (scale));
g_object_notify (G_OBJECT (scale), "channel");
}
}
/**
* gimp_color_scale_set_color:
* @scale: a #GimpColorScale widget
* @color: the new color.
*
* Changes the color value of the @scale.
**/
void
gimp_color_scale_set_color (GimpColorScale *scale,
GeglColor *color)
{
GeglColor *old_color;
g_return_if_fail (GIMP_IS_COLOR_SCALE (scale));
g_return_if_fail (GEGL_IS_COLOR (color));
old_color = scale->color;
scale->color = gegl_color_duplicate (color);
if (! gimp_color_is_perceptually_identical (old_color, scale->color))
{
scale->needs_render = TRUE;
gtk_widget_queue_draw (GTK_WIDGET (scale));
}
g_object_unref (old_color);
}
/**
* gimp_color_scale_set_color_config:
* @scale: a #GimpColorScale widget.
* @config: a #GimpColorConfig object.
*
* Sets the color management configuration to use with this color scale.
*
* Since: 2.10
*/
void
gimp_color_scale_set_color_config (GimpColorScale *scale,
GimpColorConfig *config)
{
g_return_if_fail (GIMP_IS_COLOR_SCALE (scale));
g_return_if_fail (config == NULL || GIMP_IS_COLOR_CONFIG (config));
if (config != scale->config)
{
if (scale->config)
{
g_signal_handlers_disconnect_by_func (scale->config,
gimp_color_scale_notify_config,
scale);
}
g_set_object (&scale->config, config);
if (scale->config)
{
g_signal_connect (scale->config, "notify",
G_CALLBACK (gimp_color_scale_notify_config),
scale);
gimp_color_scale_notify_config (scale->config, NULL, scale);
}
}
}
/* as in gtkrange.c */
static gboolean
should_invert (GtkRange *range)
{
gboolean inverted = gtk_range_get_inverted (range);
gboolean flippable = gtk_range_get_flippable (range);
if (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)) ==
GTK_ORIENTATION_HORIZONTAL)
{
return
(inverted && !flippable) ||
(inverted && flippable &&
gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_LTR) ||
(!inverted && flippable &&
gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL);
}
else
{
return inverted;
}
}
static void
gimp_color_scale_render (GimpColorScale *scale)
{
GtkRange *range = GTK_RANGE (scale);
gdouble rgb[4];
gfloat hsv[3];
gfloat lch[3];
gint multiplier = 1;
guint x, y;
gdouble *channel_value = NULL;
gfloat *channel_value_f = NULL;
gboolean from_hsv = FALSE;
gboolean from_lch = FALSE;
gboolean invert;
guchar *buf;
guchar *d;
if ((buf = scale->buf) == NULL)
return;
if (scale->channel == GIMP_COLOR_SELECTOR_ALPHA)
{
gimp_color_scale_render_alpha (scale);
return;
}
gegl_color_get_pixel (scale->color, babl_format_with_space ("R'G'B'A double", scale->format), rgb);
gegl_color_get_pixel (scale->color, babl_format_with_space ("HSV float", scale->format), hsv);
gegl_color_get_pixel (scale->color, babl_format ("CIE LCH(ab) float"), lch);
switch (scale->channel)
{
case GIMP_COLOR_SELECTOR_HUE: channel_value_f = &hsv[0]; break;
case GIMP_COLOR_SELECTOR_SATURATION: channel_value_f = &hsv[1]; break;
case GIMP_COLOR_SELECTOR_VALUE: channel_value_f = &hsv[2]; break;
case GIMP_COLOR_SELECTOR_RED: channel_value = &rgb[0]; break;
case GIMP_COLOR_SELECTOR_GREEN: channel_value = &rgb[1]; break;
case GIMP_COLOR_SELECTOR_BLUE: channel_value = &rgb[2]; break;
case GIMP_COLOR_SELECTOR_ALPHA: channel_value = &rgb[3]; break;
case GIMP_COLOR_SELECTOR_LCH_LIGHTNESS: channel_value_f = &lch[0]; break;
case GIMP_COLOR_SELECTOR_LCH_CHROMA: channel_value_f = &lch[1]; break;
case GIMP_COLOR_SELECTOR_LCH_HUE: channel_value_f = &lch[2]; break;
}
switch (scale->channel)
{
case GIMP_COLOR_SELECTOR_HUE:
case GIMP_COLOR_SELECTOR_SATURATION:
case GIMP_COLOR_SELECTOR_VALUE:
from_hsv = TRUE;
break;
case GIMP_COLOR_SELECTOR_LCH_LIGHTNESS:
multiplier = 100;
from_lch = TRUE;
break;
case GIMP_COLOR_SELECTOR_LCH_CHROMA:
multiplier = 200;
from_lch = TRUE;
break;
case GIMP_COLOR_SELECTOR_LCH_HUE:
multiplier = 360;
from_lch = TRUE;
break;
default:
break;
}
invert = should_invert (range);
switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)))
{
case GTK_ORIENTATION_HORIZONTAL:
for (x = 0, d = buf; x < scale->width; x++, d += 3)
{
gdouble value = (gdouble) x * multiplier / (gdouble) (scale->width - 1);
if (invert)
value = multiplier - value;
if (channel_value)
*channel_value = value;
else if (channel_value_f)
*channel_value_f = (gfloat) value;
if (from_hsv)
babl_process (fish_hsv_to_rgb, &hsv, &rgb, 1);
else if (from_lch)
babl_process (fish_lch_to_rgb, &lch, &rgb, 1);
/* This is only checking if a color is within the sRGB gamut. I want
* to check compared to the image's space (anySpace) or softproof
* space. TODO.
*/
if (rgb[0] < 0.0 || rgb[0] > 1.0 ||
rgb[1] < 0.0 || rgb[1] > 1.0 ||
rgb[2] < 0.0 || rgb[2] > 1.0)
{
d[0] = scale->oog_color[0];
d[1] = scale->oog_color[1];
d[2] = scale->oog_color[2];
}
else
{
d[0] = rgb[0] * 255;
d[1] = rgb[1] * 255;
d[2] = rgb[2] * 255;
}
}
d = buf + scale->width * 3;
for (y = 1; y < scale->height; y++)
{
memcpy (d, buf, scale->width * 3);
d += scale->width * 3;
}
break;
case GTK_ORIENTATION_VERTICAL:
for (y = 0; y < scale->height; y++)
{
gdouble value = (gdouble) y * multiplier / (gdouble) (scale->height - 1);
guchar u8rgb[3];
if (invert)
value = multiplier - value;
if (channel_value)
*channel_value = value;
else if (channel_value_f)
*channel_value_f = (gfloat) value;
if (from_hsv)
babl_process (fish_hsv_to_rgb, &hsv, &rgb, 1);
else if (from_lch)
babl_process (fish_lch_to_rgb, &lch, &rgb, 1);
if (rgb[0] < 0.0 || rgb[0] > 1.0 ||
rgb[1] < 0.0 || rgb[1] > 1.0 ||
rgb[2] < 0.0 || rgb[2] > 1.0)
{
u8rgb[0] = scale->oog_color[0];
u8rgb[1] = scale->oog_color[1];
u8rgb[2] = scale->oog_color[2];
}
else
{
u8rgb[0] = rgb[0] * 255;
u8rgb[1] = rgb[1] * 255;
u8rgb[2] = rgb[2] * 255;
}
for (x = 0, d = buf; x < scale->width; x++, d += 3)
{
d[0] = u8rgb[0];
d[1] = u8rgb[1];
d[2] = u8rgb[2];
}
buf += scale->width * 3;
}
break;
}
}
static void
gimp_color_scale_render_alpha (GimpColorScale *scale)
{
GtkRange *range = GTK_RANGE (scale);
gdouble rgb[3];
gboolean invert;
gdouble a;
guint x, y;
guchar *buf;
guchar *d, *l;
invert = should_invert (range);
buf = scale->buf;
gegl_color_get_pixel (scale->color,
babl_format_with_space ("R'G'B' double", scale->format),
rgb);
switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)))
{
case GTK_ORIENTATION_HORIZONTAL:
{
guchar *light;
guchar *dark;
light = buf;
/* this won't work correctly for very thin scales */
dark = (scale->height > GIMP_CHECK_SIZE_SM ?
buf + GIMP_CHECK_SIZE_SM * 3 * scale->width : light);
for (x = 0, d = light, l = dark; x < scale->width; x++)
{
if ((x % GIMP_CHECK_SIZE_SM) == 0)
{
guchar *t;
t = d;
d = l;
l = t;
}
a = (gdouble) x / (gdouble) (scale->width - 1);
if (invert)
a = 1.0 - a;
l[0] = (GIMP_CHECK_LIGHT + (rgb[0] - GIMP_CHECK_LIGHT) * a) * 255.999;
l[1] = (GIMP_CHECK_LIGHT + (rgb[1] - GIMP_CHECK_LIGHT) * a) * 255.999;
l[2] = (GIMP_CHECK_LIGHT + (rgb[2] - GIMP_CHECK_LIGHT) * a) * 255.999;
l += 3;
d[0] = (GIMP_CHECK_DARK + (rgb[0] - GIMP_CHECK_DARK) * a) * 255.999;
d[1] = (GIMP_CHECK_DARK + (rgb[1] - GIMP_CHECK_DARK) * a) * 255.999;
d[2] = (GIMP_CHECK_DARK + (rgb[2] - GIMP_CHECK_DARK) * a) * 255.999;
d += 3;
}
for (y = 0, d = buf; y < scale->height; y++, d += 3 * scale->width)
{
if (y == 0 || y == GIMP_CHECK_SIZE_SM)
continue;
if ((y / GIMP_CHECK_SIZE_SM) & 1)
memcpy (d, dark, 3 * scale->width);
else
memcpy (d, light, 3 * scale->width);
}
}
break;
case GTK_ORIENTATION_VERTICAL:
{
guchar light[3] = {0xff, 0xff, 0xff};
guchar dark[3] = {0xff, 0xff, 0xff};
for (y = 0, d = buf; y < scale->height; y++, d += scale->width * 3)
{
a = (gdouble) y / (gdouble) (scale->height - 1);
if (invert)
a = 1.0 - a;
light[0] = (GIMP_CHECK_LIGHT + (rgb[0] - GIMP_CHECK_LIGHT) * a) * 255.999;
light[1] = (GIMP_CHECK_LIGHT + (rgb[1] - GIMP_CHECK_LIGHT) * a) * 255.999;
light[2] = (GIMP_CHECK_LIGHT + (rgb[2] - GIMP_CHECK_LIGHT) * a) * 255.999;
dark[0] = (GIMP_CHECK_DARK + (rgb[0] - GIMP_CHECK_DARK) * a) * 255.999;
dark[1] = (GIMP_CHECK_DARK + (rgb[1] - GIMP_CHECK_DARK) * a) * 255.999;
dark[2] = (GIMP_CHECK_DARK + (rgb[2] - GIMP_CHECK_DARK) * a) * 255.999;
for (x = 0, l = d; x < scale->width; x++, l += 3)
{
if (((x / GIMP_CHECK_SIZE_SM) ^ (y / GIMP_CHECK_SIZE_SM)) & 1)
{
l[0] = light[0];
l[1] = light[1];
l[2] = light[2];
}
else
{
l[0] = dark[0];
l[1] = dark[1];
l[2] = dark[2];
}
}
}
}
break;
}
}
static void
gimp_color_scale_notify_config (GimpColorConfig *config,
const GParamSpec *pspec,
GimpColorScale *scale)
{
GeglColor *color;
color = gimp_color_config_get_out_of_gamut_color (config);
/* TODO: shouldn't this be color-managed too, using the target space into
* consideration?
*/
gegl_color_get_pixel (color, babl_format ("R'G'B' u8"), scale->oog_color);
scale->needs_render = TRUE;
g_object_unref (color);
}

View file

@ -0,0 +1,53 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorscale.h
* Copyright (C) 2002 Sven Neumann <sven@gimp.org>
* Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_COLOR_SCALE_H__
#define __GIMP_COLOR_SCALE_H__
G_BEGIN_DECLS
#define GIMP_TYPE_COLOR_SCALE (gimp_color_scale_get_type ())
G_DECLARE_FINAL_TYPE (GimpColorScale, gimp_color_scale, GIMP, COLOR_SCALE, GtkScale)
GtkWidget * gimp_color_scale_new (GtkOrientation orientation,
GimpColorSelectorChannel channel);
void gimp_color_scale_set_format (GimpColorScale *scale,
const Babl *format);
void gimp_color_scale_set_channel (GimpColorScale *scale,
GimpColorSelectorChannel channel);
void gimp_color_scale_set_color (GimpColorScale *scale,
GeglColor *color);
void gimp_color_scale_set_color_config (GimpColorScale *scale,
GimpColorConfig *config);
G_END_DECLS
#endif /* __GIMP_COLOR_SCALE_H__ */

View file

@ -0,0 +1,112 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorscaleentry.c
* Copyright (C) 2020 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpcolor/gimpcolor.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpbase/gimpbase.h"
#include "gimpwidgets.h"
/**
* SECTION: gimpcolorscaleentry
* @title: GimpColorScaleEntry
* @short_description: Widget containing a color scale, a spin button
* and a label.
*
* This widget is a subclass of #GimpScaleEntry showing a
* #GimpColorScale instead of a #GtkScale.
**/
struct _GimpColorScaleEntry
{
GimpScaleEntry parent_instance;
};
static GtkWidget * gimp_color_scale_entry_new_range_widget (GtkAdjustment *adjustment);
G_DEFINE_TYPE (GimpColorScaleEntry, gimp_color_scale_entry, GIMP_TYPE_SCALE_ENTRY)
#define parent_class gimp_color_scale_entry_parent_class
static void
gimp_color_scale_entry_class_init (GimpColorScaleEntryClass *klass)
{
GimpScaleEntryClass *entry_class = GIMP_SCALE_ENTRY_CLASS (klass);
entry_class->new_range_widget = gimp_color_scale_entry_new_range_widget;
}
static void
gimp_color_scale_entry_init (GimpColorScaleEntry *entry)
{
}
static GtkWidget *
gimp_color_scale_entry_new_range_widget (GtkAdjustment *adjustment)
{
GtkWidget *scale;
g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), NULL);
scale = gimp_color_scale_new (GTK_ORIENTATION_HORIZONTAL,
GIMP_COLOR_SELECTOR_VALUE);
gtk_range_set_adjustment (GTK_RANGE (scale), adjustment);
return scale;
}
/**
* gimp_color_scale_entry_new:
* @text: The text for the #GtkLabel.
* @value: The initial value.
* @lower: The lower boundary.
* @upper: The upper boundary.
* @digits: The number of decimal digits.
*
* Returns: (transfer full): The new #GimpColorScale widget.
**/
GtkWidget *
gimp_color_scale_entry_new (const gchar *text,
gdouble value,
gdouble lower,
gdouble upper,
guint digits)
{
GtkWidget *entry;
entry = g_object_new (GIMP_TYPE_COLOR_SCALE_ENTRY,
"label", text,
"value", value,
"lower", lower,
"upper", upper,
"digits", digits,
NULL);
return entry;
}

View file

@ -0,0 +1,45 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorscaleentry.h
* Copyright (C) 2020 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_COLOR_SCALE_ENTRY_H__
#define __GIMP_COLOR_SCALE_ENTRY_H__
#include <libgimpwidgets/gimpscaleentry.h>
G_BEGIN_DECLS
#define GIMP_TYPE_COLOR_SCALE_ENTRY (gimp_color_scale_entry_get_type ())
G_DECLARE_FINAL_TYPE (GimpColorScaleEntry, gimp_color_scale_entry, GIMP, COLOR_SCALE_ENTRY, GimpScaleEntry)
GtkWidget * gimp_color_scale_entry_new (const gchar *text,
gdouble value,
gdouble lower,
gdouble upper,
guint digits);
G_END_DECLS
#endif /* __GIMP_COLOR_SCALE_ENTRY_H__ */

View file

@ -0,0 +1,997 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorscales.c
* Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
*
* based on color_notebook module
* Copyright (C) 1998 Austin Donnelly <austin@greenend.org.uk>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpcolor/gimpcolor.h"
#include "libgimpmath/gimpmath.h"
#include "gimpwidgetstypes.h"
#include "gimpcolorscale.h"
#include "gimpcolorscales.h"
#include "gimpwidgets.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimpcolorscales
* @title: GimpColorScales
* @short_description: A #GimpColorSelector implementation.
*
* The #GimpColorScales widget is an implementation of a
* #GimpColorSelector. It shows a group of #GimpColorScale widgets
* that allow to adjust the HSV, LCH, and RGB color channels.
**/
enum
{
PROP_0,
PROP_SHOW_RGB_U8,
PROP_SHOW_HSV
};
enum
{
GIMP_COLOR_SELECTOR_RED_U8 = GIMP_COLOR_SELECTOR_LCH_HUE + 1,
GIMP_COLOR_SELECTOR_GREEN_U8,
GIMP_COLOR_SELECTOR_BLUE_U8,
GIMP_COLOR_SELECTOR_ALPHA_U8
};
typedef struct _ColorScale ColorScale;
struct _ColorScale
{
GimpColorSelectorChannel channel;
gdouble default_value;
gdouble scale_min_value;
gdouble scale_max_value;
gdouble scale_inc;
gdouble spin_min_value;
gdouble spin_max_value;
};
struct _GimpColorScales
{
GimpColorSelector parent_instance;
const Babl *format;
gboolean show_rgb_u8;
GBinding *show_rgb_u8_binding;
GBinding *show_hsv_binding;
GtkWidget *lch_group;
GtkWidget *hsv_group;
GtkWidget *rgb_percent_group;
GtkWidget *rgb_u8_group;
GtkWidget *alpha_percent_group;
GtkWidget *alpha_u8_group;
GtkWidget *dummy_u8_toggle;
GtkWidget *toggles[14];
GtkWidget *scales[14];
GList *profile_labels;
};
static void gimp_color_scales_dispose (GObject *object);
static void gimp_color_scales_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_color_scales_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_color_scales_togg_sensitive (GimpColorSelector *selector,
gboolean sensitive);
static void gimp_color_scales_togg_visible (GimpColorSelector *selector,
gboolean visible);
static void gimp_color_scales_set_show_alpha (GimpColorSelector *selector,
gboolean show_alpha);
static void gimp_color_scales_set_color (GimpColorSelector *selector,
GeglColor *color);
static void gimp_color_scales_set_channel (GimpColorSelector *selector,
GimpColorSelectorChannel channel);
static void gimp_color_scales_set_model_visible
(GimpColorSelector *selector,
GimpColorSelectorModel model,
gboolean visible);
static void gimp_color_scales_set_config (GimpColorSelector *selector,
GimpColorConfig *config);
static void gimp_color_scales_set_format (GimpColorSelector *selector,
const Babl *format);
static void gimp_color_scales_update_visible (GimpColorScales *scales);
static void gimp_color_scales_update_scales (GimpColorScales *scales,
gint skip);
static void gimp_color_scales_toggle_changed (GtkWidget *widget,
GimpColorScales *scales);
static void gimp_color_scales_scale_changed (GtkWidget *scale,
GimpColorScales *scales);
static void gimp_color_scales_toggle_lch_hsv (GtkToggleButton *toggle,
GimpColorScales *scales);
G_DEFINE_TYPE (GimpColorScales, gimp_color_scales, GIMP_TYPE_COLOR_SELECTOR)
#define parent_class gimp_color_scales_parent_class
static const ColorScale scale_defs[] =
{
{ GIMP_COLOR_SELECTOR_HUE, 0, 0, 360, 30, 0, 360 },
{ GIMP_COLOR_SELECTOR_SATURATION, 0, 0, 100, 10, 0, 500 },
{ GIMP_COLOR_SELECTOR_VALUE, 0, 0, 100, 10, 0, 500 },
{ GIMP_COLOR_SELECTOR_RED, 0, 0, 100, 10, -500, 500 },
{ GIMP_COLOR_SELECTOR_GREEN, 0, 0, 100, 10, -500, 500 },
{ GIMP_COLOR_SELECTOR_BLUE, 0, 0, 100, 10, -500, 500 },
{ GIMP_COLOR_SELECTOR_ALPHA, 0, 0, 100, 10, 0, 100 },
{ GIMP_COLOR_SELECTOR_LCH_LIGHTNESS, 0, 0, 100, 10, 0, 300 },
{ GIMP_COLOR_SELECTOR_LCH_CHROMA, 0, 0, 200, 10, 0, 300 },
{ GIMP_COLOR_SELECTOR_LCH_HUE, 0, 0, 360, 30, 0, 360 },
{ (GimpColorSelectorChannel) GIMP_COLOR_SELECTOR_RED_U8,
0, 0, 255, 16, -1275, 1275 },
{ (GimpColorSelectorChannel) GIMP_COLOR_SELECTOR_GREEN_U8,
0, 0, 255, 16, -1275, 1275 },
{ (GimpColorSelectorChannel) GIMP_COLOR_SELECTOR_BLUE_U8,
0, 0, 255, 16, -1275, 1275 },
{ (GimpColorSelectorChannel) GIMP_COLOR_SELECTOR_ALPHA_U8,
0, 0, 255, 16, 0, 255 }
};
static void
gimp_color_scales_class_init (GimpColorScalesClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GimpColorSelectorClass *selector_class = GIMP_COLOR_SELECTOR_CLASS (klass);
object_class->dispose = gimp_color_scales_dispose;
object_class->get_property = gimp_color_scales_get_property;
object_class->set_property = gimp_color_scales_set_property;
selector_class->name = _("Scales");
selector_class->help_id = "gimp-colorselector-scales";
selector_class->icon_name = GIMP_ICON_DIALOG_TOOL_OPTIONS;
selector_class->set_toggles_visible = gimp_color_scales_togg_visible;
selector_class->set_toggles_sensitive = gimp_color_scales_togg_sensitive;
selector_class->set_show_alpha = gimp_color_scales_set_show_alpha;
selector_class->set_color = gimp_color_scales_set_color;
selector_class->set_channel = gimp_color_scales_set_channel;
selector_class->set_model_visible = gimp_color_scales_set_model_visible;
selector_class->set_config = gimp_color_scales_set_config;
selector_class->set_format = gimp_color_scales_set_format;
g_object_class_install_property (object_class, PROP_SHOW_RGB_U8,
g_param_spec_boolean ("show-rgb-u8",
"Show RGB 0..255",
"Show RGB 0..255 scales",
FALSE,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_SHOW_HSV,
g_param_spec_boolean ("show-hsv",
"Show HSV",
"Show HSV instead of LCH",
FALSE,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
gtk_widget_class_set_css_name (widget_class, "GimpColorScales");
}
static GtkWidget *
create_group (GimpColorScales *scales,
GSList **radio_group,
GtkSizeGroup *size_group0,
GtkSizeGroup *size_group1,
GtkSizeGroup *size_group2,
GimpColorSelectorChannel first_channel,
GimpColorSelectorChannel last_channel)
{
GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
GtkWidget *grid;
GEnumClass *enum_class;
GtkWidget *label = NULL;
gboolean add_label = FALSE;
gint row;
gint i;
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), 1);
gtk_grid_set_column_spacing (GTK_GRID (grid), 1);
enum_class = g_type_class_ref (GIMP_TYPE_COLOR_SELECTOR_CHANNEL);
for (i = first_channel, row = 0; i <= last_channel; i++, row++)
{
const GimpEnumDesc *enum_desc;
gint enum_value = i;
gboolean is_u8 = FALSE;
if ((enum_value >= GIMP_COLOR_SELECTOR_RED_U8 &&
enum_value <= GIMP_COLOR_SELECTOR_BLUE_U8) ||
(enum_value >= GIMP_COLOR_SELECTOR_HUE &&
enum_value <= GIMP_COLOR_SELECTOR_BLUE))
add_label = TRUE;
if (enum_value >= GIMP_COLOR_SELECTOR_RED_U8 &&
enum_value <= GIMP_COLOR_SELECTOR_ALPHA_U8)
{
enum_value -= 7;
is_u8 = TRUE;
}
enum_desc = gimp_enum_get_desc (enum_class, enum_value);
if (i == GIMP_COLOR_SELECTOR_ALPHA ||
i == GIMP_COLOR_SELECTOR_ALPHA_U8)
{
/* just to allocate the space via the size group */
scales->toggles[i] = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
}
else
{
scales->toggles[i] = gtk_radio_button_new (*radio_group);
*radio_group =
gtk_radio_button_get_group (GTK_RADIO_BUTTON (scales->toggles[i]));
if (enum_value == gimp_color_selector_get_channel (selector))
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scales->toggles[i]),
TRUE);
if (is_u8)
{
/* bind the RGB U8 toggles to the RGB percent toggles */
g_object_bind_property (scales->toggles[i - 7], "active",
scales->toggles[i], "active",
G_BINDING_SYNC_CREATE |
G_BINDING_BIDIRECTIONAL);
}
else
{
g_signal_connect (scales->toggles[i], "toggled",
G_CALLBACK (gimp_color_scales_toggle_changed),
scales);
}
}
gtk_grid_attach (GTK_GRID (grid), scales->toggles[i], 0, row, 1, 1);
if (gimp_color_selector_get_toggles_visible (selector))
gtk_widget_show (scales->toggles[i]);
gimp_help_set_help_data (scales->toggles[i],
gettext (enum_desc->value_help), NULL);
gtk_size_group_add_widget (size_group0, scales->toggles[i]);
scales->scales[i] =
gimp_color_scale_entry_new (gettext (enum_desc->value_desc),
scale_defs[i].default_value,
scale_defs[i].spin_min_value,
scale_defs[i].spin_max_value,
1);
gtk_grid_attach (GTK_GRID (grid), scales->scales[i], 1, row, 3, 1);
gimp_label_spin_set_increments (GIMP_LABEL_SPIN (scales->scales[i]),
1.0, scale_defs[i].scale_inc);
gimp_help_set_help_data (scales->scales[i],
gettext (enum_desc->value_help),
NULL);
gtk_widget_show (scales->scales[i]);
gimp_scale_entry_set_bounds (GIMP_SCALE_ENTRY (scales->scales[i]),
scale_defs[i].scale_min_value,
scale_defs[i].scale_max_value,
TRUE);
g_object_add_weak_pointer (G_OBJECT (scales->scales[i]),
(gpointer) &scales->scales[i]);
gimp_color_scale_set_channel (GIMP_COLOR_SCALE (gimp_scale_entry_get_range (GIMP_SCALE_ENTRY (scales->scales[i]))),
enum_value);
gtk_size_group_add_widget (size_group1, scales->scales[i]);
gtk_size_group_add_widget (size_group2,
gimp_label_spin_get_spin_button (GIMP_LABEL_SPIN (scales->scales[i])));
g_signal_connect (scales->scales[i], "value-changed",
G_CALLBACK (gimp_color_scales_scale_changed),
scales);
}
if (add_label)
{
GtkWidget *scrolled_window;
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_EXTERNAL, GTK_POLICY_NEVER);
gtk_grid_attach (GTK_GRID (grid), scrolled_window, 1, row, 3, 1);
gtk_widget_set_visible (scrolled_window, TRUE);
label = gtk_label_new (NULL);
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
gtk_label_set_text (GTK_LABEL (label), _("Profile: sRGB"));
gtk_container_add (GTK_CONTAINER (scrolled_window), label);
gtk_widget_set_visible (label, TRUE);
scales->profile_labels = g_list_prepend (scales->profile_labels, label);
}
g_type_class_unref (enum_class);
return grid;
}
static void
gimp_color_scales_init (GimpColorScales *scales)
{
GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
GtkSizeGroup *size_group0;
GtkSizeGroup *size_group1;
GtkSizeGroup *size_group2;
GtkWidget *hbox;
GtkWidget *radio1;
GtkWidget *radio2;
GtkWidget *grid;
GSList *main_group;
GSList *u8_group;
gtk_box_set_spacing (GTK_BOX (scales), 5);
scales->show_rgb_u8_binding = NULL;
scales->show_hsv_binding = NULL;
/* don't need the toggles for our own operation */
gimp_color_selector_set_toggles_visible (selector, FALSE);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
gtk_box_pack_start (GTK_BOX (scales), hbox, 0, 0, FALSE);
gtk_widget_show (hbox);
main_group = NULL;
u8_group = NULL;
scales->profile_labels = NULL;
scales->dummy_u8_toggle = gtk_radio_button_new (NULL);
g_object_ref_sink (scales->dummy_u8_toggle);
u8_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (scales->dummy_u8_toggle));
size_group0 = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
size_group1 = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
size_group2 = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
scales->rgb_percent_group =
grid = create_group (scales, &main_group,
size_group0, size_group1, size_group2,
GIMP_COLOR_SELECTOR_RED,
GIMP_COLOR_SELECTOR_BLUE);
gtk_box_pack_start (GTK_BOX (scales), grid, FALSE, FALSE, 0);
scales->rgb_u8_group =
grid = create_group (scales, &u8_group,
size_group0, size_group1, size_group2,
(GimpColorSelectorChannel) GIMP_COLOR_SELECTOR_RED_U8,
(GimpColorSelectorChannel) GIMP_COLOR_SELECTOR_BLUE_U8);
gtk_box_pack_start (GTK_BOX (scales), grid, FALSE, FALSE, 0);
scales->lch_group =
grid = create_group (scales, &main_group,
size_group0, size_group1, size_group2,
GIMP_COLOR_SELECTOR_LCH_LIGHTNESS,
GIMP_COLOR_SELECTOR_LCH_HUE);
gtk_box_pack_start (GTK_BOX (scales), grid, FALSE, FALSE, 0);
scales->hsv_group =
grid = create_group (scales, &main_group,
size_group0, size_group1, size_group2,
GIMP_COLOR_SELECTOR_HUE,
GIMP_COLOR_SELECTOR_VALUE);
gtk_box_pack_start (GTK_BOX (scales), grid, FALSE, FALSE, 0);
scales->alpha_percent_group =
grid = create_group (scales, &main_group,
size_group0, size_group1, size_group2,
GIMP_COLOR_SELECTOR_ALPHA,
GIMP_COLOR_SELECTOR_ALPHA);
gtk_box_pack_start (GTK_BOX (scales), grid, FALSE, FALSE, 0);
scales->alpha_u8_group =
grid = create_group (scales, &u8_group,
size_group0, size_group1, size_group2,
(GimpColorSelectorChannel) GIMP_COLOR_SELECTOR_ALPHA_U8,
(GimpColorSelectorChannel) GIMP_COLOR_SELECTOR_ALPHA_U8);
gtk_box_pack_start (GTK_BOX (scales), grid, FALSE, FALSE, 0);
g_object_unref (size_group0);
g_object_unref (size_group1);
g_object_unref (size_group2);
radio1 = gtk_radio_button_new_with_label (NULL, _("0..100"));
radio2 = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio1),
_("0..255"));
gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (radio1), FALSE);
gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (radio2), FALSE);
gtk_box_pack_start (GTK_BOX (hbox), radio1, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (hbox), radio2, FALSE, FALSE, 0);
gtk_widget_show (radio1);
gtk_widget_show (radio2);
if (scales->show_rgb_u8)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio2), TRUE);
else
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio1), TRUE);
g_object_bind_property (G_OBJECT (radio2), "active",
G_OBJECT (scales), "show-rgb-u8",
G_BINDING_SYNC_CREATE |
G_BINDING_BIDIRECTIONAL);
radio1 = gtk_radio_button_new_with_label (NULL, _("LCh"));
radio2 = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio1),
_("HSV"));
gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (radio1), FALSE);
gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (radio2), FALSE);
gtk_box_pack_end (GTK_BOX (hbox), radio2, FALSE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (hbox), radio1, FALSE, FALSE, 0);
gtk_widget_show (radio1);
gtk_widget_show (radio2);
if (gimp_color_selector_get_model_visible (selector,
GIMP_COLOR_SELECTOR_MODEL_HSV))
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio2), TRUE);
else
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio1), TRUE);
g_object_bind_property (G_OBJECT (radio2), "active",
G_OBJECT (scales), "show-hsv",
G_BINDING_SYNC_CREATE |
G_BINDING_BIDIRECTIONAL);
g_signal_connect (radio1, "toggled",
G_CALLBACK (gimp_color_scales_toggle_lch_hsv),
scales);
gimp_color_scales_update_visible (scales);
}
static void
gimp_color_scales_dispose (GObject *object)
{
GimpColorScales *scales = GIMP_COLOR_SCALES (object);
g_clear_object (&scales->dummy_u8_toggle);
g_clear_pointer (&scales->show_rgb_u8_binding, g_binding_unbind);
g_clear_pointer (&scales->show_hsv_binding, g_binding_unbind);
g_clear_pointer (&scales->profile_labels, g_list_free);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gimp_color_scales_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpColorScales *scales = GIMP_COLOR_SCALES (object);
gboolean hsv;
switch (property_id)
{
case PROP_SHOW_RGB_U8:
g_value_set_boolean (value, scales->show_rgb_u8);
break;
case PROP_SHOW_HSV:
hsv = gimp_color_selector_get_model_visible (GIMP_COLOR_SELECTOR (object),
GIMP_COLOR_SELECTOR_MODEL_HSV);
g_value_set_boolean (value, hsv);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_color_scales_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpColorScales *scales = GIMP_COLOR_SCALES (object);
gboolean show_hsv;
switch (property_id)
{
case PROP_SHOW_RGB_U8:
gimp_color_scales_set_show_rgb_u8 (scales, g_value_get_boolean (value));
break;
case PROP_SHOW_HSV:
show_hsv = g_value_get_boolean (value);
gimp_color_selector_set_model_visible (GIMP_COLOR_SELECTOR (object),
GIMP_COLOR_SELECTOR_MODEL_LCH,
! show_hsv);
gimp_color_selector_set_model_visible (GIMP_COLOR_SELECTOR (object),
GIMP_COLOR_SELECTOR_MODEL_HSV,
show_hsv);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_color_scales_togg_sensitive (GimpColorSelector *selector,
gboolean sensitive)
{
GimpColorScales *scales = GIMP_COLOR_SCALES (selector);
gint i;
for (i = 0; i < G_N_ELEMENTS (scale_defs); i++)
if (scales->toggles[i])
gtk_widget_set_sensitive (scales->toggles[i], sensitive);
}
static void
gimp_color_scales_togg_visible (GimpColorSelector *selector,
gboolean visible)
{
GimpColorScales *scales = GIMP_COLOR_SCALES (selector);
gint i;
for (i = 0; i < G_N_ELEMENTS (scale_defs); i++)
if (scales->toggles[i])
gtk_widget_set_visible (scales->toggles[i], visible);
}
static void
gimp_color_scales_set_show_alpha (GimpColorSelector *selector,
gboolean show_alpha)
{
gimp_color_scales_update_visible (GIMP_COLOR_SCALES (selector));
}
static void
gimp_color_scales_set_color (GimpColorSelector *selector,
GeglColor *color)
{
GimpColorScales *scales = GIMP_COLOR_SCALES (selector);
gimp_color_scales_update_scales (scales, -1);
}
static void
gimp_color_scales_set_channel (GimpColorSelector *selector,
GimpColorSelectorChannel channel)
{
GimpColorScales *scales = GIMP_COLOR_SCALES (selector);
if (GTK_IS_RADIO_BUTTON (scales->toggles[channel]))
{
g_signal_handlers_block_by_func (scales->toggles[channel],
gimp_color_scales_toggle_changed,
scales);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scales->toggles[channel]),
TRUE);
g_signal_handlers_unblock_by_func (scales->toggles[channel],
gimp_color_scales_toggle_changed,
scales);
}
}
static void
gimp_color_scales_set_model_visible (GimpColorSelector *selector,
GimpColorSelectorModel model,
gboolean visible)
{
gimp_color_scales_update_visible (GIMP_COLOR_SCALES (selector));
}
static void
gimp_color_scales_set_config (GimpColorSelector *selector,
GimpColorConfig *config)
{
GimpColorScales *scales = GIMP_COLOR_SCALES (selector);
gint i;
g_clear_pointer (&scales->show_rgb_u8_binding, g_binding_unbind);
g_clear_pointer (&scales->show_hsv_binding, g_binding_unbind);
if (config)
{
scales->show_rgb_u8_binding = g_object_bind_property (config, "show-rgb-u8",
scales, "show-rgb-u8",
G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
scales->show_hsv_binding = g_object_bind_property (config, "show-hsv",
scales, "show-hsv",
G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
}
for (i = 0; i < G_N_ELEMENTS (scale_defs); i++)
{
if (scales->scales[i])
gimp_color_scale_set_color_config (GIMP_COLOR_SCALE (gimp_scale_entry_get_range (GIMP_SCALE_ENTRY (scales->scales[i]))),
config);
}
}
static void
gimp_color_scales_set_format (GimpColorSelector *selector,
const Babl *format)
{
GimpColorScales *scales = GIMP_COLOR_SCALES (selector);
scales->format = format;
if (format == NULL || babl_format_get_space (format) == babl_space ("sRGB"))
{
for (GList *iter = scales->profile_labels; iter; iter = iter->next)
{
gtk_label_set_text (GTK_LABEL (iter->data), _("Profile: sRGB"));
gimp_help_set_help_data (iter->data, NULL, NULL);
}
}
else
{
GimpColorProfile *profile = NULL;
const gchar *icc;
gint icc_len;
icc = babl_space_get_icc (babl_format_get_space (format), &icc_len);
profile = gimp_color_profile_new_from_icc_profile ((const guint8 *) icc, icc_len, NULL);
if (profile != NULL)
{
gchar *text;
text = g_strdup_printf (_("Profile: %s"), gimp_color_profile_get_label (profile));
for (GList *iter = scales->profile_labels; iter; iter = iter->next)
{
gtk_label_set_text (GTK_LABEL (iter->data), text);
gimp_help_set_help_data (iter->data,
gimp_color_profile_get_summary (profile),
NULL);
}
g_free (text);
}
else
{
for (GList *iter = scales->profile_labels; iter; iter = iter->next)
{
gtk_label_set_markup (GTK_LABEL (iter->data), _("Profile: <i>unknown</i>"));
gimp_help_set_help_data (iter->data, NULL, NULL);
}
}
g_clear_object (&profile);
}
gimp_color_scales_update_scales (scales, -1);
}
/* public functions */
void
gimp_color_scales_set_show_rgb_u8 (GimpColorScales *scales,
gboolean show_rgb_u8)
{
g_return_if_fail (GIMP_IS_COLOR_SCALES (scales));
show_rgb_u8 = show_rgb_u8 ? TRUE : FALSE;
if (show_rgb_u8 != scales->show_rgb_u8)
{
scales->show_rgb_u8 = show_rgb_u8;
g_object_notify (G_OBJECT (scales), "show-rgb-u8");
gimp_color_scales_update_visible (scales);
}
}
gboolean
gimp_color_scales_get_show_rgb_u8 (GimpColorScales *scales)
{
g_return_val_if_fail (GIMP_IS_COLOR_SCALES (scales), FALSE);
return scales->show_rgb_u8;
}
/* private functions */
static void
gimp_color_scales_update_visible (GimpColorScales *scales)
{
GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
gboolean show_alpha;
gboolean rgb_visible;
gboolean lch_visible;
gboolean hsv_visible;
show_alpha = gimp_color_selector_get_show_alpha (selector);
rgb_visible = gimp_color_selector_get_model_visible (selector,
GIMP_COLOR_SELECTOR_MODEL_RGB);
lch_visible = gimp_color_selector_get_model_visible (selector,
GIMP_COLOR_SELECTOR_MODEL_LCH);
hsv_visible = gimp_color_selector_get_model_visible (selector,
GIMP_COLOR_SELECTOR_MODEL_HSV);
gtk_widget_set_visible (scales->rgb_u8_group,
rgb_visible && scales->show_rgb_u8);
gtk_widget_set_visible (scales->rgb_percent_group,
rgb_visible && ! scales->show_rgb_u8);
gtk_widget_set_visible (scales->lch_group, lch_visible);
gtk_widget_set_visible (scales->hsv_group, hsv_visible);
gtk_widget_set_visible (scales->alpha_percent_group,
show_alpha && ! scales->show_rgb_u8);
gtk_widget_set_visible (scales->alpha_u8_group,
show_alpha && scales->show_rgb_u8);
}
static void
gimp_color_scales_update_scales (GimpColorScales *scales,
gint skip)
{
GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
GeglColor *color = gimp_color_selector_get_color (selector);
gdouble pixel[4];
gfloat pixel_f[4];
gdouble values[G_N_ELEMENTS (scale_defs)];
gint i;
gegl_color_get_pixel (color, babl_format_with_space ("HSV float", scales->format), pixel_f);
values[GIMP_COLOR_SELECTOR_HUE] = pixel_f[0] * 360.0;
values[GIMP_COLOR_SELECTOR_SATURATION] = pixel_f[1] * 100.0;
values[GIMP_COLOR_SELECTOR_VALUE] = pixel_f[2] * 100.0;
gegl_color_get_pixel (color, babl_format_with_space ("R'G'B'A double", scales->format), pixel);
values[GIMP_COLOR_SELECTOR_RED] = pixel[0] * 100.0;
values[GIMP_COLOR_SELECTOR_GREEN] = pixel[1] * 100.0;
values[GIMP_COLOR_SELECTOR_BLUE] = pixel[2] * 100.0;
values[GIMP_COLOR_SELECTOR_ALPHA] = pixel[3] * 100.0;
values[GIMP_COLOR_SELECTOR_RED_U8] = pixel[0] * 255.0;
values[GIMP_COLOR_SELECTOR_GREEN_U8] = pixel[1] * 255.0;
values[GIMP_COLOR_SELECTOR_BLUE_U8] = pixel[2] * 255.0;
values[GIMP_COLOR_SELECTOR_ALPHA_U8] = pixel[3] * 255.0;
gegl_color_get_pixel (color, babl_format ("CIE LCH(ab) float"), pixel_f);
values[GIMP_COLOR_SELECTOR_LCH_LIGHTNESS] = pixel_f[0];
values[GIMP_COLOR_SELECTOR_LCH_CHROMA] = pixel_f[1];
values[GIMP_COLOR_SELECTOR_LCH_HUE] = pixel_f[2];
for (i = 0; i < G_N_ELEMENTS (scale_defs); i++)
{
if (i != skip)
{
g_signal_handlers_block_by_func (scales->scales[i],
gimp_color_scales_scale_changed,
scales);
gimp_label_spin_set_value (GIMP_LABEL_SPIN (scales->scales[i]), values[i]);
g_signal_handlers_unblock_by_func (scales->scales[i],
gimp_color_scales_scale_changed,
scales);
}
gimp_color_scale_set_format (GIMP_COLOR_SCALE (gimp_scale_entry_get_range (GIMP_SCALE_ENTRY (scales->scales[i]))),
scales->format);
gimp_color_scale_set_color (GIMP_COLOR_SCALE (gimp_scale_entry_get_range (GIMP_SCALE_ENTRY (scales->scales[i]))), color);
}
g_object_unref (color);
}
static void
gimp_color_scales_toggle_changed (GtkWidget *widget,
GimpColorScales *scales)
{
GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
{
gint i;
for (i = 0; i < G_N_ELEMENTS (scale_defs); i++)
{
if (widget == scales->toggles[i])
{
gimp_color_selector_set_channel (selector, i);
if (i < GIMP_COLOR_SELECTOR_RED ||
i > GIMP_COLOR_SELECTOR_BLUE)
{
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scales->dummy_u8_toggle),
TRUE);
}
break;
}
}
}
}
static void
gimp_color_scales_scale_changed (GtkWidget *scale,
GimpColorScales *scales)
{
GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
GeglColor *color = gimp_color_selector_get_color (selector);
gdouble value = gimp_label_spin_get_value (GIMP_LABEL_SPIN (scale));
gfloat lch[4];
gfloat hsv[4];
gdouble rgb[4];
gint i;
for (i = 0; i < G_N_ELEMENTS (scale_defs); i++)
if (scales->scales[i] == scale)
break;
gegl_color_get_pixel (color, babl_format_with_space ("R'G'B'A double", scales->format), rgb);
gegl_color_get_pixel (color, babl_format_with_space ("HSVA float", scales->format), hsv);
gegl_color_get_pixel (color, babl_format ("CIE LCH(ab) alpha float"), lch);
switch (i)
{
case GIMP_COLOR_SELECTOR_HUE:
hsv[0] = value / 360.0f;
break;
case GIMP_COLOR_SELECTOR_SATURATION:
hsv[1] = value / 100.0f;
break;
case GIMP_COLOR_SELECTOR_VALUE:
hsv[2] = value / 100.0f;
break;
case GIMP_COLOR_SELECTOR_RED:
rgb[0] = value / 100.0;
break;
case GIMP_COLOR_SELECTOR_GREEN:
rgb[1] = value / 100.0;
break;
case GIMP_COLOR_SELECTOR_BLUE:
rgb[2] = value / 100.0;
break;
case GIMP_COLOR_SELECTOR_ALPHA:
gimp_color_set_alpha (color, value / 100.0);
break;
case GIMP_COLOR_SELECTOR_LCH_LIGHTNESS:
lch[0] = (gfloat) value;
break;
case GIMP_COLOR_SELECTOR_LCH_CHROMA:
lch[1] = (gfloat) value;
break;
case GIMP_COLOR_SELECTOR_LCH_HUE:
lch[2] = (gfloat) value;
break;
case GIMP_COLOR_SELECTOR_RED_U8:
rgb[0] = value / 255.0;
break;
case GIMP_COLOR_SELECTOR_GREEN_U8:
rgb[1] = value / 255.0;
break;
case GIMP_COLOR_SELECTOR_BLUE_U8:
rgb[2] = value / 255.0;
break;
case GIMP_COLOR_SELECTOR_ALPHA_U8:
gimp_color_set_alpha (color, value / 255.0);
break;
}
if ((i >= GIMP_COLOR_SELECTOR_HUE) &&
(i <= GIMP_COLOR_SELECTOR_VALUE))
{
gegl_color_set_pixel (color, babl_format_with_space ("HSVA float", scales->format), hsv);
}
else if ((i >= GIMP_COLOR_SELECTOR_LCH_LIGHTNESS) &&
(i <= GIMP_COLOR_SELECTOR_LCH_HUE))
{
gegl_color_set_pixel (color, babl_format ("CIE LCH(ab) alpha float"), lch);
}
else if (((i >= GIMP_COLOR_SELECTOR_RED) &&
(i <= GIMP_COLOR_SELECTOR_BLUE)) ||
((i >= GIMP_COLOR_SELECTOR_RED_U8) &&
(i <= GIMP_COLOR_SELECTOR_BLUE_U8)))
{
gegl_color_set_pixel (color, babl_format_with_space ("R'G'B'A double", scales->format), rgb);
}
gimp_color_selector_set_color (selector, color);
g_object_unref (color);
}
static void
gimp_color_scales_toggle_lch_hsv (GtkToggleButton *toggle,
GimpColorScales *scales)
{
GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
gboolean show_hsv = ! gtk_toggle_button_get_active (toggle);
gimp_color_selector_set_model_visible (selector,
GIMP_COLOR_SELECTOR_MODEL_LCH,
! show_hsv);
gimp_color_selector_set_model_visible (selector,
GIMP_COLOR_SELECTOR_MODEL_HSV,
show_hsv);
g_object_set (scales, "show-hsv", show_hsv, NULL);
}

View file

@ -0,0 +1,48 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorscales.h
* Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
*
* based on color_notebook module
* Copyright (C) 1998 Austin Donnelly <austin@greenend.org.uk>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_COLOR_SCALES_H__
#define __GIMP_COLOR_SCALES_H__
#include <libgimpwidgets/gimpcolorselector.h>
G_BEGIN_DECLS
#define GIMP_TYPE_COLOR_SCALES (gimp_color_scales_get_type ())
G_DECLARE_FINAL_TYPE (GimpColorScales, gimp_color_scales, GIMP, COLOR_SCALES, GimpColorSelector)
void gimp_color_scales_set_show_rgb_u8 (GimpColorScales *scales,
gboolean show_rgb_u8);
gboolean gimp_color_scales_get_show_rgb_u8 (GimpColorScales *scales);
G_END_DECLS
#endif /* __GIMP_COLOR_SCALES_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorselect.h
* Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
*
* based on color_notebook module
* Copyright (C) 1998 Austin Donnelly <austin@greenend.org.uk>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#ifndef __GIMP_COLOR_SELECT_H__
#define __GIMP_COLOR_SELECT_H__
G_BEGIN_DECLS
#define GIMP_TYPE_COLOR_SELECT (gimp_color_select_get_type ())
G_DECLARE_FINAL_TYPE (GimpColorSelect, gimp_color_select, GIMP, COLOR_SELECT, GimpColorSelector)
G_END_DECLS
#endif /* __GIMP_COLOR_SELECT_H__ */

View file

@ -0,0 +1,820 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorselection.c
* Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpcolor/gimpcolor.h"
#include "libgimpconfig/gimpconfig.h"
#include "gimpwidgetstypes.h"
#include "gimpcolorarea.h"
#include "gimpcolornotebook.h"
#include "gimpcolorscales.h"
#include "gimpcolorselect.h"
#include "gimpcolorselection.h"
#include "gimphelpui.h"
#include "gimpicons.h"
#include "gimpwidgets.h"
#include "gimpwidgets-private.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimpcolorselection
* @title: GimpColorSelection
* @short_description: Widget for doing a color selection.
*
* Widget for doing a color selection.
**/
#define COLOR_AREA_SIZE 20
typedef enum
{
UPDATE_NOTEBOOK = 1 << 0,
UPDATE_SCALES = 1 << 1,
UPDATE_ENTRY = 1 << 2,
UPDATE_COLOR = 1 << 3
} UpdateType;
#define UPDATE_ALL (UPDATE_NOTEBOOK | \
UPDATE_SCALES | \
UPDATE_ENTRY | \
UPDATE_COLOR)
enum
{
COLOR_CHANGED,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_CONFIG
};
struct _GimpColorSelection
{
GtkBox parent_instance;
gboolean show_alpha;
GeglColor *color;
GimpColorSelectorChannel channel;
GtkWidget *left_vbox;
GtkWidget *right_vbox;
GtkWidget *notebook;
GtkWidget *scales;
GtkWidget *new_color;
GtkWidget *old_color;
};
static void gimp_color_selection_finalize (GObject *object);
static void gimp_color_selection_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_color_selection_switch_page (GtkWidget *widget,
gpointer page,
guint page_num,
GimpColorSelection *selection);
static void gimp_color_selection_notebook_changed (GimpColorSelector *selector,
GeglColor *color,
GimpColorSelection *selection);
static void gimp_color_selection_scales_changed (GimpColorSelector *selector,
GeglColor *color,
GimpColorSelection *selection);
static void gimp_color_selection_color_picked (GtkWidget *widget,
const GeglColor *rgb,
GimpColorSelection *selection);
static void gimp_color_selection_entry_changed (GimpColorHexEntry *entry,
GimpColorSelection *selection);
static void gimp_color_selection_channel_changed (GimpColorSelector *selector,
GimpColorSelectorChannel channel,
GimpColorSelection *selection);
static void gimp_color_selection_new_color_changed (GtkWidget *widget,
GimpColorSelection *selection);
static void gimp_color_selection_update (GimpColorSelection *selection,
UpdateType update);
G_DEFINE_TYPE (GimpColorSelection, gimp_color_selection, GTK_TYPE_BOX)
#define parent_class gimp_color_selection_parent_class
static guint selection_signals[LAST_SIGNAL] = { 0, };
static void
gimp_color_selection_class_init (GimpColorSelectionClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gimp_color_selection_finalize;
object_class->set_property = gimp_color_selection_set_property;
g_object_class_install_property (object_class, PROP_CONFIG,
g_param_spec_object ("config",
"Config",
"The color config used by this color selection",
GIMP_TYPE_COLOR_CONFIG,
G_PARAM_WRITABLE));
selection_signals[COLOR_CHANGED] =
g_signal_new ("color-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (klass), "GimpColorSelection");
}
static void
gimp_color_selection_init (GimpColorSelection *selection)
{
GtkWidget *main_hbox;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *frame;
GtkWidget *label;
GtkWidget *entry;
GtkWidget *button;
GtkSizeGroup *new_group;
GtkSizeGroup *old_group;
selection->show_alpha = TRUE;
gtk_orientable_set_orientation (GTK_ORIENTABLE (selection),
GTK_ORIENTATION_VERTICAL);
selection->color = gegl_color_new ("black");
selection->channel = GIMP_COLOR_SELECTOR_HUE;
main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start (GTK_BOX (selection), main_hbox, TRUE, TRUE, 0);
gtk_widget_show (main_hbox);
/* The left vbox with the notebook */
selection->left_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_box_pack_start (GTK_BOX (main_hbox), selection->left_vbox,
TRUE, TRUE, 0);
gtk_widget_show (selection->left_vbox);
if (_gimp_ensure_modules_func)
{
g_type_class_ref (GIMP_TYPE_COLOR_SELECT);
_gimp_ensure_modules_func ();
}
selection->notebook = gimp_color_selector_new (GIMP_TYPE_COLOR_NOTEBOOK, selection->color, selection->channel);
if (_gimp_ensure_modules_func)
g_type_class_unref (g_type_class_peek (GIMP_TYPE_COLOR_SELECT));
gimp_color_selector_set_toggles_visible
(GIMP_COLOR_SELECTOR (selection->notebook), FALSE);
gtk_box_pack_start (GTK_BOX (selection->left_vbox), selection->notebook,
TRUE, TRUE, 0);
gtk_widget_show (selection->notebook);
g_signal_connect (selection->notebook, "color-changed",
G_CALLBACK (gimp_color_selection_notebook_changed),
selection);
g_signal_connect (gimp_color_notebook_get_notebook (GIMP_COLOR_NOTEBOOK (selection->notebook)),
"switch-page",
G_CALLBACK (gimp_color_selection_switch_page),
selection);
/* The hbox for the color_areas */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_end (GTK_BOX (selection->left_vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
/* The labels */
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
gtk_widget_show (vbox);
label = gtk_label_new (_("Current:"));
gtk_label_set_xalign (GTK_LABEL (label), 1.0);
gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
gtk_widget_show (label);
new_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
gtk_size_group_add_widget (new_group, label);
g_object_unref (new_group);
label = gtk_label_new (_("Old:"));
gtk_label_set_xalign (GTK_LABEL (label), 1.0);
gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
gtk_widget_show (label);
old_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
gtk_size_group_add_widget (old_group, label);
g_object_unref (old_group);
/* The color areas */
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
gtk_widget_show (frame);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add (GTK_CONTAINER (frame), vbox);
gtk_widget_show (vbox);
selection->new_color = gimp_color_area_new (selection->color,
selection->show_alpha ?
GIMP_COLOR_AREA_SMALL_CHECKS :
GIMP_COLOR_AREA_FLAT,
GDK_BUTTON1_MASK |
GDK_BUTTON2_MASK);
gtk_size_group_add_widget (new_group, selection->new_color);
gtk_box_pack_start (GTK_BOX (vbox), selection->new_color, FALSE, FALSE, 0);
gtk_widget_show (selection->new_color);
g_signal_connect (selection->new_color, "color-changed",
G_CALLBACK (gimp_color_selection_new_color_changed),
selection);
selection->old_color = gimp_color_area_new (selection->color,
selection->show_alpha ?
GIMP_COLOR_AREA_SMALL_CHECKS :
GIMP_COLOR_AREA_FLAT,
GDK_BUTTON1_MASK |
GDK_BUTTON2_MASK);
gtk_drag_dest_unset (selection->old_color);
gtk_size_group_add_widget (old_group, selection->old_color);
gtk_box_pack_start (GTK_BOX (vbox), selection->old_color, FALSE, FALSE, 0);
gtk_widget_show (selection->old_color);
/* The right vbox with color scales */
selection->right_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_box_pack_start (GTK_BOX (main_hbox), selection->right_vbox,
TRUE, TRUE, 0);
gtk_widget_show (selection->right_vbox);
selection->scales = gimp_color_selector_new (GIMP_TYPE_COLOR_SCALES, selection->color, selection->channel);
gimp_color_selector_set_toggles_visible
(GIMP_COLOR_SELECTOR (selection->scales), TRUE);
gimp_color_selector_set_show_alpha (GIMP_COLOR_SELECTOR (selection->scales),
selection->show_alpha);
gtk_box_pack_start (GTK_BOX (selection->right_vbox), selection->scales,
TRUE, TRUE, 0);
gtk_widget_show (selection->scales);
g_signal_connect (selection->scales, "channel-changed",
G_CALLBACK (gimp_color_selection_channel_changed),
selection);
g_signal_connect (selection->scales, "color-changed",
G_CALLBACK (gimp_color_selection_scales_changed),
selection);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start (GTK_BOX (selection->right_vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
/* The color picker */
button = gimp_pick_button_new ();
gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
g_signal_connect (button, "color-picked",
G_CALLBACK (gimp_color_selection_color_picked),
selection);
/* The hex triplet entry */
entry = gimp_color_hex_entry_new ();
gtk_box_pack_end (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
gtk_widget_show (entry);
label = gtk_label_new_with_mnemonic (_("HTML _notation:"));
gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
g_object_set_data (G_OBJECT (selection), "color-hex-entry", entry);
g_signal_connect (entry, "color-changed",
G_CALLBACK (gimp_color_selection_entry_changed),
selection);
}
static void
gimp_color_selection_finalize (GObject *object)
{
GimpColorSelection *selection = GIMP_COLOR_SELECTION (object);
g_object_unref (selection->color);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_color_selection_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpColorSelection *selection = GIMP_COLOR_SELECTION (object);
switch (property_id)
{
case PROP_CONFIG:
gimp_color_selection_set_config (selection, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* gimp_color_selection_new:
*
* Creates a new #GimpColorSelection widget.
*
* Returns: The new #GimpColorSelection widget.
**/
GtkWidget *
gimp_color_selection_new (void)
{
return g_object_new (GIMP_TYPE_COLOR_SELECTION, NULL);
}
/**
* gimp_color_selection_set_show_alpha:
* @selection: A #GimpColorSelection widget.
* @show_alpha: The new @show_alpha setting.
*
* Sets the @show_alpha property of the @selection widget.
**/
void
gimp_color_selection_set_show_alpha (GimpColorSelection *selection,
gboolean show_alpha)
{
g_return_if_fail (GIMP_IS_COLOR_SELECTION (selection));
if (show_alpha != selection->show_alpha)
{
selection->show_alpha = show_alpha ? TRUE : FALSE;
gimp_color_selector_set_show_alpha
(GIMP_COLOR_SELECTOR (selection->notebook), selection->show_alpha);
gimp_color_selector_set_show_alpha
(GIMP_COLOR_SELECTOR (selection->scales), selection->show_alpha);
gimp_color_area_set_type (GIMP_COLOR_AREA (selection->new_color),
selection->show_alpha ?
GIMP_COLOR_AREA_SMALL_CHECKS :
GIMP_COLOR_AREA_FLAT);
gimp_color_area_set_type (GIMP_COLOR_AREA (selection->old_color),
selection->show_alpha ?
GIMP_COLOR_AREA_SMALL_CHECKS :
GIMP_COLOR_AREA_FLAT);
}
}
/**
* gimp_color_selection_get_show_alpha:
* @selection: A #GimpColorSelection widget.
*
* Returns the @selection's @show_alpha property.
*
* Returns: %TRUE if the #GimpColorSelection has alpha controls.
**/
gboolean
gimp_color_selection_get_show_alpha (GimpColorSelection *selection)
{
g_return_val_if_fail (GIMP_IS_COLOR_SELECTION (selection), FALSE);
return selection->show_alpha;
}
/**
* gimp_color_selection_set_color:
* @selection: A #GimpColorSelection widget.
* @color: The @color to set as current color.
*
* Sets the #GimpColorSelection's current color to the new @color.
**/
void
gimp_color_selection_set_color (GimpColorSelection *selection,
GeglColor *color)
{
GeglColor *old_color;
UpdateType update;
g_return_if_fail (GIMP_IS_COLOR_SELECTION (selection));
g_return_if_fail (GEGL_IS_COLOR (color));
old_color = selection->color;
selection->color = gegl_color_duplicate (color);
update = UPDATE_ALL;
if (gimp_color_is_perceptually_identical (color, old_color))
update &= ~UPDATE_COLOR;
gimp_color_selection_update (selection, update);
gimp_color_selection_color_changed (selection);
g_object_unref (old_color);
}
/**
* gimp_color_selection_get_color:
* @selection: A #GimpColorSelection widget.
*
* This function returns the #GimpColorSelection's current color.
*
* Returns: (transfer full): the currently selected color.
**/
GeglColor *
gimp_color_selection_get_color (GimpColorSelection *selection)
{
g_return_val_if_fail (GIMP_IS_COLOR_SELECTION (selection), NULL);
return gegl_color_duplicate (selection->color);
}
/**
* gimp_color_selection_set_old_color:
* @selection: A #GimpColorSelection widget.
* @color: The @color to set as old color.
*
* Sets the #GimpColorSelection's old color.
**/
void
gimp_color_selection_set_old_color (GimpColorSelection *selection,
GeglColor *color)
{
g_return_if_fail (GIMP_IS_COLOR_SELECTION (selection));
g_return_if_fail (GEGL_IS_COLOR (color));
gimp_color_area_set_color (GIMP_COLOR_AREA (selection->old_color), color);
}
/**
* gimp_color_selection_get_old_color:
* @selection: A #GimpColorSelection widget.
*
* Returns: (transfer full): the old color.
**/
GeglColor *
gimp_color_selection_get_old_color (GimpColorSelection *selection)
{
g_return_val_if_fail (GIMP_IS_COLOR_SELECTION (selection), NULL);
return gimp_color_area_get_color (GIMP_COLOR_AREA (selection->old_color));
}
/**
* gimp_color_selection_reset:
* @selection: A #GimpColorSelection widget.
*
* Sets the #GimpColorSelection's current color to its old color.
**/
void
gimp_color_selection_reset (GimpColorSelection *selection)
{
GeglColor *color;
g_return_if_fail (GIMP_IS_COLOR_SELECTION (selection));
color = gimp_color_area_get_color (GIMP_COLOR_AREA (selection->old_color));
gimp_color_selection_set_color (selection, color);
g_object_unref (color);
}
/**
* gimp_color_selection_color_changed:
* @selection: A #GimpColorSelection widget.
*
* Emits the "color-changed" signal.
**/
void
gimp_color_selection_color_changed (GimpColorSelection *selection)
{
g_return_if_fail (GIMP_IS_COLOR_SELECTION (selection));
g_signal_emit (selection, selection_signals[COLOR_CHANGED], 0);
}
/**
* gimp_color_selection_set_format:
* @selection: A #GimpColorSelection widget.
* @format: A Babl format, with space.
*
* Updates all selectors with the current format.
*
* Since: 3.0
*/
void
gimp_color_selection_set_format (GimpColorSelection *selection,
const Babl *format)
{
g_return_if_fail (GIMP_IS_COLOR_SELECTION (selection));
gimp_color_notebook_set_format (GIMP_COLOR_NOTEBOOK (selection->notebook),
format);
gimp_color_selector_set_format (GIMP_COLOR_SELECTOR (selection->scales),
format);
g_signal_emit (selection, selection_signals[COLOR_CHANGED], 0);
}
/**
* gimp_color_selection_set_simulation:
* @selection: A #GimpColorSelection widget.
* @profile: A #GimpColorProfile object.
* @intent: A #GimpColorRenderingIntent enum.
* @bpc: A gboolean.
*
* Sets the simulation options to use with this color selection.
*
* Since: 3.0
*/
void
gimp_color_selection_set_simulation (GimpColorSelection *selection,
GimpColorProfile *profile,
GimpColorRenderingIntent intent,
gboolean bpc)
{
g_return_if_fail (GIMP_IS_COLOR_SELECTION (selection));
gimp_color_notebook_set_simulation (GIMP_COLOR_NOTEBOOK (selection->notebook),
profile,
intent,
bpc);
g_signal_emit (selection, selection_signals[COLOR_CHANGED], 0);
}
/**
* gimp_color_selection_set_config:
* @selection: A #GimpColorSelection widget.
* @config: A #GimpColorConfig object.
*
* Sets the color management configuration to use with this color selection.
*
* Since: 2.4
*/
void
gimp_color_selection_set_config (GimpColorSelection *selection,
GimpColorConfig *config)
{
g_return_if_fail (GIMP_IS_COLOR_SELECTION (selection));
g_return_if_fail (config == NULL || GIMP_IS_COLOR_CONFIG (config));
gimp_color_selector_set_config (GIMP_COLOR_SELECTOR (selection->notebook),
config);
gimp_color_selector_set_config (GIMP_COLOR_SELECTOR (selection->scales),
config);
gimp_color_area_set_color_config (GIMP_COLOR_AREA (selection->old_color),
config);
gimp_color_area_set_color_config (GIMP_COLOR_AREA (selection->new_color),
config);
}
/**
* gimp_color_selection_get_notebook:
* @selection: A #GimpColorSelection widget.
*
* Returns: (transfer none): The selection's #GimpColorNotebook.
*
* Since: 3.0
*/
GtkWidget *
gimp_color_selection_get_notebook (GimpColorSelection *selection)
{
g_return_val_if_fail (GIMP_IS_COLOR_SELECTION (selection), NULL);
return selection->notebook;
}
/**
* gimp_color_selection_get_right_vbox:
* @selection: A #GimpColorSelection widget.
*
* Returns: (transfer none) (type GtkBox): The selection's right #GtkBox which
* contains the color scales.
*
* Since: 3.0
*/
GtkWidget *
gimp_color_selection_get_right_vbox (GimpColorSelection *selection)
{
g_return_val_if_fail (GIMP_IS_COLOR_SELECTION (selection), NULL);
return selection->right_vbox;
}
/* private functions */
static void
gimp_color_selection_switch_page (GtkWidget *widget,
gpointer page,
guint page_num,
GimpColorSelection *selection)
{
GimpColorNotebook *notebook;
GimpColorSelector *current;
gboolean sensitive;
notebook = GIMP_COLOR_NOTEBOOK (selection->notebook);
current = gimp_color_notebook_get_current_selector (notebook);
sensitive = (GIMP_COLOR_SELECTOR_GET_CLASS (current)->set_channel != NULL);
gimp_color_selector_set_toggles_sensitive
(GIMP_COLOR_SELECTOR (selection->scales), sensitive);
}
static void
gimp_color_selection_notebook_changed (GimpColorSelector *selector,
GeglColor *color,
GimpColorSelection *selection)
{
GeglColor *old_color;
UpdateType update;
old_color = selection->color;
selection->color = gegl_color_duplicate (color);
update = UPDATE_SCALES | UPDATE_ENTRY;
if (! gimp_color_is_perceptually_identical (color, old_color))
update |= UPDATE_COLOR;
gimp_color_selection_update (selection, update);
gimp_color_selection_color_changed (selection);
g_object_unref (old_color);
}
static void
gimp_color_selection_scales_changed (GimpColorSelector *selector,
GeglColor *color,
GimpColorSelection *selection)
{
UpdateType update;
GeglColor *old_color;
old_color = selection->color;
selection->color = gegl_color_duplicate (color);
update = UPDATE_ENTRY | UPDATE_NOTEBOOK;
if (! gimp_color_is_perceptually_identical (color, old_color))
update |= UPDATE_COLOR;
gimp_color_selection_update (selection, update);
gimp_color_selection_color_changed (selection);
g_object_unref (old_color);
}
static void
gimp_color_selection_color_picked (GtkWidget *widget,
const GeglColor *rgb,
GimpColorSelection *selection)
{
if (rgb)
gimp_color_selection_set_color (selection, (GeglColor *) rgb);
}
static void
gimp_color_selection_entry_changed (GimpColorHexEntry *entry,
GimpColorSelection *selection)
{
g_object_unref (selection->color);
selection->color = gimp_color_hex_entry_get_color (entry);
gimp_color_selection_update (selection,
UPDATE_NOTEBOOK | UPDATE_SCALES | UPDATE_COLOR);
gimp_color_selection_color_changed (selection);
}
static void
gimp_color_selection_channel_changed (GimpColorSelector *selector,
GimpColorSelectorChannel channel,
GimpColorSelection *selection)
{
selection->channel = channel;
gimp_color_selector_set_channel (GIMP_COLOR_SELECTOR (selection->notebook),
selection->channel);
}
static void
gimp_color_selection_new_color_changed (GtkWidget *widget,
GimpColorSelection *selection)
{
g_object_unref (selection->color);
selection->color = gimp_color_area_get_color (GIMP_COLOR_AREA (widget));
gimp_color_selection_update (selection,
UPDATE_NOTEBOOK | UPDATE_SCALES | UPDATE_ENTRY);
gimp_color_selection_color_changed (selection);
}
static void
gimp_color_selection_update (GimpColorSelection *selection,
UpdateType update)
{
if (update & UPDATE_NOTEBOOK)
{
g_signal_handlers_block_by_func (selection->notebook,
gimp_color_selection_notebook_changed,
selection);
gimp_color_selector_set_color (GIMP_COLOR_SELECTOR (selection->notebook), selection->color);
g_signal_handlers_unblock_by_func (selection->notebook,
gimp_color_selection_notebook_changed,
selection);
}
if (update & UPDATE_SCALES)
{
g_signal_handlers_block_by_func (selection->scales,
gimp_color_selection_scales_changed,
selection);
gimp_color_selector_set_color (GIMP_COLOR_SELECTOR (selection->scales), selection->color);
g_signal_handlers_unblock_by_func (selection->scales,
gimp_color_selection_scales_changed,
selection);
}
if (update & UPDATE_ENTRY)
{
GimpColorHexEntry *entry;
entry = g_object_get_data (G_OBJECT (selection), "color-hex-entry");
g_signal_handlers_block_by_func (entry,
gimp_color_selection_entry_changed,
selection);
gimp_color_hex_entry_set_color (entry, selection->color);
g_signal_handlers_unblock_by_func (entry,
gimp_color_selection_entry_changed,
selection);
}
if (update & UPDATE_COLOR)
{
g_signal_handlers_block_by_func (selection->new_color,
gimp_color_selection_new_color_changed,
selection);
gimp_color_area_set_color (GIMP_COLOR_AREA (selection->new_color), selection->color);
g_signal_handlers_unblock_by_func (selection->new_color,
gimp_color_selection_new_color_changed,
selection);
}
}

View file

@ -0,0 +1,72 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorselection.h
* Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_COLOR_SELECTION_H__
#define __GIMP_COLOR_SELECTION_H__
G_BEGIN_DECLS
/* For information look at the html documentation */
#define GIMP_TYPE_COLOR_SELECTION (gimp_color_selection_get_type ())
G_DECLARE_FINAL_TYPE (GimpColorSelection, gimp_color_selection, GIMP, COLOR_SELECTION, GtkBox)
GtkWidget * gimp_color_selection_new (void);
void gimp_color_selection_set_show_alpha (GimpColorSelection *selection,
gboolean show_alpha);
gboolean gimp_color_selection_get_show_alpha (GimpColorSelection *selection);
void gimp_color_selection_set_color (GimpColorSelection *selection,
GeglColor *color);
GeglColor * gimp_color_selection_get_color (GimpColorSelection *selection);
void gimp_color_selection_set_old_color (GimpColorSelection *selection,
GeglColor *color);
GeglColor * gimp_color_selection_get_old_color (GimpColorSelection *selection);
void gimp_color_selection_reset (GimpColorSelection *selection);
void gimp_color_selection_color_changed (GimpColorSelection *selection);
void gimp_color_selection_set_format (GimpColorSelection *selection,
const Babl *format);
void gimp_color_selection_set_simulation (GimpColorSelection *selection,
GimpColorProfile *profile,
GimpColorRenderingIntent intent,
gboolean bpc);
void gimp_color_selection_set_config (GimpColorSelection *selection,
GimpColorConfig *config);
GtkWidget * gimp_color_selection_get_notebook (GimpColorSelection *selection);
GtkWidget * gimp_color_selection_get_right_vbox (GimpColorSelection *selection);
G_END_DECLS
#endif /* __GIMP_COLOR_SELECTION_H__ */

View file

@ -0,0 +1,814 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorselector.c
* Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
*
* based on:
* Colour selector module
* Copyright (C) 1999 Austin Donnelly <austin@greenend.org.uk>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpcolor/gimpcolor.h"
#include "libgimpconfig/gimpconfig.h"
#include "gimpwidgetstypes.h"
#include "gimpcolorselector.h"
#include "gimpicons.h"
#include "gimpwidgetsmarshal.h"
/**
* SECTION: gimpcolorselector
* @title: GimpColorSelector
* @short_description: Pluggable GIMP color selector modules.
* @see_also: #GModule, #GTypeModule, #GimpModule
*
* Functions and definitions for creating pluggable GIMP color
* selector modules.
**/
enum
{
COLOR_CHANGED,
CHANNEL_CHANGED,
MODEL_VISIBLE_CHANGED,
SIMULATION,
LAST_SIGNAL
};
typedef struct _GimpColorSelectorPrivate
{
gboolean toggles_visible;
gboolean toggles_sensitive;
gboolean show_alpha;
gboolean model_visible[3];
GimpColorSelectorChannel channel;
GeglColor *color;
gboolean simulation;
GimpColorProfile *simulation_profile;
GimpColorRenderingIntent simulation_intent;
gboolean simulation_bpc;
} GimpColorSelectorPrivate;
static void gimp_color_selector_dispose (GObject *object);
static void gimp_color_selector_emit_color_changed (GimpColorSelector *selector);
static void gimp_color_selector_emit_channel_changed (GimpColorSelector *selector);
static void gimp_color_selector_emit_model_visible_changed (GimpColorSelector *selector,
GimpColorSelectorModel model);
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GimpColorSelector, gimp_color_selector, GTK_TYPE_BOX)
#define parent_class gimp_color_selector_parent_class
static guint selector_signals[LAST_SIGNAL] = { 0 };
static void
gimp_color_selector_class_init (GimpColorSelectorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gimp_color_selector_dispose;
selector_signals[COLOR_CHANGED] =
g_signal_new ("color-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpColorSelectorClass, color_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
GEGL_TYPE_COLOR);
selector_signals[CHANNEL_CHANGED] =
g_signal_new ("channel-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpColorSelectorClass, channel_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
GIMP_TYPE_COLOR_SELECTOR_CHANNEL);
selector_signals[MODEL_VISIBLE_CHANGED] =
g_signal_new ("model-visible-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpColorSelectorClass, model_visible_changed),
NULL, NULL,
_gimp_widgets_marshal_VOID__ENUM_BOOLEAN,
G_TYPE_NONE, 2,
GIMP_TYPE_COLOR_SELECTOR_MODEL,
G_TYPE_BOOLEAN);
selector_signals[SIMULATION] =
g_signal_new ("simulation",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpColorSelectorClass, simulation),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
G_TYPE_BOOLEAN);
klass->name = "Unnamed";
klass->help_id = NULL;
klass->icon_name = GIMP_ICON_PALETTE;
klass->set_toggles_visible = NULL;
klass->set_toggles_sensitive = NULL;
klass->set_show_alpha = NULL;
klass->set_color = NULL;
klass->set_channel = NULL;
klass->set_model_visible = NULL;
klass->color_changed = NULL;
klass->channel_changed = NULL;
klass->model_visible_changed = NULL;
klass->set_config = NULL;
}
static void
gimp_color_selector_init (GimpColorSelector *selector)
{
GimpColorSelectorPrivate *priv;
priv = gimp_color_selector_get_instance_private (selector);
priv->toggles_visible = TRUE;
priv->toggles_sensitive = TRUE;
priv->show_alpha = TRUE;
gtk_orientable_set_orientation (GTK_ORIENTABLE (selector),
GTK_ORIENTATION_VERTICAL);
priv->channel = GIMP_COLOR_SELECTOR_RED;
priv->color = gegl_color_new ("black");
priv->model_visible[GIMP_COLOR_SELECTOR_MODEL_RGB] = TRUE;
priv->model_visible[GIMP_COLOR_SELECTOR_MODEL_LCH] = TRUE;
priv->model_visible[GIMP_COLOR_SELECTOR_MODEL_HSV] = FALSE;
priv->simulation = FALSE;
priv->simulation_profile = NULL;
priv->simulation_intent = GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC;
priv->simulation_bpc = FALSE;
}
static void
gimp_color_selector_dispose (GObject *object)
{
GimpColorSelector *selector = GIMP_COLOR_SELECTOR (object);
GimpColorSelectorPrivate *priv;
priv = gimp_color_selector_get_instance_private (selector);
gimp_color_selector_set_config (selector, NULL);
g_clear_object (&priv->color);
g_clear_object (&priv->simulation_profile);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
/* public functions */
/**
* gimp_color_selector_new: (skip)
* @selector_type: The #GType of the selector to create.
* @color: The initial color to be edited.
* @channel: The selector's initial channel.
*
* Creates a new #GimpColorSelector widget of type @selector_type.
*
* Note that this is mostly internal API to be used by other widgets.
*
* Please use gimp_color_selection_new() for the "GIMP-typical" color
* selection widget. Also see gimp_color_button_new().
*
* Returns: the new #GimpColorSelector widget.
**/
GtkWidget *
gimp_color_selector_new (GType selector_type,
GeglColor *color,
GimpColorSelectorChannel channel)
{
GimpColorSelector *selector;
g_return_val_if_fail (g_type_is_a (selector_type, GIMP_TYPE_COLOR_SELECTOR), NULL);
g_return_val_if_fail (GEGL_IS_COLOR (color), NULL);
selector = g_object_new (selector_type, NULL);
gimp_color_selector_set_color (selector, color);
gimp_color_selector_set_channel (selector, channel);
return GTK_WIDGET (selector);
}
/**
* gimp_color_selector_set_toggles_visible:
* @selector: A #GimpColorSelector widget.
* @visible: The new @visible setting.
*
* Sets the @visible property of the @selector's toggles.
*
* This function has no effect if this @selector instance has no
* toggles to switch channels.
**/
void
gimp_color_selector_set_toggles_visible (GimpColorSelector *selector,
gboolean visible)
{
GimpColorSelectorPrivate *priv;
g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
priv = gimp_color_selector_get_instance_private (selector);
if (priv->toggles_visible != visible)
{
GimpColorSelectorClass *selector_class;
priv->toggles_visible = visible ? TRUE : FALSE;
selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (selector);
if (selector_class->set_toggles_visible)
selector_class->set_toggles_visible (selector, visible);
}
}
/**
* gimp_color_selector_get_toggles_visible:
* @selector: A #GimpColorSelector widget.
*
* Returns the @visible property of the @selector's toggles.
*
* Returns: %TRUE if the #GimpColorSelector's toggles are visible.
*
* Since: 2.10
**/
gboolean
gimp_color_selector_get_toggles_visible (GimpColorSelector *selector)
{
GimpColorSelectorPrivate *priv;
g_return_val_if_fail (GIMP_IS_COLOR_SELECTOR (selector), FALSE);
priv = gimp_color_selector_get_instance_private (selector);
return priv->toggles_visible;
}
/**
* gimp_color_selector_set_toggles_sensitive:
* @selector: A #GimpColorSelector widget.
* @sensitive: The new @sensitive setting.
*
* Sets the @sensitive property of the @selector's toggles.
*
* This function has no effect if this @selector instance has no
* toggles to switch channels.
**/
void
gimp_color_selector_set_toggles_sensitive (GimpColorSelector *selector,
gboolean sensitive)
{
GimpColorSelectorPrivate *priv;
g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
priv = gimp_color_selector_get_instance_private (selector);
if (priv->toggles_sensitive != sensitive)
{
GimpColorSelectorClass *selector_class;
priv->toggles_sensitive = sensitive ? TRUE : FALSE;
selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (selector);
if (selector_class->set_toggles_sensitive)
selector_class->set_toggles_sensitive (selector, sensitive);
}
}
/**
* gimp_color_selector_get_toggles_sensitive:
* @selector: A #GimpColorSelector widget.
*
* Returns the @sensitive property of the @selector's toggles.
*
* Returns: %TRUE if the #GimpColorSelector's toggles are sensitive.
*
* Since: 2.10
**/
gboolean
gimp_color_selector_get_toggles_sensitive (GimpColorSelector *selector)
{
GimpColorSelectorPrivate *priv;
g_return_val_if_fail (GIMP_IS_COLOR_SELECTOR (selector), FALSE);
priv = gimp_color_selector_get_instance_private (selector);
return priv->toggles_sensitive;
}
/**
* gimp_color_selector_set_show_alpha:
* @selector: A #GimpColorSelector widget.
* @show_alpha: The new @show_alpha setting.
*
* Sets the @show_alpha property of the @selector widget.
**/
void
gimp_color_selector_set_show_alpha (GimpColorSelector *selector,
gboolean show_alpha)
{
GimpColorSelectorPrivate *priv;
g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
priv = gimp_color_selector_get_instance_private (selector);
if (show_alpha != priv->show_alpha)
{
GimpColorSelectorClass *selector_class;
priv->show_alpha = show_alpha ? TRUE : FALSE;
selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (selector);
if (selector_class->set_show_alpha)
selector_class->set_show_alpha (selector, show_alpha);
}
}
/**
* gimp_color_selector_get_show_alpha:
* @selector: A #GimpColorSelector widget.
*
* Returns the @selector's @show_alpha property.
*
* Returns: %TRUE if the #GimpColorSelector has alpha controls.
*
* Since: 2.10
**/
gboolean
gimp_color_selector_get_show_alpha (GimpColorSelector *selector)
{
GimpColorSelectorPrivate *priv;
g_return_val_if_fail (GIMP_IS_COLOR_SELECTOR (selector), FALSE);
priv = gimp_color_selector_get_instance_private (selector);
return priv->show_alpha;
}
/**
* gimp_color_selector_set_color:
* @selector: A #GimpColorSelector widget.
* @color: The new color.
*
* Sets the color shown in the @selector widget.
**/
void
gimp_color_selector_set_color (GimpColorSelector *selector,
GeglColor *color)
{
GimpColorSelectorClass *selector_class;
GimpColorSelectorPrivate *priv;
g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
g_return_if_fail (GEGL_IS_COLOR (color));
priv = gimp_color_selector_get_instance_private (selector);
g_object_unref (priv->color);
priv->color = gegl_color_duplicate (color);
selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (selector);
if (selector_class->set_color)
selector_class->set_color (selector, priv->color);
gimp_color_selector_emit_color_changed (selector);
}
/**
* gimp_color_selector_get_color:
* @selector: A #GimpColorSelector widget.
*
* Retrieves the color shown in the @selector widget.
*
* Returns: (transfer full): a copy of the selected color.
* Since: 2.10
**/
GeglColor *
gimp_color_selector_get_color (GimpColorSelector *selector)
{
GimpColorSelectorPrivate *priv;
g_return_val_if_fail (GIMP_IS_COLOR_SELECTOR (selector), NULL);
priv = gimp_color_selector_get_instance_private (selector);
return gegl_color_duplicate (priv->color);
}
/**
* gimp_color_selector_set_channel:
* @selector: A #GimpColorSelector widget.
* @channel: The new @channel setting.
*
* Sets the @channel property of the @selector widget.
*
* Changes between displayed channels if this @selector instance has
* the ability to show different channels.
* This will also update the color model if needed.
**/
void
gimp_color_selector_set_channel (GimpColorSelector *selector,
GimpColorSelectorChannel channel)
{
GimpColorSelectorPrivate *priv;
g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
priv = gimp_color_selector_get_instance_private (selector);
if (channel != priv->channel)
{
GimpColorSelectorClass *selector_class;
GimpColorSelectorModel model = -1;
priv->channel = channel;
switch (channel)
{
case GIMP_COLOR_SELECTOR_RED:
case GIMP_COLOR_SELECTOR_GREEN:
case GIMP_COLOR_SELECTOR_BLUE:
model = GIMP_COLOR_SELECTOR_MODEL_RGB;
break;
case GIMP_COLOR_SELECTOR_HUE:
case GIMP_COLOR_SELECTOR_SATURATION:
case GIMP_COLOR_SELECTOR_VALUE:
model = GIMP_COLOR_SELECTOR_MODEL_HSV;
break;
case GIMP_COLOR_SELECTOR_LCH_LIGHTNESS:
case GIMP_COLOR_SELECTOR_LCH_CHROMA:
case GIMP_COLOR_SELECTOR_LCH_HUE:
model = GIMP_COLOR_SELECTOR_MODEL_LCH;
break;
case GIMP_COLOR_SELECTOR_ALPHA:
/* Alpha channel does not change the color model. */
break;
default:
/* Should not happen. */
g_return_if_reached ();
break;
}
selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (selector);
if (selector_class->set_channel)
selector_class->set_channel (selector, channel);
gimp_color_selector_emit_channel_changed (selector);
if (model != -1)
{
/* make visibility of LCH and HSV mutuallky exclusive */
if (model == GIMP_COLOR_SELECTOR_MODEL_HSV)
{
gimp_color_selector_set_model_visible (selector,
GIMP_COLOR_SELECTOR_MODEL_LCH,
FALSE);
}
else if (model == GIMP_COLOR_SELECTOR_MODEL_LCH)
{
gimp_color_selector_set_model_visible (selector,
GIMP_COLOR_SELECTOR_MODEL_HSV,
FALSE);
}
gimp_color_selector_set_model_visible (selector, model, TRUE);
}
}
}
/**
* gimp_color_selector_get_channel:
* @selector: A #GimpColorSelector widget.
*
* Returns the @selector's current channel.
*
* Returns: The #GimpColorSelectorChannel currently shown by the
* @selector.
*
* Since: 2.10
**/
GimpColorSelectorChannel
gimp_color_selector_get_channel (GimpColorSelector *selector)
{
GimpColorSelectorPrivate *priv;
g_return_val_if_fail (GIMP_IS_COLOR_SELECTOR (selector),
GIMP_COLOR_SELECTOR_RED);
priv = gimp_color_selector_get_instance_private (selector);
return priv->channel;
}
/**
* gimp_color_selector_set_model_visible:
* @selector: A #GimpColorSelector widget.
* @model: The affected #GimpColorSelectorModel.
* @visible: The new visible setting.
*
* Sets the @model visible/invisible on the @selector widget.
*
* Toggles visibility of displayed models if this @selector instance
* has the ability to show different color models.
*
* Since: 2.10
**/
void
gimp_color_selector_set_model_visible (GimpColorSelector *selector,
GimpColorSelectorModel model,
gboolean visible)
{
GimpColorSelectorPrivate *priv;
g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
priv = gimp_color_selector_get_instance_private (selector);
visible = visible ? TRUE : FALSE;
if (visible != priv->model_visible[model])
{
GimpColorSelectorClass *selector_class;
priv->model_visible[model] = visible;
selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (selector);
if (selector_class->set_model_visible)
selector_class->set_model_visible (selector, model, visible);
gimp_color_selector_emit_model_visible_changed (selector, model);
}
}
/**
* gimp_color_selector_get_model_visible:
* @selector: A #GimpColorSelector widget.
* @model: The #GimpColorSelectorModel.
*
* Returns: whether @model is visible in @selector.
*
* Since: 2.10
**/
gboolean
gimp_color_selector_get_model_visible (GimpColorSelector *selector,
GimpColorSelectorModel model)
{
GimpColorSelectorPrivate *priv;
g_return_val_if_fail (GIMP_IS_COLOR_SELECTOR (selector), FALSE);
priv = gimp_color_selector_get_instance_private (selector);
return priv->model_visible[model];
}
/**
* gimp_color_selector_set_config:
* @selector: a #GimpColorSelector widget.
* @config: a #GimpColorConfig object.
*
* Sets the color management configuration to use with this color selector.
*
* Since: 2.4
*/
void
gimp_color_selector_set_config (GimpColorSelector *selector,
GimpColorConfig *config)
{
GimpColorSelectorClass *selector_class;
g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
g_return_if_fail (config == NULL || GIMP_IS_COLOR_CONFIG (config));
selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (selector);
if (selector_class->set_config)
selector_class->set_config (selector, config);
}
/**
* gimp_color_selector_set_format
* @selector: a #GimpColorSelector widget.
* @format: a Babl format, with space.
*
* Sets the babl format representing the color model and the space this
* @selector is supposed to display values for. Depending on the type of color
* selector, it may trigger various UX changes, or none at all.
*
* Since: 3.0
*/
void
gimp_color_selector_set_format (GimpColorSelector *selector,
const Babl *format)
{
GimpColorSelectorClass *selector_class;
g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (selector);
if (selector_class->set_format)
selector_class->set_format (selector, format);
}
/**
* gimp_color_selector_set_simulation
* @selector: a #GimpColorSelector widget.
* @profile: a #GimpColorProfile object.
* @intent: a #GimpColorRenderingIntent enum.
* @bpc: a gboolean.
*
* Sets the simulation options to use with this color selector.
*
* Since: 3.0
*/
void
gimp_color_selector_set_simulation (GimpColorSelector *selector,
GimpColorProfile *profile,
GimpColorRenderingIntent intent,
gboolean bpc)
{
GimpColorSelectorClass *selector_class;
GimpColorSelectorPrivate *priv;
g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
g_return_if_fail (profile == NULL || GIMP_IS_COLOR_PROFILE (profile));
selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (selector);
priv = gimp_color_selector_get_instance_private (selector);
if ((profile && ! priv->simulation_profile) ||
(! profile && priv->simulation_profile) ||
(profile && ! gimp_color_profile_is_equal (profile, priv->simulation_profile)) ||
intent != priv->simulation_intent ||
bpc != priv->simulation_bpc)
{
g_set_object (&priv->simulation_profile, profile);
priv->simulation_intent = intent;
priv->simulation_bpc = bpc;
if (selector_class->set_simulation)
selector_class->set_simulation (selector, profile, intent, bpc);
}
}
gboolean
gimp_color_selector_get_simulation (GimpColorSelector *selector,
GimpColorProfile **profile,
GimpColorRenderingIntent *intent,
gboolean *bpc)
{
GimpColorSelectorPrivate *priv;
g_return_val_if_fail (GIMP_IS_COLOR_SELECTOR (selector), FALSE);
priv = gimp_color_selector_get_instance_private (selector);
if (profile)
*profile = priv->simulation_profile;
if (intent)
*intent = priv->simulation_intent;
if (bpc)
*bpc = priv->simulation_bpc;
return priv->simulation;
}
gboolean
gimp_color_selector_enable_simulation (GimpColorSelector *selector,
gboolean enabled)
{
GimpColorSelectorPrivate *priv;
g_return_val_if_fail (GIMP_IS_COLOR_SELECTOR (selector), FALSE);
priv = gimp_color_selector_get_instance_private (selector);
if (priv->simulation != enabled)
{
if (! enabled || priv->simulation_profile)
{
priv->simulation = enabled;
g_signal_emit (selector, selector_signals[SIMULATION], 0, enabled);
}
}
return priv->simulation;
}
/* Private functions. */
/**
* gimp_color_selector_emit_color_changed:
* @selector: A #GimpColorSelector widget.
*
* Emits the "color-changed" signal.
*/
static void
gimp_color_selector_emit_color_changed (GimpColorSelector *selector)
{
GimpColorSelectorPrivate *priv;
g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
priv = gimp_color_selector_get_instance_private (selector);
g_signal_emit (selector, selector_signals[COLOR_CHANGED], 0, priv->color);
}
/**
* gimp_color_selector_emit_channel_changed:
* @selector: A #GimpColorSelector widget.
*
* Emits the "channel-changed" signal.
*/
static void
gimp_color_selector_emit_channel_changed (GimpColorSelector *selector)
{
GimpColorSelectorPrivate *priv;
g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
priv = gimp_color_selector_get_instance_private (selector);
g_signal_emit (selector, selector_signals[CHANNEL_CHANGED], 0,
priv->channel);
}
/**
* gimp_color_selector_emit_model_visible_changed:
* @selector: A #GimpColorSelector widget.
* @model: The #GimpColorSelectorModel where visibility changed.
*
* Emits the "model-visible-changed" signal.
*
* Since: 2.10
*/
static void
gimp_color_selector_emit_model_visible_changed (GimpColorSelector *selector,
GimpColorSelectorModel model)
{
GimpColorSelectorPrivate *priv;
g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
priv = gimp_color_selector_get_instance_private (selector);
g_signal_emit (selector, selector_signals[MODEL_VISIBLE_CHANGED], 0,
model, priv->model_visible[model]);
}

View file

@ -0,0 +1,165 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorselector.h
* Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
*
* based on:
* Colour selector module
* Copyright (C) 1999 Austin Donnelly <austin@greenend.org.uk>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_COLOR_SELECTOR_H__
#define __GIMP_COLOR_SELECTOR_H__
G_BEGIN_DECLS
/* For information look at the html documentation */
/**
* GIMP_COLOR_SELECTOR_SIZE:
*
* The suggested size for a color area in a #GimpColorSelector
* implementation.
**/
#define GIMP_COLOR_SELECTOR_SIZE 150
/**
* GIMP_COLOR_SELECTOR_BAR_SIZE:
*
* The suggested width for a color bar in a #GimpColorSelector
* implementation.
**/
#define GIMP_COLOR_SELECTOR_BAR_SIZE 15
#define GIMP_TYPE_COLOR_SELECTOR (gimp_color_selector_get_type ())
G_DECLARE_DERIVABLE_TYPE (GimpColorSelector, gimp_color_selector, GIMP, COLOR_SELECTOR, GtkBox)
struct _GimpColorSelectorClass
{
GtkBoxClass parent_class;
const gchar *name;
const gchar *help_id;
const gchar *icon_name;
/* virtual functions */
void (* set_toggles_visible) (GimpColorSelector *selector,
gboolean visible);
void (* set_toggles_sensitive) (GimpColorSelector *selector,
gboolean sensitive);
void (* set_show_alpha) (GimpColorSelector *selector,
gboolean show_alpha);
void (* set_color) (GimpColorSelector *selector,
GeglColor *color);
void (* set_channel) (GimpColorSelector *selector,
GimpColorSelectorChannel channel);
void (* set_model_visible) (GimpColorSelector *selector,
GimpColorSelectorModel model,
gboolean visible);
void (* set_config) (GimpColorSelector *selector,
GimpColorConfig *config);
void (* set_format) (GimpColorSelector *selector,
const Babl *format);
void (* set_simulation) (GimpColorSelector *selector,
GimpColorProfile *profile,
GimpColorRenderingIntent intent,
gboolean bpc);
/* signals */
void (* color_changed) (GimpColorSelector *selector,
GeglColor *color);
void (* channel_changed) (GimpColorSelector *selector,
GimpColorSelectorChannel channel);
void (* model_visible_changed) (GimpColorSelector *selector,
GimpColorSelectorModel model,
gboolean visible);
void (* simulation) (GimpColorSelector *selector,
gboolean enabled);
/* Padding for future expansion */
void (* _gimp_reserved0) (void);
void (* _gimp_reserved1) (void);
void (* _gimp_reserved2) (void);
void (* _gimp_reserved3) (void);
void (* _gimp_reserved4) (void);
void (* _gimp_reserved5) (void);
void (* _gimp_reserved6) (void);
void (* _gimp_reserved7) (void);
void (* _gimp_reserved8) (void);
void (* _gimp_reserved9) (void);
};
GtkWidget * gimp_color_selector_new (GType selector_type,
GeglColor *color,
GimpColorSelectorChannel channel);
void gimp_color_selector_set_toggles_visible (GimpColorSelector *selector,
gboolean visible);
gboolean gimp_color_selector_get_toggles_visible (GimpColorSelector *selector);
void gimp_color_selector_set_toggles_sensitive (GimpColorSelector *selector,
gboolean sensitive);
gboolean gimp_color_selector_get_toggles_sensitive (GimpColorSelector *selector);
void gimp_color_selector_set_show_alpha (GimpColorSelector *selector,
gboolean show_alpha);
gboolean gimp_color_selector_get_show_alpha (GimpColorSelector *selector);
void gimp_color_selector_set_color (GimpColorSelector *selector,
GeglColor *color);
GeglColor * gimp_color_selector_get_color (GimpColorSelector *selector);
void gimp_color_selector_set_channel (GimpColorSelector *selector,
GimpColorSelectorChannel channel);
GimpColorSelectorChannel
gimp_color_selector_get_channel (GimpColorSelector *selector);
void gimp_color_selector_set_model_visible (GimpColorSelector *selector,
GimpColorSelectorModel model,
gboolean visible);
gboolean gimp_color_selector_get_model_visible (GimpColorSelector *selector,
GimpColorSelectorModel model);
void gimp_color_selector_set_config (GimpColorSelector *selector,
GimpColorConfig *config);
void gimp_color_selector_set_format (GimpColorSelector *selector,
const Babl *format);
void gimp_color_selector_set_simulation (GimpColorSelector *selector,
GimpColorProfile *profile,
GimpColorRenderingIntent intent,
gboolean bpc);
gboolean gimp_color_selector_get_simulation (GimpColorSelector *selector,
GimpColorProfile **profile,
GimpColorRenderingIntent *intent,
gboolean *bpc);
gboolean gimp_color_selector_enable_simulation (GimpColorSelector *selector,
gboolean enabled);
G_END_DECLS
#endif /* __GIMP_COLOR_SELECTOR_H__ */

View file

@ -0,0 +1,278 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcontroller.c
* Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpcolor/gimpcolor.h"
#include "libgimpconfig/gimpconfig.h"
#include "gimpwidgetstypes.h"
#include "gimpwidgetsmarshal.h"
#include "libgimp/libgimp-intl.h"
#define GIMP_ENABLE_CONTROLLER_UNDER_CONSTRUCTION
#include "gimpcontroller.h"
#include "gimpicons.h"
/**
* SECTION: gimpcontroller
* @title: GimpController
* @short_description: Pluggable GIMP input controller modules.
*
* An abstract interface for implementing arbitrary input controllers.
**/
enum
{
PROP_0,
PROP_NAME,
PROP_STATE
};
enum
{
EVENT,
LAST_SIGNAL
};
typedef struct _GimpControllerPrivate
{
gchar *name;
gchar *state;
} GimpControllerPrivate;
#define GET_PRIVATE(obj) ((GimpControllerPrivate *) gimp_controller_get_instance_private ((GimpController *) (obj)))
static void gimp_controller_finalize (GObject *object);
static void gimp_controller_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_controller_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GimpController, gimp_controller, G_TYPE_OBJECT,
G_ADD_PRIVATE (GimpController)
G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL))
#define parent_class gimp_controller_parent_class
static guint controller_signals[LAST_SIGNAL] = { 0 };
static void
gimp_controller_class_init (GimpControllerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gimp_controller_finalize;
object_class->set_property = gimp_controller_set_property;
object_class->get_property = gimp_controller_get_property;
klass->name = "Unnamed";
klass->help_domain = NULL;
klass->help_id = NULL;
klass->icon_name = GIMP_ICON_CONTROLLER;
klass->get_n_events = NULL;
klass->get_event_name = NULL;
klass->event = NULL;
g_object_class_install_property (object_class, PROP_NAME,
g_param_spec_string ("name",
"Name",
_("The controller's name"),
"Unnamed Controller",
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_STATE,
g_param_spec_string ("state",
"State",
_("The controller's state, as human-readable string"),
"Unknown",
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
controller_signals[EVENT] =
g_signal_new ("event",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GimpControllerClass, event),
g_signal_accumulator_true_handled, NULL,
_gimp_widgets_marshal_BOOLEAN__POINTER,
G_TYPE_BOOLEAN, 1,
G_TYPE_POINTER);
}
static void
gimp_controller_init (GimpController *controller)
{
}
static void
gimp_controller_finalize (GObject *object)
{
GimpController *controller = GIMP_CONTROLLER (object);
GimpControllerPrivate *priv = GET_PRIVATE (controller);
g_clear_pointer (&priv->name, g_free);
g_clear_pointer (&priv->state, g_free);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_controller_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpController *controller = GIMP_CONTROLLER (object);
GimpControllerPrivate *priv = GET_PRIVATE (controller);
switch (property_id)
{
case PROP_NAME:
if (priv->name)
g_free (priv->name);
priv->name = g_value_dup_string (value);
break;
case PROP_STATE:
if (priv->state)
g_free (priv->state);
priv->state = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_controller_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpController *controller = GIMP_CONTROLLER (object);
GimpControllerPrivate *priv = GET_PRIVATE (controller);
switch (property_id)
{
case PROP_NAME:
g_value_set_string (value, priv->name);
break;
case PROP_STATE:
g_value_set_string (value, priv->state);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
GimpController *
gimp_controller_new (GType controller_type)
{
GimpController *controller;
g_return_val_if_fail (g_type_is_a (controller_type, GIMP_TYPE_CONTROLLER),
NULL);
controller = g_object_new (controller_type, NULL);
return controller;
}
gint
gimp_controller_get_n_events (GimpController *controller)
{
g_return_val_if_fail (GIMP_IS_CONTROLLER (controller), 0);
if (GIMP_CONTROLLER_GET_CLASS (controller)->get_n_events)
return GIMP_CONTROLLER_GET_CLASS (controller)->get_n_events (controller);
return 0;
}
const gchar *
gimp_controller_get_event_name (GimpController *controller,
gint event_id)
{
const gchar *name = NULL;
g_return_val_if_fail (GIMP_IS_CONTROLLER (controller), NULL);
if (GIMP_CONTROLLER_GET_CLASS (controller)->get_event_name)
name = GIMP_CONTROLLER_GET_CLASS (controller)->get_event_name (controller,
event_id);
if (! name)
name = "<invalid event id>";
return name;
}
const gchar *
gimp_controller_get_event_blurb (GimpController *controller,
gint event_id)
{
const gchar *blurb = NULL;
g_return_val_if_fail (GIMP_IS_CONTROLLER (controller), NULL);
if (GIMP_CONTROLLER_GET_CLASS (controller)->get_event_blurb)
blurb = GIMP_CONTROLLER_GET_CLASS (controller)->get_event_blurb (controller,
event_id);
if (! blurb)
blurb = "<invalid event id>";
return blurb;
}
gboolean
gimp_controller_event (GimpController *controller,
const GimpControllerEvent *event)
{
gboolean retval = FALSE;
g_return_val_if_fail (GIMP_IS_CONTROLLER (controller), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
g_signal_emit (controller, controller_signals[EVENT], 0,
event, &retval);
return retval;
}

View file

@ -0,0 +1,173 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcontroller.h
* Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#ifndef GIMP_ENABLE_CONTROLLER_UNDER_CONSTRUCTION
#error GimpController is unstable API under construction
#endif
#ifndef __GIMP_CONTROLLER_H__
#define __GIMP_CONTROLLER_H__
G_BEGIN_DECLS
/* For information look at the html documentation */
/**
* GimpControllerEventType:
* @GIMP_CONTROLLER_EVENT_TRIGGER: the event is a simple trigger
* @GIMP_CONTROLLER_EVENT_VALUE: the event carries a double value
*
* Event types for #GimpController.
**/
typedef enum
{
GIMP_CONTROLLER_EVENT_TRIGGER,
GIMP_CONTROLLER_EVENT_VALUE
} GimpControllerEventType;
typedef struct _GimpControllerEventAny GimpControllerEventAny;
typedef struct _GimpControllerEventTrigger GimpControllerEventTrigger;
typedef struct _GimpControllerEventValue GimpControllerEventValue;
typedef union _GimpControllerEvent GimpControllerEvent;
/**
* GimpControllerEventAny:
* @type: The event's #GimpControllerEventType
* @source: The event's source #GimpController
* @event_id: The event's ID
*
* Generic controller event. Every event has these three members at the
* beginning of its struct
**/
struct _GimpControllerEventAny
{
GimpControllerEventType type;
GimpController *source;
gint event_id;
};
/**
* GimpControllerEventTrigger:
* @type: The event's #GimpControllerEventType
* @source: The event's source #GimpController
* @event_id: The event's ID
*
* Trigger controller event.
**/
struct _GimpControllerEventTrigger
{
GimpControllerEventType type;
GimpController *source;
gint event_id;
};
/**
* GimpControllerEventValue:
* @type: The event's #GimpControllerEventType
* @source: The event's source #GimpController
* @event_id: The event's ID
* @value: The event's value
*
* Value controller event.
**/
struct _GimpControllerEventValue
{
GimpControllerEventType type;
GimpController *source;
gint event_id;
GValue value;
};
/**
* GimpControllerEvent:
* @type: The event type
* @any: GimpControllerEventAny
* @trigger: GimpControllerEventTrigger
* @value: GimpControllerEventValue
*
* A union to hjold all event event types
**/
union _GimpControllerEvent
{
GimpControllerEventType type;
GimpControllerEventAny any;
GimpControllerEventTrigger trigger;
GimpControllerEventValue value;
};
#define GIMP_TYPE_CONTROLLER (gimp_controller_get_type ())
G_DECLARE_DERIVABLE_TYPE (GimpController, gimp_controller, GIMP, CONTROLLER, GObject)
struct _GimpControllerClass
{
GObjectClass parent_class;
const gchar *name;
const gchar *help_domain;
const gchar *help_id;
const gchar *icon_name;
/* virtual functions */
gint (* get_n_events) (GimpController *controller);
const gchar * (* get_event_name) (GimpController *controller,
gint event_id);
const gchar * (* get_event_blurb) (GimpController *controller,
gint event_id);
/* signals */
gboolean (* event) (GimpController *controller,
const GimpControllerEvent *event);
/* Padding for future expansion */
void (* _gimp_reserved0) (void);
void (* _gimp_reserved1) (void);
void (* _gimp_reserved2) (void);
void (* _gimp_reserved3) (void);
void (* _gimp_reserved4) (void);
void (* _gimp_reserved5) (void);
void (* _gimp_reserved6) (void);
void (* _gimp_reserved7) (void);
void (* _gimp_reserved8) (void);
void (* _gimp_reserved9) (void);
};
GimpController * gimp_controller_new (GType controller_type);
gint gimp_controller_get_n_events (GimpController *controller);
const gchar * gimp_controller_get_event_name (GimpController *controller,
gint event_id);
const gchar * gimp_controller_get_event_blurb (GimpController *controller,
gint event_id);
/* protected */
gboolean gimp_controller_event (GimpController *controller,
const GimpControllerEvent *event);
G_END_DECLS
#endif /* __GIMP_CONTROLLER_H__ */

812
libgimpwidgets/gimpdialog.c Normal file
View file

@ -0,0 +1,812 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimpdialog.c
* Copyright (C) 2000-2003 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "gimpwidgetstypes.h"
#include "gimpdialog.h"
#include "gimphelpui.h"
#include "gimpwidgetsutils.h"
#include "libgimp/libgimp-intl.h"
#ifdef G_OS_WIN32
#include <dwmapi.h>
#include <gdk/gdkwin32.h>
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
#endif
#endif
/**
* SECTION: gimpdialog
* @title: GimpDialog
* @short_description: Constructors for #GtkDialog's and action_areas as
* well as other dialog-related stuff.
*
* Constructors for #GtkDialog's and action_areas as well as other
* dialog-related stuff.
**/
enum
{
PROP_0,
PROP_HELP_FUNC,
PROP_HELP_ID,
PROP_PARENT
};
typedef struct _GimpDialogPrivate
{
GimpHelpFunc help_func;
gchar *help_id;
GtkWidget *help_button;
GBytes *window_handle;
} GimpDialogPrivate;
#define GET_PRIVATE(obj) ((GimpDialogPrivate *) (gimp_dialog_get_instance_private (GIMP_DIALOG (obj))))
static void gimp_dialog_constructed (GObject *object);
static void gimp_dialog_dispose (GObject *object);
static void gimp_dialog_finalize (GObject *object);
static void gimp_dialog_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_dialog_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_dialog_hide (GtkWidget *widget);
static gboolean gimp_dialog_delete_event (GtkWidget *widget,
GdkEventAny *event);
static void gimp_dialog_close (GtkDialog *dialog);
static void gimp_dialog_response (GtkDialog *dialog,
gint response_id);
#ifdef G_OS_WIN32
static void gimp_dialog_set_title_bar_theme (GtkWidget *dialog);
#endif
G_DEFINE_TYPE_WITH_PRIVATE (GimpDialog, gimp_dialog, GTK_TYPE_DIALOG)
#define parent_class gimp_dialog_parent_class
static gboolean show_help_button = TRUE;
static void
gimp_dialog_class_init (GimpDialogClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass);
object_class->constructed = gimp_dialog_constructed;
object_class->dispose = gimp_dialog_dispose;
object_class->finalize = gimp_dialog_finalize;
object_class->set_property = gimp_dialog_set_property;
object_class->get_property = gimp_dialog_get_property;
widget_class->hide = gimp_dialog_hide;
widget_class->delete_event = gimp_dialog_delete_event;
dialog_class->close = gimp_dialog_close;
/**
* GimpDialog:help-func:
*
* Since: 2.2
**/
g_object_class_install_property (object_class, PROP_HELP_FUNC,
g_param_spec_pointer ("help-func",
"Help Func",
"The help function to call when F1 is hit",
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
/**
* GimpDialog:help-id:
*
* Since: 2.2
**/
g_object_class_install_property (object_class, PROP_HELP_ID,
g_param_spec_string ("help-id",
"Help ID",
"The help ID to pass to help-func",
NULL,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
/**
* GimpDialog:parent:
*
* Since: 2.8
**/
g_object_class_install_property (object_class, PROP_PARENT,
g_param_spec_object ("parent",
"Parent",
"The dialog's parent widget",
GTK_TYPE_WIDGET,
GIMP_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY));
}
static void
gimp_dialog_init (GimpDialog *dialog)
{
g_signal_connect (dialog, "response",
G_CALLBACK (gimp_dialog_response),
NULL);
#ifdef G_OS_WIN32
g_signal_connect (GTK_WIDGET (dialog), "realize",
G_CALLBACK (gimp_dialog_set_title_bar_theme),
NULL);
#endif
}
static void
gimp_dialog_constructed (GObject *object)
{
GimpDialogPrivate *private = GET_PRIVATE (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
if (private->help_func)
gimp_help_connect (GTK_WIDGET (object), NULL,
private->help_func, private->help_id,
object, NULL);
if (show_help_button && private->help_func && private->help_id)
{
private->help_button = gtk_dialog_add_button (GTK_DIALOG (object),
_("_Help"),
GTK_RESPONSE_HELP);
}
gimp_widget_set_native_handle (GTK_WIDGET (object), &private->window_handle);
}
static void
gimp_dialog_dispose (GObject *object)
{
GdkDisplay *display = NULL;
GimpDialogPrivate *private = GET_PRIVATE (object);
if (g_main_depth () == 0)
{
display = gtk_widget_get_display (GTK_WIDGET (object));
g_object_ref (display);
}
gimp_widget_free_native_handle (GTK_WIDGET (object), &private->window_handle);
G_OBJECT_CLASS (parent_class)->dispose (object);
if (display)
{
gdk_display_flush (display);
g_object_unref (display);
}
}
static void
gimp_dialog_finalize (GObject *object)
{
GimpDialogPrivate *private = GET_PRIVATE (object);
g_clear_pointer (&private->help_id, g_free);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_dialog_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpDialogPrivate *private = GET_PRIVATE (object);
switch (property_id)
{
case PROP_HELP_FUNC:
private->help_func = g_value_get_pointer (value);
break;
case PROP_HELP_ID:
g_free (private->help_id);
private->help_id = g_value_dup_string (value);
gimp_help_set_help_data (GTK_WIDGET (object), NULL, private->help_id);
break;
case PROP_PARENT:
{
GtkWidget *parent = g_value_get_object (value);
if (parent)
{
if (GTK_IS_WINDOW (parent))
{
gtk_window_set_transient_for (GTK_WINDOW (object),
GTK_WINDOW (parent));
}
else
{
GtkWidget *toplevel;
toplevel = gtk_widget_get_toplevel (parent);
if (GTK_IS_WINDOW (toplevel))
{
gtk_window_set_transient_for (GTK_WINDOW (object),
GTK_WINDOW (toplevel));
}
else
{
gtk_window_set_screen (GTK_WINDOW (object),
gtk_widget_get_screen (parent));
gtk_window_set_position (GTK_WINDOW (object),
GTK_WIN_POS_MOUSE);
}
}
}
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_dialog_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpDialogPrivate *private = GET_PRIVATE (object);
switch (property_id)
{
case PROP_HELP_FUNC:
g_value_set_pointer (value, private->help_func);
break;
case PROP_HELP_ID:
g_value_set_string (value, private->help_id);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_dialog_hide (GtkWidget *widget)
{
/* set focus to NULL so focus_out callbacks are invoked synchronously */
gtk_window_set_focus (GTK_WINDOW (widget), NULL);
GTK_WIDGET_CLASS (parent_class)->hide (widget);
}
static gboolean
gimp_dialog_delete_event (GtkWidget *widget,
GdkEventAny *event)
{
return TRUE;
}
static void
gimp_dialog_close (GtkDialog *dialog)
{
/* Synthesize delete_event to close dialog. */
GtkWidget *widget = GTK_WIDGET (dialog);
if (gtk_widget_get_window (widget))
{
GdkEvent *event = gdk_event_new (GDK_DELETE);
event->any.window = g_object_ref (gtk_widget_get_window (widget));
event->any.send_event = TRUE;
gtk_main_do_event (event);
gdk_event_free (event);
}
}
static void
gimp_dialog_response (GtkDialog *dialog,
gint response_id)
{
GimpDialogPrivate *private = GET_PRIVATE (dialog);
GtkWidget *widget = gtk_dialog_get_widget_for_response (dialog,
response_id);
if (widget &&
(! GTK_IS_BUTTON (widget) ||
gtk_widget_get_focus_on_click (widget)))
{
gtk_widget_grab_focus (widget);
}
/* if our own help button was activated, abort "response" and
* call our help callback.
*/
if (response_id == GTK_RESPONSE_HELP &&
widget == private->help_button)
{
g_signal_stop_emission_by_name (dialog, "response");
if (private->help_func)
private->help_func (private->help_id, dialog);
}
}
/**
* gimp_dialog_new: (skip)
* @title: The dialog's title which will be set with
* gtk_window_set_title().
* @role: The dialog's @role which will be set with
* gtk_window_set_role().
* @parent: (nullable): The @parent widget of this dialog.
* @flags: The @flags (see the #GtkDialog documentation).
* @help_func: The function which will be called if the user presses "F1".
* @help_id: The help_id which will be passed to @help_func.
* @...: A %NULL-terminated @va_list destribing the
* action_area buttons.
*
* Creates a new @GimpDialog widget.
*
* This function simply packs the action_area arguments passed in "..."
* into a @va_list variable and passes everything to gimp_dialog_new_valist().
*
* For a description of the format of the @va_list describing the
* action_area buttons see gtk_dialog_new_with_buttons().
*
* Returns: A #GimpDialog.
**/
GtkWidget *
gimp_dialog_new (const gchar *title,
const gchar *role,
GtkWidget *parent,
GtkDialogFlags flags,
GimpHelpFunc help_func,
const gchar *help_id,
...)
{
GtkWidget *dialog;
va_list args;
g_return_val_if_fail (parent == NULL || GTK_IS_WIDGET (parent), NULL);
g_return_val_if_fail (title != NULL, NULL);
g_return_val_if_fail (role != NULL, NULL);
va_start (args, help_id);
dialog = gimp_dialog_new_valist (title, role,
parent, flags,
help_func, help_id,
args);
va_end (args);
return dialog;
}
/**
* gimp_dialog_new_valist: (skip)
* @title: The dialog's title which will be set with
* gtk_window_set_title().
* @role: The dialog's @role which will be set with
* gtk_window_set_role().
* @parent: The @parent widget of this dialog or %NULL.
* @flags: The @flags (see the #GtkDialog documentation).
* @help_func: The function which will be called if the user presses "F1".
* @help_id: The help_id which will be passed to @help_func.
* @args: A @va_list destribing the action_area buttons.
*
* Creates a new @GimpDialog widget. If a GtkWindow is specified as
* @parent then the dialog will be made transient for this window.
*
* For a description of the format of the @va_list describing the
* action_area buttons see gtk_dialog_new_with_buttons().
*
* Returns: A #GimpDialog.
**/
GtkWidget *
gimp_dialog_new_valist (const gchar *title,
const gchar *role,
GtkWidget *parent,
GtkDialogFlags flags,
GimpHelpFunc help_func,
const gchar *help_id,
va_list args)
{
GtkWidget *dialog;
gboolean use_header_bar;
g_return_val_if_fail (title != NULL, NULL);
g_return_val_if_fail (role != NULL, NULL);
g_return_val_if_fail (parent == NULL || GTK_IS_WIDGET (parent), NULL);
g_object_get (gtk_settings_get_default (),
"gtk-dialogs-use-header", &use_header_bar,
NULL);
dialog = g_object_new (GIMP_TYPE_DIALOG,
"title", title,
"role", role,
"modal", (flags & GTK_DIALOG_MODAL),
"help-func", help_func,
"help-id", help_id,
"parent", parent,
"use-header-bar", use_header_bar,
NULL);
if (parent)
{
if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
g_signal_connect_object (parent, "destroy",
G_CALLBACK (gimp_dialog_close),
dialog, G_CONNECT_SWAPPED);
}
gimp_dialog_add_buttons_valist (GIMP_DIALOG (dialog), args);
return dialog;
}
/**
* gimp_dialog_add_button:
* @dialog: The @dialog to add a button to.
* @button_text: text of button, or stock ID.
* @response_id: response ID for the button.
*
* This function is essentially the same as gtk_dialog_add_button()
* except it ensures there is only one help button and automatically
* sets the RESPONSE_OK widget as the default response.
*
* Returns: (type Gtk.Widget) (transfer none): the button widget that was added.
**/
GtkWidget *
gimp_dialog_add_button (GimpDialog *dialog,
const gchar *button_text,
gint response_id)
{
GtkWidget *button;
gboolean use_header_bar;
/* hide the automatically added help button if another one is added */
if (response_id == GTK_RESPONSE_HELP)
{
GimpDialogPrivate *private = GET_PRIVATE (dialog);
if (private->help_button)
{
gtk_widget_destroy (private->help_button);
private->help_button = NULL;
}
}
button = gtk_dialog_add_button (GTK_DIALOG (dialog), button_text,
response_id);
g_object_get (dialog,
"use-header-bar", &use_header_bar,
NULL);
if (use_header_bar &&
(response_id == GTK_RESPONSE_OK ||
response_id == GTK_RESPONSE_CANCEL ||
response_id == GTK_RESPONSE_CLOSE))
{
GtkWidget *header = gtk_dialog_get_header_bar (GTK_DIALOG (dialog));
if (response_id == GTK_RESPONSE_OK)
gtk_dialog_set_default_response (GTK_DIALOG (dialog),
GTK_RESPONSE_OK);
gtk_container_child_set (GTK_CONTAINER (header), button,
"position", 0,
NULL);
}
return button;
}
/**
* gimp_dialog_add_buttons: (skip)
* @dialog: The @dialog to add buttons to.
* @...: button_text-response_id pairs.
*
* This function is essentially the same as gtk_dialog_add_buttons()
* except it calls gimp_dialog_add_button() instead of gtk_dialog_add_button()
**/
void
gimp_dialog_add_buttons (GimpDialog *dialog,
...)
{
va_list args;
va_start (args, dialog);
gimp_dialog_add_buttons_valist (dialog, args);
va_end (args);
}
/**
* gimp_dialog_add_buttons_valist: (skip)
* @dialog: The @dialog to add buttons to.
* @args: The buttons as va_list.
*
* This function is essentially the same as gimp_dialog_add_buttons()
* except it takes a va_list instead of '...'
**/
void
gimp_dialog_add_buttons_valist (GimpDialog *dialog,
va_list args)
{
const gchar *button_text;
gint response_id;
g_return_if_fail (GIMP_IS_DIALOG (dialog));
while ((button_text = va_arg (args, const gchar *)))
{
response_id = va_arg (args, gint);
gimp_dialog_add_button (dialog, button_text, response_id);
}
}
typedef struct
{
GtkDialog *dialog;
gint response_id;
GMainLoop *loop;
gboolean destroyed;
} RunInfo;
static void
run_shutdown_loop (RunInfo *ri)
{
if (g_main_loop_is_running (ri->loop))
g_main_loop_quit (ri->loop);
}
static void
run_unmap_handler (GtkDialog *dialog,
RunInfo *ri)
{
run_shutdown_loop (ri);
}
static void
run_response_handler (GtkDialog *dialog,
gint response_id,
RunInfo *ri)
{
ri->response_id = response_id;
run_shutdown_loop (ri);
}
static gint
run_delete_handler (GtkDialog *dialog,
GdkEventAny *event,
RunInfo *ri)
{
run_shutdown_loop (ri);
return TRUE; /* Do not destroy */
}
static void
run_destroy_handler (GtkDialog *dialog,
RunInfo *ri)
{
/* shutdown_loop will be called by run_unmap_handler */
ri->destroyed = TRUE;
}
/**
* gimp_dialog_run:
* @dialog: a #GimpDialog
*
* This function does exactly the same as gtk_dialog_run() except it
* does not make the dialog modal while the #GMainLoop is running.
*
* Returns: response ID
**/
gint
gimp_dialog_run (GimpDialog *dialog)
{
RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL };
gulong response_handler;
gulong unmap_handler;
gulong destroy_handler;
gulong delete_handler;
g_return_val_if_fail (GIMP_IS_DIALOG (dialog), -1);
g_object_ref (dialog);
gtk_window_present (GTK_WINDOW (dialog));
#ifdef G_OS_WIN32
gimp_dialog_set_title_bar_theme (GTK_WIDGET (dialog));
#endif
response_handler = g_signal_connect (dialog, "response",
G_CALLBACK (run_response_handler),
&ri);
unmap_handler = g_signal_connect (dialog, "unmap",
G_CALLBACK (run_unmap_handler),
&ri);
delete_handler = g_signal_connect (dialog, "delete-event",
G_CALLBACK (run_delete_handler),
&ri);
destroy_handler = g_signal_connect (dialog, "destroy",
G_CALLBACK (run_destroy_handler),
&ri);
ri.loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (ri.loop);
g_main_loop_unref (ri.loop);
ri.loop = NULL;
ri.destroyed = FALSE;
if (!ri.destroyed)
{
g_signal_handler_disconnect (dialog, response_handler);
g_signal_handler_disconnect (dialog, unmap_handler);
g_signal_handler_disconnect (dialog, delete_handler);
g_signal_handler_disconnect (dialog, destroy_handler);
}
g_object_unref (dialog);
return ri.response_id;
}
/**
* gimp_dialog_set_alternative_button_order_from_array:
* @dialog: The #GimpDialog
* @n_buttons: The size of @order
* @order: (array length=n_buttons): array of buttons' response ids.
*
* Reorder @dialog's buttons if [property@Gtk.Settings:gtk-alternative-button-order]
* is set to TRUE. This is mostly a wrapper around the GTK function
* [method@Gtk.Dialog.set_alternative_button_order], except it won't
* output a deprecation warning.
*
* Since: 3.0
**/
void
gimp_dialog_set_alternative_button_order_from_array (GimpDialog *dialog,
gint n_buttons,
gint *order)
{
/* since we don't know yet what to do about alternative button order,
* just hide the warnings for now...
*/
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
gtk_dialog_set_alternative_button_order_from_array (GTK_DIALOG (dialog), n_buttons, order);
G_GNUC_END_IGNORE_DEPRECATIONS;
}
/**
* gimp_dialog_get_native_handle:
* @dialog: The #GimpDialog
*
* Returns an opaque data handle representing the window in the currently
* running platform. You should not try to use this directly. Usually this is to
* be used in functions such as [func@Gimp.brushes_popup] which will allow the
* core process to set this [class@Dialog] as parent to the newly created popup.
*
* Returns: (transfer none): an opaque [struct@GLib.Bytes] identifying this
* window.
*
* Since: 3.0
**/
GBytes *
gimp_dialog_get_native_handle (GimpDialog *dialog)
{
return GET_PRIVATE (dialog)->window_handle;
}
/**
* gimp_dialogs_show_help_button: (skip)
* @show: whether a help button should be added when creating a GimpDialog
*
* This function is for internal use only.
*
* Since: 2.2
**/
void
gimp_dialogs_show_help_button (gboolean show)
{
show_help_button = show ? TRUE : FALSE;
}
#ifdef G_OS_WIN32
void
gimp_dialog_set_title_bar_theme (GtkWidget *dialog)
{
HWND hwnd;
gboolean use_dark_mode = FALSE;
GdkWindow *window = NULL;
window = gtk_widget_get_window (GTK_WIDGET (dialog));
if (window)
{
GtkStyleContext *style;
GdkRGBA *color = NULL;
hwnd = (HWND) gdk_win32_window_get_handle (window);
/* Workaround since we don't have access to GimpGuiConfig.
* If the background color is below the threshold, then we're
* likely in dark mode.
*/
style = gtk_widget_get_style_context (GTK_WIDGET (dialog));
gtk_style_context_get (style, gtk_style_context_get_state (style),
GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &color,
NULL);
if (color)
{
if (color->red < 0.5 && color->green < 0.5 && color->blue < 0.5)
use_dark_mode = TRUE;
gdk_rgba_free (color);
}
DwmSetWindowAttribute (hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE,
&use_dark_mode, sizeof (use_dark_mode));
UpdateWindow (hwnd);
}
}
#endif

104
libgimpwidgets/gimpdialog.h Normal file
View file

@ -0,0 +1,104 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimpdialog.h
* Copyright (C) 2000-2003 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_DIALOG_H__
#define __GIMP_DIALOG_H__
G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
#define GIMP_TYPE_DIALOG (gimp_dialog_get_type ())
G_DECLARE_DERIVABLE_TYPE (GimpDialog, gimp_dialog, GIMP, DIALOG, GtkDialog)
struct _GimpDialogClass
{
GtkDialogClass parent_class;
/* Padding for future expansion */
void (* _gimp_reserved0) (void);
void (* _gimp_reserved1) (void);
void (* _gimp_reserved2) (void);
void (* _gimp_reserved3) (void);
void (* _gimp_reserved4) (void);
void (* _gimp_reserved5) (void);
void (* _gimp_reserved6) (void);
void (* _gimp_reserved7) (void);
void (* _gimp_reserved8) (void);
void (* _gimp_reserved9) (void);
};
GtkWidget * gimp_dialog_new (const gchar *title,
const gchar *role,
GtkWidget *parent,
GtkDialogFlags flags,
GimpHelpFunc help_func,
const gchar *help_id,
...) G_GNUC_NULL_TERMINATED;
GtkWidget * gimp_dialog_new_valist (const gchar *title,
const gchar *role,
GtkWidget *parent,
GtkDialogFlags flags,
GimpHelpFunc help_func,
const gchar *help_id,
va_list args);
GtkWidget * gimp_dialog_add_button (GimpDialog *dialog,
const gchar *button_text,
gint response_id);
void gimp_dialog_add_buttons (GimpDialog *dialog,
...) G_GNUC_NULL_TERMINATED;
void gimp_dialog_add_buttons_valist (GimpDialog *dialog,
va_list args);
gint gimp_dialog_run (GimpDialog *dialog);
void gimp_dialog_set_alternative_button_order_from_array
(GimpDialog *dialog,
gint n_buttons,
gint *order);
GBytes * gimp_dialog_get_native_handle (GimpDialog *dialog);
/* for internal use only! */
void gimp_dialogs_show_help_button (gboolean show);
/* gimp_dialog_set_alternative_button_order() doesn't need a dedicated
* wrapper function because anyway it won't be introspectable.
* GObject-Introspection bindings will have to use
* gimp_dialog_set_alternative_button_order_from_array().
*/
#define gimp_dialog_set_alternative_button_order(d,f...) \
G_GNUC_BEGIN_IGNORE_DEPRECATIONS; \
gtk_dialog_set_alternative_button_order(d,f); \
G_GNUC_END_IGNORE_DEPRECATIONS;
G_END_DECLS
#endif /* __GIMP_DIALOG_H__ */

666
libgimpwidgets/gimpeevl.c Normal file
View file

@ -0,0 +1,666 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpeevl.c
* Copyright (C) 2008 Fredrik Alstromer <roe@excu.se>
* Copyright (C) 2008 Martin Nordholts <martinn@svn.gnome.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
/* Introducing eevl eva, the evaluator. A straightforward recursive
* descent parser, no fuss, no new dependencies. The lexer is hand
* coded, tedious, not extremely fast but works. It evaluates the
* expression as it goes along, and does not create a parse tree or
* anything, and will not optimize anything. It uses doubles for
* precision, with the given use case, that's enough to combat any
* rounding errors (as opposed to optimizing the evaluation).
*
* It relies on external unit resolving through a callback and does
* elementary dimensionality constraint check (e.g. "2 mm + 3 px * 4
* in" is an error, as L + L^2 is a mismatch). It uses setjmp/longjmp
* for try/catch like pattern on error, it uses g_strtod() for numeric
* conversions and it's non-destructive in terms of the parameters, and
* it's reentrant.
*
* EBNF:
*
* expression ::= term { ('+' | '-') term }* |
* <empty string> ;
*
* term ::= ratio { ( '*' | '/' ) ratio }* ;
*
* ratio ::= signed factor { ':' signed factor }* ;
*
* signed factor ::= ( '+' | '-' )? factor ;
*
* factor ::= quantity ( '^' signed factor )? ;
*
* quantity ::= number unit? | '(' expression ')' ;
*
* number ::= ? what g_strtod() consumes ? ;
*
* unit ::= simple unit ( '^' signed factor )? ;
*
* simple unit ::= ? what not g_strtod() consumes and not whitespace ? ;
*
* The code should match the EBNF rather closely (except for the
* non-terminal unit factor, which is inlined into factor) for
* maintainability reasons.
*
* It will allow 1++1 and 1+-1 (resulting in 2 and 0, respectively),
* but I figured one might want that, and I don't think it's going to
* throw anyone off.
*/
#include "config.h"
#include <setjmp.h>
#include <string.h>
#include <glib-object.h>
#include "libgimpmath/gimpmath.h"
#include "gimpeevl.h"
#include "gimpwidgets-error.h"
#include "libgimp/libgimp-intl.h"
typedef enum
{
GIMP_EEVL_TOKEN_NUM = 30000,
GIMP_EEVL_TOKEN_IDENTIFIER = 30001,
GIMP_EEVL_TOKEN_ANY = 40000,
GIMP_EEVL_TOKEN_END = 50000
} GimpEevlTokenType;
typedef struct
{
GimpEevlTokenType type;
union
{
gdouble fl;
struct
{
const gchar *c;
gint size;
};
} value;
} GimpEevlToken;
typedef struct
{
const gchar *string;
GimpEevlOptions options;
GimpEevlToken current_token;
const gchar *start_of_current_token;
jmp_buf catcher;
const gchar *error_message;
} GimpEevl;
static void gimp_eevl_init (GimpEevl *eva,
const gchar *string,
const GimpEevlOptions *options);
static GimpEevlQuantity gimp_eevl_complete (GimpEevl *eva);
static GimpEevlQuantity gimp_eevl_expression (GimpEevl *eva);
static GimpEevlQuantity gimp_eevl_term (GimpEevl *eva);
static GimpEevlQuantity gimp_eevl_ratio (GimpEevl *eva);
static GimpEevlQuantity gimp_eevl_signed_factor (GimpEevl *eva);
static GimpEevlQuantity gimp_eevl_factor (GimpEevl *eva);
static GimpEevlQuantity gimp_eevl_quantity (GimpEevl *eva);
static gboolean gimp_eevl_accept (GimpEevl *eva,
GimpEevlTokenType token_type,
GimpEevlToken *consumed_token);
static void gimp_eevl_lex (GimpEevl *eva);
static void gimp_eevl_lex_accept_count (GimpEevl *eva,
gint count,
GimpEevlTokenType token_type);
static void gimp_eevl_lex_accept_to (GimpEevl *eva,
gchar *to,
GimpEevlTokenType token_type);
static void gimp_eevl_move_past_whitespace (GimpEevl *eva);
static gboolean gimp_eevl_unit_identifier_start (gunichar c);
static gboolean gimp_eevl_unit_identifier_continue (gunichar c);
static gint gimp_eevl_unit_identifier_size (const gchar *s,
gint start);
static void gimp_eevl_expect (GimpEevl *eva,
GimpEevlTokenType token_type,
GimpEevlToken *value);
static void gimp_eevl_error (GimpEevl *eva,
gchar *msg);
/**
* gimp_eevl_evaluate:
* @string: The NULL-terminated string to be evaluated.
* @options: Evaluations options.
* @result: Result of evaluation.
* @error_pos: Will point to the position within the string,
* before which the parse / evaluation error
* occurred. Will be set to null if no error occurred.
* @error_message: Will point to a static string with a semi-descriptive
* error message if parsing / evaluation failed.
*
* Evaluates the given arithmetic expression, along with an optional dimension
* analysis, and basic unit conversions.
*
* All units conversions factors are relative to some implicit
* base-unit (which in GIMP is inches). This is also the unit of the
* returned value.
*
* Returns: A #GimpEevlQuantity with a value given in the base unit along with
* the order of the dimension (i.e. if the base unit is inches, a dimension
* order of two means in^2).
**/
gboolean
gimp_eevl_evaluate (const gchar *string,
const GimpEevlOptions *options,
GimpEevlQuantity *result,
const gchar **error_pos,
GError **error)
{
GimpEevl eva;
g_return_val_if_fail (g_utf8_validate (string, -1, NULL), FALSE);
g_return_val_if_fail (options != NULL, FALSE);
g_return_val_if_fail (options->unit_resolver_proc != NULL, FALSE);
g_return_val_if_fail (result != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
gimp_eevl_init (&eva,
string,
options);
if (!setjmp (eva.catcher)) /* try... */
{
*result = gimp_eevl_complete (&eva);
return TRUE;
}
else /* catch.. */
{
if (error_pos)
*error_pos = eva.start_of_current_token;
g_set_error_literal (error,
GIMP_WIDGETS_ERROR,
GIMP_WIDGETS_PARSE_ERROR,
eva.error_message);
return FALSE;
}
}
static void
gimp_eevl_init (GimpEevl *eva,
const gchar *string,
const GimpEevlOptions *options)
{
eva->string = string;
eva->options = *options;
eva->current_token.type = GIMP_EEVL_TOKEN_END;
eva->error_message = NULL;
/* Preload symbol... */
gimp_eevl_lex (eva);
}
static GimpEevlQuantity
gimp_eevl_complete (GimpEevl *eva)
{
GimpEevlQuantity result = {0, 0};
GimpEevlQuantity default_unit_factor;
gdouble default_unit_offset;
/* Empty expression evaluates to 0 */
if (gimp_eevl_accept (eva, GIMP_EEVL_TOKEN_END, NULL))
return result;
result = gimp_eevl_expression (eva);
/* There should be nothing left to parse by now */
gimp_eevl_expect (eva, GIMP_EEVL_TOKEN_END, 0);
eva->options.unit_resolver_proc (NULL,
&default_unit_factor,
&default_unit_offset,
eva->options.data);
/* Entire expression is dimensionless, apply default unit if
* applicable
*/
if (result.dimension == 0 && default_unit_factor.dimension != 0)
{
result.value /= default_unit_factor.value;
result.value += default_unit_offset;
result.dimension = default_unit_factor.dimension;
}
return result;
}
static GimpEevlQuantity
gimp_eevl_expression (GimpEevl *eva)
{
gboolean subtract;
GimpEevlQuantity evaluated_terms;
evaluated_terms = gimp_eevl_term (eva);
/* continue evaluating terms, chained with + or -. */
for (subtract = FALSE;
gimp_eevl_accept (eva, '+', NULL) ||
(subtract = gimp_eevl_accept (eva, '-', NULL));
subtract = FALSE)
{
GimpEevlQuantity new_term = gimp_eevl_term (eva);
/* If dimensions mismatch, attempt default unit assignment */
if (new_term.dimension != evaluated_terms.dimension)
{
GimpEevlQuantity default_unit_factor;
gdouble default_unit_offset;
eva->options.unit_resolver_proc (NULL,
&default_unit_factor,
&default_unit_offset,
eva->options.data);
if (new_term.dimension == 0 &&
evaluated_terms.dimension == default_unit_factor.dimension)
{
new_term.value /= default_unit_factor.value;
new_term.value += default_unit_offset;
new_term.dimension = default_unit_factor.dimension;
}
else if (evaluated_terms.dimension == 0 &&
new_term.dimension == default_unit_factor.dimension)
{
evaluated_terms.value /= default_unit_factor.value;
evaluated_terms.value += default_unit_offset;
evaluated_terms.dimension = default_unit_factor.dimension;
}
else
{
gimp_eevl_error (eva, "Dimension mismatch during addition");
}
}
evaluated_terms.value += (subtract ? -new_term.value : new_term.value);
}
return evaluated_terms;
}
static GimpEevlQuantity
gimp_eevl_term (GimpEevl *eva)
{
gboolean division;
GimpEevlQuantity evaluated_ratios;
evaluated_ratios = gimp_eevl_ratio (eva);
for (division = FALSE;
gimp_eevl_accept (eva, '*', NULL) ||
(division = gimp_eevl_accept (eva, '/', NULL));
division = FALSE)
{
GimpEevlQuantity new_ratio = gimp_eevl_ratio (eva);
if (division)
{
evaluated_ratios.value /= new_ratio.value;
evaluated_ratios.dimension -= new_ratio.dimension;
}
else
{
evaluated_ratios.value *= new_ratio.value;
evaluated_ratios.dimension += new_ratio.dimension;
}
}
return evaluated_ratios;
}
static GimpEevlQuantity
gimp_eevl_ratio (GimpEevl *eva)
{
GimpEevlQuantity evaluated_signed_factors;
if (! eva->options.ratio_expressions)
return gimp_eevl_signed_factor (eva);
evaluated_signed_factors = gimp_eevl_signed_factor (eva);
while (gimp_eevl_accept (eva, ':', NULL))
{
GimpEevlQuantity new_signed_factor = gimp_eevl_signed_factor (eva);
if (eva->options.ratio_invert)
{
GimpEevlQuantity temp;
temp = evaluated_signed_factors;
evaluated_signed_factors = new_signed_factor;
new_signed_factor = temp;
}
evaluated_signed_factors.value *= eva->options.ratio_quantity.value /
new_signed_factor.value;
evaluated_signed_factors.dimension += eva->options.ratio_quantity.dimension -
new_signed_factor.dimension;
}
return evaluated_signed_factors;
}
static GimpEevlQuantity
gimp_eevl_signed_factor (GimpEevl *eva)
{
GimpEevlQuantity result;
gboolean negate = FALSE;
if (! gimp_eevl_accept (eva, '+', NULL))
negate = gimp_eevl_accept (eva, '-', NULL);
result = gimp_eevl_factor (eva);
if (negate) result.value = -result.value;
return result;
}
static GimpEevlQuantity
gimp_eevl_factor (GimpEevl *eva)
{
GimpEevlQuantity evaluated_factor;
evaluated_factor = gimp_eevl_quantity (eva);
if (gimp_eevl_accept (eva, '^', NULL))
{
GimpEevlQuantity evaluated_exponent;
evaluated_exponent = gimp_eevl_signed_factor (eva);
if (evaluated_exponent.dimension != 0)
gimp_eevl_error (eva, "Exponent is not a dimensionless quantity");
evaluated_factor.value = pow (evaluated_factor.value,
evaluated_exponent.value);
evaluated_factor.dimension *= evaluated_exponent.value;
}
return evaluated_factor;
}
static GimpEevlQuantity
gimp_eevl_quantity (GimpEevl *eva)
{
GimpEevlQuantity evaluated_quantity = { 0, 0 };
GimpEevlToken consumed_token;
if (gimp_eevl_accept (eva,
GIMP_EEVL_TOKEN_NUM,
&consumed_token))
{
evaluated_quantity.value = consumed_token.value.fl;
}
else if (gimp_eevl_accept (eva, '(', NULL))
{
evaluated_quantity = gimp_eevl_expression (eva);
gimp_eevl_expect (eva, ')', 0);
}
else
{
gimp_eevl_error (eva, "Expected number or '('");
}
if (eva->current_token.type == GIMP_EEVL_TOKEN_IDENTIFIER)
{
gchar *identifier;
GimpEevlQuantity factor;
gdouble offset;
gimp_eevl_accept (eva,
GIMP_EEVL_TOKEN_ANY,
&consumed_token);
identifier = g_newa (gchar, consumed_token.value.size + 1);
strncpy (identifier, consumed_token.value.c, consumed_token.value.size);
identifier[consumed_token.value.size] = '\0';
if (eva->options.unit_resolver_proc (identifier,
&factor,
&offset,
eva->options.data))
{
if (gimp_eevl_accept (eva, '^', NULL))
{
GimpEevlQuantity evaluated_exponent;
evaluated_exponent = gimp_eevl_signed_factor (eva);
if (evaluated_exponent.dimension != 0)
{
gimp_eevl_error (eva,
"Exponent is not a dimensionless quantity");
}
if (offset != 0.0)
{
gimp_eevl_error (eva,
"Invalid unit exponent");
}
factor.value = pow (factor.value, evaluated_exponent.value);
factor.dimension *= evaluated_exponent.value;
}
evaluated_quantity.value /= factor.value;
evaluated_quantity.value += offset;
evaluated_quantity.dimension += factor.dimension;
}
else
{
gimp_eevl_error (eva, "Unit was not resolved");
}
}
return evaluated_quantity;
}
static gboolean
gimp_eevl_accept (GimpEevl *eva,
GimpEevlTokenType token_type,
GimpEevlToken *consumed_token)
{
gboolean existed = FALSE;
if (token_type == eva->current_token.type ||
token_type == GIMP_EEVL_TOKEN_ANY)
{
existed = TRUE;
if (consumed_token)
*consumed_token = eva->current_token;
/* Parse next token */
gimp_eevl_lex (eva);
}
return existed;
}
static void
gimp_eevl_lex (GimpEevl *eva)
{
const gchar *s;
gimp_eevl_move_past_whitespace (eva);
s = eva->string;
eva->start_of_current_token = s;
if (! s || s[0] == '\0')
{
/* We're all done */
eva->current_token.type = GIMP_EEVL_TOKEN_END;
}
else if (s[0] == '+' || s[0] == '-')
{
/* Snatch these before the g_strtod() does, otherwise they might
* be used in a numeric conversion.
*/
gimp_eevl_lex_accept_count (eva, 1, s[0]);
}
else
{
/* Attempt to parse a numeric value */
gchar *endptr = NULL;
gdouble value = g_strtod (s, &endptr);
if (endptr && endptr != s)
{
/* A numeric could be parsed, use it */
eva->current_token.value.fl = value;
gimp_eevl_lex_accept_to (eva, endptr, GIMP_EEVL_TOKEN_NUM);
}
else if (gimp_eevl_unit_identifier_start (s[0]))
{
/* Unit identifier */
eva->current_token.value.c = s;
eva->current_token.value.size = gimp_eevl_unit_identifier_size (s, 0);
gimp_eevl_lex_accept_count (eva,
eva->current_token.value.size,
GIMP_EEVL_TOKEN_IDENTIFIER);
}
else
{
/* Everything else is a single character token */
gimp_eevl_lex_accept_count (eva, 1, s[0]);
}
}
}
static void
gimp_eevl_lex_accept_count (GimpEevl *eva,
gint count,
GimpEevlTokenType token_type)
{
eva->current_token.type = token_type;
eva->string += count;
}
static void
gimp_eevl_lex_accept_to (GimpEevl *eva,
gchar *to,
GimpEevlTokenType token_type)
{
eva->current_token.type = token_type;
eva->string = to;
}
static void
gimp_eevl_move_past_whitespace (GimpEevl *eva)
{
if (! eva->string)
return;
while (g_ascii_isspace (*eva->string))
eva->string++;
}
static gboolean
gimp_eevl_unit_identifier_start (gunichar c)
{
return (g_unichar_isalpha (c) ||
c == (gunichar) '%' ||
c == (gunichar) '\'');
}
static gboolean
gimp_eevl_unit_identifier_continue (gunichar c)
{
return (gimp_eevl_unit_identifier_start (c) ||
g_unichar_isdigit (c));
}
/**
* gimp_eevl_unit_identifier_size:
* @s:
* @start:
*
* Returns: Size of identifier in bytes (not including NULL
* terminator).
**/
static gint
gimp_eevl_unit_identifier_size (const gchar *string,
gint start_offset)
{
const gchar *start = g_utf8_offset_to_pointer (string, start_offset);
const gchar *s = start;
gunichar c = g_utf8_get_char (s);
gint length = 0;
if (gimp_eevl_unit_identifier_start (c))
{
s = g_utf8_next_char (s);
c = g_utf8_get_char (s);
length++;
while (gimp_eevl_unit_identifier_continue (c))
{
s = g_utf8_next_char (s);
c = g_utf8_get_char (s);
length++;
}
}
return g_utf8_offset_to_pointer (start, length) - start;
}
static void
gimp_eevl_expect (GimpEevl *eva,
GimpEevlTokenType token_type,
GimpEevlToken *value)
{
if (! gimp_eevl_accept (eva, token_type, value))
gimp_eevl_error (eva, "Unexpected token");
}
static void
gimp_eevl_error (GimpEevl *eva,
gchar *msg)
{
eva->error_message = msg;
longjmp (eva->catcher, 1);
}

99
libgimpwidgets/gimpeevl.h Normal file
View file

@ -0,0 +1,99 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpeevl.h
* Copyright (C) 2008-2009 Fredrik Alstromer <roe@excu.se>
* Copyright (C) 2008-2009 Martin Nordholts <martinn@svn.gnome.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#ifndef __GIMP_EEVL_H__
#define __GIMP_EEVL_H__
G_BEGIN_DECLS
/**
* GimpEevlQuantity:
* @value: In reference units.
* @dimension: in has a dimension of 1, in^2 has a dimension of 2 etc
*/
typedef struct
{
gdouble value;
gint dimension;
} GimpEevlQuantity;
/**
* GimpEevlUnitResolverProc:
* @identifier: Identifier of unit to resolve or %NULL if default unit
* should be resolved.
* @factor: Units per reference unit. For example, in GIMP the
* reference unit is inches so resolving "mm" should
* return 25.4 since there are 25.4 millimeters per inch.
* @offset: Offset to apply after scaling the value according to @factor.
* @data: Data given to gimp_eevl_evaluate().
*
* Returns: If the unit was successfully resolved or not.
*
*/
typedef gboolean (* GimpEevlUnitResolverProc) (const gchar *identifier,
GimpEevlQuantity *factor,
gdouble *offset,
gpointer data);
/**
* GimpEevlOptions:
* @unit_resolver_proc: Unit resolver callback.
* @data: Data passed to unit resolver.
* @ratio_expressions: Allow ratio expressions
* @ratio_invert: Invert ratios
* @ratio_quantity: Quantity to multiply ratios by
*/
typedef struct
{
GimpEevlUnitResolverProc unit_resolver_proc;
gpointer data;
gboolean ratio_expressions;
gboolean ratio_invert;
GimpEevlQuantity ratio_quantity;
} GimpEevlOptions;
#define GIMP_EEVL_OPTIONS_INIT \
((const GimpEevlOptions) \
{ \
.unit_resolver_proc = NULL, \
.data = NULL, \
\
.ratio_expressions = FALSE, \
.ratio_invert = FALSE, \
.ratio_quantity = {0.0, 0} \
})
gboolean gimp_eevl_evaluate (const gchar *string,
const GimpEevlOptions *options,
GimpEevlQuantity *result,
const gchar **error_pos,
GError **error);
G_END_DECLS
#endif /* __GIMP_EEVL_H__ */

View file

@ -0,0 +1,211 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpenumcombobox.c
* Copyright (C) 2004 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "gimpwidgetstypes.h"
#include "gimpenumcombobox.h"
#include "gimpenumstore.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimpenumcombobox
* @title: GimpEnumComboBox
* @short_description: A #GimpIntComboBox subclass for selecting an enum value.
*
* A #GtkComboBox subclass for selecting an enum value.
**/
enum
{
PROP_0,
PROP_MODEL
};
static void gimp_enum_combo_box_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_enum_combo_box_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
G_DEFINE_TYPE (GimpEnumComboBox, gimp_enum_combo_box,
GIMP_TYPE_INT_COMBO_BOX)
#define parent_class gimp_enum_combo_box_parent_class
static void
gimp_enum_combo_box_class_init (GimpEnumComboBoxClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = gimp_enum_combo_box_set_property;
object_class->get_property = gimp_enum_combo_box_get_property;
/* override the "model" property of GtkComboBox */
g_object_class_install_property (object_class,
PROP_MODEL,
g_param_spec_object ("model",
"Model",
"The enum store used by this combo box",
GIMP_TYPE_ENUM_STORE,
GIMP_PARAM_READWRITE));
}
static void
gimp_enum_combo_box_init (GimpEnumComboBox *combo_box)
{
}
static void
gimp_enum_combo_box_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkComboBox *combo_box = GTK_COMBO_BOX (object);
switch (prop_id)
{
case PROP_MODEL:
gtk_combo_box_set_model (combo_box, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gimp_enum_combo_box_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkComboBox *combo_box = GTK_COMBO_BOX (object);
switch (prop_id)
{
case PROP_MODEL:
g_value_set_object (value, gtk_combo_box_get_model (combo_box));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/**
* gimp_enum_combo_box_new:
* @enum_type: the #GType of an enum.
*
* Creates a #GtkComboBox readily filled with all enum values from a
* given @enum_type. The enum needs to be registered to the type
* system. It should also have %GimpEnumDesc descriptions registered
* that contain translatable value names. This is the case for the
* enums used in the GIMP PDB functions.
*
* This is just a convenience function. If you need more control over
* the enum values that appear in the combo_box, you can create your
* own #GimpEnumStore and use gimp_enum_combo_box_new_with_model().
*
* Returns: a new #GimpEnumComboBox.
*
* Since: 2.4
**/
GtkWidget *
gimp_enum_combo_box_new (GType enum_type)
{
GtkListStore *store;
GtkWidget *combo_box;
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
store = gimp_enum_store_new (enum_type);
combo_box = g_object_new (GIMP_TYPE_ENUM_COMBO_BOX,
"model", store,
NULL);
g_object_unref (store);
return combo_box;
}
/**
* gimp_enum_combo_box_new_with_model
* @enum_store: a #GimpEnumStore to use as the model
*
* Creates a #GtkComboBox for the given @enum_store.
*
* Returns: a new #GimpEnumComboBox.
*
* Since: 2.4
**/
GtkWidget *
gimp_enum_combo_box_new_with_model (GimpEnumStore *enum_store)
{
g_return_val_if_fail (GIMP_IS_ENUM_STORE (enum_store), NULL);
return g_object_new (GIMP_TYPE_ENUM_COMBO_BOX,
"model", enum_store,
NULL);
}
/**
* gimp_enum_combo_box_set_icon_prefix:
* @combo_box: a #GimpEnumComboBox
* @icon_prefix: a prefix to create icon names from enum values
*
* Attempts to create icons for all items in the @combo_box. See
* gimp_enum_store_set_icon_prefix() to find out what to use as
* @icon_prefix.
*
* Since: 2.10
**/
void
gimp_enum_combo_box_set_icon_prefix (GimpEnumComboBox *combo_box,
const gchar *icon_prefix)
{
GtkTreeModel *model;
g_return_if_fail (GIMP_IS_ENUM_COMBO_BOX (combo_box));
model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
gimp_enum_store_set_icon_prefix (GIMP_ENUM_STORE (model), icon_prefix);
}

View file

@ -0,0 +1,62 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpenumcombobox.h
* Copyright (C) 2004 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_ENUM_COMBO_BOX_H__
#define __GIMP_ENUM_COMBO_BOX_H__
#include <libgimpwidgets/gimpintcombobox.h>
G_BEGIN_DECLS
#define GIMP_TYPE_ENUM_COMBO_BOX (gimp_enum_combo_box_get_type ())
G_DECLARE_DERIVABLE_TYPE (GimpEnumComboBox, gimp_enum_combo_box, GIMP, ENUM_COMBO_BOX, GimpIntComboBox)
struct _GimpEnumComboBoxClass
{
GimpIntComboBoxClass parent_class;
/* Padding for future expansion */
void (* _gimp_reserved0) (void);
void (* _gimp_reserved1) (void);
void (* _gimp_reserved2) (void);
void (* _gimp_reserved3) (void);
void (* _gimp_reserved4) (void);
void (* _gimp_reserved5) (void);
void (* _gimp_reserved6) (void);
void (* _gimp_reserved7) (void);
void (* _gimp_reserved8) (void);
void (* _gimp_reserved9) (void);
};
GtkWidget * gimp_enum_combo_box_new (GType enum_type);
GtkWidget * gimp_enum_combo_box_new_with_model (GimpEnumStore *enum_store);
void gimp_enum_combo_box_set_icon_prefix (GimpEnumComboBox *combo_box,
const gchar *icon_prefix);
G_END_DECLS
#endif /* __GIMP_ENUM_COMBO_BOX_H__ */

View file

@ -0,0 +1,226 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpenumlabel.c
* Copyright (C) 2005 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "gimpwidgetstypes.h"
#include "gimpenumlabel.h"
/**
* SECTION: gimpenumlabel
* @title: GimpEnumLabel
* @short_description: A #GtkLabel subclass that displays an enum value.
*
* A #GtkLabel subclass that displays an enum value.
**/
enum
{
PROP_0,
PROP_ENUM_TYPE,
PROP_ENUM_VALUE
};
struct _GimpEnumLabel
{
GtkLabel parent_instance;
GEnumClass *enum_class;
};
static void gimp_enum_label_finalize (GObject *object);
static void gimp_enum_label_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_enum_label_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
G_DEFINE_TYPE (GimpEnumLabel, gimp_enum_label, GTK_TYPE_LABEL)
#define parent_class gimp_enum_label_parent_class
static void
gimp_enum_label_class_init (GimpEnumLabelClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gimp_enum_label_finalize;
object_class->get_property = gimp_enum_label_get_property;
object_class->set_property = gimp_enum_label_set_property;
/**
* GimpEnumLabel:enum-type:
*
* The #GType of the enum.
*
* Since: 2.8
**/
g_object_class_install_property (object_class, PROP_ENUM_TYPE,
g_param_spec_gtype ("enum-type",
"Enum Type",
"The type of the displayed enum",
G_TYPE_NONE,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
/**
* GimpEnumLabel:enum-value:
*
* The value to display.
*
* Since: 2.8
**/
g_object_class_install_property (object_class, PROP_ENUM_VALUE,
g_param_spec_int ("enum-value",
"Enum Value",
"The enum value to display",
G_MININT, G_MAXINT, 0,
GIMP_PARAM_WRITABLE |
G_PARAM_CONSTRUCT));
}
static void
gimp_enum_label_init (GimpEnumLabel *enum_label)
{
}
static void
gimp_enum_label_finalize (GObject *object)
{
GimpEnumLabel *label = GIMP_ENUM_LABEL (object);
g_clear_pointer (&label->enum_class, g_type_class_unref);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_enum_label_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpEnumLabel *label = GIMP_ENUM_LABEL (object);
switch (property_id)
{
case PROP_ENUM_TYPE:
if (label->enum_class)
g_value_set_gtype (value, G_TYPE_FROM_CLASS (label->enum_class));
else
g_value_set_gtype (value, G_TYPE_NONE);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_enum_label_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpEnumLabel *label = GIMP_ENUM_LABEL (object);
switch (property_id)
{
case PROP_ENUM_TYPE:
label->enum_class = g_type_class_ref (g_value_get_gtype (value));
break;
case PROP_ENUM_VALUE:
gimp_enum_label_set_value (label, g_value_get_int (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* gimp_enum_label_new:
* @enum_type: the #GType of an enum
* @value: an enum value
*
* Returns: a new #GimpEnumLabel.
*
* Since: 2.4
**/
GtkWidget *
gimp_enum_label_new (GType enum_type,
gint value)
{
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
return g_object_new (GIMP_TYPE_ENUM_LABEL,
"enum-type", enum_type,
"enum-value", value,
NULL);
}
/**
* gimp_enum_label_set_value
* @label: a #GimpEnumLabel
* @value: an enum value
*
* Since: 2.4
**/
void
gimp_enum_label_set_value (GimpEnumLabel *label,
gint value)
{
const gchar *nick;
const gchar *desc;
g_return_if_fail (GIMP_IS_ENUM_LABEL (label));
if (! gimp_enum_get_value (G_TYPE_FROM_CLASS (label->enum_class), value,
NULL, &nick, &desc, NULL))
{
g_warning ("%s: %d is not valid for enum of type '%s'",
G_STRLOC, value,
g_type_name (G_TYPE_FROM_CLASS (label->enum_class)));
return;
}
if (! desc)
desc = nick;
gtk_label_set_text (GTK_LABEL (label), desc);
}

View file

@ -0,0 +1,44 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpenumlabel.h
* Copyright (C) 2005 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_ENUM__LABEL_H__
#define __GIMP_ENUM__LABEL_H__
G_BEGIN_DECLS
#define GIMP_TYPE_ENUM_LABEL (gimp_enum_label_get_type ())
G_DECLARE_FINAL_TYPE (GimpEnumLabel, gimp_enum_label, GIMP, ENUM_LABEL, GtkScale)
GtkWidget * gimp_enum_label_new (GType enum_type,
gint value);
void gimp_enum_label_set_value (GimpEnumLabel *label,
gint value);
G_END_DECLS
#endif /* __GIMP_ENUM_LABEL_H__ */

View file

@ -0,0 +1,392 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpenumstore.c
* Copyright (C) 2004-2007 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "gimpwidgetstypes.h"
#include "gimpenumstore.h"
/**
* SECTION: gimpenumstore
* @title: GimpEnumStore
* @short_description: A #GimpIntStore subclass that keeps enum values.
*
* A #GimpIntStore subclass that keeps enum values.
**/
enum
{
PROP_0,
PROP_ENUM_TYPE
};
struct _GimpEnumStore
{
GimpIntStore parent_instance;
GEnumClass *enum_class;
};
static void gimp_enum_store_finalize (GObject *object);
static void gimp_enum_store_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_enum_store_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_enum_store_add_value (GtkListStore *store,
GEnumValue *value);
G_DEFINE_TYPE (GimpEnumStore, gimp_enum_store, GIMP_TYPE_INT_STORE)
#define parent_class gimp_enum_store_parent_class
static void
gimp_enum_store_class_init (GimpEnumStoreClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gimp_enum_store_finalize;
object_class->set_property = gimp_enum_store_set_property;
object_class->get_property = gimp_enum_store_get_property;
/**
* GimpEnumStore:enum-type:
*
* Sets the #GType of the enum to be used in the store.
*
* Since: 2.4
*/
g_object_class_install_property (object_class,
PROP_ENUM_TYPE,
g_param_spec_gtype ("enum-type",
"Enum Type",
"The type of the enum",
G_TYPE_ENUM,
G_PARAM_CONSTRUCT_ONLY |
GIMP_PARAM_READWRITE));
}
static void
gimp_enum_store_init (GimpEnumStore *store)
{
}
static void
gimp_enum_store_finalize (GObject *object)
{
GimpEnumStore *store = GIMP_ENUM_STORE (object);
g_clear_pointer (&store->enum_class, g_type_class_unref);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_enum_store_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpEnumStore *store = GIMP_ENUM_STORE (object);
switch (property_id)
{
case PROP_ENUM_TYPE:
g_return_if_fail (store->enum_class == NULL);
store->enum_class = g_type_class_ref (g_value_get_gtype (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_enum_store_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpEnumStore *store = GIMP_ENUM_STORE (object);
switch (property_id)
{
case PROP_ENUM_TYPE:
g_value_set_gtype (value, (store->enum_class ?
G_TYPE_FROM_CLASS (store->enum_class) :
G_TYPE_NONE));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_enum_store_add_value (GtkListStore *store,
GEnumValue *value)
{
GimpEnumStore *enum_store = GIMP_ENUM_STORE (store);
GtkTreeIter iter = { 0, };
const gchar *desc;
const gchar *abbrev;
gchar *stripped;
desc = gimp_enum_value_get_desc (enum_store->enum_class, value);
abbrev = gimp_enum_value_get_abbrev (enum_store->enum_class, value);
/* no mnemonics in combo boxes */
stripped = gimp_strip_uline (desc);
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
GIMP_INT_STORE_VALUE, value->value,
GIMP_INT_STORE_LABEL, stripped,
GIMP_INT_STORE_ABBREV, abbrev,
-1);
g_free (stripped);
}
/**
* gimp_enum_store_new:
* @enum_type: the #GType of an enum.
*
* Creates a new #GimpEnumStore, derived from #GtkListStore and fills
* it with enum values. The enum needs to be registered to the type
* system and should have translatable value names.
*
* Returns: a new #GimpEnumStore.
*
* Since: 2.4
**/
GtkListStore *
gimp_enum_store_new (GType enum_type)
{
GtkListStore *store;
GEnumClass *enum_class;
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
enum_class = g_type_class_ref (enum_type);
store = gimp_enum_store_new_with_range (enum_type,
enum_class->minimum,
enum_class->maximum);
g_type_class_unref (enum_class);
return store;
}
/**
* gimp_enum_store_new_with_range:
* @enum_type: the #GType of an enum.
* @minimum: the minimum value to include
* @maximum: the maximum value to include
*
* Creates a new #GimpEnumStore like gimp_enum_store_new() but allows
* to limit the enum values to a certain range. Values smaller than
* @minimum or larger than @maximum are not added to the store.
*
* Returns: a new #GimpEnumStore.
*
* Since: 2.4
**/
GtkListStore *
gimp_enum_store_new_with_range (GType enum_type,
gint minimum,
gint maximum)
{
GimpEnumStore *enum_store;
GtkListStore *store;
GEnumValue *value;
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
store = g_object_new (GIMP_TYPE_ENUM_STORE,
"enum-type", enum_type,
NULL);
enum_store = GIMP_ENUM_STORE (store);
for (value = enum_store->enum_class->values;
value->value_name;
value++)
{
if (value->value < minimum || value->value > maximum)
continue;
gimp_enum_store_add_value (store, value);
}
return store;
}
/**
* gimp_enum_store_new_with_values: (skip)
* @enum_type: the #GType of an enum.
* @n_values: the number of enum values to include
* @...: a list of enum values (exactly @n_values)
*
* Creates a new #GimpEnumStore like gimp_enum_store_new() but allows
* to explicitly list the enum values that should be added to the
* store.
*
* Returns: a new #GimpEnumStore.
*
* Since: 2.4
**/
GtkListStore *
gimp_enum_store_new_with_values (GType enum_type,
gint n_values,
...)
{
GtkListStore *store;
va_list args;
va_start (args, n_values);
store = gimp_enum_store_new_with_values_valist (enum_type, n_values, args);
va_end (args);
return store;
}
/**
* gimp_enum_store_new_with_values_valist: (skip)
* @enum_type: the #GType of an enum.
* @n_values: the number of enum values to include
* @args: a va_list of enum values (exactly @n_values)
*
* See gimp_enum_store_new_with_values().
*
* Returns: a new #GimpEnumStore.
*
* Since: 2.4
**/
GtkListStore *
gimp_enum_store_new_with_values_valist (GType enum_type,
gint n_values,
va_list args)
{
GimpEnumStore *enum_store;
GtkListStore *store;
GEnumValue *value;
gint i;
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
g_return_val_if_fail (n_values > 1, NULL);
store = g_object_new (GIMP_TYPE_ENUM_STORE,
"enum-type", enum_type,
NULL);
enum_store = GIMP_ENUM_STORE (store);
for (i = 0; i < n_values; i++)
{
value = g_enum_get_value (enum_store->enum_class,
va_arg (args, gint));
if (value)
gimp_enum_store_add_value (store, value);
}
return store;
}
/**
* gimp_enum_store_set_icon_prefix:
* @store: a #GimpEnumStore
* @icon_prefix: a prefix to create icon names from enum values
*
* Creates an icon name for each enum value in the @store by appending
* the value's nick to the given @icon_prefix, separated by a hyphen.
*
* See also: gimp_enum_combo_box_set_icon_prefix().
*
* Since: 2.10
**/
void
gimp_enum_store_set_icon_prefix (GimpEnumStore *store,
const gchar *icon_prefix)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean iter_valid;
g_return_if_fail (GIMP_IS_ENUM_STORE (store));
model = GTK_TREE_MODEL (store);
for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &iter))
{
gchar *icon_name = NULL;
if (icon_prefix)
{
GEnumValue *enum_value;
gint value;
gtk_tree_model_get (model, &iter,
GIMP_INT_STORE_VALUE, &value,
-1);
enum_value = g_enum_get_value (store->enum_class, value);
if (enum_value)
{
icon_name = g_strconcat (icon_prefix, "-",
enum_value->value_nick,
NULL);
}
}
gtk_list_store_set (GTK_LIST_STORE (store), &iter,
GIMP_INT_STORE_ICON_NAME, icon_name,
-1);
if (icon_name)
g_free (icon_name);
}
}

View file

@ -0,0 +1,55 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpenumstore.h
* Copyright (C) 2004 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_ENUM_STORE_H__
#define __GIMP_ENUM_STORE_H__
#include <libgimpwidgets/gimpintstore.h>
G_BEGIN_DECLS
#define GIMP_TYPE_ENUM_STORE (gimp_enum_store_get_type ())
G_DECLARE_FINAL_TYPE (GimpEnumStore, gimp_enum_store, GIMP, ENUM_STORE, GimpIntStore)
GtkListStore * gimp_enum_store_new (GType enum_type);
GtkListStore * gimp_enum_store_new_with_range (GType enum_type,
gint minimum,
gint maximum);
GtkListStore * gimp_enum_store_new_with_values (GType enum_type,
gint n_values,
...);
GtkListStore * gimp_enum_store_new_with_values_valist (GType enum_type,
gint n_values,
va_list args);
void gimp_enum_store_set_icon_prefix (GimpEnumStore *store,
const gchar *icon_prefix);
G_END_DECLS
#endif /* __GIMP_ENUM_STORE_H__ */

View file

@ -0,0 +1,509 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpenumwidgets.c
* Copyright (C) 2002-2004 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "gimpwidgetstypes.h"
#include "gimpenumwidgets.h"
#include "gimpframe.h"
#include "gimphelpui.h"
/**
* SECTION: gimpenumwidgets
* @title: GimpEnumWidgets
* @short_description: A set of utility functions to create widgets
* based on enums.
*
* A set of utility functions to create widgets based on enums.
**/
/**
* gimp_enum_radio_box_new:
* @enum_type: the #GType of an enum.
* @callback: (nullable): a callback to connect to the "toggled" signal of each
* #GtkRadioButton that is created.
* @callback_data: data to pass to the @callback.
* @callback_data_destroy: Destroy function for @callback_data.
* @first_button: (out) (optional) (transfer none):
* Returns the first button in the created group.
*
* Creates a new group of #GtkRadioButtons representing the enum
* values. A group of radiobuttons is a good way to represent enums
* with up to three or four values. Often it is better to use a
* #GimpEnumComboBox instead.
*
* Returns: (transfer full): a new #GtkBox holding a group of #GtkRadioButtons.
*
* Since: 2.4
**/
GtkWidget *
gimp_enum_radio_box_new (GType enum_type,
GCallback callback,
gpointer callback_data,
GDestroyNotify callback_data_destroy,
GtkWidget **first_button)
{
GEnumClass *enum_class;
GtkWidget *vbox;
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
enum_class = g_type_class_ref (enum_type);
vbox = gimp_enum_radio_box_new_with_range (enum_type,
enum_class->minimum,
enum_class->maximum,
callback, callback_data,
callback_data_destroy,
first_button);
g_type_class_unref (enum_class);
return vbox;
}
/**
* gimp_enum_radio_box_new_with_range:
* @minimum: the minimum enum value
* @maximum: the maximum enum value
* @enum_type: the #GType of an enum.
* @callback: (nullable): a callback to connect to the "toggled" signal of each
* #GtkRadioButton that is created.
* @callback_data: data to pass to the @callback.
* @callback_data_destroy: Destroy function for @callback_data.
* @first_button: (out) (optional) (transfer none):
* Returns the first button in the created group.
*
* Just like gimp_enum_radio_box_new(), this function creates a group
* of radio buttons, but additionally it supports limiting the range
* of available enum values.
*
* Returns: (transfer full): a new vertical #GtkBox holding a group of
* #GtkRadioButtons
*
* Since: 2.4
**/
GtkWidget *
gimp_enum_radio_box_new_with_range (GType enum_type,
gint minimum,
gint maximum,
GCallback callback,
gpointer callback_data,
GDestroyNotify callback_data_destroy,
GtkWidget **first_button)
{
GtkWidget *vbox;
GtkWidget *button;
GEnumClass *enum_class;
GEnumValue *value;
GSList *group = NULL;
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
enum_class = g_type_class_ref (enum_type);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 1);
g_object_weak_ref (G_OBJECT (vbox),
(GWeakNotify) g_type_class_unref, enum_class);
if (callback_data_destroy)
g_object_weak_ref (G_OBJECT (vbox),
(GWeakNotify) callback_data_destroy, callback_data);
if (first_button)
*first_button = NULL;
for (value = enum_class->values; value->value_name; value++)
{
const gchar *desc;
if (value->value < minimum || value->value > maximum)
continue;
desc = gimp_enum_value_get_desc (enum_class, value);
button = gtk_radio_button_new_with_mnemonic (group, desc);
if (first_button && *first_button == NULL)
*first_button = button;
group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
g_object_set_data (G_OBJECT (button), "gimp-item-data",
GINT_TO_POINTER (value->value));
if (callback)
g_signal_connect (button, "toggled",
callback,
callback_data);
}
return vbox;
}
/**
* gimp_enum_radio_frame_new:
* @enum_type: the #GType of an enum.
* @label_widget: (nullable): a #GtkWidget to use as label for the frame
* that will hold the radio box.
* @callback: (nullable): a callback to connect to the "toggled" signal of each
* #GtkRadioButton that is created.
* @callback_data: data to pass to the @callback.
* @callback_data_destroy: Destroy function for @callback_data.
* @first_button: (out) (optional) (transfer none):
* Returns the first button in the created group.
*
* Calls gimp_enum_radio_box_new() and puts the resulting vbox into a
* #GtkFrame.
*
* Returns: (transfer full): a new #GtkFrame holding a group of #GtkRadioButtons.
*
* Since: 2.4
**/
GtkWidget *
gimp_enum_radio_frame_new (GType enum_type,
GtkWidget *label_widget,
GCallback callback,
gpointer callback_data,
GDestroyNotify callback_data_destroy,
GtkWidget **first_button)
{
GtkWidget *frame;
GtkWidget *radio_box;
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
g_return_val_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget),
NULL);
frame = gimp_frame_new (NULL);
if (label_widget)
{
gtk_frame_set_label_widget (GTK_FRAME (frame), label_widget);
gtk_widget_show (label_widget);
}
radio_box = gimp_enum_radio_box_new (enum_type,
callback, callback_data,
callback_data_destroy,
first_button);
gtk_container_add (GTK_CONTAINER (frame), radio_box);
gtk_widget_show (radio_box);
return frame;
}
/**
* gimp_enum_radio_frame_new_with_range:
* @enum_type: the #GType of an enum.
* @minimum: the minimum enum value
* @maximum: the maximum enum value
* @label_widget: (nullable): a widget to put into the frame that will hold the radio box.
* @callback: (nullable): a callback to connect to the "toggled" signal of each
* #GtkRadioButton that is created.
* @callback_data: data to pass to the @callback.
* @callback_data_destroy: Destroy function for @callback_data.
* @first_button: (out) (optional) (transfer none):
* Returns the first button in the created group.
*
* Calls gimp_enum_radio_box_new_with_range() and puts the resulting
* vertical box into a #GtkFrame.
*
* Returns: (transfer full): a new #GtkFrame holding a group of #GtkRadioButtons.
*
* Since: 2.4
**/
GtkWidget *
gimp_enum_radio_frame_new_with_range (GType enum_type,
gint minimum,
gint maximum,
GtkWidget *label_widget,
GCallback callback,
gpointer callback_data,
GDestroyNotify callback_data_destroy,
GtkWidget **first_button)
{
GtkWidget *frame;
GtkWidget *radio_box;
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
g_return_val_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget),
NULL);
frame = gimp_frame_new (NULL);
if (label_widget)
{
gtk_frame_set_label_widget (GTK_FRAME (frame), label_widget);
gtk_widget_show (label_widget);
}
radio_box = gimp_enum_radio_box_new_with_range (enum_type,
minimum,
maximum,
callback, callback_data,
callback_data_destroy,
first_button);
gtk_container_add (GTK_CONTAINER (frame), radio_box);
gtk_widget_show (radio_box);
return frame;
}
/**
* gimp_enum_icon_box_new:
* @enum_type: the #GType of an enum.
* @icon_prefix: the prefix of the group of icon names to use.
* @icon_size: the icon size for the icons
* @callback: (nullable): a callback to connect to the "toggled" signal of each
* #GtkRadioButton that is created.
* @callback_data: data to pass to the @callback.
* @callback_data_destroy: Destroy function for @callback_data.
* @first_button: (out) (optional) (transfer none):
* Returns the first button in the created group.
*
* Creates a horizontal box of radio buttons with named icons. The
* icon name for each icon is created by appending the enum_value's
* nick to the given @icon_prefix.
*
* Returns: (transfer full): a new horizontal #GtkBox holding a group of #GtkRadioButtons.
*
* Since: 2.10
**/
GtkWidget *
gimp_enum_icon_box_new (GType enum_type,
const gchar *icon_prefix,
GtkIconSize icon_size,
GCallback callback,
gpointer callback_data,
GDestroyNotify callback_data_destroy,
GtkWidget **first_button)
{
GEnumClass *enum_class;
GtkWidget *box;
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
enum_class = g_type_class_ref (enum_type);
box = gimp_enum_icon_box_new_with_range (enum_type,
enum_class->minimum,
enum_class->maximum,
icon_prefix, icon_size,
callback, callback_data,
callback_data_destroy,
first_button);
g_type_class_unref (enum_class);
return box;
}
/**
* gimp_enum_icon_box_new_with_range:
* @enum_type: the #GType of an enum.
* @minimum: the minumim enum value
* @maximum: the maximum enum value
* @icon_prefix: the prefix of the group of icon names to use.
* @icon_size: the icon size for the icons
* @callback: (nullable): a callback to connect to the "toggled" signal of each
* #GtkRadioButton that is created.
* @callback_data: data to pass to the @callback.
* @callback_data_destroy: Destroy function for @callback_data.
* @first_button: (out) (optional) (transfer none):
* Returns the first button in the created group.
*
* Just like gimp_enum_icon_box_new(), this function creates a group
* of radio buttons, but additionally it supports limiting the range
* of available enum values.
*
* Returns: (transfer full): a new horizontal #GtkBox holding a group of #GtkRadioButtons.
*
* Since: 2.10
**/
GtkWidget *
gimp_enum_icon_box_new_with_range (GType enum_type,
gint minimum,
gint maximum,
const gchar *icon_prefix,
GtkIconSize icon_size,
GCallback callback,
gpointer callback_data,
GDestroyNotify callback_data_destroy,
GtkWidget **first_button)
{
GtkWidget *hbox;
GtkWidget *button;
GtkWidget *image;
GEnumClass *enum_class;
GEnumValue *value;
gchar *icon_name;
GSList *group = NULL;
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
g_return_val_if_fail (icon_prefix != NULL, NULL);
enum_class = g_type_class_ref (enum_type);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
g_object_weak_ref (G_OBJECT (hbox),
(GWeakNotify) g_type_class_unref, enum_class);
if (callback_data_destroy)
g_object_weak_ref (G_OBJECT (hbox),
(GWeakNotify) callback_data_destroy, callback_data);
if (first_button)
*first_button = NULL;
for (value = enum_class->values; value->value_name; value++)
{
if (value->value < minimum || value->value > maximum)
continue;
button = gtk_radio_button_new (group);
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE);
if (first_button && *first_button == NULL)
*first_button = button;
icon_name = g_strconcat (icon_prefix, "-", value->value_nick, NULL);
image = gtk_image_new_from_icon_name (icon_name, icon_size);
g_free (icon_name);
if (image)
{
gtk_container_add (GTK_CONTAINER (button), image);
gtk_widget_show (image);
}
gimp_help_set_help_data (button,
gimp_enum_value_get_desc (enum_class, value),
NULL);
group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
g_object_set_data (G_OBJECT (button), "gimp-item-data",
GINT_TO_POINTER (value->value));
if (callback)
g_signal_connect (button, "toggled",
callback,
callback_data);
}
return hbox;
}
/**
* gimp_enum_icon_box_set_child_padding:
* @icon_box: an icon box widget
* @xpad: horizontal padding
* @ypad: vertical padding
*
* Sets the padding of all buttons in a box created by
* gimp_enum_icon_box_new().
*
* Since: 2.10
**/
void
gimp_enum_icon_box_set_child_padding (GtkWidget *icon_box,
gint xpad,
gint ypad)
{
GList *children;
GList *list;
g_return_if_fail (GTK_IS_CONTAINER (icon_box));
children = gtk_container_get_children (GTK_CONTAINER (icon_box));
for (list = children; list; list = g_list_next (list))
{
GtkWidget *child = gtk_bin_get_child (GTK_BIN (list->data));
gint start, end;
gint top, bottom;
g_object_get (child,
"margin-start", &start,
"margin-end", &end,
"margin-top", &top,
"margin-bottom", &bottom,
NULL);
g_object_set (child,
"margin-start", xpad < 0 ? start : xpad,
"margin-end", xpad < 0 ? end : xpad,
"margin-top", ypad < 0 ? top : ypad,
"margin-bottom", ypad < 0 ? bottom : ypad,
NULL);
}
g_list_free (children);
}
/**
* gimp_enum_icon_box_set_icon_size:
* @icon_box: an icon box widget
* @icon_size: the #GtkIconSize enum
*
* Sets the icon size of all buttons in a box created by
* gimp_enum_icon_box_new().
*
* Since: 3.0
**/
void
gimp_enum_icon_box_set_icon_size (GtkWidget *icon_box,
GtkIconSize icon_size)
{
GList *children;
GList *list;
g_return_if_fail (GTK_IS_CONTAINER (icon_box));
children = gtk_container_get_children (GTK_CONTAINER (icon_box));
for (list = children; list; list = g_list_next (list))
{
GtkWidget *child = gtk_bin_get_child (GTK_BIN (list->data));
g_object_set (child,
"icon-size", icon_size,
NULL);
}
g_list_free (children);
}

View file

@ -0,0 +1,84 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpenumwidgets.h
* Copyright (C) 2002-2004 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_ENUM_WIDGETS_H__
#define __GIMP_ENUM_WIDGETS_H__
G_BEGIN_DECLS
GtkWidget * gimp_enum_radio_box_new (GType enum_type,
GCallback callback,
gpointer callback_data,
GDestroyNotify callback_data_destroy,
GtkWidget **first_button);
GtkWidget * gimp_enum_radio_box_new_with_range (GType enum_type,
gint minimum,
gint maximum,
GCallback callback,
gpointer callback_data,
GDestroyNotify callback_data_destroy,
GtkWidget **first_button);
GtkWidget * gimp_enum_radio_frame_new (GType enum_type,
GtkWidget *label_widget,
GCallback callback,
gpointer callback_data,
GDestroyNotify callback_data_destroy,
GtkWidget **first_button);
GtkWidget * gimp_enum_radio_frame_new_with_range (GType enum_type,
gint minimum,
gint maximum,
GtkWidget *label_widget,
GCallback callback,
gpointer callback_data,
GDestroyNotify callback_data_destroy,
GtkWidget **first_button);
GtkWidget * gimp_enum_icon_box_new (GType enum_type,
const gchar *icon_prefix,
GtkIconSize icon_size,
GCallback callback,
gpointer callback_data,
GDestroyNotify callback_data_destroy,
GtkWidget **first_button);
GtkWidget * gimp_enum_icon_box_new_with_range (GType enum_type,
gint minimum,
gint maximum,
const gchar *icon_prefix,
GtkIconSize icon_size,
GCallback callback,
gpointer callback_data,
GDestroyNotify callback_data_destroy,
GtkWidget **first_button);
void gimp_enum_icon_box_set_child_padding (GtkWidget *icon_box,
gint xpad,
gint ypad);
void gimp_enum_icon_box_set_icon_size (GtkWidget *icon_box,
GtkIconSize icon_size);
G_END_DECLS
#endif /* __GIMP_ENUM_WIDGETS_H__ */

View file

@ -0,0 +1,758 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpfilechooser.h
* Copyright (C) 2025 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "gimpicons.h"
#include "gimpwidgetstypes.h"
#include "gimpfilechooser.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimpfilechooser
* @title: GimpFileChooser
* @short_description: A widget allowing to select a file.
*
* The chooser contains an optional label and other interface allowing
* to select files for different use cases.
*
* Since: 3.0
**/
enum
{
PROP_0,
PROP_ACTION,
PROP_LABEL,
PROP_TITLE,
PROP_FILE,
N_PROPS
};
struct _GimpFileChooser
{
GtkBox parent_instance;
GFile *file;
gchar *title;
gchar *label;
GimpFileChooserAction action;
GtkWidget *label_widget;
GtkWidget *button;
GtkWidget *entry;
GtkWidget *dialog;
gboolean invalid_file;
};
static void gimp_file_chooser_constructed (GObject *object);
static void gimp_file_chooser_finalize (GObject *object);
static void gimp_file_chooser_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_file_chooser_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_file_chooser_button_selection_changed (GtkFileChooser *widget,
GimpFileChooser *chooser);
static void gimp_file_chooser_dialog_response (GtkDialog *dialog,
gint response_id,
GimpFileChooser *chooser);
static void gimp_file_chooser_button_clicked (GtkButton *button,
GimpFileChooser *chooser);
static void gimp_file_chooser_entry_text_notify (GtkEntry *entry,
const GParamSpec *pspec,
GimpFileChooser *chooser);
static GParamSpec *file_button_props[N_PROPS] = { NULL, };
/* Note: I initially wanted to implement the GtkFileChooser interface,
* but it looks like GTK made GtkFileChooserIface private. So it can't
* be implemented outside of core GTK widgets.
*/
G_DEFINE_FINAL_TYPE (GimpFileChooser, gimp_file_chooser, GTK_TYPE_BOX)
static void
gimp_file_chooser_class_init (GimpFileChooserClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = gimp_file_chooser_constructed;
object_class->finalize = gimp_file_chooser_finalize;
object_class->set_property = gimp_file_chooser_set_property;
object_class->get_property = gimp_file_chooser_get_property;
/**
* GimpFileChooser:action:
*
* The action determining the chooser UI.
*
* Since: 3.0
*/
file_button_props[PROP_ACTION] =
g_param_spec_enum ("action",
"Action",
"The action determining the chooser UI",
GIMP_TYPE_FILE_CHOOSER_ACTION,
GTK_FILE_CHOOSER_ACTION_OPEN,
GIMP_PARAM_READWRITE |
G_PARAM_EXPLICIT_NOTIFY);
/**
* GimpFileChooser:label:
*
* Label text with mnemonic.
*
* Since: 3.0
*/
file_button_props[PROP_LABEL] =
g_param_spec_string ("label",
"Label",
"The label to be used next to the button",
NULL,
GIMP_PARAM_READWRITE |
G_PARAM_EXPLICIT_NOTIFY);
/**
* GimpFileChooser:title:
*
* The title to be used for the file selection popup dialog.
* If %NULL, the "label" property is used instead.
*
* Since: 3.0
*/
file_button_props[PROP_TITLE] =
g_param_spec_string ("title",
"Title",
"The title to be used for the file selection popup dialog "
"and as placeholder text in file entry.",
"File Selection",
GIMP_PARAM_READWRITE |
G_PARAM_EXPLICIT_NOTIFY);
/**
* GimpFileChooser:file:
*
* The currently selected file.
*
* Since: 3.0
*/
file_button_props[PROP_FILE] =
gimp_param_spec_file ("file", "File",
"The currently selected file",
GIMP_FILE_CHOOSER_ACTION_ANY,
TRUE, NULL,
GIMP_PARAM_READWRITE |
G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class,
N_PROPS, file_button_props);
}
static void
gimp_file_chooser_init (GimpFileChooser *chooser)
{
gtk_orientable_set_orientation (GTK_ORIENTABLE (chooser),
GTK_ORIENTATION_HORIZONTAL);
gtk_box_set_spacing (GTK_BOX (chooser), 6);
chooser->action = GIMP_FILE_CHOOSER_ACTION_OPEN;
chooser->button = NULL;
chooser->entry = NULL;
chooser->dialog = NULL;
chooser->file = NULL;
chooser->invalid_file = FALSE;
}
static void
gimp_file_chooser_constructed (GObject *object)
{
GimpFileChooser *chooser = GIMP_FILE_CHOOSER (object);
chooser->label_widget = gtk_label_new (NULL);
gtk_box_pack_start (GTK_BOX (chooser), chooser->label_widget, FALSE, FALSE, 0);
if (chooser->label)
gtk_label_set_text_with_mnemonic (GTK_LABEL (chooser->label_widget), chooser->label);
gtk_label_set_xalign (GTK_LABEL (chooser->label_widget), 0.0);
gtk_widget_set_visible (chooser->label_widget, chooser->label != NULL);
gimp_file_chooser_set_action (chooser, chooser->action);
G_OBJECT_CLASS (gimp_file_chooser_parent_class)->constructed (object);
}
static void
gimp_file_chooser_finalize (GObject *object)
{
GimpFileChooser *chooser = GIMP_FILE_CHOOSER (object);
g_clear_pointer (&chooser->title, g_free);
g_clear_pointer (&chooser->label, g_free);
g_clear_object (&chooser->file);
G_OBJECT_CLASS (gimp_file_chooser_parent_class)->finalize (object);
}
static void
gimp_file_chooser_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpFileChooser *chooser = GIMP_FILE_CHOOSER (object);
switch (property_id)
{
case PROP_ACTION:
gimp_file_chooser_set_action (chooser, g_value_get_enum (value));
break;
case PROP_LABEL:
gimp_file_chooser_set_label (chooser, g_value_get_string (value));
break;
case PROP_TITLE:
gimp_file_chooser_set_title (chooser, g_value_get_string (value));
break;
case PROP_FILE:
gimp_file_chooser_set_file (chooser, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_file_chooser_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpFileChooser *chooser = GIMP_FILE_CHOOSER (object);
switch (property_id)
{
case PROP_ACTION:
g_value_set_enum (value, chooser->action);
break;
case PROP_LABEL:
g_value_set_string (value, chooser->label);
break;
case PROP_TITLE:
g_value_set_string (value, chooser->title);
break;
case PROP_FILE:
g_value_set_object (value, chooser->file);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* gimp_file_chooser_new:
* @action: the action determining the UI created for this widget.
* @label: (nullable): Label or %NULL for no label.
* @title: (nullable): Title of the dialog to use or %NULL to reuse @label.
* @file: (nullable): Initial file.
*
* Creates a new #GtkWidget that lets a user choose a file according to
* @action.
*
* [enum@Gimp.FileChooserAction.ANY] is not a valid value for @action.
*
* Returns: A [class@GimpUi.FileChooser].
*
* Since: 3.0
*/
GtkWidget *
gimp_file_chooser_new (GimpFileChooserAction action,
const gchar *label,
const gchar *title,
GFile *file)
{
GtkWidget *chooser;
g_return_val_if_fail (action != GIMP_FILE_CHOOSER_ACTION_ANY, NULL);
g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
chooser = g_object_new (GIMP_TYPE_FILE_CHOOSER,
"action", action,
"label", label,
"title", title,
"file", file,
NULL);
return chooser;
}
/**
* gimp_file_chooser_get_action:
* @chooser: A #GimpFileChooser
*
* Gets the current action.
*
* Returns: the action which determined the UI of @chooser.
*
* Since: 3.0
*/
GimpFileChooserAction
gimp_file_chooser_get_action (GimpFileChooser *chooser)
{
g_return_val_if_fail (GIMP_IS_FILE_CHOOSER (chooser), GIMP_FILE_CHOOSER_ACTION_ANY);
return chooser->action;
}
/**
* gimp_file_chooser_set_action:
* @chooser: A #GimpFileChooser
* @action: Action to set.
*
* Changes how @chooser is set to select a file. It may completely
* change the internal widget structure so you should not depend on a
* specific widget composition.
*
* Warning: with GTK deprecations, we may have soon to change the
* internal implementation. So this is all the more reason for you not
* to rely on specific child widgets being present (e.g.: we use
* currently [class@Gtk.FileChooserButton] internally but it was removed
* in GTK4 so we will eventually replace it by custom code). We will
* also likely move to native file dialogs at some point.
*
* [enum@Gimp.FileChooserAction.ANY] is not a valid value for @action.
*
* Since: 3.0
*/
void
gimp_file_chooser_set_action (GimpFileChooser *chooser,
GimpFileChooserAction action)
{
gchar *uri_path = NULL;
g_return_if_fail (GIMP_IS_FILE_CHOOSER (chooser));
g_return_if_fail (action != GIMP_FILE_CHOOSER_ACTION_ANY);
if (chooser->button)
gtk_widget_destroy (chooser->button);
if (chooser->entry)
gtk_widget_destroy (chooser->entry);
if (chooser->dialog)
gtk_widget_destroy (chooser->dialog);
chooser->button = NULL;
chooser->entry = NULL;
chooser->dialog = NULL;
switch (action)
{
case GIMP_FILE_CHOOSER_ACTION_OPEN:
case GIMP_FILE_CHOOSER_ACTION_SELECT_FOLDER:
chooser->button = gtk_file_chooser_button_new (chooser->title, (GtkFileChooserAction) action);
gtk_box_pack_start (GTK_BOX (chooser), chooser->button, FALSE, FALSE, 0);
g_signal_connect (chooser->button, "selection-changed",
G_CALLBACK (gimp_file_chooser_button_selection_changed),
chooser);
gtk_widget_set_visible (chooser->button, TRUE);
gtk_label_set_mnemonic_widget (GTK_LABEL (chooser->label_widget), chooser->button);
break;
case GIMP_FILE_CHOOSER_ACTION_SAVE:
case GIMP_FILE_CHOOSER_ACTION_CREATE_FOLDER:
chooser->entry = gtk_entry_new ();
gtk_box_pack_start (GTK_BOX (chooser), chooser->entry, TRUE, TRUE, 0);
if (chooser->file)
{
uri_path = g_file_get_path (chooser->file);
if (! uri_path)
uri_path = g_file_get_uri (chooser->file);
}
if (! uri_path)
uri_path = g_strdup ("");
gtk_entry_set_text (GTK_ENTRY (chooser->entry), uri_path);
g_signal_connect (chooser->entry, "notify::text",
G_CALLBACK (gimp_file_chooser_entry_text_notify),
chooser);
gtk_entry_set_placeholder_text (GTK_ENTRY (chooser->entry), chooser->title);
gtk_widget_set_visible (chooser->entry, TRUE);
chooser->button = gtk_button_new_from_icon_name (GIMP_ICON_FILE_MANAGER, GTK_ICON_SIZE_SMALL_TOOLBAR);
gtk_box_pack_start (GTK_BOX (chooser), chooser->button, FALSE, FALSE, 0);
gtk_widget_set_visible (chooser->button, TRUE);
g_signal_connect (chooser->button, "clicked",
G_CALLBACK (gimp_file_chooser_button_clicked),
chooser);
gtk_label_set_mnemonic_widget (GTK_LABEL (chooser->label_widget), chooser->entry);
break;
case GIMP_FILE_CHOOSER_ACTION_ANY:
g_return_if_reached ();
}
chooser->action = action;
gimp_param_spec_file_set_action (file_button_props[PROP_FILE], action);
g_object_notify_by_pspec (G_OBJECT (chooser), file_button_props[PROP_ACTION]);
g_free (uri_path);
}
/**
* gimp_file_chooser_get_file:
* @chooser: A #GimpFileChooser
*
* Gets the currently selected file.
*
* Returns: (transfer none): an internal copy of the file which must not be freed.
*
* Since: 3.0
*/
GFile *
gimp_file_chooser_get_file (GimpFileChooser *chooser)
{
g_return_val_if_fail (GIMP_IS_FILE_CHOOSER (chooser), NULL);
return chooser->file;
}
/**
* gimp_file_chooser_set_file:
* @chooser: A #GimpFileChooser
* @file: File to set.
*
* Sets the currently selected file.
*
* Since: 3.0
*/
void
gimp_file_chooser_set_file (GimpFileChooser *chooser,
GFile *file)
{
GFile *current_file = NULL;
gchar *uri_path = NULL;
g_return_if_fail (GIMP_IS_FILE_CHOOSER (chooser));
g_return_if_fail (file == NULL || G_IS_FILE (file));
current_file = chooser->file ? g_object_ref (chooser->file) : NULL;
g_clear_object (&chooser->file);
chooser->file = file ? g_object_ref (file) : NULL;
if ((current_file != NULL && file == NULL) ||
(file != NULL && current_file == NULL) ||
(file != NULL && current_file != NULL && ! g_file_equal (file, current_file)))
{
switch (chooser->action)
{
case GTK_FILE_CHOOSER_ACTION_OPEN:
case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
gtk_file_chooser_set_file (GTK_FILE_CHOOSER (chooser->button), file, NULL);
break;
case GTK_FILE_CHOOSER_ACTION_SAVE:
case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
if (chooser->dialog)
gtk_file_chooser_set_file (GTK_FILE_CHOOSER (chooser->dialog), file, NULL);
if (file)
{
uri_path = g_file_get_path (file);
if (! uri_path)
uri_path = g_file_get_uri (file);
}
if (! uri_path)
uri_path = g_strdup ("");
g_signal_handlers_block_by_func (chooser->entry,
G_CALLBACK (gimp_file_chooser_entry_text_notify),
chooser);
if (! chooser->invalid_file)
gtk_entry_set_text (GTK_ENTRY (chooser->entry), uri_path);
g_signal_handlers_unblock_by_func (chooser->entry,
G_CALLBACK (gimp_file_chooser_entry_text_notify),
chooser);
break;
case GIMP_FILE_CHOOSER_ACTION_ANY:
g_return_if_reached ();
}
}
g_object_notify_by_pspec (G_OBJECT (chooser), file_button_props[PROP_FILE]);
g_clear_object (&current_file);
g_free (uri_path);
}
/**
* gimp_file_chooser_get_label:
* @chooser: A #GimpFileChooser
*
* Gets the current label text. A %NULL label means that the label
* widget is hidden.
*
* Note: the label text may contain a mnemonic.
*
* Returns: (nullable): the label set.
*
* Since: 3.0
*/
const gchar *
gimp_file_chooser_get_label (GimpFileChooser *chooser)
{
g_return_val_if_fail (GIMP_IS_FILE_CHOOSER (chooser), NULL);
return chooser->label;
}
/**
* gimp_file_chooser_set_label:
* @chooser: A [class@FileChooser].
* @text: (nullable): Label text.
*
* Set the label text with mnemonic.
*
* Setting a %NULL label text will hide the label widget.
*
* Since: 3.0
*/
void
gimp_file_chooser_set_label (GimpFileChooser *chooser,
const gchar *text)
{
g_return_if_fail (GIMP_IS_FILE_CHOOSER (chooser));
g_free (chooser->label);
chooser->label = g_strdup (text);
gtk_widget_set_visible (chooser->label_widget, text != NULL);
if (chooser->label_widget)
{
if (text != NULL)
gtk_label_set_text_with_mnemonic (GTK_LABEL (chooser->label_widget), text);
gtk_widget_set_visible (chooser->label_widget, text != NULL);
}
g_object_notify_by_pspec (G_OBJECT (chooser), file_button_props[PROP_LABEL]);
}
/**
* gimp_file_chooser_get_title:
* @chooser: A #GimpFileChooser
*
* Gets the text currently used for the file dialog's title and for
* entry's placeholder text.
*
* A %NULL value means that the file dialog uses default title and the
* entry has no placeholder text.
*
* Returns: (nullable): the text used for the title of @chooser's dialog.
*
* Since: 3.0
*/
const gchar *
gimp_file_chooser_get_title (GimpFileChooser *chooser)
{
g_return_val_if_fail (GIMP_IS_FILE_CHOOSER (chooser), NULL);
return chooser->title;
}
/**
* gimp_file_chooser_set_title:
* @chooser: A [class@FileChooser].
* @text: (nullable): Dialog's title text.
*
* Set the text to be used for the file dialog's title and for entry's
* placeholder text.
*
* Setting a %NULL title @text will mean that the file dialog will use a
* generic title and there will be no placeholder text in the entry.
*
* Since: 3.0
*/
void
gimp_file_chooser_set_title (GimpFileChooser *chooser,
const gchar *text)
{
g_return_if_fail (GIMP_IS_FILE_CHOOSER (chooser));
g_free (chooser->title);
chooser->title = g_strdup (text);
if (chooser->dialog)
gtk_window_set_title (GTK_WINDOW (chooser->dialog), chooser->title);
if (chooser->entry)
gtk_entry_set_placeholder_text (GTK_ENTRY (chooser->entry), chooser->title);
g_object_notify_by_pspec (G_OBJECT (chooser), file_button_props[PROP_TITLE]);
}
/**
* gimp_file_chooser_get_label_widget:
* @chooser: A [class@FileChooser].
*
* Returns the label widget. This can be useful for instance when
* aligning dialog's widgets with a [class@Gtk.SizeGroup].
*
* Returns: (transfer none): the [class@Gtk.Widget] showing the label text.
*
* Since: 3.0
*/
GtkWidget *
gimp_file_chooser_get_label_widget (GimpFileChooser *chooser)
{
g_return_val_if_fail (GIMP_IS_FILE_CHOOSER (chooser), NULL);
return chooser->label_widget;
}
/* Private Functions */
static void
gimp_file_chooser_button_selection_changed (GtkFileChooser *widget,
GimpFileChooser *chooser)
{
GFile *file;
file = gtk_file_chooser_get_file (widget);
g_signal_handlers_block_by_func (chooser->button,
G_CALLBACK (gimp_file_chooser_button_selection_changed),
chooser);
gimp_file_chooser_set_file (chooser, file);
g_signal_handlers_unblock_by_func (chooser->button,
G_CALLBACK (gimp_file_chooser_button_selection_changed),
chooser);
g_clear_object (&file);
}
static void
gimp_file_chooser_dialog_response (GtkDialog *dialog,
gint response_id,
GimpFileChooser *chooser)
{
GFile *file = NULL;
switch (response_id)
{
case GTK_RESPONSE_OK:
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
gimp_file_chooser_set_file (chooser, file);
break;
case GTK_RESPONSE_CANCEL:
case GTK_RESPONSE_DELETE_EVENT:
default:
break;
}
gtk_widget_destroy (GTK_WIDGET (dialog));
chooser->dialog = NULL;
g_clear_object (&file);
}
static void
gimp_file_chooser_button_clicked (GtkButton *button,
GimpFileChooser *chooser)
{
GtkWidget *toplevel;
if (chooser->dialog)
{
gtk_window_present (GTK_WINDOW (chooser->dialog));
return;
}
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (chooser));
chooser->dialog = gtk_file_chooser_dialog_new ((const gchar *) chooser->title,
GTK_WINDOW (toplevel),
(GtkFileChooserAction) chooser->action,
_("_OK"), GTK_RESPONSE_OK,
_("_Cancel"), GTK_RESPONSE_CANCEL,
NULL);
if (chooser->file)
gtk_file_chooser_set_file (GTK_FILE_CHOOSER (chooser->dialog), chooser->file, NULL);
g_signal_connect (chooser->dialog, "response",
G_CALLBACK (gimp_file_chooser_dialog_response),
chooser);
gtk_widget_set_visible (chooser->dialog, TRUE);
}
static void
gimp_file_chooser_entry_text_notify (GtkEntry *entry,
const GParamSpec *pspec,
GimpFileChooser *chooser)
{
GParamSpec *chooser_pspec;
GFile *file;
GValue value = G_VALUE_INIT;
chooser_pspec = file_button_props[PROP_FILE];
file = g_file_parse_name (gtk_entry_get_text (entry));
g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (chooser_pspec));
g_value_set_object (&value, G_OBJECT (file));
if (! g_param_value_validate (chooser_pspec, &value))
{
gimp_file_chooser_set_file (chooser, file);
gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
}
else
{
chooser->invalid_file = TRUE;
gimp_file_chooser_set_file (chooser, NULL);
chooser->invalid_file = FALSE;
/* XXX When not validating, I initially wanted to set the entry
* borders to the error_color from the current theme. But I
* settled with a simpler icon, which works well too IMO.
*/
gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, GIMP_ICON_WILBER_EEK);
}
g_value_unset (&value);
g_clear_object (&file);
}

View file

@ -0,0 +1,61 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpfilechooser.c
* Copyright (C) 2025 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_FILE_CHOOSER_H__
#define __GIMP_FILE_CHOOSER_H__
G_BEGIN_DECLS
#define GIMP_TYPE_FILE_CHOOSER (gimp_file_chooser_get_type ())
G_DECLARE_FINAL_TYPE (GimpFileChooser, gimp_file_chooser, GIMP, FILE_CHOOSER, GtkBox)
GtkWidget * gimp_file_chooser_new (GimpFileChooserAction action,
const gchar *label,
const gchar *title,
GFile *file);
GimpFileChooserAction gimp_file_chooser_get_action (GimpFileChooser *chooser);
void gimp_file_chooser_set_action (GimpFileChooser *chooser,
GimpFileChooserAction action);
GFile * gimp_file_chooser_get_file (GimpFileChooser *chooser);
void gimp_file_chooser_set_file (GimpFileChooser *chooser,
GFile *file);
const gchar * gimp_file_chooser_get_label (GimpFileChooser *chooser);
void gimp_file_chooser_set_label (GimpFileChooser *chooser,
const gchar *text);
const gchar * gimp_file_chooser_get_title (GimpFileChooser *chooser);
void gimp_file_chooser_set_title (GimpFileChooser *chooser,
const gchar *text);
GtkWidget * gimp_file_chooser_get_label_widget (GimpFileChooser *chooser);
G_END_DECLS
#endif /* __GIMP_FILE_CHOOSER_H__ */

View file

@ -0,0 +1,529 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpfileentry.c
* Copyright (C) 1999-2004 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "gimpwidgetstypes.h"
#include "gimpdialog.h"
#undef GIMP_DISABLE_DEPRECATED
#include "gimpfileentry.h"
#include "gimphelpui.h"
#include "gimpicons.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimpfileentry
* @title: GimpFileEntry
* @short_description: Widget for entering a filename.
* @see_also: #GimpPathEditor
*
* This widget is used to enter filenames or directories.
*
* There is a #GtkEntry for entering the filename manually and a "..."
* button which will pop up a #GtkFileChooserDialog.
*
* You can restrict the #GimpFileEntry to directories. In this
* case the filename listbox of the #GtkFileChooser dialog will be
* set to directory mode.
*
* If you specify @check_valid as %TRUE in _gimp_file_entry_new() the
* entered filename will be checked for validity and a pixmap will be
* shown which indicates if the file exists or not.
*
* Whenever the user changes the filename, the "filename_changed"
* signal will be emitted.
**/
enum
{
FILENAME_CHANGED,
LAST_SIGNAL
};
struct _GimpFileEntry
{
GtkBox parent_instance;
GtkWidget *file_exists;
GtkWidget *entry;
GtkWidget *browse_button;
GtkWidget *file_dialog;
gchar *title;
gboolean dir_only;
gboolean check_valid;
};
static void gimp_file_entry_dispose (GObject *object);
static void gimp_file_entry_entry_changed (GtkWidget *widget,
GtkWidget *button);
static void gimp_file_entry_entry_activate (GtkWidget *widget,
GimpFileEntry *entry);
static gint gimp_file_entry_entry_focus_out (GtkWidget *widget,
GdkEvent *event,
GimpFileEntry *entry);
static void gimp_file_entry_file_manager_clicked (GtkWidget *widget,
GimpFileEntry *entry);
static void gimp_file_entry_browse_clicked (GtkWidget *widget,
GimpFileEntry *entry);
static void gimp_file_entry_check_filename (GimpFileEntry *entry);
G_DEFINE_TYPE (GimpFileEntry, _gimp_file_entry, GTK_TYPE_BOX)
#define parent_class _gimp_file_entry_parent_class
static guint gimp_file_entry_signals[LAST_SIGNAL] = { 0 };
static void
_gimp_file_entry_class_init (GimpFileEntryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
/**
* GimpFileEntry::filename-changed:
*
* This signal is emitted whenever the user changes the filename.
**/
gimp_file_entry_signals[FILENAME_CHANGED] =
g_signal_new ("filename-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->dispose = gimp_file_entry_dispose;
}
static void
_gimp_file_entry_init (GimpFileEntry *entry)
{
GtkWidget *image;
GtkWidget *button;
entry->title = NULL;
entry->file_dialog = NULL;
entry->check_valid = FALSE;
entry->file_exists = NULL;
gtk_orientable_set_orientation (GTK_ORIENTABLE (entry),
GTK_ORIENTATION_HORIZONTAL);
gtk_box_set_spacing (GTK_BOX (entry), 4);
gtk_box_set_homogeneous (GTK_BOX (entry), FALSE);
button = gtk_button_new ();
gtk_box_pack_end (GTK_BOX (entry), button, FALSE, FALSE, 0);
gtk_widget_show (button);
gtk_widget_set_sensitive (button, FALSE);
image = gtk_image_new_from_icon_name (GIMP_ICON_FILE_MANAGER,
GTK_ICON_SIZE_BUTTON);
gtk_container_add (GTK_CONTAINER (button), image);
gtk_widget_show (image);
g_signal_connect (button, "clicked",
G_CALLBACK (gimp_file_entry_file_manager_clicked),
entry);
gimp_help_set_help_data (button,
_("Show file location in the file manager"),
NULL);
entry->browse_button = gtk_button_new ();
gtk_box_pack_end (GTK_BOX (entry), entry->browse_button, FALSE, FALSE, 0);
gtk_widget_show (entry->browse_button);
image = gtk_image_new_from_icon_name (GIMP_ICON_DOCUMENT_OPEN,
GTK_ICON_SIZE_BUTTON);
gtk_container_add (GTK_CONTAINER (entry->browse_button), image);
gtk_widget_show (image);
g_signal_connect (entry->browse_button, "clicked",
G_CALLBACK (gimp_file_entry_browse_clicked),
entry);
entry->entry = gtk_entry_new ();
gtk_box_pack_end (GTK_BOX (entry), entry->entry, TRUE, TRUE, 0);
gtk_widget_show (entry->entry);
g_signal_connect (entry->entry, "changed",
G_CALLBACK (gimp_file_entry_entry_changed),
button);
g_signal_connect (entry->entry, "activate",
G_CALLBACK (gimp_file_entry_entry_activate),
entry);
g_signal_connect (entry->entry, "focus-out-event",
G_CALLBACK (gimp_file_entry_entry_focus_out),
entry);
}
static void
gimp_file_entry_dispose (GObject *object)
{
GimpFileEntry *entry = GIMP_FILE_ENTRY (object);
g_clear_pointer (&entry->file_dialog, gtk_widget_destroy);
g_clear_pointer (&entry->title, g_free);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
/**
* _gimp_file_entry_new:
* @title: The title of the #GimpFileEntry dialog.
* @filename: The initial filename.
* @dir_only: %TRUE if the file entry should accept directories only.
* @check_valid: %TRUE if the widget should check if the entered file
* really exists.
*
* You should use #GtkFileChooserButton instead.
*
* Returns: A pointer to the new #GimpFileEntry widget.
**/
GtkWidget *
_gimp_file_entry_new (const gchar *title,
const gchar *filename,
gboolean dir_only,
gboolean check_valid)
{
GimpFileEntry *entry;
entry = g_object_new (GIMP_TYPE_FILE_ENTRY, NULL);
entry->title = g_strdup (title);
entry->dir_only = dir_only;
entry->check_valid = check_valid;
gimp_help_set_help_data (entry->browse_button,
entry->dir_only ?
_("Open a file selector to browse your folders") :
_("Open a file selector to browse your files"),
NULL);
if (check_valid)
{
entry->file_exists = gtk_image_new_from_icon_name ("gtk-no",
GTK_ICON_SIZE_BUTTON);
gtk_box_pack_start (GTK_BOX (entry), entry->file_exists, FALSE, FALSE, 0);
gtk_widget_show (entry->file_exists);
gimp_help_set_help_data (entry->file_exists,
entry->dir_only ?
_("Indicates whether or not the folder exists") :
_("Indicates whether or not the file exists"),
NULL);
}
_gimp_file_entry_set_filename (entry, filename);
return GTK_WIDGET (entry);
}
/**
* _gimp_file_entry_get_filename:
* @entry: The file entry you want to know the filename from.
*
* Note that you have to g_free() the returned string.
*
* Returns: The file or directory the user has entered.
**/
gchar *
_gimp_file_entry_get_filename (GimpFileEntry *entry)
{
gchar *utf8;
gchar *filename;
g_return_val_if_fail (GIMP_IS_FILE_ENTRY (entry), NULL);
utf8 = gtk_editable_get_chars (GTK_EDITABLE (entry->entry), 0, -1);
filename = g_filename_from_utf8 (utf8, -1, NULL, NULL, NULL);
g_free (utf8);
return filename;
}
/**
* _gimp_file_entry_set_filename:
* @entry: The file entry you want to set the filename for.
* @filename: The new filename.
*
* If you specified @check_valid as %TRUE in _gimp_file_entry_new()
* the #GimpFileEntry will immediately check the validity of the file
* name.
**/
void
_gimp_file_entry_set_filename (GimpFileEntry *entry,
const gchar *filename)
{
gchar *utf8;
g_return_if_fail (GIMP_IS_FILE_ENTRY (entry));
if (filename)
utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
else
utf8 = g_strdup ("");
gtk_entry_set_text (GTK_ENTRY (entry->entry), utf8);
g_free (utf8);
/* update everything
*/
gimp_file_entry_entry_activate (entry->entry, entry);
}
/**
* _gimp_file_entry_get_entry:
* @entry: The #GimpFileEntry.
*
* Returns: (transfer none): the #GtkEntry internally used by the
* widget. The object belongs to @entry and should not be
* freed.
**/
GtkWidget *
_gimp_file_entry_get_entry (GimpFileEntry *entry)
{
return entry->entry;
}
/* Private Functions */
static void
gimp_file_entry_entry_changed (GtkWidget *widget,
GtkWidget *button)
{
const gchar *text = gtk_entry_get_text (GTK_ENTRY (widget));
if (text && strlen (text))
gtk_widget_set_sensitive (button, TRUE);
else
gtk_widget_set_sensitive (button, FALSE);
}
static void
gimp_file_entry_entry_activate (GtkWidget *widget,
GimpFileEntry *entry)
{
gchar *utf8;
gchar *filename;
gint len;
/* filenames still need more sanity checking
* (erase double G_DIR_SEPARATORS, ...)
*/
utf8 = gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1);
utf8 = g_strstrip (utf8);
while (((len = strlen (utf8)) > 1) &&
(utf8[len - 1] == G_DIR_SEPARATOR))
utf8[len - 1] = '\0';
filename = g_filename_from_utf8 (utf8, -1, NULL, NULL, NULL);
g_signal_handlers_block_by_func (entry->entry,
gimp_file_entry_entry_activate,
entry);
gtk_entry_set_text (GTK_ENTRY (entry->entry), utf8);
g_signal_handlers_unblock_by_func (entry->entry,
gimp_file_entry_entry_activate,
entry);
if (entry->file_dialog)
gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (entry->file_dialog),
filename);
g_free (filename);
g_free (utf8);
gimp_file_entry_check_filename (entry);
gtk_editable_set_position (GTK_EDITABLE (entry->entry), -1);
g_signal_emit (entry, gimp_file_entry_signals[FILENAME_CHANGED], 0);
}
static gboolean
gimp_file_entry_entry_focus_out (GtkWidget *widget,
GdkEvent *event,
GimpFileEntry *entry)
{
gimp_file_entry_entry_activate (widget, entry);
return FALSE;
}
/* local callback of gimp_file_entry_browse_clicked() */
static void
gimp_file_entry_chooser_response (GtkWidget *dialog,
gint response_id,
GimpFileEntry *entry)
{
if (response_id == GTK_RESPONSE_OK)
{
gchar *filename;
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
_gimp_file_entry_set_filename (entry, filename);
g_free (filename);
}
gtk_widget_hide (dialog);
}
static void
gimp_file_entry_file_manager_clicked (GtkWidget *widget,
GimpFileEntry *entry)
{
gchar *utf8;
GFile *file;
GError *error = NULL;
utf8 = gtk_editable_get_chars (GTK_EDITABLE (entry->entry), 0, -1);
file = g_file_parse_name (utf8);
g_free (utf8);
if (! gimp_file_show_in_file_manager (file, &error))
{
g_message (_("Can't show file in file manager: %s"),
error->message);
g_clear_error (&error);
}
g_object_unref (file);
}
static void
gimp_file_entry_browse_clicked (GtkWidget *widget,
GimpFileEntry *entry)
{
GtkFileChooser *chooser;
gchar *utf8;
gchar *filename;
utf8 = gtk_editable_get_chars (GTK_EDITABLE (entry->entry), 0, -1);
filename = g_filename_from_utf8 (utf8, -1, NULL, NULL, NULL);
g_free (utf8);
if (! entry->file_dialog)
{
const gchar *title = entry->title;
if (! title)
{
if (entry->dir_only)
title = _("Select Folder");
else
title = _("Select File");
}
entry->file_dialog =
gtk_file_chooser_dialog_new (title, NULL,
entry->dir_only ?
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER :
GTK_FILE_CHOOSER_ACTION_OPEN,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_OK"), GTK_RESPONSE_OK,
NULL);
gimp_dialog_set_alternative_button_order (GTK_DIALOG (entry->file_dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
chooser = GTK_FILE_CHOOSER (entry->file_dialog);
gtk_window_set_position (GTK_WINDOW (chooser), GTK_WIN_POS_MOUSE);
gtk_window_set_role (GTK_WINDOW (chooser),
"gimp-file-entry-file-dialog");
g_signal_connect (chooser, "response",
G_CALLBACK (gimp_file_entry_chooser_response),
entry);
g_signal_connect (chooser, "delete-event",
G_CALLBACK (gtk_true),
NULL);
g_signal_connect_swapped (entry, "unmap",
G_CALLBACK (gtk_widget_hide),
chooser);
}
else
{
chooser = GTK_FILE_CHOOSER (entry->file_dialog);
}
gtk_file_chooser_set_filename (chooser, filename);
g_free (filename);
gtk_window_set_screen (GTK_WINDOW (chooser), gtk_widget_get_screen (widget));
gtk_window_present (GTK_WINDOW (chooser));
}
static void
gimp_file_entry_check_filename (GimpFileEntry *entry)
{
gchar *utf8;
gchar *filename;
gboolean exists;
if (! entry->check_valid || ! entry->file_exists)
return;
utf8 = gtk_editable_get_chars (GTK_EDITABLE (entry->entry), 0, -1);
filename = g_filename_from_utf8 (utf8, -1, NULL, NULL, NULL);
g_free (utf8);
if (entry->dir_only)
exists = g_file_test (filename, G_FILE_TEST_IS_DIR);
else
exists = g_file_test (filename, G_FILE_TEST_IS_REGULAR);
g_free (filename);
gtk_image_set_from_icon_name (GTK_IMAGE (entry->file_exists),
exists ? "gtk-yes" : "gtk-no",
GTK_ICON_SIZE_BUTTON);
}

View file

@ -0,0 +1,66 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpfileentry.h
* Copyright (C) 1999-2004 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#ifndef GIMP_DISABLE_DEPRECATED
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_FILE_ENTRY_H__
#define __GIMP_FILE_ENTRY_H__
G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
/* The whole GimpFileEntry widget was deprecated in 2006 (commit
* 99f979e1189) apparently to be replaced by GtkFileChooserButton, yet
* it is still used in the GimpPathEditor. For GIMP 3.0, we removed the
* header from installed headers and made the class and all functions
* internal, though we still use it.
*
* TODO: eventually we want either this widget fully removed and
* replaced by a generic GTK widget or reimplemented and made public
* again.
*/
#define GIMP_TYPE_FILE_ENTRY (_gimp_file_entry_get_type ())
G_DECLARE_FINAL_TYPE (GimpFileEntry, _gimp_file_entry, GIMP, FILE_ENTRY, GtkBox)
G_GNUC_INTERNAL GtkWidget * _gimp_file_entry_new (const gchar *title,
const gchar *filename,
gboolean dir_only,
gboolean check_valid);
G_GNUC_INTERNAL gchar * _gimp_file_entry_get_filename (GimpFileEntry *entry);
G_GNUC_INTERNAL void _gimp_file_entry_set_filename (GimpFileEntry *entry,
const gchar *filename);
G_GNUC_INTERNAL GtkWidget * _gimp_file_entry_get_entry (GimpFileEntry *entry);
G_END_DECLS
#endif /* __GIMP_FILE_ENTRY_H__ */
#endif /* GIMP_DISABLE_DEPRECATED */

264
libgimpwidgets/gimpframe.c Normal file
View file

@ -0,0 +1,264 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpframe.c
* Copyright (C) 2004 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <babl/babl.h>
#include <gtk/gtk.h>
#include "gimpwidgetstypes.h"
#include "gimpframe.h"
#include "gimpwidgetsutils.h"
/**
* SECTION: gimpframe
* @title: GimpFrame
* @short_description: A widget providing a HIG-compliant subclass
* of #GtkFrame.
*
* A widget providing a HIG-compliant subclass of #GtkFrame.
**/
#define DEFAULT_LABEL_SPACING 6
#define DEFAULT_LABEL_BOLD TRUE
#define GIMP_FRAME_INDENT_KEY "gimp-frame-indent"
#define GIMP_FRAME_IN_EXPANDER_KEY "gimp-frame-in-expander"
static void gimp_frame_style_updated (GtkWidget *widget);
static gboolean gimp_frame_draw (GtkWidget *widget,
cairo_t *cr);
static void gimp_frame_label_widget_notify (GimpFrame *frame);
static void gimp_frame_child_added (GimpFrame *frame,
GtkWidget *child,
gpointer user_data);
static void gimp_frame_apply_margins (GimpFrame *frame);
static gint gimp_frame_get_indent (GimpFrame *frame);
static gint gimp_frame_get_label_spacing (GimpFrame *frame);
G_DEFINE_TYPE (GimpFrame, gimp_frame, GTK_TYPE_FRAME)
#define parent_class gimp_frame_parent_class
static void
gimp_frame_class_init (GimpFrameClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
widget_class->style_updated = gimp_frame_style_updated;
widget_class->draw = gimp_frame_draw;
gtk_widget_class_install_style_property (widget_class,
g_param_spec_boolean ("label-bold",
"Label Bold",
"Whether the frame's label should be bold",
DEFAULT_LABEL_BOLD,
G_PARAM_READABLE));
gtk_widget_class_install_style_property (widget_class,
g_param_spec_int ("label-spacing",
"Label Spacing",
"The spacing between the label and the frame content",
0,
G_MAXINT,
DEFAULT_LABEL_SPACING,
G_PARAM_READABLE));
}
static void
gimp_frame_init (GimpFrame *frame)
{
g_signal_connect (frame, "notify::label-widget",
G_CALLBACK (gimp_frame_label_widget_notify),
NULL);
g_signal_connect (frame, "add",
G_CALLBACK (gimp_frame_child_added),
NULL);
}
static void
gimp_frame_style_updated (GtkWidget *widget)
{
GTK_WIDGET_CLASS (parent_class)->style_updated (widget);
/* font changes invalidate the indentation */
g_object_set_data (G_OBJECT (widget), GIMP_FRAME_INDENT_KEY, NULL);
gimp_frame_label_widget_notify (GIMP_FRAME (widget));
gimp_frame_apply_margins (GIMP_FRAME (widget));
}
static gboolean
gimp_frame_draw (GtkWidget *widget,
cairo_t *cr)
{
GtkWidgetClass *widget_class = g_type_class_peek_parent (parent_class);
return widget_class->draw (widget, cr);
}
static void
gimp_frame_label_widget_notify (GimpFrame *frame)
{
GtkWidget *label_widget = gtk_frame_get_label_widget (GTK_FRAME (frame));
if (label_widget)
{
GtkLabel *label = NULL;
if (GTK_IS_LABEL (label_widget))
{
gfloat xalign, yalign;
label = GTK_LABEL (label_widget);
gtk_frame_get_label_align (GTK_FRAME (frame), &xalign, &yalign);
gtk_label_set_xalign (GTK_LABEL (label), xalign);
gtk_label_set_yalign (GTK_LABEL (label), yalign);
}
else if (GTK_IS_BIN (label_widget))
{
GtkWidget *child = gtk_bin_get_child (GTK_BIN (label_widget));
if (GTK_IS_LABEL (child))
label = GTK_LABEL (child);
}
if (label)
{
gboolean bold;
gtk_widget_style_get (GTK_WIDGET (frame),
"label-bold", &bold,
NULL);
gimp_label_set_attributes (label,
PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
-1);
}
}
}
static void
gimp_frame_child_added (GimpFrame *frame,
GtkWidget *child,
gpointer user_data)
{
gimp_frame_apply_margins (frame);
}
static void
gimp_frame_apply_margins (GimpFrame *frame)
{
GtkWidget *child = gtk_bin_get_child (GTK_BIN (frame));
if (child)
{
gtk_widget_set_margin_start (child, gimp_frame_get_indent (frame));
gtk_widget_set_margin_top (child, gimp_frame_get_label_spacing (frame));
}
}
static gint
gimp_frame_get_indent (GimpFrame *frame)
{
gint width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (frame),
GIMP_FRAME_INDENT_KEY));
if (! width)
{
PangoLayout *layout;
/* the HIG suggests to use four spaces so do just that */
layout = gtk_widget_create_pango_layout (GTK_WIDGET (frame), " ");
pango_layout_get_pixel_size (layout, &width, NULL);
g_object_unref (layout);
g_object_set_data (G_OBJECT (frame),
GIMP_FRAME_INDENT_KEY, GINT_TO_POINTER (width));
}
return width;
}
static gint
gimp_frame_get_label_spacing (GimpFrame *frame)
{
GtkWidget *label_widget = gtk_frame_get_label_widget (GTK_FRAME (frame));
gint spacing = 0;
if ((label_widget && gtk_widget_get_visible (label_widget)) ||
(g_object_get_data (G_OBJECT (frame), GIMP_FRAME_IN_EXPANDER_KEY)))
{
gtk_widget_style_get (GTK_WIDGET (frame),
"label-spacing", &spacing,
NULL);
}
return spacing;
}
/**
* gimp_frame_new:
* @label: (nullable): text to set as the frame's title label (or %NULL for no title)
*
* Creates a #GimpFrame widget. A #GimpFrame is a HIG-compliant
* variant of #GtkFrame. It doesn't render a frame at all but
* otherwise behaves like a frame. The frame's title is rendered in
* bold and the frame content is indented four spaces as suggested by
* the GNOME HIG (see https://developer.gnome.org/hig/stable/).
*
* Returns: a new #GimpFrame widget
*
* Since: 2.2
**/
GtkWidget *
gimp_frame_new (const gchar *label)
{
GtkWidget *frame;
gboolean expander = FALSE;
/* somewhat hackish, should perhaps be an object property of GimpFrame */
if (label && strcmp (label, "<expander>") == 0)
{
expander = TRUE;
label = NULL;
}
frame = g_object_new (GIMP_TYPE_FRAME,
"label", label,
NULL);
if (expander)
g_object_set_data (G_OBJECT (frame),
GIMP_FRAME_IN_EXPANDER_KEY, (gpointer) TRUE);
return frame;
}

View file

@ -0,0 +1,60 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpframe.h
* Copyright (C) 2004 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_FRAME_H__
#define __GIMP_FRAME_H__
G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
#define GIMP_TYPE_FRAME (gimp_frame_get_type ())
G_DECLARE_DERIVABLE_TYPE (GimpFrame, gimp_frame, GIMP, FRAME, GtkFrame)
struct _GimpFrameClass
{
GtkFrameClass parent_class;
/* Padding for future expansion */
void (* _gimp_reserved0) (void);
void (* _gimp_reserved1) (void);
void (* _gimp_reserved2) (void);
void (* _gimp_reserved3) (void);
void (* _gimp_reserved4) (void);
void (* _gimp_reserved5) (void);
void (* _gimp_reserved6) (void);
void (* _gimp_reserved7) (void);
void (* _gimp_reserved8) (void);
void (* _gimp_reserved9) (void);
};
GtkWidget * gimp_frame_new (const gchar *label);
G_END_DECLS
#endif /* __GIMP_FRAME_H__ */

659
libgimpwidgets/gimphelpui.c Normal file
View file

@ -0,0 +1,659 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimphelpui.c
* Copyright (C) 2000-2003 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "gimpwidgets.h"
#include "gimpwidgets-private.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimphelpui
* @title: GimpHelpUI
* @short_description: Functions for setting tooltip and help identifier
* used by the GIMP help system.
*
* Functions for setting tooltip and help identifier used by the GIMP
* help system.
**/
typedef enum
{
GIMP_WIDGET_HELP_TOOLTIP = GTK_WIDGET_HELP_TOOLTIP,
GIMP_WIDGET_HELP_WHATS_THIS = GTK_WIDGET_HELP_WHATS_THIS,
GIMP_WIDGET_HELP_TYPE_HELP = 0xff
} GimpWidgetHelpType;
/* local function prototypes */
static const gchar * gimp_help_get_help_data (GtkWidget *widget,
GtkWidget **help_widget,
gpointer *ret_data);
static gboolean gimp_help_callback (GtkWidget *widget,
GimpWidgetHelpType help_type,
GimpHelpFunc help_func);
static void gimp_help_menu_item_set_tooltip (GtkWidget *widget,
const gchar *tooltip,
const gchar *help_id);
static gboolean gimp_help_menu_item_query_tooltip (GtkWidget *widget,
gint x,
gint y,
gboolean keyboard_mode,
GtkTooltip *tooltip);
static gboolean gimp_context_help_idle_start (gpointer widget);
static gboolean gimp_context_help_button_press (GtkWidget *widget,
GdkEventButton *bevent,
gpointer data);
static gboolean gimp_context_help_key_press (GtkWidget *widget,
GdkEventKey *kevent,
gpointer data);
static gboolean gimp_context_help_idle_show_help (gpointer data);
/* public functions */
/**
* gimp_standard_help_func:
* @help_id: A unique help identifier.
* @help_data: The @help_data passed to gimp_help_connect().
*
* This is the standard GIMP help function which does nothing but calling
* gimp_help(). It is the right function to use in almost all cases.
**/
void
gimp_standard_help_func (const gchar *help_id,
gpointer help_data)
{
if (! _gimp_standard_help_func)
{
g_warning ("%s: you must call gimp_widgets_init() before using "
"the help system", G_STRFUNC);
return;
}
(* _gimp_standard_help_func) (help_id, help_data);
}
/**
* gimp_help_connect:
* @widget: The widget you want to connect the help accelerator for.
* Will be a #GtkWindow in most cases.
* @tooltip: (nullable): The text for this widget's tooltip. For windows, you
* usually want to set %NULL.
* @help_func: The function which will be called if the user presses "F1".
* @help_id: The @help_id which will be passed to @help_func.
* @help_data: The @help_data pointer which will be passed to @help_func.
* @help_data_destroy: Destroy function for @help_data.
*
* Note that this function is automatically called by all libgimp dialog
* constructors. You only have to call it for windows/dialogs you created
* "manually".
*
* Most of the time, what you want to call for non-windows widgets is
* simply [func@GimpUi.help_set_help_data]. Yet if you need to set up an
* @help_func, call `gimp_help_connect` instead. Note that `gimp_help_set_help_data`
* is implied, so you don't have to call it too.
**/
void
gimp_help_connect (GtkWidget *widget,
const gchar *tooltip,
GimpHelpFunc help_func,
const gchar *help_id,
gpointer help_data,
GDestroyNotify help_data_destroy)
{
static gboolean initialized = FALSE;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (help_func != NULL);
/* set up the help signals
*/
if (! initialized)
{
GtkBindingSet *binding_set;
binding_set =
gtk_binding_set_by_class (g_type_class_peek (GTK_TYPE_WIDGET));
gtk_binding_entry_add_signal (binding_set, GDK_KEY_F1, 0,
"show-help", 1,
GTK_TYPE_WIDGET_HELP_TYPE,
GIMP_WIDGET_HELP_TYPE_HELP);
gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_F1, 0,
"show-help", 1,
GTK_TYPE_WIDGET_HELP_TYPE,
GIMP_WIDGET_HELP_TYPE_HELP);
initialized = TRUE;
}
gimp_help_set_help_data (widget, tooltip, help_id);
g_object_set_data_full (G_OBJECT (widget), "gimp-help-data",
help_data, help_data_destroy);
g_signal_connect (widget, "show-help",
G_CALLBACK (gimp_help_callback),
help_func);
gtk_widget_add_events (widget, GDK_BUTTON_PRESS_MASK);
}
/**
* gimp_help_set_help_data:
* @widget: The #GtkWidget you want to set a @tooltip and/or @help_id for.
* @tooltip: (nullable): The text for this widget's tooltip (or %NULL).
* @help_id: The @help_id for the #GtkTipsQuery tooltips inspector.
*
* The reason why we don't use gtk_widget_set_tooltip_text() is that
* elements in the GIMP user interface should, if possible, also have
* a @help_id set for context-sensitive help.
*
* This function can be called with %NULL for @tooltip. Use this feature
* if you want to set a help link for a widget which shouldn't have
* a visible tooltip.
**/
void
gimp_help_set_help_data (GtkWidget *widget,
const gchar *tooltip,
const gchar *help_id)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
gtk_widget_set_tooltip_text (widget, tooltip);
if (GTK_IS_MENU_ITEM (widget))
gimp_help_menu_item_set_tooltip (widget, tooltip, help_id);
g_object_set_qdata (G_OBJECT (widget), GIMP_HELP_ID, (gpointer) help_id);
}
/**
* gimp_help_set_help_data_with_markup:
* @widget: The #GtkWidget you want to set a @tooltip and/or @help_id for.
* @tooltip: The markup for this widget's tooltip (or %NULL).
* @help_id: The @help_id for the #GtkTipsQuery tooltips inspector.
*
* Just like gimp_help_set_help_data(), but supports to pass text
* which is marked up with <link linkend="PangoMarkupFormat">Pango
* text markup language</link>.
*
* Since: 2.6
**/
void
gimp_help_set_help_data_with_markup (GtkWidget *widget,
const gchar *tooltip,
const gchar *help_id)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
gtk_widget_set_tooltip_markup (widget, tooltip);
if (GTK_IS_MENU_ITEM (widget))
gimp_help_menu_item_set_tooltip (widget, tooltip, help_id);
g_object_set_qdata (G_OBJECT (widget), GIMP_HELP_ID, (gpointer) help_id);
}
/**
* gimp_context_help:
* @widget: Any #GtkWidget on the screen.
*
* This function invokes the context help inspector.
*
* The mouse cursor will turn turn into a question mark and the user can
* click on any widget of the application which started the inspector.
*
* If the widget the user clicked on has a @help_id string attached
* (see gimp_help_set_help_data()), the corresponding help page will
* be displayed. Otherwise the help system will ascend the widget hierarchy
* until it finds an attached @help_id string (which should be the
* case at least for every window/dialog).
**/
void
gimp_context_help (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
gimp_help_callback (widget, GIMP_WIDGET_HELP_WHATS_THIS, NULL);
}
/**
* gimp_help_id_quark:
*
* This function returns the #GQuark which should be used as key when
* attaching help IDs to widgets and objects.
*
* Returns: The #GQuark.
*
* Since: 2.2
**/
GQuark
gimp_help_id_quark (void)
{
static GQuark quark = 0;
if (! quark)
quark = g_quark_from_static_string ("gimp-help-id");
return quark;
}
/* private functions */
static const gchar *
gimp_help_get_help_data (GtkWidget *widget,
GtkWidget **help_widget,
gpointer *ret_data)
{
const gchar *help_id = NULL;
gpointer help_data = NULL;
for (; widget; widget = gtk_widget_get_parent (widget))
{
help_id = g_object_get_qdata (G_OBJECT (widget), GIMP_HELP_ID);
help_data = g_object_get_data (G_OBJECT (widget), "gimp-help-data");
if (help_id)
{
if (help_widget)
*help_widget = widget;
if (ret_data)
*ret_data = help_data;
return help_id;
}
}
if (help_widget)
*help_widget = NULL;
if (ret_data)
*ret_data = NULL;
return NULL;
}
static gboolean
gimp_help_callback (GtkWidget *widget,
GimpWidgetHelpType help_type,
GimpHelpFunc help_func)
{
switch (help_type)
{
case GIMP_WIDGET_HELP_TYPE_HELP:
if (help_func)
{
help_func (g_object_get_qdata (G_OBJECT (widget), GIMP_HELP_ID),
g_object_get_data (G_OBJECT (widget), "gimp-help-data"));
}
return TRUE;
case GIMP_WIDGET_HELP_WHATS_THIS:
g_idle_add (gimp_context_help_idle_start, widget);
return TRUE;
default:
break;
}
return FALSE;
}
static void
gimp_help_menu_item_set_tooltip (GtkWidget *widget,
const gchar *tooltip,
const gchar *help_id)
{
g_return_if_fail (GTK_IS_MENU_ITEM (widget));
g_object_set (widget, "has-tooltip", FALSE, NULL);
g_signal_handlers_disconnect_by_func (widget,
gimp_help_menu_item_query_tooltip,
NULL);
if (tooltip && help_id)
g_signal_connect (widget, "query-tooltip",
G_CALLBACK (gimp_help_menu_item_query_tooltip),
NULL);
if (tooltip)
g_object_set (widget, "has-tooltip", TRUE, NULL);
}
static gboolean
gimp_help_menu_item_query_tooltip (GtkWidget *widget,
gint x,
gint y,
gboolean keyboard_mode,
GtkTooltip *tooltip)
{
GtkWidget *vbox;
GtkWidget *label;
gchar *text;
gboolean use_markup = TRUE;
text = gtk_widget_get_tooltip_markup (widget);
if (! text)
{
text = gtk_widget_get_tooltip_text (widget);
use_markup = FALSE;
}
if (! text)
return FALSE;
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
label = gtk_label_new (text);
gtk_label_set_use_markup (GTK_LABEL (label), use_markup);
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
gtk_widget_show (label);
g_free (text);
label = gtk_label_new (_("Press F1 for more help"));
gimp_label_set_attributes (GTK_LABEL (label),
PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
PANGO_ATTR_SCALE, PANGO_SCALE_SMALL,
-1);
gtk_label_set_xalign (GTK_LABEL (label), 1.0);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
gtk_tooltip_set_custom (tooltip, vbox);
return TRUE;
}
/* Do all the actual context help calls in idle functions and check for
* some widget holding a grab before starting the query because strange
* things happen if (1) the help browser pops up while the query has
* grabbed the pointer or (2) the query grabs the pointer while some
* other part of GIMP has grabbed it (e.g. a tool, eek)
*/
static gboolean
gimp_context_help_idle_start (gpointer widget)
{
if (! gtk_grab_get_current ())
{
GdkDisplay *display;
GtkWidget *invisible;
GdkCursor *cursor;
GdkGrabStatus status;
invisible = gtk_invisible_new_for_screen (gtk_widget_get_screen (widget));
gtk_widget_show (invisible);
display = gtk_widget_get_display (invisible);
cursor = gdk_cursor_new_for_display (display, GDK_QUESTION_ARROW);
status = gdk_seat_grab (gdk_display_get_default_seat (display),
gtk_widget_get_window (invisible),
GDK_SEAT_CAPABILITY_ALL, TRUE,
cursor,
NULL, NULL, NULL);
g_object_unref (cursor);
if (status != GDK_GRAB_SUCCESS)
{
gtk_widget_destroy (invisible);
return FALSE;
}
gtk_grab_add (invisible);
g_signal_connect (invisible, "button-press-event",
G_CALLBACK (gimp_context_help_button_press),
NULL);
g_signal_connect (invisible, "key-press-event",
G_CALLBACK (gimp_context_help_key_press),
NULL);
}
return FALSE;
}
/* find widget code shamelessly stolen from gtkinspector */
typedef struct
{
gint x;
gint y;
gboolean found;
gboolean first;
GtkWidget *res_widget;
} FindWidgetData;
static void
find_widget (GtkWidget *widget,
FindWidgetData *data)
{
GtkAllocation new_allocation;
gint x_offset = 0;
gint y_offset = 0;
gtk_widget_get_allocation (widget, &new_allocation);
if (data->found || !gtk_widget_get_mapped (widget))
return;
/* Note that in the following code, we only count the
* position as being inside a WINDOW widget if it is inside
* widget->window; points that are outside of widget->window
* but within the allocation are not counted. This is consistent
* with the way we highlight drag targets.
*/
if (gtk_widget_get_has_window (widget))
{
new_allocation.x = 0;
new_allocation.y = 0;
}
if (gtk_widget_get_parent (widget) && !data->first)
{
GdkWindow *window;
window = gtk_widget_get_window (widget);
while (window != gtk_widget_get_window (gtk_widget_get_parent (widget)))
{
gint tx, ty, twidth, theight;
if (window == NULL)
return;
twidth = gdk_window_get_width (window);
theight = gdk_window_get_height (window);
if (new_allocation.x < 0)
{
new_allocation.width += new_allocation.x;
new_allocation.x = 0;
}
if (new_allocation.y < 0)
{
new_allocation.height += new_allocation.y;
new_allocation.y = 0;
}
if (new_allocation.x + new_allocation.width > twidth)
new_allocation.width = twidth - new_allocation.x;
if (new_allocation.y + new_allocation.height > theight)
new_allocation.height = theight - new_allocation.y;
gdk_window_get_position (window, &tx, &ty);
new_allocation.x += tx;
x_offset += tx;
new_allocation.y += ty;
y_offset += ty;
window = gdk_window_get_parent (window);
}
}
if ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
(data->x < new_allocation.x + new_allocation.width) &&
(data->y < new_allocation.y + new_allocation.height))
{
/* First, check if the drag is in a valid drop site in
* one of our children
*/
if (GTK_IS_CONTAINER (widget))
{
FindWidgetData new_data = *data;
new_data.x -= x_offset;
new_data.y -= y_offset;
new_data.found = FALSE;
new_data.first = FALSE;
gtk_container_forall (GTK_CONTAINER (widget),
(GtkCallback)find_widget,
&new_data);
data->found = new_data.found;
if (data->found)
data->res_widget = new_data.res_widget;
}
/* If not, and this widget is registered as a drop site, check to
* emit "drag_motion" to check if we are actually in
* a drop site.
*/
if (!data->found)
{
data->found = TRUE;
data->res_widget = widget;
}
}
}
static GtkWidget *
find_widget_at_pointer (GdkDevice *device)
{
GtkWidget *widget = NULL;
GdkWindow *pointer_window;
gint x, y;
FindWidgetData data;
pointer_window = gdk_device_get_window_at_position (device, NULL, NULL);
if (pointer_window)
{
gpointer widget_ptr;
gdk_window_get_user_data (pointer_window, &widget_ptr);
widget = widget_ptr;
}
if (widget)
{
gdk_window_get_device_position (gtk_widget_get_window (widget),
device, &x, &y, NULL);
data.x = x;
data.y = y;
data.found = FALSE;
data.first = TRUE;
find_widget (widget, &data);
if (data.found)
return data.res_widget;
return widget;
}
return NULL;
}
static gboolean
gimp_context_help_button_press (GtkWidget *widget,
GdkEventButton *bevent,
gpointer data)
{
GdkDisplay *display = gtk_widget_get_display (widget);
GdkSeat *seat = gdk_display_get_default_seat (display);
GdkDevice *device = gdk_seat_get_pointer (seat);
GtkWidget *event_widget = find_widget_at_pointer (device);
if (event_widget && bevent->button == 1 && bevent->type == GDK_BUTTON_PRESS)
{
gtk_grab_remove (widget);
gdk_seat_ungrab (seat);
gtk_widget_destroy (widget);
if (event_widget != widget)
g_idle_add (gimp_context_help_idle_show_help, event_widget);
}
return TRUE;
}
static gboolean
gimp_context_help_key_press (GtkWidget *widget,
GdkEventKey *kevent,
gpointer data)
{
if (kevent->keyval == GDK_KEY_Escape)
{
GdkDisplay *display = gtk_widget_get_display (widget);
gtk_grab_remove (widget);
gdk_seat_ungrab (gdk_display_get_default_seat (display));
gtk_widget_destroy (widget);
}
return TRUE;
}
static gboolean
gimp_context_help_idle_show_help (gpointer data)
{
GtkWidget *help_widget;
const gchar *help_id = NULL;
gpointer help_data = NULL;
help_id = gimp_help_get_help_data (GTK_WIDGET (data), &help_widget,
&help_data);
if (help_id)
gimp_standard_help_func (help_id, help_data);
return FALSE;
}

View file

@ -0,0 +1,75 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimphelpui.h
* Copyright (C) 2000-2003 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_HELP_UI_H__
#define __GIMP_HELP_UI_H__
G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
/* the standard gimp help function
*/
void gimp_standard_help_func (const gchar *help_id,
gpointer help_data);
/* connect the help callback of a window */
void gimp_help_connect (GtkWidget *widget,
const gchar *tooltip,
GimpHelpFunc help_func,
const gchar *help_id,
gpointer help_data,
GDestroyNotify help_data_destroy);
/* set help data for non-window widgets */
void gimp_help_set_help_data (GtkWidget *widget,
const gchar *tooltip,
const gchar *help_id);
/* set help data with markup for non-window widgets */
void gimp_help_set_help_data_with_markup (GtkWidget *widget,
const gchar *tooltip,
const gchar *help_id);
/* activate the context help inspector */
void gimp_context_help (GtkWidget *widget);
/**
* GIMP_HELP_ID:
*
* The #GQuark used to attach GIMP help IDs to widgets.
*
* Since: 2.2
**/
#define GIMP_HELP_ID (gimp_help_id_quark ())
GQuark gimp_help_id_quark (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __GIMP_HELP_UI_H__ */

View file

@ -0,0 +1,224 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimphintbox.c
* Copyright (C) 2006 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "gimpwidgets.h"
/**
* SECTION: gimphintbox
* @title: GimpHintBox
* @short_description: Displays a wilber icon and a text.
*
* Displays a wilber icon and a text.
**/
enum
{
PROP_0,
PROP_ICON_NAME,
PROP_HINT
};
struct _GimpHintBox
{
GtkBox parent_instance;
gchar *icon_name;
gchar *hint;
};
static void gimp_hint_box_constructed (GObject *object);
static void gimp_hint_box_finalize (GObject *object);
static void gimp_hint_box_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_hint_box_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
G_DEFINE_TYPE (GimpHintBox, gimp_hint_box, GTK_TYPE_BOX)
#define parent_class gimp_hint_box_parent_class
static void
gimp_hint_box_class_init (GimpHintBoxClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = gimp_hint_box_constructed;
object_class->finalize = gimp_hint_box_finalize;
object_class->set_property = gimp_hint_box_set_property;
object_class->get_property = gimp_hint_box_get_property;
g_object_class_install_property (object_class, PROP_ICON_NAME,
g_param_spec_string ("icon-name",
"Icon Name",
"The icon to show next to the hint",
GIMP_ICON_DIALOG_INFORMATION,
G_PARAM_CONSTRUCT_ONLY |
GIMP_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_HINT,
g_param_spec_string ("hint",
"Hint",
"The hint to display",
NULL,
G_PARAM_CONSTRUCT_ONLY |
GIMP_PARAM_READWRITE));
}
static void
gimp_hint_box_init (GimpHintBox *box)
{
gtk_orientable_set_orientation (GTK_ORIENTABLE (box),
GTK_ORIENTATION_HORIZONTAL);
}
static void
gimp_hint_box_constructed (GObject *object)
{
GimpHintBox *box = GIMP_HINT_BOX (object);
GtkWidget *image = NULL;
GtkWidget *label;
G_OBJECT_CLASS (parent_class)->constructed (object);
gtk_box_set_spacing (GTK_BOX (box), 12);
if (box->icon_name)
{
image = gtk_image_new_from_icon_name (box->icon_name,
GTK_ICON_SIZE_DIALOG);
}
if (image)
{
gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0);
gtk_widget_set_visible (image, TRUE);
}
label = g_object_new (GTK_TYPE_LABEL,
"label", box->hint,
"wrap", TRUE,
"justify", GTK_JUSTIFY_LEFT,
"xalign", 0.0,
"yalign", 0.5,
NULL);
gimp_label_set_attributes (GTK_LABEL (label),
PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
-1);
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
gtk_widget_set_visible (label, TRUE);
}
static void
gimp_hint_box_finalize (GObject *object)
{
GimpHintBox *box = GIMP_HINT_BOX (object);
g_clear_pointer (&box->icon_name, g_free);
g_clear_pointer (&box->hint, g_free);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_hint_box_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpHintBox *box = GIMP_HINT_BOX (object);
switch (property_id)
{
case PROP_ICON_NAME:
box->icon_name = g_value_dup_string (value);
break;
case PROP_HINT:
box->hint = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_hint_box_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpHintBox *box = GIMP_HINT_BOX (object);
switch (property_id)
{
case PROP_ICON_NAME:
g_value_set_string (value, box->icon_name);
break;
case PROP_HINT:
g_value_set_string (value, box->hint);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* gimp_hint_box_new:
* @hint: text to display as a user hint
*
* Creates a new widget that shows a text label showing @hint,
* decorated with a GIMP_ICON_INFO wilber icon.
*
* Returns: a new widget
*
* Since GIMP 2.4
**/
GtkWidget *
gimp_hint_box_new (const gchar *hint)
{
g_return_val_if_fail (hint != NULL, NULL);
return g_object_new (GIMP_TYPE_HINT_BOX,
"hint", hint,
NULL);
}

View file

@ -0,0 +1,43 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimphintbox.h
* Copyright (C) 2006 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_HINT_BOX_H__
#define __GIMP_HINT_BOX_H__
G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
#define GIMP_TYPE_HINT_BOX (gimp_hint_box_get_type ())
G_DECLARE_FINAL_TYPE (GimpHintBox, gimp_hint_box, GIMP, HINT_BOX, GtkBox)
GtkWidget * gimp_hint_box_new (const gchar *hint);
G_END_DECLS
#endif /* __GIMP_HINT_BOX_H__ */

320
libgimpwidgets/gimpicons.c Normal file
View file

@ -0,0 +1,320 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpicons.c
* Copyright (C) 2001-2015 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "gimpicons.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimpicons
* @title: GimpIcons
* @short_description: Prebuilt common menu/toolbar items and
* corresponding icons
*
* GIMP registers a set of menu/toolbar items and corresponding icons
* in addition to the standard GTK+ stock items. These can be used
* just like GTK+ stock items. GIMP also overrides a few of the GTK+
* icons (namely the ones in dialog size).
*
* Stock icons may have a RTL variant which gets used for
* right-to-left locales.
**/
#define LIBGIMP_DOMAIN GETTEXT_PACKAGE "-libgimp"
#define GIMP_TOILET_PAPER "gimp-toilet-paper"
#define GIMP_DEFAULT_ICON_THEME "Default"
static GFile *icon_theme_path = NULL;
static GFile *default_search_path = NULL;
static void
gimp_icons_change_icon_theme (GFile *new_search_path)
{
GFile *old_search_path = g_file_get_parent (icon_theme_path);
if (! default_search_path)
default_search_path = gimp_data_directory_file ("icons", NULL);
if (! g_file_equal (new_search_path, old_search_path))
{
GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
if (g_file_equal (old_search_path, default_search_path))
{
/* if the old icon theme is in the default search path,
* simply prepend the new theme's path
*/
gchar *path_str = g_file_get_path (new_search_path);
gtk_icon_theme_prepend_search_path (icon_theme, path_str);
g_free (path_str);
}
else
{
/* if the old theme is not in the default search path,
* we need to deal with the search path's first element
*/
gchar **paths;
gint n_paths;
gtk_icon_theme_get_search_path (icon_theme, &paths, &n_paths);
if (g_file_equal (new_search_path, default_search_path))
{
/* when switching to a theme in the default path, remove
* the first search path element, the default search path
* is still in the search path
*/
gtk_icon_theme_set_search_path (icon_theme,
(const gchar **) paths + 1,
n_paths - 1);
}
else
{
/* when switching between two non-default search paths, replace
* the first element of the search path with the new
* theme's path
*/
g_free (paths[0]);
paths[0] = g_file_get_path (new_search_path);
gtk_icon_theme_set_search_path (icon_theme,
(const gchar **) paths, n_paths);
}
g_strfreev (paths);
}
}
g_object_unref (old_search_path);
}
static void
gimp_icons_notify_system_icon_theme (GObject *settings,
GParamSpec *param,
gpointer unused)
{
GdkScreen *screen = gdk_screen_get_default ();
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_STRING);
if (gdk_screen_get_setting (screen, "gtk-icon-theme-name", &value))
{
const gchar *new_system_icon_theme = g_value_get_string (&value);
gchar *cur_system_icon_theme = NULL;
g_object_get (settings,
"gtk-fallback-icon-theme", &cur_system_icon_theme,
NULL);
if (g_strcmp0 (cur_system_icon_theme, new_system_icon_theme))
{
g_object_set (settings,
"gtk-fallback-icon-theme", new_system_icon_theme,
NULL);
g_object_notify (settings, "gtk-icon-theme-name");
}
g_free (cur_system_icon_theme);
}
g_value_unset (&value);
}
static gboolean
gimp_icons_sanity_check (GFile *path,
const gchar *theme_name)
{
gboolean exists = FALSE;
GFile *child = g_file_get_child (path, theme_name);
if (g_file_query_exists (child, NULL))
{
GFile *index = g_file_get_child (child, "index.theme");
if (g_file_query_exists (index, NULL))
exists = TRUE;
else
g_printerr ("%s: Icon theme path has no '%s/index.theme': %s\n",
G_STRFUNC, theme_name, gimp_file_get_utf8_name (path));
g_object_unref (index);
}
else
g_printerr ("%s: Icon theme path has no '%s' subdirectory: %s\n",
G_STRFUNC, theme_name, gimp_file_get_utf8_name (path));
g_object_unref (child);
return exists;
}
gboolean
gimp_icons_set_icon_theme (GFile *path)
{
gchar *icon_theme_name;
GFile *search_path;
gboolean success = FALSE;
g_return_val_if_fail (path == NULL || G_IS_FILE (path), FALSE);
if (path)
path = g_object_ref (path);
else
path = gimp_data_directory_file ("icons", GIMP_DEFAULT_ICON_THEME, NULL);
search_path = g_file_get_parent (path);
icon_theme_name = g_file_get_basename (path);
if (gimp_icons_sanity_check (search_path, "hicolor") &&
gimp_icons_sanity_check (search_path, icon_theme_name))
{
if (icon_theme_path)
{
/* this is an icon theme change */
gimp_icons_change_icon_theme (search_path);
if (! g_file_equal (icon_theme_path, path))
{
g_object_unref (icon_theme_path);
icon_theme_path = g_object_ref (path);
}
}
else
{
/* this is the first call upon initialization */
icon_theme_path = g_object_ref (path);
}
g_object_set (gtk_settings_get_for_screen (gdk_screen_get_default ()),
"gtk-icon-theme-name", icon_theme_name,
NULL);
success = TRUE;
}
g_free (icon_theme_name);
g_object_unref (search_path);
g_object_unref (path);
return success;
}
/**
* gimp_icons_init:
*
* Initializes the GIMP stock icon factory.
*
* You don't need to call this function as gimp_ui_init() already does
* this for you.
*/
void
gimp_icons_init (void)
{
static gboolean initialized = FALSE;
GtkSettings *settings;
GdkPixbuf *pixbuf;
GError *error = NULL;
gchar *icons_dir;
gchar *system_icon_theme;
gchar *gimp_icon_theme;
if (initialized)
return;
/* always prepend the default icon theme, it's never removed from
* the path again and acts as fallback for missing icons in other
* themes.
*/
if (! default_search_path)
default_search_path = gimp_data_directory_file ("icons",
NULL);
icons_dir = g_file_get_path (default_search_path);
gtk_icon_theme_prepend_search_path (gtk_icon_theme_get_default (),
icons_dir);
g_free (icons_dir);
/* if an icon theme was chosen before init(), change to it */
if (icon_theme_path)
{
GFile *search_path = g_file_get_parent (icon_theme_path);
if (!g_file_equal (search_path, default_search_path))
{
gchar *icon_dir = g_file_get_path (search_path);
gtk_icon_theme_prepend_search_path (gtk_icon_theme_get_default (),
icon_dir);
g_free (icon_dir);
}
g_object_unref (search_path);
gimp_icon_theme = g_file_get_basename (icon_theme_path);
}
else
{
gimp_icon_theme = g_strdup (GIMP_DEFAULT_ICON_THEME);
}
settings = gtk_settings_get_for_screen (gdk_screen_get_default ());
g_object_get (settings, "gtk-icon-theme-name", &system_icon_theme, NULL);
g_object_set (settings,
"gtk-fallback-icon-theme", system_icon_theme,
"gtk-icon-theme-name", gimp_icon_theme,
NULL);
g_free (gimp_icon_theme);
g_free (system_icon_theme);
g_signal_connect (settings, "notify::gtk-icon-theme-name",
G_CALLBACK (gimp_icons_notify_system_icon_theme), NULL);
pixbuf = gdk_pixbuf_new_from_resource ("/org/gimp/icons/64/gimp-wilber-eek.png",
&error);
if (pixbuf)
{
gtk_icon_theme_add_resource_path (gtk_icon_theme_get_default (),
"/org/gimp/icons/64/gimp-wilber-eek.png");
g_object_unref (pixbuf);
}
else
{
g_critical ("Failed to create icon image: %s", error->message);
g_clear_error (&error);
}
initialized = TRUE;
}

440
libgimpwidgets/gimpicons.h Normal file
View file

@ -0,0 +1,440 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpicons.h
* Copyright (C) 2001-2015 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_ICONS_H__
#define __GIMP_ICONS_H__
G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
/* random actions that don't fit in any category */
#define GIMP_ICON_ATTACH "gimp-attach"
#define GIMP_ICON_DETACH "gimp-detach"
#define GIMP_ICON_INVERT "gimp-invert"
#define GIMP_ICON_RECORD "media-record"
#define GIMP_ICON_RESET "gimp-reset"
#define GIMP_ICON_SHRED "gimp-shred"
/* random states/things that don't fit in any category */
#define GIMP_ICON_BUSINESS_CARD "gimp-business-card"
#define GIMP_ICON_CHAR_PICKER "gimp-char-picker"
#define GIMP_ICON_CURSOR "gimp-cursor"
#define GIMP_ICON_DISPLAY "gimp-display"
#define GIMP_ICON_GEGL "gimp-gegl"
#define GIMP_ICON_LINKED "gimp-linked"
#define GIMP_ICON_MARKER "gimp-marker"
#define GIMP_ICON_SMARTPHONE "gimp-smartphone"
#define GIMP_ICON_TRANSPARENCY "gimp-transparency"
#define GIMP_ICON_VIDEO "gimp-video"
#define GIMP_ICON_VISIBLE "gimp-visible"
#define GIMP_ICON_WEB "gimp-web"
/* random objects/entities that don't fit in any category */
#define GIMP_ICON_BRUSH GIMP_ICON_TOOL_PAINTBRUSH
#define GIMP_ICON_BUFFER GIMP_ICON_EDIT_PASTE
#define GIMP_ICON_COLORMAP "gimp-colormap"
#define GIMP_ICON_DYNAMICS "gimp-dynamics"
#define GIMP_ICON_FILE_MANAGER "gimp-file-manager"
#define GIMP_ICON_FONT "gtk-select-font"
#define GIMP_ICON_GRADIENT GIMP_ICON_TOOL_GRADIENT
#define GIMP_ICON_GRID "gimp-grid"
#define GIMP_ICON_INPUT_DEVICE "gimp-input-device"
#define GIMP_ICON_MYPAINT_BRUSH GIMP_ICON_TOOL_MYPAINT_BRUSH
#define GIMP_ICON_PALETTE "gtk-select-color"
#define GIMP_ICON_PATTERN "gimp-pattern"
#define GIMP_ICON_PLUGIN "gimp-plugin"
#define GIMP_ICON_SAMPLE_POINT "gimp-sample-point"
#define GIMP_ICON_SYMMETRY "gimp-symmetry"
#define GIMP_ICON_TEMPLATE "gimp-template"
#define GIMP_ICON_TOOL_PRESET "gimp-tool-preset"
/* not really icons */
#define GIMP_ICON_FRAME "gimp-frame"
#define GIMP_ICON_TEXTURE "gimp-texture"
/* icons that follow, or at least try to follow the FDO naming and
* category conventions; and groups of icons with a common prefix;
* all sorted alphabetically
*
* see also:
* https://specifications.freedesktop.org/icon-naming-spec/latest/ar01s04.html
*
* When icons are available as standard Freedesktop icons, we use these
* in priority. As a fallback, we use standard GTK+ icons. As last
* fallback, we create our own icons under the "gimp-" namespace.
*/
#define GIMP_ICON_APPLICATION_EXIT "application-exit"
#define GIMP_ICON_ASPECT_PORTRAIT "gimp-portrait"
#define GIMP_ICON_ASPECT_LANDSCAPE "gimp-landscape"
#define GIMP_ICON_CAP_BUTT "gimp-cap-butt"
#define GIMP_ICON_CAP_ROUND "gimp-cap-round"
#define GIMP_ICON_CAP_SQUARE "gimp-cap-square"
#define GIMP_ICON_CENTER "gimp-center"
#define GIMP_ICON_CENTER_HORIZONTAL "gimp-hcenter"
#define GIMP_ICON_CENTER_VERTICAL "gimp-vcenter"
#define GIMP_ICON_CHAIN_HORIZONTAL "gimp-hchain"
#define GIMP_ICON_CHAIN_HORIZONTAL_BROKEN "gimp-hchain-broken"
#define GIMP_ICON_CHAIN_VERTICAL "gimp-vchain"
#define GIMP_ICON_CHAIN_VERTICAL_BROKEN "gimp-vchain-broken"
#define GIMP_ICON_CHANNEL "gimp-channel"
#define GIMP_ICON_CHANNEL_ALPHA "gimp-channel-alpha"
#define GIMP_ICON_CHANNEL_BLUE "gimp-channel-blue"
#define GIMP_ICON_CHANNEL_GRAY "gimp-channel-gray"
#define GIMP_ICON_CHANNEL_GREEN "gimp-channel-green"
#define GIMP_ICON_CHANNEL_INDEXED "gimp-channel-indexed"
#define GIMP_ICON_CHANNEL_RED "gimp-channel-red"
#define GIMP_ICON_CLOSE "gimp-close"
#define GIMP_ICON_CLOSE_ALL "gimp-close-all"
#define GIMP_ICON_COLOR_PICKER_BLACK "gimp-color-picker-black"
#define GIMP_ICON_COLOR_PICKER_GRAY "gimp-color-picker-gray"
#define GIMP_ICON_COLOR_PICKER_WHITE "gimp-color-picker-white"
#define GIMP_ICON_COLOR_PICK_FROM_SCREEN "gimp-color-pick-from-screen"
#define GIMP_ICON_COLOR_SELECTOR_CMYK "gimp-color-cmyk"
#define GIMP_ICON_COLOR_SELECTOR_TRIANGLE "gimp-color-triangle"
#define GIMP_ICON_COLOR_SELECTOR_WATER "gimp-color-water"
#define GIMP_ICON_COLOR_SPACE_LINEAR "gimp-color-space-linear"
#define GIMP_ICON_COLOR_SPACE_NON_LINEAR "gimp-color-space-non-linear"
#define GIMP_ICON_COLOR_SPACE_PERCEPTUAL "gimp-color-space-perceptual"
#define GIMP_ICON_COLORS_DEFAULT "gimp-default-colors"
#define GIMP_ICON_COLORS_SWAP "gimp-swap-colors"
#define GIMP_ICON_CONTROLLER "gimp-controller"
#define GIMP_ICON_CONTROLLER_KEYBOARD "gimp-controller-keyboard"
#define GIMP_ICON_CONTROLLER_LINUX_INPUT "gimp-controller-linux-input"
#define GIMP_ICON_CONTROLLER_MIDI "gimp-controller-midi"
#define GIMP_ICON_CONTROLLER_MOUSE GIMP_ICON_CURSOR
#define GIMP_ICON_CONTROLLER_WHEEL "gimp-controller-wheel"
#define GIMP_ICON_CONVERT_RGB "gimp-convert-rgb"
#define GIMP_ICON_CONVERT_GRAYSCALE "gimp-convert-grayscale"
#define GIMP_ICON_CONVERT_INDEXED "gimp-convert-indexed"
#define GIMP_ICON_CONVERT_PRECISION GIMP_ICON_CONVERT_RGB
#define GIMP_ICON_CURVE_FREE "gimp-curve-free"
#define GIMP_ICON_CURVE_SMOOTH "gimp-curve-smooth"
#define GIMP_ICON_DIALOG_CHANNELS "gimp-channels"
#define GIMP_ICON_DIALOG_DASHBOARD "gimp-dashboard"
#define GIMP_ICON_DIALOG_DEVICE_STATUS "gimp-device-status"
#define GIMP_ICON_DIALOG_ERROR "dialog-error"
#define GIMP_ICON_DIALOG_IMAGES "gimp-images"
#define GIMP_ICON_DIALOG_INFORMATION "dialog-information"
#define GIMP_ICON_DIALOG_LAYERS "gimp-layers"
#define GIMP_ICON_DIALOG_NAVIGATION "gimp-navigation"
#define GIMP_ICON_DIALOG_PATHS "gimp-paths"
#define GIMP_ICON_DIALOG_QUESTION "dialog-question"
#define GIMP_ICON_DIALOG_RESHOW_FILTER "gimp-reshow-filter"
#define GIMP_ICON_DIALOG_TOOLS "gimp-tools"
#define GIMP_ICON_DIALOG_TOOL_OPTIONS "gimp-tool-options"
#define GIMP_ICON_DIALOG_UNDO_HISTORY "gimp-undo-history"
#define GIMP_ICON_DIALOG_WARNING "dialog-warning"
#define GIMP_ICON_DISPLAY_FILTER "gimp-display-filter"
#define GIMP_ICON_DISPLAY_FILTER_CLIP_WARNING "gimp-display-filter-clip-warning"
#define GIMP_ICON_DISPLAY_FILTER_COLORBLIND "gimp-display-filter-colorblind"
#define GIMP_ICON_DISPLAY_FILTER_CONTRAST "gimp-display-filter-contrast"
#define GIMP_ICON_DISPLAY_FILTER_GAMMA "gimp-display-filter-gamma"
#define GIMP_ICON_DISPLAY_FILTER_LCMS "gimp-display-filter-lcms"
#define GIMP_ICON_DISPLAY_FILTER_PROOF "gimp-display-filter-proof"
#define GIMP_ICON_LOCK "gimp-lock"
#define GIMP_ICON_LOCK_ALPHA "gimp-lock-alpha"
#define GIMP_ICON_LOCK_CONTENT "gimp-lock-content"
#define GIMP_ICON_LOCK_PATH "gimp-lock-path"
#define GIMP_ICON_LOCK_POSITION "gimp-lock-position"
#define GIMP_ICON_LOCK_VISIBILITY "gimp-lock-visibility"
#define GIMP_ICON_LOCK_MULTI "gimp-lock-multi"
#define GIMP_ICON_DOCUMENT_NEW "document-new"
#define GIMP_ICON_DOCUMENT_OPEN "document-open"
#define GIMP_ICON_DOCUMENT_OPEN_RECENT "document-open-recent"
#define GIMP_ICON_DOCUMENT_PAGE_SETUP "document-page-setup"
#define GIMP_ICON_DOCUMENT_PRINT "document-print"
#define GIMP_ICON_DOCUMENT_PRINT_RESOLUTION "document-print"
#define GIMP_ICON_DOCUMENT_PROPERTIES "document-properties"
#define GIMP_ICON_DOCUMENT_REVERT "document-revert"
#define GIMP_ICON_DOCUMENT_SAVE "document-save"
#define GIMP_ICON_DOCUMENT_SAVE_AS "document-save-as"
#define GIMP_ICON_EDIT "gtk-edit"
#define GIMP_ICON_EDIT_CLEAR "edit-clear"
#define GIMP_ICON_EDIT_COPY "edit-copy"
#define GIMP_ICON_EDIT_CUT "edit-cut"
#define GIMP_ICON_EDIT_DELETE "edit-delete"
#define GIMP_ICON_EDIT_FIND "edit-find"
#define GIMP_ICON_EDIT_PASTE "edit-paste"
#define GIMP_ICON_EDIT_PASTE_AS_NEW "gimp-paste-as-new"
#define GIMP_ICON_EDIT_PASTE_INTO "gimp-paste-into"
#define GIMP_ICON_EDIT_REDO "edit-redo"
#define GIMP_ICON_EDIT_UNDO "edit-undo"
#define GIMP_ICON_EFFECT "gimp-effects"
#define GIMP_ICON_EVEN_HORIZONTAL_GAP "gimp-even-horizontal-gap"
#define GIMP_ICON_EVEN_VERTICAL_GAP "gimp-even-vertical-gap"
#define GIMP_ICON_FILL_HORIZONTAL "gimp-hfill"
#define GIMP_ICON_FILL_VERTICAL "gimp-vfill"
#define GIMP_ICON_FOLDER_NEW "folder-new"
#define GIMP_ICON_FORMAT_INDENT_MORE "format-indent-more"
#define GIMP_ICON_FORMAT_INDENT_LESS "format-indent-less"
#define GIMP_ICON_FORMAT_JUSTIFY_CENTER "format-justify-center"
#define GIMP_ICON_FORMAT_JUSTIFY_FILL "format-justify-fill"
#define GIMP_ICON_FORMAT_JUSTIFY_LEFT "format-justify-left"
#define GIMP_ICON_FORMAT_JUSTIFY_RIGHT "format-justify-right"
#define GIMP_ICON_FORMAT_TEXT_BOLD "format-text-bold"
#define GIMP_ICON_FORMAT_TEXT_ITALIC "format-text-italic"
#define GIMP_ICON_FORMAT_TEXT_STRIKETHROUGH "format-text-strikethrough"
#define GIMP_ICON_FORMAT_TEXT_UNDERLINE "format-text-underline"
#define GIMP_ICON_FORMAT_TEXT_DIRECTION_LTR "format-text-direction-ltr"
#define GIMP_ICON_FORMAT_TEXT_DIRECTION_RTL "format-text-direction-rtl"
#define GIMP_ICON_FORMAT_TEXT_DIRECTION_TTB_RTL "gimp-text-dir-ttb-rtl" /* use FDO */
#define GIMP_ICON_FORMAT_TEXT_DIRECTION_TTB_RTL_UPRIGHT "gimp-text-dir-ttb-rtl-upright" /* use FDO */
#define GIMP_ICON_FORMAT_TEXT_DIRECTION_TTB_LTR "gimp-text-dir-ttb-ltr" /* use FDO */
#define GIMP_ICON_FORMAT_TEXT_DIRECTION_TTB_LTR_UPRIGHT "gimp-text-dir-ttb-ltr-upright" /* use FDO */
#define GIMP_ICON_FORMAT_TEXT_SPACING_LETTER "gimp-letter-spacing"
#define GIMP_ICON_FORMAT_TEXT_SPACING_LINE "gimp-line-spacing"
#define GIMP_ICON_GRADIENT_LINEAR "gimp-gradient-linear"
#define GIMP_ICON_GRADIENT_BILINEAR "gimp-gradient-bilinear"
#define GIMP_ICON_GRADIENT_RADIAL "gimp-gradient-radial"
#define GIMP_ICON_GRADIENT_SQUARE "gimp-gradient-square"
#define GIMP_ICON_GRADIENT_CONICAL_SYMMETRIC "gimp-gradient-conical-symmetric"
#define GIMP_ICON_GRADIENT_CONICAL_ASYMMETRIC "gimp-gradient-conical-asymmetric"
#define GIMP_ICON_GRADIENT_SHAPEBURST_ANGULAR "gimp-gradient-shapeburst-angular"
#define GIMP_ICON_GRADIENT_SHAPEBURST_SPHERICAL "gimp-gradient-shapeburst-spherical"
#define GIMP_ICON_GRADIENT_SHAPEBURST_DIMPLED "gimp-gradient-shapeburst-dimpled"
#define GIMP_ICON_GRADIENT_SPIRAL_CLOCKWISE "gimp-gradient-spiral-clockwise"
#define GIMP_ICON_GRADIENT_SPIRAL_ANTICLOCKWISE "gimp-gradient-spiral-anticlockwise"
#define GIMP_ICON_GRAVITY_EAST "gimp-gravity-east"
#define GIMP_ICON_GRAVITY_NORTH "gimp-gravity-north"
#define GIMP_ICON_GRAVITY_NORTH_EAST "gimp-gravity-north-east"
#define GIMP_ICON_GRAVITY_NORTH_WEST "gimp-gravity-north-west"
#define GIMP_ICON_GRAVITY_SOUTH "gimp-gravity-south"
#define GIMP_ICON_GRAVITY_SOUTH_EAST "gimp-gravity-south-east"
#define GIMP_ICON_GRAVITY_SOUTH_WEST "gimp-gravity-south-west"
#define GIMP_ICON_GRAVITY_WEST "gimp-gravity-west"
#define GIMP_ICON_GO_BOTTOM "go-bottom"
#define GIMP_ICON_GO_DOWN "go-down"
#define GIMP_ICON_GO_FIRST "go-first"
#define GIMP_ICON_GO_HOME "go-home"
#define GIMP_ICON_GO_LAST "go-last"
#define GIMP_ICON_GO_TOP "go-top"
#define GIMP_ICON_GO_UP "go-up"
#define GIMP_ICON_GO_PREVIOUS "go-previous"
#define GIMP_ICON_GO_NEXT "go-next"
#define GIMP_ICON_HELP "system-help"
#define GIMP_ICON_HELP_ABOUT "help-about"
#define GIMP_ICON_HELP_USER_MANUAL "gimp-user-manual"
#define GIMP_ICON_HISTOGRAM "gimp-histogram"
#define GIMP_ICON_HISTOGRAM_LINEAR "gimp-histogram-linear"
#define GIMP_ICON_HISTOGRAM_LOGARITHMIC "gimp-histogram-logarithmic"
#define GIMP_ICON_IMAGE "gimp-image"
#define GIMP_ICON_IMAGE_OPEN "gimp-image-open"
#define GIMP_ICON_IMAGE_RELOAD "gimp-image-reload"
#define GIMP_ICON_JOIN_MITER "gimp-join-miter"
#define GIMP_ICON_JOIN_ROUND "gimp-join-round"
#define GIMP_ICON_JOIN_BEVEL "gimp-join-bevel"
#define GIMP_ICON_LAYER "gimp-layer"
#define GIMP_ICON_LAYER_ANCHOR "gimp-anchor"
#define GIMP_ICON_LAYER_FLOATING_SELECTION "gimp-floating-selection"
#define GIMP_ICON_LAYER_MASK "gimp-layer-mask"
#define GIMP_ICON_LAYER_MERGE_DOWN "gimp-merge-down"
#define GIMP_ICON_LAYER_TEXT_LAYER "gimp-text-layer"
#define GIMP_ICON_LAYER_TO_IMAGESIZE "gimp-layer-to-imagesize"
#define GIMP_ICON_LIST "gimp-list"
#define GIMP_ICON_LIST_ADD "list-add"
#define GIMP_ICON_LIST_REMOVE "list-remove"
#define GIMP_ICON_MENU_LEFT "gimp-menu-left"
#define GIMP_ICON_MENU_RIGHT "gimp-menu-right"
#define GIMP_ICON_OBJECT_DUPLICATE "gimp-duplicate"
#define GIMP_ICON_OBJECT_FLIP_HORIZONTAL "object-flip-horizontal"
#define GIMP_ICON_OBJECT_FLIP_VERTICAL "object-flip-vertical"
#define GIMP_ICON_OBJECT_RESIZE "gimp-resize"
#define GIMP_ICON_OBJECT_ROTATE_180 "gimp-rotate-180"
#define GIMP_ICON_OBJECT_ROTATE_270 "object-rotate-left"
#define GIMP_ICON_OBJECT_ROTATE_90 "object-rotate-right"
#define GIMP_ICON_OBJECT_SCALE "gimp-scale"
#define GIMP_ICON_PATH "gimp-path"
#define GIMP_ICON_PATH_STROKE "gimp-path-stroke"
#define GIMP_ICON_PIVOT_CENTER "gimp-pivot-center"
#define GIMP_ICON_PIVOT_EAST "gimp-pivot-east"
#define GIMP_ICON_PIVOT_NORTH "gimp-pivot-north"
#define GIMP_ICON_PIVOT_NORTH_EAST "gimp-pivot-north-east"
#define GIMP_ICON_PIVOT_NORTH_WEST "gimp-pivot-north-west"
#define GIMP_ICON_PIVOT_SOUTH "gimp-pivot-south"
#define GIMP_ICON_PIVOT_SOUTH_EAST "gimp-pivot-south-east"
#define GIMP_ICON_PIVOT_SOUTH_WEST "gimp-pivot-south-west"
#define GIMP_ICON_PIVOT_WEST "gimp-pivot-west"
#define GIMP_ICON_PREFERENCES_SYSTEM "preferences-system"
#define GIMP_ICON_PROCESS_STOP "process-stop"
#define GIMP_ICON_QUICK_MASK_OFF "gimp-quick-mask-off"
#define GIMP_ICON_QUICK_MASK_ON "gimp-quick-mask-on"
#define GIMP_ICON_SELECTION "gimp-selection"
#define GIMP_ICON_SELECTION_ADD "gimp-selection-add"
#define GIMP_ICON_SELECTION_ALL "gimp-selection-all"
#define GIMP_ICON_SELECTION_BORDER "gimp-selection-border"
#define GIMP_ICON_SELECTION_GROW "gimp-selection-grow"
#define GIMP_ICON_SELECTION_INTERSECT "gimp-selection-intersect"
#define GIMP_ICON_SELECTION_NONE "gimp-selection-none"
#define GIMP_ICON_SELECTION_REPLACE "gimp-selection-replace"
#define GIMP_ICON_SELECTION_SHRINK "gimp-selection-shrink"
#define GIMP_ICON_SELECTION_STROKE "gimp-selection-stroke"
#define GIMP_ICON_SELECTION_SUBTRACT "gimp-selection-subtract"
#define GIMP_ICON_SELECTION_TO_CHANNEL "gimp-selection-to-channel"
#define GIMP_ICON_SELECTION_TO_PATH "gimp-selection-to-path"
#define GIMP_ICON_SHAPE_CIRCLE "gimp-shape-circle"
#define GIMP_ICON_SHAPE_DIAMOND "gimp-shape-diamond"
#define GIMP_ICON_SHAPE_SQUARE "gimp-shape-square"
#define GIMP_ICON_SYSTEM_RUN "system-run"
#define GIMP_ICON_TOOL_AIRBRUSH "gimp-tool-airbrush"
#define GIMP_ICON_TOOL_ALIGN "gimp-tool-align"
#define GIMP_ICON_TOOL_BLUR "gimp-tool-blur"
#define GIMP_ICON_TOOL_BRIGHTNESS_CONTRAST "gimp-tool-brightness-contrast"
#define GIMP_ICON_TOOL_BUCKET_FILL "gimp-tool-bucket-fill"
#define GIMP_ICON_TOOL_BY_COLOR_SELECT "gimp-tool-by-color-select"
#define GIMP_ICON_TOOL_CAGE "gimp-tool-cage"
#define GIMP_ICON_TOOL_CLONE "gimp-tool-clone"
#define GIMP_ICON_TOOL_COLORIZE "gimp-tool-colorize"
#define GIMP_ICON_TOOL_COLOR_BALANCE "gimp-tool-color-balance"
#define GIMP_ICON_TOOL_COLOR_PICKER "gimp-tool-color-picker"
#define GIMP_ICON_TOOL_COLOR_TEMPERATURE "gimp-tool-color-temperature"
#define GIMP_ICON_TOOL_CROP "gimp-tool-crop"
#define GIMP_ICON_TOOL_CURVES "gimp-tool-curves"
#define GIMP_ICON_TOOL_DESATURATE "gimp-tool-desaturate"
#define GIMP_ICON_TOOL_DODGE "gimp-tool-dodge"
#define GIMP_ICON_TOOL_ELLIPSE_SELECT "gimp-tool-ellipse-select"
#define GIMP_ICON_TOOL_ERASER "gimp-tool-eraser"
#define GIMP_ICON_TOOL_EXPOSURE "gimp-tool-exposure"
#define GIMP_ICON_TOOL_FLIP "gimp-tool-flip"
#define GIMP_ICON_TOOL_FOREGROUND_SELECT "gimp-tool-foreground-select"
#define GIMP_ICON_TOOL_FREE_SELECT "gimp-tool-free-select"
#define GIMP_ICON_TOOL_FUZZY_SELECT "gimp-tool-fuzzy-select"
#define GIMP_ICON_TOOL_GRADIENT "gimp-tool-gradient"
#define GIMP_ICON_TOOL_HANDLE_TRANSFORM "gimp-tool-handle-transform"
#define GIMP_ICON_TOOL_HEAL "gimp-tool-heal"
#define GIMP_ICON_TOOL_HUE_SATURATION "gimp-tool-hue-saturation"
#define GIMP_ICON_TOOL_INK "gimp-tool-ink"
#define GIMP_ICON_TOOL_ISCISSORS "gimp-tool-iscissors"
#define GIMP_ICON_TOOL_LEVELS "gimp-tool-levels"
#define GIMP_ICON_TOOL_MEASURE "gimp-tool-measure"
#define GIMP_ICON_TOOL_MOVE "gimp-tool-move"
#define GIMP_ICON_TOOL_MYPAINT_BRUSH "gimp-tool-mypaint-brush"
#define GIMP_ICON_TOOL_N_POINT_DEFORMATION "gimp-tool-n-point-deformation"
#define GIMP_ICON_TOOL_OFFSET "gimp-tool-offset"
#define GIMP_ICON_TOOL_PAINTBRUSH "gimp-tool-paintbrush"
#define GIMP_ICON_TOOL_PAINT_SELECT "gimp-tool-paint-select"
#define GIMP_ICON_TOOL_PATH "gimp-tool-path"
#define GIMP_ICON_TOOL_PENCIL "gimp-tool-pencil"
#define GIMP_ICON_TOOL_PERSPECTIVE "gimp-tool-perspective"
#define GIMP_ICON_TOOL_PERSPECTIVE_CLONE "gimp-tool-perspective-clone"
#define GIMP_ICON_TOOL_POSTERIZE "gimp-tool-posterize"
#define GIMP_ICON_TOOL_RECT_SELECT "gimp-tool-rect-select"
#define GIMP_ICON_TOOL_ROTATE "gimp-tool-rotate"
#define GIMP_ICON_TOOL_SCALE "gimp-tool-scale"
#define GIMP_ICON_TOOL_SEAMLESS_CLONE "gimp-tool-seamless-clone"
#define GIMP_ICON_TOOL_SHADOWS_HIGHLIGHTS "gimp-tool-shadows-highlights"
#define GIMP_ICON_TOOL_SHEAR "gimp-tool-shear"
#define GIMP_ICON_TOOL_SMUDGE "gimp-tool-smudge"
#define GIMP_ICON_TOOL_TEXT "gimp-tool-text"
#define GIMP_ICON_TOOL_THRESHOLD "gimp-tool-threshold"
#define GIMP_ICON_TOOL_TRANSFORM_3D "gimp-tool-transform-3d"
#define GIMP_ICON_TOOL_UNIFIED_TRANSFORM "gimp-tool-unified-transform"
#define GIMP_ICON_TOOL_WARP "gimp-tool-warp"
#define GIMP_ICON_TOOL_ZOOM "gimp-tool-zoom"
#define GIMP_ICON_TRANSFORM_3D_CAMERA "gimp-transform-3d-camera"
#define GIMP_ICON_TRANSFORM_3D_MOVE "gimp-transform-3d-move"
#define GIMP_ICON_TRANSFORM_3D_ROTATE "gimp-transform-3d-rotate"
#define GIMP_ICON_VIEW_REFRESH "view-refresh"
#define GIMP_ICON_VIEW_FULLSCREEN "view-fullscreen"
#define GIMP_ICON_VIEW_SHRINK_WRAP "view-shrink-wrap"
#define GIMP_ICON_VIEW_ZOOM_FILL "view-zoom-fill"
#define GIMP_ICON_WILBER "gimp-wilber"
#define GIMP_ICON_WILBER_EEK "gimp-wilber-eek"
#define GIMP_ICON_WINDOW_CLOSE "window-close"
#define GIMP_ICON_WINDOW_MOVE_TO_SCREEN "gimp-move-to-screen"
#define GIMP_ICON_WINDOW_NEW "window-new"
#define GIMP_ICON_ZOOM_IN "zoom-in"
#define GIMP_ICON_ZOOM_ORIGINAL "zoom-original"
#define GIMP_ICON_ZOOM_OUT "zoom-out"
#define GIMP_ICON_ZOOM_FIT_BEST "zoom-fit-best"
#define GIMP_ICON_ZOOM_FOLLOW_WINDOW "gimp-zoom-follow-window"
void gimp_icons_init (void);
gboolean gimp_icons_set_icon_theme (GFile *path);
G_END_DECLS
#endif /* __GIMP_ICONS_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,113 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpintcombobox.h
* Copyright (C) 2004 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_INT_COMBO_BOX_H__
#define __GIMP_INT_COMBO_BOX_H__
G_BEGIN_DECLS
#define GIMP_TYPE_INT_COMBO_BOX (gimp_int_combo_box_get_type ())
G_DECLARE_DERIVABLE_TYPE (GimpIntComboBox, gimp_int_combo_box, GIMP, INT_COMBO_BOX, GtkComboBox)
struct _GimpIntComboBoxClass
{
GtkComboBoxClass parent_class;
/* Padding for future expansion */
void (* _gimp_reserved0) (void);
void (* _gimp_reserved1) (void);
void (* _gimp_reserved2) (void);
void (* _gimp_reserved3) (void);
void (* _gimp_reserved4) (void);
void (* _gimp_reserved5) (void);
void (* _gimp_reserved6) (void);
void (* _gimp_reserved7) (void);
void (* _gimp_reserved8) (void);
void (* _gimp_reserved9) (void);
};
/**
* GimpIntSensitivityFunc:
* @value:
* @data: (closure):
*/
typedef gboolean (* GimpIntSensitivityFunc) (gint value,
gpointer data);
GtkWidget * gimp_int_combo_box_new (const gchar *first_label,
gint first_value,
...) G_GNUC_NULL_TERMINATED;
GtkWidget * gimp_int_combo_box_new_valist (const gchar *first_label,
gint first_value,
va_list values);
GtkWidget * gimp_int_combo_box_new_array (gint n_values,
const gchar *labels[]);
void gimp_int_combo_box_prepend (GimpIntComboBox *combo_box,
...);
void gimp_int_combo_box_append (GimpIntComboBox *combo_box,
...);
gboolean gimp_int_combo_box_set_active (GimpIntComboBox *combo_box,
gint value);
gboolean gimp_int_combo_box_get_active (GimpIntComboBox *combo_box,
gint *value);
gboolean
gimp_int_combo_box_set_active_by_user_data (GimpIntComboBox *combo_box,
gpointer user_data);
gboolean
gimp_int_combo_box_get_active_user_data (GimpIntComboBox *combo_box,
gpointer *user_data);
gulong gimp_int_combo_box_connect (GimpIntComboBox *combo_box,
gint value,
GCallback callback,
gpointer data,
GDestroyNotify data_destroy);
void gimp_int_combo_box_set_label (GimpIntComboBox *combo_box,
const gchar *label);
const gchar * gimp_int_combo_box_get_label (GimpIntComboBox *combo_box);
void gimp_int_combo_box_set_layout (GimpIntComboBox *combo_box,
GimpIntComboBoxLayout layout);
GimpIntComboBoxLayout
gimp_int_combo_box_get_layout (GimpIntComboBox *combo_box);
void gimp_int_combo_box_set_sensitivity (GimpIntComboBox *combo_box,
GimpIntSensitivityFunc func,
gpointer data,
GDestroyNotify destroy);
G_END_DECLS
#endif /* __GIMP_INT_COMBO_BOX_H__ */

View file

@ -0,0 +1,796 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpintradioframe.c
* Copyright (C) 2022 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <libintl.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "gimpwidgetstypes.h"
#include "gimpintradioframe.h"
#include "gimpintstore.h"
/**
* SECTION: gimpintradioframe
* @title: GimpIntRadioFrame
* @short_description: A widget providing radio buttons for integer
* values (e.g. enums).
*
* A widget providing a frame with title, containing grouped radio
* buttons, each associated with an integer value and random user data.
**/
enum
{
PROP_0,
PROP_VALUE,
PROP_STORE,
};
struct _GimpIntRadioFrame
{
GimpFrame parent_instance;
gchar *label;
GimpIntStore *store;
GSList *group;
gint value;
GtkWidget *box;
GimpIntRadioFrameSensitivityFunc sensitivity_func;
gpointer sensitivity_data;
GDestroyNotify sensitivity_destroy;
};
static void gimp_int_radio_frame_constructed (GObject *object);
static void gimp_int_radio_frame_finalize (GObject *object);
static void gimp_int_radio_frame_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_int_radio_frame_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static gboolean gimp_int_radio_frame_draw (GtkWidget *widget,
cairo_t *cr,
gpointer user_data);
static void gimp_int_radio_frame_fill (GimpIntRadioFrame *frame);
static void gimp_int_radio_frame_set_store (GimpIntRadioFrame *frame,
GimpIntStore *store);
static void gimp_int_radio_frame_update_sensitivity (GimpIntRadioFrame *frame);
static void gimp_int_radio_frame_button_toggled (GtkToggleButton *button,
GimpIntRadioFrame *frame);
G_DEFINE_TYPE (GimpIntRadioFrame, gimp_int_radio_frame, GIMP_TYPE_FRAME)
#define parent_class gimp_int_radio_frame_parent_class
static void
gimp_int_radio_frame_class_init (GimpIntRadioFrameClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = gimp_int_radio_frame_constructed;
object_class->finalize = gimp_int_radio_frame_finalize;
object_class->set_property = gimp_int_radio_frame_set_property;
object_class->get_property = gimp_int_radio_frame_get_property;
/**
* GimpIntRadioFrame:value:
*
* The active value
*
* Since: 3.0
*/
g_object_class_install_property (object_class, PROP_VALUE,
g_param_spec_int ("value",
"Value",
"Value of active item",
G_MININT, G_MAXINT, 0,
GIMP_PARAM_READWRITE |
G_PARAM_EXPLICIT_NOTIFY));
/**
* GimpIntRadioFrame:store:
*
* The %GimpIntStore from which the radio frame takes the values shown
* in the list.
*
* Since: 3.0
*/
g_object_class_install_property (object_class,
PROP_STORE,
g_param_spec_object ("store",
"GimpRadioFrame int store",
"The int store for the radio frame",
GIMP_TYPE_INT_STORE,
GIMP_PARAM_READWRITE |
G_PARAM_EXPLICIT_NOTIFY));
}
static void
gimp_int_radio_frame_init (GimpIntRadioFrame *radio_frame)
{
radio_frame->box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
gtk_container_add (GTK_CONTAINER (radio_frame), radio_frame->box);
gtk_widget_show (GTK_WIDGET (radio_frame->box));
}
static void
gimp_int_radio_frame_constructed (GObject *object)
{
G_OBJECT_CLASS (parent_class)->constructed (object);
g_signal_connect (object, "draw",
G_CALLBACK (gimp_int_radio_frame_draw),
NULL);
}
static void
gimp_int_radio_frame_finalize (GObject *object)
{
GimpIntRadioFrame *frame = GIMP_INT_RADIO_FRAME (object);
g_clear_pointer (&frame->label, g_free);
g_clear_object (&frame->store);
g_clear_pointer (&frame->group, g_slist_free);
if (frame->sensitivity_destroy)
{
GDestroyNotify d = frame->sensitivity_destroy;
frame->sensitivity_destroy = NULL;
d (frame->sensitivity_data);
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_int_radio_frame_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
case PROP_VALUE:
gimp_int_radio_frame_set_active (GIMP_INT_RADIO_FRAME (object),
g_value_get_int (value));
break;
case PROP_STORE:
gimp_int_radio_frame_set_store (GIMP_INT_RADIO_FRAME (object),
g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_int_radio_frame_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpIntRadioFrame *frame = GIMP_INT_RADIO_FRAME (object);
switch (property_id)
{
case PROP_VALUE:
g_value_set_int (value, frame->value);
break;
case PROP_STORE:
g_value_set_object (value, frame->store);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/* Public functions */
/**
* gimp_int_radio_frame_new_from_store:
* @title: the frame label.
* @store: the %GimpIntStore to generate radio buttons from.
*
* Creates a %GimpIntRadioFrame containing radio buttons for each item
* in the @store. The created widget takes ownership of @store.
*
* If you need to construct an empty #GimpIntRadioFrame, it's best to use
* g_object_new (GIMP_TYPE_INT_RADIO_FRAME, NULL).
*
* If you want to have a frame title with a mnemonic, set @title to
* %NULL instead and call [method@IntRadioFrame.set_title] instead.
*
* Returns: a new #GimpIntRadioFrame.
*
* Since: 3.0
**/
GtkWidget *
gimp_int_radio_frame_new_from_store (const gchar *title,
GimpIntStore *store)
{
GtkWidget *radio_frame;
radio_frame = g_object_new (GIMP_TYPE_INT_RADIO_FRAME,
"label", title,
"store", store,
NULL);
return radio_frame;
}
/**
* gimp_int_radio_frame_new: (skip)
* @first_label: the label of the first item
* @first_value: the value of the first item
* @...: a %NULL terminated list of more label, value pairs
*
* Creates a GtkComboBox that has integer values associated with each
* item. The items to fill the combo box with are specified as a %NULL
* terminated list of label/value pairs.
*
* If you need to construct an empty #GimpIntRadioFrame, it's best to use
* g_object_new (GIMP_TYPE_INT_RADIO_FRAME, NULL).
*
* Returns: a new #GimpIntRadioFrame.
*
* Since: 3.0
**/
GtkWidget *
gimp_int_radio_frame_new (const gchar *first_label,
gint first_value,
...)
{
GtkWidget *radio_frame;
va_list args;
va_start (args, first_value);
radio_frame = gimp_int_radio_frame_new_valist (first_label, first_value, args);
va_end (args);
return radio_frame;
}
/**
* gimp_int_radio_frame_new_valist: (skip)
* @first_label: the label of the first item
* @first_value: the value of the first item
* @values: a va_list with more values
*
* A variant of gimp_int_radio_frame_new() that takes a va_list of
* label/value pairs.
*
* Returns: a new #GimpIntRadioFrame.
*
* Since: 3.0
**/
GtkWidget *
gimp_int_radio_frame_new_valist (const gchar *first_label,
gint first_value,
va_list values)
{
GtkWidget *radio_frame;
GtkListStore *store;
store = gimp_int_store_new_valist (first_label, first_value, values);
radio_frame = g_object_new (GIMP_TYPE_INT_RADIO_FRAME,
"store", store,
NULL);
return radio_frame;
}
/**
* gimp_int_radio_frame_new_array: (rename-to gimp_int_radio_frame_new)
* @labels: (array zero-terminated=1): a %NULL-terminated array of labels.
*
* A variant of gimp_int_radio_frame_new() that takes an array of labels.
* The array indices are used as values.
*
* Returns: a new #GimpIntRadioFrame.
*
* Since: 3.0
**/
GtkWidget *
gimp_int_radio_frame_new_array (const gchar *labels[])
{
GtkWidget *frame;
GtkListStore *store;
gint i;
g_return_val_if_fail (labels != NULL, NULL);
frame = g_object_new (GIMP_TYPE_INT_RADIO_FRAME, NULL);
store = GTK_LIST_STORE (GIMP_INT_RADIO_FRAME (frame)->store);
for (i = 0; labels[i] != NULL; i++)
{
GtkTreeIter iter;
if (labels[i])
{
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
GIMP_INT_STORE_VALUE, i,
GIMP_INT_STORE_LABEL, labels[i],
-1);
}
}
return frame;
}
/**
* gimp_int_radio_frame_prepend: (skip)
* @radio_frame: a #GimpIntRadioFrame
* @...: pairs of column number and value, terminated with -1
*
* This function provides a convenient way to prepend items to a
* #GimpIntRadioFrame. It prepends a row to the @radio_frame's list store
* and calls gtk_list_store_set() for you.
*
* The column number must be taken from the enum #GimpIntStoreColumns.
*
* Since: 3.0
**/
void
gimp_int_radio_frame_prepend (GimpIntRadioFrame *radio_frame,
...)
{
GtkListStore *store;
GtkTreeIter iter;
va_list args;
g_return_if_fail (GIMP_IS_INT_RADIO_FRAME (radio_frame));
store = GTK_LIST_STORE (radio_frame->store);
va_start (args, radio_frame);
gtk_list_store_prepend (store, &iter);
gtk_list_store_set_valist (store, &iter, args);
va_end (args);
}
/**
* gimp_int_radio_frame_append: (skip)
* @radio_frame: a #GimpIntRadioFrame
* @...: pairs of column number and value, terminated with -1
*
* This function provides a convenient way to append items to a
* #GimpIntRadioFrame. It appends a row to the @radio_frame's list store
* and calls gtk_list_store_set() for you.
*
* The column number must be taken from the enum #GimpIntStoreColumns.
*
* Since: 3.0
**/
void
gimp_int_radio_frame_append (GimpIntRadioFrame *radio_frame,
...)
{
GtkListStore *store;
GtkTreeIter iter;
va_list args;
g_return_if_fail (GIMP_IS_INT_RADIO_FRAME (radio_frame));
store = GTK_LIST_STORE (radio_frame->store);
va_start (args, radio_frame);
gtk_list_store_append (store, &iter);
gtk_list_store_set_valist (store, &iter, args);
va_end (args);
}
/**
* gimp_int_radio_frame_set_title:
* @frame: a #GimpIntRadioFrame
* @title: the frame title or %NULL for none.
* @with_mnemonic: whether @title has a mnemonic.
*
* Change the @frame's title, possibly with a mnemonic.
*
* Since: 3.0
**/
void
gimp_int_radio_frame_set_title (GimpIntRadioFrame *frame,
const gchar *title,
gboolean with_mnemonic)
{
g_return_if_fail (GIMP_IS_INT_RADIO_FRAME (frame));
gtk_frame_set_label (GTK_FRAME (frame), NULL);
gtk_frame_set_label_widget (GTK_FRAME (frame), NULL);
if (with_mnemonic && title)
{
GtkWidget *label;
label = gtk_label_new_with_mnemonic (title);
gtk_frame_set_label_widget (GTK_FRAME (frame), label);
gtk_widget_show (label);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), frame->group->data);
}
else
{
gtk_frame_set_label (GTK_FRAME (frame), title);
}
}
/**
* gimp_int_radio_frame_set_active:
* @radio_frame: a #GimpIntRadioFrame
* @value: an integer value
*
* Looks up the item that belongs to the given @value and makes it the
* selected item in the @radio_frame.
*
* Returns: %TRUE on success (value changed or not) or %FALSE if there
* was no item for this value.
*
* Since: 3.0
**/
gboolean
gimp_int_radio_frame_set_active (GimpIntRadioFrame *frame,
gint value)
{
GtkWidget *button;
GSList *iter = frame->group;
g_return_val_if_fail (GIMP_IS_INT_RADIO_FRAME (frame), FALSE);
for (; iter; iter = g_slist_next (iter))
{
button = GTK_WIDGET (iter->data);
if (g_object_get_data (G_OBJECT (button), "gimp-radio-frame-value") ==
GINT_TO_POINTER (value))
{
if (! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
return TRUE;
}
}
return FALSE;
}
/**
* gimp_int_radio_frame_get_active:
* @radio_frame: a #GimpIntRadioFrame
*
* Returns: the value of the active item.
*
* Since:3.0
**/
gint
gimp_int_radio_frame_get_active (GimpIntRadioFrame *frame)
{
g_return_val_if_fail (GIMP_IS_INT_RADIO_FRAME (frame), FALSE);
return frame->value;
}
/**
* gimp_int_radio_frame_set_active_by_user_data:
* @radio_frame: a #GimpIntRadioFrame
* @user_data: an integer value
*
* Looks up the item that has the given @user_data and makes it the
* selected item in the @radio_frame.
*
* Returns: %TRUE on success or %FALSE if there was no item for
* this user-data.
*
* Since: 3.0
**/
gboolean
gimp_int_radio_frame_set_active_by_user_data (GimpIntRadioFrame *radio_frame,
gpointer user_data)
{
GtkTreeIter iter;
g_return_val_if_fail (GIMP_IS_INT_RADIO_FRAME (radio_frame), FALSE);
if (gimp_int_store_lookup_by_user_data (GTK_TREE_MODEL (radio_frame->store),
user_data, &iter))
{
gint value;
gtk_tree_model_get (GTK_TREE_MODEL (radio_frame->store), &iter,
GIMP_INT_STORE_VALUE, &value,
-1);
gimp_int_radio_frame_set_active (radio_frame, value);
return TRUE;
}
return FALSE;
}
/**
* gimp_int_radio_frame_get_active_user_data:
* @radio_frame: a #GimpIntRadioFrame
* @user_data: (out) (transfer none): return location for the gpointer value
*
* Retrieves the user-data of the selected (active) item in the @radio_frame.
*
* Returns: %TRUE if @user_data has been set or %FALSE if no item was
* active.
*
* Since: 3.0
**/
gboolean
gimp_int_radio_frame_get_active_user_data (GimpIntRadioFrame *radio_frame,
gpointer *user_data)
{
GtkTreeIter iter;
g_return_val_if_fail (GIMP_IS_INT_RADIO_FRAME (radio_frame), FALSE);
g_return_val_if_fail (user_data != NULL, FALSE);
if (gimp_int_store_lookup_by_value (GTK_TREE_MODEL (radio_frame->store),
radio_frame->value, &iter))
{
gtk_tree_model_get (GTK_TREE_MODEL (radio_frame->store), &iter,
GIMP_INT_STORE_USER_DATA, user_data,
-1);
return TRUE;
}
return FALSE;
}
/**
* gimp_int_radio_frame_set_sensitivity:
* @radio_frame: a #GimpIntRadioFrame
* @func: a function that returns a boolean value, or %NULL to unset
* @data: data to pass to @func
* @destroy: destroy notification for @data
*
* Sets a function that is used to decide about the sensitivity of radio
* buttons in the @radio_frame. Use this if you want to set certain
* radio buttons insensitive.
*
* Calling gtk_widget_queue_draw() on the @radio_frame will cause the
* sensitivity to be updated.
*
* Since: 3.0
**/
void
gimp_int_radio_frame_set_sensitivity (GimpIntRadioFrame *radio_frame,
GimpIntRadioFrameSensitivityFunc func,
gpointer data,
GDestroyNotify destroy)
{
g_return_if_fail (GIMP_IS_INT_RADIO_FRAME (radio_frame));
if (radio_frame->sensitivity_destroy)
{
GDestroyNotify destroy = radio_frame->sensitivity_destroy;
radio_frame->sensitivity_destroy = NULL;
destroy (radio_frame->sensitivity_data);
}
radio_frame->sensitivity_func = func;
radio_frame->sensitivity_data = data;
radio_frame->sensitivity_destroy = destroy;
}
/* Private functions */
static gboolean
gimp_int_radio_frame_draw (GtkWidget *widget,
cairo_t *cr,
gpointer user_data)
{
gimp_int_radio_frame_update_sensitivity (GIMP_INT_RADIO_FRAME (widget));
return FALSE;
}
static void
gimp_int_radio_frame_fill (GimpIntRadioFrame *frame)
{
g_return_if_fail (GIMP_IS_INT_RADIO_FRAME (frame));
g_clear_pointer (&frame->group, g_slist_free);
gtk_container_foreach (GTK_CONTAINER (frame->box),
(GtkCallback) gtk_widget_destroy, NULL);
if (frame->store)
{
GtkTreeModel *model;
GSList *group = NULL;
GtkTreeIter iter;
gboolean iter_valid;
model = GTK_TREE_MODEL (frame->store);
for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &iter))
{
GtkWidget *button;
gchar *label;
gint value;
gtk_tree_model_get (model, &iter,
GIMP_INT_STORE_LABEL, &label,
GIMP_INT_STORE_VALUE, &value,
-1);
button = gtk_radio_button_new_with_mnemonic (group, label);
gtk_box_pack_start (GTK_BOX (frame->box), button, FALSE, FALSE, 0);
gtk_widget_show (button);
g_free (label);
group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
g_object_set_data (G_OBJECT (button), "gimp-radio-frame-value",
GINT_TO_POINTER (value));
g_signal_connect (button, "toggled",
G_CALLBACK (gimp_int_radio_frame_button_toggled),
frame);
}
frame->group = g_slist_copy (group);
gimp_int_radio_frame_set_active (frame, frame->value);
}
}
static void
gimp_int_radio_frame_set_store (GimpIntRadioFrame *frame,
GimpIntStore *store)
{
g_return_if_fail (GIMP_IS_INT_RADIO_FRAME (frame));
g_return_if_fail (GIMP_IS_INT_STORE (store));
if (frame->store == store)
return;
if (frame->store)
{
g_signal_handlers_disconnect_by_func (frame->store,
(GCallback) gimp_int_radio_frame_fill,
NULL);
g_object_unref (frame->store);
}
frame->store = g_object_ref (store);
if (frame->store)
{
g_signal_connect_object (frame->store, "row-changed",
(GCallback) gimp_int_radio_frame_fill,
frame, G_CONNECT_SWAPPED);
g_signal_connect_object (frame->store, "row-deleted",
(GCallback) gimp_int_radio_frame_fill,
frame, G_CONNECT_SWAPPED);
g_signal_connect_object (frame->store, "row-inserted",
(GCallback) gimp_int_radio_frame_fill,
frame, G_CONNECT_SWAPPED);
g_signal_connect_object (frame->store, "rows-reordered",
(GCallback) gimp_int_radio_frame_fill,
frame, G_CONNECT_SWAPPED);
}
gimp_int_radio_frame_fill (frame);
g_object_notify (G_OBJECT (frame), "store");
}
static void
gimp_int_radio_frame_update_sensitivity (GimpIntRadioFrame *frame)
{
GSList *iter = frame->group;
g_return_if_fail (GIMP_IS_INT_RADIO_FRAME (frame));
if (! frame->sensitivity_func)
return;
for (; iter; iter = g_slist_next (iter))
{
GtkWidget *button = GTK_WIDGET (iter->data);
GtkTreeIter tree_iter;
gint value;
value = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "gimp-radio-frame-value"));
gtk_widget_set_sensitive (button, TRUE);
if (gimp_int_store_lookup_by_value (GTK_TREE_MODEL (frame->store), value, &tree_iter))
{
gpointer user_data;
gint new_value = value;
gtk_tree_model_get (GTK_TREE_MODEL (frame->store), &tree_iter,
GIMP_INT_STORE_USER_DATA, &user_data,
-1);
if (! frame->sensitivity_func (value, user_data, &new_value,
frame->sensitivity_data))
{
if (new_value != value)
gimp_int_radio_frame_set_active (frame, new_value);
gtk_widget_set_sensitive (button, FALSE);
}
}
}
}
static void
gimp_int_radio_frame_button_toggled (GtkToggleButton *button,
GimpIntRadioFrame *frame)
{
g_return_if_fail (GIMP_IS_INT_RADIO_FRAME (frame));
g_return_if_fail (GTK_IS_RADIO_BUTTON (button));
if (gtk_toggle_button_get_active (button))
{
gint value;
value = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "gimp-radio-frame-value"));
if (frame->value != value)
{
frame->value = value;
g_object_notify (G_OBJECT (frame), "value");
}
}
}

View file

@ -0,0 +1,103 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpintradioframe.h
* Copyright (C) 2022 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#include <libgimpwidgets/gimpframe.h>
#ifndef __GIMP_INT_RADIO_FRAME_H__
#define __GIMP_INT_RADIO_FRAME_H__
G_BEGIN_DECLS
#define GIMP_TYPE_INT_RADIO_FRAME (gimp_int_radio_frame_get_type ())
G_DECLARE_FINAL_TYPE (GimpIntRadioFrame, gimp_int_radio_frame, GIMP, INT_RADIO_FRAME, GimpFrame)
/**
* GimpIntRadioFrameSensitivityFunc:
* @value: the value associated with a radio button.
* @user_data: the data associated with a radio button.
* @new_value: the value to check instead if the function returns %FALSE.
* @data: (closure): the data set in gimp_int_radio_frame_set_sensitivity()
*
* Signature for a function called on each radio button value and data,
* each time the %GimpIntRadioFrame is drawn, to make some radio button
* insensitive.
* If the function returns %FALSE, it usually means that the value is
* not a valid choice in current situation. In this case, you might want
* to toggle instead another value automatically. Set @new_value to the
* value to toggle. If you leave this untouched, the radio button will
* stay toggled despite being insensitive. This is up to you to decide
* whether this is meaningful.
*
* Returns: %TRUE if the button stays sensitive, %FALSE otherwise.
*/
typedef gboolean (* GimpIntRadioFrameSensitivityFunc) (gint value,
gpointer user_data,
gint *new_value,
gpointer data);
GtkWidget * gimp_int_radio_frame_new_from_store (const gchar *title,
GimpIntStore *store);
GtkWidget * gimp_int_radio_frame_new (const gchar *first_label,
gint first_value,
...) G_GNUC_NULL_TERMINATED;
GtkWidget * gimp_int_radio_frame_new_valist (const gchar *first_label,
gint first_value,
va_list values);
GtkWidget * gimp_int_radio_frame_new_array (const gchar *labels[]);
void gimp_int_radio_frame_prepend (GimpIntRadioFrame *radio_frame,
...);
void gimp_int_radio_frame_append (GimpIntRadioFrame *radio_frame,
...);
void gimp_int_radio_frame_set_title (GimpIntRadioFrame *frame,
const gchar *title,
gboolean with_mnemonic);
gboolean gimp_int_radio_frame_set_active (GimpIntRadioFrame *radio_frame,
gint value);
gint gimp_int_radio_frame_get_active (GimpIntRadioFrame *radio_frame);
gboolean
gimp_int_radio_frame_set_active_by_user_data (GimpIntRadioFrame *radio_frame,
gpointer user_data);
gboolean
gimp_int_radio_frame_get_active_user_data (GimpIntRadioFrame *radio_frame,
gpointer *user_data);
void gimp_int_radio_frame_set_sensitivity (GimpIntRadioFrame *radio_frame,
GimpIntRadioFrameSensitivityFunc func,
gpointer data,
GDestroyNotify destroy);
G_END_DECLS
#endif /* __GIMP_INT_RADIO_FRAME_H__ */

View file

@ -0,0 +1,453 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpintstore.c
* Copyright (C) 2004-2007 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "gimpwidgetstypes.h"
#include "gimpintstore.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimpintstore
* @title: GimpIntStore
* @short_description: A model for integer based name-value pairs
* (e.g. enums)
*
* A model for integer based name-value pairs (e.g. enums)
**/
enum
{
PROP_0,
PROP_USER_DATA_TYPE
};
typedef struct _GimpIntStorePrivate
{
GtkListStore parent_instance;
GtkTreeIter *empty_iter;
GType user_data_type;
} GimpIntStorePrivate;
#define GET_PRIVATE(obj) ((GimpIntStorePrivate *) gimp_int_store_get_instance_private ((GimpIntStore *) (obj)))
static void gimp_int_store_tree_model_init (GtkTreeModelIface *iface);
static void gimp_int_store_constructed (GObject *object);
static void gimp_int_store_finalize (GObject *object);
static void gimp_int_store_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_int_store_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_int_store_row_inserted (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter);
static void gimp_int_store_row_deleted (GtkTreeModel *model,
GtkTreePath *path);
static void gimp_int_store_add_empty (GimpIntStore *store);
G_DEFINE_TYPE_WITH_CODE (GimpIntStore, gimp_int_store, GTK_TYPE_LIST_STORE,
G_ADD_PRIVATE (GimpIntStore)
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
gimp_int_store_tree_model_init))
#define parent_class gimp_int_store_parent_class
static GtkTreeModelIface *parent_iface = NULL;
static void
gimp_int_store_class_init (GimpIntStoreClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = gimp_int_store_constructed;
object_class->finalize = gimp_int_store_finalize;
object_class->set_property = gimp_int_store_set_property;
object_class->get_property = gimp_int_store_get_property;
/**
* GimpIntStore:user-data-type:
*
* Sets the #GType for the GIMP_INT_STORE_USER_DATA column.
*
* You need to set this property when constructing the store if you want
* to use the GIMP_INT_STORE_USER_DATA column and want to have the store
* handle ref-counting of your user data.
*
* Since: 2.4
*/
g_object_class_install_property (object_class,
PROP_USER_DATA_TYPE,
g_param_spec_gtype ("user-data-type",
"User Data Type",
"The GType of the user_data column",
G_TYPE_NONE,
G_PARAM_CONSTRUCT_ONLY |
GIMP_PARAM_READWRITE));
}
static void
gimp_int_store_tree_model_init (GtkTreeModelIface *iface)
{
parent_iface = g_type_interface_peek_parent (iface);
iface->row_inserted = gimp_int_store_row_inserted;
iface->row_deleted = gimp_int_store_row_deleted;
}
static void
gimp_int_store_init (GimpIntStore *store)
{
}
static void
gimp_int_store_constructed (GObject *object)
{
GimpIntStore *store = GIMP_INT_STORE (object);
GimpIntStorePrivate *priv = GET_PRIVATE (store);
GType types[GIMP_INT_STORE_NUM_COLUMNS];
G_OBJECT_CLASS (parent_class)->constructed (object);
types[GIMP_INT_STORE_VALUE] = G_TYPE_INT;
types[GIMP_INT_STORE_LABEL] = G_TYPE_STRING;
types[GIMP_INT_STORE_ABBREV] = G_TYPE_STRING;
types[GIMP_INT_STORE_ICON_NAME] = G_TYPE_STRING;
types[GIMP_INT_STORE_PIXBUF] = GDK_TYPE_PIXBUF;
types[GIMP_INT_STORE_USER_DATA] = (priv->user_data_type != G_TYPE_NONE ?
priv->user_data_type : G_TYPE_POINTER);
gtk_list_store_set_column_types (GTK_LIST_STORE (store),
GIMP_INT_STORE_NUM_COLUMNS, types);
gimp_int_store_add_empty (store);
}
static void
gimp_int_store_finalize (GObject *object)
{
GimpIntStorePrivate *priv = GET_PRIVATE (object);
g_clear_pointer (&priv->empty_iter, gtk_tree_iter_free);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_int_store_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpIntStorePrivate *priv = GET_PRIVATE (object);
switch (property_id)
{
case PROP_USER_DATA_TYPE:
priv->user_data_type = g_value_get_gtype (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_int_store_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpIntStorePrivate *priv = GET_PRIVATE (object);
switch (property_id)
{
case PROP_USER_DATA_TYPE:
g_value_set_gtype (value, priv->user_data_type);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_int_store_row_inserted (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter)
{
GimpIntStore *store = GIMP_INT_STORE (model);
GimpIntStorePrivate *priv = GET_PRIVATE (store);
if (parent_iface->row_inserted)
parent_iface->row_inserted (model, path, iter);
if (priv->empty_iter &&
memcmp (iter, priv->empty_iter, sizeof (GtkTreeIter)))
{
gtk_list_store_remove (GTK_LIST_STORE (store), priv->empty_iter);
gtk_tree_iter_free (priv->empty_iter);
priv->empty_iter = NULL;
}
}
static void
gimp_int_store_row_deleted (GtkTreeModel *model,
GtkTreePath *path)
{
if (parent_iface->row_deleted)
parent_iface->row_deleted (model, path);
}
static void
gimp_int_store_add_empty (GimpIntStore *store)
{
GimpIntStorePrivate *priv = GET_PRIVATE (store);
GtkTreeIter iter = { 0, };
g_return_if_fail (priv->empty_iter == NULL);
gtk_list_store_prepend (GTK_LIST_STORE (store), &iter);
gtk_list_store_set (GTK_LIST_STORE (store), &iter,
GIMP_INT_STORE_VALUE, -1,
/* This string appears in an empty menu as in
* "nothing selected and nothing to select"
*/
GIMP_INT_STORE_LABEL, (_("(Empty)")),
-1);
priv->empty_iter = gtk_tree_iter_copy (&iter);
}
/**
* gimp_int_store_new: (skip)
* @first_label: the label of the first item
* @first_value: the value of the first item
* @...: a %NULL terminated list of more label, value pairs
*
* Creates a #GtkListStore with a number of useful columns.
* #GimpIntStore is especially useful if the items you want to store
* are identified using an integer value.
*
* If you need to construct an empty #GimpIntStore, it's best to use
* g_object_new (GIMP_TYPE_INT_STORE, NULL).
*
* Returns: a new #GimpIntStore.
*
* Since: 2.2
**/
GtkListStore *
gimp_int_store_new (const gchar *first_label,
gint first_value,
...)
{
GtkListStore *store;
va_list args;
va_start (args, first_value);
store = gimp_int_store_new_valist (first_label, first_value, args);
va_end (args);
return store;
}
/**
* gimp_int_store_new_valist: (skip)
* @first_label: the label of the first item
* @first_value: the value of the first item
* @values: a va_list with more values
*
* A variant of gimp_int_store_new() that takes a va_list of
* label/value pairs.
*
* Returns: a new #GimpIntStore.
*
* Since: 3.0
**/
GtkListStore *
gimp_int_store_new_valist (const gchar *first_label,
gint first_value,
va_list values)
{
GtkListStore *store;
const gchar *label;
gint value;
store = g_object_new (GIMP_TYPE_INT_STORE, NULL);
for (label = first_label, value = first_value;
label;
label = va_arg (values, const gchar *), value = va_arg (values, gint))
{
GtkTreeIter iter = { 0, };
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
GIMP_INT_STORE_VALUE, value,
GIMP_INT_STORE_LABEL, label,
-1);
}
return store;
}
/**
* gimp_int_store_new_array: (rename-to gimp_int_store_new)
* @n_values: the number of values
* @labels: (array length=n_values): an array of labels
*
* A variant of gimp_int_store_new() that takes an array of labels.
* The array indices are used as values.
*
* Returns: a new #GtkListStore.
*
* Since: 3.0
**/
GtkListStore *
gimp_int_store_new_array (gint n_values,
const gchar *labels[])
{
GtkListStore *store;
gint i;
g_return_val_if_fail (n_values >= 0, NULL);
g_return_val_if_fail (labels != NULL || n_values == 0, NULL);
store = g_object_new (GIMP_TYPE_INT_STORE, NULL);
for (i = 0; i < n_values; i++)
{
GtkTreeIter iter;
if (labels[i])
{
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
GIMP_INT_STORE_VALUE, i,
GIMP_INT_STORE_LABEL, gettext (labels[i]),
-1);
}
}
return store;
}
/**
* gimp_int_store_lookup_by_value:
* @model: a #GimpIntStore
* @value: an integer value to lookup in the @model
* @iter: (out): return location for the iter of the given @value
*
* Iterate over the @model looking for @value.
*
* Returns: %TRUE if the value has been located and @iter is
* valid, %FALSE otherwise.
*
* Since: 2.2
**/
gboolean
gimp_int_store_lookup_by_value (GtkTreeModel *model,
gint value,
GtkTreeIter *iter)
{
gboolean iter_valid;
g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
g_return_val_if_fail (iter != NULL, FALSE);
for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, iter))
{
gint this;
gtk_tree_model_get (model, iter,
GIMP_INT_STORE_VALUE, &this,
-1);
if (this == value)
break;
}
return iter_valid;
}
/**
* gimp_int_store_lookup_by_user_data:
* @model: a #GimpIntStore
* @user_data: a gpointer "user-data" to lookup in the @model
* @iter: (out): return location for the iter of the given @user_data
*
* Iterate over the @model looking for @user_data.
*
* Returns: %TRUE if the user-data has been located and @iter is
* valid, %FALSE otherwise.
*
* Since: 2.10
**/
gboolean
gimp_int_store_lookup_by_user_data (GtkTreeModel *model,
gpointer user_data,
GtkTreeIter *iter)
{
gboolean iter_valid = FALSE;
g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
g_return_val_if_fail (iter != NULL, FALSE);
for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, iter))
{
gpointer this;
gtk_tree_model_get (model, iter,
GIMP_INT_STORE_USER_DATA, &this,
-1);
if (this == user_data)
break;
}
return (gboolean) iter_valid;
}

View file

@ -0,0 +1,96 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpintstore.c
* Copyright (C) 2004 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_INT_STORE_H__
#define __GIMP_INT_STORE_H__
G_BEGIN_DECLS
/**
* GimpIntStoreColumns:
* @GIMP_INT_STORE_VALUE: the integer value
* @GIMP_INT_STORE_LABEL: a human-readable label
* @GIMP_INT_STORE_ABBREV: an abbreviated label
* @GIMP_INT_STORE_ICON_NAME: an icon name
* @GIMP_INT_STORE_PIXBUF: a #GdkPixbuf
* @GIMP_INT_STORE_USER_DATA: arbitrary user data
* @GIMP_INT_STORE_NUM_COLUMNS: the number of columns
*
* The column types of #GimpIntStore.
**/
typedef enum
{
GIMP_INT_STORE_VALUE,
GIMP_INT_STORE_LABEL,
GIMP_INT_STORE_ABBREV,
GIMP_INT_STORE_ICON_NAME,
GIMP_INT_STORE_PIXBUF,
GIMP_INT_STORE_USER_DATA,
GIMP_INT_STORE_NUM_COLUMNS
} GimpIntStoreColumns;
#define GIMP_TYPE_INT_STORE (gimp_int_store_get_type ())
G_DECLARE_DERIVABLE_TYPE (GimpIntStore, gimp_int_store, GIMP, INT_STORE, GtkListStore)
struct _GimpIntStoreClass
{
GtkListStoreClass parent_class;
/* Padding for future expansion */
void (* _gimp_reserved0) (void);
void (* _gimp_reserved1) (void);
void (* _gimp_reserved2) (void);
void (* _gimp_reserved3) (void);
void (* _gimp_reserved4) (void);
void (* _gimp_reserved5) (void);
void (* _gimp_reserved6) (void);
void (* _gimp_reserved7) (void);
void (* _gimp_reserved8) (void);
void (* _gimp_reserved9) (void);
};
GtkListStore * gimp_int_store_new (const gchar *first_label,
gint first_value,
...) G_GNUC_NULL_TERMINATED;
GtkListStore * gimp_int_store_new_valist (const gchar *first_label,
gint first_value,
va_list values);
GtkListStore * gimp_int_store_new_array (gint n_values,
const gchar *labels[]);
gboolean gimp_int_store_lookup_by_value (GtkTreeModel *model,
gint value,
GtkTreeIter *iter);
gboolean gimp_int_store_lookup_by_user_data (GtkTreeModel *model,
gpointer user_data,
GtkTreeIter *iter);
G_END_DECLS
#endif /* __GIMP_INT_STORE_H__ */

View file

@ -0,0 +1,441 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimplabelcolor.c
* Copyright (C) 2022 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpcolor/gimpcolor.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpbase/gimpbase.h"
#include "gimpwidgets.h"
#include "gimpwidgets-private.h"
/**
* SECTION: gimplabelcolor
* @title: GimpLabelColor
* @short_description: Widget containing a color widget and a label.
*
* This widget is a subclass of #GimpLabeled with a #GtkColor.
**/
enum
{
VALUE_CHANGED,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_VALUE,
PROP_EDITABLE,
N_PROPS
};
static GParamSpec *object_props[N_PROPS] = { NULL, };
struct _GimpLabelColor
{
GimpLabeled parent_instance;
GtkWidget *area;
gboolean editable;
};
static void gimp_label_color_constructed (GObject *object);
static void gimp_label_color_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_label_color_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static GtkWidget * gimp_label_color_populate (GimpLabeled *color,
gint *x,
gint *y,
gint *width,
gint *height);
G_DEFINE_TYPE (GimpLabelColor, gimp_label_color, GIMP_TYPE_LABELED)
#define parent_class gimp_label_color_parent_class
static guint gimp_label_color_signals[LAST_SIGNAL] = { 0 };
static void
gimp_label_color_class_init (GimpLabelColorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GimpLabeledClass *labeled_class = GIMP_LABELED_CLASS (klass);
gimp_label_color_signals[VALUE_CHANGED] =
g_signal_new ("value-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->constructed = gimp_label_color_constructed;
object_class->set_property = gimp_label_color_set_property;
object_class->get_property = gimp_label_color_get_property;
labeled_class->populate = gimp_label_color_populate;
babl_init ();
/**
* GimpLabelColor:value:
*
* The currently set value.
*
* Since: 3.0
**/
object_props[PROP_VALUE] = gimp_param_spec_color_from_string ("value",
"Color",
"The displayed color",
TRUE, "black",
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT);
/**
* GimpLabelColor:editable:
*
* Whether the color can be edited.
*
* Since: 3.0
**/
object_props[PROP_EDITABLE] = g_param_spec_boolean ("editable",
"Whether the color can be edited",
"Whether the color can be edited",
FALSE,
GIMP_PARAM_READWRITE);
g_object_class_install_properties (object_class, N_PROPS, object_props);
}
static void
gimp_label_color_init (GimpLabelColor *color)
{
GeglColor *black = gegl_color_new ("black");
color->editable = FALSE;
color->area = gimp_color_area_new (black, GIMP_COLOR_AREA_SMALL_CHECKS,
GDK_BUTTON1_MASK | GDK_BUTTON2_MASK);
/* Typically for a labelled color area, a small square next to your
* label is probably what you want to display.
*/
gtk_widget_set_size_request (color->area, 20, 20);
g_object_unref (black);
}
static void
gimp_label_color_constructed (GObject *object)
{
GimpLabelColor *color = GIMP_LABEL_COLOR (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
/* This is important to make this object into a property widget. It
* will allow config object to bind the "value" property of this
* widget, and therefore be updated automatically.
*/
g_object_bind_property (G_OBJECT (color->area), "color",
G_OBJECT (color), "value",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
}
static void
gimp_label_color_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpLabelColor *lcolor = GIMP_LABEL_COLOR (object);
switch (property_id)
{
case PROP_VALUE:
{
GeglColor *new_color;
GeglColor *color;
new_color = g_value_get_object (value);
g_object_get (lcolor->area,
"color", &color,
NULL);
/* Avoid looping forever since we have bound this widget's
* "value" property with the color button "value" property.
*/
if (! gimp_color_is_perceptually_identical (color, new_color))
{
g_object_set (lcolor->area, "color", new_color, NULL);
g_signal_emit (object, gimp_label_color_signals[VALUE_CHANGED], 0);
}
g_object_unref (color);
}
break;
case PROP_EDITABLE:
if (lcolor->editable != g_value_get_boolean (value))
{
const gchar *dialog_title;
GimpLabeled *labeled;
GeglColor *color;
GimpColorAreaType type;
gboolean attached;
labeled = GIMP_LABELED (lcolor);
/* Reuse the label contents (without mnemonics) as dialog
* title for the color selection. This is why the "editable"
* property must not be a G_PARAM_CONSTRUCT.
*/
dialog_title = gtk_label_get_text (GTK_LABEL (gimp_labeled_get_label (labeled)));
attached = (gtk_widget_get_parent (lcolor->area) != NULL);
g_object_get (lcolor->area,
"type", &type,
"color", &color,
NULL);
gtk_widget_destroy (lcolor->area);
lcolor->editable = g_value_get_boolean (value);
if (lcolor->editable)
lcolor->area = gimp_color_button_new (dialog_title,
20, 20, color, type);
else
lcolor->area = gimp_color_area_new (color, type,
GDK_BUTTON1_MASK | GDK_BUTTON2_MASK);
g_object_unref (color);
gtk_widget_set_size_request (lcolor->area, 20, 20);
g_object_bind_property (G_OBJECT (lcolor->area), "color",
G_OBJECT (lcolor), "value",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
if (attached)
{
gtk_grid_attach (GTK_GRID (lcolor), lcolor->area, 1, 0, 1, 1);
gtk_widget_show (lcolor->area);
g_signal_emit_by_name (object, "mnemonic-widget-changed", lcolor->area);
}
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_label_color_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpLabelColor *lcolor = GIMP_LABEL_COLOR (object);
switch (property_id)
{
case PROP_VALUE:
{
GeglColor *color;
g_object_get (lcolor->area,
"color", &color,
NULL);
g_value_take_object (value, color);
}
break;
case PROP_EDITABLE:
g_value_set_boolean (value, lcolor->editable);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static GtkWidget *
gimp_label_color_populate (GimpLabeled *labeled,
gint *x,
gint *y,
gint *width,
gint *height)
{
GimpLabelColor *color = GIMP_LABEL_COLOR (labeled);
gtk_grid_attach (GTK_GRID (color), color->area, 1, 0, 1, 1);
/* Make sure the label and color won't be glued next to each other's. */
gtk_grid_set_column_spacing (GTK_GRID (color),
4 * gtk_widget_get_scale_factor (GTK_WIDGET (color)));
gtk_widget_show (color->area);
return color->area;
}
/* Public Functions */
/**
* gimp_label_color_new:
* @label: The text for the #GtkLabel.
* @color: The color displayed.
*
* Creates a #GimpLabelColor which contains a widget and displays a
* color area. By default, the color area is of type
* %GIMP_COLOR_AREA_SMALL_CHECKS, which means transparency of @color
* will be shown.
*
* Moreover in the non-editable case, the color is draggable to other
* widgets accepting color drops with buttons 1 and 2.
* In the editable case, the @label is reused as the color chooser's
* dialog title.
*
* If you wish to customize any of these default behaviors, get the
* #GimpColorArea or #GimpColorButton with gimp_label_color_get_color_widget().
*
* Returns: (transfer full): The new #GimpLabelColor widget.
**/
GtkWidget *
gimp_label_color_new (const gchar *label,
GeglColor *color,
gboolean editable)
{
GtkWidget *labeled;
labeled = g_object_new (GIMP_TYPE_LABEL_COLOR,
"label", label,
"value", color,
"editable", editable,
NULL);
return labeled;
}
/**
* gimp_label_color_set_value:
* @color: The #GtkLabelColor.
* @value: A new value.
*
* This function sets the value in the #GtkColor inside @color.
**/
void
gimp_label_color_set_value (GimpLabelColor *color,
GeglColor *value)
{
g_return_if_fail (GIMP_IS_LABEL_COLOR (color));
g_return_if_fail (GEGL_IS_COLOR (value));
g_object_set (color,
"value", value,
NULL);
}
/**
* gimp_label_color_get_value:
* @color: The #GtkLabelColor.
*
* This function returns the value shown by @color.
*
* Returns: (transfer full): a copy of the [class@Gegl.Color] used by the widget.
**/
GeglColor *
gimp_label_color_get_value (GimpLabelColor *color)
{
GeglColor *value = NULL;
GeglColor *retval;
g_return_val_if_fail (GIMP_IS_LABEL_COLOR (color), NULL);
g_object_get (color->area,
"color", &value,
NULL);
retval = gegl_color_duplicate (value);
g_clear_object (&value);
return retval;
}
/**
* gimp_label_color_set_editable:
* @color: The #GtkLabelColor.
* @editable: Whether the color should be editable.
*
* Changes the editability of the color.
**/
void
gimp_label_color_set_editable (GimpLabelColor *color,
gboolean editable)
{
g_return_if_fail (GIMP_IS_LABEL_COLOR (color));
g_object_set (color, "editable", editable, NULL);
}
/**
* gimp_label_color_is_editable:
* @color: The #GtkLabelColor.
*
* This function tells whether the color widget allows to edit the
* color.
* Returns: %TRUE if the color is editable.
**/
gboolean
gimp_label_color_is_editable (GimpLabelColor *color)
{
g_return_val_if_fail (GIMP_IS_LABEL_COLOR (color), FALSE);
return GIMP_IS_COLOR_SELECT (color->area);
}
/**
* gimp_label_color_get_color_widget:
* @color: The #GimpLabelColor
*
* This function returns the color widget packed in @color, which can be
* either a #GimpColorButton (if the @color is editable) or a
* #GimpColorArea otherwise.
*
* Returns: (transfer none): The color widget packed in @color.
**/
GtkWidget *
gimp_label_color_get_color_widget (GimpLabelColor *color)
{
g_return_val_if_fail (GIMP_IS_LABEL_COLOR (color), NULL);
return color->area;
}

View file

@ -0,0 +1,58 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimplabelcolor.h
* Copyright (C) 2022 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_LABEL_COLOR_H__
#define __GIMP_LABEL_COLOR_H__
#include <libgimpwidgets/gimplabeled.h>
G_BEGIN_DECLS
#define GIMP_TYPE_LABEL_COLOR (gimp_label_color_get_type ())
G_DECLARE_FINAL_TYPE (GimpLabelColor, gimp_label_color, GIMP, LABEL_COLOR, GimpLabeled)
GtkWidget * gimp_label_color_new (const gchar *label,
GeglColor *color,
gboolean editable);
/* TODO: it would be interesting for such a widget to have an API to
* customize the label being also above or below, left or right. I could
* imagine wanting to pretty-list several colors with specific layouts.
*/
void gimp_label_color_set_value (GimpLabelColor *color,
GeglColor *value);
GeglColor * gimp_label_color_get_value (GimpLabelColor *color);
void gimp_label_color_set_editable (GimpLabelColor *color,
gboolean editable);
gboolean gimp_label_color_is_editable (GimpLabelColor *color);
GtkWidget * gimp_label_color_get_color_widget (GimpLabelColor *color);
G_END_DECLS
#endif /* __GIMP_LABEL_COLOR_H__ */

View file

@ -0,0 +1,274 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimplabeled.c
* Copyright (C) 2020 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpmath/gimpmath.h"
#include "libgimpbase/gimpbase.h"
#include "gimpwidgets.h"
/**
* SECTION: gimplabeled
* @title: GimpLabeled
* @short_description: Widget containing a label as mnemonic for another
* widget.
*
* This widget is a #GtkGrid showing a #GtkLabel used as mnemonic on
* another widget.
**/
enum
{
MNEMONIC_WIDGET_CHANGED,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_LABEL,
};
typedef struct _GimpLabeledPrivate
{
GtkWidget *label;
} GimpLabeledPrivate;
static void gimp_labeled_constructed (GObject *object);
static void gimp_labeled_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_labeled_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_labeled_real_mnemonic_widget_changed (GimpLabeled *labeled,
GtkWidget *widget);
G_DEFINE_TYPE_WITH_PRIVATE (GimpLabeled, gimp_labeled, GTK_TYPE_GRID)
#define parent_class gimp_labeled_parent_class
static guint signals[LAST_SIGNAL] = { 0 };
static void
gimp_labeled_class_init (GimpLabeledClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = gimp_labeled_constructed;
object_class->set_property = gimp_labeled_set_property;
object_class->get_property = gimp_labeled_get_property;
signals[MNEMONIC_WIDGET_CHANGED] =
g_signal_new ("mnemonic-widget-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpLabeledClass, mnemonic_widget_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
GTK_TYPE_WIDGET);
klass->mnemonic_widget_changed = gimp_labeled_real_mnemonic_widget_changed;
/**
* GimpLabeled:label:
*
* Label text with pango markup and mnemonic.
*
* Since: 3.0
**/
g_object_class_install_property (object_class, PROP_LABEL,
g_param_spec_string ("label",
"Label text",
"The text of the label part of this widget",
NULL,
GIMP_PARAM_READWRITE));
}
static void
gimp_labeled_init (GimpLabeled *labeled)
{
}
static void
gimp_labeled_constructed (GObject *object)
{
GimpLabeledClass *klass;
GimpLabeled *labeled = GIMP_LABELED (object);
GimpLabeledPrivate *priv = gimp_labeled_get_instance_private (labeled);
GtkWidget *mnemonic_widget;
gint x = 0;
gint y = 0;
gint width = 1;
gint height = 1;
G_OBJECT_CLASS (parent_class)->constructed (object);
priv->label = gtk_label_new_with_mnemonic (NULL);
gtk_label_set_xalign (GTK_LABEL (priv->label), 0.0);
klass = GIMP_LABELED_GET_CLASS (labeled);
g_return_if_fail (klass->populate);
mnemonic_widget = klass->populate (labeled, &x, &y, &width, &height);
g_signal_emit (object, signals[MNEMONIC_WIDGET_CHANGED], 0,
mnemonic_widget);
gtk_grid_attach (GTK_GRID (labeled), priv->label, x, y, width, height);
gtk_widget_show (priv->label);
}
static void
gimp_labeled_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpLabeled *entry = GIMP_LABELED (object);
GimpLabeledPrivate *priv = gimp_labeled_get_instance_private (entry);
switch (property_id)
{
case PROP_LABEL:
{
/* This should not happen since the property is **not** set with
* G_PARAM_CONSTRUCT, hence the label should exist when the
* property is first set.
*/
g_return_if_fail (priv->label);
gtk_label_set_markup_with_mnemonic (GTK_LABEL (priv->label),
g_value_get_string (value));
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_labeled_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpLabeled *entry = GIMP_LABELED (object);
GimpLabeledPrivate *priv = gimp_labeled_get_instance_private (entry);
switch (property_id)
{
case PROP_LABEL:
g_value_set_string (value, gtk_label_get_label (GTK_LABEL (priv->label)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_labeled_real_mnemonic_widget_changed (GimpLabeled *labeled,
GtkWidget *widget)
{
GimpLabeledPrivate *priv = gimp_labeled_get_instance_private (labeled);
gtk_label_set_mnemonic_widget (GTK_LABEL (priv->label), widget);
}
/* Public functions */
/**
* gimp_labeled_get_label:
* @labeled: The #GimpLabeled.
*
* This function returns the #GtkLabel packed in @labeled. This can be
* useful if you need to customize some aspects of the widget.
*
* Returns: (transfer none) (type GtkLabel): The #GtkLabel contained in @labeled.
**/
GtkWidget *
gimp_labeled_get_label (GimpLabeled *labeled)
{
GimpLabeledPrivate *priv = gimp_labeled_get_instance_private (labeled);
g_return_val_if_fail (GIMP_IS_LABELED (labeled), NULL);
return priv->label;
}
/**
* gimp_labeled_get_text:
* @labeled: the #GimpLabeled.
*
* This function will return exactly what you entered with
* gimp_labeled_set_text() or through the "label" property because this
* class expects labels to have mnemonics (and allows Pango formatting).
* To obtain instead the text as displayed with mnemonics and markup
* removed, call:
* |[<!-- language="C" -->
* gtk_label_get_text (GTK_LABEL (gimp_labeled_get_label (@labeled)));
* ]|
*
* Returns: the label text as entered, which includes pango markup and
* mnemonics similarly to gtk_label_get_label().
*/
const gchar *
gimp_labeled_get_text (GimpLabeled *labeled)
{
GimpLabeledPrivate *priv = gimp_labeled_get_instance_private (labeled);
g_return_val_if_fail (GIMP_IS_LABELED (labeled), NULL);
return gtk_label_get_label (GTK_LABEL (priv->label));
}
/**
* gimp_labeled_set_text:
* @labeled: the #GimpLabeled.
* @text: label text with Pango markup and mnemonic.
*
* This is the equivalent of running
* gtk_label_set_markup_with_mnemonic() on the #GtkLabel as a
* #GimpLabeled expects a mnemonic. Pango markup are also allowed.
*/
void
gimp_labeled_set_text (GimpLabeled *labeled,
const gchar *text)
{
GimpLabeledPrivate *priv = gimp_labeled_get_instance_private (labeled);
g_return_if_fail (GIMP_IS_LABELED (labeled));
gtk_label_set_markup_with_mnemonic (GTK_LABEL (priv->label), text);
}

View file

@ -0,0 +1,87 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimplabeled.h
* Copyright (C) 2020 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_LABELED_H__
#define __GIMP_LABELED_H__
G_BEGIN_DECLS
#define GIMP_TYPE_LABELED (gimp_labeled_get_type ())
G_DECLARE_DERIVABLE_TYPE (GimpLabeled, gimp_labeled, GIMP, LABELED, GtkGrid)
struct _GimpLabeledClass
{
GtkGridClass parent_class;
/* Signals */
void (* mnemonic_widget_changed) (GimpLabeled *labeled,
GtkWidget *widget);
/* Class methods */
/**
* GimpLabelledClass::populate:
*
* Fill the #GtkGrid with any necessary widget and sets the
* coordinates and dimensions the #GtkLabel should be attached to.
* By default, @x, @y, @width and @height will be pre-filled with 0,
* 0, 1 and 1 respectively, i.e. the top-left of the grid. There is no
* need to edit these output variables unless your subclass wants the
* label to be placed elsewhere.
*
* Returns: (transfer none): the #GtkWidget which the label must be
* set as mnemonic to.
**/
GtkWidget * (* populate) (GimpLabeled *labeled,
gint *x,
gint *y,
gint *width,
gint *height);
/* Padding for future expansion */
void (* _gimp_reserved0) (void);
void (* _gimp_reserved1) (void);
void (* _gimp_reserved2) (void);
void (* _gimp_reserved3) (void);
void (* _gimp_reserved4) (void);
void (* _gimp_reserved5) (void);
void (* _gimp_reserved6) (void);
void (* _gimp_reserved7) (void);
void (* _gimp_reserved8) (void);
void (* _gimp_reserved9) (void);
};
GtkWidget * gimp_labeled_get_label (GimpLabeled *labeled);
const gchar * gimp_labeled_get_text (GimpLabeled *labeled);
void gimp_labeled_set_text (GimpLabeled *labeled,
const gchar *text);
G_END_DECLS
#endif /* __GIMP_LABELED_H__ */

View file

@ -0,0 +1,289 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimplabelentry.c
* Copyright (C) 2022 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpcolor/gimpcolor.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpbase/gimpbase.h"
#include "gimpwidgets.h"
/**
* SECTION: gimplabelentry
* @title: GimpLabelEntry
* @short_description: Widget containing an entry and a label.
*
* This widget is a subclass of #GimpLabeled with a #GtkEntry.
**/
enum
{
VALUE_CHANGED,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_VALUE,
};
struct _GimpLabelEntry
{
GimpLabeled parent_class;
GtkWidget *entry;
};
static void gimp_label_entry_constructed (GObject *object);
static void gimp_label_entry_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_label_entry_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static GtkWidget * gimp_label_entry_populate (GimpLabeled *entry,
gint *x,
gint *y,
gint *width,
gint *height);
G_DEFINE_TYPE (GimpLabelEntry, gimp_label_entry, GIMP_TYPE_LABELED)
#define parent_class gimp_label_entry_parent_class
static guint gimp_label_entry_signals[LAST_SIGNAL] = { 0 };
static void
gimp_label_entry_class_init (GimpLabelEntryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GimpLabeledClass *labeled_class = GIMP_LABELED_CLASS (klass);
gimp_label_entry_signals[VALUE_CHANGED] =
g_signal_new ("value-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->constructed = gimp_label_entry_constructed;
object_class->set_property = gimp_label_entry_set_property;
object_class->get_property = gimp_label_entry_get_property;
labeled_class->populate = gimp_label_entry_populate;
/**
* GimpLabelEntry:value:
*
* The currently set value.
*
* Since: 3.0
**/
g_object_class_install_property (object_class, PROP_VALUE,
g_param_spec_string ("value",
"Entry text",
"The text in the entry",
NULL,
GIMP_PARAM_READWRITE));
}
static void
gimp_label_entry_init (GimpLabelEntry *entry)
{
entry->entry = gtk_entry_new ();
}
static void
gimp_label_entry_constructed (GObject *object)
{
GimpLabelEntry *entry = GIMP_LABEL_ENTRY (object);
GtkEntryBuffer *buffer = gtk_entry_get_buffer (GTK_ENTRY (entry->entry));
G_OBJECT_CLASS (parent_class)->constructed (object);
/* This is important to make this object into a property widget. It
* will allow config object to bind the "value" property of this
* widget, and therefore be updated automatically.
*/
g_object_bind_property (G_OBJECT (buffer), "text",
G_OBJECT (entry), "value",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
}
static void
gimp_label_entry_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpLabelEntry *entry = GIMP_LABEL_ENTRY (object);
switch (property_id)
{
case PROP_VALUE:
{
GtkEntryBuffer *buffer = gtk_entry_get_buffer (GTK_ENTRY (entry->entry));
/* Avoid looping forever since we have bound this widget's
* "value" property with the entry button "value" property.
*/
if (g_strcmp0 (gtk_entry_buffer_get_text (buffer),
g_value_get_string (value)) != 0)
gtk_entry_buffer_set_text (buffer, g_value_get_string (value), -1);
g_signal_emit (object, gimp_label_entry_signals[VALUE_CHANGED], 0);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_label_entry_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpLabelEntry *entry = GIMP_LABEL_ENTRY (object);
switch (property_id)
{
case PROP_VALUE:
{
GtkEntryBuffer *buffer = gtk_entry_get_buffer (GTK_ENTRY (entry->entry));
g_value_set_string (value, gtk_entry_buffer_get_text (buffer));
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static GtkWidget *
gimp_label_entry_populate (GimpLabeled *labeled,
gint *x,
gint *y,
gint *width,
gint *height)
{
GimpLabelEntry *entry = GIMP_LABEL_ENTRY (labeled);
gtk_grid_attach (GTK_GRID (entry), entry->entry, 1, 0, 1, 1);
/* Make sure the label and entry won't be glued next to each other's. */
gtk_grid_set_column_spacing (GTK_GRID (entry),
4 * gtk_widget_get_scale_factor (GTK_WIDGET (entry)));
gtk_widget_show (entry->entry);
return entry->entry;
}
/* Public Functions */
/**
* gimp_label_entry_new:
* @label: The text for the #GtkLabel.
*
*
* Returns: (transfer full): The new #GimpLabelEntry widget.
**/
GtkWidget *
gimp_label_entry_new (const gchar *label)
{
GtkWidget *labeled;
labeled = g_object_new (GIMP_TYPE_LABEL_ENTRY,
"label", label,
NULL);
return labeled;
}
/**
* gimp_label_entry_set_value:
* @entry: The #GtkLabelEntry.
* @value: A new value.
*
* This function sets the value in the #GtkEntry inside @entry.
**/
void
gimp_label_entry_set_value (GimpLabelEntry *entry,
const gchar *value)
{
g_return_if_fail (GIMP_IS_LABEL_ENTRY (entry));
g_object_set (entry,
"value", value,
NULL);
}
/**
* gimp_label_entry_get_value:
* @entry: The #GtkLabelEntry.
*
* This function returns the value shown by @entry.
*
* Returns: (transfer none): The value currently set.
**/
const gchar *
gimp_label_entry_get_value (GimpLabelEntry *entry)
{
GtkEntryBuffer *buffer;
g_return_val_if_fail (GIMP_IS_LABEL_ENTRY (entry), NULL);
buffer = gtk_entry_get_buffer (GTK_ENTRY (entry->entry));
return gtk_entry_buffer_get_text (buffer);
}
/**
* gimp_label_entry_get_entry:
* @entry: The #GimpLabelEntry
*
* This function returns the #GtkEntry packed in @entry.
*
* Returns: (transfer none): The #GtkEntry contained in @entry.
**/
GtkWidget *
gimp_label_entry_get_entry (GimpLabelEntry *entry)
{
g_return_val_if_fail (GIMP_IS_LABEL_ENTRY (entry), NULL);
return entry->entry;
}

View file

@ -0,0 +1,47 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimplabelentry.h
* Copyright (C) 2022 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_LABEL_ENTRY_H__
#define __GIMP_LABEL_ENTRY_H__
#include <libgimpwidgets/gimplabeled.h>
G_BEGIN_DECLS
#define GIMP_TYPE_LABEL_ENTRY (gimp_label_entry_get_type ())
G_DECLARE_FINAL_TYPE (GimpLabelEntry, gimp_label_entry, GIMP, LABEL_ENTRY, GimpLabeled)
GtkWidget * gimp_label_entry_new (const gchar *label);
void gimp_label_entry_set_value (GimpLabelEntry *entry,
const gchar *value);
const gchar * gimp_label_entry_get_value (GimpLabelEntry *entry);
GtkWidget * gimp_label_entry_get_entry (GimpLabelEntry *entry);
G_END_DECLS
#endif /* __GIMP_LABEL_ENTRY_H__ */

View file

@ -0,0 +1,265 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimplabelintwidget.c
* Copyright (C) 2020 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpcolor/gimpcolor.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpbase/gimpbase.h"
#include "gimplabelintwidget.h"
/**
* SECTION: gimplabelintwidget
* @title: GimpLabelIntWidget
* @short_description: Widget containing a label and a widget with an
* integer "value" property.
*
* This widget is a subclass of #GimpLabeled.
**/
enum
{
VALUE_CHANGED,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_VALUE,
PROP_WIDGET,
};
struct _GimpLabelIntWidget
{
GimpLabeled parent_instance;
GtkWidget *widget;
gint value;
};
static void gimp_label_int_widget_constructed (GObject *object);
static void gimp_label_int_widget_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_label_int_widget_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static GtkWidget * gimp_label_int_widget_populate (GimpLabeled *widget,
gint *x,
gint *y,
gint *width,
gint *height);
G_DEFINE_TYPE (GimpLabelIntWidget, gimp_label_int_widget, GIMP_TYPE_LABELED)
#define parent_class gimp_label_int_widget_parent_class
static guint gimp_label_int_widget_signals[LAST_SIGNAL] = { 0 };
static void
gimp_label_int_widget_class_init (GimpLabelIntWidgetClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GimpLabeledClass *labeled_class = GIMP_LABELED_CLASS (klass);
gimp_label_int_widget_signals[VALUE_CHANGED] =
g_signal_new ("value-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->constructed = gimp_label_int_widget_constructed;
object_class->set_property = gimp_label_int_widget_set_property;
object_class->get_property = gimp_label_int_widget_get_property;
labeled_class->populate = gimp_label_int_widget_populate;
/**
* GimpLabelIntWidget:value:
*
* The currently set value.
*
* Since: 3.0
**/
g_object_class_install_property (object_class, PROP_VALUE,
g_param_spec_int ("value", NULL,
"Current value",
G_MININT, G_MAXINT, 0,
GIMP_PARAM_READWRITE));
/**
* GimpLabelIntWidget:widget:
*
* The widget holding an integer value.
*
* Since: 3.0
**/
g_object_class_install_property (object_class, PROP_WIDGET,
g_param_spec_object ("widget", NULL,
"Integer widget",
GTK_TYPE_WIDGET,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
}
static void
gimp_label_int_widget_init (GimpLabelIntWidget *widget)
{
}
static void
gimp_label_int_widget_constructed (GObject *object)
{
GimpLabelIntWidget *widget = GIMP_LABEL_INT_WIDGET (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
gtk_grid_set_column_spacing (GTK_GRID (widget), 6);
gtk_grid_set_row_spacing (GTK_GRID (widget), 6);
/* This is important to make this object into a property widget. It
* will allow config object to bind the "value" property of this
* widget, and therefore be updated automatically.
*/
g_object_bind_property (G_OBJECT (widget->widget), "value",
object, "value",
G_BINDING_BIDIRECTIONAL |
G_BINDING_SYNC_CREATE);
}
static void
gimp_label_int_widget_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpLabelIntWidget *widget = GIMP_LABEL_INT_WIDGET (object);
switch (property_id)
{
case PROP_VALUE:
widget->value = g_value_get_int (value);
g_signal_emit (object, gimp_label_int_widget_signals[VALUE_CHANGED], 0);
break;
case PROP_WIDGET:
widget->widget = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_label_int_widget_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpLabelIntWidget *widget = GIMP_LABEL_INT_WIDGET (object);
switch (property_id)
{
case PROP_VALUE:
g_value_set_int (value, widget->value);
break;
case PROP_WIDGET:
g_value_set_object (value, widget->widget);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static GtkWidget *
gimp_label_int_widget_populate (GimpLabeled *labeled,
gint *x,
gint *y,
gint *width,
gint *height)
{
GimpLabelIntWidget *widget = GIMP_LABEL_INT_WIDGET (labeled);
gtk_grid_attach (GTK_GRID (widget), widget->widget, 1, 0, 1, 1);
gtk_widget_show (widget->widget);
return widget->widget;
}
/* Public Functions */
/**
* gimp_label_int_widget_new:
* @text: The text for the #GtkLabel.
* @widget: (transfer full): The #GtkWidget to use.
*
* Creates a new #GimpLabelIntWidget whose "value" property is bound to
* that of @widget (which must therefore have such an integer property).
*
* Returns: (transfer full): The new #GimpLabelIntWidget widget.
**/
GtkWidget *
gimp_label_int_widget_new (const gchar *text,
GtkWidget *widget)
{
GtkWidget *int_widget;
GParamSpec *pspec;
g_return_val_if_fail ((pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (widget),
"value")) &&
G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_INT,
NULL);
int_widget = g_object_new (GIMP_TYPE_LABEL_INT_WIDGET,
"label", text,
"widget", widget,
NULL);
return int_widget;
}
/**
* gimp_label_int_widget_get_widget:
* @widget: the #GimpLabelIntWidget.
*
* Returns: (transfer none): The new #GtkWidget packed next to the label.
**/
GtkWidget *
gimp_label_int_widget_get_widget (GimpLabelIntWidget *widget)
{
g_return_val_if_fail (GIMP_IS_LABEL_INT_WIDGET (widget), NULL);
return widget->widget;
}

View file

@ -0,0 +1,45 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimplabelintwidget.h
* Copyright (C) 2020 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_LABEL_INT_WIDGET_H__
#define __GIMP_LABEL_INT_WIDGET_H__
#include <libgimpwidgets/gimplabeled.h>
G_BEGIN_DECLS
#define GIMP_TYPE_LABEL_INT_WIDGET (gimp_label_int_widget_get_type ())
G_DECLARE_FINAL_TYPE (GimpLabelIntWidget, gimp_label_int_widget, GIMP, LABEL_INT_WIDGET, GimpLabeled)
GtkWidget * gimp_label_int_widget_new (const gchar *text,
GtkWidget *widget);
GtkWidget * gimp_label_int_widget_get_widget (GimpLabelIntWidget *widget);
G_END_DECLS
#endif /* __GIMP_LABEL_INT_WIDGET_H__ */

View file

@ -0,0 +1,529 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimplabelspin.c
* Copyright (C) 2020 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpcolor/gimpcolor.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpbase/gimpbase.h"
#include "gimpwidgets.h"
/**
* SECTION: gimplabelspin
* @title: GimpLabelSpin
* @short_description: Widget containing a spin button and a label.
*
* This widget is a subclass of #GimpLabeled with a #GimpSpinButton.
**/
enum
{
VALUE_CHANGED,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_VALUE,
PROP_LOWER,
PROP_UPPER,
PROP_DIGITS,
};
typedef struct _GimpLabelSpinPrivate
{
GimpLabeled parent_instance;
GtkWidget *spinbutton;
GtkAdjustment *spin_adjustment;
gint digits;
gdouble value;
} GimpLabelSpinPrivate;
static void gimp_label_spin_constructed (GObject *object);
static void gimp_label_spin_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_label_spin_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static GtkWidget * gimp_label_spin_populate (GimpLabeled *spin,
gint *x,
gint *y,
gint *width,
gint *height);
static void gimp_label_spin_update_spin_width (GimpLabelSpin *spin,
gdouble lower,
gdouble upper,
guint digits);
static void gimp_label_spin_update_settings (GimpLabelSpin *spin);
G_DEFINE_TYPE_WITH_PRIVATE (GimpLabelSpin, gimp_label_spin, GIMP_TYPE_LABELED)
#define parent_class gimp_label_spin_parent_class
static guint gimp_label_spin_signals[LAST_SIGNAL] = { 0 };
static void
gimp_label_spin_class_init (GimpLabelSpinClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GimpLabeledClass *labeled_class = GIMP_LABELED_CLASS (klass);
gimp_label_spin_signals[VALUE_CHANGED] =
g_signal_new ("value-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpLabelSpinClass, value_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->constructed = gimp_label_spin_constructed;
object_class->set_property = gimp_label_spin_set_property;
object_class->get_property = gimp_label_spin_get_property;
labeled_class->populate = gimp_label_spin_populate;
/**
* GimpLabelSpin:value:
*
* The currently set value.
*
* Since: 3.0
**/
g_object_class_install_property (object_class, PROP_VALUE,
g_param_spec_double ("value", NULL,
"Current value",
-G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
GIMP_PARAM_READWRITE));
/**
* GimpLabelSpin:lower:
*
* The lower bound of the spin button.
*
* Since: 3.0
**/
g_object_class_install_property (object_class, PROP_LOWER,
g_param_spec_double ("lower", NULL,
"Minimum value",
-G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
/**
* GimpLabelSpin:upper:
*
* The upper bound of the spin button.
*
* Since: 3.0
**/
g_object_class_install_property (object_class, PROP_UPPER,
g_param_spec_double ("upper", NULL,
"Max value",
-G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
/**
* GimpLabelSpin:digits:
*
* The number of decimal places to display. If -1, then the number is
* estimated.
*
* Since: 3.0
**/
g_object_class_install_property (object_class, PROP_DIGITS,
g_param_spec_int ("digits", NULL,
"The number of decimal places to display",
-1, G_MAXINT, -1,
GIMP_PARAM_READWRITE));
}
static void
gimp_label_spin_init (GimpLabelSpin *spin)
{
GimpLabelSpinPrivate *priv = gimp_label_spin_get_instance_private (spin);
/* We want the adjustment to exist at init so that construction
* properties can apply (default values are bogus but should be
* properly overridden with expected values if the object was created
* with gimp_label_spin_new().
*/
priv->spin_adjustment = gtk_adjustment_new (0.0, 0.0, 100.0, 1.0, 10.0, 0.0);
gtk_grid_set_row_spacing (GTK_GRID (spin), 6);
gtk_grid_set_column_spacing (GTK_GRID (spin), 6);
}
static void
gimp_label_spin_constructed (GObject *object)
{
GimpLabelSpin *spin = GIMP_LABEL_SPIN (object);
GimpLabelSpinPrivate *priv = gimp_label_spin_get_instance_private (spin);
G_OBJECT_CLASS (parent_class)->constructed (object);
/* This is important to make this object into a property widget. It
* will allow config object to bind the "value" property of this
* widget, and therefore be updated automatically.
*/
g_object_bind_property (G_OBJECT (priv->spin_adjustment), "value",
G_OBJECT (spin), "value",
G_BINDING_BIDIRECTIONAL |
G_BINDING_SYNC_CREATE);
gimp_label_spin_update_settings (spin);
}
static void
gimp_label_spin_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpLabelSpin *spin = GIMP_LABEL_SPIN (object);
GimpLabelSpinPrivate *priv = gimp_label_spin_get_instance_private (spin);
switch (property_id)
{
case PROP_VALUE:
if (priv->value != g_value_get_double (value))
{
priv->value = g_value_get_double (value);
g_signal_emit (object, gimp_label_spin_signals[VALUE_CHANGED], 0);
}
break;
case PROP_LOWER:
gtk_adjustment_set_lower (priv->spin_adjustment,
g_value_get_double (value));
if (priv->spinbutton)
{
gimp_label_spin_update_settings (spin);
}
break;
case PROP_UPPER:
gtk_adjustment_set_upper (priv->spin_adjustment,
g_value_get_double (value));
if (priv->spinbutton)
{
gimp_label_spin_update_settings (spin);
}
break;
case PROP_DIGITS:
if (priv->spinbutton)
{
priv->digits = g_value_get_int (value);
gimp_label_spin_update_settings (spin);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_label_spin_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpLabelSpin *spin = GIMP_LABEL_SPIN (object);
GimpLabelSpinPrivate *priv = gimp_label_spin_get_instance_private (spin);
GtkSpinButton *spinbutton = GTK_SPIN_BUTTON (priv->spinbutton);
switch (property_id)
{
case PROP_VALUE:
g_value_set_double (value, priv->value);
break;
case PROP_LOWER:
g_value_set_double (value, gtk_adjustment_get_lower (priv->spin_adjustment));
break;
case PROP_UPPER:
g_value_set_double (value, gtk_adjustment_get_upper (priv->spin_adjustment));
break;
case PROP_DIGITS:
g_value_set_uint (value, gtk_spin_button_get_digits (spinbutton));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static GtkWidget *
gimp_label_spin_populate (GimpLabeled *labeled,
gint *x,
gint *y,
gint *width,
gint *height)
{
GimpLabelSpin *spin = GIMP_LABEL_SPIN (labeled);
GimpLabelSpinPrivate *priv = gimp_label_spin_get_instance_private (spin);
priv->spinbutton = gimp_spin_button_new (priv->spin_adjustment, 2.0, 2.0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (priv->spinbutton), TRUE);
gtk_grid_attach (GTK_GRID (spin), priv->spinbutton, 1, 0, 1, 1);
gtk_widget_show (priv->spinbutton);
return priv->spinbutton;
}
static void
gimp_label_spin_update_spin_width (GimpLabelSpin *spin,
gdouble lower,
gdouble upper,
guint digits)
{
GimpLabelSpinPrivate *priv = gimp_label_spin_get_instance_private (spin);
gint width = 0;
g_return_if_fail (GIMP_IS_LABEL_SPIN (spin));
/* Necessary size to display the max/min integer values, with optional
* negative sign.
*/
width = (gint) floor (log10 (upper) + 1) + (upper < 0.0 ? 1 : 0);
width = MAX (width, (gint) floor (log10 (lower) + 1) + (lower < 0.0 ? 1 : 0));
/* Adding decimal digits and separator. */
width += (digits > 0 ? 1 + digits : 0);
/* Overlong spin button are useless. */
width = MIN (10, width);
gtk_entry_set_width_chars (GTK_ENTRY (priv->spinbutton), width);
}
static void
gimp_label_spin_update_settings (GimpLabelSpin *spin)
{
GimpLabelSpinPrivate *priv = gimp_label_spin_get_instance_private (spin);
gdouble lower;
gdouble upper;
gdouble step;
gdouble page;
gint digits = priv->digits;
gboolean adjust_step = (digits == 0);
g_return_if_fail (GIMP_IS_LABEL_SPIN (spin));
g_object_get (spin,
"lower", &lower,
"upper", &upper,
NULL);
g_return_if_fail (upper >= lower);
gimp_range_estimate_settings (lower, upper, &step, &page,
digits < 0 ? &digits: NULL);
if (adjust_step && digits == 0 && step < 1.0)
{
step = 1.0;
if (page < step)
page = step;
}
gimp_label_spin_set_increments (spin, step, page);
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (priv->spinbutton), (guint) digits);
gimp_label_spin_update_spin_width (spin, lower, upper, (guint) digits);
}
/* Public Functions */
/**
* gimp_label_spin_new:
* @text: The text for the #GtkLabel.
* @value: The initial value.
* @lower: The lower boundary.
* @upper: The upper boundary.
* @digits: The number of decimal digits.
*
* Suitable increment values are estimated based on the [@lower, @upper]
* range.
* If @digits is -1, then it will also be estimated based on the same
* range. Digits estimation will always be at least 1, so if you want to
* show integer values only, set 0 explicitly.
*
* Returns: (transfer full): The new #GimpLabelSpin widget.
**/
GtkWidget *
gimp_label_spin_new (const gchar *text,
gdouble value,
gdouble lower,
gdouble upper,
gint digits)
{
GtkWidget *labeled;
g_return_val_if_fail (upper >= lower, NULL);
g_return_val_if_fail (digits >= -1, NULL);
labeled = g_object_new (GIMP_TYPE_LABEL_SPIN,
"label", text,
"value", value,
"lower", lower,
"upper", upper,
"digits", digits,
NULL);
return labeled;
}
/**
* gimp_label_spin_set_value:
* @spin: The #GtkLabelSpin.
* @value: A new value.
*
* This function sets the value shown by @spin.
**/
void
gimp_label_spin_set_value (GimpLabelSpin *spin,
gdouble value)
{
g_return_if_fail (GIMP_IS_LABEL_SPIN (spin));
g_object_set (spin,
"value", value,
NULL);
}
/**
* gimp_label_spin_get_value:
* @spin: The #GtkLabelSpin.
*
* This function returns the value shown by @spin.
*
* Returns: The value currently set.
**/
gdouble
gimp_label_spin_get_value (GimpLabelSpin *spin)
{
gdouble value;
g_return_val_if_fail (GIMP_IS_LABEL_SPIN (spin), 0.0);
g_object_get (spin,
"value", &value,
NULL);
return value;
}
/**
* gimp_label_spin_set_increments:
* @spin: the #GimpLabelSpin.
* @step: the step increment.
* @page: the page increment.
*
* Set the step and page increments of the spin button.
* By default, these increment values are automatically computed
* depending on the range based on common usage. So you will likely not
* need to run this for most case. Yet if you want specific increments
* (which the widget cannot guess), you can call this function.
*/
void
gimp_label_spin_set_increments (GimpLabelSpin *spin,
gdouble step,
gdouble page)
{
GimpLabelSpinPrivate *priv = gimp_label_spin_get_instance_private (spin);
GtkSpinButton *spinbutton;
gdouble lower;
gdouble upper;
g_return_if_fail (GIMP_IS_LABEL_SPIN (spin));
g_return_if_fail (step <= page);
spinbutton = GTK_SPIN_BUTTON (priv->spinbutton);
gtk_spin_button_get_range (spinbutton, &lower, &upper);
g_return_if_fail (upper >= lower);
g_return_if_fail ((upper == lower || step <= upper - lower) &&
(upper == lower || page <= upper - lower));
g_object_freeze_notify (G_OBJECT (spinbutton));
gtk_adjustment_set_step_increment (gtk_spin_button_get_adjustment (spinbutton), step);
gtk_adjustment_set_page_increment (gtk_spin_button_get_adjustment (spinbutton), page);
g_object_thaw_notify (G_OBJECT (spinbutton));
g_object_set (priv->spinbutton,
"climb-rate", step,
NULL);
}
/**
* gimp_label_spin_set_digits:
* @spin: the #GimpLabelSpin.
* @digits: the number of decimal places to display.
*
* Set the number of decimal place to display in the @spin's entry.
* If @digits is -1, then it will also be estimated based on @spin's
* range. Digits estimation will always be at least 1, so if you want to
* show integer values only, set 0 explicitly.
*/
void
gimp_label_spin_set_digits (GimpLabelSpin *spin,
gint digits)
{
g_return_if_fail (GIMP_IS_LABEL_SPIN (spin));
g_object_set (spin,
"digits", digits,
NULL);
}
/**
* gimp_label_spin_get_spin_button:
* @spin: The #GimpLabelSpin
*
* This function returns the #GimpSpinButton packed in @spin.
*
* Returns: (transfer none): The #GimpSpinButton contained in @spin.
**/
GtkWidget *
gimp_label_spin_get_spin_button (GimpLabelSpin *spin)
{
GimpLabelSpinPrivate *priv = gimp_label_spin_get_instance_private (spin);
g_return_val_if_fail (GIMP_IS_LABEL_SPIN (spin), NULL);
return priv->spinbutton;
}

View file

@ -0,0 +1,76 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpcolorscaleentry.h
* Copyright (C) 2020 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_LABEL_SPIN_H__
#define __GIMP_LABEL_SPIN_H__
#include <libgimpwidgets/gimplabeled.h>
G_BEGIN_DECLS
#define GIMP_TYPE_LABEL_SPIN (gimp_label_spin_get_type ())
G_DECLARE_DERIVABLE_TYPE (GimpLabelSpin, gimp_label_spin, GIMP, LABEL_SPIN, GimpLabeled)
struct _GimpLabelSpinClass
{
GimpLabeledClass parent_class;
/* Signals */
void (* value_changed) (GimpLabelSpin *spin);
/* Padding for future expansion */
void (* _gimp_reserved0) (void);
void (* _gimp_reserved1) (void);
void (* _gimp_reserved2) (void);
void (* _gimp_reserved3) (void);
void (* _gimp_reserved4) (void);
void (* _gimp_reserved5) (void);
void (* _gimp_reserved6) (void);
void (* _gimp_reserved7) (void);
void (* _gimp_reserved8) (void);
void (* _gimp_reserved9) (void);
};
GtkWidget * gimp_label_spin_new (const gchar *text,
gdouble value,
gdouble lower,
gdouble upper,
gint digits);
void gimp_label_spin_set_value (GimpLabelSpin *spin,
gdouble value);
gdouble gimp_label_spin_get_value (GimpLabelSpin *spin);
void gimp_label_spin_set_increments (GimpLabelSpin *spin,
gdouble step,
gdouble page);
void gimp_label_spin_set_digits (GimpLabelSpin *spin,
gint digits);
GtkWidget * gimp_label_spin_get_spin_button (GimpLabelSpin *spin);
G_END_DECLS
#endif /* __GIMP_LABEL_SPIN_H__ */

View file

@ -0,0 +1,282 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimplabelstringwidget.c
* Copyright (C) 2023 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpcolor/gimpcolor.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpbase/gimpbase.h"
#include "gimplabelstringwidget.h"
/**
* SECTION: gimplabelstringwidget
* @title: GimpLabelStringWidget
* @short_description: Widget containing a label and a widget with a
* string "value" property.
*
* This widget is a subclass of #GimpLabeled.
**/
enum
{
VALUE_CHANGED,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_VALUE,
PROP_WIDGET,
};
struct _GimpLabelStringWidget
{
GimpLabeled parent_instance;
GtkWidget *widget;
gchar *value;
};
static void gimp_label_string_widget_constructed (GObject *object);
static void gimp_label_string_widget_finalize (GObject *object);
static void gimp_label_string_widget_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_label_string_widget_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static GtkWidget * gimp_label_string_widget_populate (GimpLabeled *widget,
gint *x,
gint *y,
gint *width,
gint *height);
G_DEFINE_TYPE (GimpLabelStringWidget, gimp_label_string_widget, GIMP_TYPE_LABELED)
#define parent_class gimp_label_string_widget_parent_class
static guint gimp_label_string_widget_signals[LAST_SIGNAL] = { 0 };
static void
gimp_label_string_widget_class_init (GimpLabelStringWidgetClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GimpLabeledClass *labeled_class = GIMP_LABELED_CLASS (klass);
gimp_label_string_widget_signals[VALUE_CHANGED] =
g_signal_new ("value-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->constructed = gimp_label_string_widget_constructed;
object_class->finalize = gimp_label_string_widget_finalize;
object_class->set_property = gimp_label_string_widget_set_property;
object_class->get_property = gimp_label_string_widget_get_property;
labeled_class->populate = gimp_label_string_widget_populate;
/**
* GimpLabelStringWidget:value:
*
* The currently set value.
*
* Since: 3.0
**/
g_object_class_install_property (object_class, PROP_VALUE,
g_param_spec_string ("value", NULL,
"Current value",
NULL,
GIMP_PARAM_READWRITE));
/**
* GimpLabelStringWidget:widget:
*
* The widget holding a string property named "value".
*
* Since: 3.0
**/
g_object_class_install_property (object_class, PROP_WIDGET,
g_param_spec_object ("widget", NULL,
"String widget",
GTK_TYPE_WIDGET,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
}
static void
gimp_label_string_widget_init (GimpLabelStringWidget *widget)
{
}
static void
gimp_label_string_widget_constructed (GObject *object)
{
GimpLabelStringWidget *widget = GIMP_LABEL_STRING_WIDGET (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
gtk_grid_set_column_spacing (GTK_GRID (widget), 6);
gtk_grid_set_row_spacing (GTK_GRID (widget), 6);
/* This is important to make this object into a property widget. It
* will allow config object to bind the "value" property of this
* widget, and therefore be updated automatically.
*/
g_object_bind_property (G_OBJECT (widget->widget), "value",
object, "value",
G_BINDING_BIDIRECTIONAL |
G_BINDING_SYNC_CREATE);
}
static void
gimp_label_string_widget_finalize (GObject *object)
{
GimpLabelStringWidget *widget = GIMP_LABEL_STRING_WIDGET (object);
g_free (widget->value);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_label_string_widget_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpLabelStringWidget *widget = GIMP_LABEL_STRING_WIDGET (object);
switch (property_id)
{
case PROP_VALUE:
if (g_strcmp0 (widget->value, g_value_get_string (value)) != 0)
{
g_free (widget->value);
widget->value = g_value_dup_string (value);
g_signal_emit (object, gimp_label_string_widget_signals[VALUE_CHANGED], 0);
}
break;
case PROP_WIDGET:
widget->widget = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_label_string_widget_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpLabelStringWidget *widget = GIMP_LABEL_STRING_WIDGET (object);
switch (property_id)
{
case PROP_VALUE:
g_value_set_string (value, widget->value);
break;
case PROP_WIDGET:
g_value_set_object (value, widget->widget);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static GtkWidget *
gimp_label_string_widget_populate (GimpLabeled *labeled,
gint *x,
gint *y,
gint *width,
gint *height)
{
GimpLabelStringWidget *widget = GIMP_LABEL_STRING_WIDGET (labeled);
gtk_grid_attach (GTK_GRID (widget), widget->widget, 1, 0, 1, 1);
gtk_widget_show (widget->widget);
return widget->widget;
}
/* Public Functions */
/**
* gimp_label_string_widget_new:
* @text: The text for the #GtkLabel.
* @widget: (transfer full): The #GtkWidget to use.
*
* Creates a new #GimpLabelStringWidget whose "value" property is bound to
* that of @widget (which must therefore have such a string property).
*
* Returns: (transfer full): The new #GimpLabelStringWidget widget.
**/
GtkWidget *
gimp_label_string_widget_new (const gchar *text,
GtkWidget *widget)
{
GtkWidget *string_widget;
GParamSpec *pspec;
g_return_val_if_fail ((pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (widget),
"value")) &&
(G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_STRING ||
G_PARAM_SPEC_TYPE (pspec) == GIMP_TYPE_PARAM_CHOICE),
NULL);
string_widget = g_object_new (GIMP_TYPE_LABEL_STRING_WIDGET,
"label", text,
"widget", widget,
NULL);
return string_widget;
}
/**
* gimp_label_string_widget_get_widget:
* @widget: the #GimpLabelStringWidget.
*
* Returns: (transfer none): The new #GtkWidget packed next to the label.
**/
GtkWidget *
gimp_label_string_widget_get_widget (GimpLabelStringWidget *widget)
{
g_return_val_if_fail (GIMP_IS_LABEL_STRING_WIDGET (widget), NULL);
return widget->widget;
}

View file

@ -0,0 +1,45 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimplabelstringwidget.h
* Copyright (C) 2023 Jehan
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_LABEL_STRING_WIDGET_H__
#define __GIMP_LABEL_STRING_WIDGET_H__
#include <libgimpwidgets/gimplabeled.h>
G_BEGIN_DECLS
#define GIMP_TYPE_LABEL_STRING_WIDGET (gimp_label_string_widget_get_type ())
G_DECLARE_FINAL_TYPE (GimpLabelStringWidget, gimp_label_string_widget, GIMP, LABEL_STRING_WIDGET, GimpLabeled)
GtkWidget * gimp_label_string_widget_new (const gchar *text,
GtkWidget *widget);
GtkWidget * gimp_label_string_widget_get_widget (GimpLabelStringWidget *widget);
G_END_DECLS
#endif /* __GIMP_LABEL_STRING_WIDGET_H__ */

View file

@ -0,0 +1,323 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpmemsizeentry.c
* Copyright (C) 2000-2003 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "gimpwidgetstypes.h"
#include "gimpmemsizeentry.h"
#include "gimpwidgets.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimpmemsizeentry
* @title: GimpMemSizeEntry
* @short_description: A composite widget to enter a memory size.
*
* Similar to a #GimpSizeEntry but instead of lengths, this widget is
* used to let the user enter memory sizes. A combo box allows one to
* switch between Kilobytes, Megabytes and Gigabytes. Used in the GIMP
* preferences dialog.
**/
enum
{
VALUE_CHANGED,
LAST_SIGNAL
};
struct _GimpMemsizeEntry
{
GtkBox parent_instance;
guint64 value;
guint64 lower;
guint64 upper;
guint shift;
/* adjustment is owned by spinbutton. Do not unref() it. */
GtkAdjustment *adjustment;
GtkWidget *spinbutton;
GtkWidget *menu;
} ;
static void gimp_memsize_entry_adj_callback (GtkAdjustment *adj,
GimpMemsizeEntry *entry);
static void gimp_memsize_entry_unit_callback (GtkWidget *widget,
GimpMemsizeEntry *entry);
static guint64 gimp_memsize_entry_get_rounded_value (GimpMemsizeEntry *entry,
guint64 value);
G_DEFINE_TYPE (GimpMemsizeEntry, gimp_memsize_entry, GTK_TYPE_BOX)
#define parent_class gimp_memsize_entry_parent_class
static guint gimp_memsize_entry_signals[LAST_SIGNAL] = { 0 };
static void
gimp_memsize_entry_class_init (GimpMemsizeEntryClass *klass)
{
gimp_memsize_entry_signals[VALUE_CHANGED] =
g_signal_new ("value-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
}
static void
gimp_memsize_entry_init (GimpMemsizeEntry *entry)
{
gtk_orientable_set_orientation (GTK_ORIENTABLE (entry),
GTK_ORIENTATION_HORIZONTAL);
gtk_box_set_spacing (GTK_BOX (entry), 4);
}
static void
gimp_memsize_entry_adj_callback (GtkAdjustment *adj,
GimpMemsizeEntry *entry)
{
guint64 size = gtk_adjustment_get_value (adj);
if (gimp_memsize_entry_get_rounded_value (entry, entry->value) != size)
/* Do not allow losing accuracy if the converted/displayed value
* stays the same.
*/
entry->value = size << entry->shift;
g_signal_emit (entry, gimp_memsize_entry_signals[VALUE_CHANGED], 0);
}
static void
gimp_memsize_entry_unit_callback (GtkWidget *widget,
GimpMemsizeEntry *entry)
{
guint shift;
gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), (gint *) &shift);
#if _MSC_VER < 1300
# define CAST (gint64)
#else
# define CAST
#endif
if (shift != entry->shift)
{
entry->shift = shift;
gtk_adjustment_configure (entry->adjustment,
gimp_memsize_entry_get_rounded_value (entry, entry->value),
CAST entry->lower >> shift,
CAST entry->upper >> shift,
gtk_adjustment_get_step_increment (entry->adjustment),
gtk_adjustment_get_page_increment (entry->adjustment),
gtk_adjustment_get_page_size (entry->adjustment));
}
#undef CAST
}
/**
* gimp_memsize_entry_get_rounded_value:
* @entry: #GimpMemsizeEntry whose set unit is used.
* @value: value to convert to @entry unit, and rounded.
*
* Returns: the proper integer value to be displayed for current unit.
* This value has been appropriately rounded to the nearest
* integer, away from zero.
*/
static guint64
gimp_memsize_entry_get_rounded_value (GimpMemsizeEntry *entry,
guint64 value)
{
guint64 converted;
#if _MSC_VER < 1300
# define CAST (gint64)
#else
# define CAST
#endif
converted = (CAST value >> entry->shift) +
((CAST entry->value >> (entry->shift - 1)) & 1);
#undef CAST
return converted;
}
/**
* gimp_memsize_entry_new:
* @value: the initial value (in Bytes)
* @lower: the lower limit for the value (in Bytes)
* @upper: the upper limit for the value (in Bytes)
*
* Creates a new #GimpMemsizeEntry which is a #GtkHBox with a #GtkSpinButton
* and a #GtkOptionMenu all setup to allow the user to enter memory sizes.
*
* Returns: Pointer to the new #GimpMemsizeEntry.
**/
GtkWidget *
gimp_memsize_entry_new (guint64 value,
guint64 lower,
guint64 upper)
{
GimpMemsizeEntry *entry;
guint shift;
#if _MSC_VER < 1300
# define CAST (gint64)
#else
# define CAST
#endif
g_return_val_if_fail (value >= lower && value <= upper, NULL);
entry = g_object_new (GIMP_TYPE_MEMSIZE_ENTRY, NULL);
for (shift = 30; shift > 10; shift -= 10)
{
if (value > (G_GUINT64_CONSTANT (1) << shift) &&
value % (G_GUINT64_CONSTANT (1) << shift) == 0)
break;
}
entry->value = value;
entry->lower = lower;
entry->upper = upper;
entry->shift = shift;
entry->adjustment = gtk_adjustment_new (gimp_memsize_entry_get_rounded_value (entry,
entry->value),
CAST (lower >> shift),
CAST (upper >> shift),
1, 8, 0);
entry->spinbutton = gimp_spin_button_new (entry->adjustment, 1.0, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (entry->spinbutton), TRUE);
#undef CAST
gtk_entry_set_width_chars (GTK_ENTRY (entry->spinbutton), 7);
gtk_box_pack_start (GTK_BOX (entry), entry->spinbutton, FALSE, FALSE, 0);
gtk_widget_show (entry->spinbutton);
g_signal_connect (entry->adjustment, "value-changed",
G_CALLBACK (gimp_memsize_entry_adj_callback),
entry);
entry->menu = gimp_int_combo_box_new (_("Kibibyte"), 10,
_("Mebibyte"), 20,
_("Gibibyte"), 30,
NULL);
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (entry->menu), shift);
g_signal_connect (entry->menu, "changed",
G_CALLBACK (gimp_memsize_entry_unit_callback),
entry);
gtk_box_pack_start (GTK_BOX (entry), entry->menu, FALSE, FALSE, 0);
gtk_widget_show (entry->menu);
return GTK_WIDGET (entry);
}
/**
* gimp_memsize_entry_set_value:
* @entry: a #GimpMemsizeEntry
* @value: the new value (in Bytes)
*
* Sets the @entry's value. Please note that the #GimpMemsizeEntry rounds
* the value to full Kilobytes.
**/
void
gimp_memsize_entry_set_value (GimpMemsizeEntry *entry,
guint64 value)
{
guint shift;
g_return_if_fail (GIMP_IS_MEMSIZE_ENTRY (entry));
g_return_if_fail (value >= entry->lower && value <= entry->upper);
for (shift = 30; shift > 10; shift -= 10)
{
if (value > (G_GUINT64_CONSTANT (1) << shift) &&
value % (G_GUINT64_CONSTANT (1) << shift) == 0)
break;
}
if (shift != entry->shift)
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (entry->menu), shift);
gtk_adjustment_set_value (entry->adjustment,
(gdouble) gimp_memsize_entry_get_rounded_value (entry, value));
#undef CASE
}
/**
* gimp_memsize_entry_get_value:
* @entry: a #GimpMemsizeEntry
*
* Retrieves the current value from a #GimpMemsizeEntry.
*
* Returns: the current value of @entry (in Bytes).
**/
guint64
gimp_memsize_entry_get_value (GimpMemsizeEntry *entry)
{
g_return_val_if_fail (GIMP_IS_MEMSIZE_ENTRY (entry), 0);
return entry->value;
}
/**
* gimp_memsize_entry_get_spinbutton:
* @entry: a #GimpMemsizeEntry
*
* Returns: (transfer none) (type GtkSpinButton): the entry's #GtkSpinbutton.
*
* Since: 3.0
**/
GtkWidget *
gimp_memsize_entry_get_spinbutton (GimpMemsizeEntry *entry)
{
g_return_val_if_fail (GIMP_IS_MEMSIZE_ENTRY (entry), 0);
return entry->spinbutton;
}

View file

@ -0,0 +1,48 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpmemsizeentry.h
* Copyright (C) 2000-2003 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_MEMSIZE_ENTRY_H__
#define __GIMP_MEMSIZE_ENTRY_H__
G_BEGIN_DECLS
#define GIMP_TYPE_MEMSIZE_ENTRY (gimp_memsize_entry_get_type ())
G_DECLARE_FINAL_TYPE (GimpMemsizeEntry, gimp_memsize_entry, GIMP, MEMSIZE_ENTRY, GtkBox)
GtkWidget * gimp_memsize_entry_new (guint64 value,
guint64 lower,
guint64 upper);
void gimp_memsize_entry_set_value (GimpMemsizeEntry *entry,
guint64 value);
guint64 gimp_memsize_entry_get_value (GimpMemsizeEntry *entry);
GtkWidget * gimp_memsize_entry_get_spinbutton (GimpMemsizeEntry *entry);
G_END_DECLS
#endif /* __GIMP_MEMSIZE_ENTRY_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,74 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpratioentry.h
* Copyright (C) 2006 Simon Budig <simon@gimp.org>
* Copyright (C) 2007 Sven Neumann <sven@gimp.org>
* Copyright (C) 2007 Martin Nordholts <martin@svn.gnome.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_NUMBER_PAIR_ENTRY_H__
#define __GIMP_NUMBER_PAIR_ENTRY_H__
G_BEGIN_DECLS
#define GIMP_TYPE_NUMBER_PAIR_ENTRY (gimp_number_pair_entry_get_type ())
G_DECLARE_FINAL_TYPE (GimpNumberPairEntry, gimp_number_pair_entry, GIMP, NUMBER_PAIR_ENTRY, GtkEntry)
GtkWidget * gimp_number_pair_entry_new (const gchar *separators,
gboolean allow_simplification,
gdouble min_valid_value,
gdouble max_valid_value);
void gimp_number_pair_entry_set_default_values (GimpNumberPairEntry *entry,
gdouble left,
gdouble right);
void gimp_number_pair_entry_get_default_values (GimpNumberPairEntry *entry,
gdouble *left,
gdouble *right);
void gimp_number_pair_entry_set_values (GimpNumberPairEntry *entry,
gdouble left,
gdouble right);
void gimp_number_pair_entry_get_values (GimpNumberPairEntry *entry,
gdouble *left,
gdouble *right);
void gimp_number_pair_entry_set_default_text (GimpNumberPairEntry *entry,
const gchar *string);
const gchar * gimp_number_pair_entry_get_default_text (GimpNumberPairEntry *entry);
void gimp_number_pair_entry_set_ratio (GimpNumberPairEntry *entry,
gdouble ratio);
gdouble gimp_number_pair_entry_get_ratio (GimpNumberPairEntry *entry);
void gimp_number_pair_entry_set_aspect (GimpNumberPairEntry *entry,
GimpAspectType aspect);
GimpAspectType gimp_number_pair_entry_get_aspect (GimpNumberPairEntry *entry);
void gimp_number_pair_entry_set_user_override (GimpNumberPairEntry *entry,
gboolean user_override);
gboolean gimp_number_pair_entry_get_user_override (GimpNumberPairEntry *entry);
G_END_DECLS
#endif /* __GIMP_NUMBER_PAIR_ENTRY_H__ */

View file

@ -0,0 +1,513 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpoffsetarea.c
* Copyright (C) 2001 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gtk/gtk.h>
#include "gimpwidgetstypes.h"
#include "gimpwidgetsmarshal.h"
#include "gimpoffsetarea.h"
/**
* SECTION: gimpoffsetarea
* @title: GimpOffsetArea
* @short_description: Widget to control image offsets.
*
* Widget to control image offsets.
**/
#define DRAWING_AREA_SIZE 200
enum
{
OFFSETS_CHANGED,
LAST_SIGNAL
};
struct _GimpOffsetArea
{
GtkDrawingArea parent_instance;
gint orig_width;
gint orig_height;
gint width;
gint height;
gint offset_x;
gint offset_y;
gdouble display_ratio_x;
gdouble display_ratio_y;
};
static void gimp_offset_area_resize (GimpOffsetArea *area);
static void gimp_offset_area_realize (GtkWidget *widget);
static void gimp_offset_area_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static gboolean gimp_offset_area_event (GtkWidget *widget,
GdkEvent *event);
static gboolean gimp_offset_area_draw (GtkWidget *widget,
cairo_t *cr);
G_DEFINE_TYPE (GimpOffsetArea, gimp_offset_area, GTK_TYPE_DRAWING_AREA)
#define parent_class gimp_offset_area_parent_class
static guint gimp_offset_area_signals[LAST_SIGNAL] = { 0 };
static void
gimp_offset_area_class_init (GimpOffsetAreaClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
gimp_offset_area_signals[OFFSETS_CHANGED] =
g_signal_new ("offsets-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
_gimp_widgets_marshal_VOID__INT_INT,
G_TYPE_NONE, 2,
G_TYPE_INT,
G_TYPE_INT);
widget_class->size_allocate = gimp_offset_area_size_allocate;
widget_class->realize = gimp_offset_area_realize;
widget_class->event = gimp_offset_area_event;
widget_class->draw = gimp_offset_area_draw;
}
static void
gimp_offset_area_init (GimpOffsetArea *area)
{
area->display_ratio_x = 1.0;
area->display_ratio_y = 1.0;
gtk_widget_add_events (GTK_WIDGET (area),
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_BUTTON1_MOTION_MASK);
}
/**
* gimp_offset_area_new:
* @orig_width: the original width
* @orig_height: the original height
*
* Creates a new #GimpOffsetArea widget. A #GimpOffsetArea can be used
* when resizing an image or a drawable to allow the user to interactively
* specify the new offsets.
*
* Returns: the new #GimpOffsetArea widget.
**/
GtkWidget *
gimp_offset_area_new (gint orig_width,
gint orig_height)
{
GimpOffsetArea *area;
g_return_val_if_fail (orig_width > 0, NULL);
g_return_val_if_fail (orig_height > 0, NULL);
area = g_object_new (GIMP_TYPE_OFFSET_AREA, NULL);
area->orig_width = area->width = orig_width;
area->orig_height = area->height = orig_height;
gimp_offset_area_resize (area);
return GTK_WIDGET (area);
}
/**
* gimp_offset_area_set_pixbuf:
* @offset_area: a #GimpOffsetArea.
* @pixbuf: a #GdkPixbuf.
*
* Sets the pixbuf which represents the original image/drawable which
* is being offset.
*
* Since: 2.2
**/
void
gimp_offset_area_set_pixbuf (GimpOffsetArea *area,
GdkPixbuf *pixbuf)
{
g_return_if_fail (GIMP_IS_OFFSET_AREA (area));
g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
g_object_set_data_full (G_OBJECT (area), "pixbuf",
gdk_pixbuf_copy (pixbuf),
(GDestroyNotify) g_object_unref);
gtk_widget_queue_draw (GTK_WIDGET (area));
}
/**
* gimp_offset_area_set_size:
* @offset_area: a #GimpOffsetArea.
* @width: the new width
* @height: the new height
*
* Sets the size of the image/drawable displayed by the #GimpOffsetArea.
* If the offsets change as a result of this change, the "offsets-changed"
* signal is emitted.
**/
void
gimp_offset_area_set_size (GimpOffsetArea *area,
gint width,
gint height)
{
g_return_if_fail (GIMP_IS_OFFSET_AREA (area));
g_return_if_fail (width > 0 && height > 0);
if (area->width != width || area->height != height)
{
gint offset_x;
gint offset_y;
area->width = width;
area->height = height;
if (area->orig_width <= area->width)
offset_x = CLAMP (area->offset_x,
0, area->width - area->orig_width);
else
offset_x = CLAMP (area->offset_x,
area->width - area->orig_width, 0);
if (area->orig_height <= area->height)
offset_y = CLAMP (area->offset_y,
0, area->height - area->orig_height);
else
offset_y = CLAMP (area->offset_y,
area->height - area->orig_height, 0);
if (offset_x != area->offset_x || offset_y != area->offset_y)
{
area->offset_x = offset_x;
area->offset_y = offset_y;
g_signal_emit (area,
gimp_offset_area_signals[OFFSETS_CHANGED], 0,
offset_x, offset_y);
}
gimp_offset_area_resize (area);
}
}
/**
* gimp_offset_area_set_offsets:
* @offset_area: a #GimpOffsetArea.
* @offset_x: the X offset
* @offset_y: the Y offset
*
* Sets the offsets of the image/drawable displayed by the #GimpOffsetArea.
* It does not emit the "offsets-changed" signal.
**/
void
gimp_offset_area_set_offsets (GimpOffsetArea *area,
gint offset_x,
gint offset_y)
{
g_return_if_fail (GIMP_IS_OFFSET_AREA (area));
if (area->offset_x != offset_x || area->offset_y != offset_y)
{
if (area->orig_width <= area->width)
area->offset_x = CLAMP (offset_x,
0, area->width - area->orig_width);
else
area->offset_x = CLAMP (offset_x,
area->width - area->orig_width, 0);
if (area->orig_height <= area->height)
area->offset_y = CLAMP (offset_y,
0, area->height - area->orig_height);
else
area->offset_y = CLAMP (offset_y,
area->height - area->orig_height, 0);
gtk_widget_queue_draw (GTK_WIDGET (area));
}
}
static void
gimp_offset_area_resize (GimpOffsetArea *area)
{
gint width;
gint height;
gdouble ratio;
if (area->orig_width == 0 || area->orig_height == 0)
return;
if (area->orig_width <= area->width)
width = area->width;
else
width = area->orig_width * 2 - area->width;
if (area->orig_height <= area->height)
height = area->height;
else
height = area->orig_height * 2 - area->height;
ratio = (gdouble) DRAWING_AREA_SIZE / (gdouble) MAX (width, height);
width = ratio * (gdouble) width;
height = ratio * (gdouble) height;
gtk_widget_set_size_request (GTK_WIDGET (area), width, height);
gtk_widget_queue_resize (GTK_WIDGET (area));
}
static void
gimp_offset_area_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
GimpOffsetArea *area = GIMP_OFFSET_AREA (widget);
GdkPixbuf *pixbuf;
GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
area->display_ratio_x = ((gdouble) allocation->width /
((area->orig_width <= area->width) ?
area->width :
area->orig_width * 2 - area->width));
area->display_ratio_y = ((gdouble) allocation->height /
((area->orig_height <= area->height) ?
area->height :
area->orig_height * 2 - area->height));
pixbuf = g_object_get_data (G_OBJECT (area), "pixbuf");
if (pixbuf)
{
GdkPixbuf *copy;
gint pixbuf_width;
gint pixbuf_height;
pixbuf_width = area->display_ratio_x * area->orig_width;
pixbuf_width = MAX (pixbuf_width, 1);
pixbuf_height = area->display_ratio_y * area->orig_height;
pixbuf_height = MAX (pixbuf_height, 1);
copy = g_object_get_data (G_OBJECT (area), "pixbuf-copy");
if (copy &&
(pixbuf_width != gdk_pixbuf_get_width (copy) ||
pixbuf_height != gdk_pixbuf_get_height (copy)))
{
copy = NULL;
}
if (! copy)
{
copy = gdk_pixbuf_scale_simple (pixbuf, pixbuf_width, pixbuf_height,
GDK_INTERP_NEAREST);
g_object_set_data_full (G_OBJECT (area), "pixbuf-copy",
copy, (GDestroyNotify) g_object_unref);
}
}
}
static void
gimp_offset_area_realize (GtkWidget *widget)
{
GdkCursor *cursor;
GTK_WIDGET_CLASS (parent_class)->realize (widget);
cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
GDK_FLEUR);
gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
g_object_unref (cursor);
}
static gboolean
gimp_offset_area_event (GtkWidget *widget,
GdkEvent *event)
{
static gint orig_offset_x = 0;
static gint orig_offset_y = 0;
static gint start_x = 0;
static gint start_y = 0;
GimpOffsetArea *area = GIMP_OFFSET_AREA (widget);
gint offset_x;
gint offset_y;
if (area->orig_width == 0 || area->orig_height == 0)
return FALSE;
switch (event->type)
{
case GDK_BUTTON_PRESS:
if (event->button.button == 1)
{
gtk_grab_add (widget);
orig_offset_x = area->offset_x;
orig_offset_y = area->offset_y;
start_x = event->button.x;
start_y = event->button.y;
}
break;
case GDK_MOTION_NOTIFY:
offset_x = (orig_offset_x +
(event->motion.x - start_x) / area->display_ratio_x);
offset_y = (orig_offset_y +
(event->motion.y - start_y) / area->display_ratio_y);
if (area->offset_x != offset_x || area->offset_y != offset_y)
{
gimp_offset_area_set_offsets (area, offset_x, offset_y);
g_signal_emit (area,
gimp_offset_area_signals[OFFSETS_CHANGED], 0,
area->offset_x, area->offset_y);
}
break;
case GDK_BUTTON_RELEASE:
if (event->button.button == 1)
{
gtk_grab_remove (widget);
start_x = start_y = 0;
}
break;
default:
return FALSE;
}
return TRUE;
}
static gboolean
gimp_offset_area_draw (GtkWidget *widget,
cairo_t *cr)
{
GimpOffsetArea *area = GIMP_OFFSET_AREA (widget);
GtkStyleContext *context = gtk_widget_get_style_context (widget);
GtkAllocation allocation;
GdkPixbuf *pixbuf;
gint w, h;
gint x, y;
gtk_widget_get_allocation (widget, &allocation);
x = (area->display_ratio_x *
((area->orig_width <= area->width) ?
area->offset_x :
area->offset_x + area->orig_width - area->width));
y = (area->display_ratio_y *
((area->orig_height <= area->height) ?
area->offset_y :
area->offset_y + area->orig_height - area->height));
w = area->display_ratio_x * area->orig_width;
w = MAX (w, 1);
h = area->display_ratio_y * area->orig_height;
h = MAX (h, 1);
pixbuf = g_object_get_data (G_OBJECT (widget), "pixbuf-copy");
if (pixbuf)
{
gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y);
cairo_paint (cr);
cairo_rectangle (cr, x + 0.5, y + 0.5, w - 1, h - 1);
cairo_set_line_width (cr, 1.0);
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
cairo_stroke (cr);
}
else
{
gtk_render_frame (context, cr, x, y, w, h);
}
if (area->orig_width > area->width ||
area->orig_height > area->height)
{
gint line_width;
if (area->orig_width > area->width)
{
x = area->display_ratio_x * (area->orig_width - area->width);
w = area->display_ratio_x * area->width;
}
else
{
x = -1;
w = allocation.width + 2;
}
if (area->orig_height > area->height)
{
y = area->display_ratio_y * (area->orig_height - area->height);
h = area->display_ratio_y * area->height;
}
else
{
y = -1;
h = allocation.height + 2;
}
w = MAX (w, 1);
h = MAX (h, 1);
line_width = MIN (3, MIN (w, h));
cairo_rectangle (cr,
x + line_width / 2.0,
y + line_width / 2.0,
MAX (w - line_width, 1),
MAX (h - line_width, 1));
cairo_set_line_width (cr, line_width);
cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.6);
cairo_stroke_preserve (cr);
cairo_set_line_width (cr, 1.0);
cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.8);
cairo_stroke (cr);
}
return FALSE;
}

View file

@ -0,0 +1,53 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpoffsetarea.h
* Copyright (C) 2001 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_OFFSET_AREA_H__
#define __GIMP_OFFSET_AREA_H__
G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
#define GIMP_TYPE_OFFSET_AREA (gimp_offset_area_get_type ())
G_DECLARE_FINAL_TYPE (GimpOffsetArea, gimp_offset_area, GIMP, OFFSET_AREA, GtkDrawingArea)
GtkWidget * gimp_offset_area_new (gint orig_width,
gint orig_height);
void gimp_offset_area_set_pixbuf (GimpOffsetArea *offset_area,
GdkPixbuf *pixbuf);
void gimp_offset_area_set_size (GimpOffsetArea *offset_area,
gint width,
gint height);
void gimp_offset_area_set_offsets (GimpOffsetArea *offset_area,
gint offset_x,
gint offset_y);
G_END_DECLS
#endif /* __GIMP_OFFSET_AREA_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,75 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimppageselector.h
* Copyright (C) 2005 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
#endif
#ifndef __GIMP_PAGE_SELECTOR_H__
#define __GIMP_PAGE_SELECTOR_H__
G_BEGIN_DECLS
#define GIMP_TYPE_PAGE_SELECTOR (gimp_page_selector_get_type ())
G_DECLARE_FINAL_TYPE (GimpPageSelector, gimp_page_selector, GIMP, PAGE_SELECTOR, GtkBox)
GtkWidget * gimp_page_selector_new (void);
void gimp_page_selector_set_n_pages (GimpPageSelector *selector,
gint n_pages);
gint gimp_page_selector_get_n_pages (GimpPageSelector *selector);
void gimp_page_selector_set_target (GimpPageSelector *selector,
GimpPageSelectorTarget target);
GimpPageSelectorTarget
gimp_page_selector_get_target (GimpPageSelector *selector);
void gimp_page_selector_set_page_thumbnail (GimpPageSelector *selector,
gint page_no,
GdkPixbuf *thumbnail);
GdkPixbuf * gimp_page_selector_get_page_thumbnail (GimpPageSelector *selector,
gint page_no);
void gimp_page_selector_set_page_label (GimpPageSelector *selector,
gint page_no,
const gchar *label);
gchar * gimp_page_selector_get_page_label (GimpPageSelector *selector,
gint page_no);
void gimp_page_selector_select_all (GimpPageSelector *selector);
void gimp_page_selector_unselect_all (GimpPageSelector *selector);
void gimp_page_selector_select_page (GimpPageSelector *selector,
gint page_no);
void gimp_page_selector_unselect_page (GimpPageSelector *selector,
gint page_no);
gboolean gimp_page_selector_page_is_selected (GimpPageSelector *selector,
gint page_no);
gint * gimp_page_selector_get_selected_pages (GimpPageSelector *selector,
gint *n_selected_pages);
void gimp_page_selector_select_range (GimpPageSelector *selector,
const gchar *range);
gchar * gimp_page_selector_get_selected_range (GimpPageSelector *selector);
G_END_DECLS
#endif /* __GIMP_PAGE_SELECTOR_H__ */

View file

@ -0,0 +1,894 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimppatheditor.c
* Copyright (C) 1999-2004 Michael Natterer <mitch@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "gimpwidgetstypes.h"
#undef GIMP_DISABLE_DEPRECATED
#include "gimpfileentry.h"
#include "gimphelpui.h"
#include "gimpicons.h"
#include "gimppatheditor.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimppatheditor
* @title: GimpPathEditor
* @short_description: Widget for editing a file search path.
* @see_also: #GimpFileEntry, #G_SEARCHPATH_SEPARATOR
*
* This widget is used to edit file search paths.
*
* It shows a list of all directories which are in the search
* path. You can click a directory to select it. The widget provides a
* #GimpFileEntry to change the currently selected directory.
*
* There are buttons to add or delete directories as well as "up" and
* "down" buttons to change the order in which the directories will be
* searched.
*
* Whenever the user adds, deletes, changes or reorders a directory of
* the search path, the "path_changed" signal will be emitted.
**/
enum
{
PATH_CHANGED,
WRITABLE_CHANGED,
LAST_SIGNAL
};
enum
{
COLUMN_UTF8,
COLUMN_DIRECTORY,
COLUMN_WRITABLE,
NUM_COLUMNS
};
struct _GimpPathEditor
{
GtkBox parent_instance;
GtkWidget *upper_hbox;
GtkWidget *new_button;
GtkWidget *up_button;
GtkWidget *down_button;
GtkWidget *delete_button;
GtkWidget *file_entry;
GtkListStore *dir_list;
GtkTreeSelection *sel;
GtkTreePath *sel_path;
GtkTreeViewColumn *writable_column;
gint num_items;
};
static void gimp_path_editor_new_clicked (GtkWidget *widget,
GimpPathEditor *editor);
static void gimp_path_editor_move_clicked (GtkWidget *widget,
GimpPathEditor *editor);
static void gimp_path_editor_delete_clicked (GtkWidget *widget,
GimpPathEditor *editor);
static void gimp_path_editor_file_entry_changed (GtkWidget *widget,
GimpPathEditor *editor);
static void gimp_path_editor_selection_changed (GtkTreeSelection *sel,
GimpPathEditor *editor);
static void gimp_path_editor_writable_toggled (GtkCellRendererToggle *toggle,
gchar *path_str,
GimpPathEditor *editor);
G_DEFINE_TYPE (GimpPathEditor, gimp_path_editor, GTK_TYPE_BOX)
#define parent_class gimp_path_editor_parent_class
static guint gimp_path_editor_signals[LAST_SIGNAL] = { 0 };
static void
gimp_path_editor_class_init (GimpPathEditorClass *klass)
{
/**
* GimpPathEditor::path-changed:
*
* This signal is emitted whenever the user adds, deletes, modifies
* or reorders an element of the search path.
**/
gimp_path_editor_signals[PATH_CHANGED] =
g_signal_new ("path-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
/**
* GimpPathEditor::writable-changed:
*
* This signal is emitted whenever the "writable" column of a directory
* is changed, either by the user clicking on it or by calling
* gimp_path_editor_set_dir_writable().
**/
gimp_path_editor_signals[WRITABLE_CHANGED] =
g_signal_new ("writable-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
}
static void
gimp_path_editor_init (GimpPathEditor *editor)
{
GtkWidget *button_box;
GtkWidget *button;
GtkWidget *image;
GtkWidget *scrolled_window;
GtkWidget *tv;
GtkTreeViewColumn *col;
GtkCellRenderer *renderer;
editor->file_entry = NULL;
editor->sel_path = NULL;
editor->num_items = 0;
gtk_orientable_set_orientation (GTK_ORIENTABLE (editor),
GTK_ORIENTATION_VERTICAL);
editor->upper_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
gtk_box_pack_start (GTK_BOX (editor), editor->upper_hbox, FALSE, TRUE, 0);
gtk_widget_show (editor->upper_hbox);
button_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_set_homogeneous (GTK_BOX (button_box), TRUE);
gtk_box_pack_start (GTK_BOX (editor->upper_hbox), button_box, FALSE, TRUE, 0);
gtk_widget_show (button_box);
editor->new_button = button = gtk_button_new ();
gtk_box_pack_start (GTK_BOX (button_box), button, TRUE, TRUE, 0);
gtk_widget_show (button);
image = gtk_image_new_from_icon_name (GIMP_ICON_DOCUMENT_NEW,
GTK_ICON_SIZE_BUTTON);
gtk_container_add (GTK_CONTAINER (button), image);
gtk_widget_show (image);
g_signal_connect (button, "clicked",
G_CALLBACK (gimp_path_editor_new_clicked),
editor);
gimp_help_set_help_data (editor->new_button,
_("Add a new folder"),
NULL);
editor->up_button = button = gtk_button_new ();
gtk_widget_set_sensitive (button, FALSE);
gtk_box_pack_start (GTK_BOX (button_box), button, TRUE, TRUE, 0);
gtk_widget_show (button);
image = gtk_image_new_from_icon_name (GIMP_ICON_GO_UP,
GTK_ICON_SIZE_BUTTON);
gtk_container_add (GTK_CONTAINER (button), image);
gtk_widget_show (image);
g_signal_connect (button, "clicked",
G_CALLBACK (gimp_path_editor_move_clicked),
editor);
gimp_help_set_help_data (editor->up_button,
_("Move the selected folder up"),
NULL);
editor->down_button = button = gtk_button_new ();
gtk_widget_set_sensitive (button, FALSE);
gtk_box_pack_start (GTK_BOX (button_box), button, TRUE, TRUE, 0);
gtk_widget_show (button);
image = gtk_image_new_from_icon_name (GIMP_ICON_GO_DOWN,
GTK_ICON_SIZE_BUTTON);
gtk_container_add (GTK_CONTAINER (button), image);
gtk_widget_show (image);
g_signal_connect (button, "clicked",
G_CALLBACK (gimp_path_editor_move_clicked),
editor);
gimp_help_set_help_data (editor->down_button,
_("Move the selected folder down"),
NULL);
editor->delete_button = button = gtk_button_new ();
gtk_widget_set_sensitive (button, FALSE);
gtk_box_pack_start (GTK_BOX (button_box), button, TRUE, TRUE, 0);
gtk_widget_show (button);
image = gtk_image_new_from_icon_name (GIMP_ICON_EDIT_DELETE,
GTK_ICON_SIZE_BUTTON);
gtk_container_add (GTK_CONTAINER (button), image);
gtk_widget_show (image);
g_signal_connect (button, "clicked",
G_CALLBACK (gimp_path_editor_delete_clicked),
editor);
gimp_help_set_help_data (editor->delete_button,
_("Remove the selected folder from the list"),
NULL);
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_SHADOW_IN);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_ALWAYS);
gtk_box_pack_start (GTK_BOX (editor), scrolled_window, TRUE, TRUE, 2);
gtk_widget_show (scrolled_window);
editor->dir_list = gtk_list_store_new (NUM_COLUMNS,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_BOOLEAN);
tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (editor->dir_list));
g_object_unref (editor->dir_list);
renderer = gtk_cell_renderer_toggle_new ();
g_signal_connect (renderer, "toggled",
G_CALLBACK (gimp_path_editor_writable_toggled),
editor);
editor->writable_column = col = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (col, _("Writable"));
gtk_tree_view_column_pack_start (col, renderer, FALSE);
gtk_tree_view_column_add_attribute (col, renderer, "active", COLUMN_WRITABLE);
gtk_tree_view_append_column (GTK_TREE_VIEW (tv), col);
gtk_tree_view_column_set_visible (col, FALSE);
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv),
-1, _("Folder"),
gtk_cell_renderer_text_new (),
"text", COLUMN_UTF8,
NULL);
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tv), TRUE);
gtk_container_add (GTK_CONTAINER (scrolled_window), tv);
gtk_widget_show (tv);
editor->sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
g_signal_connect (editor->sel, "changed",
G_CALLBACK (gimp_path_editor_selection_changed),
editor);
}
/**
* gimp_path_editor_new:
* @title: The title of the #GtkFileChooser dialog which can be popped up.
* @path: (nullable): The initial search path.
*
* Creates a new #GimpPathEditor widget.
*
* The elements of the initial search path must be separated with the
* #G_SEARCHPATH_SEPARATOR character.
*
* Returns: A pointer to the new #GimpPathEditor widget.
**/
GtkWidget *
gimp_path_editor_new (const gchar *title,
const gchar *path)
{
GimpPathEditor *editor;
g_return_val_if_fail (title != NULL, NULL);
editor = g_object_new (GIMP_TYPE_PATH_EDITOR, NULL);
editor->file_entry = _gimp_file_entry_new (title, "", TRUE, TRUE);
gtk_widget_set_sensitive (editor->file_entry, FALSE);
gtk_box_pack_start (GTK_BOX (editor->upper_hbox), editor->file_entry,
TRUE, TRUE, 0);
gtk_widget_show (editor->file_entry);
g_signal_connect (editor->file_entry, "filename-changed",
G_CALLBACK (gimp_path_editor_file_entry_changed),
editor);
if (path)
gimp_path_editor_set_path (editor, path);
return GTK_WIDGET (editor);
}
/**
* gimp_path_editor_get_path:
* @editor: The path editor you want to get the search path from.
*
* The elements of the returned search path string are separated with the
* #G_SEARCHPATH_SEPARATOR character.
*
* Note that you have to g_free() the returned string.
*
* Returns: The search path the user has selected in the path editor.
**/
gchar *
gimp_path_editor_get_path (GimpPathEditor *editor)
{
GtkTreeModel *model;
GString *path;
GtkTreeIter iter;
gboolean iter_valid;
g_return_val_if_fail (GIMP_IS_PATH_EDITOR (editor), g_strdup (""));
model = GTK_TREE_MODEL (editor->dir_list);
path = g_string_new ("");
for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &iter))
{
gchar *dir;
gtk_tree_model_get (model, &iter,
COLUMN_DIRECTORY, &dir,
-1);
if (path->len > 0)
g_string_append_c (path, G_SEARCHPATH_SEPARATOR);
g_string_append (path, dir);
g_free (dir);
}
return g_string_free (path, FALSE);
}
/**
* gimp_path_editor_set_path:
* @editor: The path editor you want to set the search path from.
* @path: The new path to set.
*
* The elements of the initial search path must be separated with the
* %G_SEARCHPATH_SEPARATOR character.
**/
void
gimp_path_editor_set_path (GimpPathEditor *editor,
const gchar *path)
{
gchar *old_path;
GList *path_list;
GList *list;
g_return_if_fail (GIMP_IS_PATH_EDITOR (editor));
old_path = gimp_path_editor_get_path (editor);
if (old_path && path && strcmp (old_path, path) == 0)
{
g_free (old_path);
return;
}
g_free (old_path);
path_list = gimp_path_parse (path, 256, FALSE, NULL);
gtk_list_store_clear (editor->dir_list);
for (list = path_list; list; list = g_list_next (list))
{
gchar *directory = list->data;
gchar *utf8;
GtkTreeIter iter;
utf8 = g_filename_to_utf8 (directory, -1, NULL, NULL, NULL);
gtk_list_store_append (editor->dir_list, &iter);
gtk_list_store_set (editor->dir_list, &iter,
COLUMN_UTF8, utf8,
COLUMN_DIRECTORY, directory,
COLUMN_WRITABLE, FALSE,
-1);
g_free (utf8);
editor->num_items++;
}
gimp_path_free (path_list);
g_signal_emit (editor, gimp_path_editor_signals[PATH_CHANGED], 0);
}
gchar *
gimp_path_editor_get_writable_path (GimpPathEditor *editor)
{
GtkTreeModel *model;
GString *path;
GtkTreeIter iter;
gboolean iter_valid;
g_return_val_if_fail (GIMP_IS_PATH_EDITOR (editor), g_strdup (""));
model = GTK_TREE_MODEL (editor->dir_list);
path = g_string_new ("");
for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &iter))
{
gchar *dir;
gboolean dir_writable;
gtk_tree_model_get (model, &iter,
COLUMN_DIRECTORY, &dir,
COLUMN_WRITABLE, &dir_writable,
-1);
if (dir_writable)
{
if (path->len > 0)
g_string_append_c (path, G_SEARCHPATH_SEPARATOR);
g_string_append (path, dir);
}
g_free (dir);
}
return g_string_free (path, FALSE);
}
void
gimp_path_editor_set_writable_path (GimpPathEditor *editor,
const gchar *path)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean iter_valid;
GList *path_list;
gboolean writable_changed = FALSE;
g_return_if_fail (GIMP_IS_PATH_EDITOR (editor));
gtk_tree_view_column_set_visible (editor->writable_column, TRUE);
path_list = gimp_path_parse (path, 256, FALSE, NULL);
model = GTK_TREE_MODEL (editor->dir_list);
for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &iter))
{
gchar *dir;
gboolean dir_writable;
gboolean new_writable = FALSE;
gtk_tree_model_get (model, &iter,
COLUMN_DIRECTORY, &dir,
COLUMN_WRITABLE, &dir_writable,
-1);
if (g_list_find_custom (path_list, dir, (GCompareFunc) strcmp))
new_writable = TRUE;
g_free (dir);
if (dir_writable != new_writable)
{
gtk_list_store_set (editor->dir_list, &iter,
COLUMN_WRITABLE, new_writable,
-1);
writable_changed = TRUE;
}
}
gimp_path_free (path_list);
if (writable_changed)
g_signal_emit (editor, gimp_path_editor_signals[WRITABLE_CHANGED], 0);
}
gboolean
gimp_path_editor_get_dir_writable (GimpPathEditor *editor,
const gchar *directory)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean iter_valid;
g_return_val_if_fail (GIMP_IS_PATH_EDITOR (editor), FALSE);
g_return_val_if_fail (directory != NULL, FALSE);
model = GTK_TREE_MODEL (editor->dir_list);
for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &iter))
{
gchar *dir;
gboolean dir_writable;
gtk_tree_model_get (model, &iter,
COLUMN_DIRECTORY, &dir,
COLUMN_WRITABLE, &dir_writable,
-1);
if (! strcmp (dir, directory))
{
g_free (dir);
return dir_writable;
}
g_free (dir);
}
return FALSE;
}
void
gimp_path_editor_set_dir_writable (GimpPathEditor *editor,
const gchar *directory,
gboolean writable)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean iter_valid;
g_return_if_fail (GIMP_IS_PATH_EDITOR (editor));
g_return_if_fail (directory != NULL);
model = GTK_TREE_MODEL (editor->dir_list);
for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &iter))
{
gchar *dir;
gboolean dir_writable;
gtk_tree_model_get (model, &iter,
COLUMN_DIRECTORY, &dir,
COLUMN_WRITABLE, &dir_writable,
-1);
if (! strcmp (dir, directory) && dir_writable != writable)
{
gtk_list_store_set (editor->dir_list, &iter,
COLUMN_WRITABLE, writable ? TRUE : FALSE,
-1);
g_signal_emit (editor, gimp_path_editor_signals[WRITABLE_CHANGED], 0);
g_free (dir);
break;
}
g_free (dir);
}
}
/* editorate functions */
static void
gimp_path_editor_new_clicked (GtkWidget *widget,
GimpPathEditor *editor)
{
if (editor->sel_path)
{
g_signal_handlers_block_by_func (editor->sel,
gimp_path_editor_selection_changed,
editor);
gtk_tree_selection_unselect_path (editor->sel, editor->sel_path);
g_signal_handlers_unblock_by_func (editor->sel,
gimp_path_editor_selection_changed,
editor);
gtk_tree_path_free (editor->sel_path);
editor->sel_path = NULL;
}
gtk_widget_set_sensitive (editor->delete_button, FALSE);
gtk_widget_set_sensitive (editor->up_button, FALSE);
gtk_widget_set_sensitive (editor->down_button, FALSE);
gtk_widget_set_sensitive (editor->file_entry, TRUE);
gtk_editable_set_position
(GTK_EDITABLE (_gimp_file_entry_get_entry (GIMP_FILE_ENTRY (editor->file_entry))), -1);
gtk_widget_grab_focus
(_gimp_file_entry_get_entry (GIMP_FILE_ENTRY (editor->file_entry)));
}
static void
gimp_path_editor_move_clicked (GtkWidget *widget,
GimpPathEditor *editor)
{
GtkTreePath *path;
GtkTreeModel *model;
GtkTreeIter iter1, iter2;
gchar *utf81, *utf82;
gchar *dir1, *dir2;
gboolean writable1, writable2;
if (editor->sel_path == NULL)
return;
path = gtk_tree_path_copy (editor->sel_path);
if (widget == editor->up_button)
gtk_tree_path_prev (path);
else
gtk_tree_path_next (path);
model = GTK_TREE_MODEL (editor->dir_list);
gtk_tree_model_get_iter (model, &iter1, editor->sel_path);
gtk_tree_model_get_iter (model, &iter2, path);
gtk_tree_model_get (model, &iter1,
COLUMN_UTF8, &utf81,
COLUMN_DIRECTORY, &dir1,
COLUMN_WRITABLE, &writable1,
-1);
gtk_tree_model_get (model, &iter2,
COLUMN_UTF8, &utf82,
COLUMN_DIRECTORY, &dir2,
COLUMN_WRITABLE, &writable2,
-1);
gtk_list_store_set (editor->dir_list, &iter1,
COLUMN_UTF8, utf82,
COLUMN_DIRECTORY, dir2,
COLUMN_WRITABLE, writable2,
-1);
gtk_list_store_set (editor->dir_list, &iter2,
COLUMN_UTF8, utf81,
COLUMN_DIRECTORY, dir1,
COLUMN_WRITABLE, writable1,
-1);
g_free (utf81);
g_free (utf82);
g_free (dir2);
g_free (dir1);
gtk_tree_selection_select_iter (editor->sel, &iter2);
g_signal_emit (editor, gimp_path_editor_signals[PATH_CHANGED], 0);
}
static void
gimp_path_editor_delete_clicked (GtkWidget *widget,
GimpPathEditor *editor)
{
GtkTreeIter iter;
gboolean dir_writable;
if (editor->sel_path == NULL)
return;
gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->dir_list), &iter,
editor->sel_path);
gtk_tree_model_get (GTK_TREE_MODEL (editor->dir_list), &iter,
COLUMN_WRITABLE, &dir_writable,
-1);
gtk_list_store_remove (editor->dir_list, &iter);
editor->num_items--;
if (editor->num_items == 0)
{
gtk_tree_path_free (editor->sel_path);
editor->sel_path = NULL;
g_signal_handlers_block_by_func (editor->file_entry,
gimp_path_editor_file_entry_changed,
editor);
_gimp_file_entry_set_filename (GIMP_FILE_ENTRY (editor->file_entry), "");
g_signal_handlers_unblock_by_func (editor->file_entry,
gimp_path_editor_file_entry_changed,
editor);
gtk_widget_set_sensitive (editor->delete_button, FALSE);
gtk_widget_set_sensitive (editor->up_button, FALSE);
gtk_widget_set_sensitive (editor->down_button, FALSE);
gtk_widget_set_sensitive (editor->file_entry, FALSE);
}
else
{
gint *indices;
indices = gtk_tree_path_get_indices (editor->sel_path);
if ((indices[0] == editor->num_items) && (indices[0] > 0))
gtk_tree_path_prev (editor->sel_path);
gtk_tree_selection_select_path (editor->sel, editor->sel_path);
}
g_signal_emit (editor, gimp_path_editor_signals[PATH_CHANGED], 0);
if (dir_writable)
g_signal_emit (editor, gimp_path_editor_signals[WRITABLE_CHANGED], 0);
}
static void
gimp_path_editor_file_entry_changed (GtkWidget *widget,
GimpPathEditor *editor)
{
gchar *dir;
gchar *utf8;
GtkTreeIter iter;
dir = _gimp_file_entry_get_filename (GIMP_FILE_ENTRY (widget));
if (strcmp (dir, "") == 0)
{
g_free (dir);
return;
}
utf8 = g_filename_display_name (dir);
if (editor->sel_path == NULL)
{
gtk_list_store_append (editor->dir_list, &iter);
gtk_list_store_set (editor->dir_list, &iter,
COLUMN_UTF8, utf8,
COLUMN_DIRECTORY, dir,
COLUMN_WRITABLE, FALSE,
-1);
editor->num_items++;
gtk_tree_selection_select_iter (editor->sel, &iter);
}
else
{
gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->dir_list), &iter,
editor->sel_path);
gtk_list_store_set (editor->dir_list, &iter,
COLUMN_UTF8, utf8,
COLUMN_DIRECTORY, dir,
-1);
}
g_free (dir);
g_free (utf8);
g_signal_emit (editor, gimp_path_editor_signals[PATH_CHANGED], 0);
}
static void
gimp_path_editor_selection_changed (GtkTreeSelection *sel,
GimpPathEditor *editor)
{
GtkTreeIter iter;
gchar *directory;
gint *indices;
if (gtk_tree_selection_get_selected (sel, NULL, &iter))
{
gtk_tree_model_get (GTK_TREE_MODEL (editor->dir_list), &iter,
COLUMN_DIRECTORY, &directory,
-1);
g_signal_handlers_block_by_func (editor->file_entry,
gimp_path_editor_file_entry_changed,
editor);
_gimp_file_entry_set_filename (GIMP_FILE_ENTRY (editor->file_entry),
directory);
g_signal_handlers_unblock_by_func (editor->file_entry,
gimp_path_editor_file_entry_changed,
editor);
g_free (directory);
if (editor->sel_path)
gtk_tree_path_free (editor->sel_path);
editor->sel_path =
gtk_tree_model_get_path (GTK_TREE_MODEL (editor->dir_list), &iter);
indices = gtk_tree_path_get_indices (editor->sel_path);
gtk_widget_set_sensitive (editor->delete_button, TRUE);
gtk_widget_set_sensitive (editor->up_button, (indices[0] > 0));
gtk_widget_set_sensitive (editor->down_button,
(indices[0] < (editor->num_items - 1)));
gtk_widget_set_sensitive (editor->file_entry, TRUE);
}
else
{
g_signal_handlers_block_by_func (sel,
gimp_path_editor_selection_changed,
editor);
gtk_tree_selection_select_path (editor->sel, editor->sel_path);
g_signal_handlers_unblock_by_func (sel,
gimp_path_editor_selection_changed,
editor);
}
}
static void
gimp_path_editor_writable_toggled (GtkCellRendererToggle *toggle,
gchar *path_str,
GimpPathEditor *editor)
{
GtkTreePath *path;
GtkTreeIter iter;
path = gtk_tree_path_new_from_string (path_str);
if (gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->dir_list), &iter, path))
{
gboolean dir_writable;
gtk_tree_model_get (GTK_TREE_MODEL (editor->dir_list), &iter,
COLUMN_WRITABLE, &dir_writable,
-1);
gtk_list_store_set (editor->dir_list, &iter,
COLUMN_WRITABLE, ! dir_writable,
-1);
g_signal_emit (editor, gimp_path_editor_signals[WRITABLE_CHANGED], 0);
}
gtk_tree_path_free (path);
}

Some files were not shown because too many files have changed in this diff Show more