/* 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 "libpikawidgets/pikawidgets.h" #include "actions-types.h" #include "core/pika.h" #include "core/pikacontainer.h" #include "core/pikacontext.h" #include "core/pikaimage.h" #include "core/pikatooloptions.h" #include "core/pikatoolinfo.h" #include "widgets/pikaactionfactory.h" #include "widgets/pikaactiongroup.h" #include "widgets/pikacontainereditor.h" #include "widgets/pikacontainerview.h" #include "widgets/pikadock.h" #include "widgets/pikadockable.h" #include "widgets/pikadockwindow.h" #include "widgets/pikaimageeditor.h" #include "widgets/pikaitemtreeview.h" #include "display/pikadisplay.h" #include "display/pikadisplayshell.h" #include "display/pikaimagewindow.h" #include "display/pikanavigationeditor.h" #include "display/pikastatusbar.h" #include "dialogs/dialogs.h" #include "actions.h" #include "brush-editor-actions.h" #include "brushes-actions.h" #include "buffers-actions.h" #include "channels-actions.h" #include "colormap-actions.h" #include "context-actions.h" #include "cursor-info-actions.h" #include "dashboard-actions.h" #include "debug-actions.h" #include "dialogs-actions.h" #include "dock-actions.h" #include "dockable-actions.h" #include "documents-actions.h" #include "drawable-actions.h" #include "dynamics-actions.h" #include "dynamics-editor-actions.h" #include "edit-actions.h" #include "error-console-actions.h" #include "file-actions.h" #include "filters-actions.h" #include "fonts-actions.h" #include "gradient-editor-actions.h" #include "gradients-actions.h" #include "help-actions.h" #include "image-actions.h" #include "images-actions.h" #include "layers-actions.h" #include "mypaint-brushes-actions.h" #include "palette-editor-actions.h" #include "palettes-actions.h" #include "patterns-actions.h" #include "plug-in-actions.h" #include "quick-mask-actions.h" #include "sample-points-actions.h" #include "select-actions.h" #include "templates-actions.h" #include "text-editor-actions.h" #include "text-tool-actions.h" #include "tool-options-actions.h" #include "tool-presets-actions.h" #include "tool-preset-editor-actions.h" #include "tools-actions.h" #include "vector-toolpath-actions.h" #include "vectors-actions.h" #include "view-actions.h" #include "windows-actions.h" #include "pika-intl.h" /* global variables */ PikaActionFactory *global_action_factory = NULL; /* private variables */ static const PikaActionFactoryEntry action_groups[] = { { "brush-editor", N_("Brush Editor"), PIKA_ICON_BRUSH, brush_editor_actions_setup, brush_editor_actions_update }, { "brushes", N_("Brushes"), PIKA_ICON_BRUSH, brushes_actions_setup, brushes_actions_update }, { "buffers", N_("Buffers"), PIKA_ICON_BUFFER, buffers_actions_setup, buffers_actions_update }, { "channels", N_("Channels"), PIKA_ICON_CHANNEL, channels_actions_setup, channels_actions_update }, { "colormap", N_("Colormap"), PIKA_ICON_COLORMAP, colormap_actions_setup, colormap_actions_update }, { "context", N_("Context"), PIKA_ICON_DIALOG_TOOL_OPTIONS /* well... */, context_actions_setup, context_actions_update }, { "cursor-info", N_("Pointer Information"), NULL, cursor_info_actions_setup, cursor_info_actions_update }, { "dashboard", N_("Dashboard"), PIKA_ICON_DIALOG_DASHBOARD, dashboard_actions_setup, dashboard_actions_update }, { "debug", N_("Debug"), NULL, debug_actions_setup, debug_actions_update }, { "dialogs", N_("Dialogs"), NULL, dialogs_actions_setup, dialogs_actions_update }, { "dock", N_("Dock"), NULL, dock_actions_setup, dock_actions_update }, { "dockable", N_("Dockable"), NULL, dockable_actions_setup, dockable_actions_update }, { "documents", N_("Document History"), NULL, documents_actions_setup, documents_actions_update }, { "drawable", N_("Drawable"), PIKA_ICON_LAYER, drawable_actions_setup, drawable_actions_update }, { "dynamics", N_("Paint Dynamics"), PIKA_ICON_DYNAMICS, dynamics_actions_setup, dynamics_actions_update }, { "dynamics-editor", N_("Paint Dynamics Editor"), PIKA_ICON_DYNAMICS, dynamics_editor_actions_setup, dynamics_editor_actions_update }, { "edit", N_("Edit"), PIKA_ICON_EDIT, edit_actions_setup, edit_actions_update }, { "error-console", N_("Error Console"), PIKA_ICON_DIALOG_WARNING, error_console_actions_setup, error_console_actions_update }, { "file", N_("File"), "text-x-generic", file_actions_setup, file_actions_update }, { "filters", N_("Filters"), PIKA_ICON_GEGL, filters_actions_setup, filters_actions_update }, { "fonts", N_("Fonts"), PIKA_ICON_FONT, fonts_actions_setup, fonts_actions_update }, { "gradient-editor", N_("Gradient Editor"), PIKA_ICON_GRADIENT, gradient_editor_actions_setup, gradient_editor_actions_update }, { "gradients", N_("Gradients"), PIKA_ICON_GRADIENT, gradients_actions_setup, gradients_actions_update }, { "tool-presets", N_("Tool Presets"), PIKA_ICON_TOOL_PRESET, tool_presets_actions_setup, tool_presets_actions_update }, { "tool-preset-editor", N_("Tool Preset Editor"), PIKA_ICON_TOOL_PRESET, tool_preset_editor_actions_setup, tool_preset_editor_actions_update }, { "help", N_("Help"), "help-browser", help_actions_setup, help_actions_update }, { "image", N_("Image"), PIKA_ICON_IMAGE, image_actions_setup, image_actions_update }, { "images", N_("Images"), PIKA_ICON_IMAGE, images_actions_setup, images_actions_update }, { "layers", N_("Layers"), PIKA_ICON_LAYER, layers_actions_setup, layers_actions_update }, { "mypaint-brushes", N_("MyPaint Brushes"), PIKA_ICON_MYPAINT_BRUSH, mypaint_brushes_actions_setup, mypaint_brushes_actions_update }, { "palette-editor", N_("Palette Editor"), PIKA_ICON_PALETTE, palette_editor_actions_setup, palette_editor_actions_update }, { "palettes", N_("Palettes"), PIKA_ICON_PALETTE, palettes_actions_setup, palettes_actions_update }, { "patterns", N_("Patterns"), PIKA_ICON_PATTERN, patterns_actions_setup, patterns_actions_update }, { "plug-in", N_("Plug-ins"), PIKA_ICON_PLUGIN, plug_in_actions_setup, plug_in_actions_update }, { "quick-mask", N_("Quick Mask"), PIKA_ICON_QUICK_MASK_ON, quick_mask_actions_setup, quick_mask_actions_update }, { "sample-points", N_("Sample Points"), PIKA_ICON_SAMPLE_POINT, sample_points_actions_setup, sample_points_actions_update }, { "select", N_("Select"), PIKA_ICON_SELECTION, select_actions_setup, select_actions_update }, { "templates", N_("Templates"), PIKA_ICON_TEMPLATE, templates_actions_setup, templates_actions_update }, { "text-tool", N_("Text Tool"), PIKA_ICON_EDIT, text_tool_actions_setup, text_tool_actions_update }, { "text-editor", N_("Text Editor"), PIKA_ICON_EDIT, text_editor_actions_setup, text_editor_actions_update }, { "tool-options", N_("Tool Options"), PIKA_ICON_DIALOG_TOOL_OPTIONS, tool_options_actions_setup, tool_options_actions_update }, { "tools", N_("Tools"), PIKA_ICON_DIALOG_TOOLS, tools_actions_setup, tools_actions_update }, { "vector-toolpath", N_("Path Toolpath"), PIKA_ICON_PATH, vector_toolpath_actions_setup, vector_toolpath_actions_update }, { "vectors", N_("Paths"), PIKA_ICON_PATH, vectors_actions_setup, vectors_actions_update }, { "view", N_("View"), PIKA_ICON_VISIBLE, view_actions_setup, view_actions_update }, { "windows", N_("Windows"), NULL, windows_actions_setup, windows_actions_update } }; /* public functions */ void actions_init (Pika *pika) { gint i; g_return_if_fail (PIKA_IS_PIKA (pika)); g_return_if_fail (global_action_factory == NULL); global_action_factory = pika_action_factory_new (pika); for (i = 0; i < G_N_ELEMENTS (action_groups); i++) pika_action_factory_group_register (global_action_factory, action_groups[i].identifier, gettext (action_groups[i].label), action_groups[i].icon_name, action_groups[i].setup_func, action_groups[i].update_func); } void actions_exit (Pika *pika) { g_return_if_fail (PIKA_IS_PIKA (pika)); g_return_if_fail (global_action_factory != NULL); g_return_if_fail (global_action_factory->pika == pika); g_clear_object (&global_action_factory); } Pika * action_data_get_pika (gpointer data) { Pika *result = NULL; static gboolean recursion = FALSE; if (! data || recursion) return NULL; recursion = TRUE; if (PIKA_IS_PIKA (data)) result = data; if (! result) { PikaDisplay *display = action_data_get_display (data); if (display) result = display->pika; } if (! result) { PikaContext *context = action_data_get_context (data); if (context) result = context->pika; } recursion = FALSE; return result; } PikaContext * action_data_get_context (gpointer data) { PikaContext *result = NULL; static gboolean recursion = FALSE; if (! data || recursion) return NULL; recursion = TRUE; if (PIKA_IS_DOCK (data)) result = pika_dock_get_context ((PikaDock *) data); else if (PIKA_IS_DOCK_WINDOW (data)) result = pika_dock_window_get_context (((PikaDockWindow *) data)); else if (PIKA_IS_CONTAINER_VIEW (data)) result = pika_container_view_get_context ((PikaContainerView *) data); else if (PIKA_IS_CONTAINER_EDITOR (data)) result = pika_container_view_get_context (((PikaContainerEditor *) data)->view); else if (PIKA_IS_IMAGE_EDITOR (data)) result = ((PikaImageEditor *) data)->context; else if (PIKA_IS_NAVIGATION_EDITOR (data)) result = ((PikaNavigationEditor *) data)->context; if (! result) { Pika *pika = action_data_get_pika (data); if (pika) result = pika_get_user_context (pika); } recursion = FALSE; return result; } PikaImage * action_data_get_image (gpointer data) { PikaImage *result = NULL; static gboolean recursion = FALSE; if (! data || recursion) return NULL; recursion = TRUE; if (PIKA_IS_ITEM_TREE_VIEW (data)) { result = pika_item_tree_view_get_image ((PikaItemTreeView *) data); recursion = FALSE; return result; } else if (PIKA_IS_IMAGE_EDITOR (data)) { result = ((PikaImageEditor *) data)->image; recursion = FALSE; return result; } if (! result) { PikaDisplay *display = action_data_get_display (data); if (display) result = pika_display_get_image (display); } if (! result) { PikaContext *context = action_data_get_context (data); if (context) result = pika_context_get_image (context); } recursion = FALSE; return result; } PikaDisplay * action_data_get_display (gpointer data) { PikaDisplay *result = NULL; static gboolean recursion = FALSE; if (! data || recursion) return NULL; recursion = TRUE; if (PIKA_IS_DISPLAY (data)) result = data; else if (PIKA_IS_IMAGE_WINDOW (data)) { PikaDisplayShell *shell = pika_image_window_get_active_shell (data); result = shell ? shell->display : NULL; } if (! result) { PikaContext *context = action_data_get_context (data); if (context) result = pika_context_get_display (context); } recursion = FALSE; return result; } PikaDisplayShell * action_data_get_shell (gpointer data) { PikaDisplayShell *result = NULL; static gboolean recursion = FALSE; if (! data || recursion) return NULL; recursion = TRUE; if (! result) { PikaDisplay *display = action_data_get_display (data); if (display) result = pika_display_get_shell (display); } recursion = FALSE; return result; } GtkWidget * action_data_get_widget (gpointer data) { GtkWidget *result = NULL; static gboolean recursion = FALSE; if (! data || recursion) return NULL; recursion = TRUE; if (GTK_IS_WIDGET (data)) result = data; if (! result) { PikaDisplay *display = action_data_get_display (data); if (display) result = GTK_WIDGET (pika_display_get_shell (display)); } if (! result) result = dialogs_get_toolbox (); recursion = FALSE; return result; } gint action_data_sel_count (gpointer data) { if (PIKA_IS_CONTAINER_EDITOR (data)) { PikaContainerEditor *editor; editor = PIKA_CONTAINER_EDITOR (data); return pika_container_view_get_selected (editor->view, NULL, NULL); } else { return 0; } } /* action_select_value: * @select_type: * @value: * @min: * max: * def: * small_inc: * inc: * skip_inc: * delta_factor: * wrap: * * For any valid enum @value (which are all negative), the corresponding * action computes the semantic value (default, first, next, etc.). * For a positive @value, it is considered as a per-thousand value * between the @min and @max (possibly wrapped if @wrap is set, clamped * otherwise), allowing to compute the returned value. * * Returns: the computed value to use for the action. */ gdouble action_select_value (PikaActionSelectType select_type, gdouble value, gdouble min, gdouble max, gdouble def, gdouble small_inc, gdouble inc, gdouble skip_inc, gdouble delta_factor, gboolean wrap) { switch (select_type) { case PIKA_ACTION_SELECT_SET_TO_DEFAULT: value = def; break; case PIKA_ACTION_SELECT_FIRST: value = min; break; case PIKA_ACTION_SELECT_LAST: value = max; break; case PIKA_ACTION_SELECT_SMALL_PREVIOUS: value -= small_inc; break; case PIKA_ACTION_SELECT_SMALL_NEXT: value += small_inc; break; case PIKA_ACTION_SELECT_PREVIOUS: value -= inc; break; case PIKA_ACTION_SELECT_NEXT: value += inc; break; case PIKA_ACTION_SELECT_SKIP_PREVIOUS: value -= skip_inc; break; case PIKA_ACTION_SELECT_SKIP_NEXT: value += skip_inc; break; case PIKA_ACTION_SELECT_PERCENT_PREVIOUS: g_return_val_if_fail (delta_factor >= 0.0, value); value /= (1.0 + delta_factor); break; case PIKA_ACTION_SELECT_PERCENT_NEXT: g_return_val_if_fail (delta_factor >= 0.0, value); value *= (1.0 + delta_factor); break; default: if ((gint) select_type >= 0) value = (gdouble) select_type * (max - min) / 1000.0 + min; else g_return_val_if_reached (value); break; } if (wrap) { while (value < min) value = max - (min - value); while (value > max) value = min + (value - max); } else { value = CLAMP (value, min, max); } return value; } void action_select_property (PikaActionSelectType select_type, PikaDisplay *display, GObject *object, const gchar *property_name, gdouble small_inc, gdouble inc, gdouble skip_inc, gdouble delta_factor, gboolean wrap) { GParamSpec *pspec; g_return_if_fail (display == NULL || PIKA_IS_DISPLAY (display)); g_return_if_fail (G_IS_OBJECT (object)); g_return_if_fail (property_name != NULL); pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), property_name); if (G_IS_PARAM_SPEC_DOUBLE (pspec)) { gdouble value; g_object_get (object, property_name, &value, NULL); value = action_select_value (select_type, value, G_PARAM_SPEC_DOUBLE (pspec)->minimum, G_PARAM_SPEC_DOUBLE (pspec)->maximum, G_PARAM_SPEC_DOUBLE (pspec)->default_value, small_inc, inc, skip_inc, delta_factor, wrap); g_object_set (object, property_name, value, NULL); if (display) { const gchar *blurb = g_param_spec_get_blurb (pspec); if (blurb) { /* value description and new value shown in the status bar */ action_message (display, object, _("%s: %.2f"), blurb, value); } } } else if (G_IS_PARAM_SPEC_INT (pspec)) { gint value; g_object_get (object, property_name, &value, NULL); value = action_select_value (select_type, value, G_PARAM_SPEC_INT (pspec)->minimum, G_PARAM_SPEC_INT (pspec)->maximum, G_PARAM_SPEC_INT (pspec)->default_value, small_inc, inc, skip_inc, delta_factor, wrap); g_object_set (object, property_name, value, NULL); if (display) { const gchar *blurb = g_param_spec_get_blurb (pspec); if (blurb) { /* value description and new value shown in the status bar */ action_message (display, object, _("%s: %d"), blurb, value); } } } else { g_return_if_reached (); } } PikaObject * action_select_object (PikaActionSelectType select_type, PikaContainer *container, PikaObject *current) { gint select_index; gint n_children; g_return_val_if_fail (PIKA_IS_CONTAINER (container), NULL); g_return_val_if_fail (current == NULL || PIKA_IS_OBJECT (current), NULL); if (! current && select_type != PIKA_ACTION_SELECT_FIRST && select_type != PIKA_ACTION_SELECT_LAST) return NULL; n_children = pika_container_get_n_children (container); if (n_children == 0) return NULL; switch (select_type) { case PIKA_ACTION_SELECT_FIRST: select_index = 0; break; case PIKA_ACTION_SELECT_LAST: select_index = n_children - 1; break; case PIKA_ACTION_SELECT_PREVIOUS: select_index = pika_container_get_child_index (container, current) - 1; break; case PIKA_ACTION_SELECT_NEXT: select_index = pika_container_get_child_index (container, current) + 1; break; case PIKA_ACTION_SELECT_SKIP_PREVIOUS: select_index = pika_container_get_child_index (container, current) - 10; break; case PIKA_ACTION_SELECT_SKIP_NEXT: select_index = pika_container_get_child_index (container, current) + 10; break; default: if ((gint) select_type >= 0) select_index = (gint) select_type; else g_return_val_if_reached (current); break; } select_index = CLAMP (select_index, 0, n_children - 1); return pika_container_get_child_by_index (container, select_index); } void action_message (PikaDisplay *display, GObject *object, const gchar *format, ...) { PikaDisplayShell *shell = pika_display_get_shell (display); PikaStatusbar *statusbar = pika_display_shell_get_statusbar (shell); const gchar *icon_name = NULL; va_list args; if (PIKA_IS_TOOL_OPTIONS (object)) { PikaToolInfo *tool_info = PIKA_TOOL_OPTIONS (object)->tool_info; icon_name = pika_viewable_get_icon_name (PIKA_VIEWABLE (tool_info)); } else if (PIKA_IS_VIEWABLE (object)) { icon_name = pika_viewable_get_icon_name (PIKA_VIEWABLE (object)); } va_start (args, format); pika_statusbar_push_temp_valist (statusbar, PIKA_MESSAGE_INFO, icon_name, format, args); va_end (args); }