/* 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 "tools-types.h"

#include "config/gimpdisplayconfig.h"

#include "core/gimp.h"
#include "core/gimpguide.h"
#include "core/gimpimage.h"
#include "core/gimpimage-arrange.h"
#include "core/gimpimage-guides.h"
#include "core/gimpimage-undo.h"
#include "core/gimplayer.h"
#include "core/gimptoolinfo.h"
#include "core/gimplist.h"

#include "widgets/gimphelp-ids.h"

#include "display/gimpdisplay.h"
#include "display/gimpdisplayshell.h"
#include "display/gimpdisplayshell-appearance.h"

#include "gimpalignoptions.h"
#include "gimpaligntool.h"
#include "gimptoolcontrol.h"

#include "gimp-intl.h"


/*  local function prototypes  */

static GObject * gimp_align_tool_constructor (GType              type,
                                              guint              n_params,
                                              GObjectConstructParam *params);
static gboolean gimp_align_tool_initialize   (GimpTool          *tool,
                                              GimpDisplay       *display);
static void   gimp_align_tool_finalize       (GObject           *object);

static void   gimp_align_tool_control        (GimpTool          *tool,
                                              GimpToolAction     action,
                                              GimpDisplay       *display);
static void   gimp_align_tool_button_press   (GimpTool          *tool,
                                              GimpCoords        *coords,
                                              guint32            time,
                                              GdkModifierType    state,
                                              GimpDisplay       *display);
static void   gimp_align_tool_button_release (GimpTool          *tool,
                                              GimpCoords        *coords,
                                              guint32            time,
                                              GdkModifierType    state,
                                              GimpDisplay       *display);
static void   gimp_align_tool_motion         (GimpTool          *tool,
                                              GimpCoords        *coords,
                                              guint32            time,
                                              GdkModifierType    state,
                                              GimpDisplay       *display);
static void   gimp_align_tool_cursor_update  (GimpTool          *tool,
                                              GimpCoords        *coords,
                                              GdkModifierType    state,
                                              GimpDisplay       *display);

static void   gimp_align_tool_draw           (GimpDrawTool      *draw_tool);

static GtkWidget *button_with_stock          (GimpAlignmentType  action,
                                              GimpAlignTool     *align_tool);
static GtkWidget *gimp_align_tool_controls   (GimpAlignTool     *align_tool);
static void   do_alignment                   (GtkWidget         *widget,
                                              gpointer           data);
static void   clear_selected_object          (GObject           *object,
                                              GimpAlignTool     *align_tool);
static void   clear_all_selected_objects     (GimpAlignTool     *align_tool);
static GimpLayer *select_layer_by_coords     (GimpImage         *image,
                                              gint               x,
                                              gint               y);
void          gimp_image_arrange_objects     (GimpImage         *image,
                                              GList             *list,
                                              GimpAlignmentType  alignment,
                                              GObject           *reference,
                                              GimpAlignmentType  reference_alignment,
                                              gint               offset);

G_DEFINE_TYPE (GimpAlignTool, gimp_align_tool, GIMP_TYPE_DRAW_TOOL)

#define parent_class gimp_align_tool_parent_class


void
gimp_align_tool_register (GimpToolRegisterCallback  callback,
                          gpointer                  data)
{
  (* callback) (GIMP_TYPE_ALIGN_TOOL,
                GIMP_TYPE_ALIGN_OPTIONS,
                gimp_align_options_gui,
                0,
                "gimp-align-tool",
                _("Alignment Tool"),
                _("Align or arrange layers and other items"),
                N_("_Align"), "Q",
                NULL, GIMP_HELP_TOOL_MOVE,
                GIMP_STOCK_TOOL_ALIGN,
                data);
}

