summaryrefslogtreecommitdiffstats
path: root/libgimp/gimpgradientselectbutton.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgimp/gimpgradientselectbutton.c')
-rw-r--r--libgimp/gimpgradientselectbutton.c628
1 files changed, 628 insertions, 0 deletions
diff --git a/libgimp/gimpgradientselectbutton.c b/libgimp/gimpgradientselectbutton.c
new file mode 100644
index 0000000..fac6e3a
--- /dev/null
+++ b/libgimp/gimpgradientselectbutton.c
@@ -0,0 +1,628 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpgradientselectbutton.c
+ * Copyright (C) 1998 Andy Thomas
+ *
+ * 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 "libgimpwidgets/gimpwidgets.h"
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimpgradientselectbutton.h"
+#include "gimpuimarshal.h"
+
+#include "libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpgradientselectbutton
+ * @title: GimpGradientSelectButton
+ * @short_description: A button which pops up a gradient select dialog.
+ *
+ * A button which pops up a gradient select dialog.
+ **/
+
+
+#define CELL_HEIGHT 18
+#define CELL_WIDTH 84
+
+
+#define GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE(obj) ((GimpGradientSelectButtonPrivate *) gimp_gradient_select_button_get_instance_private ((GimpGradientSelectButton *) (obj)))
+
+typedef struct _GimpGradientSelectButtonPrivate GimpGradientSelectButtonPrivate;
+
+struct _GimpGradientSelectButtonPrivate
+{
+ gchar *title;
+
+ gchar *gradient_name; /* Local copy */
+ gint sample_size;
+ gboolean reverse;
+ gint n_samples;
+ gdouble *gradient_data; /* Local copy */
+
+ GtkWidget *inside;
+ GtkWidget *preview;
+};
+
+enum
+{
+ GRADIENT_SET,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_TITLE,
+ PROP_GRADIENT_NAME
+};
+
+
+/* local function prototypes */
+
+static void gimp_gradient_select_button_finalize (GObject *object);
+
+static void gimp_gradient_select_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_gradient_select_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_gradient_select_button_clicked (GimpGradientSelectButton *button);
+
+static void gimp_gradient_select_button_callback (const gchar *gradient_name,
+ gint n_samples,
+ const gdouble *gradient_data,
+ gboolean dialog_closing,
+ gpointer user_data);
+
+static void gimp_gradient_select_preview_size_allocate
+ (GtkWidget *widget,
+ GtkAllocation *allocation,
+ GimpGradientSelectButton *button);
+static gboolean gimp_gradient_select_preview_expose (GtkWidget *preview,
+ GdkEventExpose *event,
+ GimpGradientSelectButton *button);
+
+static void gimp_gradient_select_drag_data_received (GimpGradientSelectButton *button,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time);
+
+static GtkWidget * gimp_gradient_select_button_create_inside (GimpGradientSelectButton *button);
+
+
+static const GtkTargetEntry target = { "application/x-gimp-gradient-name", 0 };
+
+static guint gradient_button_signals[LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpGradientSelectButton,
+ gimp_gradient_select_button,
+ GIMP_TYPE_SELECT_BUTTON)
+
+
+static void
+gimp_gradient_select_button_class_init (GimpGradientSelectButtonClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpSelectButtonClass *select_button_class = GIMP_SELECT_BUTTON_CLASS (klass);
+
+ object_class->finalize = gimp_gradient_select_button_finalize;
+ object_class->set_property = gimp_gradient_select_button_set_property;
+ object_class->get_property = gimp_gradient_select_button_get_property;
+
+ select_button_class->select_destroy = gimp_gradient_select_destroy;
+
+ klass->gradient_set = NULL;
+
+ /**
+ * GimpGradientSelectButton:title:
+ *
+ * The title to be used for the gradient selection popup dialog.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_TITLE,
+ g_param_spec_string ("title",
+ "Title",
+ "The title to be used for the gradient selection popup dialog",
+ _("Gradient Selection"),
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GimpGradientSelectButton:gradient-name:
+ *
+ * The name of the currently selected gradient.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_GRADIENT_NAME,
+ g_param_spec_string ("gradient-name",
+ "Gradient name",
+ "The name of the currently selected gradient",
+ NULL,
+ GIMP_PARAM_READWRITE));
+
+ /**
+ * GimpGradientSelectButton::gradient-set:
+ * @widget: the object which received the signal.
+ * @gradient_name: the name of the currently selected gradient.
+ * @width: width of the gradient
+ * @grad_data: gradient data
+ * @dialog_closing: whether the dialog was closed or not.
+ *
+ * The ::gradient-set signal is emitted when the user selects a gradient.
+ *
+ * Since: 2.4
+ */
+ gradient_button_signals[GRADIENT_SET] =
+ g_signal_new ("gradient-set",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpGradientSelectButtonClass, gradient_set),
+ NULL, NULL,
+ _gimpui_marshal_VOID__STRING_INT_POINTER_BOOLEAN,
+ G_TYPE_NONE, 4,
+ G_TYPE_STRING,
+ G_TYPE_INT,
+ G_TYPE_POINTER,
+ G_TYPE_BOOLEAN);
+}
+
+static void
+gimp_gradient_select_button_init (GimpGradientSelectButton *button)
+{
+ GimpGradientSelectButtonPrivate *priv;
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+
+ priv->gradient_name = gimp_context_get_gradient ();
+ priv->sample_size = CELL_WIDTH;
+ priv->reverse = FALSE;
+
+ priv->inside = gimp_gradient_select_button_create_inside (button);
+ gtk_container_add (GTK_CONTAINER (button), priv->inside);
+}
+
+/**
+ * gimp_gradient_select_button_new:
+ * @title: Title of the dialog to use or %NULL to use the default title.
+ * @gradient_name: Initial gradient name.
+ *
+ * Creates a new #GtkWidget that completely controls the selection of
+ * a gradient. This widget is suitable for placement in a table in a
+ * plug-in dialog.
+ *
+ * Returns: A #GtkWidget that you can use in your UI.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_gradient_select_button_new (const gchar *title,
+ const gchar *gradient_name)
+{
+ GtkWidget *button;
+
+ if (title)
+ button = g_object_new (GIMP_TYPE_GRADIENT_SELECT_BUTTON,
+ "title", title,
+ "gradient-name", gradient_name,
+ NULL);
+ else
+ button = g_object_new (GIMP_TYPE_GRADIENT_SELECT_BUTTON,
+ "gradient-name", gradient_name,
+ NULL);
+
+ return button;
+}
+
+/**
+ * gimp_gradient_select_button_get_gradient:
+ * @button: A #GimpGradientSelectButton
+ *
+ * Retrieves the name of currently selected gradient.
+ *
+ * Returns: an internal copy of the gradient name which must not be freed.
+ *
+ * Since: 2.4
+ */
+const gchar *
+gimp_gradient_select_button_get_gradient (GimpGradientSelectButton *button)
+{
+ GimpGradientSelectButtonPrivate *priv;
+
+ g_return_val_if_fail (GIMP_IS_GRADIENT_SELECT_BUTTON (button), NULL);
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+ return priv->gradient_name;
+}
+
+/**
+ * gimp_gradient_select_button_set_gradient:
+ * @button: A #GimpGradientSelectButton
+ * @gradient_name: Gradient name to set.
+ *
+ * Sets the current gradient for the gradient select button.
+ *
+ * Since: 2.4
+ */
+void
+gimp_gradient_select_button_set_gradient (GimpGradientSelectButton *button,
+ const gchar *gradient_name)
+{
+ GimpGradientSelectButtonPrivate *priv;
+ GimpSelectButton *select_button;
+
+ g_return_if_fail (GIMP_IS_GRADIENT_SELECT_BUTTON (button));
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ if (select_button->temp_callback)
+ {
+ gimp_gradients_set_popup (select_button->temp_callback, gradient_name);
+ }
+ else
+ {
+ gchar *name;
+ gdouble *samples;
+ gint n_samples;
+
+ if (gradient_name && *gradient_name)
+ name = g_strdup (gradient_name);
+ else
+ name = gimp_context_get_gradient ();
+
+ if (gimp_gradient_get_uniform_samples (name,
+ priv->sample_size,
+ priv->reverse,
+ &n_samples,
+ &samples))
+ {
+ gimp_gradient_select_button_callback (name,
+ n_samples, samples,
+ FALSE, button);
+
+ g_free (samples);
+ }
+
+ g_free (name);
+ }
+}
+
+
+/* private functions */
+
+static void
+gimp_gradient_select_button_finalize (GObject *object)
+{
+ GimpGradientSelectButtonPrivate *priv;
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (object);
+
+ g_clear_pointer (&priv->gradient_name, g_free);
+ g_clear_pointer (&priv->gradient_data, g_free);
+ g_clear_pointer (&priv->title, g_free);
+
+ G_OBJECT_CLASS (gimp_gradient_select_button_parent_class)->finalize (object);
+}
+
+static void
+gimp_gradient_select_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpGradientSelectButton *button;
+ GimpGradientSelectButtonPrivate *priv;
+
+ button = GIMP_GRADIENT_SELECT_BUTTON (object);
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+
+ switch (property_id)
+ {
+ case PROP_TITLE:
+ priv->title = g_value_dup_string (value);
+ break;
+ case PROP_GRADIENT_NAME:
+ gimp_gradient_select_button_set_gradient (button,
+ g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_gradient_select_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpGradientSelectButton *button;
+ GimpGradientSelectButtonPrivate *priv;
+
+ button = GIMP_GRADIENT_SELECT_BUTTON (object);
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+
+ switch (property_id)
+ {
+ case PROP_TITLE:
+ g_value_set_string (value, priv->title);
+ break;
+ case PROP_GRADIENT_NAME:
+ g_value_set_string (value, priv->gradient_name);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_gradient_select_button_callback (const gchar *gradient_name,
+ gint n_samples,
+ const gdouble *gradient_data,
+ gboolean dialog_closing,
+ gpointer user_data)
+{
+ GimpGradientSelectButton *button;
+ GimpGradientSelectButtonPrivate *priv;
+ GimpSelectButton *select_button;
+
+ button = GIMP_GRADIENT_SELECT_BUTTON (user_data);
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ g_free (priv->gradient_name);
+ g_free (priv->gradient_data);
+
+ priv->gradient_name = g_strdup (gradient_name);
+ priv->n_samples = n_samples;
+ priv->gradient_data = g_memdup (gradient_data, n_samples * sizeof (gdouble));
+
+ gtk_widget_queue_draw (priv->preview);
+
+ if (dialog_closing)
+ select_button->temp_callback = NULL;
+
+ g_signal_emit (button, gradient_button_signals[GRADIENT_SET], 0,
+ gradient_name, n_samples, gradient_data, dialog_closing);
+ g_object_notify (G_OBJECT (button), "gradient-name");
+}
+
+static void
+gimp_gradient_select_button_clicked (GimpGradientSelectButton *button)
+{
+ GimpGradientSelectButtonPrivate *priv;
+ GimpSelectButton *select_button;
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ if (select_button->temp_callback)
+ {
+ /* calling gimp_gradients_set_popup() raises the dialog */
+ gimp_gradients_set_popup (select_button->temp_callback,
+ priv->gradient_name);
+ }
+ else
+ {
+ select_button->temp_callback =
+ gimp_gradient_select_new (priv->title, priv->gradient_name,
+ priv->sample_size,
+ gimp_gradient_select_button_callback,
+ button);
+ }
+}
+
+static void
+gimp_gradient_select_preview_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation,
+ GimpGradientSelectButton *button)
+{
+ gdouble *samples;
+ gint n_samples;
+ GimpGradientSelectButtonPrivate *priv;
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+
+ if (gimp_gradient_get_uniform_samples (priv->gradient_name,
+ allocation->width,
+ priv->reverse,
+ &n_samples,
+ &samples))
+ {
+ g_free (priv->gradient_data);
+
+ priv->sample_size = allocation->width;
+ priv->n_samples = n_samples;
+ priv->gradient_data = samples;
+ }
+}
+
+static gboolean
+gimp_gradient_select_preview_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ GimpGradientSelectButton *button)
+{
+ GimpGradientSelectButtonPrivate *priv;
+ GtkAllocation allocation;
+ cairo_t *cr;
+ cairo_pattern_t *pattern;
+ cairo_surface_t *surface;
+ const gdouble *src;
+ guchar *dest;
+ gint width;
+ gint x;
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+
+ src = priv->gradient_data;
+ if (! src)
+ return FALSE;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ cr = gdk_cairo_create (event->window);
+
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ pattern = gimp_cairo_checkerboard_create (cr, GIMP_CHECK_SIZE_SM, NULL, NULL);
+ cairo_set_source (cr, pattern);
+ cairo_pattern_destroy (pattern);
+
+ cairo_paint (cr);
+
+ width = priv->n_samples / 4;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, 1);
+
+ for (x = 0, dest = cairo_image_surface_get_data (surface);
+ x < width;
+ x++, src += 4, dest += 4)
+ {
+ GimpRGB color;
+ guchar r, g, b, a;
+
+ gimp_rgba_set (&color, src[0], src[1], src[2], src[3]);
+ gimp_rgba_get_uchar (&color, &r, &g, &b, &a);
+
+ GIMP_CAIRO_ARGB32_SET_PIXEL(dest, r, g, b, a);
+ }
+
+ cairo_surface_mark_dirty (surface);
+
+ pattern = cairo_pattern_create_for_surface (surface);
+ cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT);
+ cairo_surface_destroy (surface);
+
+ cairo_scale (cr, (gdouble) allocation.width / (gdouble) width, 1.0);
+
+ cairo_set_source (cr, pattern);
+ cairo_pattern_destroy (pattern);
+
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+
+static void
+gimp_gradient_select_drag_data_received (GimpGradientSelectButton *button,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ gint length = gtk_selection_data_get_length (selection);
+ gchar *str;
+
+ if (gtk_selection_data_get_format (selection) != 8 || length < 1)
+ {
+ g_warning ("%s: received invalid gradient data", G_STRFUNC);
+ return;
+ }
+
+ str = g_strndup ((const gchar *) gtk_selection_data_get_data (selection),
+ length);
+
+ if (g_utf8_validate (str, -1, NULL))
+ {
+ gint pid;
+ gpointer unused;
+ gint name_offset = 0;
+
+ if (sscanf (str, "%i:%p:%n", &pid, &unused, &name_offset) >= 2 &&
+ pid == gimp_getpid () && name_offset > 0)
+ {
+ gchar *name = str + name_offset;
+
+ gimp_gradient_select_button_set_gradient (button, name);
+ }
+ }
+
+ g_free (str);
+}
+
+static GtkWidget *
+gimp_gradient_select_button_create_inside (GimpGradientSelectButton *gradient_button)
+{
+ GtkWidget *button;
+ GimpGradientSelectButtonPrivate *priv;
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (gradient_button);
+
+ gtk_widget_push_composite_child ();
+
+ button = gtk_button_new ();
+
+ priv->preview = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (priv->preview, CELL_WIDTH, CELL_HEIGHT);
+ gtk_container_add (GTK_CONTAINER (button), priv->preview);
+
+ g_signal_connect (priv->preview, "size-allocate",
+ G_CALLBACK (gimp_gradient_select_preview_size_allocate),
+ gradient_button);
+
+ g_signal_connect (priv->preview, "expose-event",
+ G_CALLBACK (gimp_gradient_select_preview_expose),
+ gradient_button);
+
+ gtk_widget_show_all (button);
+
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gimp_gradient_select_button_clicked),
+ gradient_button);
+
+ gtk_drag_dest_set (GTK_WIDGET (button),
+ GTK_DEST_DEFAULT_HIGHLIGHT |
+ GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_DROP,
+ &target, 1,
+ GDK_ACTION_COPY);
+
+ g_signal_connect_swapped (button, "drag-data-received",
+ G_CALLBACK (gimp_gradient_select_drag_data_received),
+ gradient_button);
+
+ gtk_widget_pop_composite_child ();
+
+ return button;
+}