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