gimpaligntool.c 32.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/* 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"

27 28
#include "config/gimpdisplayconfig.h"

29
#include "core/gimp.h"
30
#include "core/gimpguide.h"
31
#include "core/gimpimage.h"
32 33 34
#include "core/gimpimage-arrange.h"
#include "core/gimpimage-guides.h"
#include "core/gimpimage-undo.h"
35
#include "core/gimplayer.h"
36
#include "core/gimplist.h"
37 38 39

#include "widgets/gimphelp-ids.h"

40
#include "display/gimpdisplay.h"
41 42
#include "display/gimpdisplayshell.h"
#include "display/gimpdisplayshell-appearance.h"
43

44 45 46 47 48 49 50
#include "gimpalignoptions.h"
#include "gimpaligntool.h"
#include "gimptoolcontrol.h"

#include "gimp-intl.h"


51
/*  local function prototypes  */
52

53 54
static GObject * gimp_align_tool_constructor (GType              type,
                                              guint              n_params,
55
                                              GObjectConstructParam *params);
56
static gboolean gimp_align_tool_initialize   (GimpTool          *tool,
57
                                              GimpDisplay       *display);
58
static void   gimp_align_tool_finalize       (GObject           *object);
59

60 61 62
static void   gimp_align_tool_control        (GimpTool          *tool,
                                              GimpToolAction     action,
                                              GimpDisplay       *display);
63
static void   gimp_align_tool_button_press   (GimpTool          *tool,
64 65 66
                                              GimpCoords        *coords,
                                              guint32            time,
                                              GdkModifierType    state,
67
                                              GimpDisplay       *display);
68 69 70 71 72 73 74 75 76 77
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);
78
static void   gimp_align_tool_cursor_update  (GimpTool          *tool,
79 80
                                              GimpCoords        *coords,
                                              GdkModifierType    state,
81
                                              GimpDisplay       *display);
82 83 84

static void   gimp_align_tool_draw           (GimpDrawTool      *draw_tool);

85 86 87
static GtkWidget *button_with_stock          (GimpAlignmentType  action,
                                              GimpAlignTool     *align_tool);
static GtkWidget *gimp_align_tool_controls   (GimpAlignTool     *align_tool);
88
static void   do_alignment                   (GtkWidget         *widget,
89
                                              gpointer           data);
90
static void   clear_selected_object          (GObject           *object,
91
                                              GimpAlignTool     *align_tool);
92
static void   clear_all_selected_objects     (GimpAlignTool     *align_tool);
93 94 95
static GimpLayer *select_layer_by_coords     (GimpImage         *image,
                                              gint               x,
                                              gint               y);
96 97 98 99 100 101
void          gimp_image_arrange_objects     (GimpImage         *image,
                                              GList             *list,
                                              GimpAlignmentType  alignment,
                                              GObject           *reference,
                                              GimpAlignmentType  reference_alignment,
                                              gint               offset);
102

103
G_DEFINE_TYPE (GimpAlignTool, gimp_align_tool, GIMP_TYPE_DRAW_TOOL)
104

105
#define parent_class gimp_align_tool_parent_class
106 107 108 109


void
gimp_align_tool_register (GimpToolRegisterCallback  callback,
110
                          gpointer                  data)
