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