static void
gimp_align_tool_class_init (GimpAlignToolClass *klass)
{
  GObjectClass      *object_class    = G_OBJECT_CLASS (klass);
  GimpToolClass     *tool_class      = GIMP_TOOL_CLASS (klass);
  GimpDrawToolClass *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass);

  object_class->finalize     = gimp_align_tool_finalize;
  object_class->constructor  = gimp_align_tool_constructor;

  tool_class->initialize     = gimp_align_tool_initialize;
  tool_class->control        = gimp_align_tool_control;
  tool_class->button_press   = gimp_align_tool_button_press;
  tool_class->button_release = gimp_align_tool_button_release;
  tool_class->motion         = gimp_align_tool_motion;
  tool_class->cursor_update  = gimp_align_tool_cursor_update;

  draw_tool_class->draw      = gimp_align_tool_draw;
}

static void
gimp_align_tool_init (GimpAlignTool *align_tool)
{
  GimpTool *tool = GIMP_TOOL (align_tool);

  align_tool->controls        = NULL;

  align_tool->selected_objects = NULL;

  align_tool->align_type = GIMP_ALIGN_LEFT;

  align_tool->horz_offset = 0;
  align_tool->vert_offset = 0;

  gimp_tool_control_set_snap_to     (tool->control, FALSE);
  gimp_tool_control_set_tool_cursor (tool->control, GIMP_TOOL_CURSOR_MOVE);

}

static GObject *
gimp_align_tool_constructor (GType                  type,
                             guint                  n_params,
                             GObjectConstructParam *params)
{
  GObject       *object;
  GimpTool      *tool;
  GimpAlignTool *align_tool;
  GtkContainer  *container;
  GObject       *options;

  object = G_OBJECT_CLASS (parent_class)->constructor (type, n_params, params);

  tool       = GIMP_TOOL (object);
  align_tool = GIMP_ALIGN_TOOL (object);

  g_assert (GIMP_IS_TOOL_INFO (tool->tool_info));

  options = G_OBJECT (tool->tool_info->tool_options);

  container = GTK_CONTAINER (g_object_get_data (options,
                                                "controls-container"));

  align_tool->controls = gimp_align_tool_controls (align_tool);
  gtk_container_add (container, align_tool->controls);

  return object;
}

static void
gimp_align_tool_finalize (GObject *object)
{
  GimpTool      *tool       = GIMP_TOOL (object);
  GimpAlignTool *align_tool = GIMP_ALIGN_TOOL (object);

  if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (object)))
    gimp_draw_tool_stop (GIMP_DRAW_TOOL (object));

  if (gimp_tool_control_is_active (tool->control))
    gimp_tool_control_halt (tool->control);

  if (align_tool->controls)
    {
      gtk_widget_destroy (align_tool->controls);
      align_tool->controls = NULL;
    }

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

static gboolean
gimp_align_tool_initialize (GimpTool    *tool,
                            GimpDisplay *display)
{
  if (tool->display != display)
    {
    }

  return TRUE;
}

static void
gimp_align_tool_control (GimpTool       *tool,
                         GimpToolAction  action,
                         GimpDisplay    *display)
{
  GimpAlignTool *align_tool = GIMP_ALIGN_TOOL (tool);

  switch (action)
    {
    case GIMP_TOOL_ACTION_PAUSE:
    case GIMP_TOOL_ACTION_RESUME:
      break;

    case GIMP_TOOL_ACTION_HALT:
      clear_all_selected_objects (align_tool);
      gimp_tool_pop_status (tool, display);
      break;
    }

  GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
}

static void
gimp_align_tool_button_press (GimpTool        *tool,
                              GimpCoords      *coords,
                              guint32          time,
                              GdkModifierType  state,
                              GimpDisplay     *display)
{
  GimpAlignTool    *align_tool  = GIMP_ALIGN_TOOL (tool);

  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));

  /*  If the tool was being used in another image...reset it  */

  if (display != tool->display)
    {
      if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tool)))
        gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));

      clear_all_selected_objects (align_tool);
    }

  if (! gimp_tool_control_is_active (tool->control))
    gimp_tool_control_activate (tool->control);

  tool->display = display;

  align_tool->x1 = align_tool->x0 = coords->x;
  align_tool->y1 = align_tool->y0 = coords->y;

  if (! gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tool)))
    gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);

  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
}

/*
 * some rather complex logic here.  If the user clicks without modifiers,
 * then we start a new list, and use the first object in it as reference.
 * If the user clicks using Shift, or draws a rubber-band box, then
 * we add objects to the list, but do not specify which one should
 * be used as reference.
 */
