1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20 #include <stdlib.h>
21
22 #include <gegl.h>
23 #include <gtk/gtk.h>
24
25 #include "libgimpbase/gimpbase.h"
26 #include "libgimpwidgets/gimpwidgets.h"
27 #include "libgimpwidgets/gimpwidgets-private.h"
28
29 #include "gui-types.h"
30
31 #include "config/gimpguiconfig.h"
32
33 #include "core/gimp.h"
34 #include "core/gimpcontainer.h"
35 #include "core/gimpcontext.h"
36 #include "core/gimpimage.h"
37 #include "core/gimptoolinfo.h"
38
39 #include "plug-in/gimpenvirontable.h"
40 #include "plug-in/gimppluginmanager.h"
41
42 #include "display/gimpdisplay.h"
43 #include "display/gimpdisplay-foreach.h"
44 #include "display/gimpdisplayshell.h"
45 #include "display/gimpstatusbar.h"
46
47 #include "tools/gimp-tools.h"
48 #include "tools/gimptool.h"
49 #include "tools/tool_manager.h"
50
51 #include "widgets/gimpaction.h"
52 #include "widgets/gimpactiongroup.h"
53 #include "widgets/gimpaction-history.h"
54 #include "widgets/gimpclipboard.h"
55 #include "widgets/gimpcolorselectorpalette.h"
56 #include "widgets/gimpcontrollers.h"
57 #include "widgets/gimpdevices.h"
58 #include "widgets/gimpdialogfactory.h"
59 #include "widgets/gimpdnd.h"
60 #include "widgets/gimprender.h"
61 #include "widgets/gimphelp.h"
62 #include "widgets/gimphelp-ids.h"
63 #include "widgets/gimpmenufactory.h"
64 #include "widgets/gimpmessagebox.h"
65 #include "widgets/gimpsessioninfo.h"
66 #include "widgets/gimpuimanager.h"
67 #include "widgets/gimpwidgets-utils.h"
68 #include "widgets/gimplanguagestore-parser.h"
69
70 #include "actions/actions.h"
71 #include "actions/windows-commands.h"
72
73 #include "menus/menus.h"
74
75 #include "dialogs/dialogs.h"
76
77 #include "gimpuiconfigurer.h"
78 #include "gui.h"
79 #include "gui-unique.h"
80 #include "gui-vtable.h"
81 #include "icon-themes.h"
82 #include "session.h"
83 #include "splash.h"
84 #include "themes.h"
85
86 #ifdef GDK_WINDOWING_QUARTZ
87 #import <AppKit/AppKit.h>
88 #include <gtkosxapplication.h>
89 #include <gdk/gdkquartz.h>
90
91 /* Forward declare since we are building against old SDKs. */
92 #if !defined(MAC_OS_X_VERSION_10_12) || \
93 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12
94
95 @interface NSWindow(ForwardDeclarations)
96 + (void)setAllowsAutomaticWindowTabbing:(BOOL)allow;
97 @end
98
99 #endif
100
101 #endif /* GDK_WINDOWING_QUARTZ */
102
103 #include "gimp-intl.h"
104
105
106 /* local function prototypes */
107
108 static gchar * gui_sanity_check (void);
109 static void gui_help_func (const gchar *help_id,
110 gpointer help_data);
111 static gboolean gui_get_background_func (GimpRGB *color);
112 static gboolean gui_get_foreground_func (GimpRGB *color);
113
114 static void gui_initialize_after_callback (Gimp *gimp,
115 GimpInitStatusFunc callback);
116
117 static void gui_restore_callback (Gimp *gimp,
118 GimpInitStatusFunc callback);
119 static void gui_restore_after_callback (Gimp *gimp,
120 GimpInitStatusFunc callback);
121
122 static gboolean gui_exit_callback (Gimp *gimp,
123 gboolean force);
124 static gboolean gui_exit_after_callback (Gimp *gimp,
125 gboolean force);
126
127 static void gui_show_tooltips_notify (GimpGuiConfig *gui_config,
128 GParamSpec *pspec,
129 Gimp *gimp);
130 static void gui_show_help_button_notify (GimpGuiConfig *gui_config,
131 GParamSpec *pspec,
132 Gimp *gimp);
133 static void gui_user_manual_notify (GimpGuiConfig *gui_config,
134 GParamSpec *pspec,
135 Gimp *gimp);
136 static void gui_single_window_mode_notify (GimpGuiConfig *gui_config,
137 GParamSpec *pspec,
138 GimpUIConfigurer *ui_configurer);
139 static void gui_tearoff_menus_notify (GimpGuiConfig *gui_config,
140 GParamSpec *pspec,
141 GtkUIManager *manager);
142
143 static void gui_clipboard_changed (Gimp *gimp);
144
145 static void gui_menu_show_tooltip (GimpUIManager *manager,
146 const gchar *tooltip,
147 Gimp *gimp);
148 static void gui_menu_hide_tooltip (GimpUIManager *manager,
149 Gimp *gimp);
150
151 static void gui_display_changed (GimpContext *context,
152 GimpDisplay *display,
153 Gimp *gimp);
154
155 static void gui_compare_accelerator (gpointer data,
156 const gchar *accel_path,
157 guint accel_key,
158 GdkModifierType accel_mods,
159 gboolean changed);
160 static void gui_check_unique_accelerator (gpointer data,
161 const gchar *accel_path,
162 guint accel_key,
163 GdkModifierType accel_mods,
164 gboolean changed);
165 static gboolean gui_check_action_exists (const gchar *accel_path);
166
167
168 /* private variables */
169
170 static Gimp *the_gui_gimp = NULL;
171 static GimpUIManager *image_ui_manager = NULL;
172 static GimpUIConfigurer *ui_configurer = NULL;
173 static GdkScreen *initial_screen = NULL;
174 static gint initial_monitor = -1;
175
176
177 /* public functions */
178
179 void
gui_libs_init(GOptionContext * context)180 gui_libs_init (GOptionContext *context)
181 {
182 g_return_if_fail (context != NULL);
183
184 g_option_context_add_group (context, gtk_get_option_group (TRUE));
185 }
186
187 void
gui_abort(const gchar * abort_message)188 gui_abort (const gchar *abort_message)
189 {
190 GtkWidget *dialog;
191 GtkWidget *box;
192
193 g_return_if_fail (abort_message != NULL);
194
195 dialog = gimp_dialog_new (_("GIMP Message"), "gimp-abort",
196 NULL, GTK_DIALOG_MODAL, NULL, NULL,
197
198 _("_OK"), GTK_RESPONSE_OK,
199
200 NULL);
201
202 gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
203
204 box = g_object_new (GIMP_TYPE_MESSAGE_BOX,
205 "icon-name", GIMP_ICON_WILBER_EEK,
206 "border-width", 12,
207 NULL);
208
209 gimp_message_box_set_text (GIMP_MESSAGE_BOX (box), "%s", abort_message);
210
211 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
212 box, TRUE, TRUE, 0);
213 gtk_widget_show (box);
214
215 gimp_dialog_run (GIMP_DIALOG (dialog));
216
217 exit (EXIT_FAILURE);
218 }
219
220 GimpInitStatusFunc
gui_init(Gimp * gimp,gboolean no_splash)221 gui_init (Gimp *gimp,
222 gboolean no_splash)
223 {
224 GimpInitStatusFunc status_callback = NULL;
225 gchar *abort_message;
226
227 g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
228 g_return_val_if_fail (the_gui_gimp == NULL, NULL);
229
230 abort_message = gui_sanity_check ();
231 if (abort_message)
232 gui_abort (abort_message);
233
234 the_gui_gimp = gimp;
235
236 /* TRANSLATORS: there is no need to translate this in GIMP. This uses
237 * "gtk20" domain as a special trick to determine language direction,
238 * but xgettext extracts it anyway mistakenly into GIMP po files.
239 * Leave an empty string as translation. It does not matter.
240 */
241 if (g_strcmp0 (dgettext ("gtk20", "default:LTR"), "default:RTL") == 0)
242 /* Normally this should have been taken care of during command line
243 * parsing as a post-parse hook of gtk_get_option_group(), using the
244 * system locales.
245 * But user config may have overridden the language, therefore we must
246 * check the widget directions again.
247 */
248 gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL);
249 else
250 gtk_widget_set_default_direction (GTK_TEXT_DIR_LTR);
251
252 gui_unique_init (gimp);
253 gimp_language_store_parser_init ();
254
255 /* initialize icon themes before gimp_widgets_init() so we avoid
256 * setting the configured theme twice
257 */
258 icon_themes_init (gimp);
259
260 gimp_widgets_init (gui_help_func,
261 gui_get_foreground_func,
262 gui_get_background_func,
263 NULL);
264
265 g_type_class_ref (GIMP_TYPE_COLOR_SELECT);
266
267 /* disable automatic startup notification */
268 gtk_window_set_auto_startup_notification (FALSE);
269
270 #ifdef GDK_WINDOWING_QUARTZ
271 /* Before the first window is created (typically the splash window),
272 * we need to disable automatic tabbing behavior introduced on Sierra.
273 * This is known to cause all kinds of weird issues (see for instance
274 * Bugzilla #776294) and needs proper GTK+ support if we would want to
275 * enable it.
276 */
277 if ([NSWindow respondsToSelector:@selector(setAllowsAutomaticWindowTabbing:)])
278 [NSWindow setAllowsAutomaticWindowTabbing:NO];
279
280 /* MacOS 11 (Big Sur) has added a new, dynamic "accent" as default.
281 * This uses a 10-bit colorspace so every GIMP drawing operation
282 * has the additional cost of an 8-bit (ARGB) to 10-bit conversion.
283 * Let's disable this mode to regain the lost performance.
284 */
285 if (gdk_quartz_osx_version () >= GDK_OSX_BIG_SUR)
286 {
287 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
288 [userDefaults setBool: NO forKey:@"NSViewUsesAutomaticLayerBackingStores"];
289 }
290
291 #endif /* GDK_WINDOWING_QUARTZ */
292
293 gimp_dnd_init (gimp);
294
295 themes_init (gimp);
296
297 initial_monitor = gimp_get_monitor_at_pointer (&initial_screen);
298 gtk_widget_set_default_colormap (gdk_screen_get_rgb_colormap (initial_screen));
299
300 if (! no_splash)
301 {
302 splash_create (gimp->be_verbose, initial_screen, initial_monitor);
303 status_callback = splash_update;
304 }
305
306 g_signal_connect_after (gimp, "initialize",
307 G_CALLBACK (gui_initialize_after_callback),
308 NULL);
309
310 g_signal_connect (gimp, "restore",
311 G_CALLBACK (gui_restore_callback),
312 NULL);
313 g_signal_connect_after (gimp, "restore",
314 G_CALLBACK (gui_restore_after_callback),
315 NULL);
316
317 g_signal_connect (gimp, "exit",
318 G_CALLBACK (gui_exit_callback),
319 NULL);
320 g_signal_connect_after (gimp, "exit",
321 G_CALLBACK (gui_exit_after_callback),
322 NULL);
323
324 return status_callback;
325 }
326
327 /*
328 * gui_recover:
329 * @n_recoveries: number of recovered files.
330 *
331 * Query the user interactively if files were saved from a previous
332 * crash, asking whether to try and recover or discard them.
333 *
334 * Returns: TRUE if answer is to try and recover, FALSE otherwise.
335 */
336 gboolean
gui_recover(gint n_recoveries)337 gui_recover (gint n_recoveries)
338 {
339 GtkWidget *dialog;
340 GtkWidget *box;
341 gboolean recover;
342
343 dialog = gimp_dialog_new (_("Image Recovery"), "gimp-recovery",
344 NULL, GTK_DIALOG_MODAL, NULL, NULL,
345 _("_Discard"), GTK_RESPONSE_CANCEL,
346 _("_Recover"), GTK_RESPONSE_OK,
347 NULL);
348 gtk_dialog_set_default_response (GTK_DIALOG (dialog),
349 GTK_RESPONSE_OK);
350
351 box = gimp_message_box_new (GIMP_ICON_WILBER_EEK);
352 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
353 box, TRUE, TRUE, 0);
354 gtk_widget_show (box);
355
356 gimp_message_box_set_primary_text (GIMP_MESSAGE_BOX (box),
357 _("Eeek! It looks like GIMP recovered from a crash!"));
358
359 gimp_message_box_set_text (GIMP_MESSAGE_BOX (box),
360 /* TRANSLATORS: even if English singular form does
361 * not use %d, you can use %d for translation in
362 * any singular/plural form of your language if
363 * suited. It will just work and be replaced by the
364 * number of images as expected.
365 */
366 ngettext ("An image was salvaged from the crash. "
367 "Do you want to try and recover it?",
368 "%d images were salvaged from the crash. "
369 "Do you want to try and recover them?",
370 n_recoveries), n_recoveries);
371
372 recover = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
373 gtk_widget_destroy (dialog);
374
375 return recover;
376 }
377
378 gint
gui_get_initial_monitor(Gimp * gimp,GdkScreen ** screen)379 gui_get_initial_monitor (Gimp *gimp,
380 GdkScreen **screen)
381 {
382 g_return_val_if_fail (GIMP_IS_GIMP (gimp), 0);
383 g_return_val_if_fail (screen != NULL, 0);
384
385 *screen = initial_screen;
386
387 return initial_monitor;
388 }
389
390
391 /* private functions */
392
393 static gchar *
gui_sanity_check(void)394 gui_sanity_check (void)
395 {
396 #define GTK_REQUIRED_MAJOR 2
397 #define GTK_REQUIRED_MINOR 24
398 #define GTK_REQUIRED_MICRO 10
399
400 const gchar *mismatch = gtk_check_version (GTK_REQUIRED_MAJOR,
401 GTK_REQUIRED_MINOR,
402 GTK_REQUIRED_MICRO);
403
404 if (mismatch)
405 {
406 return g_strdup_printf
407 ("%s\n\n"
408 "GIMP requires GTK+ version %d.%d.%d or later.\n"
409 "Installed GTK+ version is %d.%d.%d.\n\n"
410 "Somehow you or your software packager managed\n"
411 "to install GIMP with an older GTK+ version.\n\n"
412 "Please upgrade to GTK+ version %d.%d.%d or later.",
413 mismatch,
414 GTK_REQUIRED_MAJOR, GTK_REQUIRED_MINOR, GTK_REQUIRED_MICRO,
415 gtk_major_version, gtk_minor_version, gtk_micro_version,
416 GTK_REQUIRED_MAJOR, GTK_REQUIRED_MINOR, GTK_REQUIRED_MICRO);
417 }
418
419 #undef GTK_REQUIRED_MAJOR
420 #undef GTK_REQUIRED_MINOR
421 #undef GTK_REQUIRED_MICRO
422
423 return NULL;
424 }
425
426 static void
gui_help_func(const gchar * help_id,gpointer help_data)427 gui_help_func (const gchar *help_id,
428 gpointer help_data)
429 {
430 g_return_if_fail (GIMP_IS_GIMP (the_gui_gimp));
431
432 gimp_help (the_gui_gimp, NULL, NULL, help_id);
433 }
434
435 static gboolean
gui_get_foreground_func(GimpRGB * color)436 gui_get_foreground_func (GimpRGB *color)
437 {
438 g_return_val_if_fail (color != NULL, FALSE);
439 g_return_val_if_fail (GIMP_IS_GIMP (the_gui_gimp), FALSE);
440
441 gimp_context_get_foreground (gimp_get_user_context (the_gui_gimp), color);
442
443 return TRUE;
444 }
445
446 static gboolean
gui_get_background_func(GimpRGB * color)447 gui_get_background_func (GimpRGB *color)
448 {
449 g_return_val_if_fail (color != NULL, FALSE);
450 g_return_val_if_fail (GIMP_IS_GIMP (the_gui_gimp), FALSE);
451
452 gimp_context_get_background (gimp_get_user_context (the_gui_gimp), color);
453
454 return TRUE;
455 }
456
457 static void
gui_initialize_after_callback(Gimp * gimp,GimpInitStatusFunc status_callback)458 gui_initialize_after_callback (Gimp *gimp,
459 GimpInitStatusFunc status_callback)
460 {
461 const gchar *name = NULL;
462
463 g_return_if_fail (GIMP_IS_GIMP (gimp));
464
465 if (gimp->be_verbose)
466 g_print ("INIT: %s\n", G_STRFUNC);
467
468 #if defined (GDK_WINDOWING_X11)
469 name = "DISPLAY";
470 #elif defined (GDK_WINDOWING_DIRECTFB) || defined (GDK_WINDOWING_FB)
471 name = "GDK_DISPLAY";
472 #endif
473
474 /* TODO: Need to care about display migration with GTK+ 2.2 at some point */
475
476 if (name)
477 {
478 gchar *display = gdk_get_display ();
479
480 gimp_environ_table_add (gimp->plug_in_manager->environ_table,
481 name, display, NULL);
482 g_free (display);
483 }
484
485 gimp_tools_init (gimp);
486
487 gimp_context_set_tool (gimp_get_user_context (gimp),
488 gimp_tool_info_get_standard (gimp));
489 }
490
491 static void
gui_restore_callback(Gimp * gimp,GimpInitStatusFunc status_callback)492 gui_restore_callback (Gimp *gimp,
493 GimpInitStatusFunc status_callback)
494 {
495 GimpDisplayConfig *display_config = GIMP_DISPLAY_CONFIG (gimp->config);
496 GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (gimp->config);
497
498 if (gimp->be_verbose)
499 g_print ("INIT: %s\n", G_STRFUNC);
500
501 gui_vtable_init (gimp);
502
503 if (! gui_config->show_tooltips)
504 gimp_help_disable_tooltips ();
505
506 g_signal_connect (gui_config, "notify::show-tooltips",
507 G_CALLBACK (gui_show_tooltips_notify),
508 gimp);
509
510 gimp_dialogs_show_help_button (gui_config->use_help &&
511 gui_config->show_help_button);
512
513 g_signal_connect (gui_config, "notify::use-help",
514 G_CALLBACK (gui_show_help_button_notify),
515 gimp);
516 g_signal_connect (gui_config, "notify::user-manual-online",
517 G_CALLBACK (gui_user_manual_notify),
518 gimp);
519 g_signal_connect (gui_config, "notify::show-help-button",
520 G_CALLBACK (gui_show_help_button_notify),
521 gimp);
522
523 g_signal_connect (gimp_get_user_context (gimp), "display-changed",
524 G_CALLBACK (gui_display_changed),
525 gimp);
526
527 /* make sure the monitor resolution is valid */
528 if (display_config->monitor_res_from_gdk ||
529 display_config->monitor_xres < GIMP_MIN_RESOLUTION ||
530 display_config->monitor_yres < GIMP_MIN_RESOLUTION)
531 {
532 gdouble xres, yres;
533
534 gimp_get_monitor_resolution (initial_screen,
535 initial_monitor,
536 &xres, &yres);
537
538 g_object_set (gimp->config,
539 "monitor-xresolution", xres,
540 "monitor-yresolution", yres,
541 "monitor-resolution-from-windowing-system", TRUE,
542 NULL);
543 }
544
545 actions_init (gimp);
546 menus_init (gimp, global_action_factory);
547 gimp_render_init (gimp);
548
549 dialogs_init (gimp, global_menu_factory);
550
551 gimp_clipboard_init (gimp);
552 if (gimp_get_clipboard_image (gimp))
553 gimp_clipboard_set_image (gimp, gimp_get_clipboard_image (gimp));
554 else
555 gimp_clipboard_set_buffer (gimp, gimp_get_clipboard_buffer (gimp));
556
557 g_signal_connect (gimp, "clipboard-changed",
558 G_CALLBACK (gui_clipboard_changed),
559 NULL);
560
561 gimp_devices_init (gimp);
562 gimp_controllers_init (gimp);
563 session_init (gimp);
564
565 g_type_class_unref (g_type_class_ref (GIMP_TYPE_COLOR_SELECTOR_PALETTE));
566
567 status_callback (NULL, _("Tool Options"), 1.0);
568 gimp_tools_restore (gimp);
569 }
570
571 #ifdef GDK_WINDOWING_QUARTZ
572 static void
gui_add_to_app_menu(GimpUIManager * ui_manager,GtkosxApplication * osx_app,const gchar * action_path,gint index)573 gui_add_to_app_menu (GimpUIManager *ui_manager,
574 GtkosxApplication *osx_app,
575 const gchar *action_path,
576 gint index)
577 {
578 GtkWidget *item;
579
580 item = gimp_ui_manager_get_widget (ui_manager, action_path);
581
582 if (GTK_IS_MENU_ITEM (item))
583 gtkosx_application_insert_app_menu_item (osx_app, GTK_WIDGET (item), index);
584 }
585
586 static gboolean
gui_quartz_quit_callback(GtkosxApplication * osx_app,GimpUIManager * ui_manager)587 gui_quartz_quit_callback (GtkosxApplication *osx_app,
588 GimpUIManager *ui_manager)
589 {
590 gimp_ui_manager_activate_action (ui_manager, "file", "file-quit");
591
592 return TRUE;
593 }
594 #endif
595
596 static void
gui_restore_after_callback(Gimp * gimp,GimpInitStatusFunc status_callback)597 gui_restore_after_callback (Gimp *gimp,
598 GimpInitStatusFunc status_callback)
599 {
600 GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (gimp->config);
601 GimpDisplay *display;
602
603 if (gimp->be_verbose)
604 g_print ("INIT: %s\n", G_STRFUNC);
605
606 gimp->message_handler = GIMP_MESSAGE_BOX;
607
608 /* load the recent documents after gimp_real_restore() because we
609 * need the mime-types implemented by plug-ins
610 */
611 status_callback (NULL, _("Documents"), 0.9);
612 gimp_recent_list_load (gimp);
613
614 /* enable this to always have icons everywhere */
615 if (g_getenv ("GIMP_ICONS_LIKE_A_BOSS"))
616 {
617 GdkScreen *screen = gdk_screen_get_default ();
618
619 g_object_set (G_OBJECT (gtk_settings_get_for_screen (screen)),
620 "gtk-button-images", TRUE,
621 "gtk-menu-images", TRUE,
622 NULL);
623 }
624
625 if (gui_config->restore_accels)
626 menus_restore (gimp);
627
628 ui_configurer = g_object_new (GIMP_TYPE_UI_CONFIGURER,
629 "gimp", gimp,
630 NULL);
631
632 image_ui_manager = gimp_menu_factory_manager_new (global_menu_factory,
633 "<Image>",
634 gimp,
635 gui_config->tearoff_menus);
636 gimp_ui_manager_update (image_ui_manager, gimp);
637
638 /* Check that every accelerator is unique. */
639 gtk_accel_map_foreach_unfiltered (NULL,
640 gui_check_unique_accelerator);
641
642 gimp_action_history_init (gimp);
643
644 g_signal_connect_object (gui_config, "notify::single-window-mode",
645 G_CALLBACK (gui_single_window_mode_notify),
646 ui_configurer, 0);
647 g_signal_connect_object (gui_config, "notify::tearoff-menus",
648 G_CALLBACK (gui_tearoff_menus_notify),
649 image_ui_manager, 0);
650 g_signal_connect (image_ui_manager, "show-tooltip",
651 G_CALLBACK (gui_menu_show_tooltip),
652 gimp);
653 g_signal_connect (image_ui_manager, "hide-tooltip",
654 G_CALLBACK (gui_menu_hide_tooltip),
655 gimp);
656
657 gimp_devices_restore (gimp);
658 gimp_controllers_restore (gimp, image_ui_manager);
659
660 if (status_callback == splash_update)
661 splash_destroy ();
662
663 if (gimp_get_show_gui (gimp))
664 {
665 GimpDisplayShell *shell;
666 GtkWidget *toplevel;
667
668 /* create the empty display */
669 display = GIMP_DISPLAY (gimp_create_display (gimp, NULL,
670 GIMP_UNIT_PIXEL, 1.0,
671 G_OBJECT (initial_screen),
672 initial_monitor));
673
674 shell = gimp_display_get_shell (display);
675
676 if (gui_config->restore_session)
677 session_restore (gimp,
678 initial_screen,
679 initial_monitor);
680
681 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell));
682
683 #ifdef GDK_WINDOWING_QUARTZ
684 {
685 GtkosxApplication *osx_app;
686 GtkWidget *menu;
687 GtkWidget *item;
688
689 [[NSUserDefaults standardUserDefaults] setObject:@"NO"
690 forKey:@"NSTreatUnknownArgumentsAsOpen"];
691
692 osx_app = gtkosx_application_get ();
693
694 menu = gimp_ui_manager_get_widget (image_ui_manager,
695 "/image-menubar");
696 /* menu should have window parent for accelerator support */
697 gtk_widget_set_parent(menu, toplevel);
698
699 if (GTK_IS_MENU_ITEM (menu))
700 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
701
702 /* do not activate OSX menu if tests are running */
703 if (! g_getenv ("GIMP_TESTING_ABS_TOP_SRCDIR"))
704 gtkosx_application_set_menu_bar (osx_app, GTK_MENU_SHELL (menu));
705
706 gtkosx_application_set_use_quartz_accelerators (osx_app, FALSE);
707
708 gui_add_to_app_menu (image_ui_manager, osx_app,
709 "/image-menubar/Help/dialogs-about", 0);
710 gui_add_to_app_menu (image_ui_manager, osx_app,
711 "/image-menubar/Help/dialogs-search-action", 1);
712
713 #define PREFERENCES "/image-menubar/Edit/Preferences/"
714
715 gui_add_to_app_menu (image_ui_manager, osx_app,
716 PREFERENCES "dialogs-preferences", 3);
717 gui_add_to_app_menu (image_ui_manager, osx_app,
718 PREFERENCES "dialogs-input-devices", 4);
719 gui_add_to_app_menu (image_ui_manager, osx_app,
720 PREFERENCES "dialogs-keyboard-shortcuts", 5);
721 gui_add_to_app_menu (image_ui_manager, osx_app,
722 PREFERENCES "dialogs-module-dialog", 6);
723 gui_add_to_app_menu (image_ui_manager, osx_app,
724 PREFERENCES "plug-in-unit-editor", 7);
725
726 #undef PREFERENCES
727
728 item = gtk_separator_menu_item_new ();
729 gtkosx_application_insert_app_menu_item (osx_app, item, 8);
730
731 item = gimp_ui_manager_get_widget (image_ui_manager,
732 "/image-menubar/File/file-quit");
733 gtk_widget_hide (item);
734
735 g_signal_connect (osx_app, "NSApplicationBlockTermination",
736 G_CALLBACK (gui_quartz_quit_callback),
737 image_ui_manager);
738
739 gtkosx_application_ready (osx_app);
740 }
741 #endif /* GDK_WINDOWING_QUARTZ */
742
743 /* move keyboard focus to the display */
744 gtk_window_present (GTK_WINDOW (toplevel));
745 }
746
747 /* indicate that the application has finished loading */
748 gdk_notify_startup_complete ();
749
750 /* clear startup monitor variables */
751 initial_screen = NULL;
752 initial_monitor = -1;
753 }
754
755 static gboolean
gui_exit_callback(Gimp * gimp,gboolean force)756 gui_exit_callback (Gimp *gimp,
757 gboolean force)
758 {
759 GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (gimp->config);
760 GimpTool *active_tool;
761
762 if (gimp->be_verbose)
763 g_print ("EXIT: %s\n", G_STRFUNC);
764
765 if (! force && gimp_displays_dirty (gimp))
766 {
767 GdkScreen *screen;
768 gint monitor;
769
770 monitor = gimp_get_monitor_at_pointer (&screen);
771
772 gimp_dialog_factory_dialog_raise (gimp_dialog_factory_get_singleton (),
773 screen, monitor,
774 "gimp-quit-dialog", -1);
775
776 return TRUE; /* stop exit for now */
777 }
778
779 gimp->message_handler = GIMP_CONSOLE;
780
781 gui_unique_exit ();
782
783 /* If any modifier is set when quitting (typically when exiting with
784 * Ctrl-q for instance!), when serializing the tool options, it will
785 * save any alternate value instead of the main one. Make sure that
786 * any modifier is reset before saving options.
787 */
788 active_tool = tool_manager_get_active (gimp);
789 if (active_tool && active_tool->focus_display)
790 gimp_tool_set_modifier_state (active_tool, 0, active_tool->focus_display);
791
792 if (gui_config->save_session_info)
793 session_save (gimp, FALSE);
794
795 if (gui_config->save_device_status)
796 gimp_devices_save (gimp, FALSE);
797
798 if (TRUE /* gui_config->save_controllers */)
799 gimp_controllers_save (gimp);
800
801 g_signal_handlers_disconnect_by_func (gimp_get_user_context (gimp),
802 gui_display_changed,
803 gimp);
804
805 gimp_displays_delete (gimp);
806
807 if (gui_config->save_accels)
808 menus_save (gimp, FALSE);
809
810 gimp_tools_save (gimp, gui_config->save_tool_options, FALSE);
811 gimp_tools_exit (gimp);
812
813 gimp_language_store_parser_clean ();
814
815 return FALSE; /* continue exiting */
816 }
817
818 static gboolean
gui_exit_after_callback(Gimp * gimp,gboolean force)819 gui_exit_after_callback (Gimp *gimp,
820 gboolean force)
821 {
822 if (gimp->be_verbose)
823 g_print ("EXIT: %s\n", G_STRFUNC);
824
825 g_signal_handlers_disconnect_by_func (gimp->config,
826 gui_show_help_button_notify,
827 gimp);
828 g_signal_handlers_disconnect_by_func (gimp->config,
829 gui_user_manual_notify,
830 gimp);
831 g_signal_handlers_disconnect_by_func (gimp->config,
832 gui_show_tooltips_notify,
833 gimp);
834
835 gimp_action_history_exit (gimp);
836
837 g_object_unref (image_ui_manager);
838 image_ui_manager = NULL;
839
840 g_object_unref (ui_configurer);
841 ui_configurer = NULL;
842
843 /* exit the clipboard before shutting down the GUI because it runs
844 * a whole lot of code paths. See bug #731389.
845 */
846 g_signal_handlers_disconnect_by_func (gimp,
847 G_CALLBACK (gui_clipboard_changed),
848 NULL);
849 gimp_clipboard_exit (gimp);
850
851 session_exit (gimp);
852 menus_exit (gimp);
853 actions_exit (gimp);
854 gimp_render_exit (gimp);
855
856 gimp_controllers_exit (gimp);
857 gimp_devices_exit (gimp);
858 dialogs_exit (gimp);
859 themes_exit (gimp);
860
861 g_type_class_unref (g_type_class_peek (GIMP_TYPE_COLOR_SELECT));
862
863 return FALSE; /* continue exiting */
864 }
865
866 static void
gui_show_tooltips_notify(GimpGuiConfig * gui_config,GParamSpec * param_spec,Gimp * gimp)867 gui_show_tooltips_notify (GimpGuiConfig *gui_config,
868 GParamSpec *param_spec,
869 Gimp *gimp)
870 {
871 if (gui_config->show_tooltips)
872 gimp_help_enable_tooltips ();
873 else
874 gimp_help_disable_tooltips ();
875 }
876
877 static void
gui_show_help_button_notify(GimpGuiConfig * gui_config,GParamSpec * param_spec,Gimp * gimp)878 gui_show_help_button_notify (GimpGuiConfig *gui_config,
879 GParamSpec *param_spec,
880 Gimp *gimp)
881 {
882 gimp_dialogs_show_help_button (gui_config->use_help &&
883 gui_config->show_help_button);
884 }
885
886 static void
gui_user_manual_notify(GimpGuiConfig * gui_config,GParamSpec * param_spec,Gimp * gimp)887 gui_user_manual_notify (GimpGuiConfig *gui_config,
888 GParamSpec *param_spec,
889 Gimp *gimp)
890 {
891 gimp_help_user_manual_changed (gimp);
892 }
893
894 static void
gui_single_window_mode_notify(GimpGuiConfig * gui_config,GParamSpec * pspec,GimpUIConfigurer * ui_configurer)895 gui_single_window_mode_notify (GimpGuiConfig *gui_config,
896 GParamSpec *pspec,
897 GimpUIConfigurer *ui_configurer)
898 {
899 gimp_ui_configurer_configure (ui_configurer,
900 gui_config->single_window_mode);
901 }
902 static void
gui_tearoff_menus_notify(GimpGuiConfig * gui_config,GParamSpec * pspec,GtkUIManager * manager)903 gui_tearoff_menus_notify (GimpGuiConfig *gui_config,
904 GParamSpec *pspec,
905 GtkUIManager *manager)
906 {
907 gtk_ui_manager_set_add_tearoffs (manager, gui_config->tearoff_menus);
908 }
909
910 static void
gui_clipboard_changed(Gimp * gimp)911 gui_clipboard_changed (Gimp *gimp)
912 {
913 if (gimp_get_clipboard_image (gimp))
914 gimp_clipboard_set_image (gimp, gimp_get_clipboard_image (gimp));
915 else
916 gimp_clipboard_set_buffer (gimp, gimp_get_clipboard_buffer (gimp));
917 }
918
919 static void
gui_menu_show_tooltip(GimpUIManager * manager,const gchar * tooltip,Gimp * gimp)920 gui_menu_show_tooltip (GimpUIManager *manager,
921 const gchar *tooltip,
922 Gimp *gimp)
923 {
924 GimpContext *context = gimp_get_user_context (gimp);
925 GimpDisplay *display = gimp_context_get_display (context);
926
927 if (display)
928 {
929 GimpDisplayShell *shell = gimp_display_get_shell (display);
930 GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell);
931
932 gimp_statusbar_push (statusbar, "menu-tooltip",
933 NULL, "%s", tooltip);
934 }
935 }
936
937 static void
gui_menu_hide_tooltip(GimpUIManager * manager,Gimp * gimp)938 gui_menu_hide_tooltip (GimpUIManager *manager,
939 Gimp *gimp)
940 {
941 GimpContext *context = gimp_get_user_context (gimp);
942 GimpDisplay *display = gimp_context_get_display (context);
943
944 if (display)
945 {
946 GimpDisplayShell *shell = gimp_display_get_shell (display);
947 GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell);
948
949 gimp_statusbar_pop (statusbar, "menu-tooltip");
950 }
951 }
952
953 static void
gui_display_changed(GimpContext * context,GimpDisplay * display,Gimp * gimp)954 gui_display_changed (GimpContext *context,
955 GimpDisplay *display,
956 Gimp *gimp)
957 {
958 if (! display)
959 {
960 GimpImage *image = gimp_context_get_image (context);
961
962 if (image)
963 {
964 GList *list;
965
966 for (list = gimp_get_display_iter (gimp);
967 list;
968 list = g_list_next (list))
969 {
970 GimpDisplay *display2 = list->data;
971
972 if (gimp_display_get_image (display2) == image)
973 {
974 gimp_context_set_display (context, display2);
975
976 /* stop the emission of the original signal
977 * (the emission of the recursive signal is finished)
978 */
979 g_signal_stop_emission_by_name (context, "display-changed");
980 return;
981 }
982 }
983
984 gimp_context_set_image (context, NULL);
985 }
986 }
987
988 gimp_ui_manager_update (image_ui_manager, display);
989 }
990
991 typedef struct
992 {
993 const gchar *path;
994 guint key;
995 GdkModifierType mods;
996 }
997 accelData;
998
999 static void
gui_compare_accelerator(gpointer data,const gchar * accel_path,guint accel_key,GdkModifierType accel_mods,gboolean changed)1000 gui_compare_accelerator (gpointer data,
1001 const gchar *accel_path,
1002 guint accel_key,
1003 GdkModifierType accel_mods,
1004 gboolean changed)
1005 {
1006 accelData *accel = data;
1007
1008 if (accel->key == accel_key && accel->mods == accel_mods &&
1009 g_strcmp0 (accel->path, accel_path))
1010 {
1011 g_printerr ("Actions \"%s\" and \"%s\" use the same accelerator.\n"
1012 " Disabling the accelerator on \"%s\".\n",
1013 accel->path, accel_path, accel_path);
1014 gtk_accel_map_change_entry (accel_path, 0, 0, FALSE);
1015 }
1016 }
1017
1018 static void
gui_check_unique_accelerator(gpointer data,const gchar * accel_path,guint accel_key,GdkModifierType accel_mods,gboolean changed)1019 gui_check_unique_accelerator (gpointer data,
1020 const gchar *accel_path,
1021 guint accel_key,
1022 GdkModifierType accel_mods,
1023 gboolean changed)
1024 {
1025 if (gtk_accelerator_valid (accel_key, accel_mods) &&
1026 gui_check_action_exists (accel_path))
1027 {
1028 accelData accel;
1029
1030 accel.path = accel_path;
1031 accel.key = accel_key;
1032 accel.mods = accel_mods;
1033
1034 gtk_accel_map_foreach_unfiltered (&accel,
1035 gui_compare_accelerator);
1036 }
1037 }
1038
1039 static gboolean
gui_check_action_exists(const gchar * accel_path)1040 gui_check_action_exists (const gchar *accel_path)
1041 {
1042 GimpUIManager *manager;
1043 gboolean action_exists = FALSE;
1044 GList *list;
1045
1046 manager = gimp_ui_managers_from_name ("<Image>")->data;
1047
1048 for (list = gimp_ui_manager_get_action_groups (manager);
1049 list;
1050 list = g_list_next (list))
1051 {
1052 GimpActionGroup *group = list->data;
1053 GList *actions = NULL;
1054 GList *list2;
1055
1056 actions = gimp_action_group_list_actions (group);
1057
1058 for (list2 = actions; list2; list2 = g_list_next (list2))
1059 {
1060 GimpAction *action = list2->data;
1061 const gchar *path = gimp_action_get_accel_path (action);
1062
1063 if (g_strcmp0 (path, accel_path) == 0)
1064 {
1065 action_exists = TRUE;
1066 break;
1067 }
1068 }
1069
1070 g_list_free (actions);
1071
1072 if (action_exists)
1073 break;
1074 }
1075
1076 return action_exists;
1077 }
1078