1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpwidgets-utils.c
5  * Copyright (C) 1999-2003 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 #ifdef GDK_WINDOWING_WIN32
29 #include <gdk/gdkwin32.h>
30 #endif
31 
32 #ifdef GDK_WINDOWING_X11
33 #include <gdk/gdkx.h>
34 #endif
35 
36 #ifdef PLATFORM_OSX
37 #include <ApplicationServices/ApplicationServices.h>
38 #endif
39 
40 #include "libgimpbase/gimpbase.h"
41 #include "libgimpconfig/gimpconfig.h"
42 #include "libgimpmath/gimpmath.h"
43 #include "libgimpcolor/gimpcolor.h"
44 #include "libgimpwidgets/gimpwidgets.h"
45 
46 #include "widgets-types.h"
47 
48 #include "gegl/gimp-babl.h"
49 
50 #include "gimpaction.h"
51 #include "gimpdialogfactory.h"
52 #include "gimpdock.h"
53 #include "gimpdockcontainer.h"
54 #include "gimpdockwindow.h"
55 #include "gimperrordialog.h"
56 #include "gimpsessioninfo.h"
57 #include "gimpwidgets-utils.h"
58 
59 #include "gimp-intl.h"
60 
61 
62 #define GIMP_TOOL_OPTIONS_GUI_KEY      "gimp-tool-options-gui"
63 #define GIMP_TOOL_OPTIONS_GUI_FUNC_KEY "gimp-tool-options-gui-func"
64 
65 
66 /**
67  * gimp_menu_position:
68  * @menu: a #GtkMenu widget
69  * @x: pointer to horizontal position
70  * @y: pointer to vertical position
71  *
72  * Positions a #GtkMenu so that it pops up on screen.  This function
73  * takes care of the preferred popup direction (taken from the widget
74  * render direction) and it handles multiple monitors representing a
75  * single #GdkScreen (Xinerama).
76  *
77  * You should call this function with @x and @y initialized to the
78  * origin of the menu. This is typically the center of the widget the
79  * menu is popped up from. gimp_menu_position() will then decide if
80  * and how these initial values need to be changed.
81  **/
82 void
gimp_menu_position(GtkMenu * menu,gint * x,gint * y)83 gimp_menu_position (GtkMenu *menu,
84                     gint    *x,
85                     gint    *y)
86 {
87   GtkWidget      *widget;
88   GdkScreen      *screen;
89   GtkRequisition  requisition;
90   GdkRectangle    rect;
91   gint            monitor;
92 
93   g_return_if_fail (GTK_IS_MENU (menu));
94   g_return_if_fail (x != NULL);
95   g_return_if_fail (y != NULL);
96 
97   widget = GTK_WIDGET (menu);
98 
99   screen = gtk_widget_get_screen (widget);
100 
101   monitor = gdk_screen_get_monitor_at_point (screen, *x, *y);
102   gdk_screen_get_monitor_workarea (screen, monitor, &rect);
103 
104   gtk_menu_set_screen (menu, screen);
105 
106   gtk_widget_size_request (widget, &requisition);
107 
108   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
109     {
110       *x -= requisition.width;
111       if (*x < rect.x)
112         *x += requisition.width;
113     }
114   else
115     {
116       if (*x + requisition.width > rect.x + rect.width)
117         *x -= requisition.width;
118     }
119 
120   if (*x < rect.x)
121     *x = rect.x;
122 
123   if (*y + requisition.height > rect.y + rect.height)
124     *y -= requisition.height;
125 
126   if (*y < rect.y)
127     *y = rect.y;
128 }
129 
130 /**
131  * gimp_button_menu_position:
132  * @button: a button widget to popup the menu from
133  * @menu: the menu to position
134  * @position: the preferred popup direction for the menu (left or right)
135  * @x: return location for x coordinate
136  * @y: return location for y coordinate
137  *
138  * Utility function to position a menu that pops up from a button.
139  **/
140 void
gimp_button_menu_position(GtkWidget * button,GtkMenu * menu,GtkPositionType position,gint * x,gint * y)141 gimp_button_menu_position (GtkWidget       *button,
142                            GtkMenu         *menu,
143                            GtkPositionType  position,
144                            gint            *x,
145                            gint            *y)
146 {
147   GdkScreen      *screen;
148   GtkAllocation   button_allocation;
149   GtkRequisition  menu_requisition;
150   GdkRectangle    rect;
151   gint            monitor;
152 
153   g_return_if_fail (GTK_IS_WIDGET (button));
154   g_return_if_fail (gtk_widget_get_realized (button));
155   g_return_if_fail (GTK_IS_MENU (menu));
156   g_return_if_fail (x != NULL);
157   g_return_if_fail (y != NULL);
158 
159   gtk_widget_get_allocation (button, &button_allocation);
160 
161   if (gtk_widget_get_direction (button) == GTK_TEXT_DIR_RTL)
162     {
163       switch (position)
164         {
165         case GTK_POS_LEFT:   position = GTK_POS_RIGHT;  break;
166         case GTK_POS_RIGHT:  position = GTK_POS_LEFT;   break;
167         default:
168           break;
169         }
170     }
171 
172   *x = 0;
173   *y = 0;
174 
175   if (! gtk_widget_get_has_window (button))
176     {
177       *x += button_allocation.x;
178       *y += button_allocation.y;
179     }
180 
181   gdk_window_get_root_coords (gtk_widget_get_window (button), *x, *y, x, y);
182 
183   gtk_widget_size_request (GTK_WIDGET (menu), &menu_requisition);
184 
185   screen = gtk_widget_get_screen (button);
186 
187   monitor = gdk_screen_get_monitor_at_point (screen, *x, *y);
188   gdk_screen_get_monitor_workarea (screen, monitor, &rect);
189 
190   gtk_menu_set_screen (menu, screen);
191 
192   switch (position)
193     {
194     case GTK_POS_LEFT:
195       *x -= menu_requisition.width;
196       if (*x < rect.x)
197         *x += menu_requisition.width + button_allocation.width;
198       break;
199 
200     case GTK_POS_RIGHT:
201       *x += button_allocation.width;
202       if (*x + menu_requisition.width > rect.x + rect.width)
203         *x -= button_allocation.width + menu_requisition.width;
204       break;
205 
206     default:
207       g_warning ("%s: unhandled position (%d)", G_STRFUNC, position);
208       break;
209     }
210 
211   if (*y + menu_requisition.height > rect.y + rect.height)
212     *y -= menu_requisition.height - button_allocation.height;
213 
214   if (*y < rect.y)
215     *y = rect.y;
216 }
217 
218 void
gimp_table_attach_icon(GtkTable * table,gint row,const gchar * icon_name,GtkWidget * widget,gint colspan,gboolean left_align)219 gimp_table_attach_icon (GtkTable    *table,
220                         gint         row,
221                         const gchar *icon_name,
222                         GtkWidget   *widget,
223                         gint         colspan,
224                         gboolean     left_align)
225 {
226   GtkWidget *image;
227 
228   g_return_if_fail (GTK_IS_TABLE (table));
229   g_return_if_fail (icon_name != NULL);
230   g_return_if_fail (GTK_IS_WIDGET (widget));
231 
232   image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON);
233   gtk_misc_set_alignment (GTK_MISC (image), 1.0, 0.5);
234   gtk_table_attach (table, image, 0, 1, row, row + 1,
235                     GTK_FILL, GTK_FILL, 0, 0);
236   gtk_widget_show (image);
237 
238   if (left_align)
239     {
240       GtkWidget *hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
241 
242       gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
243       gtk_widget_show (widget);
244 
245       widget = hbox;
246     }
247 
248   gtk_table_attach (table, widget, 1, 1 + colspan, row, row + 1,
249                     GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
250   gtk_widget_show (widget);
251 }
252 
253 void
gimp_enum_radio_box_add(GtkBox * box,GtkWidget * widget,gint enum_value,gboolean below)254 gimp_enum_radio_box_add (GtkBox    *box,
255                          GtkWidget *widget,
256                          gint       enum_value,
257                          gboolean   below)
258 {
259   GList *children;
260   GList *list;
261   gint   pos;
262 
263   g_return_if_fail (GTK_IS_BOX (box));
264   g_return_if_fail (GTK_IS_WIDGET (widget));
265 
266   children = gtk_container_get_children (GTK_CONTAINER (box));
267 
268   for (list = children, pos = 1;
269        list;
270        list = g_list_next (list), pos++)
271     {
272       if (GTK_IS_RADIO_BUTTON (list->data) &&
273           GPOINTER_TO_INT (g_object_get_data (list->data, "gimp-item-data")) ==
274           enum_value)
275         {
276           GtkWidget *radio = list->data;
277           GtkWidget *hbox;
278 
279           hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
280           gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 0);
281           gtk_box_reorder_child (GTK_BOX (box), hbox, pos);
282 
283           if (below)
284             {
285               GtkWidget *spacer;
286               gint       indicator_size;
287               gint       indicator_spacing;
288               gint       focus_width;
289               gint       focus_padding;
290               gint       border_width;
291 
292               gtk_widget_style_get (radio,
293                                     "indicator-size",    &indicator_size,
294                                     "indicator-spacing", &indicator_spacing,
295                                     "focus-line-width",  &focus_width,
296                                     "focus-padding",     &focus_padding,
297                                     NULL);
298 
299               border_width = gtk_container_get_border_width (GTK_CONTAINER (radio));
300 
301               spacer = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
302               gtk_widget_set_size_request (spacer,
303                                            indicator_size +
304                                            3 * indicator_spacing +
305                                            focus_width +
306                                            focus_padding +
307                                            border_width,
308                                            -1);
309               gtk_box_pack_start (GTK_BOX (hbox), spacer, FALSE, FALSE, 0);
310               gtk_widget_show (spacer);
311             }
312           else
313             {
314               GtkSizeGroup *size_group;
315 
316               size_group = g_object_get_data (G_OBJECT (box), "size-group");
317 
318               if (! size_group)
319                 {
320                   size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
321                   g_object_set_data (G_OBJECT (box), "size-group", size_group);
322 
323                   gtk_size_group_add_widget (size_group, radio);
324                   g_object_unref (size_group);
325                 }
326               else
327                 {
328                   gtk_size_group_add_widget (size_group, radio);
329                 }
330 
331               gtk_box_set_spacing (GTK_BOX (hbox), 4);
332 
333               g_object_ref (radio);
334               gtk_container_remove (GTK_CONTAINER (box), radio);
335               gtk_box_pack_start (GTK_BOX (hbox), radio, FALSE, FALSE, 0);
336               g_object_unref (radio);
337             }
338 
339           gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
340           gtk_widget_show (widget);
341 
342           g_object_bind_property (radio,  "active",
343                                   widget, "sensitive",
344                                   G_BINDING_SYNC_CREATE);
345 
346           gtk_widget_show (hbox);
347 
348           break;
349         }
350     }
351 
352   g_list_free (children);
353 }
354 
355 void
gimp_enum_radio_frame_add(GtkFrame * frame,GtkWidget * widget,gint enum_value,gboolean below)356 gimp_enum_radio_frame_add (GtkFrame  *frame,
357                            GtkWidget *widget,
358                            gint       enum_value,
359                            gboolean   below)
360 {
361   GtkWidget *box;
362 
363   g_return_if_fail (GTK_IS_FRAME (frame));
364   g_return_if_fail (GTK_IS_WIDGET (widget));
365 
366   box = gtk_bin_get_child (GTK_BIN (frame));
367 
368   g_return_if_fail (GTK_IS_BOX (box));
369 
370   gimp_enum_radio_box_add (GTK_BOX (box), widget, enum_value, below);
371 }
372 
373 /**
374  * gimp_widget_load_icon:
375  * @widget:                  parent widget (to determine icon theme and
376  *                           style)
377  * @icon_name:               icon name
378  * @size:                    requested pixel size
379  *
380  * Loads an icon into a pixbuf with size as close as possible to @size.
381  * If icon does not exist or fail to load, the function will fallback to
382  * "gimp-wilber-eek" instead to prevent NULL pixbuf. As a last resort,
383  * if even the fallback failed to load, a magenta @size square will be
384  * returned, so this function is guaranteed to always return a
385  * #GdkPixbuf.
386  *
387  * Return value: a newly allocated #GdkPixbuf containing @icon_name at
388  * size @size or a fallback icon/size.
389  **/
390 GdkPixbuf *
gimp_widget_load_icon(GtkWidget * widget,const gchar * icon_name,gint size)391 gimp_widget_load_icon (GtkWidget   *widget,
392                        const gchar *icon_name,
393                        gint         size)
394 {
395   GdkPixbuf    *pixbuf;
396   GtkIconTheme *icon_theme;
397   gint         *icon_sizes;
398   gint          closest_size = -1;
399   gint          min_diff     = G_MAXINT;
400   gint          i;
401 
402   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
403   g_return_val_if_fail (icon_name != NULL, NULL);
404 
405   icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
406 
407   if (! gtk_icon_theme_has_icon (icon_theme, icon_name))
408     {
409       g_printerr ("WARNING: icon theme has no icon '%s'.\n",
410                   icon_name);
411 
412       return gtk_icon_theme_load_icon (icon_theme, GIMP_ICON_WILBER_EEK,
413                                        size, 0, NULL);
414     }
415 
416   icon_sizes = gtk_icon_theme_get_icon_sizes (icon_theme, icon_name);
417 
418   for (i = 0; icon_sizes[i]; i++)
419     {
420       if (icon_sizes[i] > 0 &&
421           icon_sizes[i] <= size)
422         {
423           if (size - icon_sizes[i] < min_diff)
424             {
425               min_diff     = size - icon_sizes[i];
426               closest_size = icon_sizes[i];
427             }
428         }
429     }
430 
431   g_free (icon_sizes);
432 
433   if (closest_size != -1)
434     size = closest_size;
435 
436   pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name, size,
437                                      GTK_ICON_LOOKUP_USE_BUILTIN, NULL);
438 
439   if (! pixbuf)
440     {
441       /* The icon was seemingly present in the current icon theme, yet
442        * it failed to load. Maybe the file is broken?
443        * As last resort, try to load "gimp-wilber-eek" as fallback.
444        * Note that we are not making more checks, so if the fallback
445        * icon fails to load as well, the function may still return NULL.
446        */
447       g_printerr ("WARNING: icon '%s' failed to load. Check the files "
448                   "in your icon theme.\n", icon_name);
449 
450       pixbuf = gtk_icon_theme_load_icon (icon_theme,
451                                          GIMP_ICON_WILBER_EEK,
452                                          size, 0, NULL);
453       if (! pixbuf)
454         {
455           /* As last resort, just draw an ugly magenta square. */
456           guchar *data;
457           gint    rowstride = 3 * size;
458           gint    i, j;
459 
460           g_printerr ("WARNING: icon '%s' failed to load. Check the files "
461                       "in your icon theme.\n", GIMP_ICON_WILBER_EEK);
462 
463           data = g_new (guchar, rowstride * size);
464           for (i = 0; i < size; i++)
465             {
466               for (j = 0; j < size; j++)
467                 {
468                   data[i * rowstride + j * 3] = 255;
469                   data[i * rowstride + j * 3 + 1] = 0;
470                   data[i * rowstride + j * 3 + 2] = 255;
471                 }
472             }
473           pixbuf = gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB, FALSE,
474                                              8, size, size, rowstride,
475                                              (GdkPixbufDestroyNotify) g_free,
476                                              NULL);
477         }
478     }
479 
480   return pixbuf;
481 }
482 
483 GtkIconSize
gimp_get_icon_size(GtkWidget * widget,const gchar * icon_name,GtkIconSize max_size,gint width,gint height)484 gimp_get_icon_size (GtkWidget   *widget,
485                     const gchar *icon_name,
486                     GtkIconSize  max_size,
487                     gint         width,
488                     gint         height)
489 {
490   GtkIconSet   *icon_set;
491   GtkIconSize  *sizes;
492   gint          n_sizes;
493   gint          i;
494   gint          width_diff  = 1024;
495   gint          height_diff = 1024;
496   gint          max_width;
497   gint          max_height;
498   GtkIconSize   icon_size = GTK_ICON_SIZE_MENU;
499   GtkSettings  *settings;
500 
501   g_return_val_if_fail (GTK_IS_WIDGET (widget), icon_size);
502   g_return_val_if_fail (icon_name != NULL, icon_size);
503   g_return_val_if_fail (width > 0, icon_size);
504   g_return_val_if_fail (height > 0, icon_size);
505 
506   icon_set = gtk_style_lookup_icon_set (gtk_widget_get_style (widget),
507                                         icon_name);
508 
509   if (! icon_set)
510     return GTK_ICON_SIZE_INVALID;
511 
512   settings = gtk_widget_get_settings (widget);
513 
514   if (! gtk_icon_size_lookup_for_settings (settings, max_size,
515                                            &max_width, &max_height))
516     {
517       max_width  = 1024;
518       max_height = 1024;
519     }
520 
521   gtk_icon_set_get_sizes (icon_set, &sizes, &n_sizes);
522 
523   for (i = 0; i < n_sizes; i++)
524     {
525       gint icon_width;
526       gint icon_height;
527 
528       if (gtk_icon_size_lookup_for_settings (settings, sizes[i],
529                                              &icon_width, &icon_height))
530         {
531           if (icon_width  <= width      &&
532               icon_height <= height     &&
533               icon_width  <= max_width  &&
534               icon_height <= max_height &&
535               ((width  - icon_width)  < width_diff ||
536                (height - icon_height) < height_diff))
537             {
538               width_diff  = width  - icon_width;
539               height_diff = height - icon_height;
540 
541               icon_size = sizes[i];
542             }
543         }
544     }
545 
546   g_free (sizes);
547 
548   return icon_size;
549 }
550 
551 GimpTabStyle
gimp_preview_tab_style_to_icon(GimpTabStyle tab_style)552 gimp_preview_tab_style_to_icon (GimpTabStyle tab_style)
553 {
554   switch (tab_style)
555     {
556     case GIMP_TAB_STYLE_PREVIEW:
557       tab_style = GIMP_TAB_STYLE_ICON;
558       break;
559 
560     case GIMP_TAB_STYLE_PREVIEW_NAME:
561       tab_style = GIMP_TAB_STYLE_ICON_NAME;
562       break;
563 
564     case GIMP_TAB_STYLE_PREVIEW_BLURB:
565       tab_style = GIMP_TAB_STYLE_ICON_BLURB;
566       break;
567 
568     default:
569       break;
570     }
571 
572   return tab_style;
573 }
574 
575 const gchar *
gimp_get_mod_string(GdkModifierType modifiers)576 gimp_get_mod_string (GdkModifierType modifiers)
577 {
578   static GHashTable *mod_labels;
579   gchar             *label;
580 
581   if (! modifiers)
582     return NULL;
583 
584   if (G_UNLIKELY (! mod_labels))
585     mod_labels = g_hash_table_new (g_int_hash, g_int_equal);
586 
587   modifiers = gimp_replace_virtual_modifiers (modifiers);
588 
589   label = g_hash_table_lookup (mod_labels, &modifiers);
590 
591   if (! label)
592     {
593       GtkAccelLabelClass *accel_label_class;
594 
595       label = gtk_accelerator_get_label (0, modifiers);
596 
597       accel_label_class = g_type_class_ref (GTK_TYPE_ACCEL_LABEL);
598 
599       if (accel_label_class->mod_separator &&
600           *accel_label_class->mod_separator)
601         {
602           gchar *sep = g_strrstr (label, accel_label_class->mod_separator);
603 
604           if (sep - label ==
605               strlen (label) - strlen (accel_label_class->mod_separator))
606             *sep = '\0';
607         }
608 
609       g_type_class_unref (accel_label_class);
610 
611       g_hash_table_insert (mod_labels,
612                            g_memdup (&modifiers, sizeof (GdkModifierType)),
613                            label);
614     }
615 
616   return label;
617 }
618 
619 #define BUF_SIZE 100
620 /**
621  * gimp_suggest_modifiers:
622  * @message:                 initial text for the message
623  * @modifiers:               bit mask of modifiers that should be suggested
624  * @extend_selection_format: optional format string for the
625  *                           "Extend selection" modifier
626  * @toggle_behavior_format:  optional format string for the
627  *                           "Toggle behavior" modifier
628  * @alt_format:              optional format string for the Alt modifier
629  *
630  * Utility function to build a message suggesting to use some
631  * modifiers for performing different actions (only Shift, Ctrl and
632  * Alt are currently supported).  If some of these modifiers are
633  * already active, they will not be suggested.  The optional format
634  * strings #extend_selection_format, #toggle_behavior_format and
635  * #alt_format may be used to describe what the modifier will do.
636  * They must contain a single '%%s' which will be replaced by the name
637  * of the modifier.  They can also be %NULL if the modifier name
638  * should be left alone.
639  *
640  * Return value: a newly allocated string containing the message.
641  **/
642 gchar *
gimp_suggest_modifiers(const gchar * message,GdkModifierType modifiers,const gchar * extend_selection_format,const gchar * toggle_behavior_format,const gchar * alt_format)643 gimp_suggest_modifiers (const gchar     *message,
644                         GdkModifierType  modifiers,
645                         const gchar     *extend_selection_format,
646                         const gchar     *toggle_behavior_format,
647                         const gchar     *alt_format)
648 {
649   GdkModifierType  extend_mask = gimp_get_extend_selection_mask ();
650   GdkModifierType  toggle_mask = gimp_get_toggle_behavior_mask ();
651   gchar            msg_buf[3][BUF_SIZE];
652   gint             num_msgs = 0;
653   gboolean         try      = FALSE;
654 
655   if (modifiers & extend_mask)
656     {
657       if (extend_selection_format && *extend_selection_format)
658         {
659           g_snprintf (msg_buf[num_msgs], BUF_SIZE, extend_selection_format,
660                       gimp_get_mod_string (extend_mask));
661         }
662       else
663         {
664           g_strlcpy (msg_buf[num_msgs],
665                      gimp_get_mod_string (extend_mask), BUF_SIZE);
666           try = TRUE;
667         }
668 
669       num_msgs++;
670     }
671 
672   if (modifiers & toggle_mask)
673     {
674       if (toggle_behavior_format && *toggle_behavior_format)
675         {
676           g_snprintf (msg_buf[num_msgs], BUF_SIZE, toggle_behavior_format,
677                       gimp_get_mod_string (toggle_mask));
678         }
679       else
680         {
681           g_strlcpy (msg_buf[num_msgs],
682                      gimp_get_mod_string (toggle_mask), BUF_SIZE);
683           try = TRUE;
684         }
685 
686       num_msgs++;
687     }
688 
689   if (modifiers & GDK_MOD1_MASK)
690     {
691       if (alt_format && *alt_format)
692         {
693           g_snprintf (msg_buf[num_msgs], BUF_SIZE, alt_format,
694                       gimp_get_mod_string (GDK_MOD1_MASK));
695         }
696       else
697         {
698           g_strlcpy (msg_buf[num_msgs],
699                      gimp_get_mod_string (GDK_MOD1_MASK), BUF_SIZE);
700           try = TRUE;
701         }
702 
703       num_msgs++;
704     }
705 
706   /* This convoluted way to build the message using multiple format strings
707    * tries to make the messages easier to translate to other languages.
708    */
709 
710   switch (num_msgs)
711     {
712     case 1:
713       return g_strdup_printf (try ? _("%s (try %s)") : _("%s (%s)"),
714                               message, msg_buf[0]);
715 
716     case 2:
717       return g_strdup_printf (_("%s (try %s, %s)"),
718                               message, msg_buf[0], msg_buf[1]);
719 
720     case 3:
721       return g_strdup_printf (_("%s (try %s, %s, %s)"),
722                               message, msg_buf[0], msg_buf[1], msg_buf[2]);
723     }
724 
725   return g_strdup (message);
726 }
727 #undef BUF_SIZE
728 
729 GimpChannelOps
gimp_modifiers_to_channel_op(GdkModifierType modifiers)730 gimp_modifiers_to_channel_op (GdkModifierType  modifiers)
731 {
732   GdkModifierType extend_mask = gimp_get_extend_selection_mask ();
733   GdkModifierType modify_mask = gimp_get_modify_selection_mask ();
734 
735   if (modifiers & extend_mask)
736     {
737       if (modifiers & modify_mask)
738         {
739           return GIMP_CHANNEL_OP_INTERSECT;
740         }
741       else
742         {
743           return GIMP_CHANNEL_OP_ADD;
744         }
745     }
746   else if (modifiers & modify_mask)
747     {
748       return GIMP_CHANNEL_OP_SUBTRACT;
749     }
750 
751   return GIMP_CHANNEL_OP_REPLACE;
752 }
753 
754 GdkModifierType
gimp_replace_virtual_modifiers(GdkModifierType modifiers)755 gimp_replace_virtual_modifiers (GdkModifierType modifiers)
756 {
757   GdkDisplay      *display = gdk_display_get_default ();
758   GdkModifierType  result  = 0;
759   gint             i;
760 
761   for (i = 0; i < 8; i++)
762     {
763       GdkModifierType real = 1 << i;
764 
765       if (modifiers & real)
766         {
767           GdkModifierType virtual = real;
768 
769           gdk_keymap_add_virtual_modifiers (gdk_keymap_get_for_display (display),
770                                             &virtual);
771 
772           if (virtual == real)
773             result |= virtual;
774           else
775             result |= virtual & ~real;
776         }
777     }
778 
779   return result;
780 }
781 
782 GdkModifierType
gimp_get_primary_accelerator_mask(void)783 gimp_get_primary_accelerator_mask (void)
784 {
785   GdkDisplay *display = gdk_display_get_default ();
786 
787   return gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
788                                        GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR);
789 }
790 
791 GdkModifierType
gimp_get_extend_selection_mask(void)792 gimp_get_extend_selection_mask (void)
793 {
794   GdkDisplay *display = gdk_display_get_default ();
795 
796   return gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
797                                        GDK_MODIFIER_INTENT_EXTEND_SELECTION);
798 }
799 
800 GdkModifierType
gimp_get_modify_selection_mask(void)801 gimp_get_modify_selection_mask (void)
802 {
803   GdkDisplay *display = gdk_display_get_default ();
804 
805   return gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
806                                        GDK_MODIFIER_INTENT_MODIFY_SELECTION);
807 }
808 
809 GdkModifierType
gimp_get_toggle_behavior_mask(void)810 gimp_get_toggle_behavior_mask (void)
811 {
812   GdkDisplay *display = gdk_display_get_default ();
813 
814   /* use the modify selection modifier */
815   return gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
816                                        GDK_MODIFIER_INTENT_MODIFY_SELECTION);
817 }
818 
819 GdkModifierType
gimp_get_constrain_behavior_mask(void)820 gimp_get_constrain_behavior_mask (void)
821 {
822   GdkDisplay *display = gdk_display_get_default ();
823 
824   /* use the modify selection modifier */
825   return gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
826                                        GDK_MODIFIER_INTENT_MODIFY_SELECTION);
827 }
828 
829 GdkModifierType
gimp_get_all_modifiers_mask(void)830 gimp_get_all_modifiers_mask (void)
831 {
832   GdkDisplay *display = gdk_display_get_default ();
833 
834   return (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK |
835           gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
836                                         GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR) |
837           gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
838                                         GDK_MODIFIER_INTENT_EXTEND_SELECTION) |
839           gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
840                                         GDK_MODIFIER_INTENT_MODIFY_SELECTION));
841 }
842 
843 /**
844  * gimp_get_monitor_resolution:
845  * @screen: a #GdkScreen
846  * @monitor: a monitor number
847  * @xres: returns the horizontal monitor resolution (in dpi)
848  * @yres: returns the vertical monitor resolution (in dpi)
849  *
850  * Retrieves the monitor's resolution from GDK.
851  **/
852 void
gimp_get_monitor_resolution(GdkScreen * screen,gint monitor,gdouble * xres,gdouble * yres)853 gimp_get_monitor_resolution (GdkScreen *screen,
854                              gint       monitor,
855                              gdouble   *xres,
856                              gdouble   *yres)
857 {
858   GdkRectangle size_pixels;
859   gint         width_mm, height_mm;
860   gdouble      x = 0.0;
861   gdouble      y = 0.0;
862 #ifdef PLATFORM_OSX
863   CGSize       size;
864 #endif
865 
866   g_return_if_fail (GDK_IS_SCREEN (screen));
867   g_return_if_fail (xres != NULL);
868   g_return_if_fail (yres != NULL);
869 
870 #ifndef PLATFORM_OSX
871   gdk_screen_get_monitor_geometry (screen, monitor, &size_pixels);
872 
873   width_mm  = gdk_screen_get_monitor_width_mm  (screen, monitor);
874   height_mm = gdk_screen_get_monitor_height_mm (screen, monitor);
875 #else
876   width_mm  = 0;
877   height_mm = 0;
878   size = CGDisplayScreenSize (kCGDirectMainDisplay);
879   if (!CGSizeEqualToSize (size, CGSizeZero))
880     {
881       width_mm  = size.width;
882       height_mm = size.height;
883     }
884   size_pixels.width  = CGDisplayPixelsWide (kCGDirectMainDisplay);
885   size_pixels.height = CGDisplayPixelsHigh (kCGDirectMainDisplay);
886 #endif
887   /*
888    * From xdpyinfo.c:
889    *
890    * there are 2.54 centimeters to an inch; so there are 25.4 millimeters.
891    *
892    *     dpi = N pixels / (M millimeters / (25.4 millimeters / 1 inch))
893    *         = N pixels / (M inch / 25.4)
894    *         = N * 25.4 pixels / M inch
895    */
896 
897   if (width_mm > 0 && height_mm > 0)
898     {
899       x = (size_pixels.width  * 25.4) / (gdouble) width_mm;
900       y = (size_pixels.height * 25.4) / (gdouble) height_mm;
901     }
902 
903   if (x < GIMP_MIN_RESOLUTION || x > GIMP_MAX_RESOLUTION ||
904       y < GIMP_MIN_RESOLUTION || y > GIMP_MAX_RESOLUTION)
905     {
906       g_printerr ("gimp_get_monitor_resolution(): GDK returned bogus "
907                   "values for the monitor resolution, using 96 dpi instead.\n");
908 
909       x = 96.0;
910       y = 96.0;
911     }
912 
913   /*  round the value to full integers to give more pleasant results  */
914   *xres = ROUND (x);
915   *yres = ROUND (y);
916 }
917 
918 
919 /**
920  * gimp_rgb_get_gdk_color:
921  * @rgb: the source color as #GimpRGB
922  * @gdk_color: pointer to a #GdkColor
923  *
924  * Initializes @gdk_color from a #GimpRGB. This function does not
925  * allocate the color for you. Depending on how you want to use it,
926  * you may have to call gdk_colormap_alloc_color().
927  **/
928 void
gimp_rgb_get_gdk_color(const GimpRGB * rgb,GdkColor * gdk_color)929 gimp_rgb_get_gdk_color (const GimpRGB *rgb,
930                         GdkColor      *gdk_color)
931 {
932   guchar r, g, b;
933 
934   g_return_if_fail (rgb != NULL);
935   g_return_if_fail (gdk_color != NULL);
936 
937   gimp_rgb_get_uchar (rgb, &r, &g, &b);
938 
939   gdk_color->red   = (r << 8) | r;
940   gdk_color->green = (g << 8) | g;
941   gdk_color->blue  = (b << 8) | b;
942 }
943 
944 /**
945  * gimp_rgb_set_gdk_color:
946  * @rgb: a #GimpRGB that is to be set
947  * @gdk_color: pointer to the source #GdkColor
948  *
949  * Initializes @rgb from a #GdkColor. This function does not touch
950  * the alpha value of @rgb.
951  **/
952 void
gimp_rgb_set_gdk_color(GimpRGB * rgb,const GdkColor * gdk_color)953 gimp_rgb_set_gdk_color (GimpRGB        *rgb,
954                         const GdkColor *gdk_color)
955 {
956   guchar r, g, b;
957 
958   g_return_if_fail (rgb != NULL);
959   g_return_if_fail (gdk_color != NULL);
960 
961   r = gdk_color->red   >> 8;
962   g = gdk_color->green >> 8;
963   b = gdk_color->blue  >> 8;
964 
965   gimp_rgb_set_uchar (rgb, r, g, b);
966 }
967 
968 void
gimp_window_set_hint(GtkWindow * window,GimpWindowHint hint)969 gimp_window_set_hint (GtkWindow      *window,
970                       GimpWindowHint  hint)
971 {
972   g_return_if_fail (GTK_IS_WINDOW (window));
973 
974   switch (hint)
975     {
976     case GIMP_WINDOW_HINT_NORMAL:
977       gtk_window_set_type_hint (window, GDK_WINDOW_TYPE_HINT_NORMAL);
978       break;
979 
980     case GIMP_WINDOW_HINT_UTILITY:
981       gtk_window_set_type_hint (window, GDK_WINDOW_TYPE_HINT_UTILITY);
982       break;
983 
984     case GIMP_WINDOW_HINT_KEEP_ABOVE:
985       gtk_window_set_keep_above (window, TRUE);
986       break;
987     }
988 }
989 
990 /**
991  * gimp_window_get_native_id:
992  * @window: a #GtkWindow
993  *
994  * This function is used to pass a window handle to plug-ins so that
995  * they can set their dialog windows transient to the parent window.
996  *
997  * Return value: a native window ID of the window's #GdkWindow or 0
998  *               if the window isn't realized yet
999  */
1000 guint32
gimp_window_get_native_id(GtkWindow * window)1001 gimp_window_get_native_id (GtkWindow *window)
1002 {
1003   g_return_val_if_fail (GTK_IS_WINDOW (window), 0);
1004 
1005 #ifdef GDK_NATIVE_WINDOW_POINTER
1006 #ifdef __GNUC__
1007 #warning gimp_window_get_native() unimplementable for the target windowing system
1008 #endif
1009   return 0;
1010 #endif
1011 
1012 #ifdef GDK_WINDOWING_WIN32
1013   if (window && gtk_widget_get_realized (GTK_WIDGET (window)))
1014     return GDK_WINDOW_HWND (gtk_widget_get_window (GTK_WIDGET (window)));
1015 #endif
1016 
1017 #ifdef GDK_WINDOWING_X11
1018   if (window && gtk_widget_get_realized (GTK_WIDGET (window)))
1019     return GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (window)));
1020 #endif
1021 
1022   return 0;
1023 }
1024 
1025 static void
gimp_window_transient_realized(GtkWidget * window,GdkWindow * parent)1026 gimp_window_transient_realized (GtkWidget *window,
1027                                 GdkWindow *parent)
1028 {
1029   if (gtk_widget_get_realized (window))
1030     gdk_window_set_transient_for (gtk_widget_get_window (window), parent);
1031 }
1032 
1033 /* similar to what we have in libgimp/gimpui.c */
1034 static GdkWindow *
gimp_get_foreign_window(guint32 window)1035 gimp_get_foreign_window (guint32 window)
1036 {
1037 #ifdef GDK_WINDOWING_X11
1038   return gdk_x11_window_foreign_new_for_display (gdk_display_get_default (),
1039                                                  window);
1040 #endif
1041 
1042 #ifdef GDK_WINDOWING_WIN32
1043   return gdk_win32_window_foreign_new_for_display (gdk_display_get_default (),
1044                                                    window);
1045 #endif
1046 
1047   return NULL;
1048 }
1049 
1050 void
gimp_window_set_transient_for(GtkWindow * window,guint32 parent_ID)1051 gimp_window_set_transient_for (GtkWindow *window,
1052                                guint32    parent_ID)
1053 {
1054   /* Cross-process transient-for is broken in gdk/win32 <= 2.10.6. It
1055    * causes hangs, at least when used as by the gimp and script-fu
1056    * processes. In some newer GTK+ version it will be fixed to be a
1057    * no-op. If it eventually is fixed to actually work, change this to
1058    * a run-time check of GTK+ version. Remember to change also the
1059    * function with the same name in libgimp/gimpui.c
1060    */
1061 #ifndef GDK_WINDOWING_WIN32
1062   GdkWindow *parent;
1063 
1064   parent = gimp_get_foreign_window (parent_ID);
1065   if (! parent)
1066     return;
1067 
1068   if (gtk_widget_get_realized (GTK_WIDGET (window)))
1069     gdk_window_set_transient_for (gtk_widget_get_window (GTK_WIDGET (window)),
1070                                   parent);
1071 
1072   g_signal_connect_object (window, "realize",
1073                            G_CALLBACK (gimp_window_transient_realized),
1074                            parent, 0);
1075 
1076   g_object_unref (parent);
1077 #endif
1078 }
1079 
1080 static gboolean
gimp_widget_accel_find_func(GtkAccelKey * key,GClosure * closure,gpointer data)1081 gimp_widget_accel_find_func (GtkAccelKey *key,
1082                              GClosure    *closure,
1083                              gpointer     data)
1084 {
1085   return (GClosure *) data == closure;
1086 }
1087 
1088 static void
gimp_widget_accel_changed(GtkAccelGroup * accel_group,guint unused1,GdkModifierType unused2,GClosure * accel_closure,GtkWidget * widget)1089 gimp_widget_accel_changed (GtkAccelGroup   *accel_group,
1090                            guint            unused1,
1091                            GdkModifierType  unused2,
1092                            GClosure        *accel_closure,
1093                            GtkWidget       *widget)
1094 {
1095   GClosure *widget_closure;
1096 
1097   widget_closure = g_object_get_data (G_OBJECT (widget), "gimp-accel-closure");
1098 
1099   if (accel_closure == widget_closure)
1100     {
1101       GimpAction  *action;
1102       GtkAccelKey *accel_key;
1103       const gchar *tooltip;
1104       const gchar *help_id;
1105 
1106       action = g_object_get_data (G_OBJECT (widget), "gimp-accel-action");
1107 
1108       tooltip = gimp_action_get_tooltip (action);
1109       help_id = gimp_action_get_help_id (action);
1110 
1111       accel_key = gtk_accel_group_find (accel_group,
1112                                         gimp_widget_accel_find_func,
1113                                         accel_closure);
1114 
1115       if (accel_key            &&
1116           accel_key->accel_key &&
1117           (accel_key->accel_flags & GTK_ACCEL_VISIBLE))
1118         {
1119           gchar *escaped = g_markup_escape_text (tooltip, -1);
1120           gchar *accel   = gtk_accelerator_get_label (accel_key->accel_key,
1121                                                       accel_key->accel_mods);
1122           gchar *tmp     = g_strdup_printf ("%s  <b>%s</b>", escaped, accel);
1123 
1124           g_free (accel);
1125           g_free (escaped);
1126 
1127           gimp_help_set_help_data_with_markup (widget, tmp, help_id);
1128           g_free (tmp);
1129         }
1130       else
1131         {
1132           gimp_help_set_help_data (widget, tooltip, help_id);
1133         }
1134     }
1135 }
1136 
1137 static void   gimp_accel_help_widget_weak_notify (gpointer  accel_group,
1138                                                   GObject  *where_widget_was);
1139 
1140 static void
gimp_accel_help_accel_group_weak_notify(gpointer widget,GObject * where_accel_group_was)1141 gimp_accel_help_accel_group_weak_notify (gpointer  widget,
1142                                          GObject  *where_accel_group_was)
1143 {
1144   g_object_weak_unref (widget,
1145                        gimp_accel_help_widget_weak_notify,
1146                        where_accel_group_was);
1147 
1148   g_object_set_data (widget, "gimp-accel-group", NULL);
1149 }
1150 
1151 static void
gimp_accel_help_widget_weak_notify(gpointer accel_group,GObject * where_widget_was)1152 gimp_accel_help_widget_weak_notify (gpointer  accel_group,
1153                                     GObject  *where_widget_was)
1154 {
1155   g_object_weak_unref (accel_group,
1156                        gimp_accel_help_accel_group_weak_notify,
1157                        where_widget_was);
1158 }
1159 
1160 void
gimp_widget_set_accel_help(GtkWidget * widget,GimpAction * action)1161 gimp_widget_set_accel_help (GtkWidget  *widget,
1162                             GimpAction *action)
1163 {
1164   GtkAccelGroup *accel_group;
1165   GClosure      *accel_closure;
1166 
1167   accel_group = g_object_get_data (G_OBJECT (widget), "gimp-accel-group");
1168 
1169   if (accel_group)
1170     {
1171       g_signal_handlers_disconnect_by_func (accel_group,
1172                                             gimp_widget_accel_changed,
1173                                             widget);
1174       g_object_weak_unref (G_OBJECT (accel_group),
1175                            gimp_accel_help_accel_group_weak_notify,
1176                            widget);
1177       g_object_weak_unref (G_OBJECT (widget),
1178                            gimp_accel_help_widget_weak_notify,
1179                            accel_group);
1180       g_object_set_data (G_OBJECT (widget), "gimp-accel-group", NULL);
1181     }
1182 
1183   accel_closure = gimp_action_get_accel_closure (action);
1184 
1185   if (accel_closure)
1186     {
1187       accel_group = gtk_accel_group_from_accel_closure (accel_closure);
1188 
1189       g_object_set_data (G_OBJECT (widget), "gimp-accel-group",
1190                          accel_group);
1191       g_object_weak_ref (G_OBJECT (accel_group),
1192                          gimp_accel_help_accel_group_weak_notify,
1193                          widget);
1194       g_object_weak_ref (G_OBJECT (widget),
1195                          gimp_accel_help_widget_weak_notify,
1196                          accel_group);
1197 
1198       g_object_set_data (G_OBJECT (widget), "gimp-accel-closure",
1199                          accel_closure);
1200       g_object_set_data (G_OBJECT (widget), "gimp-accel-action",
1201                          action);
1202 
1203       g_signal_connect_object (accel_group, "accel-changed",
1204                                G_CALLBACK (gimp_widget_accel_changed),
1205                                widget, 0);
1206 
1207       gimp_widget_accel_changed (accel_group,
1208                                  0, 0,
1209                                  accel_closure,
1210                                  widget);
1211     }
1212   else
1213     {
1214       gimp_help_set_help_data (widget,
1215                                gimp_action_get_tooltip (action),
1216                                gimp_action_get_help_id (action));
1217 
1218     }
1219 }
1220 
1221 const gchar *
gimp_get_message_icon_name(GimpMessageSeverity severity)1222 gimp_get_message_icon_name (GimpMessageSeverity severity)
1223 {
1224   switch (severity)
1225     {
1226     case GIMP_MESSAGE_INFO:
1227       return GIMP_ICON_DIALOG_INFORMATION;
1228 
1229     case GIMP_MESSAGE_WARNING:
1230       return GIMP_ICON_DIALOG_WARNING;
1231 
1232     case GIMP_MESSAGE_ERROR:
1233       return GIMP_ICON_DIALOG_ERROR;
1234 
1235     case GIMP_MESSAGE_BUG_WARNING:
1236     case GIMP_MESSAGE_BUG_CRITICAL:
1237       return GIMP_ICON_WILBER_EEK;
1238     }
1239 
1240   g_return_val_if_reached (GIMP_ICON_DIALOG_WARNING);
1241 }
1242 
1243 gboolean
gimp_get_color_tag_color(GimpColorTag color_tag,GimpRGB * color,gboolean inherited)1244 gimp_get_color_tag_color (GimpColorTag  color_tag,
1245                           GimpRGB      *color,
1246                           gboolean      inherited)
1247 {
1248   static const struct
1249   {
1250     guchar r;
1251     guchar g;
1252     guchar b;
1253   }
1254   colors[] =
1255   {
1256     {    0,   0,   0  }, /* none   */
1257     {   84, 102, 159  }, /* blue   */
1258     {  111, 143,  48  }, /* green  */
1259     {  210, 182,  45  }, /* yellow */
1260     {  217, 122,  38  }, /* orange */
1261     {   87,  53,  25  }, /* brown  */
1262     {  170,  42,  47  }, /* red    */
1263     {   99,  66, 174  }, /* violet */
1264     {   87,  87,  87  }  /* gray   */
1265   };
1266 
1267   g_return_val_if_fail (color != NULL, FALSE);
1268   g_return_val_if_fail (color_tag < G_N_ELEMENTS (colors), FALSE);
1269 
1270   if (color_tag > GIMP_COLOR_TAG_NONE)
1271     {
1272       gimp_rgba_set_uchar (color,
1273                            colors[color_tag].r,
1274                            colors[color_tag].g,
1275                            colors[color_tag].b,
1276                            255);
1277 
1278       if (inherited)
1279         {
1280           gimp_rgb_composite (color, &(GimpRGB) {1.0, 1.0, 1.0, 0.2},
1281                               GIMP_RGB_COMPOSITE_NORMAL);
1282         }
1283 
1284       return TRUE;
1285     }
1286 
1287   return FALSE;
1288 }
1289 
1290 void
gimp_pango_layout_set_scale(PangoLayout * layout,gdouble scale)1291 gimp_pango_layout_set_scale (PangoLayout *layout,
1292                              gdouble      scale)
1293 {
1294   PangoAttrList  *attrs;
1295   PangoAttribute *attr;
1296 
1297   g_return_if_fail (PANGO_IS_LAYOUT (layout));
1298 
1299   attrs = pango_attr_list_new ();
1300 
1301   attr = pango_attr_scale_new (scale);
1302   attr->start_index = 0;
1303   attr->end_index   = -1;
1304   pango_attr_list_insert (attrs, attr);
1305 
1306   pango_layout_set_attributes (layout, attrs);
1307   pango_attr_list_unref (attrs);
1308 }
1309 
1310 void
gimp_pango_layout_set_weight(PangoLayout * layout,PangoWeight weight)1311 gimp_pango_layout_set_weight (PangoLayout *layout,
1312                               PangoWeight  weight)
1313 {
1314   PangoAttrList  *attrs;
1315   PangoAttribute *attr;
1316 
1317   g_return_if_fail (PANGO_IS_LAYOUT (layout));
1318 
1319   attrs = pango_attr_list_new ();
1320 
1321   attr = pango_attr_weight_new (weight);
1322   attr->start_index = 0;
1323   attr->end_index   = -1;
1324   pango_attr_list_insert (attrs, attr);
1325 
1326   pango_layout_set_attributes (layout, attrs);
1327   pango_attr_list_unref (attrs);
1328 }
1329 
1330 static gboolean
gimp_highlight_widget_expose(GtkWidget * widget,GdkEventExpose * event,gpointer data)1331 gimp_highlight_widget_expose (GtkWidget      *widget,
1332                               GdkEventExpose *event,
1333                               gpointer        data)
1334 {
1335   /* this code is a modified version of gtk+'s gtk_drag_highlight_expose(),
1336    * changing the highlight color from black to the widget's text color, which
1337    * improves its visibility when using a dark theme.
1338    */
1339 
1340   gint x, y, width, height;
1341 
1342   if (gtk_widget_is_drawable (widget))
1343     {
1344       GdkWindow      *window;
1345       GtkStyle       *style;
1346       const GdkColor *color;
1347       cairo_t        *cr;
1348 
1349       window = gtk_widget_get_window (widget);
1350       style  = gtk_widget_get_style (widget);
1351 
1352       if (!gtk_widget_get_has_window (widget))
1353         {
1354           GtkAllocation allocation;
1355 
1356           gtk_widget_get_allocation (widget, &allocation);
1357 
1358           x = allocation.x;
1359           y = allocation.y;
1360           width = allocation.width;
1361           height = allocation.height;
1362         }
1363       else
1364         {
1365           x = 0;
1366           y = 0;
1367           width = gdk_window_get_width (window);
1368           height = gdk_window_get_height (window);
1369         }
1370 
1371       gtk_paint_shadow (style, window,
1372                         GTK_STATE_NORMAL, GTK_SHADOW_OUT,
1373                         &event->area, widget, "dnd",
1374                         x, y, width, height);
1375 
1376       color = &style->text[GTK_STATE_NORMAL];
1377 
1378       cr = gdk_cairo_create (gtk_widget_get_window (widget));
1379       cairo_set_source_rgb (cr,
1380                             (gdouble) color->red   / 0xffff,
1381                             (gdouble) color->green / 0xffff,
1382                             (gdouble) color->blue  / 0xffff);
1383       cairo_set_line_width (cr, 1.0);
1384       cairo_rectangle (cr,
1385                        x + 0.5, y + 0.5,
1386                        width - 1, height - 1);
1387       cairo_stroke (cr);
1388       cairo_destroy (cr);
1389     }
1390 
1391   return FALSE;
1392 }
1393 
1394 /**
1395  * gimp_highlight_widget:
1396  * @widget:
1397  * @highlight:
1398  *
1399  * Turns highlighting for @widget on or off according to
1400  * @highlight, in a similar fashion to gtk_drag_highlight()
1401  * and gtk_drag_unhighlight().
1402  **/
1403 void
gimp_highlight_widget(GtkWidget * widget,gboolean highlight)1404 gimp_highlight_widget (GtkWidget *widget,
1405                        gboolean   highlight)
1406 {
1407   g_return_if_fail (GTK_IS_WIDGET (widget));
1408 
1409   if (highlight)
1410     {
1411       g_signal_connect_after (widget, "expose-event",
1412                               G_CALLBACK (gimp_highlight_widget_expose),
1413                               NULL);
1414     }
1415   else
1416     {
1417       g_signal_handlers_disconnect_by_func (widget,
1418                                             gimp_highlight_widget_expose,
1419                                             NULL);
1420     }
1421 
1422   gtk_widget_queue_draw (widget);
1423 }
1424 
1425 typedef struct
1426 {
1427   gint timeout_id;
1428   gint counter;
1429 } WidgetBlink;
1430 
1431 static WidgetBlink *
widget_blink_new(void)1432 widget_blink_new (void)
1433 {
1434   WidgetBlink *blink;
1435 
1436   blink = g_slice_new (WidgetBlink);
1437 
1438   blink->timeout_id = 0;
1439   blink->counter    = 0;
1440 
1441   return blink;
1442 }
1443 
1444 static void
widget_blink_free(WidgetBlink * blink)1445 widget_blink_free (WidgetBlink *blink)
1446 {
1447   if (blink->timeout_id)
1448     {
1449       g_source_remove (blink->timeout_id);
1450       blink->timeout_id = 0;
1451     }
1452 
1453   g_slice_free (WidgetBlink, blink);
1454 }
1455 
1456 static gboolean
gimp_widget_blink_timeout(GtkWidget * widget)1457 gimp_widget_blink_timeout (GtkWidget *widget)
1458 {
1459   WidgetBlink *blink;
1460 
1461   blink = g_object_get_data (G_OBJECT (widget), "gimp-widget-blink");
1462 
1463   gimp_highlight_widget (widget, blink->counter % 2 == 1);
1464   blink->counter++;
1465 
1466   if (blink->counter == 3)
1467     {
1468       blink->timeout_id = 0;
1469 
1470       g_object_set_data (G_OBJECT (widget), "gimp-widget-blink", NULL);
1471 
1472       return G_SOURCE_REMOVE;
1473     }
1474 
1475   return G_SOURCE_CONTINUE;
1476 }
1477 
1478 void
gimp_widget_blink(GtkWidget * widget)1479 gimp_widget_blink (GtkWidget *widget)
1480 {
1481   WidgetBlink *blink;
1482 
1483   g_return_if_fail (GTK_IS_WIDGET (widget));
1484 
1485   blink = widget_blink_new ();
1486 
1487   g_object_set_data_full (G_OBJECT (widget), "gimp-widget-blink", blink,
1488                           (GDestroyNotify) widget_blink_free);
1489 
1490   blink->timeout_id = g_timeout_add (150,
1491                                      (GSourceFunc) gimp_widget_blink_timeout,
1492                                      widget);
1493 
1494   gimp_highlight_widget (widget, TRUE);
1495 
1496   while ((widget = gtk_widget_get_parent (widget)))
1497     gimp_widget_blink_cancel (widget);
1498 }
1499 
gimp_widget_blink_cancel(GtkWidget * widget)1500 void gimp_widget_blink_cancel (GtkWidget *widget)
1501 {
1502   g_return_if_fail (GTK_IS_WIDGET (widget));
1503 
1504   if (g_object_get_data (G_OBJECT (widget), "gimp-widget-blink"))
1505     {
1506       gimp_highlight_widget (widget, FALSE);
1507 
1508       g_object_set_data (G_OBJECT (widget), "gimp-widget-blink", NULL);
1509     }
1510 }
1511 
1512 /**
1513  * gimp_dock_with_window_new:
1514  * @factory: a #GimpDialogFacotry
1515  * @screen:  the #GdkScreen the dock window should appear on
1516  * @toolbox: if %TRUE; gives a "gimp-toolbox-window" with a
1517  *           "gimp-toolbox", "gimp-dock-window"+"gimp-dock"
1518  *           otherwise
1519  *
1520  * Returns: the newly created #GimpDock with the #GimpDockWindow
1521  **/
1522 GtkWidget *
gimp_dock_with_window_new(GimpDialogFactory * factory,GdkScreen * screen,gint monitor,gboolean toolbox)1523 gimp_dock_with_window_new (GimpDialogFactory *factory,
1524                            GdkScreen         *screen,
1525                            gint               monitor,
1526                            gboolean           toolbox)
1527 {
1528   GtkWidget         *dock_window;
1529   GimpDockContainer *dock_container;
1530   GtkWidget         *dock;
1531   GimpUIManager     *ui_manager;
1532 
1533   g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
1534   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
1535 
1536   /* Create a dock window to put the dock in. We need to create the
1537    * dock window before the dock because the dock has a dependency to
1538    * the ui manager in the dock window
1539    */
1540   dock_window = gimp_dialog_factory_dialog_new (factory, screen, monitor,
1541                                                 NULL /*ui_manager*/,
1542                                                 (toolbox ?
1543                                                  "gimp-toolbox-window" :
1544                                                  "gimp-dock-window"),
1545                                                 -1 /*view_size*/,
1546                                                 FALSE /*present*/);
1547 
1548   dock_container = GIMP_DOCK_CONTAINER (dock_window);
1549   ui_manager     = gimp_dock_container_get_ui_manager (dock_container);
1550   dock           = gimp_dialog_factory_dialog_new (factory,
1551                                                    screen,
1552                                                    monitor,
1553                                                    ui_manager,
1554                                                    (toolbox ?
1555                                                     "gimp-toolbox" :
1556                                                     "gimp-dock"),
1557                                                    -1 /*view_size*/,
1558                                                    FALSE /*present*/);
1559 
1560   if (dock)
1561     gimp_dock_window_add_dock (GIMP_DOCK_WINDOW (dock_window),
1562                                GIMP_DOCK (dock),
1563                                -1);
1564 
1565   return dock;
1566 }
1567 
1568 GtkWidget *
gimp_tools_get_tool_options_gui(GimpToolOptions * tool_options)1569 gimp_tools_get_tool_options_gui (GimpToolOptions *tool_options)
1570 {
1571   GtkWidget *widget;
1572 
1573   widget = g_object_get_data (G_OBJECT (tool_options),
1574                               GIMP_TOOL_OPTIONS_GUI_KEY);
1575 
1576   if (! widget)
1577     {
1578       GimpToolOptionsGUIFunc func;
1579 
1580       func = g_object_get_data (G_OBJECT (tool_options),
1581                                 GIMP_TOOL_OPTIONS_GUI_FUNC_KEY);
1582 
1583       if (func)
1584         {
1585           widget = func (tool_options);
1586 
1587           gimp_tools_set_tool_options_gui (tool_options, widget);
1588         }
1589     }
1590 
1591   return widget;
1592 }
1593 
1594 void
gimp_tools_set_tool_options_gui(GimpToolOptions * tool_options,GtkWidget * widget)1595 gimp_tools_set_tool_options_gui (GimpToolOptions *tool_options,
1596                                  GtkWidget       *widget)
1597 {
1598   GtkWidget *prev_widget;
1599 
1600   prev_widget = g_object_get_data (G_OBJECT (tool_options),
1601                                    GIMP_TOOL_OPTIONS_GUI_KEY);
1602 
1603   if (widget == prev_widget)
1604     return;
1605 
1606   if (prev_widget)
1607     gtk_widget_destroy (prev_widget);
1608 
1609   g_object_set_data_full (G_OBJECT (tool_options),
1610                           GIMP_TOOL_OPTIONS_GUI_KEY,
1611                           widget ? g_object_ref_sink (widget)      : NULL,
1612                           widget ? (GDestroyNotify) g_object_unref : NULL);
1613 }
1614 
1615 void
gimp_tools_set_tool_options_gui_func(GimpToolOptions * tool_options,GimpToolOptionsGUIFunc func)1616 gimp_tools_set_tool_options_gui_func (GimpToolOptions        *tool_options,
1617                                       GimpToolOptionsGUIFunc  func)
1618 {
1619   g_object_set_data (G_OBJECT (tool_options),
1620                      GIMP_TOOL_OPTIONS_GUI_FUNC_KEY,
1621                      func);
1622 }
1623 
1624 void
gimp_widget_flush_expose(GtkWidget * widget)1625 gimp_widget_flush_expose (GtkWidget *widget)
1626 {
1627   g_return_if_fail (GTK_IS_WIDGET (widget));
1628 
1629   if (! gtk_widget_is_drawable (widget))
1630     return;
1631 
1632   gdk_window_process_updates (gtk_widget_get_window (widget), FALSE);
1633   gdk_flush ();
1634 }
1635 
1636 gboolean
gimp_widget_get_fully_opaque(GtkWidget * widget)1637 gimp_widget_get_fully_opaque (GtkWidget *widget)
1638 {
1639   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
1640 
1641   return g_object_get_data (G_OBJECT (widget),
1642                             "gimp-widget-fully-opaque") != NULL;
1643 }
1644 
1645 void
gimp_widget_set_fully_opaque(GtkWidget * widget,gboolean fully_opaque)1646 gimp_widget_set_fully_opaque (GtkWidget *widget,
1647                               gboolean   fully_opaque)
1648 {
1649   g_return_if_fail (GTK_IS_WIDGET (widget));
1650 
1651   return g_object_set_data (G_OBJECT (widget),
1652                             "gimp-widget-fully-opaque",
1653                             GINT_TO_POINTER (fully_opaque));
1654 }
1655 
1656 static void
gimp_gtk_container_clear_callback(GtkWidget * widget,GtkContainer * container)1657 gimp_gtk_container_clear_callback (GtkWidget    *widget,
1658                                    GtkContainer *container)
1659 {
1660   gtk_container_remove (container, widget);
1661 }
1662 
1663 void
gimp_gtk_container_clear(GtkContainer * container)1664 gimp_gtk_container_clear (GtkContainer *container)
1665 {
1666   gtk_container_foreach (container,
1667                          (GtkCallback) gimp_gtk_container_clear_callback,
1668                          container);
1669 }
1670 
1671 void
gimp_gtk_adjustment_chain(GtkAdjustment * adjustment1,GtkAdjustment * adjustment2)1672 gimp_gtk_adjustment_chain (GtkAdjustment *adjustment1,
1673                            GtkAdjustment *adjustment2)
1674 {
1675   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment1));
1676   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment2));
1677 
1678   g_object_bind_property (adjustment1, "value",
1679                           adjustment2, "lower",
1680                           G_BINDING_SYNC_CREATE);
1681   g_object_bind_property (adjustment2, "value",
1682                           adjustment1, "upper",
1683                           G_BINDING_SYNC_CREATE);
1684 }
1685 
1686 static gboolean
gimp_print_event_free(gpointer data)1687 gimp_print_event_free (gpointer data)
1688 {
1689   g_free (data);
1690 
1691   return FALSE;
1692 }
1693 
1694 const gchar *
gimp_print_event(const GdkEvent * event)1695 gimp_print_event (const GdkEvent *event)
1696 {
1697   gchar *str;
1698 
1699   switch (event->type)
1700     {
1701     case GDK_ENTER_NOTIFY:
1702       str = g_strdup ("ENTER_NOTIFY");
1703       break;
1704 
1705     case GDK_LEAVE_NOTIFY:
1706       str = g_strdup ("LEAVE_NOTIFY");
1707       break;
1708 
1709     case GDK_PROXIMITY_IN:
1710       str = g_strdup ("PROXIMITY_IN");
1711       break;
1712 
1713     case GDK_PROXIMITY_OUT:
1714       str = g_strdup ("PROXIMITY_OUT");
1715       break;
1716 
1717     case GDK_FOCUS_CHANGE:
1718       if (event->focus_change.in)
1719         str = g_strdup ("FOCUS_IN");
1720       else
1721         str = g_strdup ("FOCUS_OUT");
1722       break;
1723 
1724     case GDK_BUTTON_PRESS:
1725       str = g_strdup_printf ("BUTTON_PRESS (%d @ %0.0f:%0.0f)",
1726                              event->button.button,
1727                              event->button.x,
1728                              event->button.y);
1729       break;
1730 
1731     case GDK_2BUTTON_PRESS:
1732       str = g_strdup_printf ("2BUTTON_PRESS (%d @ %0.0f:%0.0f)",
1733                              event->button.button,
1734                              event->button.x,
1735                              event->button.y);
1736       break;
1737 
1738     case GDK_3BUTTON_PRESS:
1739       str = g_strdup_printf ("3BUTTON_PRESS (%d @ %0.0f:%0.0f)",
1740                              event->button.button,
1741                              event->button.x,
1742                              event->button.y);
1743       break;
1744 
1745     case GDK_BUTTON_RELEASE:
1746       str = g_strdup_printf ("BUTTON_RELEASE (%d @ %0.0f:%0.0f)",
1747                              event->button.button,
1748                              event->button.x,
1749                              event->button.y);
1750       break;
1751 
1752     case GDK_SCROLL:
1753       str = g_strdup_printf ("SCROLL (%d)",
1754                              event->scroll.direction);
1755       break;
1756 
1757     case GDK_MOTION_NOTIFY:
1758       str = g_strdup_printf ("MOTION_NOTIFY (%0.0f:%0.0f %d)",
1759                              event->motion.x,
1760                              event->motion.y,
1761                              event->motion.time);
1762       break;
1763 
1764     case GDK_KEY_PRESS:
1765       str = g_strdup_printf ("KEY_PRESS (%d, %s)",
1766                              event->key.keyval,
1767                              gdk_keyval_name (event->key.keyval) ?
1768                              gdk_keyval_name (event->key.keyval) : "<none>");
1769       break;
1770 
1771     case GDK_KEY_RELEASE:
1772       str = g_strdup_printf ("KEY_RELEASE (%d, %s)",
1773                              event->key.keyval,
1774                              gdk_keyval_name (event->key.keyval) ?
1775                              gdk_keyval_name (event->key.keyval) : "<none>");
1776       break;
1777 
1778     default:
1779       str = g_strdup_printf ("UNHANDLED (type %d)",
1780                              event->type);
1781       break;
1782     }
1783 
1784   g_idle_add (gimp_print_event_free, str);
1785 
1786   return str;
1787 }
1788 
1789 gboolean
gimp_color_profile_store_add_defaults(GimpColorProfileStore * store,GimpColorConfig * config,GimpImageBaseType base_type,GimpPrecision precision,GError ** error)1790 gimp_color_profile_store_add_defaults (GimpColorProfileStore  *store,
1791                                        GimpColorConfig        *config,
1792                                        GimpImageBaseType       base_type,
1793                                        GimpPrecision           precision,
1794                                        GError                **error)
1795 {
1796   GimpColorProfile *profile;
1797   const Babl       *format;
1798   gchar            *label;
1799   GError           *my_error = NULL;
1800 
1801   g_return_val_if_fail (GIMP_IS_COLOR_PROFILE_STORE (store), FALSE);
1802   g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), FALSE);
1803   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1804 
1805   format  = gimp_babl_format (base_type, precision, TRUE);
1806   profile = gimp_babl_format_get_color_profile (format);
1807 
1808   if (base_type == GIMP_GRAY)
1809     {
1810       label = g_strdup_printf (_("Built-in grayscale (%s)"),
1811                                gimp_color_profile_get_label (profile));
1812 
1813       profile = gimp_color_config_get_gray_color_profile (config, &my_error);
1814     }
1815   else
1816     {
1817       label = g_strdup_printf (_("Built-in RGB (%s)"),
1818                                gimp_color_profile_get_label (profile));
1819 
1820       profile = gimp_color_config_get_rgb_color_profile (config, &my_error);
1821     }
1822 
1823   gimp_color_profile_store_add_file (store, NULL, label);
1824   g_free (label);
1825 
1826   if (profile)
1827     {
1828       GFile *file;
1829 
1830       if (base_type == GIMP_GRAY)
1831         {
1832           file = gimp_file_new_for_config_path (config->gray_profile, NULL);
1833 
1834           label = g_strdup_printf (_("Preferred grayscale (%s)"),
1835                                    gimp_color_profile_get_label (profile));
1836         }
1837       else
1838         {
1839           file = gimp_file_new_for_config_path (config->rgb_profile, NULL);
1840 
1841           label = g_strdup_printf (_("Preferred RGB (%s)"),
1842                                    gimp_color_profile_get_label (profile));
1843         }
1844 
1845       g_object_unref (profile);
1846 
1847       gimp_color_profile_store_add_file (store, file, label);
1848 
1849       g_object_unref (file);
1850       g_free (label);
1851 
1852       return TRUE;
1853     }
1854   else if (my_error)
1855     {
1856       g_propagate_error (error, my_error);
1857 
1858       return FALSE;
1859     }
1860 
1861   return TRUE;
1862 }
1863 
1864 static void
connect_path_show(GimpColorProfileChooserDialog * dialog)1865 connect_path_show (GimpColorProfileChooserDialog *dialog)
1866 {
1867   GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog);
1868   GFile          *file    = gtk_file_chooser_get_file (chooser);
1869 
1870   if (file)
1871     {
1872       /*  if something is already selected in this dialog,
1873        *  leave it alone
1874        */
1875       g_object_unref (file);
1876     }
1877   else
1878     {
1879       GObject     *config;
1880       const gchar *property;
1881       gchar       *path = NULL;
1882 
1883       config   = g_object_get_data (G_OBJECT (dialog), "profile-path-config");
1884       property = g_object_get_data (G_OBJECT (dialog), "profile-path-property");
1885 
1886       g_object_get (config, property, &path, NULL);
1887 
1888       if (path)
1889         {
1890           GFile *folder = gimp_file_new_for_config_path (path, NULL);
1891 
1892           if (folder)
1893             {
1894               gtk_file_chooser_set_current_folder_file (chooser, folder, NULL);
1895               g_object_unref (folder);
1896             }
1897 
1898           g_free (path);
1899         }
1900     }
1901 }
1902 
1903 static void
connect_path_response(GimpColorProfileChooserDialog * dialog,gint response)1904 connect_path_response (GimpColorProfileChooserDialog *dialog,
1905                        gint                           response)
1906 {
1907   if (response == GTK_RESPONSE_ACCEPT)
1908     {
1909       GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog);
1910       GFile          *file    = gtk_file_chooser_get_file (chooser);
1911 
1912       if (file)
1913         {
1914           GFile *folder = gtk_file_chooser_get_current_folder_file (chooser);
1915 
1916           if (folder)
1917             {
1918               GObject     *config;
1919               const gchar *property;
1920               gchar       *path = NULL;
1921 
1922               config   = g_object_get_data (G_OBJECT (dialog),
1923                                             "profile-path-config");
1924               property = g_object_get_data (G_OBJECT (dialog),
1925                                             "profile-path-property");
1926 
1927               path = gimp_file_get_config_path (folder, NULL);
1928 
1929               g_object_set (config, property, path, NULL);
1930 
1931               if (path)
1932                 g_free (path);
1933 
1934               g_object_unref (folder);
1935             }
1936 
1937           g_object_unref (file);
1938         }
1939     }
1940 }
1941 
1942 void
gimp_color_profile_chooser_dialog_connect_path(GtkWidget * dialog,GObject * config,const gchar * property_name)1943 gimp_color_profile_chooser_dialog_connect_path (GtkWidget   *dialog,
1944                                                 GObject     *config,
1945                                                 const gchar *property_name)
1946 {
1947   g_return_if_fail (GIMP_IS_COLOR_PROFILE_CHOOSER_DIALOG (dialog));
1948   g_return_if_fail (G_IS_OBJECT (config));
1949   g_return_if_fail (property_name != NULL);
1950 
1951   g_object_set_data_full (G_OBJECT (dialog), "profile-path-config",
1952                           g_object_ref (config),
1953                           (GDestroyNotify) g_object_unref);
1954   g_object_set_data_full (G_OBJECT (dialog), "profile-path-property",
1955                           g_strdup (property_name),
1956                           (GDestroyNotify) g_free);
1957 
1958   g_signal_connect (dialog, "show",
1959                     G_CALLBACK (connect_path_show),
1960                     NULL);
1961   g_signal_connect (dialog, "response",
1962                     G_CALLBACK (connect_path_response),
1963                     NULL);
1964 }
1965