static void
gimp_align_tool_button_release (GimpTool        *tool,
                                GimpCoords      *coords,
                                guint32          time,
                                GdkModifierType  state,
                                GimpDisplay     *display)
{
  GimpAlignTool    *align_tool = GIMP_ALIGN_TOOL (tool);
  GimpDisplayShell *shell      = GIMP_DISPLAY_SHELL (display->shell);
  GObject          *object     = NULL;
  GimpImage        *image      = display->image;
  gint              i;

  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));

  if (state & GDK_BUTTON3_MASK) /* cancel this action */
    {
      align_tool->x1 = align_tool->x0;
      align_tool->y1 = align_tool->y0;

      gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
      return;
    }

  if (! (state & GDK_SHIFT_MASK)) /* start a new list */
    {
      clear_all_selected_objects (align_tool);
      align_tool->set_reference = FALSE;
    }

#define EPSILON 3

  /* if mouse has moved less than EPSILON pixels since button press, select
     the nearest thing, otherwise make a rubber-band rectangle */
  if (hypot (coords->x - align_tool->x0, coords->y - align_tool->y0) < EPSILON)
    {
      GimpVectors *vectors;
      GimpGuide   *guide;
      GimpLayer   *layer;
      gint         snap_distance;

      snap_distance =
        GIMP_DISPLAY_CONFIG (display->image->gimp->config)->snap_distance;

      if (gimp_draw_tool_on_vectors (GIMP_DRAW_TOOL (tool), display,
                                     coords, snap_distance, snap_distance,
                                     NULL, NULL, NULL, NULL, NULL,
                                     &vectors))
        {
          object = G_OBJECT (vectors);
        }
      else if (gimp_display_shell_get_show_guides (shell) &&
               (guide = gimp_image_find_guide (display->image,
                                               coords->x, coords->y,
                                               FUNSCALEX (shell, snap_distance),
                                               FUNSCALEY (shell, snap_distance))))
        {
          object = G_OBJECT (guide);
        }
      else
        {
          if ((layer = select_layer_by_coords (display->image,
                                               coords->x, coords->y)))
            {
              object = G_OBJECT (layer);
            }
        }

      if (object)
        {
          if (! g_list_find (align_tool->selected_objects, object))
            {
              align_tool->selected_objects
                = g_list_append (align_tool->selected_objects, object);
              g_signal_connect (object, "removed",
                                G_CALLBACK (clear_selected_object),
                                (gpointer) align_tool);

              /* if an object has been selected using unmodified click,
               * it should be used as the reference
               */
              if (! (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)))
                align_tool->set_reference = TRUE;
            }
        }
    }
  else  /* FIXME: look for vectors too */
    {
      gint   X0    = MIN (coords->x, align_tool->x0);
      gint   X1    = MAX (coords->x, align_tool->x0);
      gint   Y0    = MIN (coords->y, align_tool->y0);
      gint   Y1    = MAX (coords->y, align_tool->y0);
      GList *list;

      for (list = GIMP_LIST (image->layers)->list;
           list;
           list = g_list_next (list))
        {
          GimpLayer *layer = list->data;
          gint       x0, y0, x1, y1;

          if (! gimp_item_get_visible (GIMP_ITEM (layer)))
            continue;

          gimp_item_offsets (GIMP_ITEM (layer), &x0, &y0);
          x1 = x0 + gimp_item_width (GIMP_ITEM (layer));
          y1 = y0 + gimp_item_height (GIMP_ITEM (layer));

          if (x0 < X0 || y0 < Y0 || x1 > X1 || y1 > Y1)
            continue;

          if (g_list_find (align_tool->selected_objects, layer))
            continue;

          align_tool->selected_objects
            = g_list_append (align_tool->selected_objects, layer);
          g_signal_connect (layer, "removed", G_CALLBACK (clear_selected_object),
                            (gpointer) align_tool);
        }
    }

  for (i = 0; i < ALIGN_TOOL_NUM_BUTTONS; i++)
    gtk_widget_set_sensitive (align_tool->button[i],
                              (align_tool->selected_objects != NULL));

  align_tool->x1 = align_tool->x0;
  align_tool->y1 = align_tool->y0;

  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
}

