1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpcursorview.c
5  * Copyright (C) 2005-2016 Michael Natterer <mitch@gimp.org>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <string.h>
24 
25 #include <gegl.h>
26 #include <gtk/gtk.h>
27 
28 #include "libgimpbase/gimpbase.h"
29 #include "libgimpmath/gimpmath.h"
30 #include "libgimpcolor/gimpcolor.h"
31 #include "libgimpwidgets/gimpwidgets.h"
32 
33 #include "display-types.h"
34 
35 #include "config/gimpcoreconfig.h"
36 
37 #include "core/gimp.h"
38 #include "core/gimpcontext.h"
39 #include "core/gimpimage.h"
40 #include "core/gimpimage-pick-color.h"
41 #include "core/gimpitem.h"
42 
43 #include "widgets/gimpcolorframe.h"
44 #include "widgets/gimpdocked.h"
45 #include "widgets/gimpmenufactory.h"
46 #include "widgets/gimpsessioninfo-aux.h"
47 
48 #include "gimpcursorview.h"
49 #include "gimpdisplay.h"
50 #include "gimpdisplayshell.h"
51 
52 #include "gimp-intl.h"
53 
54 
55 enum
56 {
57   PROP_0,
58   PROP_SAMPLE_MERGED
59 };
60 
61 
62 struct _GimpCursorViewPrivate
63 {
64   GimpEditor        parent_instance;
65 
66   GtkWidget        *coord_hbox;
67   GtkWidget        *selection_hbox;
68   GtkWidget        *color_hbox;
69 
70   GtkWidget        *pixel_x_label;
71   GtkWidget        *pixel_y_label;
72   GtkWidget        *unit_x_label;
73   GtkWidget        *unit_y_label;
74   GtkWidget        *selection_x_label;
75   GtkWidget        *selection_y_label;
76   GtkWidget        *selection_width_label;
77   GtkWidget        *selection_height_label;
78   GtkWidget        *color_frame_1;
79   GtkWidget        *color_frame_2;
80 
81   gboolean          sample_merged;
82 
83   GimpContext      *context;
84   GimpDisplayShell *shell;
85   GimpImage        *image;
86   GimpUnit          unit;
87 
88   guint             cursor_idle_id;
89   GimpImage        *cursor_image;
90   GimpUnit          cursor_unit;
91   gdouble           cursor_x;
92   gdouble           cursor_y;
93 };
94 
95 
96 static void       gimp_cursor_view_docked_iface_init     (GimpDockedInterface *iface);
97 
98 static void       gimp_cursor_view_dispose               (GObject             *object);
99 static void       gimp_cursor_view_set_property          (GObject             *object,
100                                                           guint                property_id,
101                                                           const GValue        *value,
102                                                           GParamSpec          *pspec);
103 static void       gimp_cursor_view_get_property          (GObject             *object,
104                                                           guint                property_id,
105                                                           GValue              *value,
106                                                           GParamSpec          *pspec);
107 
108 static void       gimp_cursor_view_style_set             (GtkWidget           *widget,
109                                                           GtkStyle            *prev_style);
110 
111 static void       gimp_cursor_view_set_aux_info          (GimpDocked          *docked,
112                                                           GList               *aux_info);
113 static GList    * gimp_cursor_view_get_aux_info          (GimpDocked          *docked);
114 
115 static void       gimp_cursor_view_set_context           (GimpDocked          *docked,
116                                                           GimpContext         *context);
117 static void       gimp_cursor_view_image_changed         (GimpCursorView      *view,
118                                                           GimpImage           *image,
119                                                           GimpContext         *context);
120 static void       gimp_cursor_view_mask_changed          (GimpCursorView      *view,
121                                                           GimpImage           *image);
122 static void       gimp_cursor_view_diplay_changed        (GimpCursorView      *view,
123                                                           GimpDisplay         *display,
124                                                           GimpContext         *context);
125 static void       gimp_cursor_view_shell_unit_changed    (GimpCursorView      *view,
126                                                           GParamSpec          *pspec,
127                                                           GimpDisplayShell    *shell);
128 static void       gimp_cursor_view_format_as_unit        (GimpUnit             unit,
129                                                           gchar               *output_buf,
130                                                           gint                 output_buf_size,
131                                                           gdouble              pixel_value,
132                                                           gdouble              image_res);
133 static void       gimp_cursor_view_set_label_italic      (GtkWidget           *label,
134                                                           gboolean             italic);
135 static void       gimp_cursor_view_update_selection_info (GimpCursorView      *view,
136                                                           GimpImage           *image,
137                                                           GimpUnit             unit);
138 static gboolean   gimp_cursor_view_cursor_idle           (GimpCursorView      *view);
139 
140 
141 G_DEFINE_TYPE_WITH_CODE (GimpCursorView, gimp_cursor_view, GIMP_TYPE_EDITOR,
142                          G_ADD_PRIVATE (GimpCursorView)
143                          G_IMPLEMENT_INTERFACE (GIMP_TYPE_DOCKED,
144                                                 gimp_cursor_view_docked_iface_init))
145 
146 #define parent_class gimp_cursor_view_parent_class
147 
148 static GimpDockedInterface *parent_docked_iface = NULL;
149 
150 
151 static void
gimp_cursor_view_class_init(GimpCursorViewClass * klass)152 gimp_cursor_view_class_init (GimpCursorViewClass* klass)
153 {
154   GObjectClass   *object_class = G_OBJECT_CLASS (klass);
155   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
156 
157   object_class->dispose      = gimp_cursor_view_dispose;
158   object_class->get_property = gimp_cursor_view_get_property;
159   object_class->set_property = gimp_cursor_view_set_property;
160 
161   widget_class->style_set    = gimp_cursor_view_style_set;
162 
163   g_object_class_install_property (object_class, PROP_SAMPLE_MERGED,
164                                    g_param_spec_boolean ("sample-merged",
165                                                          NULL, NULL,
166                                                          TRUE,
167                                                          GIMP_PARAM_READWRITE |
168                                                          G_PARAM_CONSTRUCT));
169 }
170 
171 static void
gimp_cursor_view_init(GimpCursorView * view)172 gimp_cursor_view_init (GimpCursorView *view)
173 {
174   GtkWidget *frame;
175   GtkWidget *table;
176   GtkWidget *toggle;
177   gint       content_spacing;
178 
179   view->priv = gimp_cursor_view_get_instance_private (view);
180 
181   view->priv->sample_merged  = TRUE;
182   view->priv->context        = NULL;
183   view->priv->shell          = NULL;
184   view->priv->image          = NULL;
185   view->priv->unit           = GIMP_UNIT_PIXEL;
186   view->priv->cursor_idle_id = 0;
187 
188   gtk_widget_style_get (GTK_WIDGET (view),
189                         "content-spacing", &content_spacing,
190                         NULL);
191 
192 
193   /* cursor information */
194 
195   view->priv->coord_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL,
196                                         content_spacing);
197   gtk_box_set_homogeneous (GTK_BOX (view->priv->coord_hbox), TRUE);
198   gtk_box_pack_start (GTK_BOX (view), view->priv->coord_hbox,
199                       FALSE, FALSE, 0);
200   gtk_widget_show (view->priv->coord_hbox);
201 
202   view->priv->selection_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL,
203                                             content_spacing);
204   gtk_box_set_homogeneous (GTK_BOX (view->priv->selection_hbox), TRUE);
205   gtk_box_pack_start (GTK_BOX (view), view->priv->selection_hbox,
206                       FALSE, FALSE, 0);
207   gtk_widget_show (view->priv->selection_hbox);
208 
209 
210   /* Pixels */
211 
212   frame = gimp_frame_new (_("Pixels"));
213   gtk_box_pack_start (GTK_BOX (view->priv->coord_hbox), frame, TRUE, TRUE, 0);
214   gtk_widget_show (frame);
215 
216   table = gtk_table_new (2, 2, FALSE);
217   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
218   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
219   gtk_container_add (GTK_CONTAINER (frame), table);
220   gtk_widget_show (table);
221 
222   view->priv->pixel_x_label = gtk_label_new (_("n/a"));
223   gtk_label_set_xalign (GTK_LABEL (view->priv->pixel_x_label), 1.0);
224   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
225                              _("X"), 0.5, 0.5,
226                              view->priv->pixel_x_label, 1, FALSE);
227 
228   view->priv->pixel_y_label = gtk_label_new (_("n/a"));
229   gtk_label_set_xalign (GTK_LABEL (view->priv->pixel_y_label), 1.0);
230   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
231                              _("Y"), 0.5, 0.5,
232                              view->priv->pixel_y_label, 1, FALSE);
233 
234 
235   /* Units */
236 
237   frame = gimp_frame_new (_("Units"));
238   gtk_box_pack_start (GTK_BOX (view->priv->coord_hbox), frame, TRUE, TRUE, 0);
239   gtk_widget_show (frame);
240 
241   table = gtk_table_new (2, 2, FALSE);
242   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
243   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
244   gtk_container_add (GTK_CONTAINER (frame), table);
245   gtk_widget_show (table);
246 
247   view->priv->unit_x_label = gtk_label_new (_("n/a"));
248   gtk_label_set_xalign (GTK_LABEL (view->priv->unit_x_label), 1.0);
249   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
250                              _("X"), 0.5, 0.5,
251                              view->priv->unit_x_label, 1, FALSE);
252 
253   view->priv->unit_y_label = gtk_label_new (_("n/a"));
254   gtk_label_set_xalign (GTK_LABEL (view->priv->unit_y_label), 1.0);
255   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
256                              _("Y"), 0.5, 0.5,
257                              view->priv->unit_y_label, 1, FALSE);
258 
259 
260   /* Selection Bounding Box */
261 
262   frame = gimp_frame_new (_("Selection"));
263   gtk_box_pack_start (GTK_BOX (view->priv->selection_hbox), frame, TRUE, TRUE, 0);
264   gtk_widget_show (frame);
265 
266   gimp_help_set_help_data (frame, _("The selection's bounding box"), NULL);
267 
268   table = gtk_table_new (2, 2, FALSE);
269   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
270   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
271   gtk_container_add (GTK_CONTAINER (frame), table);
272   gtk_widget_show (table);
273 
274   view->priv->selection_x_label = gtk_label_new (_("n/a"));
275   gtk_label_set_xalign (GTK_LABEL (view->priv->selection_x_label), 1.0);
276   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
277                              _("X"), 0.5, 0.5,
278                              view->priv->selection_x_label, 1, FALSE);
279 
280   view->priv->selection_y_label = gtk_label_new (_("n/a"));
281   gtk_label_set_xalign (GTK_LABEL (view->priv->selection_y_label), 1.0);
282   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
283                              _("Y"), 0.5, 0.5,
284                              view->priv->selection_y_label, 1, FALSE);
285 
286   frame = gimp_frame_new ("");
287   gtk_box_pack_start (GTK_BOX (view->priv->selection_hbox), frame, TRUE, TRUE, 0);
288   gtk_widget_show (frame);
289 
290   table = gtk_table_new (2, 2, FALSE);
291   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
292   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
293   gtk_container_add (GTK_CONTAINER (frame), table);
294   gtk_widget_show (table);
295 
296   view->priv->selection_width_label = gtk_label_new (_("n/a"));
297   gtk_label_set_xalign (GTK_LABEL (view->priv->selection_width_label), 1.0);
298   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
299                              /* Width */
300                              _("W"), 0.5, 0.5,
301                              view->priv->selection_width_label, 1, FALSE);
302 
303   view->priv->selection_height_label = gtk_label_new (_("n/a"));
304   gtk_label_set_xalign (GTK_LABEL (view->priv->selection_height_label), 1.0);
305   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
306                              /* Height */
307                              _("H"), 0.5, 0.5,
308                              view->priv->selection_height_label, 1, FALSE);
309 
310 
311   /* color information */
312 
313   view->priv->color_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL,
314                                         content_spacing);
315   gtk_box_set_homogeneous (GTK_BOX (view->priv->color_hbox), TRUE);
316   gtk_box_pack_start (GTK_BOX (view), view->priv->color_hbox, FALSE, FALSE, 0);
317   gtk_widget_show (view->priv->color_hbox);
318 
319   view->priv->color_frame_1 = gimp_color_frame_new ();
320   gimp_color_frame_set_mode (GIMP_COLOR_FRAME (view->priv->color_frame_1),
321                              GIMP_COLOR_PICK_MODE_PIXEL);
322   gimp_color_frame_set_ellipsize (GIMP_COLOR_FRAME (view->priv->color_frame_1),
323                                   PANGO_ELLIPSIZE_END);
324   gtk_box_pack_start (GTK_BOX (view->priv->color_hbox), view->priv->color_frame_1,
325                       TRUE, TRUE, 0);
326   gtk_widget_show (view->priv->color_frame_1);
327 
328   view->priv->color_frame_2 = gimp_color_frame_new ();
329   gimp_color_frame_set_mode (GIMP_COLOR_FRAME (view->priv->color_frame_2),
330                              GIMP_COLOR_PICK_MODE_RGB_PERCENT);
331   gtk_box_pack_start (GTK_BOX (view->priv->color_hbox), view->priv->color_frame_2,
332                       TRUE, TRUE, 0);
333   gtk_widget_show (view->priv->color_frame_2);
334 
335   /* sample merged toggle */
336 
337   toggle = gimp_prop_check_button_new (G_OBJECT (view), "sample-merged",
338                                        _("_Sample Merged"));
339   gtk_box_pack_start (GTK_BOX (view), toggle, FALSE, FALSE, 0);
340   gtk_widget_show (toggle);
341 }
342 
343 static void
gimp_cursor_view_docked_iface_init(GimpDockedInterface * iface)344 gimp_cursor_view_docked_iface_init (GimpDockedInterface *iface)
345 {
346   parent_docked_iface = g_type_interface_peek_parent (iface);
347 
348   if (! parent_docked_iface)
349     parent_docked_iface = g_type_default_interface_peek (GIMP_TYPE_DOCKED);
350 
351   iface->set_aux_info = gimp_cursor_view_set_aux_info;
352   iface->get_aux_info = gimp_cursor_view_get_aux_info;
353   iface->set_context  = gimp_cursor_view_set_context;
354 }
355 
356 static void
gimp_cursor_view_dispose(GObject * object)357 gimp_cursor_view_dispose (GObject *object)
358 {
359   GimpCursorView *view = GIMP_CURSOR_VIEW (object);
360 
361   if (view->priv->context)
362     gimp_docked_set_context (GIMP_DOCKED (view), NULL);
363 
364   if (view->priv->cursor_idle_id)
365     {
366       g_source_remove (view->priv->cursor_idle_id);
367       view->priv->cursor_idle_id = 0;
368     }
369 
370   g_clear_object (&view->priv->cursor_image);
371 
372   G_OBJECT_CLASS (parent_class)->dispose (object);
373 }
374 
375 static void
gimp_cursor_view_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)376 gimp_cursor_view_set_property (GObject      *object,
377                                guint         property_id,
378                                const GValue *value,
379                                GParamSpec   *pspec)
380 {
381   GimpCursorView *view = GIMP_CURSOR_VIEW (object);
382 
383   switch (property_id)
384     {
385     case PROP_SAMPLE_MERGED:
386       view->priv->sample_merged = g_value_get_boolean (value);
387       break;
388 
389     default:
390       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
391       break;
392     }
393 }
394 
395 static void
gimp_cursor_view_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)396 gimp_cursor_view_get_property (GObject    *object,
397                                guint       property_id,
398                                GValue     *value,
399                                GParamSpec *pspec)
400 {
401   GimpCursorView *view = GIMP_CURSOR_VIEW (object);
402 
403   switch (property_id)
404     {
405     case PROP_SAMPLE_MERGED:
406       g_value_set_boolean (value, view->priv->sample_merged);
407       break;
408 
409     default:
410       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
411       break;
412     }
413 }
414 
415 #define AUX_INFO_FRAME_1_MODE "frame-1-mode"
416 #define AUX_INFO_FRAME_2_MODE "frame-2-mode"
417 
418 static void
gimp_cursor_view_set_aux_info(GimpDocked * docked,GList * aux_info)419 gimp_cursor_view_set_aux_info (GimpDocked *docked,
420                                GList      *aux_info)
421 {
422   GimpCursorView *view = GIMP_CURSOR_VIEW (docked);
423   GList          *list;
424 
425   parent_docked_iface->set_aux_info (docked, aux_info);
426 
427   for (list = aux_info; list; list = g_list_next (list))
428     {
429       GimpSessionInfoAux *aux   = list->data;
430       GtkWidget          *frame = NULL;
431 
432       if (! strcmp (aux->name, AUX_INFO_FRAME_1_MODE))
433         frame = view->priv->color_frame_1;
434       else if (! strcmp (aux->name, AUX_INFO_FRAME_2_MODE))
435         frame = view->priv->color_frame_2;
436 
437       if (frame)
438         {
439           GEnumClass *enum_class;
440           GEnumValue *enum_value;
441 
442           enum_class = g_type_class_peek (GIMP_TYPE_COLOR_PICK_MODE);
443           enum_value = g_enum_get_value_by_nick (enum_class, aux->value);
444 
445           if (enum_value)
446             gimp_color_frame_set_mode (GIMP_COLOR_FRAME (frame),
447                                        enum_value->value);
448         }
449     }
450 }
451 
452 static GList *
gimp_cursor_view_get_aux_info(GimpDocked * docked)453 gimp_cursor_view_get_aux_info (GimpDocked *docked)
454 {
455   GimpCursorView     *view = GIMP_CURSOR_VIEW (docked);
456   GList              *aux_info;
457   const gchar        *nick;
458   GimpSessionInfoAux *aux;
459 
460   aux_info = parent_docked_iface->get_aux_info (docked);
461 
462   if (gimp_enum_get_value (GIMP_TYPE_COLOR_PICK_MODE,
463                            GIMP_COLOR_FRAME (view->priv->color_frame_1)->pick_mode,
464                            NULL, &nick, NULL, NULL))
465     {
466       aux = gimp_session_info_aux_new (AUX_INFO_FRAME_1_MODE, nick);
467       aux_info = g_list_append (aux_info, aux);
468     }
469 
470   if (gimp_enum_get_value (GIMP_TYPE_COLOR_PICK_MODE,
471                            GIMP_COLOR_FRAME (view->priv->color_frame_2)->pick_mode,
472                            NULL, &nick, NULL, NULL))
473     {
474       aux = gimp_session_info_aux_new (AUX_INFO_FRAME_2_MODE, nick);
475       aux_info = g_list_append (aux_info, aux);
476     }
477 
478   return aux_info;
479 }
480 
481 static void
gimp_cursor_view_format_as_unit(GimpUnit unit,gchar * output_buf,gint output_buf_size,gdouble pixel_value,gdouble image_res)482 gimp_cursor_view_format_as_unit (GimpUnit  unit,
483                                  gchar    *output_buf,
484                                  gint      output_buf_size,
485                                  gdouble   pixel_value,
486                                  gdouble   image_res)
487 {
488   gchar         format_buf[32];
489   gdouble       value;
490   gint          unit_digits = 0;
491   const gchar  *unit_str = "";
492 
493   value = gimp_pixels_to_units (pixel_value, unit, image_res);
494 
495   if (unit != GIMP_UNIT_PIXEL)
496     {
497       unit_digits = gimp_unit_get_scaled_digits (unit, image_res);
498       unit_str    = gimp_unit_get_abbreviation (unit);
499     }
500 
501   g_snprintf (format_buf, sizeof (format_buf),
502               "%%.%df %s", unit_digits, unit_str);
503 
504   g_snprintf (output_buf, output_buf_size, format_buf, value);
505 }
506 
507 static void
gimp_cursor_view_set_label_italic(GtkWidget * label,gboolean italic)508 gimp_cursor_view_set_label_italic (GtkWidget *label,
509                                    gboolean   italic)
510 {
511   PangoStyle attribute = italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL;
512 
513   gimp_label_set_attributes (GTK_LABEL (label),
514                              PANGO_ATTR_STYLE, attribute,
515                              -1);
516 }
517 
518 static void
gimp_cursor_view_style_set(GtkWidget * widget,GtkStyle * prev_style)519 gimp_cursor_view_style_set (GtkWidget *widget,
520                             GtkStyle  *prev_style)
521 {
522   GimpCursorView *view = GIMP_CURSOR_VIEW (widget);
523   gint            content_spacing;
524 
525   GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
526 
527   gtk_widget_style_get (GTK_WIDGET (view),
528                         "content-spacing", &content_spacing,
529                         NULL);
530 
531   gtk_box_set_spacing (GTK_BOX (view->priv->coord_hbox),     content_spacing);
532   gtk_box_set_spacing (GTK_BOX (view->priv->selection_hbox), content_spacing);
533   gtk_box_set_spacing (GTK_BOX (view->priv->color_hbox),     content_spacing);
534 }
535 
536 static void
gimp_cursor_view_set_context(GimpDocked * docked,GimpContext * context)537 gimp_cursor_view_set_context (GimpDocked  *docked,
538                               GimpContext *context)
539 {
540   GimpCursorView  *view    = GIMP_CURSOR_VIEW (docked);
541   GimpColorConfig *config  = NULL;
542   GimpDisplay     *display = NULL;
543   GimpImage       *image   = NULL;
544 
545   if (context == view->priv->context)
546     return;
547 
548   if (view->priv->context)
549     {
550       g_signal_handlers_disconnect_by_func (view->priv->context,
551                                             gimp_cursor_view_diplay_changed,
552                                             view);
553       g_signal_handlers_disconnect_by_func (view->priv->context,
554                                             gimp_cursor_view_image_changed,
555                                             view);
556 
557       g_object_unref (view->priv->context);
558     }
559 
560   view->priv->context = context;
561 
562   if (view->priv->context)
563     {
564       g_object_ref (view->priv->context);
565 
566       g_signal_connect_swapped (view->priv->context, "display-changed",
567                                 G_CALLBACK (gimp_cursor_view_diplay_changed),
568                                 view);
569 
570       g_signal_connect_swapped (view->priv->context, "image-changed",
571                                 G_CALLBACK (gimp_cursor_view_image_changed),
572                                 view);
573 
574       config  = context->gimp->config->color_management;
575       display = gimp_context_get_display (context);
576       image   = gimp_context_get_image (context);
577     }
578 
579   gimp_color_frame_set_color_config (GIMP_COLOR_FRAME (view->priv->color_frame_1),
580                                      config);
581   gimp_color_frame_set_color_config (GIMP_COLOR_FRAME (view->priv->color_frame_2),
582                                      config);
583 
584   gimp_cursor_view_diplay_changed (view, display, view->priv->context);
585   gimp_cursor_view_image_changed (view, image, view->priv->context);
586 }
587 
588 static void
gimp_cursor_view_image_changed(GimpCursorView * view,GimpImage * image,GimpContext * context)589 gimp_cursor_view_image_changed (GimpCursorView *view,
590                                 GimpImage      *image,
591                                 GimpContext    *context)
592 {
593   g_return_if_fail (GIMP_IS_CURSOR_VIEW (view));
594 
595   if (image == view->priv->image)
596     return;
597 
598   if (view->priv->image)
599     {
600       g_signal_handlers_disconnect_by_func (view->priv->image,
601                                             gimp_cursor_view_mask_changed,
602                                             view);
603     }
604 
605   view->priv->image = image;
606 
607   if (view->priv->image)
608     {
609       g_signal_connect_swapped (view->priv->image, "mask-changed",
610                                 G_CALLBACK (gimp_cursor_view_mask_changed),
611                                 view);
612     }
613 
614   gimp_cursor_view_mask_changed (view, view->priv->image);
615 }
616 
617 static void
gimp_cursor_view_mask_changed(GimpCursorView * view,GimpImage * image)618 gimp_cursor_view_mask_changed (GimpCursorView *view,
619                                GimpImage      *image)
620 {
621   gimp_cursor_view_update_selection_info (view,
622                                           view->priv->image,
623                                           view->priv->unit);
624 }
625 
626 static void
gimp_cursor_view_diplay_changed(GimpCursorView * view,GimpDisplay * display,GimpContext * context)627 gimp_cursor_view_diplay_changed (GimpCursorView *view,
628                                  GimpDisplay    *display,
629                                  GimpContext    *context)
630 {
631   GimpDisplayShell *shell = NULL;
632 
633   if (display)
634     shell = gimp_display_get_shell (display);
635 
636   if (view->priv->shell)
637     {
638       g_signal_handlers_disconnect_by_func (view->priv->shell,
639                                             gimp_cursor_view_shell_unit_changed,
640                                             view);
641     }
642 
643   view->priv->shell = shell;
644 
645   if (view->priv->shell)
646     {
647       g_signal_connect_swapped (view->priv->shell, "notify::unit",
648                                 G_CALLBACK (gimp_cursor_view_shell_unit_changed),
649                                 view);
650     }
651 
652   gimp_cursor_view_shell_unit_changed (view,
653                                        NULL,
654                                        view->priv->shell);
655 }
656 
657 static void
gimp_cursor_view_shell_unit_changed(GimpCursorView * view,GParamSpec * pspec,GimpDisplayShell * shell)658 gimp_cursor_view_shell_unit_changed (GimpCursorView   *view,
659                                      GParamSpec       *pspec,
660                                      GimpDisplayShell *shell)
661 {
662   GimpUnit new_unit = GIMP_UNIT_PIXEL;
663 
664   if (shell)
665     {
666       new_unit = gimp_display_shell_get_unit (shell);
667     }
668 
669   if (view->priv->unit != new_unit)
670     {
671       gimp_cursor_view_update_selection_info (view, view->priv->image, new_unit);
672       view->priv->unit = new_unit;
673     }
674 }
675 
676 static void
gimp_cursor_view_update_selection_info(GimpCursorView * view,GimpImage * image,GimpUnit unit)677 gimp_cursor_view_update_selection_info (GimpCursorView *view,
678                                         GimpImage      *image,
679                                         GimpUnit        unit)
680 {
681   gint x, y, width, height;
682 
683   if (image &&
684       gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)),
685                         &x, &y, &width, &height))
686     {
687       gdouble xres, yres;
688       gchar   buf[32];
689 
690       gimp_image_get_resolution (image, &xres, &yres);
691 
692       gimp_cursor_view_format_as_unit (unit, buf, sizeof (buf), x, xres);
693       gtk_label_set_text (GTK_LABEL (view->priv->selection_x_label), buf);
694 
695       gimp_cursor_view_format_as_unit (unit, buf, sizeof (buf), y, yres);
696       gtk_label_set_text (GTK_LABEL (view->priv->selection_y_label), buf);
697 
698       gimp_cursor_view_format_as_unit (unit, buf, sizeof (buf), width, xres);
699       gtk_label_set_text (GTK_LABEL (view->priv->selection_width_label), buf);
700 
701       gimp_cursor_view_format_as_unit (unit, buf, sizeof (buf), height, yres);
702       gtk_label_set_text (GTK_LABEL (view->priv->selection_height_label), buf);
703     }
704   else
705     {
706       gtk_label_set_text (GTK_LABEL (view->priv->selection_x_label),
707                           _("n/a"));
708       gtk_label_set_text (GTK_LABEL (view->priv->selection_y_label),
709                           _("n/a"));
710       gtk_label_set_text (GTK_LABEL (view->priv->selection_width_label),
711                           _("n/a"));
712       gtk_label_set_text (GTK_LABEL (view->priv->selection_height_label),
713                           _("n/a"));
714     }
715 }
716 
717 static gboolean
gimp_cursor_view_cursor_idle(GimpCursorView * view)718 gimp_cursor_view_cursor_idle (GimpCursorView *view)
719 {
720 
721   if (view->priv->cursor_image)
722     {
723       GimpImage  *image = view->priv->cursor_image;
724       GimpUnit    unit  = view->priv->cursor_unit;
725       gdouble     x     = view->priv->cursor_x;
726       gdouble     y     = view->priv->cursor_y;
727       gboolean    in_image;
728       gchar       buf[32];
729       const Babl *sample_format;
730       gdouble     pixel[4];
731       GimpRGB     color;
732       gdouble     xres;
733       gdouble     yres;
734       gint        int_x;
735       gint        int_y;
736 
737       if (unit == GIMP_UNIT_PIXEL)
738         unit = gimp_image_get_unit (image);
739 
740       gimp_image_get_resolution (image, &xres, &yres);
741 
742       in_image = (x >= 0.0 && x < gimp_image_get_width  (image) &&
743                   y >= 0.0 && y < gimp_image_get_height (image));
744 
745       g_snprintf (buf, sizeof (buf), "%d", (gint) floor (x));
746       gtk_label_set_text (GTK_LABEL (view->priv->pixel_x_label), buf);
747       gimp_cursor_view_set_label_italic (view->priv->pixel_x_label, ! in_image);
748 
749       g_snprintf (buf, sizeof (buf), "%d", (gint) floor (y));
750       gtk_label_set_text (GTK_LABEL (view->priv->pixel_y_label), buf);
751       gimp_cursor_view_set_label_italic (view->priv->pixel_y_label, ! in_image);
752 
753       gimp_cursor_view_format_as_unit (unit, buf, sizeof (buf), x, xres);
754       gtk_label_set_text (GTK_LABEL (view->priv->unit_x_label), buf);
755       gimp_cursor_view_set_label_italic (view->priv->unit_x_label, ! in_image);
756 
757       gimp_cursor_view_format_as_unit (unit, buf, sizeof (buf), y, yres);
758       gtk_label_set_text (GTK_LABEL (view->priv->unit_y_label), buf);
759       gimp_cursor_view_set_label_italic (view->priv->unit_y_label, ! in_image);
760 
761       int_x = (gint) floor (x);
762       int_y = (gint) floor (y);
763 
764       if (gimp_image_pick_color (image, NULL,
765                                  int_x, int_y,
766                                  view->priv->shell->show_all,
767                                  view->priv->sample_merged,
768                                  FALSE, 0.0,
769                                  &sample_format, pixel, &color))
770         {
771           gimp_color_frame_set_color (GIMP_COLOR_FRAME (view->priv->color_frame_1),
772                                       FALSE, sample_format, pixel, &color,
773                                       int_x, int_y);
774           gimp_color_frame_set_color (GIMP_COLOR_FRAME (view->priv->color_frame_2),
775                                       FALSE, sample_format, pixel, &color,
776                                       int_x, int_y);
777         }
778       else
779         {
780           gimp_color_frame_set_invalid (GIMP_COLOR_FRAME (view->priv->color_frame_1));
781           gimp_color_frame_set_invalid (GIMP_COLOR_FRAME (view->priv->color_frame_2));
782         }
783 
784       /* Show the selection info from the image under the cursor if any */
785       gimp_cursor_view_update_selection_info (view,
786                                               image,
787                                               view->priv->cursor_unit);
788 
789       g_clear_object (&view->priv->cursor_image);
790     }
791   else
792     {
793       gtk_label_set_text (GTK_LABEL (view->priv->pixel_x_label), _("n/a"));
794       gtk_label_set_text (GTK_LABEL (view->priv->pixel_y_label), _("n/a"));
795       gtk_label_set_text (GTK_LABEL (view->priv->unit_x_label),  _("n/a"));
796       gtk_label_set_text (GTK_LABEL (view->priv->unit_y_label),  _("n/a"));
797 
798       gimp_color_frame_set_invalid (GIMP_COLOR_FRAME (view->priv->color_frame_1));
799       gimp_color_frame_set_invalid (GIMP_COLOR_FRAME (view->priv->color_frame_2));
800 
801       /* Start showing selection info from the active image again */
802       gimp_cursor_view_update_selection_info (view,
803                                               view->priv->image,
804                                               view->priv->unit);
805     }
806 
807   view->priv->cursor_idle_id = 0;
808 
809   return G_SOURCE_REMOVE;
810 }
811 
812 
813 /*  public functions  */
814 
815 GtkWidget *
gimp_cursor_view_new(GimpMenuFactory * menu_factory)816 gimp_cursor_view_new (GimpMenuFactory *menu_factory)
817 {
818   g_return_val_if_fail (GIMP_IS_MENU_FACTORY (menu_factory), NULL);
819 
820   return g_object_new (GIMP_TYPE_CURSOR_VIEW,
821                        "menu-factory",    menu_factory,
822                        "menu-identifier", "<CursorInfo>",
823                        "ui-path",         "/cursor-info-popup",
824                        NULL);
825 }
826 
827 void
gimp_cursor_view_set_sample_merged(GimpCursorView * view,gboolean sample_merged)828 gimp_cursor_view_set_sample_merged (GimpCursorView *view,
829                                     gboolean        sample_merged)
830 {
831   g_return_if_fail (GIMP_IS_CURSOR_VIEW (view));
832 
833   sample_merged = sample_merged ? TRUE : FALSE;
834 
835   if (view->priv->sample_merged != sample_merged)
836     {
837       view->priv->sample_merged = sample_merged;
838 
839       g_object_notify (G_OBJECT (view), "sample-merged");
840     }
841 }
842 
843 gboolean
gimp_cursor_view_get_sample_merged(GimpCursorView * view)844 gimp_cursor_view_get_sample_merged (GimpCursorView *view)
845 {
846   g_return_val_if_fail (GIMP_IS_CURSOR_VIEW (view), FALSE);
847 
848   return view->priv->sample_merged;
849 }
850 
851 void
gimp_cursor_view_update_cursor(GimpCursorView * view,GimpImage * image,GimpUnit shell_unit,gdouble x,gdouble y)852 gimp_cursor_view_update_cursor (GimpCursorView   *view,
853                                 GimpImage        *image,
854                                 GimpUnit          shell_unit,
855                                 gdouble           x,
856                                 gdouble           y)
857 {
858   g_return_if_fail (GIMP_IS_CURSOR_VIEW (view));
859   g_return_if_fail (GIMP_IS_IMAGE (image));
860 
861   g_clear_object (&view->priv->cursor_image);
862 
863   view->priv->cursor_image = g_object_ref (image);
864   view->priv->cursor_unit  = shell_unit;
865   view->priv->cursor_x     = x;
866   view->priv->cursor_y     = y;
867 
868   if (view->priv->cursor_idle_id == 0)
869     {
870       view->priv->cursor_idle_id =
871         g_idle_add ((GSourceFunc) gimp_cursor_view_cursor_idle, view);
872     }
873 }
874 
875 void
gimp_cursor_view_clear_cursor(GimpCursorView * view)876 gimp_cursor_view_clear_cursor (GimpCursorView *view)
877 {
878   g_return_if_fail (GIMP_IS_CURSOR_VIEW (view));
879 
880   g_clear_object (&view->priv->cursor_image);
881 
882   if (view->priv->cursor_idle_id == 0)
883     {
884       view->priv->cursor_idle_id =
885         g_idle_add ((GSourceFunc) gimp_cursor_view_cursor_idle, view);
886     }
887 }
888