gimp/app/actions/actions.c

859 lines
24 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 "libgimpwidgets/gimpwidgets.h"
#include "actions-types.h"
#include "core/gimp.h"
#include "core/gimpcontainer.h"
#include "core/gimpcontext.h"
#include "core/gimpimage.h"
#include "core/gimpitem.h"
#include "core/gimptooloptions.h"
#include "core/gimptoolinfo.h"
#include "widgets/gimpactionfactory.h"
#include "widgets/gimpactiongroup.h"
#include "widgets/gimpcontainereditor.h"
#include "widgets/gimpcontainerview.h"
#include "widgets/gimpdock.h"
#include "widgets/gimpdockable.h"
#include "widgets/gimpdockwindow.h"
#include "widgets/gimpimageeditor.h"
#include "widgets/gimpitemtreeview.h"
#include "display/gimpdisplay.h"
#include "display/gimpdisplayshell.h"
#include "display/gimpimagewindow.h"
#include "display/gimpnavigationeditor.h"
#include "display/gimpstatusbar.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 "paths-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 "view-actions.h"
#include "windows-actions.h"
#include "gimp-intl.h"
/* global variables */
GimpActionFactory *global_action_factory = NULL;
/* private variables */
static const GimpActionFactoryEntry action_groups[] =
{
{ "brush-editor", N_("Brush Editor"), GIMP_ICON_BRUSH,
brush_editor_actions_setup,
brush_editor_actions_update },
{ "brushes", N_("Brushes"), GIMP_ICON_BRUSH,
brushes_actions_setup,
brushes_actions_update },
{ "buffers", N_("Buffers"), GIMP_ICON_BUFFER,
buffers_actions_setup,
buffers_actions_update },
{ "channels", N_("Channels"), GIMP_ICON_CHANNEL,
channels_actions_setup,
channels_actions_update },
{ "colormap", N_("Colormap"), GIMP_ICON_COLORMAP,
colormap_actions_setup,
colormap_actions_update },
{ "context", N_("Context"), GIMP_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"), GIMP_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"), GIMP_ICON_LAYER,
drawable_actions_setup,
drawable_actions_update },
{ "dynamics", N_("Paint Dynamics"), GIMP_ICON_DYNAMICS,
dynamics_actions_setup,
dynamics_actions_update },
{ "dynamics-editor", N_("Paint Dynamics Editor"), GIMP_ICON_DYNAMICS,
dynamics_editor_actions_setup,
dynamics_editor_actions_update },
{ "edit", N_("Edit"), GIMP_ICON_EDIT,
edit_actions_setup,
edit_actions_update },
{ "error-console", N_("Error Console"), GIMP_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"), GIMP_ICON_GEGL,
filters_actions_setup,
filters_actions_update },
{ "fonts", N_("Fonts"), GIMP_ICON_FONT,
fonts_actions_setup,
fonts_actions_update },
{ "gradient-editor", N_("Gradient Editor"), GIMP_ICON_GRADIENT,
gradient_editor_actions_setup,
gradient_editor_actions_update },
{ "gradients", N_("Gradients"), GIMP_ICON_GRADIENT,
gradients_actions_setup,
gradients_actions_update },
{ "tool-presets", N_("Tool Presets"), GIMP_ICON_TOOL_PRESET,
tool_presets_actions_setup,
tool_presets_actions_update },
{ "tool-preset-editor", N_("Tool Preset Editor"), GIMP_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"), GIMP_ICON_IMAGE,
image_actions_setup,
image_actions_update },
{ "images", N_("Images"), GIMP_ICON_IMAGE,
images_actions_setup,
images_actions_update },
{ "layers", N_("Layers"), GIMP_ICON_LAYER,
layers_actions_setup,
layers_actions_update },
{ "mypaint-brushes", N_("MyPaint Brushes"), GIMP_ICON_MYPAINT_BRUSH,
mypaint_brushes_actions_setup,
mypaint_brushes_actions_update },
{ "palette-editor", N_("Palette Editor"), GIMP_ICON_PALETTE,
palette_editor_actions_setup,
palette_editor_actions_update },
{ "palettes", N_("Palettes"), GIMP_ICON_PALETTE,
palettes_actions_setup,
palettes_actions_update },
{ "patterns", N_("Patterns"), GIMP_ICON_PATTERN,
patterns_actions_setup,
patterns_actions_update },
{ "plug-in", N_("Plug-ins"), GIMP_ICON_PLUGIN,
plug_in_actions_setup,
plug_in_actions_update },
{ "quick-mask", N_("Quick Mask"), GIMP_ICON_QUICK_MASK_ON,
quick_mask_actions_setup,
quick_mask_actions_update },
{ "sample-points", N_("Sample Points"), GIMP_ICON_SAMPLE_POINT,
sample_points_actions_setup,
sample_points_actions_update },
{ "select", N_("Select"), GIMP_ICON_SELECTION,
select_actions_setup,
select_actions_update },
{ "templates", N_("Templates"), GIMP_ICON_TEMPLATE,
templates_actions_setup,
templates_actions_update },
{ "text-tool", N_("Text Tool"), GIMP_ICON_EDIT,
text_tool_actions_setup,
text_tool_actions_update },
{ "text-editor", N_("Text Editor"), GIMP_ICON_EDIT,
text_editor_actions_setup,
text_editor_actions_update },
{ "tool-options", N_("Tool Options"), GIMP_ICON_DIALOG_TOOL_OPTIONS,
tool_options_actions_setup,
tool_options_actions_update },
{ "tools", N_("Tools"), GIMP_ICON_DIALOG_TOOLS,
tools_actions_setup,
tools_actions_update },
{ "vector-toolpath", N_("Path Toolpath"), GIMP_ICON_PATH,
vector_toolpath_actions_setup,
vector_toolpath_actions_update },
{ "paths", N_("Paths"), GIMP_ICON_PATH,
paths_actions_setup,
paths_actions_update },
{ "view", N_("View"), GIMP_ICON_VISIBLE,
view_actions_setup,
view_actions_update },
{ "windows", N_("Windows"), NULL,
windows_actions_setup,
windows_actions_update }
};
/* public functions */
void
actions_init (Gimp *gimp)
{
gint i;
g_return_if_fail (GIMP_IS_GIMP (gimp));
g_return_if_fail (global_action_factory == NULL);
global_action_factory = gimp_action_factory_new (gimp);
for (i = 0; i < G_N_ELEMENTS (action_groups); i++)
gimp_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 (Gimp *gimp)
{
g_return_if_fail (GIMP_IS_GIMP (gimp));
g_return_if_fail (global_action_factory != NULL);
g_return_if_fail (global_action_factory->gimp == gimp);
g_clear_object (&global_action_factory);
}
Gimp *
action_data_get_gimp (gpointer data)
{
Gimp *result = NULL;
static gboolean recursion = FALSE;
if (! data || recursion)
return NULL;
recursion = TRUE;
if (GIMP_IS_GIMP (data))
result = data;
if (! result)
{
GimpDisplay *display = action_data_get_display (data);
if (display)
result = display->gimp;
}
if (! result)
{
GimpContext *context = action_data_get_context (data);
if (context)
result = context->gimp;
}
recursion = FALSE;
return result;
}
GimpContext *
action_data_get_context (gpointer data)
{
GimpContext *result = NULL;
static gboolean recursion = FALSE;
if (! data || recursion)
return NULL;
recursion = TRUE;
if (GIMP_IS_DOCK (data))
result = gimp_dock_get_context ((GimpDock *) data);
else if (GIMP_IS_DOCK_WINDOW (data))
result = gimp_dock_window_get_context (((GimpDockWindow *) data));
else if (GIMP_IS_CONTAINER_VIEW (data))
result = gimp_container_view_get_context ((GimpContainerView *) data);
else if (GIMP_IS_CONTAINER_EDITOR (data))
result = gimp_container_view_get_context (((GimpContainerEditor *) data)->view);
else if (GIMP_IS_IMAGE_EDITOR (data))
result = ((GimpImageEditor *) data)->context;
else if (GIMP_IS_NAVIGATION_EDITOR (data))
result = ((GimpNavigationEditor *) data)->context;
if (! result)
{
Gimp *gimp = action_data_get_gimp (data);
if (gimp)
result = gimp_get_user_context (gimp);
}
recursion = FALSE;
return result;
}
GimpImage *
action_data_get_image (gpointer data)
{
GimpImage *result = NULL;
static gboolean recursion = FALSE;
if (! data || recursion)
return NULL;
recursion = TRUE;
if (GIMP_IS_ITEM_TREE_VIEW (data))
{
result = gimp_item_tree_view_get_image ((GimpItemTreeView *) data);
recursion = FALSE;
return result;
}
else if (GIMP_IS_IMAGE_EDITOR (data))
{
result = ((GimpImageEditor *) data)->image;
recursion = FALSE;
return result;
}
if (! result)
{
GimpDisplay *display = action_data_get_display (data);
if (display)
result = gimp_display_get_image (display);
}
if (! result)
{
GimpContext *context = action_data_get_context (data);
if (context)
result = gimp_context_get_image (context);
}
recursion = FALSE;
return result;
}
GimpDisplay *
action_data_get_display (gpointer data)
{
GimpDisplay *result = NULL;
static gboolean recursion = FALSE;
if (! data || recursion)
return NULL;
recursion = TRUE;
if (GIMP_IS_DISPLAY (data))
result = data;
else if (GIMP_IS_IMAGE_WINDOW (data))
{
GimpDisplayShell *shell = gimp_image_window_get_active_shell (data);
result = shell ? shell->display : NULL;
}
if (! result)
{
GimpContext *context = action_data_get_context (data);
if (context)
result = gimp_context_get_display (context);
}
recursion = FALSE;
return result;
}
GimpDisplayShell *
action_data_get_shell (gpointer data)
{
GimpDisplayShell *result = NULL;
static gboolean recursion = FALSE;
if (! data || recursion)
return NULL;
recursion = TRUE;
if (! result)
{
GimpDisplay *display = action_data_get_display (data);
if (display)
result = gimp_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)
{
GimpDisplay *display = action_data_get_display (data);
if (display)
result = GTK_WIDGET (gimp_display_get_shell (display));
}
if (! result)
result = dialogs_get_toolbox ();
recursion = FALSE;
return result;
}
gint
action_data_sel_count (gpointer data)
{
if (GIMP_IS_CONTAINER_EDITOR (data))
{
GimpContainerEditor *editor;
editor = GIMP_CONTAINER_EDITOR (data);
return gimp_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 (GimpActionSelectType 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 GIMP_ACTION_SELECT_SET_TO_DEFAULT:
value = def;
break;
case GIMP_ACTION_SELECT_FIRST:
value = min;
break;
case GIMP_ACTION_SELECT_LAST:
value = max;
break;
case GIMP_ACTION_SELECT_SMALL_PREVIOUS:
value -= small_inc;
break;
case GIMP_ACTION_SELECT_SMALL_NEXT:
value += small_inc;
break;
case GIMP_ACTION_SELECT_PREVIOUS:
value -= inc;
break;
case GIMP_ACTION_SELECT_NEXT:
value += inc;
break;
case GIMP_ACTION_SELECT_SKIP_PREVIOUS:
value -= skip_inc;
break;
case GIMP_ACTION_SELECT_SKIP_NEXT:
value += skip_inc;
break;
case GIMP_ACTION_SELECT_PERCENT_PREVIOUS:
g_return_val_if_fail (delta_factor >= 0.0, value);
value /= (1.0 + delta_factor);
break;
case GIMP_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 (GimpActionSelectType select_type,
GimpDisplay *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 || GIMP_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 ();
}
}
GimpObject *
action_select_object (GimpActionSelectType select_type,
GimpContainer *container,
GimpObject *current)
{
gint select_index;
gint n_children;
g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL);
g_return_val_if_fail (current == NULL || GIMP_IS_OBJECT (current), NULL);
if (! current &&
select_type != GIMP_ACTION_SELECT_FIRST &&
select_type != GIMP_ACTION_SELECT_LAST)
return NULL;
n_children = gimp_container_get_n_children (container);
if (n_children == 0)
return NULL;
switch (select_type)
{
case GIMP_ACTION_SELECT_FIRST:
select_index = 0;
break;
case GIMP_ACTION_SELECT_LAST:
select_index = n_children - 1;
break;
case GIMP_ACTION_SELECT_PREVIOUS:
select_index = gimp_container_get_child_index (container, current) - 1;
break;
case GIMP_ACTION_SELECT_NEXT:
select_index = gimp_container_get_child_index (container, current) + 1;
break;
case GIMP_ACTION_SELECT_SKIP_PREVIOUS:
select_index = gimp_container_get_child_index (container, current) - 10;
break;
case GIMP_ACTION_SELECT_SKIP_NEXT:
select_index = gimp_container_get_child_index (container, current) + 10;
break;
case GIMP_ACTION_SELECT_FLAT_PREVIOUS:
/* XXX We only support this case and the next for GimpItem
* containers.
*/
g_return_val_if_fail (GIMP_IS_ITEM (current), NULL);
select_index = gimp_container_get_child_index (container, current) - 1;
if (select_index < 0)
{
GimpItem *parent = gimp_item_get_parent (GIMP_ITEM (current));
if (parent)
return GIMP_OBJECT (parent);
else
return current;
}
else
{
GimpObject *prev;
GimpContainer *prev_container;
prev = gimp_container_get_child_by_index (container, select_index);
while ((prev_container = gimp_viewable_get_children (GIMP_VIEWABLE (prev))) != NULL)
{
if (gimp_viewable_get_expanded (GIMP_VIEWABLE (prev)))
{
gint n_prev_children;
n_prev_children = gimp_container_get_n_children (prev_container);
if (n_prev_children > 0)
{
select_index = n_prev_children - 1;
container = prev_container;
prev = gimp_container_get_child_by_index (container, select_index);
continue;
}
}
break;
}
}
break;
case GIMP_ACTION_SELECT_FLAT_NEXT:
{
GimpContainer *current_container;
g_return_val_if_fail (GIMP_IS_ITEM (current), NULL);
if ((current_container = gimp_viewable_get_children (GIMP_VIEWABLE (current))) != NULL &&
gimp_viewable_get_expanded (GIMP_VIEWABLE (current)) &&
gimp_container_get_n_children (current_container) > 0)
{
select_index = 0;
container = current_container;
}
else
{
select_index = gimp_container_get_child_index (container, current) + 1;
if (select_index >= n_children)
{
while (TRUE)
{
GimpItem *parent;
GimpContainer *parent_container;
gint n_parent_children;
parent = gimp_item_get_parent (GIMP_ITEM (current));
if (parent == NULL)
break;
parent_container = gimp_item_get_container (parent);
n_parent_children = gimp_container_get_n_children (parent_container);
if (gimp_container_get_child_index (parent_container, GIMP_OBJECT (parent)) + 1 < n_parent_children)
{
container = parent_container;
select_index = gimp_container_get_child_index (parent_container, GIMP_OBJECT (parent)) + 1;
break;
}
current = GIMP_OBJECT (parent);
}
}
}
}
break;
default:
if ((gint) select_type >= 0)
select_index = (gint) select_type;
else
g_return_val_if_reached (current);
break;
}
n_children = gimp_container_get_n_children (container);
select_index = CLAMP (select_index, 0, n_children - 1);
return gimp_container_get_child_by_index (container, select_index);
}
void
action_message (GimpDisplay *display,
GObject *object,
const gchar *format,
...)
{
GimpDisplayShell *shell = gimp_display_get_shell (display);
GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell);
const gchar *icon_name = NULL;
va_list args;
if (GIMP_IS_TOOL_OPTIONS (object))
{
GimpToolInfo *tool_info = GIMP_TOOL_OPTIONS (object)->tool_info;
icon_name = gimp_viewable_get_icon_name (GIMP_VIEWABLE (tool_info));
}
else if (GIMP_IS_VIEWABLE (object))
{
icon_name = gimp_viewable_get_icon_name (GIMP_VIEWABLE (object));
}
va_start (args, format);
gimp_statusbar_push_temp_valist (statusbar, GIMP_MESSAGE_INFO,
icon_name, format, args);
va_end (args);
}