static void
gimp_align_tool_motion (GimpTool        *tool,
                        GimpCoords      *coords,
                        guint32          time,
                        GdkModifierType  state,
                        GimpDisplay     *display)
{
  GimpAlignTool *align_tool = GIMP_ALIGN_TOOL (tool);

  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));

  align_tool->x1 = coords->x;
  align_tool->y1 = coords->y;

  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
}

static void
gimp_align_tool_cursor_update (GimpTool        *tool,
                               GimpCoords      *coords,
                               GdkModifierType  state,
                               GimpDisplay     *display)
{
  GimpDisplayShell  *shell      = GIMP_DISPLAY_SHELL (display->shell);
  GimpCursorType      cursor      = GIMP_CURSOR_MOUSE;
  GimpToolCursorType  tool_cursor = GIMP_TOOL_CURSOR_MOVE;
  GimpCursorModifier  modifier    = GIMP_CURSOR_MODIFIER_NONE;
  gint                snap_distance;

  snap_distance =
    GIMP_DISPLAY_CONFIG (display->image->gimp->config)->snap_distance;

  if (gimp_draw_tool_on_vectors (GIMP_DRAW_TOOL (tool), display,
                                 coords, snap_distance, snap_distance,
                                 NULL, NULL, NULL, NULL, NULL, NULL))
    {
      tool_cursor = GIMP_TOOL_CURSOR_PATHS;
    }
  else if (gimp_display_shell_get_show_guides (shell) &&
           (NULL != gimp_image_find_guide (display->image,
                                           coords->x, coords->y,
                                           FUNSCALEX (shell, snap_distance),
                                           FUNSCALEY (shell, snap_distance))))
    {
      tool_cursor = GIMP_TOOL_CURSOR_MOVE;
    }
  else
    {
      GimpLayer *layer = select_layer_by_coords (display->image,
                                                 coords->x, coords->y);

      if (layer)
        {
          tool_cursor = GIMP_TOOL_CURSOR_HAND;
        }
    }

  gimp_tool_control_set_cursor          (tool->control, cursor);
  gimp_tool_control_set_tool_cursor     (tool->control, tool_cursor);
  gimp_tool_control_set_cursor_modifier (tool->control, modifier);

  GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
}

static void
gimp_align_tool_draw (GimpDrawTool *draw_tool)
{
  GimpAlignTool *align_tool = GIMP_ALIGN_TOOL (draw_tool);
  GList         *list;
  gint           x, y, w, h;

  /* draw rubber-band rectangle */
  x = MIN (align_tool->x1, align_tool->x0);
  y = MIN (align_tool->y1, align_tool->y0);
  w = MAX (align_tool->x1, align_tool->x0) - x;
  h = MAX (align_tool->y1, align_tool->y0) - y;

  gimp_draw_tool_draw_rectangle (draw_tool,
                                 FALSE,
                                 x, y,w, h,
                                 FALSE);

  for (list = g_list_first (align_tool->selected_objects); list;
       list = g_list_next (list))
    {
      if (GIMP_IS_ITEM (list->data))
        {
          GimpItem *item = GIMP_ITEM (list->data);

          gimp_item_offsets (item, &x, &y);
          w = gimp_item_width (item);
          h = gimp_item_height (item);

          gimp_draw_tool_draw_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
                                      x, y, 4, 4,
                                      GTK_ANCHOR_NORTH_WEST, FALSE);
          gimp_draw_tool_draw_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
                                      x + w, y, 4, 4,
                                      GTK_ANCHOR_NORTH_EAST, FALSE);
          gimp_draw_tool_draw_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
                                      x, y + h, 4, 4,
                                      GTK_ANCHOR_SOUTH_WEST, FALSE);
          gimp_draw_tool_draw_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
                                      x + w, y + h, 4, 4,
                                      GTK_ANCHOR_SOUTH_EAST, FALSE);
        }
      else if (GIMP_IS_GUIDE (list->data))
        {
          GimpGuide *guide = GIMP_GUIDE (list->data);
          GimpImage *image = GIMP_TOOL (draw_tool)->display->image;
          gint       x, y;
          gint       w, h;

          switch (gimp_guide_get_orientation (guide))
            {
            case GIMP_ORIENTATION_VERTICAL:
              x = gimp_guide_get_position (guide);
              h = gimp_image_get_height (image);
              gimp_draw_tool_draw_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
                                          x, h, 4, 4,
                                          GTK_ANCHOR_SOUTH, FALSE);
              gimp_draw_tool_draw_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
                                          x, 0, 4, 4,
                                          GTK_ANCHOR_NORTH, FALSE);
              break;

            case GIMP_ORIENTATION_HORIZONTAL:
              y = gimp_guide_get_position (guide);
              w = gimp_image_get_width (image);
              gimp_draw_tool_draw_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
                                          w, y, 4, 4,
                                          GTK_ANCHOR_EAST, FALSE);
              gimp_draw_tool_draw_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
                                          0, y, 4, 4,
                                          GTK_ANCHOR_WEST, FALSE);
              break;

            default:
              break;
            }
        }
    }
}


