924 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			924 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | /* PIKA - Photo and Image Kooker Application
 | ||
|  |  * a rebranding of The GNU Image Manipulation Program (created with heckimp) | ||
|  |  * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio | ||
|  |  * | ||
|  |  * Original copyright, applying to most contents (license remains unchanged):  | ||
|  |  * Copyright (C) 1995 Spencer Kimball and Peter Mattis | ||
|  |  * | ||
|  |  * pikacursorview.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 <string.h>
 | ||
|  | 
 | ||
|  | #include <gegl.h>
 | ||
|  | #include <gtk/gtk.h>
 | ||
|  | 
 | ||
|  | #include "libpikabase/pikabase.h"
 | ||
|  | #include "libpikamath/pikamath.h"
 | ||
|  | #include "libpikacolor/pikacolor.h"
 | ||
|  | #include "libpikawidgets/pikawidgets.h"
 | ||
|  | 
 | ||
|  | #include "display-types.h"
 | ||
|  | 
 | ||
|  | #include "config/pikacoreconfig.h"
 | ||
|  | 
 | ||
|  | #include "core/pika.h"
 | ||
|  | #include "core/pikacontext.h"
 | ||
|  | #include "core/pikaimage.h"
 | ||
|  | #include "core/pikaimage-pick-color.h"
 | ||
|  | #include "core/pikaitem.h"
 | ||
|  | 
 | ||
|  | #include "widgets/pikacolorframe.h"
 | ||
|  | #include "widgets/pikadocked.h"
 | ||
|  | #include "widgets/pikamenufactory.h"
 | ||
|  | #include "widgets/pikasessioninfo-aux.h"
 | ||
|  | 
 | ||
|  | #include "pikacursorview.h"
 | ||
|  | #include "pikadisplay.h"
 | ||
|  | #include "pikadisplayshell.h"
 | ||
|  | 
 | ||
|  | #include "pika-intl.h"
 | ||
|  | 
 | ||
|  | 
 | ||
|  | enum | ||
|  | { | ||
|  |   PROP_0, | ||
|  |   PROP_PIKA, | ||
|  |   PROP_SAMPLE_MERGED | ||
|  | }; | ||
|  | 
 | ||
|  | 
 | ||
|  | struct _PikaCursorViewPrivate | ||
|  | { | ||
|  |   PikaEditor        parent_instance; | ||
|  | 
 | ||
|  |   Pika             *pika; | ||
|  | 
 | ||
|  |   GtkWidget        *coord_hbox; | ||
|  |   GtkWidget        *selection_hbox; | ||
|  |   GtkWidget        *color_hbox; | ||
|  | 
 | ||
|  |   GtkWidget        *pixel_x_label; | ||
|  |   GtkWidget        *pixel_y_label; | ||
|  |   GtkWidget        *unit_x_label; | ||
|  |   GtkWidget        *unit_y_label; | ||
|  |   GtkWidget        *selection_x_label; | ||
|  |   GtkWidget        *selection_y_label; | ||
|  |   GtkWidget        *selection_width_label; | ||
|  |   GtkWidget        *selection_height_label; | ||
|  |   GtkWidget        *color_frame_1; | ||
|  |   GtkWidget        *color_frame_2; | ||
|  | 
 | ||
|  |   gboolean          sample_merged; | ||
|  | 
 | ||
|  |   PikaContext      *context; | ||
|  |   PikaDisplayShell *shell; | ||
|  |   PikaImage        *image; | ||
|  | 
 | ||
|  |   PikaUnit          unit; | ||
|  |   guint             cursor_idle_id; | ||
|  |   PikaImage        *cursor_image; | ||
|  |   PikaUnit          cursor_unit; | ||
|  |   gdouble           cursor_x; | ||
|  |   gdouble           cursor_y; | ||
|  | }; | ||
|  | 
 | ||
|  | 
 | ||
|  | static void       pika_cursor_view_docked_iface_init     (PikaDockedInterface *iface); | ||
|  | 
 | ||
|  | static void       pika_cursor_view_dispose               (GObject             *object); | ||
|  | static void       pika_cursor_view_constructed           (GObject             *object); | ||
|  | static void       pika_cursor_view_set_property          (GObject             *object, | ||
|  |                                                           guint                property_id, | ||
|  |                                                           const GValue        *value, | ||
|  |                                                           GParamSpec          *pspec); | ||
|  | static void       pika_cursor_view_get_property          (GObject             *object, | ||
|  |                                                           guint                property_id, | ||
|  |                                                           GValue              *value, | ||
|  |                                                           GParamSpec          *pspec); | ||
|  | 
 | ||
|  | static void       pika_cursor_view_style_updated         (GtkWidget           *widget); | ||
|  | 
 | ||
|  | static void       pika_cursor_view_set_aux_info          (PikaDocked          *docked, | ||
|  |                                                           GList               *aux_info); | ||
|  | static GList    * pika_cursor_view_get_aux_info          (PikaDocked          *docked); | ||
|  | 
 | ||
|  | static void       pika_cursor_view_set_context           (PikaDocked          *docked, | ||
|  |                                                           PikaContext         *context); | ||
|  | static void       pika_cursor_view_image_changed         (PikaCursorView      *view, | ||
|  |                                                           PikaImage           *image, | ||
|  |                                                           PikaContext         *context); | ||
|  | static void       pika_cursor_view_mask_changed          (PikaCursorView      *view, | ||
|  |                                                           PikaImage           *image); | ||
|  | static void       pika_cursor_view_diplay_changed        (PikaCursorView      *view, | ||
|  |                                                           PikaDisplay         *display, | ||
|  |                                                           PikaContext         *context); | ||
|  | static void       pika_cursor_view_shell_unit_changed    (PikaCursorView      *view, | ||
|  |                                                           GParamSpec          *pspec, | ||
|  |                                                           PikaDisplayShell    *shell); | ||
|  | static void       pika_cursor_view_format_as_unit        (PikaUnit             unit, | ||
|  |                                                           gchar               *output_buf, | ||
|  |                                                           gint                 output_buf_size, | ||
|  |                                                           gdouble              pixel_value, | ||
|  |                                                           gdouble              image_res); | ||
|  | static void       pika_cursor_view_set_label_italic      (GtkWidget           *label, | ||
|  |                                                           gboolean             italic); | ||
|  | static void       pika_cursor_view_update_selection_info (PikaCursorView      *view, | ||
|  |                                                           PikaImage           *image, | ||
|  |                                                           PikaUnit             unit); | ||
|  | static gboolean   pika_cursor_view_cursor_idle           (PikaCursorView      *view); | ||
|  | 
 | ||
|  | 
 | ||
|  | G_DEFINE_TYPE_WITH_CODE (PikaCursorView, pika_cursor_view, PIKA_TYPE_EDITOR, | ||
|  |                          G_ADD_PRIVATE (PikaCursorView) | ||
|  |                          G_IMPLEMENT_INTERFACE (PIKA_TYPE_DOCKED, | ||
|  |                                                 pika_cursor_view_docked_iface_init)) | ||
|  | 
 | ||
|  | #define parent_class pika_cursor_view_parent_class
 | ||
|  | 
 | ||
|  | static PikaDockedInterface *parent_docked_iface = NULL; | ||
|  | 
 | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_class_init (PikaCursorViewClass* klass) | ||
|  | { | ||
|  |   GObjectClass   *object_class = G_OBJECT_CLASS (klass); | ||
|  |   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); | ||
|  | 
 | ||
|  |   object_class->constructed   = pika_cursor_view_constructed; | ||
|  |   object_class->dispose       = pika_cursor_view_dispose; | ||
|  |   object_class->get_property  = pika_cursor_view_get_property; | ||
|  |   object_class->set_property  = pika_cursor_view_set_property; | ||
|  | 
 | ||
|  |   widget_class->style_updated = pika_cursor_view_style_updated; | ||
|  | 
 | ||
|  |   g_object_class_install_property (object_class, PROP_PIKA, | ||
|  |                                    g_param_spec_object ("pika", | ||
|  |                                                         NULL, NULL, | ||
|  |                                                         PIKA_TYPE_PIKA, | ||
|  |                                                         PIKA_PARAM_READWRITE | | ||
|  |                                                         G_PARAM_CONSTRUCT_ONLY)); | ||
|  | 
 | ||
|  |   g_object_class_install_property (object_class, PROP_SAMPLE_MERGED, | ||
|  |                                    g_param_spec_boolean ("sample-merged", | ||
|  |                                                          NULL, NULL, | ||
|  |                                                          TRUE, | ||
|  |                                                          PIKA_PARAM_READWRITE | | ||
|  |                                                          G_PARAM_CONSTRUCT)); | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_init (PikaCursorView *view) | ||
|  | { | ||
|  |   GtkWidget *frame; | ||
|  |   GtkWidget *grid; | ||
|  |   GtkWidget *toggle; | ||
|  |   gint       content_spacing; | ||
|  | 
 | ||
|  |   view->priv = pika_cursor_view_get_instance_private (view); | ||
|  | 
 | ||
|  |   view->priv->sample_merged  = TRUE; | ||
|  |   view->priv->context        = NULL; | ||
|  |   view->priv->shell          = NULL; | ||
|  |   view->priv->image          = NULL; | ||
|  |   view->priv->unit           = PIKA_UNIT_PIXEL; | ||
|  |   view->priv->cursor_idle_id = 0; | ||
|  | 
 | ||
|  |   gtk_widget_style_get (GTK_WIDGET (view), | ||
|  |                         "content-spacing", &content_spacing, | ||
|  |                         NULL); | ||
|  | 
 | ||
|  | 
 | ||
|  |   /* cursor information */ | ||
|  | 
 | ||
