Gimp/app/gui/gui.c
Jehan 81d9da0c17 Bug 775931 - Shortcut for non-existing action shadows existing one.
Current logics of dealing with duplicate accelerators was just to delete
one randomly. This works ok in most case since we can't be in the head
of people and can't know which one they want to keep (yet we can't keep
both because that makes it very complicated to reset the shortcut
appropriately by hand/GUI, without a tedious work of researching which
other action uses the same shortcut. See commit 2a232398c4).

There is still some cases where we can be a bit more clever than random
deletion: when one of the accelerator is mapped to a non-existing
action. In this case, let's delete this accelerator in priority. Not
only the chances of this being the expected result are higher; but even
worse, there is anyway no GUI way to delete the accelerator for the
non-existing action! Thus you could try and reset your existing action's
shortcut as many times as you want in the GUI, it would always end up
deleted at next startup!

Note that if the non-existing action's shortcut has no duplicate, it
won't be deleted. This ensure that you could uninstall a plugin, then
reinstall it later and still have your custom shortcuts saved in the
meantime. A shortcut of a non-existing action will *only* be cleaned out
if it is redundant with the shortcut of an existing action.
2017-12-05 02:07:16 +01:00

988 lines
31 KiB
C

/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdlib.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpwidgets/gimpwidgets.h"
#include "libgimpwidgets/gimpwidgets-private.h"
#include "gui-types.h"
#include "config/gimpguiconfig.h"
#include "core/gimp.h"
#include "core/gimpcontainer.h"
#include "core/gimpcontext.h"
#include "core/gimpimage.h"
#include "core/gimptoolinfo.h"
#include "plug-in/gimpenvirontable.h"
#include "plug-in/gimppluginmanager.h"
#include "display/gimpdisplay.h"
#include "display/gimpdisplay-foreach.h"
#include "display/gimpdisplayshell.h"
#include "display/gimpstatusbar.h"
#include "tools/gimp-tools.h"
#include "widgets/gimpaction-history.h"
#include "widgets/gimpclipboard.h"
#include "widgets/gimpcolorselectorpalette.h"
#include "widgets/gimpcontrollers.h"
#include "widgets/gimpdevices.h"
#include "widgets/gimpdialogfactory.h"
#include "widgets/gimpdnd.h"
#include "widgets/gimprender.h"
#include "widgets/gimphelp.h"
#include "widgets/gimphelp-ids.h"
#include "widgets/gimpmenufactory.h"
#include "widgets/gimpmessagebox.h"
#include "widgets/gimpsessioninfo.h"
#include "widgets/gimpuimanager.h"
#include "widgets/gimpwidgets-utils.h"
#include "widgets/gimplanguagestore-parser.h"
#include "actions/actions.h"
#include "actions/windows-commands.h"
#include "menus/menus.h"
#include "dialogs/dialogs.h"
#include "gimpuiconfigurer.h"
#include "gui.h"
#include "gui-unique.h"
#include "gui-vtable.h"
#include "icon-themes.h"
#include "session.h"
#include "splash.h"
#include "themes.h"
#ifdef GDK_WINDOWING_QUARTZ
#import <AppKit/AppKit.h>
#include <gtkosxapplication.h>
/* Forward declare since we are building against old SDKs. */
#if !defined(MAC_OS_X_VERSION_10_12) || \
MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12
@interface NSWindow(ForwardDeclarations)
+ (void)setAllowsAutomaticWindowTabbing:(BOOL)allow;
@end
#endif
#endif /* GDK_WINDOWING_QUARTZ */
#include "gimp-intl.h"
/* local function prototypes */
static gchar * gui_sanity_check (void);
static void gui_help_func (const gchar *help_id,
gpointer help_data);
static gboolean gui_get_background_func (GimpRGB *color);
static gboolean gui_get_foreground_func (GimpRGB *color);
static void gui_initialize_after_callback (Gimp *gimp,
GimpInitStatusFunc callback);
static void gui_restore_callback (Gimp *gimp,
GimpInitStatusFunc callback);
static void gui_restore_after_callback (Gimp *gimp,
GimpInitStatusFunc callback);
static gboolean gui_exit_callback (Gimp *gimp,
gboolean force);
static gboolean gui_exit_after_callback (Gimp *gimp,
gboolean force);
static void gui_show_tooltips_notify (GimpGuiConfig *gui_config,
GParamSpec *pspec,
Gimp *gimp);
static void gui_show_help_button_notify (GimpGuiConfig *gui_config,
GParamSpec *pspec,
Gimp *gimp);
static void gui_user_manual_notify (GimpGuiConfig *gui_config,
GParamSpec *pspec,
Gimp *gimp);
static void gui_single_window_mode_notify (GimpGuiConfig *gui_config,
GParamSpec *pspec,
GimpUIConfigurer *ui_configurer);
static void gui_tearoff_menus_notify (GimpGuiConfig *gui_config,
GParamSpec *pspec,
GtkUIManager *manager);
static void gui_clipboard_changed (Gimp *gimp);
static void gui_menu_show_tooltip (GimpUIManager *manager,
const gchar *tooltip,
Gimp *gimp);
static void gui_menu_hide_tooltip (GimpUIManager *manager,
Gimp *gimp);
static void gui_display_changed (GimpContext *context,
GimpDisplay *display,
Gimp *gimp);
static void gui_compare_accelerator (gpointer data,
const gchar *accel_path,
guint accel_key,
GdkModifierType accel_mods,
gboolean changed);
static void gui_check_unique_accelerator (gpointer data,
const gchar *accel_path,
guint accel_key,
GdkModifierType accel_mods,
gboolean changed);
static gboolean gui_check_action_exists (const gchar *accel_path);
/* private variables */
static Gimp *the_gui_gimp = NULL;
static GimpUIManager *image_ui_manager = NULL;
static GimpUIConfigurer *ui_configurer = NULL;
static GdkScreen *initial_screen = NULL;
static gint initial_monitor = -1;
/* public functions */
void
gui_libs_init (GOptionContext *context)
{
g_return_if_fail (context != NULL);
g_option_context_add_group (context, gtk_get_option_group (TRUE));
}
void
gui_abort (const gchar *abort_message)
{
GtkWidget *dialog;
GtkWidget *box;
g_return_if_fail (abort_message != NULL);
dialog = gimp_dialog_new (_("GIMP Message"), "gimp-abort",
NULL, GTK_DIALOG_MODAL, NULL, NULL,
_("_OK"), GTK_RESPONSE_OK,
NULL);
gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
box = g_object_new (GIMP_TYPE_MESSAGE_BOX,
"icon-name", GIMP_ICON_WILBER_EEK,
"border-width", 12,
NULL);
gimp_message_box_set_text (GIMP_MESSAGE_BOX (box), "%s", abort_message);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
box, TRUE, TRUE, 0);
gtk_widget_show (box);
gimp_dialog_run (GIMP_DIALOG (dialog));
exit (EXIT_FAILURE);
}
GimpInitStatusFunc
gui_init (Gimp *gimp,
gboolean no_splash)
{
GimpInitStatusFunc status_callback = NULL;
gchar *abort_message;
g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
g_return_val_if_fail (the_gui_gimp == NULL, NULL);
abort_message = gui_sanity_check ();
if (abort_message)
gui_abort (abort_message);
the_gui_gimp = gimp;
/* TRANSLATORS: there is no need to translate this in GIMP. This uses
* "gtk20" domain as a special trick to determine language direction,
* but xgettext extracts it anyway mistakenly into GIMP po files.
* Leave an empty string as translation. It does not matter.
*/
if (g_strcmp0 (dgettext ("gtk20", "default:LTR"), "default:RTL") == 0)
/* Normally this should have been taken care of during command line
* parsing as a post-parse hook of gtk_get_option_group(), using the
* system locales.
* But user config may have overriden the language, therefore we must
* check the widget directions again.
*/
gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL);
else
gtk_widget_set_default_direction (GTK_TEXT_DIR_LTR);
gui_unique_init (gimp);
gimp_language_store_parser_init ();
/* initialize icon themes before gimp_widgets_init() so we avoid
* setting the configured theme twice
*/
icon_themes_init (gimp);
gimp_widgets_init (gui_help_func,
gui_get_foreground_func,
gui_get_background_func,
NULL);
g_type_class_ref (GIMP_TYPE_COLOR_SELECT);
/* disable automatic startup notification */
gtk_window_set_auto_startup_notification (FALSE);
#ifdef GDK_WINDOWING_QUARTZ
/* Before the first window is created (typically the splash window),
* we need to disable automatic tabbing behavior introduced on Sierra.
* This is known to cause all kinds of weird issues (see for instance
* Bugzilla #776294) and needs proper GTK+ support if we would want to
* enable it.
*/
if ([NSWindow respondsToSelector:@selector(setAllowsAutomaticWindowTabbing:)])
[NSWindow setAllowsAutomaticWindowTabbing:NO];
#endif /* GDK_WINDOWING_QUARTZ */
gimp_dnd_init (gimp);
themes_init (gimp);
initial_monitor = gimp_get_monitor_at_pointer (&initial_screen);
gtk_widget_set_default_colormap (gdk_screen_get_rgb_colormap (initial_screen));
if (! no_splash)
{
splash_create (gimp->be_verbose, initial_screen, initial_monitor);
status_callback = splash_update;
}
g_signal_connect_after (gimp, "initialize",
G_CALLBACK (gui_initialize_after_callback),
NULL);
g_signal_connect (gimp, "restore",
G_CALLBACK (gui_restore_callback),
NULL);
g_signal_connect_after (gimp, "restore",
G_CALLBACK (gui_restore_after_callback),
NULL);
g_signal_connect (gimp, "exit",
G_CALLBACK (gui_exit_callback),
NULL);
g_signal_connect_after (gimp, "exit",
G_CALLBACK (gui_exit_after_callback),
NULL);
return status_callback;
}
gint
gui_get_initial_monitor (Gimp *gimp,
GdkScreen **screen)
{
g_return_val_if_fail (GIMP_IS_GIMP (gimp), 0);
g_return_val_if_fail (screen != NULL, 0);
*screen = initial_screen;
return initial_monitor;
}
/* private functions */
static gchar *
gui_sanity_check (void)
{
#define GTK_REQUIRED_MAJOR 2
#define GTK_REQUIRED_MINOR 24
#define GTK_REQUIRED_MICRO 10
const gchar *mismatch = gtk_check_version (GTK_REQUIRED_MAJOR,
GTK_REQUIRED_MINOR,
GTK_REQUIRED_MICRO);
if (mismatch)
{
return g_strdup_printf
("%s\n\n"
"GIMP requires GTK+ version %d.%d.%d or later.\n"
"Installed GTK+ version is %d.%d.%d.\n\n"
"Somehow you or your software packager managed\n"
"to install GIMP with an older GTK+ version.\n\n"
"Please upgrade to GTK+ version %d.%d.%d or later.",
mismatch,
GTK_REQUIRED_MAJOR, GTK_REQUIRED_MINOR, GTK_REQUIRED_MICRO,
gtk_major_version, gtk_minor_version, gtk_micro_version,
GTK_REQUIRED_MAJOR, GTK_REQUIRED_MINOR, GTK_REQUIRED_MICRO);
}
#undef GTK_REQUIRED_MAJOR
#undef GTK_REQUIRED_MINOR
#undef GTK_REQUIRED_MICRO
return NULL;
}
static void
gui_help_func (const gchar *help_id,
gpointer help_data)
{
g_return_if_fail (GIMP_IS_GIMP (the_gui_gimp));
gimp_help (the_gui_gimp, NULL, NULL, help_id);
}
static gboolean
gui_get_foreground_func (GimpRGB *color)
{
g_return_val_if_fail (color != NULL, FALSE);
g_return_val_if_fail (GIMP_IS_GIMP (the_gui_gimp), FALSE);
gimp_context_get_foreground (gimp_get_user_context (the_gui_gimp), color);
return TRUE;
}
static gboolean
gui_get_background_func (GimpRGB *color)
{
g_return_val_if_fail (color != NULL, FALSE);
g_return_val_if_fail (GIMP_IS_GIMP (the_gui_gimp), FALSE);
gimp_context_get_background (gimp_get_user_context (the_gui_gimp), color);
return TRUE;
}
static void
gui_initialize_after_callback (Gimp *gimp,
GimpInitStatusFunc status_callback)
{
const gchar *name = NULL;
g_return_if_fail (GIMP_IS_GIMP (gimp));
if (gimp->be_verbose)
g_print ("INIT: %s\n", G_STRFUNC);
#if defined (GDK_WINDOWING_X11)
name = "DISPLAY";
#elif defined (GDK_WINDOWING_DIRECTFB) || defined (GDK_WINDOWING_FB)
name = "GDK_DISPLAY";
#endif
/* TODO: Need to care about display migration with GTK+ 2.2 at some point */
if (name)
{
gchar *display = gdk_get_display ();
gimp_environ_table_add (gimp->plug_in_manager->environ_table,
name, display, NULL);
g_free (display);
}
gimp_tools_init (gimp);
gimp_context_set_tool (gimp_get_user_context (gimp),
gimp_tool_info_get_standard (gimp));
}
static void
gui_restore_callback (Gimp *gimp,
GimpInitStatusFunc status_callback)
{
GimpDisplayConfig *display_config = GIMP_DISPLAY_CONFIG (gimp->config);
GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (gimp->config);
if (gimp->be_verbose)
g_print ("INIT: %s\n", G_STRFUNC);
gui_vtable_init (gimp);
if (! gui_config->show_tooltips)
gimp_help_disable_tooltips ();
g_signal_connect (gui_config, "notify::show-tooltips",
G_CALLBACK (gui_show_tooltips_notify),
gimp);
gimp_dialogs_show_help_button (gui_config->use_help &&
gui_config->show_help_button);
g_signal_connect (gui_config, "notify::use-help",
G_CALLBACK (gui_show_help_button_notify),
gimp);
g_signal_connect (gui_config, "notify::user-manual-online",
G_CALLBACK (gui_user_manual_notify),
gimp);
g_signal_connect (gui_config, "notify::show-help-button",
G_CALLBACK (gui_show_help_button_notify),
gimp);
g_signal_connect (gimp_get_user_context (gimp), "display-changed",
G_CALLBACK (gui_display_changed),
gimp);
/* make sure the monitor resolution is valid */
if (display_config->monitor_res_from_gdk ||
display_config->monitor_xres < GIMP_MIN_RESOLUTION ||
display_config->monitor_yres < GIMP_MIN_RESOLUTION)
{
gdouble xres, yres;
gimp_get_monitor_resolution (initial_screen,
initial_monitor,
&xres, &yres);
g_object_set (gimp->config,
"monitor-xresolution", xres,
"monitor-yresolution", yres,
"monitor-resolution-from-windowing-system", TRUE,
NULL);
}
actions_init (gimp);
menus_init (gimp, global_action_factory);
gimp_render_init (gimp);
dialogs_init (gimp, global_menu_factory);
gimp_clipboard_init (gimp);
if (gimp_get_clipboard_image (gimp))
gimp_clipboard_set_image (gimp, gimp_get_clipboard_image (gimp));
else
gimp_clipboard_set_buffer (gimp, gimp_get_clipboard_buffer (gimp));
g_signal_connect (gimp, "clipboard-changed",
G_CALLBACK (gui_clipboard_changed),
NULL);
gimp_devices_init (gimp);
gimp_controllers_init (gimp);
session_init (gimp);
g_type_class_unref (g_type_class_ref (GIMP_TYPE_COLOR_SELECTOR_PALETTE));
status_callback (NULL, _("Tool Options"), 1.0);
gimp_tools_restore (gimp);
}
#ifdef GDK_WINDOWING_QUARTZ
static void
gui_add_to_app_menu (GimpUIManager *ui_manager,
GtkosxApplication *osx_app,
const gchar *action_path,
gint index)
{
GtkWidget *item;
item = gtk_ui_manager_get_widget (GTK_UI_MANAGER (ui_manager), action_path);
if (GTK_IS_MENU_ITEM (item))
gtkosx_application_insert_app_menu_item (osx_app, GTK_WIDGET (item), index);
}
static gboolean
gui_quartz_quit_callback (GtkosxApplication *osx_app,
GimpUIManager *ui_manager)
{
gimp_ui_manager_activate_action (ui_manager, "file", "file-quit");
return TRUE;
}
#endif
static void
gui_restore_after_callback (Gimp *gimp,
GimpInitStatusFunc status_callback)
{
GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (gimp->config);
GimpDisplay *display;
if (gimp->be_verbose)
g_print ("INIT: %s\n", G_STRFUNC);
gimp->message_handler = GIMP_MESSAGE_BOX;
/* load the recent documents after gimp_real_restore() because we
* need the mime-types implemented by plug-ins
*/
status_callback (NULL, _("Documents"), 0.9);
gimp_recent_list_load (gimp);
/* enable this to always have icons everywhere */
if (g_getenv ("GIMP_ICONS_LIKE_A_BOSS"))
{
GdkScreen *screen = gdk_screen_get_default ();
g_object_set (G_OBJECT (gtk_settings_get_for_screen (screen)),
"gtk-button-images", TRUE,
"gtk-menu-images", TRUE,
NULL);
}
if (gui_config->restore_accels)
menus_restore (gimp);
ui_configurer = g_object_new (GIMP_TYPE_UI_CONFIGURER,
"gimp", gimp,
NULL);
image_ui_manager = gimp_menu_factory_manager_new (global_menu_factory,
"<Image>",
gimp,
gui_config->tearoff_menus);
gimp_ui_manager_update (image_ui_manager, gimp);
/* Check that every accelerator is unique. */
gtk_accel_map_foreach_unfiltered (NULL,
gui_check_unique_accelerator);
gimp_action_history_init (gimp);
#ifdef GDK_WINDOWING_QUARTZ
{
GtkosxApplication *osx_app;
GtkWidget *menu;
GtkWidget *item;
[[NSUserDefaults standardUserDefaults] setObject:@"NO"
forKey:@"NSTreatUnknownArgumentsAsOpen"];
osx_app = gtkosx_application_get ();
menu = gtk_ui_manager_get_widget (GTK_UI_MANAGER (image_ui_manager),
"/image-menubar");
if (GTK_IS_MENU_ITEM (menu))
menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
gtkosx_application_set_menu_bar (osx_app, GTK_MENU_SHELL (menu));
gtkosx_application_set_use_quartz_accelerators (osx_app, FALSE);
gui_add_to_app_menu (image_ui_manager, osx_app,
"/image-menubar/Help/dialogs-about", 0);
gui_add_to_app_menu (image_ui_manager, osx_app,
"/image-menubar/Help/dialogs-search-action", 1);
#define PREFERENCES "/image-menubar/Edit/Preferences/"
gui_add_to_app_menu (image_ui_manager, osx_app,
PREFERENCES "dialogs-preferences", 3);
gui_add_to_app_menu (image_ui_manager, osx_app,
PREFERENCES "dialogs-input-devices", 4);
gui_add_to_app_menu (image_ui_manager, osx_app,
PREFERENCES "dialogs-keyboard-shortcuts", 5);
gui_add_to_app_menu (image_ui_manager, osx_app,
PREFERENCES "dialogs-module-dialog", 6);
gui_add_to_app_menu (image_ui_manager, osx_app,
PREFERENCES "plug-in-unit-editor", 7);
#undef PREFERENCES
item = gtk_separator_menu_item_new ();
gtkosx_application_insert_app_menu_item (osx_app, item, 8);
item = gtk_ui_manager_get_widget (GTK_UI_MANAGER (image_ui_manager),
"/image-menubar/File/file-quit");
gtk_widget_hide (item);
g_signal_connect (osx_app, "NSApplicationBlockTermination",
G_CALLBACK (gui_quartz_quit_callback),
image_ui_manager);
gtkosx_application_ready (osx_app);
}
#endif /* GDK_WINDOWING_QUARTZ */
g_signal_connect_object (gui_config, "notify::single-window-mode",
G_CALLBACK (gui_single_window_mode_notify),
ui_configurer, 0);
g_signal_connect_object (gui_config, "notify::tearoff-menus",
G_CALLBACK (gui_tearoff_menus_notify),
image_ui_manager, 0);
g_signal_connect (image_ui_manager, "show-tooltip",
G_CALLBACK (gui_menu_show_tooltip),
gimp);
g_signal_connect (image_ui_manager, "hide-tooltip",
G_CALLBACK (gui_menu_hide_tooltip),
gimp);
gimp_devices_restore (gimp);
gimp_controllers_restore (gimp, image_ui_manager);
if (status_callback == splash_update)
splash_destroy ();
if (gimp_get_show_gui (gimp))
{
GimpDisplayShell *shell;
GtkWidget *toplevel;
/* create the empty display */
display = GIMP_DISPLAY (gimp_create_display (gimp, NULL,
GIMP_UNIT_PIXEL, 1.0,
G_OBJECT (initial_screen),
initial_monitor));
shell = gimp_display_get_shell (display);
if (gui_config->restore_session)
session_restore (gimp,
initial_screen,
initial_monitor);
/* move keyboard focus to the display */
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell));
gtk_window_present (GTK_WINDOW (toplevel));
}
/* indicate that the application has finished loading */
gdk_notify_startup_complete ();
/* clear startup monitor variables */
initial_screen = NULL;
initial_monitor = -1;
}
static gboolean
gui_exit_callback (Gimp *gimp,
gboolean force)
{
GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (gimp->config);
if (gimp->be_verbose)
g_print ("EXIT: %s\n", G_STRFUNC);
if (! force && gimp_displays_dirty (gimp))
{
GdkScreen *screen;
gint monitor;
monitor = gimp_get_monitor_at_pointer (&screen);
gimp_dialog_factory_dialog_raise (gimp_dialog_factory_get_singleton (),
screen, monitor,
"gimp-quit-dialog", -1);
return TRUE; /* stop exit for now */
}
gimp->message_handler = GIMP_CONSOLE;
gui_unique_exit ();
if (gui_config->save_session_info)
session_save (gimp, FALSE);
if (gui_config->save_device_status)
gimp_devices_save (gimp, FALSE);
if (TRUE /* gui_config->save_controllers */)
gimp_controllers_save (gimp);
g_signal_handlers_disconnect_by_func (gimp_get_user_context (gimp),
gui_display_changed,
gimp);
gimp_displays_delete (gimp);
if (gui_config->save_accels)
menus_save (gimp, FALSE);
gimp_tools_save (gimp, gui_config->save_tool_options, FALSE);
gimp_tools_exit (gimp);
gimp_language_store_parser_clean ();
return FALSE; /* continue exiting */
}
static gboolean
gui_exit_after_callback (Gimp *gimp,
gboolean force)
{
if (gimp->be_verbose)
g_print ("EXIT: %s\n", G_STRFUNC);
g_signal_handlers_disconnect_by_func (gimp->config,
gui_show_help_button_notify,
gimp);
g_signal_handlers_disconnect_by_func (gimp->config,
gui_user_manual_notify,
gimp);
g_signal_handlers_disconnect_by_func (gimp->config,
gui_show_tooltips_notify,
gimp);
gimp_action_history_exit (gimp);
g_object_unref (image_ui_manager);
image_ui_manager = NULL;
g_object_unref (ui_configurer);
ui_configurer = NULL;
/* exit the clipboard before shutting down the GUI because it runs
* a whole lot of code paths. See bug #731389.
*/
g_signal_handlers_disconnect_by_func (gimp,
G_CALLBACK (gui_clipboard_changed),
NULL);
gimp_clipboard_exit (gimp);
session_exit (gimp);
menus_exit (gimp);
actions_exit (gimp);
gimp_render_exit (gimp);
gimp_controllers_exit (gimp);
gimp_devices_exit (gimp);
dialogs_exit (gimp);
themes_exit (gimp);
g_type_class_unref (g_type_class_peek (GIMP_TYPE_COLOR_SELECT));
return FALSE; /* continue exiting */
}
static void
gui_show_tooltips_notify (GimpGuiConfig *gui_config,
GParamSpec *param_spec,
Gimp *gimp)
{
if (gui_config->show_tooltips)
gimp_help_enable_tooltips ();
else
gimp_help_disable_tooltips ();
}
static void
gui_show_help_button_notify (GimpGuiConfig *gui_config,
GParamSpec *param_spec,
Gimp *gimp)
{
gimp_dialogs_show_help_button (gui_config->use_help &&
gui_config->show_help_button);
}
static void
gui_user_manual_notify (GimpGuiConfig *gui_config,
GParamSpec *param_spec,
Gimp *gimp)
{
gimp_help_user_manual_changed (gimp);
}
static void
gui_single_window_mode_notify (GimpGuiConfig *gui_config,
GParamSpec *pspec,
GimpUIConfigurer *ui_configurer)
{
gimp_ui_configurer_configure (ui_configurer,
gui_config->single_window_mode);
}
static void
gui_tearoff_menus_notify (GimpGuiConfig *gui_config,
GParamSpec *pspec,
GtkUIManager *manager)
{
gtk_ui_manager_set_add_tearoffs (manager, gui_config->tearoff_menus);
}
static void
gui_clipboard_changed (Gimp *gimp)
{
if (gimp_get_clipboard_image (gimp))
gimp_clipboard_set_image (gimp, gimp_get_clipboard_image (gimp));
else
gimp_clipboard_set_buffer (gimp, gimp_get_clipboard_buffer (gimp));
}
static void
gui_menu_show_tooltip (GimpUIManager *manager,
const gchar *tooltip,
Gimp *gimp)
{
GimpContext *context = gimp_get_user_context (gimp);
GimpDisplay *display = gimp_context_get_display (context);
if (display)
{
GimpDisplayShell *shell = gimp_display_get_shell (display);
GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell);
gimp_statusbar_push (statusbar, "menu-tooltip",
NULL, "%s", tooltip);
}
}
static void
gui_menu_hide_tooltip (GimpUIManager *manager,
Gimp *gimp)
{
GimpContext *context = gimp_get_user_context (gimp);
GimpDisplay *display = gimp_context_get_display (context);
if (display)
{
GimpDisplayShell *shell = gimp_display_get_shell (display);
GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell);
gimp_statusbar_pop (statusbar, "menu-tooltip");
}
}
static void
gui_display_changed (GimpContext *context,
GimpDisplay *display,
Gimp *gimp)
{
if (! display)
{
GimpImage *image = gimp_context_get_image (context);
if (image)
{
GList *list;
for (list = gimp_get_display_iter (gimp);
list;
list = g_list_next (list))
{
GimpDisplay *display2 = list->data;
if (gimp_display_get_image (display2) == image)
{
gimp_context_set_display (context, display2);
/* stop the emission of the original signal
* (the emission of the recursive signal is finished)
*/
g_signal_stop_emission_by_name (context, "display-changed");
return;
}
}
gimp_context_set_image (context, NULL);
}
}
gimp_ui_manager_update (image_ui_manager, display);
}
typedef struct
{
const gchar *path;
guint key;
GdkModifierType mods;
}
accelData;
static void
gui_compare_accelerator (gpointer data,
const gchar *accel_path,
guint accel_key,
GdkModifierType accel_mods,
gboolean changed)
{
accelData *accel = data;
if (accel->key == accel_key && accel->mods == accel_mods &&
g_strcmp0 (accel->path, accel_path))
{
g_warning ("Actions \"%s\" and \"%s\" use the same accelerator.\n"
"Disable the accelerator on \"%s\".",
accel->path, accel_path, accel_path);
gtk_accel_map_change_entry (accel_path, 0, 0, FALSE);
}
}
static void
gui_check_unique_accelerator (gpointer data,
const gchar *accel_path,
guint accel_key,
GdkModifierType accel_mods,
gboolean changed)
{
if (gtk_accelerator_valid (accel_key, accel_mods) &&
gui_check_action_exists (accel_path))
{
accelData accel;
accel.path = accel_path;
accel.key = accel_key;
accel.mods = accel_mods;
gtk_accel_map_foreach_unfiltered (&accel,
gui_compare_accelerator);
}
}
static gboolean
gui_check_action_exists (const gchar *accel_path)
{
GimpUIManager *manager;
gboolean action_exists = FALSE;
GList *list;
manager = gimp_ui_managers_from_name ("<Image>")->data;
for (list = gtk_ui_manager_get_action_groups (GTK_UI_MANAGER (manager));
list;
list = g_list_next (list))
{
GtkActionGroup *group = list->data;
GList *actions = NULL;
GList *list2;
actions = gtk_action_group_list_actions (GTK_ACTION_GROUP (group));
for (list2 = actions; list2; list2 = g_list_next (list2))
{
const gchar *path;
GtkAction *action = list2->data;
path = gtk_action_get_accel_path (action);
if (g_strcmp0 (path, accel_path) == 0)
{
action_exists = TRUE;
break;
}
}
g_list_free (actions);
if (action_exists)
break;
}
return action_exists;
}