static GtkWidget *
gimp_align_tool_controls (GimpAlignTool *align_tool)
{
  GtkWidget *main_vbox;
  GtkWidget *hbox;
  GtkWidget *hbox2;
  GtkWidget *table;
  GtkWidget *label;
  GtkWidget *button;
  GtkWidget *spinbutton;
  GtkWidget *combo;
  GtkWidget *separator;
  gint       row, col, n;

  hbox = gtk_hbox_new (FALSE, 0);

  main_vbox = gtk_vbox_new (FALSE, 4);
  gtk_box_pack_start (GTK_BOX (hbox), main_vbox, FALSE, FALSE, 0);
  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 4);
  gtk_widget_show (main_vbox);

  table = gtk_table_new (8, 9, FALSE);
  gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 5);
  gtk_widget_show (table);

  gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  gtk_table_set_col_spacing (GTK_TABLE (table), 0, 10);

  row = col = 0;

  /* Top row */
  hbox2 = gtk_hbox_new (FALSE, 0);
  gtk_table_attach_defaults (GTK_TABLE (table), hbox2, 0, 8, row, row + 1);
  gtk_widget_show (hbox2);
  label = gtk_label_new (_("Align"));
  gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

  row++;

  /* align reference row */
  hbox2 = gtk_hbox_new (FALSE, 0);
  gtk_table_attach_defaults (GTK_TABLE (table), hbox2, 1, 8, row, row + 1);
  gtk_widget_show (hbox2);
  label = gtk_label_new (_("Relative to"));
  gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, FALSE, 0);
  gtk_widget_show (label);
  combo = gimp_enum_combo_box_new (GIMP_TYPE_ALIGN_REFERENCE);
  gtk_box_pack_start (GTK_BOX (hbox2), combo, FALSE, FALSE, 0);
  gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
                              GIMP_ALIGN_REFERENCE_FIRST,
                              G_CALLBACK (gimp_int_combo_box_get_active),
                              &align_tool->align_reference_type);
  gtk_widget_show (combo);

  gtk_table_set_row_spacing (GTK_TABLE (table), row, 10);
  row++;

 /* horizontal align row */

  col = 1;
  n = 0;

  button = button_with_stock (GIMP_ALIGN_LEFT, align_tool);
  gtk_table_attach_defaults (GTK_TABLE (table), button, col, col + 2, row, row + 1);
  gimp_help_set_help_data (button, _("Align left edge of target"), NULL);
  align_tool->button[n++] = button;
  ++col;
  ++col;

  button = button_with_stock (GIMP_ALIGN_HCENTER, align_tool);
  gtk_table_attach_defaults (GTK_TABLE (table), button, col, col + 2, row, row + 1);
  gimp_help_set_help_data (button, _("Align center of target"), NULL);
  align_tool->button[n++] = button;
  ++col;
  ++col;

  button = button_with_stock (GIMP_ALIGN_RIGHT, align_tool);
  gtk_table_attach_defaults (GTK_TABLE (table), button, col, col + 2, row, row + 1);
  gimp_help_set_help_data (button, _("Align right edge of target"), NULL);
  align_tool->button[n++] = button;
  ++col;
  ++col;

  row++;

  /* vertical align row */

  col = 1;

  button = button_with_stock (GIMP_ALIGN_TOP, align_tool);
  gtk_table_attach_defaults (GTK_TABLE (table), button, col, col + 2, row, row + 1);
  gimp_help_set_help_data (button, _("Align top edge of target"), NULL);
  align_tool->button[n++] = button;
  ++col;
  ++col;

  button = button_with_stock (GIMP_ALIGN_VCENTER, align_tool);
  gtk_table_attach_defaults (GTK_TABLE (table), button, col, col + 2, row, row + 1);
  gimp_help_set_help_data (button, _("Align middle of target"), NULL);
  align_tool->button[n++] = button;
  ++col;
  ++col;

  button = button_with_stock (GIMP_ALIGN_BOTTOM, align_tool);
  gtk_table_attach_defaults (GTK_TABLE (table), button, col, col + 2, row, row + 1);
  gimp_help_set_help_data (button, _("Align bottom of target"), NULL);
  align_tool->button[n++] = button;
  ++col;
  ++col;

  gtk_table_set_row_spacing (GTK_TABLE (table), row, 10);
  row++;

  /* separator */

  separator = gtk_hseparator_new ();
  gtk_table_attach_defaults (GTK_TABLE (table), separator, 0, 8, row, row + 1);
  gtk_widget_show (separator);

  row++;

  /* label row */
  hbox2 = gtk_hbox_new (FALSE, 0);
  gtk_table_attach_defaults (GTK_TABLE (table), hbox2, 0, 8, row, row + 1);
  gtk_widget_show (hbox2);
  label = gtk_label_new (_("Distribute"));
  gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

  row++;

  /* horizontal distribute row */

  col = 1;

  button = button_with_stock (GIMP_ARRANGE_LEFT, align_tool);
  gtk_table_attach_defaults (GTK_TABLE (table), button, col, col + 2, row, row + 1);
  gimp_help_set_help_data (button, _("Distribute left edges of targets"), NULL);
  align_tool->button[n++] = button;
  ++col;
  ++col;

  button = button_with_stock (GIMP_ARRANGE_HCENTER, align_tool);
  gtk_table_attach_defaults (GTK_TABLE (table), button, col, col + 2, row, row + 1);
  gimp_help_set_help_data (button, _("Distribute horiz centers of targets"), NULL);
  align_tool->button[n++] = button;
  ++col;
  ++col;

  button = button_with_stock (GIMP_ARRANGE_RIGHT, align_tool);
  gtk_table_attach_defaults (GTK_TABLE (table), button, col, col + 2, row, row + 1);
  gimp_help_set_help_data (button, _("Distribute right edges of targets"), NULL);
  align_tool->button[n++] = button;
  ++col;
  ++col;

  row++;

  /* vertical distribute row */

  col = 1;

  button = button_with_stock (GIMP_ARRANGE_TOP, align_tool);
  gtk_table_attach_defaults (GTK_TABLE (table), button, col, col + 2, row, row + 1);
  gimp_help_set_help_data (button, _("Distribute top edges of targets"), NULL);
  align_tool->button[n++] = button;
  ++col;
  ++col;

  button = button_with_stock (GIMP_ARRANGE_VCENTER, align_tool);
  gtk_table_attach_defaults (GTK_TABLE (table), button, col, col + 2, row, row + 1);
  gimp_help_set_help_data (button, _("Distribute vertical centers of targets"), NULL);
  align_tool->button[n++] = button;
  ++col;
  ++col;

  button = button_with_stock (GIMP_ARRANGE_BOTTOM, align_tool);
  gtk_table_attach_defaults (GTK_TABLE (table), button, col, col + 2, row, row + 1);
  gimp_help_set_help_data (button, _("Distribute bottoms of targets"), NULL);
  align_tool->button[n++] = button;
  ++col;
  ++col;

  row++;

  /* offset row */
  label = gtk_label_new (_("Offset:"));
  gtk_table_attach_defaults (GTK_TABLE (table), label, 1, 3, row, row + 1);
  gtk_widget_show (label);

  spinbutton = gimp_spin_button_new (&align_tool->horz_offset_adjustment,
                                     0,
                                     -100000.,
                                     100000.,
                                     1., 20., 20., 1., 0);
  gtk_table_attach_defaults (GTK_TABLE (table), spinbutton, 3, 7, row, row + 1);
  g_signal_connect (align_tool->horz_offset_adjustment, "value-changed",
                    G_CALLBACK (gimp_double_adjustment_update),
                    &align_tool->horz_offset);
  gtk_widget_show (spinbutton);

  gtk_widget_show (hbox);
  return hbox;
}