|  |   view->priv->coord_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, | ||
|  |                                         content_spacing); | ||
|  |   gtk_box_set_homogeneous (GTK_BOX (view->priv->coord_hbox), TRUE); | ||
|  |   gtk_box_pack_start (GTK_BOX (view), view->priv->coord_hbox, | ||
|  |                       FALSE, FALSE, 0); | ||
|  |   gtk_widget_show (view->priv->coord_hbox); | ||
|  | 
 | ||
|  |   view->priv->selection_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, | ||
|  |                                             content_spacing); | ||
|  |   gtk_box_set_homogeneous (GTK_BOX (view->priv->selection_hbox), TRUE); | ||
|  |   gtk_box_pack_start (GTK_BOX (view), view->priv->selection_hbox, | ||
|  |                       FALSE, FALSE, 0); | ||
|  |   gtk_widget_show (view->priv->selection_hbox); | ||
|  | 
 | ||
|  | 
 | ||
|  |   /* Pixels */ | ||
|  | 
 | ||
|  |   frame = pika_frame_new (_("Pixels")); | ||
|  |   gtk_box_pack_start (GTK_BOX (view->priv->coord_hbox), frame, TRUE, TRUE, 0); | ||
|  |   gtk_widget_show (frame); | ||
|  | 
 | ||
|  |   grid = gtk_grid_new (); | ||
|  |   gtk_grid_set_column_spacing (GTK_GRID (grid), 6); | ||
|  |   gtk_grid_set_row_spacing (GTK_GRID (grid), 2); | ||
|  |   gtk_container_add (GTK_CONTAINER (frame), grid); | ||
|  |   gtk_widget_show (grid); | ||
|  | 
 | ||
