/* 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-path-actions.h"
#include "tool-presets-actions.h"
#include "tool-preset-editor-actions.h"
#include "tools-actions.h"
#include "view-actions.h"
#include "windows-actions.h"

#include "gimp-intl.h"


/*  global variables  */

GimpActionFactory *global_action_factory = NULL;
GHashTable        *aux_filter_hash_table = 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 },
  { "tool-path", N_("Tool Path"), GIMP_ICON_PATH,
    tool_path_actions_setup,
    tool_path_actions_update },
  { "tools", N_("Tools"), GIMP_ICON_DIALOG_TOOLS,
    tools_actions_setup,
    tools_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);

  aux_filter_hash_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
}

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);
  g_hash_table_unref (aux_filter_hash_table);
}

/* XXX Temporary code to store the list of filter operations with an
 * "aux" input. This won't be necessary anymore once these filters can
 * be applied non-destructively too in the future.
 */
void
actions_filter_set_aux (const gchar *action_name)
{
  g_hash_table_add (aux_filter_hash_table, (gpointer) g_strdup (action_name));
}

gboolean
actions_filter_get_aux (const gchar *action_name)
{
  return g_hash_table_lookup (aux_filter_hash_table, action_name) != NULL;
}

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 = GIMP_CONTAINER_EDITOR (data);

      return gimp_container_view_get_selected (editor->view, 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);
}