111 112 113 114 115 116
{
  (* callback) (GIMP_TYPE_ALIGN_TOOL,
                GIMP_TYPE_ALIGN_OPTIONS,
                gimp_align_options_gui,
                0,
                "gimp-align-tool",
117
                _("Alignment Tool"),
118
                _("Align or arrange layers and other items"),
119 120
                N_("_Align"), "Q",
                NULL, GIMP_HELP_TOOL_MOVE,
Sven Neumann's avatar
Sven Neumann committed
121
                GIMP_STOCK_TOOL_ALIGN,
122 123 124 125 126 127 128 129 130 131 132 133
                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;
134

135
  tool_class->initialize     = gimp_align_tool_initialize;
136
  tool_class->control        = gimp_align_tool_control;
137
  tool_class->button_press   = gimp_align_tool_button_press;
138 139
  tool_class->button_release = gimp_align_tool_button_release;
  tool_class->motion         = gimp_align_tool_motion;
140 141 142 143 144
  tool_class->cursor_update  = gimp_align_tool_cursor_update;

  draw_tool_class->draw      = gimp_align_tool_draw;
}

145 146 147 148 149 150 151
static void
gimp_align_tool_init (GimpAlignTool *align_tool)
{
  GimpTool *tool = GIMP_TOOL (align_tool);

  align_tool->controls        = NULL;

152
  align_tool->selected_objects = NULL;
153

154
  align_tool->align_type = GIMP_ALIGN_LEFT;
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171

  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;
172
  GtkContainer  *container;
173 174 175 176 177 178
  GObject       *options;

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

  tool       = GIMP_TOOL (object);
  align_tool = GIMP_ALIGN_TOOL (object);
179
  options    = G_OBJECT (gimp_tool_get_options (tool));
180

181 182
  container = GTK_CONTAINER (g_object_get_data (options,
                                                "controls-container"));
183 184

  align_tool->controls = gimp_align_tool_controls (align_tool);
185
  gtk_container_add (container, align_tool->controls);
186 187 188 189

  return object;
}

190 191 192 193 194 195 196
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)))
197
    gimp_draw_tool_stop (GIMP_DRAW_TOOL (object));
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212

  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,
213
                            GimpDisplay *display)
214
{
215
  if (tool->display != display)
216 217 218 219 220 221
    {
    }

  return TRUE;
}

222 223 224 225 226 227 228 229 230
static void
gimp_align_tool_control (GimpTool       *tool,
                         GimpToolAction  action,
                         GimpDisplay    *display)
{
  GimpAlignTool *align_tool = GIMP_ALIGN_TOOL (tool);

  switch (action)
    {
231 232
    case GIMP_TOOL_ACTION_PAUSE:
    case GIMP_TOOL_ACTION_RESUME:
233 234
      break;

235
    case GIMP_TOOL_ACTION_HALT:
236
      clear_all_selected_objects (align_tool);
237 238 239 240 241 242 243
      gimp_tool_pop_status (tool, display);
      break;
    }

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

244 245
static void
gimp_align_tool_button_press (GimpTool        *tool,
246 247 248
                              GimpCoords      *coords,
                              guint32          time,
                              GdkModifierType  state,
249
                              GimpDisplay     *display)
250
{
251
  GimpAlignTool    *align_tool  = GIMP_ALIGN_TOOL (tool);
252 253

  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
254 255 256

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

257
  if (display != tool->display)
258 259 260 261
    {
      if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tool)))
        gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));

262
      clear_all_selected_objects (align_tool);
263 264 265 266 267
    }

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

268
  tool->display = display;
269

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

273
  if (! gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tool)))
274
    gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
275

276 277 278
  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
}

279 280 281 282 283 284 285
/*
 * 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.
 */
286 287 288 289 290 291 292
static void
gimp_align_tool_button_release (GimpTool        *tool,
                                GimpCoords      *coords,
                                guint32          time,
                                GdkModifierType  state,
                                GimpDisplay     *display)
{
293 294 295 296
  GimpAlignTool    *align_tool = GIMP_ALIGN_TOOL (tool);
  GimpDisplayShell *shell      = GIMP_DISPLAY_SHELL (display->shell);
  GObject          *object     = NULL;
  GimpImage        *image      = display->image;
297 298
  gint              i;

299 300
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));

301 302 303 304 305 306 307 308 309 310 311 312 313 314
  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;
    }
315

316 317 318 319 320
#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)
321
    {
322 323 324 325
      GimpVectors *vectors;
      GimpGuide   *guide;
      GimpLayer   *layer;
      gint         snap_distance;
326

327 328 329 330 331 332 333 334 335
      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);
336
        }
337 338 339 340 341 342 343 344 345
      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
346 347 348 349
        {
          if ((layer = select_layer_by_coords (display->image,
                                               coords->x, coords->y)))
            {
350
              object = G_OBJECT (layer);
351 352
            }
        }
353

354
      if (object)
