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