static void
do_alignment (GtkWidget *widget,
              gpointer   data)
{
  GimpAlignTool     *align_tool       = GIMP_ALIGN_TOOL (data);
  GimpAlignmentType  action;
  GimpImage         *image;
  GObject           *reference_object = NULL;
  GList             *list;
  gint               offset;

  image = GIMP_TOOL (align_tool)->display->image;
  action = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "action"));
  offset = align_tool->horz_offset;

  switch (action)
    {
    case GIMP_ALIGN_LEFT:
    case GIMP_ALIGN_HCENTER:
    case GIMP_ALIGN_RIGHT:
    case GIMP_ALIGN_TOP:
    case GIMP_ALIGN_VCENTER:
    case GIMP_ALIGN_BOTTOM:
      offset = 0;
      break;
    case GIMP_ARRANGE_LEFT:
    case GIMP_ARRANGE_HCENTER:
    case GIMP_ARRANGE_RIGHT:
    case GIMP_ARRANGE_TOP:
    case GIMP_ARRANGE_VCENTER:
    case GIMP_ARRANGE_BOTTOM:
      offset = align_tool->horz_offset;
      break;
    }

  /* if nothing is selected, just return */
  if (g_list_length (align_tool->selected_objects) == 0)
    return;

  /* if only one object is selected, use the image as reference
   * if multiple objects are selected, use the first one as reference if
   * "set_reference" is TRUE, otherwise use NULL.
   */

  list = align_tool->selected_objects;

  switch (align_tool->align_reference_type)
    {
    case GIMP_ALIGN_REFERENCE_IMAGE:
      reference_object = G_OBJECT (image);
      break;

    case GIMP_ALIGN_REFERENCE_FIRST:
      if (g_list_length (align_tool->selected_objects) == 1)
        {
          reference_object = G_OBJECT (image);
        }
      else
        {
          if (align_tool->set_reference)
            {
              reference_object = G_OBJECT (align_tool->selected_objects->data);
              list = g_list_next (align_tool->selected_objects);
            }
          else
            {
              reference_object = NULL;
            }
        }
      break;

    case GIMP_ALIGN_REFERENCE_SELECTION:
      if (image->selection_mask)
        {
          reference_object = G_OBJECT (image->selection_mask);
        }
      else
        return;
      break;

    case GIMP_ALIGN_REFERENCE_ACTIVE_LAYER:
      if (image->active_layer)
        reference_object = G_OBJECT (image->active_layer);
      else
        return;
      break;

    case GIMP_ALIGN_REFERENCE_ACTIVE_CHANNEL:
      if (image->active_channel)
        reference_object = G_OBJECT (image->active_channel);
      else
        return;
      break;

    case GIMP_ALIGN_REFERENCE_ACTIVE_PATH:
      g_print ("reference = active path not yet handled.\n");
      return;
      break;
    }

  gimp_draw_tool_pause (GIMP_DRAW_TOOL (align_tool));

  gimp_image_arrange_objects (image, list,
                              action,
                              reference_object,
                              action,
                              offset);

  gimp_draw_tool_resume (GIMP_DRAW_TOOL (align_tool));

  gimp_image_flush (image);
}