355
        {
356
          if (! g_list_find (align_tool->selected_objects, object))
357
            {
358 359 360 361
              align_tool->selected_objects
                = g_list_append (align_tool->selected_objects, object);
              g_signal_connect (object, "removed",
                                G_CALLBACK (clear_selected_object),
362
                                (gpointer) align_tool);
363 364 365 366 367 368

              /* 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;
369
            }
370 371
        }
    }
372
  else  /* FIXME: look for vectors too */
373
    {
374 375 376 377 378 379 380 381 382
      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))
383
        {
384 385 386 387 388 389 390 391 392 393 394 395 396
          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;

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

400 401 402
          align_tool->selected_objects
            = g_list_append (align_tool->selected_objects, layer);
          g_signal_connect (layer, "removed", G_CALLBACK (clear_selected_object),
403
                            (gpointer) align_tool);
404 405 406
        }
    }

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

411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
  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;
430

431 432 433 434 435
  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
}

static void
gimp_align_tool_cursor_update (GimpTool        *tool,
436 437
                               GimpCoords      *coords,
                               GdkModifierType  state,
438
                               GimpDisplay     *display)
439
{
440 441 442 443 444 445 446 447 448 449 450 451
  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))
452 453
    {
      tool_cursor = GIMP_TOOL_CURSOR_PATHS;
454 455 456 457 458 459 460 461
    }
  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;
462 463 464
    }
  else
    {
465 466
      GimpLayer *layer = select_layer_by_coords (display->image,
                                                 coords->x, coords->y);
467

468
      if (layer)
469
        {
470
          tool_cursor = GIMP_TOOL_CURSOR_HAND;
471
        }
472 473 474 475 476 477
    }

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

478
  GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
479 480 481 482 483
}

static void
gimp_align_tool_draw (GimpDrawTool *draw_tool)
{
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
  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);

499
  for (list = g_list_first (align_tool->selected_objects); list;
500
       list = g_list_next (list))
501
    {
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
      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;
            }
        }
558 559 560 561 562 563 564 565 566
    }
}


static GtkWidget *
gimp_align_tool_controls (GimpAlignTool *align_tool)
{
  GtkWidget *main_vbox;
  GtkWidget *hbox;
567
  GtkWidget *hbox2;
568 569 570
  GtkWidget *table;
  GtkWidget *label;
  GtkWidget *button;
571
  GtkWidget *spinbutton;
572 573
  GtkWidget *combo;
  GtkWidget *separator;
574
  gint       row, col, n;
575 576 577 578 579 580 581 582

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

583
  table = gtk_table_new (8, 9, FALSE);
584
  gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 5);
585 586
  gtk_widget_show (table);

587 588
  gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  gtk_table_set_col_spacing (GTK_TABLE (table), 0, 10);
589 590 591 592

  row = col = 0;

  /* Top row */
593 594 595
  hbox2 = gtk_hbox_new (FALSE, 0);
  gtk_table_attach_defaults (GTK_TABLE (table), hbox2, 0, 8, row, row + 1);
  gtk_widget_show (hbox2);
596
  label = gtk_label_new (_("Align"));
597
  gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, FALSE, 0);
598 599 600 601
  gtk_widget_show (label);

  row++;

602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
  /* 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 */
621

622
  col = 1;
623
  n = 0;
624 625 626 627

  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);
628
  align_tool->button[n++] = button;
629 630 631
  ++col;
  ++col;

632
  button = button_with_stock (GIMP_ALIGN_HCENTER, align_tool);
633 634
  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);
635
  align_tool->button[n++] = button;
636 637 638 639 640 641
  ++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);
642
  align_tool->button[n++] = button;
643 644 645 646 647
  ++col;
  ++col;

  row++;

648 649 650
  /* vertical align row */

  col = 1;
651

652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
  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;
672

673 674 675 676 677 678 679 680 681
  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);

682
  row++;
683

684
  /* label row */
685 686 687
  hbox2 = gtk_hbox_new (FALSE, 0);
  gtk_table_attach_defaults (GTK_TABLE (table), hbox2, 0, 8, row, row + 1);
  gtk_widget_show (hbox2);
688
  label = gtk_label_new (_("Distribute"));
689
  gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, FALSE, 0);
690 691 692 693
  gtk_widget_show (label);

  row++;

694
  /* horizontal distribute row */
695

696
  col = 1;
697

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

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

712
  button = button_with_stock (GIMP_ARRANGE_RIGHT, align_tool);
