Gimp/app/actions/windows-actions.c
Jehan 811d356953 app: GimpAction now has a "reason" parameter to explain being disabled.
In some cases, in particular for actions generated from plug-in
procedure right now, we were displaying the reason of the insensitivity
(typically right now, only the drawable type is cited). This was done by
appending the reason to the tooltip, separated by 2 newlines, which
resulted in extra ugly design, no nice way to style this info directly
(with pango for instance if the widget display allows it, or on a
separate info widget in a possible future, or whatnot).

Also it would mean that the action search could match a disabled action
by mistake if a search word happens to be in the reason message.

This improves the situation with the following changes:
* gimp_action_set_sensitive() now takes an optional reason string to set
  the reason message.
* Same for gimp_action_group_set_action_sensitive().
* gimp_action_get_sensitive() returns an optional reason string.
* gimp_procedure_get_sensitive()'s tooltip return value now becomes a
  reason (it won't contain anymore the tooltip and the reason
  concatenated, only the reason for separate processing).
2021-04-23 19:43:30 +02:00

618 lines
22 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 <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpwidgets/gimpwidgets.h"
#include "actions-types.h"
#include "config/gimpdisplayconfig.h"
#include "config/gimpguiconfig.h"
#include "core/gimp.h"
#include "core/gimpimage.h"
#include "core/gimplist.h"
#include "widgets/gimpaction.h"
#include "widgets/gimpactiongroup.h"
#include "widgets/gimpdialogfactory.h"
#include "widgets/gimpdock.h"
#include "widgets/gimpdockwindow.h"
#include "widgets/gimphelp-ids.h"
#include "display/gimpdisplay.h"
#include "display/gimpdisplayshell.h"
#include "dialogs/dialogs.h"
#include "windows-actions.h"
#include "windows-commands.h"
#include "gimp-intl.h"
static void windows_actions_display_add (GimpContainer *container,
GimpDisplay *display,
GimpActionGroup *group);
static void windows_actions_display_remove (GimpContainer *container,
GimpDisplay *display,
GimpActionGroup *group);
static void windows_actions_display_reorder (GimpContainer *container,
GimpDisplay *display,
gint position,
GimpActionGroup *group);
static void windows_actions_image_notify (GimpDisplay *display,
const GParamSpec *unused,
GimpActionGroup *group);
static void windows_actions_title_notify (GimpDisplayShell *shell,
const GParamSpec *unused,
GimpActionGroup *group);
static void windows_actions_update_display_accels (GimpActionGroup *group);
static void windows_actions_dock_window_added (GimpDialogFactory *factory,
GimpDockWindow *dock_window,
GimpActionGroup *group);
static void windows_actions_dock_window_removed (GimpDialogFactory *factory,
GimpDockWindow *dock_window,
GimpActionGroup *group);
static void windows_actions_dock_window_notify (GimpDockWindow *dock,
const GParamSpec *pspec,
GimpActionGroup *group);
static void windows_actions_recent_add (GimpContainer *container,
GimpSessionInfo *info,
GimpActionGroup *group);
static void windows_actions_recent_remove (GimpContainer *container,
GimpSessionInfo *info,
GimpActionGroup *group);
static void windows_actions_single_window_mode_notify (GimpDisplayConfig *config,
GParamSpec *pspec,
GimpActionGroup *group);
/* The only reason we have "Tab" in the action entries below is to
* give away the hardcoded keyboard shortcut. If the user changes the
* shortcut to something else, both that shortcut and Tab will
* work. The reason we have the shortcut hardcoded is because
* gtk_accelerator_valid() returns FALSE for GDK_tab.
*/
static const GimpActionEntry windows_actions[] =
{
{ "windows-menu", NULL, NC_("windows-action",
"_Windows") },
{ "windows-docks-menu", NULL, NC_("windows-action",
"_Recently Closed Docks") },
{ "windows-dialogs-menu", NULL, NC_("windows-action",
"_Dockable Dialogs") },
{ "windows-show-display-next", NULL,
NC_("windows-action", "Next Image"), "<alt>Tab",
NC_("windows-action", "Switch to the next image"),
windows_show_display_next_cmd_callback,
NULL },
{ "windows-show-display-previous", NULL,
NC_("windows-action", "Previous Image"), "<alt><shift>Tab",
NC_("windows-action", "Switch to the previous image"),
windows_show_display_previous_cmd_callback,
NULL },
{ "windows-tab-position", NULL, NC_("windows-action",
"_Tabs Position") },
};
static const GimpToggleActionEntry windows_toggle_actions[] =
{
{ "windows-hide-docks", NULL,
NC_("windows-action", "_Hide Docks"), "Tab",
NC_("windows-action", "When enabled, docks and other dialogs are hidden, leaving only image windows."),
windows_hide_docks_cmd_callback,
FALSE,
GIMP_HELP_WINDOWS_HIDE_DOCKS },
{ "windows-show-tabs", NULL,
NC_("windows-action", "_Show Tabs"), NULL,
NC_("windows-action", "When enabled, the image tabs bar is shown."),
windows_show_tabs_cmd_callback,
FALSE,
GIMP_HELP_WINDOWS_SHOW_TABS },
{ "windows-use-single-window-mode", NULL,
NC_("windows-action", "Single-Window _Mode"), NULL,
NC_("windows-action", "When enabled, GIMP is in a single-window mode."),
windows_use_single_window_mode_cmd_callback,
FALSE,
GIMP_HELP_WINDOWS_USE_SINGLE_WINDOW_MODE }
};
static const GimpRadioActionEntry windows_tabs_position_actions[] =
{
{ "windows-tabs-position-top", GIMP_ICON_GO_TOP,
NC_("windows-tabs-position-action", "_Top"), NULL,
NC_("windows-tabs-position-action", "Position the tabs on the top"),
GIMP_POSITION_TOP, GIMP_HELP_WINDOWS_TABS_POSITION },
{ "windows-tabs-position-bottom", GIMP_ICON_GO_BOTTOM,
NC_("windows-tabs-position-action", "_Bottom"), NULL,
NC_("windows-tabs-position-action", "Position the tabs on the bottom"),
GIMP_POSITION_BOTTOM, GIMP_HELP_WINDOWS_TABS_POSITION },
{ "windows-tabs-position-left", GIMP_ICON_GO_FIRST,
NC_("windows-tabs-position-action", "_Left"), NULL,
NC_("windows-tabs-position-action", "Position the tabs on the left"),
GIMP_POSITION_LEFT, GIMP_HELP_WINDOWS_TABS_POSITION },
{ "windows-tabs-position-right", GIMP_ICON_GO_LAST,
NC_("windows-tabs-position-action", "_Right"), NULL,
NC_("windows-tabs-position-action", "Position the tabs on the right"),
GIMP_POSITION_RIGHT, GIMP_HELP_WINDOWS_TABS_POSITION },
};
void
windows_actions_setup (GimpActionGroup *group)
{
GList *list;
gimp_action_group_add_actions (group, "windows-action",
windows_actions,
G_N_ELEMENTS (windows_actions));
gimp_action_group_add_toggle_actions (group, "windows-action",
windows_toggle_actions,
G_N_ELEMENTS (windows_toggle_actions));
gimp_action_group_add_radio_actions (group, "windows-tabs-position-action",
windows_tabs_position_actions,
G_N_ELEMENTS (windows_tabs_position_actions),
NULL, 0,
windows_set_tabs_position_cmd_callback);
gimp_action_group_set_action_hide_empty (group, "windows-docks-menu", FALSE);
g_signal_connect_object (group->gimp->displays, "add",
G_CALLBACK (windows_actions_display_add),
group, 0);
g_signal_connect_object (group->gimp->displays, "remove",
G_CALLBACK (windows_actions_display_remove),
group, 0);
g_signal_connect_object (group->gimp->displays, "reorder",
G_CALLBACK (windows_actions_display_reorder),
group, 0);
for (list = gimp_get_display_iter (group->gimp);
list;
list = g_list_next (list))
{
GimpDisplay *display = list->data;
windows_actions_display_add (group->gimp->displays, display, group);
}
g_signal_connect_object (gimp_dialog_factory_get_singleton (), "dock-window-added",
G_CALLBACK (windows_actions_dock_window_added),
group, 0);
g_signal_connect_object (gimp_dialog_factory_get_singleton (), "dock-window-removed",
G_CALLBACK (windows_actions_dock_window_removed),
group, 0);
for (list = gimp_dialog_factory_get_open_dialogs (gimp_dialog_factory_get_singleton ());
list;
list = g_list_next (list))
{
GimpDockWindow *dock_window = list->data;
if (GIMP_IS_DOCK_WINDOW (dock_window))
windows_actions_dock_window_added (gimp_dialog_factory_get_singleton (),
dock_window,
group);
}
g_signal_connect_object (global_recent_docks, "add",
G_CALLBACK (windows_actions_recent_add),
group, 0);
g_signal_connect_object (global_recent_docks, "remove",
G_CALLBACK (windows_actions_recent_remove),
group, 0);
for (list = GIMP_LIST (global_recent_docks)->queue->head;
list;
list = g_list_next (list))
{
GimpSessionInfo *info = list->data;
windows_actions_recent_add (global_recent_docks, info, group);
}
g_signal_connect_object (group->gimp->config, "notify::single-window-mode",
G_CALLBACK (windows_actions_single_window_mode_notify),
group, 0);
}
void
windows_actions_update (GimpActionGroup *group,
gpointer data)
{
GimpGuiConfig *config = GIMP_GUI_CONFIG (group->gimp->config);
const gchar *action = NULL;
#define SET_ACTIVE(action,condition) \
gimp_action_group_set_action_active (group, action, (condition) != 0)
SET_ACTIVE ("windows-use-single-window-mode", config->single_window_mode);
SET_ACTIVE ("windows-hide-docks", config->hide_docks);
SET_ACTIVE ("windows-show-tabs", config->show_tabs);
switch (config->tabs_position)
{
case GIMP_POSITION_TOP:
action = "windows-tabs-position-top";
break;
case GIMP_POSITION_BOTTOM:
action = "windows-tabs-position-bottom";
break;
case GIMP_POSITION_LEFT:
action = "windows-tabs-position-left";
break;
case GIMP_POSITION_RIGHT:
action = "windows-tabs-position-right";
break;
default:
action = "windows-tabs-position-top";
break;
}
gimp_action_group_set_action_active (group, action, TRUE);
gimp_action_group_set_action_sensitive (group, "windows-tab-position", config->single_window_mode,
_("Single-window mode disabled"));
gimp_action_group_set_action_sensitive (group, "windows-show-tabs", config->single_window_mode,
_("Single-window mode disabled"));
#undef SET_ACTIVE
}
gchar *
windows_actions_dock_window_to_action_name (GimpDockWindow *dock_window)
{
return g_strdup_printf ("windows-dock-%04d",
gimp_dock_window_get_id (dock_window));
}
/* private functions */
static void
windows_actions_display_add (GimpContainer *container,
GimpDisplay *display,
GimpActionGroup *group)
{
GimpDisplayShell *shell = gimp_display_get_shell (display);
g_signal_connect_object (display, "notify::image",
G_CALLBACK (windows_actions_image_notify),
group, 0);
g_signal_connect_object (shell, "notify::title",
G_CALLBACK (windows_actions_title_notify),
group, 0);
windows_actions_image_notify (display, NULL, group);
}
static void
windows_actions_display_remove (GimpContainer *container,
GimpDisplay *display,
GimpActionGroup *group)
{
GimpDisplayShell *shell = gimp_display_get_shell (display);
GimpAction *action;
gchar *action_name;
if (shell)
g_signal_handlers_disconnect_by_func (shell,
windows_actions_title_notify,
group);
action_name = gimp_display_get_action_name (display);
action = gimp_action_group_get_action (group, action_name);
g_free (action_name);
if (action)
gimp_action_group_remove_action_and_accel (group, action);
windows_actions_update_display_accels (group);
}
static void
windows_actions_display_reorder (GimpContainer *container,
GimpDisplay *display,
gint new_index,
GimpActionGroup *group)
{
windows_actions_update_display_accels (group);
}
static void
windows_actions_image_notify (GimpDisplay *display,
const GParamSpec *unused,
GimpActionGroup *group)
{
GimpImage *image = gimp_display_get_image (display);
GimpAction *action;
gchar *action_name;
action_name = gimp_display_get_action_name (display);
action = gimp_action_group_get_action (group, action_name);
if (! action)
{
GimpActionEntry entry;
entry.name = action_name;
entry.icon_name = GIMP_ICON_IMAGE;
entry.label = "";
entry.accelerator = NULL;
entry.tooltip = NULL;
entry.callback = windows_show_display_cmd_callback;
entry.help_id = NULL;
gimp_action_group_add_actions (group, NULL, &entry, 1);
action = gimp_action_group_get_action (group, action_name);
g_object_set_data (G_OBJECT (action), "display", display);
}
g_free (action_name);
if (image)
{
const gchar *display_name;
gchar *escaped;
gchar *title;
display_name = gimp_image_get_display_name (image);
escaped = gimp_escape_uline (display_name);
title = g_strdup_printf ("%s-%d.%d", escaped,
gimp_image_get_id (image),
gimp_display_get_instance (display));
g_free (escaped);
g_object_set (action,
"visible", TRUE,
"label", title,
"tooltip", gimp_image_get_display_path (image),
"viewable", image,
"context", gimp_get_user_context (group->gimp),
NULL);
g_free (title);
windows_actions_update_display_accels (group);
}
else
{
g_object_set (action,
"visible", FALSE,
"viewable", NULL,
NULL);
}
}
static void
windows_actions_title_notify (GimpDisplayShell *shell,
const GParamSpec *unused,
GimpActionGroup *group)
{
windows_actions_image_notify (shell->display, NULL, group);
}
static void
windows_actions_update_display_accels (GimpActionGroup *group)
{
GList *list;
gint i;
for (list = gimp_get_display_iter (group->gimp), i = 0;
list && i < 10;
list = g_list_next (list), i++)
{
GimpDisplay *display = list->data;
GimpAction *action;
gchar *action_name;
if (! gimp_display_get_image (display))
break;
action_name = gimp_display_get_action_name (display);
action = gimp_action_group_get_action (group, action_name);
g_free (action_name);
if (action)
{
const gchar *accel_path;
guint accel_key;
accel_path = gimp_action_get_accel_path (action);
if (i < 9)
accel_key = GDK_KEY_1 + i;
else
accel_key = GDK_KEY_0;
gtk_accel_map_change_entry (accel_path,
accel_key, GDK_MOD1_MASK,
TRUE);
}
}
}
static void
windows_actions_dock_window_added (GimpDialogFactory *factory,
GimpDockWindow *dock_window,
GimpActionGroup *group)
{
GimpAction *action;
GimpActionEntry entry;
gchar *action_name = windows_actions_dock_window_to_action_name (dock_window);
entry.name = action_name;
entry.icon_name = NULL;
entry.label = "";
entry.accelerator = NULL;
entry.tooltip = NULL;
entry.callback = windows_show_dock_cmd_callback;
entry.help_id = GIMP_HELP_WINDOWS_SHOW_DOCK;
gimp_action_group_add_actions (group, NULL, &entry, 1);
action = gimp_action_group_get_action (group, action_name);
g_object_set (action,
"ellipsize", PANGO_ELLIPSIZE_END,
NULL);
g_object_set_data (G_OBJECT (action), "dock-window", dock_window);
g_free (action_name);
g_signal_connect_object (dock_window, "notify::title",
G_CALLBACK (windows_actions_dock_window_notify),
group, 0);
if (gtk_window_get_title (GTK_WINDOW (dock_window)))
windows_actions_dock_window_notify (dock_window, NULL, group);
}
static void
windows_actions_dock_window_removed (GimpDialogFactory *factory,
GimpDockWindow *dock_window,
GimpActionGroup *group)
{
GimpAction *action;
gchar *action_name;
action_name = windows_actions_dock_window_to_action_name (dock_window);
action = gimp_action_group_get_action (group, action_name);
g_free (action_name);
if (action)
gimp_action_group_remove_action_and_accel (group, action);
}
static void
windows_actions_dock_window_notify (GimpDockWindow *dock_window,
const GParamSpec *pspec,
GimpActionGroup *group)
{
GimpAction *action;
gchar *action_name;
action_name = windows_actions_dock_window_to_action_name (dock_window);
action = gimp_action_group_get_action (group, action_name);
g_free (action_name);
if (action)
g_object_set (action,
"label", gtk_window_get_title (GTK_WINDOW (dock_window)),
"tooltip", gtk_window_get_title (GTK_WINDOW (dock_window)),
NULL);
}
static void
windows_actions_recent_add (GimpContainer *container,
GimpSessionInfo *info,
GimpActionGroup *group)
{
GimpAction *action;
GimpActionEntry entry;
gint info_id;
static gint info_id_counter = 1;
gchar *action_name;
info_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (info),
"recent-action-id"));
if (! info_id)
{
info_id = info_id_counter++;
g_object_set_data (G_OBJECT (info), "recent-action-id",
GINT_TO_POINTER (info_id));
}
action_name = g_strdup_printf ("windows-recent-%04d", info_id);
entry.name = action_name;
entry.icon_name = NULL;
entry.label = gimp_object_get_name (info);
entry.accelerator = NULL;
entry.tooltip = gimp_object_get_name (info);
entry.callback = windows_open_recent_cmd_callback;
entry.help_id = GIMP_HELP_WINDOWS_OPEN_RECENT_DOCK;
gimp_action_group_add_actions (group, NULL, &entry, 1);
action = gimp_action_group_get_action (group, action_name);
g_object_set (action,
"ellipsize", PANGO_ELLIPSIZE_END,
"max-width-chars", 30,
NULL);
g_object_set_data (G_OBJECT (action), "info", info);
g_free (action_name);
}
static void
windows_actions_recent_remove (GimpContainer *container,
GimpSessionInfo *info,
GimpActionGroup *group)
{
GimpAction *action;
gint info_id;
gchar *action_name;
info_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (info),
"recent-action-id"));
action_name = g_strdup_printf ("windows-recent-%04d", info_id);
action = gimp_action_group_get_action (group, action_name);
g_free (action_name);
if (action)
gimp_action_group_remove_action_and_accel (group, action);
}
static void
windows_actions_single_window_mode_notify (GimpDisplayConfig *config,
GParamSpec *pspec,
GimpActionGroup *group)
{
gimp_action_group_set_action_active (group,
"windows-use-single-window-mode",
GIMP_GUI_CONFIG (config)->single_window_mode);
}