/* The GIMP -- an 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "config.h"

#include <gtk/gtk.h>

#include "libgimpwidgets/gimpwidgets.h"

#include "actions-types.h"

#include "config/gimpguiconfig.h"

#include "core/gimp.h"
#include "core/gimpcontainer.h"
#include "core/gimpcontext.h"
#include "core/gimpimage.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/gimpimageeditor.h"
#include "widgets/gimpitemtreeview.h"

#include "display/gimpdisplay.h"
#include "display/gimpdisplayshell.h"
#include "display/gimpnavigationeditor.h"

#include "dialogs/dialogs.h"

#include "brushes-actions.h"
#include "buffers-actions.h"
#include "channels-actions.h"
#include "colormap-editor-actions.h"
#include "context-actions.h"
#include "debug-actions.h"
#include "dialogs-actions.h"
#include "dockable-actions.h"
#include "documents-actions.h"
#include "drawable-actions.h"
#include "edit-actions.h"
#include "error-console-actions.h"
#include "file-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 "palette-editor-actions.h"
#include "palettes-actions.h"
#include "patterns-actions.h"
#include "plug-in-actions.h"
#include "qmask-actions.h"
#include "select-actions.h"
#include "templates-actions.h"
#include "text-editor-actions.h"
#include "tool-options-actions.h"
#include "tools-actions.h"
#include "vectors-actions.h"
#include "view-actions.h"

#include "gimp-intl.h"


/*  global variables  */

GimpActionFactory *global_action_factory = NULL;


/*  private variables  */

static GimpActionFactoryEntry action_groups[] =
{
  { "brushes", N_("Brushes"), GIMP_STOCK_BRUSH,
    brushes_actions_setup,
    brushes_actions_update },
  { "buffers", N_("Buffers"), GIMP_STOCK_BUFFER,
    buffers_actions_setup,
    buffers_actions_update },
  { "channels", N_("Channels"), GIMP_STOCK_CHANNEL,
    channels_actions_setup,
    channels_actions_update },
  { "colormap-editor", N_("Colormap Editor"), GIMP_STOCK_INDEXED_PALETTE,
    colormap_editor_actions_setup,
    colormap_editor_actions_update },
  { "context", N_("Context"), NULL,
    context_actions_setup,
    context_actions_update },
  { "debug", N_("Debug"), NULL,
    debug_actions_setup,
    debug_actions_update },
  { "dialogs", N_("Dialogs"), NULL,
    dialogs_actions_setup,
    dialogs_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_STOCK_LAYER,
    drawable_actions_setup,
    drawable_actions_update },
  { "edit", N_("Edit"), GIMP_STOCK_EDIT,
    edit_actions_setup,
    edit_actions_update },
  { "error-console", N_("Error Console"), GIMP_STOCK_WARNING,
    error_console_actions_setup,
    error_console_actions_update },
  { "file", N_("File"), GTK_STOCK_OPEN,
    file_actions_setup,
    file_actions_update },
  { "fonts", N_("Fonts"), GIMP_STOCK_FONT,
    fonts_actions_setup,
    fonts_actions_update },
  { "gradient-editor", N_("Gradient Editor"), GIMP_STOCK_GRADIENT,
    gradient_editor_actions_setup,
    gradient_editor_actions_update },
  { "gradients", N_("Gradients"), GIMP_STOCK_GRADIENT,
    gradients_actions_setup,
    gradients_actions_update },
  { "help", N_("Help"), GTK_STOCK_HELP,
    help_actions_setup,
    help_actions_update },
  { "image", N_("Image"), GIMP_STOCK_IMAGE,
    image_actions_setup,
    image_actions_update },
  { "images", N_("Images"), GIMP_STOCK_IMAGE,
    images_actions_setup,
    images_actions_update },
  { "layers", N_("Layers"), GIMP_STOCK_LAYER,
    layers_actions_setup,
    layers_actions_update },
  { "palette-editor", N_("Palette Editor"), GIMP_STOCK_PALETTE,
    palette_editor_actions_setup,
    palette_editor_actions_update },
  { "palettes", N_("Palettes"), GIMP_STOCK_PALETTE,
    palettes_actions_setup,
    palettes_actions_update },
  { "patterns", N_("Patterns"), GIMP_STOCK_PATTERN,
    patterns_actions_setup,
    patterns_actions_update },
  { "plug-in", N_("Plug-Ins"), GIMP_STOCK_PLUGIN,
    plug_in_actions_setup,
    plug_in_actions_update },
  { "qmask", N_("QuickMask"), GIMP_STOCK_QMASK_ON,
    qmask_actions_setup,
    qmask_actions_update },
  { "select", N_("Select"), GIMP_STOCK_SELECTION,
    select_actions_setup,
    select_actions_update },
  { "templates", N_("Templates"), GIMP_STOCK_TEMPLATE,
    templates_actions_setup,
    templates_actions_update },
  { "text-editor", N_("Text Editor"), GIMP_STOCK_EDIT,
    text_editor_actions_setup,
    text_editor_actions_update },
  { "tool-options", N_("Tool Options"), GIMP_STOCK_TOOL_OPTIONS,
    tool_options_actions_setup,
    tool_options_actions_update },
  { "tools", N_("Tools"), GIMP_STOCK_TOOLS,
    tools_actions_setup,
    tools_actions_update },
  { "vectors", N_("Paths"), GIMP_STOCK_PATH,
    vectors_actions_setup,
    vectors_actions_update },
  { "view", N_("View"), GIMP_STOCK_VISIBLE,
    view_actions_setup,
    view_actions_update }
};