713
  gtk_table_attach_defaults (GTK_TABLE (table), button, col, col + 2, row, row + 1);
714
  gimp_help_set_help_data (button, _("Distribute right edges of targets"), NULL);
715
  align_tool->button[n++] = button;
716 717 718 719 720
  ++col;
  ++col;

  row++;

721
  /* vertical distribute row */
722

723
  col = 1;
724

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

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

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

746
  row++;
747

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

753 754 755 756 757 758 759 760 761 762
  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);
763

764 765
  gtk_widget_show (hbox);
  return hbox;
766 767 768 769
}


static void
770 771
do_alignment (GtkWidget *widget,
              gpointer   data)
772
{
773
  GimpAlignTool     *align_tool       = GIMP_ALIGN_TOOL (data);
774 775
  GimpAlignmentType  action;
  GimpImage         *image;
776
  GObject           *reference_object = NULL;
777 778
  GList             *list;
  gint               offset;
779 780

  image = GIMP_TOOL (align_tool)->display->image;
781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802
  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;
    }
803

804 805 806 807 808
  /* 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
809 810
   * if multiple objects are selected, use the first one as reference if
   * "set_reference" is TRUE, otherwise use NULL.
811
   */
812 813 814 815

  list = align_tool->selected_objects;

  switch (align_tool->align_reference_type)
816
    {
817
    case GIMP_ALIGN_REFERENCE_IMAGE:
818
      reference_object = G_OBJECT (image);
819 820 821 822
      break;

    case GIMP_ALIGN_REFERENCE_FIRST:
      if (g_list_length (align_tool->selected_objects) == 1)
823
        {
824
          reference_object = G_OBJECT (image);
825 826 827
        }
      else
        {
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
          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);
844
        }
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866
      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;
867
    }
868

869
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (align_tool));
870

871 872
  gimp_image_arrange_objects (image, list,
                              action,
873
                              reference_object,
874 875
                              action,
                              offset);
876 877

  gimp_draw_tool_resume (GIMP_DRAW_TOOL (align_tool));
878 879

  gimp_image_flush (image);
880 881 882 883
}



884

885 886 887 888 889 890 891 892 893 894 895 896
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;
897
    case GIMP_ALIGN_HCENTER:
898 899 900 901 902 903 904 905
      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;
906
    case GIMP_ALIGN_VCENTER:
907 908 909 910 911
      stock_id = GIMP_STOCK_VCENTER;
      break;
    case GIMP_ALIGN_BOTTOM:
      stock_id = GIMP_STOCK_GRAVITY_SOUTH;
      break;
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929
    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;
930 931 932 933 934 935 936
    default:
      stock_id = "?";
      break;
    }

  button = gtk_button_new_from_stock (stock_id);

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

939
  g_signal_connect (button, "clicked",
940
                    G_CALLBACK (do_alignment),
941
                    align_tool);
942

943
  gtk_widget_set_sensitive (button, FALSE);
944 945 946 947 948
  gtk_widget_show (button);

  return button;
}

949
static void
950 951
clear_selected_object (GObject       *object,
                       GimpAlignTool *align_tool)
952 953 954
{
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (align_tool));

955 956 957
  if (align_tool->selected_objects)
    g_signal_handlers_disconnect_by_func (object,
                                          G_CALLBACK (clear_selected_object),
958 959
                                          (gpointer) align_tool);

960 961
  align_tool->selected_objects = g_list_remove (align_tool->selected_objects,
                                                object);
962 963 964 965 966

  gimp_draw_tool_resume (GIMP_DRAW_TOOL (align_tool));
}

static void
967
clear_all_selected_objects (GimpAlignTool *align_tool)
968
{
969 970 971 972 973
  GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (align_tool);

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

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

978 979
      g_signal_handlers_disconnect_by_func (object,
                                            G_CALLBACK (clear_selected_object),
980
                                            (gpointer) align_tool);
981

982 983
      align_tool->selected_objects = g_list_remove (align_tool->selected_objects,
                                                    object);
984
    }
985 986 987

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

990 991 992 993 994 995
static GimpLayer *
select_layer_by_coords (GimpImage *image,
                        gint       x,
                        gint       y)
{
  GList *list;
996

997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
  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;
1024
}
1025 1026 1027