static GtkWidget *
button_with_stock (GimpAlignmentType  action,
                   GimpAlignTool     *align_tool)
{
  GtkWidget *button;
  gchar     *stock_id;

  switch (action)
    {
    case GIMP_ALIGN_LEFT:
      stock_id = GIMP_STOCK_GRAVITY_WEST;
      break;
    case GIMP_ALIGN_HCENTER:
      stock_id = GIMP_STOCK_HCENTER;
      break;
    case GIMP_ALIGN_RIGHT:
      stock_id = GIMP_STOCK_GRAVITY_EAST;
      break;
    case GIMP_ALIGN_TOP:
      stock_id = GIMP_STOCK_GRAVITY_NORTH;
      break;
    case GIMP_ALIGN_VCENTER:
      stock_id = GIMP_STOCK_VCENTER;
      break;
    case GIMP_ALIGN_BOTTOM:
      stock_id = GIMP_STOCK_GRAVITY_SOUTH;
      break;
    case GIMP_ARRANGE_LEFT:
      stock_id = GIMP_STOCK_GRAVITY_WEST;
      break;
    case GIMP_ARRANGE_HCENTER:
      stock_id = GIMP_STOCK_HCENTER;
      break;
    case GIMP_ARRANGE_RIGHT:
      stock_id = GIMP_STOCK_GRAVITY_EAST;
      break;
    case GIMP_ARRANGE_TOP:
      stock_id = GIMP_STOCK_GRAVITY_NORTH;
      break;
    case GIMP_ARRANGE_VCENTER:
      stock_id = GIMP_STOCK_VCENTER;
      break;
    case GIMP_ARRANGE_BOTTOM:
      stock_id = GIMP_STOCK_GRAVITY_SOUTH;
      break;
    default:
      stock_id = "?";
      break;
    }

  button = gtk_button_new_from_stock (stock_id);

  g_object_set_data (G_OBJECT (button), "action", GINT_TO_POINTER (action));

  g_signal_connect (button, "clicked",
                    G_CALLBACK (do_alignment),
                    align_tool);

  gtk_widget_set_sensitive (button, FALSE);
  gtk_widget_show (button);

  return button;
}