|  |   view->priv->pixel_x_label = gtk_label_new (_("n/a")); | ||
|  |   gtk_label_set_xalign (GTK_LABEL (view->priv->pixel_x_label), 1.0); | ||
|  |   pika_grid_attach_aligned (GTK_GRID (grid), 0, 0, | ||
|  |                             _("X"), 0.5, 0.5, | ||
|  |                             view->priv->pixel_x_label, 1); | ||
|  | 
 | ||
|  |   view->priv->pixel_y_label = gtk_label_new (_("n/a")); | ||
|  |   gtk_label_set_xalign (GTK_LABEL (view->priv->pixel_y_label), 1.0); | ||
|  |   pika_grid_attach_aligned (GTK_GRID (grid), 0, 1, | ||
|  |                             _("Y"), 0.5, 0.5, | ||
|  |                             view->priv->pixel_y_label, 1); | ||
|  | 
 | ||
|  | 
 | ||
|  |   /* Units */ | ||
|  | 
 | ||
|  |   frame = pika_frame_new (_("Units")); | ||
|  |   gtk_box_pack_start (GTK_BOX (view->priv->coord_hbox), frame, TRUE, TRUE, 0); | ||
|  |   gtk_widget_show (frame); | ||
|  | 
 | ||
|  |   grid = gtk_grid_new (); | ||
|  |   gtk_grid_set_column_spacing (GTK_GRID (grid), 4); | ||
|  |   gtk_grid_set_row_spacing (GTK_GRID (grid), 2); | ||
|  |   gtk_container_add (GTK_CONTAINER (frame), grid); | ||
|  |   gtk_widget_show (grid); | ||
|  | 
 | ||
|  |   view->priv->unit_x_label = gtk_label_new (_("n/a")); | ||
|  |   gtk_label_set_xalign (GTK_LABEL (view->priv->unit_x_label), 1.0); | ||
|  |   pika_grid_attach_aligned (GTK_GRID (grid), 0, 0, | ||
|  |                             _("X"), 0.5, 0.5, | ||
|  |                             view->priv->unit_x_label, 1); | ||
|  | 
 | ||
|  |   view->priv->unit_y_label = gtk_label_new (_("n/a")); | ||
|  |   gtk_label_set_xalign (GTK_LABEL (view->priv->unit_y_label), 1.0); | ||
|  |   pika_grid_attach_aligned (GTK_GRID (grid), 0, 1, | ||
|  |                             _("Y"), 0.5, 0.5, | ||
|  |                             view->priv->unit_y_label, 1); | ||
|  | 
 | ||
|  | 
 | ||
|  |   /* Selection Bounding Box */ | ||
|  | 
 | ||
|  |   frame = pika_frame_new (_("Selection")); | ||
|  |   gtk_box_pack_start (GTK_BOX (view->priv->selection_hbox), frame, TRUE, TRUE, 0); | ||
|  |   gtk_widget_show (frame); | ||
|  | 
 | ||
|  |   pika_help_set_help_data (frame, _("The selection's bounding box"), NULL); | ||
|  | 
 | ||
|  |   grid = gtk_grid_new (); | ||
|  |   gtk_grid_set_column_spacing (GTK_GRID (grid), 6); | ||
|  |   gtk_grid_set_row_spacing (GTK_GRID (grid), 2); | ||
|  |   gtk_container_add (GTK_CONTAINER (frame), grid); | ||
|  |   gtk_widget_show (grid); | ||
|  | 
 | ||
|  |   view->priv->selection_x_label = gtk_label_new (_("n/a")); | ||
|  |   gtk_label_set_xalign (GTK_LABEL (view->priv->selection_x_label), 1.0); | ||
|  |   pika_grid_attach_aligned (GTK_GRID (grid), 0, 0, | ||
|  |                             _("X"), 0.5, 0.5, | ||
|  |                             view->priv->selection_x_label, 1); | ||
|  | 
 | ||
|  |   view->priv->selection_y_label = gtk_label_new (_("n/a")); | ||
|  |   gtk_label_set_xalign (GTK_LABEL (view->priv->selection_y_label), 1.0); | ||
|  |   pika_grid_attach_aligned (GTK_GRID (grid), 0, 1, | ||
|  |                             _("Y"), 0.5, 0.5, | ||
|  |                             view->priv->selection_y_label, 1); | ||
|  | 
 | ||
|  |   frame = pika_frame_new (""); | ||
|  |   gtk_box_pack_start (GTK_BOX (view->priv->selection_hbox), frame, TRUE, TRUE, 0); | ||
|  |   gtk_widget_show (frame); | ||
|  | 
 | ||
|  |   grid = gtk_grid_new (); | ||
|  |   gtk_grid_set_column_spacing (GTK_GRID (grid), 4); | ||
|  |   gtk_grid_set_row_spacing (GTK_GRID (grid), 2); | ||
|  |   gtk_container_add (GTK_CONTAINER (frame), grid); | ||
|  |   gtk_widget_show (grid); | ||
|  | 
 | ||
|  |   view->priv->selection_width_label = gtk_label_new (_("n/a")); | ||
|  |   gtk_label_set_xalign (GTK_LABEL (view->priv->selection_width_label), 1.0); | ||
|  |   pika_grid_attach_aligned (GTK_GRID (grid), 0, 0, | ||
|  |                             /* Width */ | ||
|  |                             _("W"), 0.5, 0.5, | ||
|  |                             view->priv->selection_width_label, 1); | ||
|  | 
 | ||
|  |   view->priv->selection_height_label = gtk_label_new (_("n/a")); | ||
|  |   gtk_label_set_xalign (GTK_LABEL (view->priv->selection_height_label), 1.0); | ||
|  |   pika_grid_attach_aligned (GTK_GRID (grid), 0, 1, | ||
|  |                             /* Height */ | ||
|  |                             _("H"), 0.5, 0.5, | ||
|  |                             view->priv->selection_height_label, 1); | ||
|  | 
 | ||