/*  public functions  */

void
actions_init (Gimp *gimp)
{
  GimpGuiConfig *gui_config;
  gint           i;

  g_return_if_fail (GIMP_IS_GIMP (gimp));
  g_return_if_fail (global_action_factory == NULL);

  gui_config = GIMP_GUI_CONFIG (gimp->config);

  global_action_factory = gimp_action_factory_new (gimp,
                                                   gui_config->menu_mnemonics);

  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].stock_id,
                                        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_object_unref (global_action_factory);
  global_action_factory = NULL;
}

Gimp *
action_data_get_gimp (gpointer data)
{
  GimpContext *context = NULL;

  if (! data)
    return NULL;

  if (GIMP_IS_DISPLAY (data))
    return ((GimpDisplay *) data)->gimage->gimp;
  else if (GIMP_IS_GIMP (data))
    return data;
  else if (GIMP_IS_DOCK (data))
    context = ((GimpDock *) data)->context;
  else if (GIMP_IS_ITEM_TREE_VIEW (data))
    context = ((GimpItemTreeView *) data)->context;
  else if (GIMP_IS_IMAGE_EDITOR (data))
    context = ((GimpImageEditor *) data)->context;
  else if (GIMP_IS_NAVIGATION_EDITOR (data))
    context = ((GimpNavigationEditor *) data)->context;

  if (context)
    return context->gimp;

  return NULL;
}

GimpContext *
action_data_get_context (gpointer data)
{
  if (! data)
    return NULL;

  if (GIMP_IS_DISPLAY (data))
    return gimp_get_user_context (((GimpDisplay *) data)->gimage->gimp);
  else if (GIMP_IS_GIMP (data))
    return gimp_get_user_context (data);
  else if (GIMP_IS_DOCK (data))
    return ((GimpDock *) data)->context;
  else if (GIMP_IS_ITEM_TREE_VIEW (data))
    return ((GimpItemTreeView *) data)->context;
  else if (GIMP_IS_CONTAINER_VIEW (data))
    return gimp_container_view_get_context ((GimpContainerView *) data);
  else if (GIMP_IS_CONTAINER_EDITOR (data))
    return gimp_container_view_get_context (((GimpContainerEditor *) data)->view);
  else if (GIMP_IS_IMAGE_EDITOR (data))
    return ((GimpImageEditor *) data)->context;
  else if (GIMP_IS_NAVIGATION_EDITOR (data))
    return ((GimpNavigationEditor *) data)->context;

  return NULL;
}

GimpImage *
action_data_get_image (gpointer data)
{
  GimpContext *context = NULL;

  if (! data)
    return NULL;

  if (GIMP_IS_DISPLAY (data))
    return ((GimpDisplay *) data)->gimage;
  else if (GIMP_IS_GIMP (data))
    context = gimp_get_user_context (data);
  else if (GIMP_IS_DOCK (data))
    context = ((GimpDock *) data)->context;
  else if (GIMP_IS_ITEM_TREE_VIEW (data))
    return ((GimpItemTreeView *) data)->gimage;
  else if (GIMP_IS_IMAGE_EDITOR (data))
    return ((GimpImageEditor *) data)->gimage;
  else if (GIMP_IS_NAVIGATION_EDITOR (data))
    context = ((GimpNavigationEditor *) data)->context;

  if (context)
    return gimp_context_get_image (context);

  return NULL;
}

GimpDisplay *
action_data_get_display (gpointer data)
{
  GimpContext *context = NULL;

  if (! data)
    return NULL;

  if (GIMP_IS_DISPLAY (data))
    return data;
  else if (GIMP_IS_GIMP (data))
    context = gimp_get_user_context (data);
  else if (GIMP_IS_DOCK (data))
    context = ((GimpDock *) data)->context;
  else if (GIMP_IS_NAVIGATION_EDITOR (data))
    context = ((GimpNavigationEditor *) data)->context;

  if (context)
    return gimp_context_get_display (context);

  return NULL;
}

GtkWidget *
action_data_get_widget (gpointer data)
{
  if (! data)
    return NULL;

  if (GIMP_IS_DISPLAY (data))
    return ((GimpDisplay *) data)->shell;
  else if (GIMP_IS_GIMP (data))
    return dialogs_get_toolbox ();
  else if (GTK_IS_WIDGET (data))
    return data;

  return NULL;
}

gdouble
action_select_value (GimpActionSelectType  select_type,
                     gdouble               value,
                     gdouble               min,
                     gdouble               max,
                     gdouble               inc,
                     gdouble               skip_inc,
                     gboolean              wrap)
{
  switch (select_type)
    {
    case GIMP_ACTION_SELECT_FIRST:
      value = min;
      break;

    case GIMP_ACTION_SELECT_LAST:
      value = max;
      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;

    default:
      if (value >= 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 + (max - value);
    }
  else
    {
      value = CLAMP (value, min, max);
    }

  return value;
}

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)
    return NULL;

  n_children = gimp_container_num_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;

    default:
      g_return_val_if_reached (current);
      break;
    }

  select_index = CLAMP (select_index, 0, n_children - 1);

  return gimp_container_get_child_by_index (container, select_index);
}