static void
clear_selected_object (GObject       *object,
                       GimpAlignTool *align_tool)
{
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (align_tool));

  if (align_tool->selected_objects)
    g_signal_handlers_disconnect_by_func (object,
                                          G_CALLBACK (clear_selected_object),
                                          (gpointer) align_tool);

  align_tool->selected_objects = g_list_remove (align_tool->selected_objects,
                                                object);

  gimp_draw_tool_resume (GIMP_DRAW_TOOL (align_tool));
}

static void
clear_all_selected_objects (GimpAlignTool *align_tool)
{
  GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (align_tool);

  if (gimp_draw_tool_is_active (draw_tool))
    gimp_draw_tool_pause (draw_tool);

  while (align_tool->selected_objects)
    {
      GObject *object = G_OBJECT (g_list_first (align_tool->selected_objects)->data);

      g_signal_handlers_disconnect_by_func (object,
                                            G_CALLBACK (clear_selected_object),
                                            (gpointer) align_tool);

      align_tool->selected_objects = g_list_remove (align_tool->selected_objects,
                                                    object);
    }

  if (gimp_draw_tool_is_active (draw_tool))
    gimp_draw_tool_resume (draw_tool);
}

static GimpLayer *
select_layer_by_coords (GimpImage *image,
                        gint       x,
                        gint       y)
{
  GList *list;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);

  for (list = GIMP_LIST (image->layers)->list;
       list;
       list = g_list_next (list))
    {
      GimpLayer *layer = list->data;
      gint       off_x, off_y;
      gint       width, height;

      if (! gimp_item_get_visible (GIMP_ITEM (layer)))
        continue;

      gimp_item_offsets (GIMP_ITEM (layer), &off_x, &off_y);
      width = gimp_item_width (GIMP_ITEM (layer));
      height = gimp_item_height (GIMP_ITEM (layer));

      if (off_x <= x &&
          off_y <= y &&
          x < off_x + width &&
          y < off_y + height)
        {
          return layer;
        }
    }

  return NULL;
}



