1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpfgbgeditor.c
5  * Copyright (C) 2004 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 "libgimpcolor/gimpcolor.h"
29 #include "libgimpconfig/gimpconfig.h"
30 #include "libgimpwidgets/gimpwidgets.h"
31 
32 #include "widgets-types.h"
33 
34 #include "config/gimpcoreconfig.h"
35 
36 #include "core/gimp.h"
37 #include "core/gimpcontext.h"
38 #include "core/gimpimage.h"
39 #include "core/gimpimage-colormap.h"
40 #include "core/gimpmarshal.h"
41 #include "core/gimppalette.h"
42 
43 #include "gimpdnd.h"
44 #include "gimpfgbgeditor.h"
45 #include "gimpwidgets-utils.h"
46 
47 #define CHANNEL_EPSILON 1e-3
48 
49 enum
50 {
51   PROP_0,
52   PROP_CONTEXT,
53   PROP_ACTIVE_COLOR
54 };
55 
56 enum
57 {
58   COLOR_CLICKED,
59   TOOLTIP,
60   LAST_SIGNAL
61 };
62 
63 
64 static void     gimp_fg_bg_editor_dispose           (GObject          *object);
65 static void     gimp_fg_bg_editor_set_property      (GObject          *object,
66                                                      guint             property_id,
67                                                      const GValue     *value,
68                                                      GParamSpec       *pspec);
69 static void     gimp_fg_bg_editor_get_property      (GObject          *object,
70                                                      guint             property_id,
71                                                      GValue           *value,
72                                                      GParamSpec       *pspec);
73 
74 static void     gimp_fg_bg_editor_style_set         (GtkWidget        *widget,
75                                                      GtkStyle         *prev_style);
76 static gboolean gimp_fg_bg_editor_expose            (GtkWidget        *widget,
77                                                      GdkEventExpose   *eevent);
78 static gboolean gimp_fg_bg_editor_button_press      (GtkWidget        *widget,
79                                                      GdkEventButton   *bevent);
80 static gboolean gimp_fg_bg_editor_button_release    (GtkWidget        *widget,
81                                                      GdkEventButton   *bevent);
82 static gboolean gimp_fg_bg_editor_drag_motion       (GtkWidget        *widget,
83                                                      GdkDragContext   *context,
84                                                      gint              x,
85                                                      gint              y,
86                                                      guint             time);
87 static gboolean gimp_fg_bg_editor_query_tooltip     (GtkWidget        *widget,
88                                                      gint              x,
89                                                      gint              y,
90                                                      gboolean          keyboard_mode,
91                                                      GtkTooltip       *tooltip);
92 
93 static void     gimp_fg_bg_editor_drag_color        (GtkWidget        *widget,
94                                                      GimpRGB          *color,
95                                                      gpointer          data);
96 static void     gimp_fg_bg_editor_drop_color        (GtkWidget        *widget,
97                                                      gint              x,
98                                                      gint              y,
99                                                      const GimpRGB    *color,
100                                                      gpointer          data);
101 
102 static void     gimp_fg_bg_editor_create_transform  (GimpFgBgEditor   *editor);
103 static void     gimp_fg_bg_editor_destroy_transform (GimpFgBgEditor   *editor);
104 
105 static void     gimp_fg_bg_editor_image_changed     (GimpFgBgEditor   *editor,
106                                                      GimpImage        *image);
107 
108 static void     gimp_fg_bg_editor_draw_color_frame  (GimpFgBgEditor   *editor,
109                                                      cairo_t          *cr,
110                                                      const GimpRGB    *color,
111                                                      gint              x,
112                                                      gint              y,
113                                                      gint              width,
114                                                      gint              height,
115                                                      gint              corner_dx,
116                                                      gint              corner_dy);
117 
118 G_DEFINE_TYPE (GimpFgBgEditor, gimp_fg_bg_editor, GTK_TYPE_EVENT_BOX)
119 
120 #define parent_class gimp_fg_bg_editor_parent_class
121 
122 static guint  editor_signals[LAST_SIGNAL] = { 0 };
123 
124 
125 static void
gimp_fg_bg_editor_class_init(GimpFgBgEditorClass * klass)126 gimp_fg_bg_editor_class_init (GimpFgBgEditorClass *klass)
127 {
128   GObjectClass   *object_class = G_OBJECT_CLASS (klass);
129   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
130 
131   editor_signals[COLOR_CLICKED] =
132     g_signal_new ("color-clicked",
133                   G_TYPE_FROM_CLASS (klass),
134                   G_SIGNAL_RUN_FIRST,
135                   G_STRUCT_OFFSET (GimpFgBgEditorClass, color_clicked),
136                   NULL, NULL,
137                   gimp_marshal_VOID__ENUM,
138                   G_TYPE_NONE, 1,
139                   GIMP_TYPE_ACTIVE_COLOR);
140 
141   editor_signals[TOOLTIP] =
142     g_signal_new ("tooltip",
143                   G_TYPE_FROM_CLASS (klass),
144                   G_SIGNAL_RUN_FIRST,
145                   G_STRUCT_OFFSET (GimpFgBgEditorClass, tooltip),
146                   NULL, NULL,
147                   gimp_marshal_VOID__INT_OBJECT,
148                   G_TYPE_NONE, 2,
149                   G_TYPE_INT,
150                   GTK_TYPE_TOOLTIP);
151 
152   object_class->dispose              = gimp_fg_bg_editor_dispose;
153   object_class->set_property         = gimp_fg_bg_editor_set_property;
154   object_class->get_property         = gimp_fg_bg_editor_get_property;
155 
156   widget_class->style_set            = gimp_fg_bg_editor_style_set;
157   widget_class->expose_event         = gimp_fg_bg_editor_expose;
158   widget_class->button_press_event   = gimp_fg_bg_editor_button_press;
159   widget_class->button_release_event = gimp_fg_bg_editor_button_release;
160   widget_class->drag_motion          = gimp_fg_bg_editor_drag_motion;
161   widget_class->query_tooltip        = gimp_fg_bg_editor_query_tooltip;
162 
163   g_object_class_install_property (object_class, PROP_CONTEXT,
164                                    g_param_spec_object ("context",
165                                                         NULL, NULL,
166                                                         GIMP_TYPE_CONTEXT,
167                                                         GIMP_PARAM_READWRITE));
168 
169   g_object_class_install_property (object_class, PROP_ACTIVE_COLOR,
170                                    g_param_spec_enum ("active-color",
171                                                       NULL, NULL,
172                                                       GIMP_TYPE_ACTIVE_COLOR,
173                                                       GIMP_ACTIVE_COLOR_FOREGROUND,
174                                                       GIMP_PARAM_READWRITE));
175 }
176 
177 static void
gimp_fg_bg_editor_init(GimpFgBgEditor * editor)178 gimp_fg_bg_editor_init (GimpFgBgEditor *editor)
179 {
180   editor->active_color = GIMP_ACTIVE_COLOR_FOREGROUND;
181 
182   gtk_event_box_set_visible_window (GTK_EVENT_BOX (editor), FALSE);
183 
184   gtk_widget_add_events (GTK_WIDGET (editor),
185                          GDK_BUTTON_PRESS_MASK |
186                          GDK_BUTTON_RELEASE_MASK);
187 
188   gimp_dnd_color_source_add (GTK_WIDGET (editor),
189                              gimp_fg_bg_editor_drag_color, NULL);
190   gimp_dnd_color_dest_add (GTK_WIDGET (editor),
191                            gimp_fg_bg_editor_drop_color, NULL);
192 
193   gimp_widget_track_monitor (GTK_WIDGET (editor),
194                              G_CALLBACK (gimp_fg_bg_editor_destroy_transform),
195                              NULL);
196 }
197 
198 static void
gimp_fg_bg_editor_dispose(GObject * object)199 gimp_fg_bg_editor_dispose (GObject *object)
200 {
201   GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (object);
202 
203   if (editor->context)
204     gimp_fg_bg_editor_set_context (editor, NULL);
205 
206   g_clear_object (&editor->default_icon);
207   g_clear_object (&editor->swap_icon);
208 
209   G_OBJECT_CLASS (parent_class)->dispose (object);
210 }
211 
212 static void
gimp_fg_bg_editor_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)213 gimp_fg_bg_editor_set_property (GObject      *object,
214                                 guint         property_id,
215                                 const GValue *value,
216                                 GParamSpec   *pspec)
217 {
218   GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (object);
219 
220   switch (property_id)
221     {
222     case PROP_CONTEXT:
223       gimp_fg_bg_editor_set_context (editor, g_value_get_object (value));
224       break;
225     case PROP_ACTIVE_COLOR:
226       gimp_fg_bg_editor_set_active (editor, g_value_get_enum (value));
227       break;
228     default:
229       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
230       break;
231     }
232 }
233 
234 static void
gimp_fg_bg_editor_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)235 gimp_fg_bg_editor_get_property (GObject    *object,
236                                 guint       property_id,
237                                 GValue     *value,
238                                 GParamSpec *pspec)
239 {
240   GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (object);
241 
242   switch (property_id)
243     {
244     case PROP_CONTEXT:
245       g_value_set_object (value, editor->context);
246       break;
247     case PROP_ACTIVE_COLOR:
248       g_value_set_enum (value, editor->active_color);
249       break;
250     default:
251       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
252       break;
253     }
254 }
255 
256 static void
gimp_fg_bg_editor_style_set(GtkWidget * widget,GtkStyle * prev_style)257 gimp_fg_bg_editor_style_set (GtkWidget *widget,
258                              GtkStyle  *prev_style)
259 {
260   GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (widget);
261 
262   GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
263 
264   g_clear_object (&editor->default_icon);
265   g_clear_object (&editor->swap_icon);
266 }
267 
268 static gboolean
gimp_fg_bg_editor_expose(GtkWidget * widget,GdkEventExpose * eevent)269 gimp_fg_bg_editor_expose (GtkWidget      *widget,
270                           GdkEventExpose *eevent)
271 {
272   GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (widget);
273   cairo_t        *cr;
274   GtkAllocation   allocation;
275   gint            width, height;
276   gint            default_w, default_h;
277   gint            swap_w, swap_h;
278   gint            rect_w, rect_h;
279   GimpRGB         color;
280 
281   if (! gtk_widget_is_drawable (widget))
282     return FALSE;
283 
284   cr = gdk_cairo_create (eevent->window);
285   gdk_cairo_region (cr, eevent->region);
286   cairo_clip (cr);
287 
288   gtk_widget_get_allocation (widget, &allocation);
289 
290   width  = allocation.width;
291   height = allocation.height;
292 
293   cairo_translate (cr, allocation.x, allocation.y);
294 
295   /*  draw the default colors pixbuf  */
296   if (! editor->default_icon)
297     editor->default_icon = gimp_widget_load_icon (widget,
298                                                   GIMP_ICON_COLORS_DEFAULT, 12);
299 
300   default_w = gdk_pixbuf_get_width  (editor->default_icon);
301   default_h = gdk_pixbuf_get_height (editor->default_icon);
302 
303   if (default_w < width / 2 && default_h < height / 2)
304     {
305       gdk_cairo_set_source_pixbuf (cr, editor->default_icon,
306                                    0, height - default_h);
307       cairo_paint (cr);
308     }
309   else
310     {
311       default_w = default_h = 0;
312     }
313 
314   /*  draw the swap colors pixbuf  */
315   if (! editor->swap_icon)
316     editor->swap_icon = gimp_widget_load_icon (widget,
317                                                GIMP_ICON_COLORS_SWAP, 12);
318 
319   swap_w = gdk_pixbuf_get_width  (editor->swap_icon);
320   swap_h = gdk_pixbuf_get_height (editor->swap_icon);
321 
322   if (swap_w < width / 2 && swap_h < height / 2)
323     {
324       gdk_cairo_set_source_pixbuf (cr, editor->swap_icon,
325                                    width - swap_w, 0);
326       cairo_paint (cr);
327     }
328   else
329     {
330       swap_w = swap_h = 0;
331     }
332 
333   rect_h = height - MAX (default_h, swap_h) - 2;
334   rect_w = width  - MAX (default_w, swap_w) - 4;
335 
336   if (rect_h > (height * 3 / 4))
337     rect_w = MAX (rect_w - (rect_h - ((height * 3 / 4))),
338                   width * 2 / 3);
339 
340   editor->rect_width  = rect_w;
341   editor->rect_height = rect_h;
342 
343   if (! editor->transform)
344     gimp_fg_bg_editor_create_transform (editor);
345 
346   if (editor->context)
347     {
348       /*  draw the background frame  */
349       gimp_context_get_background (editor->context, &color);
350       gimp_fg_bg_editor_draw_color_frame (editor, cr, &color,
351                                           width - rect_w, height - rect_h,
352                                           rect_w,         rect_h,
353                                           +1,             +1);
354 
355 
356       /*  draw the foreground frame  */
357       gimp_context_get_foreground (editor->context, &color);
358       gimp_fg_bg_editor_draw_color_frame (editor, cr, &color,
359                                           0,              0,
360                                           rect_w,         rect_h,
361                                           -1,             -1);
362     }
363 
364   cairo_destroy (cr);
365 
366   return TRUE;
367 }
368 
369 static GimpFgBgTarget
gimp_fg_bg_editor_target(GimpFgBgEditor * editor,gint x,gint y)370 gimp_fg_bg_editor_target (GimpFgBgEditor *editor,
371                           gint            x,
372                           gint            y)
373 {
374   GtkAllocation allocation;
375   gint          width;
376   gint          height;
377   gint          rect_w = editor->rect_width;
378   gint          rect_h = editor->rect_height;
379 
380   gtk_widget_get_allocation (GTK_WIDGET (editor), &allocation);
381 
382   width  = allocation.width;
383   height = allocation.height;
384 
385   if (x > 0 && x < rect_w && y > 0 && y < rect_h)
386     {
387       return GIMP_FG_BG_TARGET_FOREGROUND;
388     }
389   else if (x > (width - rect_w)  && x < width  &&
390            y > (height - rect_h) && y < height)
391     {
392       return GIMP_FG_BG_TARGET_BACKGROUND;
393     }
394   else if (x > 0      && x < (width - rect_w) &&
395            y > rect_h && y < height)
396     {
397       return GIMP_FG_BG_TARGET_DEFAULT;
398     }
399   else if (x > rect_w && x < width &&
400            y > 0      && y < (height - rect_h))
401     {
402       return GIMP_FG_BG_TARGET_SWAP;
403     }
404 
405   return GIMP_FG_BG_TARGET_INVALID;
406 }
407 
408 static gboolean
gimp_fg_bg_editor_button_press(GtkWidget * widget,GdkEventButton * bevent)409 gimp_fg_bg_editor_button_press (GtkWidget      *widget,
410                                 GdkEventButton *bevent)
411 {
412   GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (widget);
413 
414   if (bevent->button == 1 && bevent->type == GDK_BUTTON_PRESS)
415     {
416       GimpFgBgTarget target = gimp_fg_bg_editor_target (editor,
417                                                         bevent->x, bevent->y);
418 
419       editor->click_target = GIMP_FG_BG_TARGET_INVALID;
420 
421       switch (target)
422         {
423         case GIMP_FG_BG_TARGET_FOREGROUND:
424           if (editor->active_color != GIMP_ACTIVE_COLOR_FOREGROUND)
425             gimp_fg_bg_editor_set_active (editor,
426                                           GIMP_ACTIVE_COLOR_FOREGROUND);
427           editor->click_target = GIMP_FG_BG_TARGET_FOREGROUND;
428           break;
429 
430         case GIMP_FG_BG_TARGET_BACKGROUND:
431           if (editor->active_color != GIMP_ACTIVE_COLOR_BACKGROUND)
432             gimp_fg_bg_editor_set_active (editor,
433                                           GIMP_ACTIVE_COLOR_BACKGROUND);
434           editor->click_target = GIMP_FG_BG_TARGET_BACKGROUND;
435           break;
436 
437         case GIMP_FG_BG_TARGET_SWAP:
438           if (editor->context)
439             gimp_context_swap_colors (editor->context);
440           break;
441 
442         case GIMP_FG_BG_TARGET_DEFAULT:
443           if (editor->context)
444             gimp_context_set_default_colors (editor->context);
445           break;
446 
447         default:
448           break;
449         }
450     }
451 
452   return FALSE;
453 }
454 
455 static gboolean
gimp_fg_bg_editor_button_release(GtkWidget * widget,GdkEventButton * bevent)456 gimp_fg_bg_editor_button_release (GtkWidget      *widget,
457                                   GdkEventButton *bevent)
458 {
459   GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (widget);
460 
461   if (bevent->button == 1)
462     {
463       GimpFgBgTarget target = gimp_fg_bg_editor_target (editor,
464                                                         bevent->x, bevent->y);
465 
466       if (target == editor->click_target)
467         {
468           switch (target)
469             {
470             case GIMP_FG_BG_TARGET_FOREGROUND:
471               g_signal_emit (editor, editor_signals[COLOR_CLICKED], 0,
472                              GIMP_ACTIVE_COLOR_FOREGROUND);
473               break;
474 
475             case GIMP_FG_BG_TARGET_BACKGROUND:
476               g_signal_emit (editor, editor_signals[COLOR_CLICKED], 0,
477                              GIMP_ACTIVE_COLOR_BACKGROUND);
478               break;
479 
480             default:
481               break;
482             }
483         }
484 
485       editor->click_target = GIMP_FG_BG_TARGET_INVALID;
486     }
487 
488   return FALSE;
489 }
490 
491 static gboolean
gimp_fg_bg_editor_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)492 gimp_fg_bg_editor_drag_motion (GtkWidget      *widget,
493                                GdkDragContext *context,
494                                gint            x,
495                                gint            y,
496                                guint           time)
497 {
498   GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (widget);
499   GimpFgBgTarget  target = gimp_fg_bg_editor_target (editor, x, y);
500 
501   if (target == GIMP_FG_BG_TARGET_FOREGROUND ||
502       target == GIMP_FG_BG_TARGET_BACKGROUND)
503     {
504       gdk_drag_status (context, GDK_ACTION_COPY, time);
505 
506       return TRUE;
507     }
508 
509   gdk_drag_status (context, 0, time);
510 
511   return FALSE;
512 }
513 
514 static gboolean
gimp_fg_bg_editor_query_tooltip(GtkWidget * widget,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip)515 gimp_fg_bg_editor_query_tooltip (GtkWidget  *widget,
516                                  gint        x,
517                                  gint        y,
518                                  gboolean    keyboard_mode,
519                                  GtkTooltip *tooltip)
520 {
521   if (! keyboard_mode)
522     {
523       GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (widget);
524       GimpFgBgTarget  target = gimp_fg_bg_editor_target (editor, x, y);
525 
526       if (target != GIMP_FG_BG_TARGET_INVALID)
527         {
528           g_signal_emit (widget, editor_signals[TOOLTIP], 0,
529                          target, tooltip);
530 
531           return TRUE;
532         }
533     }
534 
535   return FALSE;
536 }
537 
538 
539 /*  public functions  */
540 
541 GtkWidget *
gimp_fg_bg_editor_new(GimpContext * context)542 gimp_fg_bg_editor_new (GimpContext *context)
543 {
544   g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
545 
546   return g_object_new (GIMP_TYPE_FG_BG_EDITOR,
547                        "context", context,
548                        NULL);
549 }
550 
551 void
gimp_fg_bg_editor_set_context(GimpFgBgEditor * editor,GimpContext * context)552 gimp_fg_bg_editor_set_context (GimpFgBgEditor *editor,
553                                GimpContext    *context)
554 {
555   g_return_if_fail (GIMP_IS_FG_BG_EDITOR (editor));
556   g_return_if_fail (context == NULL || GIMP_IS_CONTEXT (context));
557 
558   if (context != editor->context)
559     {
560       if (editor->context)
561         {
562           g_signal_handlers_disconnect_by_func (editor->context,
563                                                 gtk_widget_queue_draw,
564                                                 editor);
565           g_signal_handlers_disconnect_by_func (editor->context,
566                                                 G_CALLBACK (gimp_fg_bg_editor_image_changed),
567                                                 editor);
568           g_object_unref (editor->context);
569 
570           g_signal_handlers_disconnect_by_func (editor->color_config,
571                                                 gimp_fg_bg_editor_destroy_transform,
572                                                 editor);
573           g_clear_object (&editor->color_config);
574         }
575 
576       editor->context = context;
577 
578       if (context)
579         {
580           g_object_ref (context);
581 
582           g_signal_connect_swapped (context, "foreground-changed",
583                                     G_CALLBACK (gtk_widget_queue_draw),
584                                     editor);
585           g_signal_connect_swapped (context, "background-changed",
586                                     G_CALLBACK (gtk_widget_queue_draw),
587                                     editor);
588           g_signal_connect_swapped (context, "image-changed",
589                                     G_CALLBACK (gimp_fg_bg_editor_image_changed),
590                                     editor);
591 
592           editor->color_config = g_object_ref (context->gimp->config->color_management);
593 
594           g_signal_connect_swapped (editor->color_config, "notify",
595                                     G_CALLBACK (gimp_fg_bg_editor_destroy_transform),
596                                     editor);
597         }
598 
599       gimp_fg_bg_editor_destroy_transform (editor);
600 
601       g_object_notify (G_OBJECT (editor), "context");
602     }
603 }
604 
605 void
gimp_fg_bg_editor_set_active(GimpFgBgEditor * editor,GimpActiveColor active)606 gimp_fg_bg_editor_set_active (GimpFgBgEditor  *editor,
607                               GimpActiveColor  active)
608 {
609   g_return_if_fail (GIMP_IS_FG_BG_EDITOR (editor));
610 
611   editor->active_color = active;
612   gtk_widget_queue_draw (GTK_WIDGET (editor));
613   g_object_notify (G_OBJECT (editor), "active-color");
614 }
615 
616 
617 /*  private functions  */
618 
619 static void
gimp_fg_bg_editor_drag_color(GtkWidget * widget,GimpRGB * color,gpointer data)620 gimp_fg_bg_editor_drag_color (GtkWidget *widget,
621                               GimpRGB   *color,
622                               gpointer   data)
623 {
624   GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (widget);
625 
626   if (editor->context)
627     {
628       switch (editor->active_color)
629         {
630         case GIMP_ACTIVE_COLOR_FOREGROUND:
631           gimp_context_get_foreground (editor->context, color);
632           break;
633 
634         case GIMP_ACTIVE_COLOR_BACKGROUND:
635           gimp_context_get_background (editor->context, color);
636           break;
637         }
638     }
639 }
640 
641 static void
gimp_fg_bg_editor_drop_color(GtkWidget * widget,gint x,gint y,const GimpRGB * color,gpointer data)642 gimp_fg_bg_editor_drop_color (GtkWidget     *widget,
643                               gint           x,
644                               gint           y,
645                               const GimpRGB *color,
646                               gpointer       data)
647 {
648   GimpFgBgEditor *editor = GIMP_FG_BG_EDITOR (widget);
649 
650   if (editor->context)
651     {
652       switch (gimp_fg_bg_editor_target (editor, x, y))
653         {
654         case GIMP_FG_BG_TARGET_FOREGROUND:
655           gimp_context_set_foreground (editor->context, color);
656           break;
657 
658         case GIMP_FG_BG_TARGET_BACKGROUND:
659           gimp_context_set_background (editor->context, color);
660           break;
661 
662         default:
663           break;
664         }
665     }
666 }
667 
668 static void
gimp_fg_bg_editor_create_transform(GimpFgBgEditor * editor)669 gimp_fg_bg_editor_create_transform (GimpFgBgEditor *editor)
670 {
671   if (editor->color_config)
672     {
673       static GimpColorProfile *profile = NULL;
674 
675       if (G_UNLIKELY (! profile))
676         profile = gimp_color_profile_new_rgb_srgb ();
677 
678       editor->transform =
679         gimp_widget_get_color_transform (GTK_WIDGET (editor),
680                                          editor->color_config,
681                                          profile,
682                                          babl_format ("R'G'B'A double"),
683                                          babl_format ("R'G'B'A double"));
684     }
685 }
686 
687 static void
gimp_fg_bg_editor_destroy_transform(GimpFgBgEditor * editor)688 gimp_fg_bg_editor_destroy_transform (GimpFgBgEditor *editor)
689 {
690   g_clear_object (&editor->transform);
691 
692   gtk_widget_queue_draw (GTK_WIDGET (editor));
693 }
694 
695 static void
gimp_fg_bg_editor_image_changed(GimpFgBgEditor * editor,GimpImage * image)696 gimp_fg_bg_editor_image_changed (GimpFgBgEditor *editor,
697                                  GimpImage      *image)
698 {
699   gtk_widget_queue_draw (GTK_WIDGET (editor));
700 
701   if (editor->active_image)
702     {
703       g_signal_handlers_disconnect_by_func (editor->active_image,
704                                             G_CALLBACK (gtk_widget_queue_draw),
705                                             editor);
706       if (gimp_image_get_base_type (editor->active_image) == GIMP_INDEXED)
707         {
708           GimpPalette *palette;
709 
710           palette = gimp_image_get_colormap_palette (editor->active_image);
711           g_signal_handlers_disconnect_by_func (palette,
712                                                 G_CALLBACK (gtk_widget_queue_draw),
713                                                 editor);
714         }
715     }
716   editor->active_image = image;
717   if (image)
718     {
719       g_signal_connect_swapped (image, "notify::base-type",
720                                 G_CALLBACK (gtk_widget_queue_draw),
721                                 editor);
722       g_signal_connect_swapped (image, "colormap-changed",
723                                 G_CALLBACK (gtk_widget_queue_draw),
724                                 editor);
725 
726       if (gimp_image_get_base_type (image) == GIMP_INDEXED)
727         {
728           GimpPalette *palette;
729 
730           palette = gimp_image_get_colormap_palette (editor->active_image);
731           g_signal_connect_swapped (palette, "dirty",
732                                     G_CALLBACK (gtk_widget_queue_draw),
733                                     editor);
734         }
735     }
736 }
737 
738 static void
gimp_fg_bg_editor_draw_color_frame(GimpFgBgEditor * editor,cairo_t * cr,const GimpRGB * color,gint x,gint y,gint width,gint height,gint corner_dx,gint corner_dy)739 gimp_fg_bg_editor_draw_color_frame (GimpFgBgEditor *editor,
740                                     cairo_t        *cr,
741                                     const GimpRGB  *color,
742                                     gint            x,
743                                     gint            y,
744                                     gint            width,
745                                     gint            height,
746                                     gint            corner_dx,
747                                     gint            corner_dy)
748 {
749   GimpPalette       *colormap_palette = NULL;
750   GimpImageBaseType  base_type        = GIMP_RGB;
751   GimpRGB            transformed_color;
752 
753   if (editor->active_image)
754     {
755       base_type = gimp_image_get_base_type (editor->active_image);
756 
757       if (base_type == GIMP_INDEXED)
758         {
759           colormap_palette = gimp_image_get_colormap_palette (
760             editor->active_image);
761         }
762     }
763 
764   if (editor->transform)
765     {
766       gimp_color_transform_process_pixels (editor->transform,
767                                            babl_format ("R'G'B'A double"),
768                                            color,
769                                            babl_format ("R'G'B'A double"),
770                                            &transformed_color,
771                                            1);
772     }
773   else
774     {
775       transformed_color = *color;
776     }
777 
778   cairo_save (cr);
779 
780   gimp_cairo_set_source_rgb (cr, &transformed_color);
781 
782   cairo_rectangle (cr, x, y, width, height);
783   cairo_fill (cr);
784 
785   if (editor->color_config &&
786       /* Common out-of-gamut case */
787       ((color->r < 0.0 || color->r > 1.0 ||
788         color->g < 0.0 || color->g > 1.0 ||
789         color->b < 0.0 || color->b > 1.0) ||
790        /* Indexed images */
791        (colormap_palette &&
792         ! gimp_palette_find_entry (colormap_palette, color, NULL)) ||
793        /* Grayscale images */
794        (base_type == GIMP_GRAY &&
795         (ABS (color->r - color->g) > CHANNEL_EPSILON ||
796          ABS (color->r - color->b) > CHANNEL_EPSILON ||
797          ABS (color->g - color->b) > CHANNEL_EPSILON))))
798     {
799       gint corner_x = x + 0.5 * (1.0 + corner_dx) * width;
800       gint corner_y = y + 0.5 * (1.0 + corner_dy) * height;
801       gint side     = MIN (width, height) * 2 / 3;
802 
803       cairo_move_to (cr, corner_x, corner_y);
804       cairo_line_to (cr, corner_x + side * corner_dx, corner_y);
805       cairo_line_to (cr, corner_x, corner_y + side * corner_dy);
806       cairo_close_path (cr);
807 
808       gimp_cairo_set_source_rgb (cr,
809                                  &editor->color_config->out_of_gamut_color);
810       cairo_fill (cr);
811     }
812 
813   cairo_set_line_width (cr, 1.0);
814 
815   cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
816   cairo_rectangle (cr, x + 0.5, y + 0.5, width - 1.0, height - 1.0);
817   cairo_stroke (cr);
818 
819   cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
820   cairo_rectangle (cr, x + 1.5, y + 1.5, width - 3.0, height - 3.0);
821   cairo_stroke (cr);
822 
823   cairo_restore (cr);
824 }
825