|  |   /* sample merged toggle */ | ||
|  | 
 | ||
|  |   toggle = pika_prop_check_button_new (G_OBJECT (view), "sample-merged", | ||
|  |                                        _("_Sample Merged")); | ||
|  |   gtk_box_pack_start (GTK_BOX (view), toggle, FALSE, FALSE, 0); | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_docked_iface_init (PikaDockedInterface *iface) | ||
|  | { | ||
|  |   parent_docked_iface = g_type_interface_peek_parent (iface); | ||
|  | 
 | ||
|  |   if (! parent_docked_iface) | ||
|  |     parent_docked_iface = g_type_default_interface_peek (PIKA_TYPE_DOCKED); | ||
|  | 
 | ||
|  |   iface->set_aux_info = pika_cursor_view_set_aux_info; | ||
|  |   iface->get_aux_info = pika_cursor_view_get_aux_info; | ||
|  |   iface->set_context  = pika_cursor_view_set_context; | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_constructed (GObject             *object) | ||
|  | { | ||
|  |   PikaCursorView *view = PIKA_CURSOR_VIEW (object); | ||
|  |   gint            content_spacing; | ||
|  | 
 | ||
|  |   gtk_widget_style_get (GTK_WIDGET (view), | ||
|  |                         "content-spacing", &content_spacing, | ||
|  |                         NULL); | ||
|  | 
 | ||
|  |   /* color information */ | ||
|  |   view->priv->color_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, | ||
|  |                                         content_spacing); | ||
|  |   gtk_box_set_homogeneous (GTK_BOX (view->priv->color_hbox), TRUE); | ||
|  |   gtk_box_pack_start (GTK_BOX (view), view->priv->color_hbox, FALSE, FALSE, 0); | ||
|  |   gtk_widget_show (view->priv->color_hbox); | ||
|  | 
 | ||
|  |   view->priv->color_frame_1 = pika_color_frame_new (view->priv->pika); | ||
|  |   pika_color_frame_set_mode (PIKA_COLOR_FRAME (view->priv->color_frame_1), | ||
|  |                              PIKA_COLOR_PICK_MODE_PIXEL); | ||
|  |   pika_color_frame_set_ellipsize (PIKA_COLOR_FRAME (view->priv->color_frame_1), | ||
|  |                                   PANGO_ELLIPSIZE_END); | ||
|  |   gtk_box_pack_start (GTK_BOX (view->priv->color_hbox), view->priv->color_frame_1, | ||
|  |                       TRUE, TRUE, 0); | ||
|  |   gtk_widget_show (view->priv->color_frame_1); | ||
|  | 
 | ||
|  |   view->priv->color_frame_2 = pika_color_frame_new (view->priv->pika); | ||
|  |   pika_color_frame_set_mode (PIKA_COLOR_FRAME (view->priv->color_frame_2), | ||
|  |                              PIKA_COLOR_PICK_MODE_RGB_PERCENT); | ||
|  |   gtk_box_pack_start (GTK_BOX (view->priv->color_hbox), view->priv->color_frame_2, | ||
|  |                       TRUE, TRUE, 0); | ||
|  |   gtk_widget_show (view->priv->color_frame_2); | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_dispose (GObject *object) | ||
|  | { | ||
|  |   PikaCursorView *view = PIKA_CURSOR_VIEW (object); | ||
|  | 
 | ||
|  |   if (view->priv->context) | ||
|  |     pika_docked_set_context (PIKA_DOCKED (view), NULL); | ||
|  | 
 | ||
|  |   if (view->priv->cursor_idle_id) | ||
|  |     { | ||
|  |       g_source_remove (view->priv->cursor_idle_id); | ||
|  |       view->priv->cursor_idle_id = 0; | ||
|  |     } | ||
|  | 
 | ||
|  |   view->priv->color_frame_1 = NULL; | ||
|  |   view->priv->color_frame_2 = NULL; | ||
|  | 
 | ||
|  |   g_clear_object (&view->priv->cursor_image); | ||
|  | 
 | ||
|  |   G_OBJECT_CLASS (parent_class)->dispose (object); | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_set_property (GObject      *object, | ||
|  |                                guint         property_id, | ||
|  |                                const GValue *value, | ||
|  |                                GParamSpec   *pspec) | ||
|  | { | ||
|  |   PikaCursorView *view = PIKA_CURSOR_VIEW (object); | ||
|  | 
 | ||
|  |   switch (property_id) | ||
|  |     { | ||
|  |     case PROP_PIKA: | ||
|  |       view->priv->pika = g_value_get_object (value); | ||
|  |       break; | ||
|  | 
 | ||
|  |     case PROP_SAMPLE_MERGED: | ||
|  |       view->priv->sample_merged = g_value_get_boolean (value); | ||
|  |       break; | ||
|  | 
 | ||
|  |     default: | ||
|  |       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); | ||
|  |       break; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_get_property (GObject    *object, | ||
|  |                                guint       property_id, | ||
|  |                                GValue     *value, | ||
|  |                                GParamSpec *pspec) | ||
|  | { | ||
|  |   PikaCursorView *view = PIKA_CURSOR_VIEW (object); | ||
|  | 
 | ||
|  |   switch (property_id) | ||
|  |     { | ||
|  |     case PROP_PIKA: | ||
|  |       g_value_set_object (value, view->priv->pika); | ||
|  |       break; | ||
|  | 
 | ||
|  |     case PROP_SAMPLE_MERGED: | ||
|  |       g_value_set_boolean (value, view->priv->sample_merged); | ||
|  |       break; | ||
|  | 
 | ||
|  |     default: | ||
|  |       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); | ||
|  |       break; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | #define AUX_INFO_FRAME_1_MODE "frame-1-mode"
 | ||
|  | #define AUX_INFO_FRAME_2_MODE "frame-2-mode"
 | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_set_aux_info (PikaDocked *docked, | ||
|  |                                GList      *aux_info) | ||
|  | { | ||
|  |   PikaCursorView *view = PIKA_CURSOR_VIEW (docked); | ||
|  |   GList          *list; | ||
|  | 
 | ||
|  |   parent_docked_iface->set_aux_info (docked, aux_info); | ||
|  | 
 | ||
|  |   for (list = aux_info; list; list = g_list_next (list)) | ||
|  |     { | ||
|  |       PikaSessionInfoAux *aux   = list->data; | ||
|  |       GtkWidget          *frame = NULL; | ||
|  | 
 | ||
|  |       if (! strcmp (aux->name, AUX_INFO_FRAME_1_MODE)) | ||
|  |         frame = view->priv->color_frame_1; | ||
|  |       else if (! strcmp (aux->name, AUX_INFO_FRAME_2_MODE)) | ||
|  |         frame = view->priv->color_frame_2; | ||
|  | 
 | ||
|  |       if (frame) | ||
|  |         { | ||
|  |           GEnumClass *enum_class; | ||
|  |           GEnumValue *enum_value; | ||
|  | 
 | ||
|  |           enum_class = g_type_class_peek (PIKA_TYPE_COLOR_PICK_MODE); | ||
|  |           enum_value = g_enum_get_value_by_nick (enum_class, aux->value); | ||
|  | 
 | ||
|  |           if (enum_value) | ||
|  |             pika_color_frame_set_mode (PIKA_COLOR_FRAME (frame), | ||
|  |                                        enum_value->value); | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | static GList * | ||
|  | pika_cursor_view_get_aux_info (PikaDocked *docked) | ||
|  | { | ||
|  |   PikaCursorView     *view = PIKA_CURSOR_VIEW (docked); | ||
|  |   GList              *aux_info; | ||
|  |   const gchar        *nick; | ||
|  |   PikaSessionInfoAux *aux; | ||
|  | 
 | ||
|  |   aux_info = parent_docked_iface->get_aux_info (docked); | ||
|  | 
 | ||
|  |   if (pika_enum_get_value (PIKA_TYPE_COLOR_PICK_MODE, | ||
|  |                            PIKA_COLOR_FRAME (view->priv->color_frame_1)->pick_mode, | ||
|  |                            NULL, &nick, NULL, NULL)) | ||
|  |     { | ||
|  |       aux = pika_session_info_aux_new (AUX_INFO_FRAME_1_MODE, nick); | ||
|  |       aux_info = g_list_append (aux_info, aux); | ||
|  |     } | ||
|  | 
 | ||
|  |   if (pika_enum_get_value (PIKA_TYPE_COLOR_PICK_MODE, | ||
|  |                            PIKA_COLOR_FRAME (view->priv->color_frame_2)->pick_mode, | ||
|  |                            NULL, &nick, NULL, NULL)) | ||
|  |     { | ||
|  |       aux = pika_session_info_aux_new (AUX_INFO_FRAME_2_MODE, nick); | ||
|  |       aux_info = g_list_append (aux_info, aux); | ||
|  |     } | ||
|  | 
 | ||
|  |   return aux_info; | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_format_as_unit (PikaUnit  unit, | ||
|  |                                  gchar    *output_buf, | ||
|  |                                  gint      output_buf_size, | ||
|  |                                  gdouble   pixel_value, | ||
|  |                                  gdouble   image_res) | ||
|  | { | ||
|  |   gchar         format_buf[32]; | ||
|  |   gdouble       value; | ||
|  |   gint          unit_digits = 0; | ||
|  |   const gchar  *unit_str = ""; | ||
|  | 
 | ||
|  |   value = pika_pixels_to_units (pixel_value, unit, image_res); | ||
|  | 
 | ||
|  |   if (unit != PIKA_UNIT_PIXEL) | ||
|  |     { | ||
|  |       unit_digits = pika_unit_get_scaled_digits (unit, image_res); | ||
|  |       unit_str    = pika_unit_get_abbreviation (unit); | ||
|  |     } | ||
|  | 
 | ||
|  |   g_snprintf (format_buf, sizeof (format_buf), | ||
|  |               "%%.%df %s", unit_digits, unit_str); | ||
|  | 
 | ||
|  |   g_snprintf (output_buf, output_buf_size, format_buf, value); | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_set_label_italic (GtkWidget *label, | ||
|  |                                    gboolean   italic) | ||
|  | { | ||
|  |   PangoStyle attribute = italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL; | ||
|  | 
 | ||
|  |   pika_label_set_attributes (GTK_LABEL (label), | ||
|  |                              PANGO_ATTR_STYLE, attribute, | ||
|  |                              -1); | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_style_updated (GtkWidget *widget) | ||
|  | { | ||
|  |   PikaCursorView *view = PIKA_CURSOR_VIEW (widget); | ||
|  |   gint            content_spacing; | ||
|  | 
 | ||
|  |   GTK_WIDGET_CLASS (parent_class)->style_updated (widget); | ||
|  | 
 | ||
|  |   gtk_widget_style_get (GTK_WIDGET (view), | ||
|  |                         "content-spacing", &content_spacing, | ||
|  |                         NULL); | ||
|  | 
 | ||
|  |   gtk_box_set_spacing (GTK_BOX (view->priv->coord_hbox),     content_spacing); | ||
|  |   gtk_box_set_spacing (GTK_BOX (view->priv->selection_hbox), content_spacing); | ||
|  |   gtk_box_set_spacing (GTK_BOX (view->priv->color_hbox),     content_spacing); | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_set_context (PikaDocked  *docked, | ||
|  |                               PikaContext *context) | ||
|  | { | ||
|  |   PikaCursorView  *view    = PIKA_CURSOR_VIEW (docked); | ||
|  |   PikaColorConfig *config  = NULL; | ||
|  |   PikaDisplay     *display = NULL; | ||
|  |   PikaImage       *image   = NULL; | ||
|  | 
 | ||
|  |   if (context == view->priv->context) | ||
|  |     return; | ||
|  | 
 | ||
|  |   if (view->priv->context) | ||
|  |     { | ||
|  |       g_signal_handlers_disconnect_by_func (view->priv->context, | ||
|  |                                             pika_cursor_view_diplay_changed, | ||
|  |                                             view); | ||
|  |       g_signal_handlers_disconnect_by_func (view->priv->context, | ||
|  |                                             pika_cursor_view_image_changed, | ||
|  |                                             view); | ||
|  | 
 | ||
|  |       g_object_unref (view->priv->context); | ||
|  |     } | ||
|  | 
 | ||
|  |   view->priv->context = context; | ||
|  | 
 | ||
|  |   if (view->priv->context) | ||
|  |     { | ||
|  |       g_object_ref (view->priv->context); | ||
|  | 
 | ||
|  |       g_signal_connect_swapped (view->priv->context, "display-changed", | ||
|  |                                 G_CALLBACK (pika_cursor_view_diplay_changed), | ||
|  |                                 view); | ||
|  | 
 | ||
|  |       g_signal_connect_swapped (view->priv->context, "image-changed", | ||
|  |                                 G_CALLBACK (pika_cursor_view_image_changed), | ||
|  |                                 view); | ||
|  | 
 | ||
|  |       config  = context->pika->config->color_management; | ||
|  |       display = pika_context_get_display (context); | ||
|  |       image   = pika_context_get_image (context); | ||
|  |     } | ||
|  | 
 | ||
|  |   pika_color_frame_set_color_config (PIKA_COLOR_FRAME (view->priv->color_frame_1), | ||
|  |                                      config); | ||
|  |   pika_color_frame_set_color_config (PIKA_COLOR_FRAME (view->priv->color_frame_2), | ||
|  |                                      config); | ||
|  | 
 | ||
|  |   pika_cursor_view_diplay_changed (view, display, view->priv->context); | ||
|  |   pika_cursor_view_image_changed (view, image, view->priv->context); | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_image_changed (PikaCursorView *view, | ||
|  |                                 PikaImage      *image, | ||
|  |                                 PikaContext    *context) | ||
|  | { | ||
|  |   g_return_if_fail (PIKA_IS_CURSOR_VIEW (view)); | ||
|  | 
 | ||
|  |   if (image == view->priv->image) | ||
|  |     return; | ||
|  | 
 | ||
|  |   if (view->priv->image) | ||
|  |     { | ||
|  |       g_signal_handlers_disconnect_by_func (view->priv->image, | ||
|  |                                             pika_cursor_view_mask_changed, | ||
|  |                                             view); | ||
|  |     } | ||
|  | 
 | ||
|  |   view->priv->image = image; | ||
|  | 
 | ||
|  |   if (view->priv->image) | ||
|  |     { | ||
|  |       g_signal_connect_swapped (view->priv->image, "mask-changed", | ||
|  |                                 G_CALLBACK (pika_cursor_view_mask_changed), | ||
|  |                                 view); | ||
|  |     } | ||
|  | 
 | ||
|  |   pika_cursor_view_mask_changed (view, view->priv->image); | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_mask_changed (PikaCursorView *view, | ||
|  |                                PikaImage      *image) | ||
|  | { | ||
|  |   pika_cursor_view_update_selection_info (view, | ||
|  |                                           view->priv->image, | ||
|  |                                           view->priv->unit); | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_diplay_changed (PikaCursorView *view, | ||
|  |                                  PikaDisplay    *display, | ||
|  |                                  PikaContext    *context) | ||
|  | { | ||
|  |   PikaDisplayShell *shell = NULL; | ||
|  | 
 | ||
|  |   if (display) | ||
|  |     shell = pika_display_get_shell (display); | ||
|  | 
 | ||
|  |   if (view->priv->shell) | ||
|  |     { | ||
|  |       g_signal_handlers_disconnect_by_func (view->priv->shell, | ||
|  |                                             pika_cursor_view_shell_unit_changed, | ||
|  |                                             view); | ||
|  |     } | ||
|  | 
 | ||
|  |   view->priv->shell = shell; | ||
|  | 
 | ||
|  |   if (view->priv->shell) | ||
|  |     { | ||
|  |       g_signal_connect_swapped (view->priv->shell, "notify::unit", | ||
|  |                                 G_CALLBACK (pika_cursor_view_shell_unit_changed), | ||
|  |                                 view); | ||
|  |     } | ||
|  | 
 | ||
|  |   pika_cursor_view_shell_unit_changed (view, | ||
|  |                                        NULL, | ||
|  |                                        view->priv->shell); | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_shell_unit_changed (PikaCursorView   *view, | ||
|  |                                      GParamSpec       *pspec, | ||
|  |                                      PikaDisplayShell *shell) | ||
|  | { | ||
|  |   PikaUnit new_unit = PIKA_UNIT_PIXEL; | ||
|  | 
 | ||
|  |   if (shell) | ||
|  |     { | ||
|  |       new_unit = pika_display_shell_get_unit (shell); | ||
|  |     } | ||
|  | 
 | ||
|  |   if (view->priv->unit != new_unit) | ||
|  |     { | ||
|  |       pika_cursor_view_update_selection_info (view, view->priv->image, new_unit); | ||
|  |       view->priv->unit = new_unit; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_cursor_view_update_selection_info (PikaCursorView *view, | ||
|  |                                         PikaImage      *image, | ||
|  |                                         PikaUnit        unit) | ||
|  | { | ||
|  |   gint x, y, width, height; | ||
|  | 
 | ||
|  |   if (image && | ||
|  |       pika_item_bounds (PIKA_ITEM (pika_image_get_mask (image)), | ||
|  |                         &x, &y, &width, &height)) | ||
|  |     { | ||
|  |       gdouble xres, yres; | ||
|  |       gchar   buf[32]; | ||
|  | 
 | ||
|  |       pika_image_get_resolution (image, &xres, &yres); | ||
|  | 
 | ||
|  |       pika_cursor_view_format_as_unit (unit, buf, sizeof (buf), x, xres); | ||
|  |       gtk_label_set_text (GTK_LABEL (view->priv->selection_x_label), buf); | ||
|  | 
 | ||
|  |       pika_cursor_view_format_as_unit (unit, buf, sizeof (buf), y, yres); | ||
|  |       gtk_label_set_text (GTK_LABEL (view->priv->selection_y_label), buf); | ||
|  | 
 | ||
|  |       pika_cursor_view_format_as_unit (unit, buf, sizeof (buf), width, xres); | ||
|  |       gtk_label_set_text (GTK_LABEL (view->priv->selection_width_label), buf); | ||
|  | 
 | ||
|  |       pika_cursor_view_format_as_unit (unit, buf, sizeof (buf), height, yres); | ||
|  |       gtk_label_set_text (GTK_LABEL (view->priv->selection_height_label), buf); | ||
|  |     } | ||
|  |   else | ||
|  |     { | ||
|  |       gtk_label_set_text (GTK_LABEL (view->priv->selection_x_label), | ||
|  |                           _("n/a")); | ||
|  |       gtk_label_set_text (GTK_LABEL (view->priv->selection_y_label), | ||
|  |                           _("n/a")); | ||
|  |       gtk_label_set_text (GTK_LABEL (view->priv->selection_width_label), | ||
|  |                           _("n/a")); | ||
|  |       gtk_label_set_text (GTK_LABEL (view->priv->selection_height_label), | ||
|  |                           _("n/a")); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | static gboolean | ||
|  | pika_cursor_view_cursor_idle (PikaCursorView *view) | ||
|  | { | ||
|  | 
 | ||
|  |   if (view->priv->cursor_image) | ||
|  |     { | ||
|  |       PikaImage  *image = view->priv->cursor_image; | ||
|  |       PikaUnit    unit  = view->priv->cursor_unit; | ||
|  |       gdouble     x     = view->priv->cursor_x; | ||
|  |       gdouble     y     = view->priv->cursor_y; | ||
|  |       gboolean    in_image; | ||
|  |       gchar       buf[32]; | ||
|  |       const Babl *sample_format; | ||
|  |       gdouble     pixel[4]; | ||
|  |       PikaRGB     color; | ||
|  |       gdouble     xres; | ||
|  |       gdouble     yres; | ||
|  |       gint        int_x; | ||
|  |       gint        int_y; | ||
|  | 
 | ||
|  |       if (unit == PIKA_UNIT_PIXEL) | ||
|  |         unit = pika_image_get_unit (image); | ||
|  | 
 | ||
|  |       pika_image_get_resolution (image, &xres, &yres); | ||
|  | 
 | ||
|  |       in_image = (x >= 0.0 && x < pika_image_get_width  (image) && | ||
|  |                   y >= 0.0 && y < pika_image_get_height (image)); | ||
|  | 
 | ||
|  |       g_snprintf (buf, sizeof (buf), "%d", (gint) floor (x)); | ||
|  |       gtk_label_set_text (GTK_LABEL (view->priv->pixel_x_label), buf); | ||
|  |       pika_cursor_view_set_label_italic (view->priv->pixel_x_label, ! in_image); | ||
|  | 
 | ||
|  |       g_snprintf (buf, sizeof (buf), "%d", (gint) floor (y)); | ||
|  |       gtk_label_set_text (GTK_LABEL (view->priv->pixel_y_label), buf); | ||
|  |       pika_cursor_view_set_label_italic (view->priv->pixel_y_label, ! in_image); | ||
|  | 
 | ||
|  |       pika_cursor_view_format_as_unit (unit, buf, sizeof (buf), x, xres); | ||
|  |       gtk_label_set_text (GTK_LABEL (view->priv->unit_x_label), buf); | ||
|  |       pika_cursor_view_set_label_italic (view->priv->unit_x_label, ! in_image); | ||
|  | 
 | ||
|  |       pika_cursor_view_format_as_unit (unit, buf, sizeof (buf), y, yres); | ||
|  |       gtk_label_set_text (GTK_LABEL (view->priv->unit_y_label), buf); | ||
|  |       pika_cursor_view_set_label_italic (view->priv->unit_y_label, ! in_image); | ||
|  | 
 | ||
|  |       int_x = (gint) floor (x); | ||
|  |       int_y = (gint) floor (y); | ||
|  | 
 | ||
|  |       if (pika_image_pick_color (image, NULL, | ||
|  |                                  int_x, int_y, | ||
|  |                                  view->priv->shell->show_all, | ||
|  |                                  view->priv->sample_merged, | ||
|  |                                  FALSE, 0.0, | ||
|  |                                  &sample_format, pixel, &color)) | ||
|  |         { | ||
|  |           pika_color_frame_set_color (PIKA_COLOR_FRAME (view->priv->color_frame_1), | ||
|  |                                       FALSE, sample_format, pixel, &color, | ||
|  |                                       int_x, int_y); | ||
|  |           pika_color_frame_set_color (PIKA_COLOR_FRAME (view->priv->color_frame_2), | ||
|  |                                       FALSE, sample_format, pixel, &color, | ||
|  |                                       int_x, int_y); | ||
|  |         } | ||
|  |       else | ||
|  |         { | ||
|  |           pika_color_frame_set_invalid (PIKA_COLOR_FRAME (view->priv->color_frame_1)); | ||
|  |           pika_color_frame_set_invalid (PIKA_COLOR_FRAME (view->priv->color_frame_2)); | ||
|  |         } | ||
|  | 
 | ||
|  |       /* Show the selection info from the image under the cursor if any */ | ||
|  |       pika_cursor_view_update_selection_info (view, | ||
|  |                                               image, | ||
|  |                                               view->priv->cursor_unit); | ||
|  | 
 | ||
|  |       g_clear_object (&view->priv->cursor_image); | ||
|  |     } | ||
|  |   else | ||
|  |     { | ||
|  |       gtk_label_set_text (GTK_LABEL (view->priv->pixel_x_label), _("n/a")); | ||
|  |       gtk_label_set_text (GTK_LABEL (view->priv->pixel_y_label), _("n/a")); | ||
|  |       gtk_label_set_text (GTK_LABEL (view->priv->unit_x_label),  _("n/a")); | ||
|  |       gtk_label_set_text (GTK_LABEL (view->priv->unit_y_label),  _("n/a")); | ||
|  | 
 | ||
|  |       pika_color_frame_set_invalid (PIKA_COLOR_FRAME (view->priv->color_frame_1)); | ||
|  |       pika_color_frame_set_invalid (PIKA_COLOR_FRAME (view->priv->color_frame_2)); | ||
|  | 
 | ||
|  |       /* Start showing selection info from the active image again */ | ||
|  |       pika_cursor_view_update_selection_info (view, | ||
|  |                                               view->priv->image, | ||
|  |                                               view->priv->unit); | ||
|  |     } | ||
|  | 
 | ||
|  |   view->priv->cursor_idle_id = 0; | ||
|  | 
 | ||
|  |   return G_SOURCE_REMOVE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /*  public functions  */ | ||
|  | 
 | ||
|  | GtkWidget * | ||
|  | pika_cursor_view_new (Pika            *pika, | ||
|  |                       PikaMenuFactory *menu_factory) | ||
|  | { | ||
|  |   g_return_val_if_fail (PIKA_IS_MENU_FACTORY (menu_factory), NULL); | ||
|  | 
 | ||
|  |   return g_object_new (PIKA_TYPE_CURSOR_VIEW, | ||
|  |                        "pika",            pika, | ||
|  |                        "menu-factory",    menu_factory, | ||
|  |                        "menu-identifier", "<CursorInfo>", | ||
|  |                        "ui-path",         "/cursor-info-popup", | ||
|  |                        NULL); | ||
|  | } | ||
|  | 
 | ||
|  | void | ||
|  | pika_cursor_view_set_sample_merged (PikaCursorView *view, | ||
|  |                                     gboolean        sample_merged) | ||
|  | { | ||
|  |   g_return_if_fail (PIKA_IS_CURSOR_VIEW (view)); | ||
|  | 
 | ||
|  |   sample_merged = sample_merged ? TRUE : FALSE; | ||
|  | 
 | ||
|  |   if (view->priv->sample_merged != sample_merged) | ||
|  |     { | ||
|  |       view->priv->sample_merged = sample_merged; | ||
|  | 
 | ||
|  |       g_object_notify (G_OBJECT (view), "sample-merged"); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | gboolean | ||
|  | pika_cursor_view_get_sample_merged (PikaCursorView *view) | ||
|  | { | ||
|  |   g_return_val_if_fail (PIKA_IS_CURSOR_VIEW (view), FALSE); | ||
|  | 
 | ||
|  |   return view->priv->sample_merged; | ||
|  | } | ||
|  | 
 | ||
|  | void | ||
|  | pika_cursor_view_update_cursor (PikaCursorView   *view, | ||
|  |                                 PikaImage        *image, | ||
|  |                                 PikaUnit          shell_unit, | ||
|  |                                 gdouble           x, | ||
|  |                                 gdouble           y) | ||
|  | { | ||
|  |   g_return_if_fail (PIKA_IS_CURSOR_VIEW (view)); | ||
|  |   g_return_if_fail (PIKA_IS_IMAGE (image)); | ||
|  | 
 | ||
|  |   g_clear_object (&view->priv->cursor_image); | ||
|  | 
 | ||
|  |   view->priv->cursor_image = g_object_ref (image); | ||
|  |   view->priv->cursor_unit  = shell_unit; | ||
|  |   view->priv->cursor_x     = x; | ||
|  |   view->priv->cursor_y     = y; | ||
|  | 
 | ||
|  |   if (view->priv->cursor_idle_id == 0) | ||
|  |     { | ||
|  |       view->priv->cursor_idle_id = | ||
|  |         g_idle_add ((GSourceFunc) pika_cursor_view_cursor_idle, view); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | void | ||
|  | pika_cursor_view_clear_cursor (PikaCursorView *view) | ||
|  | { | ||
|  |   g_return_if_fail (PIKA_IS_CURSOR_VIEW (view)); | ||
|  | 
 | ||
|  |   g_clear_object (&view->priv->cursor_image); | ||
|  | 
 | ||
|  |   if (view->priv->cursor_idle_id == 0) | ||
|  |     { | ||
|  |       view->priv->cursor_idle_id = | ||
|  |         g_idle_add ((GSourceFunc) pika_cursor_view_cursor_idle, view); | ||
|  |     } | ||
|  | } |