summaryrefslogtreecommitdiffstats
path: root/app/widgets/gimpsamplepointeditor.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--app/widgets/gimpsamplepointeditor.c623
1 files changed, 623 insertions, 0 deletions
diff --git a/app/widgets/gimpsamplepointeditor.c b/app/widgets/gimpsamplepointeditor.c
new file mode 100644
index 0000000..48d7f33
--- /dev/null
+++ b/app/widgets/gimpsamplepointeditor.c
@@ -0,0 +1,623 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpsamplepointeditor.c
+ * Copyright (C) 2005-2016 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 <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "widgets-types.h"
+
+#include "config/gimpcoreconfig.h"
+
+#include "core/gimp.h"
+#include "core/gimpimage.h"
+#include "core/gimpimage-pick-color.h"
+#include "core/gimpimage-sample-points.h"
+#include "core/gimpsamplepoint.h"
+
+#include "gimpcolorframe.h"
+#include "gimpmenufactory.h"
+#include "gimpsamplepointeditor.h"
+#include "gimpwidgets-utils.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_SAMPLE_MERGED
+};
+
+
+static void gimp_sample_point_editor_constructed (GObject *object);
+static void gimp_sample_point_editor_dispose (GObject *object);
+static void gimp_sample_point_editor_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_sample_point_editor_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_sample_point_editor_style_set (GtkWidget *widget,
+ GtkStyle *prev_style);
+static void gimp_sample_point_editor_set_image (GimpImageEditor *editor,
+ GimpImage *image);
+
+static void gimp_sample_point_editor_point_added (GimpImage *image,
+ GimpSamplePoint *sample_point,
+ GimpSamplePointEditor *editor);
+static void gimp_sample_point_editor_point_removed (GimpImage *image,
+ GimpSamplePoint *sample_point,
+ GimpSamplePointEditor *editor);
+static void gimp_sample_point_editor_point_moved (GimpImage *image,
+ GimpSamplePoint *sample_point,
+ GimpSamplePointEditor *editor);
+static void gimp_sample_point_editor_proj_update (GimpImage *image,
+ gboolean now,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ GimpSamplePointEditor *editor);
+static void gimp_sample_point_editor_points_changed (GimpSamplePointEditor *editor);
+static void gimp_sample_point_editor_dirty (GimpSamplePointEditor *editor,
+ gint index);
+static gboolean gimp_sample_point_editor_update (GimpSamplePointEditor *editor);
+static void gimp_sample_point_editor_mode_notify (GimpColorFrame *frame,
+ const GParamSpec *pspec,
+ GimpSamplePointEditor *editor);
+
+
+G_DEFINE_TYPE (GimpSamplePointEditor, gimp_sample_point_editor,
+ GIMP_TYPE_IMAGE_EDITOR)
+
+#define parent_class gimp_sample_point_editor_parent_class
+
+
+static void
+gimp_sample_point_editor_class_init (GimpSamplePointEditorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GimpImageEditorClass *image_editor_class = GIMP_IMAGE_EDITOR_CLASS (klass);
+
+ object_class->constructed = gimp_sample_point_editor_constructed;
+ object_class->dispose = gimp_sample_point_editor_dispose;
+ object_class->get_property = gimp_sample_point_editor_get_property;
+ object_class->set_property = gimp_sample_point_editor_set_property;
+
+ widget_class->style_set = gimp_sample_point_editor_style_set;
+
+ image_editor_class->set_image = gimp_sample_point_editor_set_image;
+
+ g_object_class_install_property (object_class, PROP_SAMPLE_MERGED,
+ g_param_spec_boolean ("sample-merged",
+ NULL, NULL,
+ TRUE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_sample_point_editor_init (GimpSamplePointEditor *editor)
+{
+ GtkWidget *scrolled_window;
+ GtkWidget *viewport;
+ GtkWidget *vbox;
+ gint content_spacing;
+
+ editor->sample_merged = TRUE;
+
+ gtk_widget_style_get (GTK_WIDGET (editor),
+ "content-spacing", &content_spacing,
+ NULL);
+
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_SHADOW_NONE);
+ gtk_box_pack_start (GTK_BOX (editor), scrolled_window, TRUE, TRUE, 0);
+ gtk_widget_show (scrolled_window);
+
+ viewport = gtk_viewport_new (NULL, NULL);
+ gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
+ gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
+ gtk_widget_show (viewport);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (viewport), vbox);
+ gtk_widget_show (vbox);
+
+ editor->empty_icon = gtk_image_new_from_icon_name (GIMP_ICON_SAMPLE_POINT,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_box_pack_start (GTK_BOX (vbox), editor->empty_icon, TRUE, TRUE, 0);
+ gtk_widget_show (editor->empty_icon);
+
+ editor->empty_label = gtk_label_new (_("This image\nhas no\nsample points"));
+ gtk_label_set_justify (GTK_LABEL (editor->empty_label), GTK_JUSTIFY_CENTER);
+ gimp_label_set_attributes (GTK_LABEL (editor->empty_label),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_box_pack_start (GTK_BOX (vbox), editor->empty_label, TRUE, TRUE, 0);
+
+ editor->table = gtk_table_new (1, 2, TRUE);
+ gtk_table_set_row_spacings (GTK_TABLE (editor->table), content_spacing);
+ gtk_table_set_col_spacings (GTK_TABLE (editor->table), content_spacing);
+ gtk_box_pack_start (GTK_BOX (vbox), editor->table, FALSE, FALSE, 0);
+ gtk_widget_show (editor->table);
+}
+
+static void
+gimp_sample_point_editor_constructed (GObject *object)
+{
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+}
+
+static void
+gimp_sample_point_editor_dispose (GObject *object)
+{
+ GimpSamplePointEditor *editor = GIMP_SAMPLE_POINT_EDITOR (object);
+
+ g_clear_pointer (&editor->color_frames, g_free);
+
+ if (editor->dirty_idle_id)
+ {
+ g_source_remove (editor->dirty_idle_id);
+ editor->dirty_idle_id = 0;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_sample_point_editor_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpSamplePointEditor *editor = GIMP_SAMPLE_POINT_EDITOR (object);
+
+ switch (property_id)
+ {
+ case PROP_SAMPLE_MERGED:
+ gimp_sample_point_editor_set_sample_merged (editor,
+ g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_sample_point_editor_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpSamplePointEditor *editor = GIMP_SAMPLE_POINT_EDITOR (object);
+
+ switch (property_id)
+ {
+ case PROP_SAMPLE_MERGED:
+ g_value_set_boolean (value, editor->sample_merged);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_sample_point_editor_style_set (GtkWidget *widget,
+ GtkStyle *prev_style)
+{
+ GimpSamplePointEditor *editor = GIMP_SAMPLE_POINT_EDITOR (widget);
+ gint content_spacing;
+
+ GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
+
+ gtk_widget_style_get (widget,
+ "content-spacing", &content_spacing,
+ NULL);
+
+ gtk_table_set_row_spacings (GTK_TABLE (editor->table), content_spacing);
+ gtk_table_set_col_spacings (GTK_TABLE (editor->table), content_spacing);
+}
+
+static void
+gimp_sample_point_editor_set_image (GimpImageEditor *image_editor,
+ GimpImage *image)
+{
+ GimpSamplePointEditor *editor = GIMP_SAMPLE_POINT_EDITOR (image_editor);
+
+ if (image_editor->image)
+ {
+ g_signal_handlers_disconnect_by_func (image_editor->image,
+ gimp_sample_point_editor_point_added,
+ editor);
+ g_signal_handlers_disconnect_by_func (image_editor->image,
+ gimp_sample_point_editor_point_removed,
+ editor);
+ g_signal_handlers_disconnect_by_func (image_editor->image,
+ gimp_sample_point_editor_point_moved,
+ editor);
+
+ g_signal_handlers_disconnect_by_func (gimp_image_get_projection (image_editor->image),
+ gimp_sample_point_editor_proj_update,
+ editor);
+ }
+
+ GIMP_IMAGE_EDITOR_CLASS (parent_class)->set_image (image_editor, image);
+
+ if (image)
+ {
+ g_signal_connect (image, "sample-point-added",
+ G_CALLBACK (gimp_sample_point_editor_point_added),
+ editor);
+ g_signal_connect (image, "sample-point-removed",
+ G_CALLBACK (gimp_sample_point_editor_point_removed),
+ editor);
+ g_signal_connect (image, "sample-point-moved",
+ G_CALLBACK (gimp_sample_point_editor_point_moved),
+ editor);
+
+ g_signal_connect (gimp_image_get_projection (image), "update",
+ G_CALLBACK (gimp_sample_point_editor_proj_update),
+ editor);
+ }
+
+ gtk_widget_set_visible (editor->empty_icon,
+ image_editor->image == NULL);
+
+ gimp_sample_point_editor_points_changed (editor);
+}
+
+
+/* public functions */
+
+GtkWidget *
+gimp_sample_point_editor_new (GimpMenuFactory *menu_factory)
+{
+ g_return_val_if_fail (GIMP_IS_MENU_FACTORY (menu_factory), NULL);
+
+ return g_object_new (GIMP_TYPE_SAMPLE_POINT_EDITOR,
+ "menu-factory", menu_factory,
+ "menu-identifier", "<SamplePoints>",
+ "ui-path", "/sample-points-popup",
+ NULL);
+}
+
+void
+gimp_sample_point_editor_set_sample_merged (GimpSamplePointEditor *editor,
+ gboolean sample_merged)
+{
+ g_return_if_fail (GIMP_IS_SAMPLE_POINT_EDITOR (editor));
+
+ sample_merged = sample_merged ? TRUE : FALSE;
+
+ if (editor->sample_merged != sample_merged)
+ {
+ editor->sample_merged = sample_merged;
+
+ gimp_sample_point_editor_dirty (editor, -1);
+
+ g_object_notify (G_OBJECT (editor), "sample-merged");
+ }
+}
+
+gboolean
+gimp_sample_point_editor_get_sample_merged (GimpSamplePointEditor *editor)
+{
+ g_return_val_if_fail (GIMP_IS_SAMPLE_POINT_EDITOR (editor), FALSE);
+
+ return editor->sample_merged;
+}
+
+/* private functions */
+
+static void
+gimp_sample_point_editor_point_added (GimpImage *image,
+ GimpSamplePoint *sample_point,
+ GimpSamplePointEditor *editor)
+{
+ gimp_sample_point_editor_points_changed (editor);
+}
+
+static void
+gimp_sample_point_editor_point_removed (GimpImage *image,
+ GimpSamplePoint *sample_point,
+ GimpSamplePointEditor *editor)
+{
+ gimp_sample_point_editor_points_changed (editor);
+}
+
+static void
+gimp_sample_point_editor_point_moved (GimpImage *image,
+ GimpSamplePoint *sample_point,
+ GimpSamplePointEditor *editor)
+{
+ gint i = g_list_index (gimp_image_get_sample_points (image), sample_point);
+
+ gimp_sample_point_editor_dirty (editor, i);
+}
+
+static void
+gimp_sample_point_editor_proj_update (GimpImage *image,
+ gboolean now,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ GimpSamplePointEditor *editor)
+{
+ GimpImageEditor *image_editor = GIMP_IMAGE_EDITOR (editor);
+ GList *sample_points;
+ gint n_points = 0;
+ GList *list;
+ gint i;
+
+ sample_points = gimp_image_get_sample_points (image_editor->image);
+
+ n_points = MIN (editor->n_color_frames, g_list_length (sample_points));
+
+ for (i = 0, list = sample_points;
+ i < n_points;
+ i++, list = g_list_next (list))
+ {
+ GimpSamplePoint *sample_point = list->data;
+ gint sp_x;
+ gint sp_y;
+
+ gimp_sample_point_get_position (sample_point, &sp_x, &sp_y);
+
+ if (sp_x >= x && sp_x < (x + width) &&
+ sp_y >= y && sp_y < (y + height))
+ {
+ gimp_sample_point_editor_dirty (editor, i);
+ }
+ }
+}
+
+static void
+gimp_sample_point_editor_points_changed (GimpSamplePointEditor *editor)
+{
+ GimpImageEditor *image_editor = GIMP_IMAGE_EDITOR (editor);
+ GList *sample_points;
+ gint n_points = 0;
+ gint i;
+
+ if (image_editor->image)
+ {
+ sample_points = gimp_image_get_sample_points (image_editor->image);
+ n_points = g_list_length (sample_points);
+ }
+
+ gtk_widget_set_visible (editor->empty_label,
+ image_editor->image && n_points == 0);
+
+ /* Keep that many color frames around so they remember their color
+ * model. Let's hope nobody uses more and notices they get reset to
+ * "pixel". See https://gitlab.gnome.org/GNOME/gimp/issues/1805
+ */
+#define RANDOM_MAGIC 16
+
+ if (n_points < editor->n_color_frames &&
+ n_points < RANDOM_MAGIC &&
+ editor->n_color_frames > RANDOM_MAGIC)
+ {
+ for (i = RANDOM_MAGIC; i < editor->n_color_frames; i++)
+ {
+ gtk_widget_destroy (editor->color_frames[i]);
+ }
+
+ editor->color_frames = g_renew (GtkWidget *, editor->color_frames,
+ RANDOM_MAGIC);
+
+ editor->n_color_frames = RANDOM_MAGIC;
+ }
+ else if (n_points > editor->n_color_frames)
+ {
+ GimpColorConfig *config;
+
+ config = image_editor->image->gimp->config->color_management;
+
+ editor->color_frames = g_renew (GtkWidget *, editor->color_frames,
+ n_points);
+
+ for (i = editor->n_color_frames; i < n_points; i++)
+ {
+ gint row = i / 2;
+ gint column = i % 2;
+
+ editor->color_frames[i] =
+ g_object_new (GIMP_TYPE_COLOR_FRAME,
+ "mode", GIMP_COLOR_PICK_MODE_PIXEL,
+ "has-number", TRUE,
+ "number", i + 1,
+ "has-color-area", TRUE,
+ "has-coords", TRUE,
+ NULL);
+
+ gimp_color_frame_set_color_config (GIMP_COLOR_FRAME (editor->color_frames[i]),
+ config);
+
+ gtk_table_attach (GTK_TABLE (editor->table), editor->color_frames[i],
+ column, column + 1, row, row + 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+
+ g_signal_connect_object (editor->color_frames[i], "notify::mode",
+ G_CALLBACK (gimp_sample_point_editor_mode_notify),
+ editor, 0);
+
+ g_object_set_data (G_OBJECT (editor->color_frames[i]),
+ "dirty", GINT_TO_POINTER (TRUE));
+ }
+
+ editor->n_color_frames = n_points;
+ }
+
+ for (i = 0; i < editor->n_color_frames; i++)
+ {
+ gtk_widget_set_visible (editor->color_frames[i], i < n_points);
+ }
+
+ if (n_points > 0)
+ gimp_sample_point_editor_dirty (editor, -1);
+}
+
+static void
+gimp_sample_point_editor_dirty (GimpSamplePointEditor *editor,
+ gint index)
+{
+ if (index >= 0)
+ {
+ g_object_set_data (G_OBJECT (editor->color_frames[index]),
+ "dirty", GINT_TO_POINTER (TRUE));
+ }
+ else
+ {
+ gint i;
+
+ for (i = 0; i < editor->n_color_frames; i++)
+ g_object_set_data (G_OBJECT (editor->color_frames[i]),
+ "dirty", GINT_TO_POINTER (TRUE));
+ }
+
+ if (editor->dirty_idle_id)
+ g_source_remove (editor->dirty_idle_id);
+
+ editor->dirty_idle_id =
+ g_idle_add ((GSourceFunc) gimp_sample_point_editor_update,
+ editor);
+}
+
+static gboolean
+gimp_sample_point_editor_update (GimpSamplePointEditor *editor)
+{
+ GimpImageEditor *image_editor = GIMP_IMAGE_EDITOR (editor);
+ GList *sample_points;
+ gint n_points;
+ GList *list;
+ gint i;
+
+ editor->dirty_idle_id = 0;
+
+ if (! image_editor->image)
+ return FALSE;
+
+ sample_points = gimp_image_get_sample_points (image_editor->image);
+
+ n_points = MIN (editor->n_color_frames, g_list_length (sample_points));
+
+ for (i = 0, list = sample_points;
+ i < n_points;
+ i++, list = g_list_next (list))
+ {
+ GimpColorFrame *color_frame = GIMP_COLOR_FRAME (editor->color_frames[i]);
+
+ if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (color_frame),
+ "dirty")))
+ {
+ GimpSamplePoint *sample_point = list->data;
+ const Babl *format;
+ gdouble pixel[4];
+ GimpRGB color;
+ GimpColorPickMode pick_mode;
+ gint x;
+ gint y;
+
+ g_object_set_data (G_OBJECT (color_frame),
+ "dirty", GINT_TO_POINTER (FALSE));
+
+ gimp_sample_point_get_position (sample_point, &x, &y);
+
+ if (gimp_image_pick_color (image_editor->image, NULL,
+ x, y,
+ FALSE,
+ editor->sample_merged,
+ FALSE, 0.0,
+ &format,
+ pixel,
+ &color))
+ {
+ gimp_color_frame_set_color (color_frame, FALSE,
+ format, pixel, &color,
+ x, y);
+ }
+ else
+ {
+ gimp_color_frame_set_invalid (color_frame);
+ }
+
+ pick_mode = gimp_sample_point_get_pick_mode (sample_point);
+
+ gimp_color_frame_set_mode (color_frame, pick_mode);
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_sample_point_editor_mode_notify (GimpColorFrame *frame,
+ const GParamSpec *pspec,
+ GimpSamplePointEditor *editor)
+{
+ GimpImageEditor *image_editor = GIMP_IMAGE_EDITOR (editor);
+ GList *sample_points;
+ gint n_points;
+ GList *list;
+ gint i;
+
+ sample_points = gimp_image_get_sample_points (image_editor->image);
+
+ n_points = MIN (editor->n_color_frames, g_list_length (sample_points));
+
+ for (i = 0, list = sample_points;
+ i < n_points;
+ i++, list = g_list_next (list))
+ {
+ if (GIMP_COLOR_FRAME (editor->color_frames[i]) == frame)
+ {
+ GimpSamplePoint *sample_point = list->data;
+ GimpColorPickMode pick_mode;
+
+ g_object_get (frame, "mode", &pick_mode, NULL);
+
+ if (pick_mode != gimp_sample_point_get_pick_mode (sample_point))
+ gimp_image_set_sample_point_pick_mode (image_editor->image,
+ sample_point,
+ pick_mode,
+ TRUE);
+ break;
+ }
+ }
+}