/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * 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 . */ #include "config.h" #include #include #include #include "libpikabase/pikabase.h" #include "libpikawidgets/pikawidgets.h" #include "actions-types.h" #include "config/pikadisplayconfig.h" #include "config/pikaguiconfig.h" #include "core/pika.h" #include "core/pikaimage.h" #include "core/pikalist.h" #include "widgets/pikaaction.h" #include "widgets/pikaactiongroup.h" #include "widgets/pikadialogfactory.h" #include "widgets/pikadock.h" #include "widgets/pikadockwindow.h" #include "widgets/pikahelp-ids.h" #include "display/pikadisplay.h" #include "display/pikadisplayshell.h" #include "dialogs/dialogs.h" #include "windows-actions.h" #include "windows-commands.h" #include "pika-intl.h" static void windows_actions_display_add (PikaContainer *container, PikaDisplay *display, PikaActionGroup *group); static void windows_actions_display_remove (PikaContainer *container, PikaDisplay *display, PikaActionGroup *group); static void windows_actions_display_reorder (PikaContainer *container, PikaDisplay *display, gint position, PikaActionGroup *group); static void windows_actions_image_notify (PikaDisplay *display, const GParamSpec *unused, PikaActionGroup *group); static void windows_actions_title_notify (PikaDisplayShell *shell, const GParamSpec *unused, PikaActionGroup *group); static void windows_actions_update_display_accels (PikaActionGroup *group); static void windows_actions_dock_window_added (PikaDialogFactory *factory, PikaDockWindow *dock_window, PikaActionGroup *group); static void windows_actions_dock_window_removed (PikaDialogFactory *factory, PikaDockWindow *dock_window, PikaActionGroup *group); static void windows_actions_dock_window_notify (PikaDockWindow *dock, const GParamSpec *pspec, PikaActionGroup *group); static void windows_actions_recent_add (PikaContainer *container, PikaSessionInfo *info, PikaActionGroup *group); static void windows_actions_recent_remove (PikaContainer *container, PikaSessionInfo *info, PikaActionGroup *group); static void windows_actions_single_window_mode_notify (PikaDisplayConfig *config, GParamSpec *pspec, PikaActionGroup *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 PikaActionEntry windows_actions[] = { { "windows-show-display-next", NULL, NC_("windows-action", "Next Image"), NULL, { "Tab", "Forward", NULL }, 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"), NULL, { "Tab", "Back", NULL }, 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 PikaToggleActionEntry windows_toggle_actions[] = { { "windows-hide-docks", NULL, NC_("windows-action", "_Hide Docks"), NULL, { NULL }, NC_("windows-action", "When enabled, docks and other dialogs are hidden, leaving only image windows."), windows_hide_docks_cmd_callback, FALSE, PIKA_HELP_WINDOWS_HIDE_DOCKS }, { "windows-show-tabs", NULL, NC_("windows-action", "_Show Tabs"), NULL, { NULL }, NC_("windows-action", "When enabled, the image tabs bar is shown."), windows_show_tabs_cmd_callback, TRUE, PIKA_HELP_WINDOWS_SHOW_TABS }, { "windows-use-single-window-mode", NULL, NC_("windows-action", "Single-Window _Mode"), NULL, { NULL }, NC_("windows-action", "When enabled, PIKA is in a single-window mode."), windows_use_single_window_mode_cmd_callback, TRUE, PIKA_HELP_WINDOWS_USE_SINGLE_WINDOW_MODE } }; static const PikaRadioActionEntry windows_tabs_position_actions[] = { { "windows-tabs-position-top", PIKA_ICON_GO_TOP, NC_("windows-tabs-position-action", "_Top"), NULL, { NULL }, NC_("windows-tabs-position-action", "Position the tabs on the top"), PIKA_POSITION_TOP, PIKA_HELP_WINDOWS_TABS_POSITION }, { "windows-tabs-position-bottom", PIKA_ICON_GO_BOTTOM, NC_("windows-tabs-position-action", "_Bottom"), NULL, { NULL }, NC_("windows-tabs-position-action", "Position the tabs on the bottom"), PIKA_POSITION_BOTTOM, PIKA_HELP_WINDOWS_TABS_POSITION }, { "windows-tabs-position-left", PIKA_ICON_GO_FIRST, NC_("windows-tabs-position-action", "_Left"), NULL, { NULL }, NC_("windows-tabs-position-action", "Position the tabs on the left"), PIKA_POSITION_LEFT, PIKA_HELP_WINDOWS_TABS_POSITION }, { "windows-tabs-position-right", PIKA_ICON_GO_LAST, NC_("windows-tabs-position-action", "_Right"), NULL, { NULL }, NC_("windows-tabs-position-action", "Position the tabs on the right"), PIKA_POSITION_RIGHT, PIKA_HELP_WINDOWS_TABS_POSITION }, }; void windows_actions_setup (PikaActionGroup *group) { GList *list; pika_action_group_add_actions (group, "windows-action", windows_actions, G_N_ELEMENTS (windows_actions)); pika_action_group_add_toggle_actions (group, "windows-action", windows_toggle_actions, G_N_ELEMENTS (windows_toggle_actions)); pika_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); g_signal_connect_object (group->pika->displays, "add", G_CALLBACK (windows_actions_display_add), group, 0); g_signal_connect_object (group->pika->displays, "remove", G_CALLBACK (windows_actions_display_remove), group, 0); g_signal_connect_object (group->pika->displays, "reorder", G_CALLBACK (windows_actions_display_reorder), group, 0); for (list = pika_get_display_iter (group->pika); list; list = g_list_next (list)) { PikaDisplay *display = list->data; windows_actions_display_add (group->pika->displays, display, group); } g_signal_connect_object (pika_dialog_factory_get_singleton (), "dock-window-added", G_CALLBACK (windows_actions_dock_window_added), group, 0); g_signal_connect_object (pika_dialog_factory_get_singleton (), "dock-window-removed", G_CALLBACK (windows_actions_dock_window_removed), group, 0); for (list = pika_dialog_factory_get_open_dialogs (pika_dialog_factory_get_singleton ()); list; list = g_list_next (list)) { PikaDockWindow *dock_window = list->data; if (PIKA_IS_DOCK_WINDOW (dock_window)) windows_actions_dock_window_added (pika_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 = PIKA_LIST (global_recent_docks)->queue->head; list; list = g_list_next (list)) { PikaSessionInfo *info = list->data; windows_actions_recent_add (global_recent_docks, info, group); } g_signal_connect_object (group->pika->config, "notify::single-window-mode", G_CALLBACK (windows_actions_single_window_mode_notify), group, 0); } void windows_actions_update (PikaActionGroup *group, gpointer data) { PikaGuiConfig *config = PIKA_GUI_CONFIG (group->pika->config); const gchar *action = NULL; #define SET_ACTIVE(action,condition) \ pika_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 PIKA_POSITION_TOP: action = "windows-tabs-position-top"; break; case PIKA_POSITION_BOTTOM: action = "windows-tabs-position-bottom"; break; case PIKA_POSITION_LEFT: action = "windows-tabs-position-left"; break; case PIKA_POSITION_RIGHT: action = "windows-tabs-position-right"; break; default: action = "windows-tabs-position-top"; break; } pika_action_group_set_action_active (group, action, TRUE); pika_action_group_set_action_sensitive (group, "windows-tab-position", config->single_window_mode, _("Single-window mode disabled")); pika_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 (PikaDockWindow *dock_window) { return g_strdup_printf ("windows-dock-%04d", pika_dock_window_get_id (dock_window)); } /* private functions */ static void windows_actions_display_add (PikaContainer *container, PikaDisplay *display, PikaActionGroup *group) { PikaDisplayShell *shell = pika_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 (PikaContainer *container, PikaDisplay *display, PikaActionGroup *group) { PikaDisplayShell *shell = pika_display_get_shell (display); PikaAction *action; gchar *action_name; if (shell) g_signal_handlers_disconnect_by_func (shell, windows_actions_title_notify, group); action_name = pika_display_get_action_name (display); action = pika_action_group_get_action (group, action_name); g_free (action_name); if (action) pika_action_group_remove_action_and_accel (group, action); windows_actions_update_display_accels (group); } static void windows_actions_display_reorder (PikaContainer *container, PikaDisplay *display, gint new_index, PikaActionGroup *group) { windows_actions_update_display_accels (group); } static void windows_actions_image_notify (PikaDisplay *display, const GParamSpec *unused, PikaActionGroup *group) { PikaImage *image = pika_display_get_image (display); PikaAction *action; gchar *action_name; action_name = pika_display_get_action_name (display); action = pika_action_group_get_action (group, action_name); if (! action) { PikaActionEntry entry = { 0 }; entry.name = action_name; entry.icon_name = PIKA_ICON_IMAGE; entry.label = ""; entry.tooltip = NULL; entry.callback = windows_show_display_cmd_callback; entry.help_id = NULL; pika_action_group_add_actions (group, NULL, &entry, 1); action = pika_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 = pika_image_get_display_name (image); escaped = pika_escape_uline (display_name); /* TRANSLATORS: label for an action allowing to show (i.e. raise the image * tab or window above others) specific images or views of image. The part * between quotes is the image name and other view identifiers. */ title = g_strdup_printf (_("Show \"%s-%d.%d\""), escaped, pika_image_get_id (image), pika_display_get_instance (display)); g_object_set (action, "visible", TRUE, "label", title, "short-label", escaped, "tooltip", pika_image_get_display_path (image), "viewable", image, NULL); g_free (title); g_free (escaped); windows_actions_update_display_accels (group); } else { g_object_set (action, "visible", FALSE, "viewable", NULL, NULL); } } static void windows_actions_title_notify (PikaDisplayShell *shell, const GParamSpec *unused, PikaActionGroup *group) { windows_actions_image_notify (shell->display, NULL, group); } static void windows_actions_update_display_accels (PikaActionGroup *group) { GList *list; gint i; for (list = pika_get_display_iter (group->pika), i = 0; list && i < 10; list = g_list_next (list), i++) { PikaDisplay *display = list->data; PikaImage *image = pika_display_get_image (display); PikaAction *action; gchar *action_name; if (image == NULL) break; action_name = pika_display_get_action_name (display); action = pika_action_group_get_action (group, action_name); g_free (action_name); if (action) { const gchar *ntooltip; gchar *tooltip; gchar *accel; if (i < 9) accel = gtk_accelerator_name (GDK_KEY_1 + i, GDK_MOD1_MASK); else accel = gtk_accelerator_name (GDK_KEY_0 + i, GDK_MOD1_MASK); pika_action_set_accels (action, (const gchar*[]) { accel, NULL }); g_free (accel); /* TRANSLATORS: the first argument (%1$s) is the image name, the * second (%2$d) is its tab order in the graphical interface. */ ntooltip = ngettext ("Switch to the first image view: %1$s", "Switch to image view %2$d: %1$s", i + 1); tooltip = g_strdup_printf (ntooltip, pika_image_get_display_path (image), i + 1); pika_action_set_tooltip (action, tooltip); g_free (tooltip); } } } static void windows_actions_dock_window_added (PikaDialogFactory *factory, PikaDockWindow *dock_window, PikaActionGroup *group) { PikaAction *action; PikaActionEntry entry = { 0 }; gchar *action_name = windows_actions_dock_window_to_action_name (dock_window); entry.name = action_name; entry.icon_name = NULL; entry.label = ""; entry.tooltip = NULL; entry.callback = windows_show_dock_cmd_callback; entry.help_id = PIKA_HELP_WINDOWS_SHOW_DOCK; pika_action_group_add_actions (group, NULL, &entry, 1); action = pika_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 (PikaDialogFactory *factory, PikaDockWindow *dock_window, PikaActionGroup *group) { PikaAction *action; gchar *action_name; action_name = windows_actions_dock_window_to_action_name (dock_window); action = pika_action_group_get_action (group, action_name); g_free (action_name); if (action) pika_action_group_remove_action_and_accel (group, action); } static void windows_actions_dock_window_notify (PikaDockWindow *dock_window, const GParamSpec *pspec, PikaActionGroup *group) { PikaAction *action; gchar *action_name; action_name = windows_actions_dock_window_to_action_name (dock_window); action = pika_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 (PikaContainer *container, PikaSessionInfo *info, PikaActionGroup *group) { PikaAction *action; PikaActionEntry entry = { 0 }; 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 = pika_object_get_name (info); entry.tooltip = pika_object_get_name (info); entry.callback = windows_open_recent_cmd_callback; entry.help_id = PIKA_HELP_WINDOWS_OPEN_RECENT_DOCK; pika_action_group_add_actions (group, NULL, &entry, 1); action = pika_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 (PikaContainer *container, PikaSessionInfo *info, PikaActionGroup *group) { PikaAction *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 = pika_action_group_get_action (group, action_name); g_free (action_name); if (action) pika_action_group_remove_action_and_accel (group, action); } static void windows_actions_single_window_mode_notify (PikaDisplayConfig *config, GParamSpec *pspec, PikaActionGroup *group) { pika_action_group_set_action_active (group, "windows-use-single-window-mode", PIKA_GUI_CONFIG (config)->single_window_mode); }