gimpaligntool.c 28.1 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
2 3
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7 8 9 10 11 12 13 14
 * (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
15
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 17 18 19
 */

#include "config.h"

20
#include <gegl.h>
21
#include <gtk/gtk.h>
22
#include <gdk/gdkkeysyms.h>
23

24
#include "libgimpmath/gimpmath.h"
25 26 27 28
#include "libgimpwidgets/gimpwidgets.h"

#include "tools-types.h"

29 30
#include "config/gimpdisplayconfig.h"

31
#include "core/gimp.h"
32
#include "core/gimpguide.h"
33
#include "core/gimpimage.h"
34
#include "core/gimpimage-arrange.h"
35
#include "core/gimpimage-pick-item.h"
36
#include "core/gimpimage-undo.h"
37 38
#include "core/gimplayer.h"

39 40
#include "vectors/gimpvectors.h"

41
#include "widgets/gimphelp-ids.h"
42
#include "widgets/gimpwidgets-utils.h"
43

44
#include "display/gimpdisplay.h"
45 46
#include "display/gimpdisplayshell.h"
#include "display/gimpdisplayshell-appearance.h"
47

48 49 50 51 52 53
#include "gimpalignoptions.h"
#include "gimpaligntool.h"
#include "gimptoolcontrol.h"

#include "gimp-intl.h"

54 55

#define EPSILON  3   /* move distance after which we do a box-select */
56

57

58
/*  local function prototypes  */
59

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
static void     gimp_align_tool_constructed    (GObject               *object);

static void     gimp_align_tool_control        (GimpTool              *tool,
                                                GimpToolAction         action,
                                                GimpDisplay           *display);
static void     gimp_align_tool_button_press   (GimpTool              *tool,
                                                const GimpCoords      *coords,
                                                guint32                time,
                                                GdkModifierType        state,
                                                GimpButtonPressType    press_type,
                                                GimpDisplay           *display);
static void     gimp_align_tool_button_release (GimpTool              *tool,
                                                const GimpCoords      *coords,
                                                guint32                time,
                                                GdkModifierType        state,
                                                GimpButtonReleaseType  release_type,
                                                GimpDisplay           *display);
static void     gimp_align_tool_motion         (GimpTool              *tool,
                                                const GimpCoords      *coords,
                                                guint32                time,
                                                GdkModifierType        state,
                                                GimpDisplay           *display);
static gboolean gimp_align_tool_key_press      (GimpTool              *tool,
                                                GdkEventKey           *kevent,
                                                GimpDisplay           *display);
static void     gimp_align_tool_oper_update    (GimpTool              *tool,
                                                const GimpCoords      *coords,
                                                GdkModifierType        state,
                                                gboolean               proximity,
                                                GimpDisplay           *display);
static void     gimp_align_tool_status_update  (GimpTool              *tool,
                                                GimpDisplay           *display,
                                                GdkModifierType        state,
                                                gboolean               proximity);
static void     gimp_align_tool_cursor_update  (GimpTool              *tool,
                                                const GimpCoords      *coords,
                                                GdkModifierType        state,
                                                GimpDisplay           *display);

static void     gimp_align_tool_draw           (GimpDrawTool          *draw_tool);

static void     gimp_align_tool_align          (GimpAlignTool         *align_tool,
                                                GimpAlignmentType      align_type);

static void     gimp_align_tool_object_removed (GObject               *object,
                                                GimpAlignTool         *align_tool);
static void     gimp_align_tool_clear_selected (GimpAlignTool         *align_tool);
107

108

109
G_DEFINE_TYPE (GimpAlignTool, gimp_align_tool, GIMP_TYPE_DRAW_TOOL)
110

111
#define parent_class gimp_align_tool_parent_class
112 113 114 115


void
gimp_align_tool_register (GimpToolRegisterCallback  callback,
116
                          gpointer                  data)
117 118 119 120 121 122
{
  (* callback) (GIMP_TYPE_ALIGN_TOOL,
                GIMP_TYPE_ALIGN_OPTIONS,
                gimp_align_options_gui,
                0,
                "gimp-align-tool",
123 124
                _("Align"),
                _("Alignment Tool: Align or arrange layers and other objects"),
125
                N_("_Align"), "Q",
126
                NULL, GIMP_HELP_TOOL_ALIGN,
127
                GIMP_ICON_TOOL_ALIGN,
128 129 130 131 132 133 134 135 136 137
                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);

138
  object_class->constructed  = gimp_align_tool_constructed;
139

140
  tool_class->control        = gimp_align_tool_control;
141
  tool_class->button_press   = gimp_align_tool_button_press;
142 143
  tool_class->button_release = gimp_align_tool_button_release;
  tool_class->motion         = gimp_align_tool_motion;
144
  tool_class->key_press      = gimp_align_tool_key_press;
145
  tool_class->oper_update    = gimp_align_tool_oper_update;
146 147 148 149 150
  tool_class->cursor_update  = gimp_align_tool_cursor_update;

  draw_tool_class->draw      = gimp_align_tool_draw;
}

151 152 153 154 155 156
static void
gimp_align_tool_init (GimpAlignTool *align_tool)
{
  GimpTool *tool = GIMP_TOOL (align_tool);

  gimp_tool_control_set_snap_to     (tool->control, FALSE);
157 158
  gimp_tool_control_set_precision   (tool->control,
                                     GIMP_CURSOR_PRECISION_PIXEL_BORDER);
159
  gimp_tool_control_set_tool_cursor (tool->control, GIMP_TOOL_CURSOR_MOVE);
160 161

  align_tool->function = ALIGN_TOOL_IDLE;
162 163
}

164 165
static void
gimp_align_tool_constructed (GObject *object)
166
{
167 168
  GimpAlignTool    *align_tool = GIMP_ALIGN_TOOL (object);
  GimpAlignOptions *options;
169

170
  G_OBJECT_CLASS (parent_class)->constructed (object);
171

172
  options = GIMP_ALIGN_TOOL_GET_OPTIONS (align_tool);
173

174 175 176
  g_signal_connect_object (options, "align-button-clicked",
                           G_CALLBACK (gimp_align_tool_align),
                           align_tool, G_CONNECT_SWAPPED);
177 178
}

179 180 181 182 183 184 185 186 187
static void
gimp_align_tool_control (GimpTool       *tool,
                         GimpToolAction  action,
                         GimpDisplay    *display)
{
  GimpAlignTool *align_tool = GIMP_ALIGN_TOOL (tool);

  switch (action)
    {
188 189
    case GIMP_TOOL_ACTION_PAUSE:
    case GIMP_TOOL_ACTION_RESUME:
190 191
      break;

192
    case GIMP_TOOL_ACTION_HALT:
193
      gimp_align_tool_clear_selected (align_tool);
194
      break;
195 196 197

    case GIMP_TOOL_ACTION_COMMIT:
      break;
198 199 200 201 202
    }

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

203
static void
204 205 206 207 208 209
gimp_align_tool_button_press (GimpTool            *tool,
                              const GimpCoords    *coords,
                              guint32              time,
                              GdkModifierType      state,
                              GimpButtonPressType  press_type,
                              GimpDisplay         *display)
210
{
211
  GimpAlignTool *align_tool  = GIMP_ALIGN_TOOL (tool);
212 213

  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
214

215
  /*  If the tool was being used in another image... reset it  */
216
  if (display != tool->display)
217
    gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
218

219
  tool->display = display;
220

221 222
  gimp_tool_control_activate (tool->control);

223 224
  align_tool->x2 = align_tool->x1 = coords->x;
  align_tool->y2 = align_tool->y1 = coords->y;
225

226
  if (! gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tool)))
227
    gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
228

229 230 231
  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
}

232
/* some rather complex logic here.  If the user clicks without modifiers,
233 234 235 236 237
 * 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.
 */
238
static void
239
gimp_align_tool_button_release (GimpTool              *tool,
240
                                const GimpCoords      *coords,
241 242 243 244
                                guint32                time,
                                GdkModifierType        state,
                                GimpButtonReleaseType  release_type,
                                GimpDisplay           *display)
245
{
246
  GimpAlignTool    *align_tool = GIMP_ALIGN_TOOL (tool);
247
  GimpAlignOptions *options    = GIMP_ALIGN_TOOL_GET_OPTIONS (tool);
248
  GimpDisplayShell *shell      = gimp_display_get_shell (display);
249
  GObject          *object     = NULL;
250
  GimpImage        *image      = gimp_display_get_image (display);
251
  GdkModifierType   extend_mask;
252 253
  gint              i;

254 255
  extend_mask = gimp_get_extend_selection_mask ();

256 257
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));

258 259
  gimp_tool_control_halt (tool->control);

260
  if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
261
    {
262 263
      align_tool->x2 = align_tool->x1;
      align_tool->y2 = align_tool->y1;
264 265 266 267 268

      gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
      return;
    }

269
  if (! (state & extend_mask)) /* start a new list */
270
    {
271
      gimp_align_tool_clear_selected (align_tool);
272 273
      align_tool->set_reference = FALSE;
    }
274

275 276 277
  /* if mouse has moved less than EPSILON pixels since button press,
   * select the nearest thing, otherwise make a rubber-band rectangle
   */
278 279
  if (hypot (coords->x - align_tool->x1,
             coords->y - align_tool->y1) < EPSILON)
280
    {
281 282 283
      GimpVectors *vectors;
      GimpGuide   *guide;
      GimpLayer   *layer;
284
      gint         snap_distance = display->config->snap_distance;
285

286 287 288 289
      if ((vectors = gimp_image_pick_vectors (image,
                                              coords->x, coords->y,
                                              FUNSCALEX (shell, snap_distance),
                                              FUNSCALEY (shell, snap_distance))))
290 291
        {
          object = G_OBJECT (vectors);
292
        }
293
      else if (gimp_display_shell_get_show_guides (shell) &&
294
               (guide = gimp_image_pick_guide (image,
295 296 297 298 299 300
                                               coords->x, coords->y,
                                               FUNSCALEX (shell, snap_distance),
                                               FUNSCALEY (shell, snap_distance))))
        {
          object = G_OBJECT (guide);
        }
301 302
      else if ((layer = gimp_image_pick_layer_by_bounds (image,
                                                         coords->x, coords->y)))
303
        {
304
          object = G_OBJECT (layer);
305
        }
306

307
      if (object)
308
        {
309
          if (! g_list_find (align_tool->selected_objects, object))
310
            {
311 312 313
              align_tool->selected_objects =
                g_list_append (align_tool->selected_objects, object);

314
              g_signal_connect (object, "removed",
315
                                G_CALLBACK (gimp_align_tool_object_removed),
316
                                align_tool);
317 318 319 320

              /* if an object has been selected using unmodified click,
               * it should be used as the reference
               */
321
              if (! (state & extend_mask))
322
                align_tool->set_reference = TRUE;
323
            }
324 325
        }
    }
326
  else  /* FIXME: look for vectors too */
327
    {
328 329 330 331
      gint   X0 = MIN (coords->x, align_tool->x1);
      gint   X1 = MAX (coords->x, align_tool->x1);
      gint   Y0 = MIN (coords->y, align_tool->y1);
      gint   Y1 = MAX (coords->y, align_tool->y1);
332
      GList *all_layers;
333 334
      GList *list;

335 336 337
      all_layers = gimp_image_get_layer_list (image);

      for (list = all_layers; list; list = g_list_next (list))
338
        {
339 340 341 342 343 344
          GimpLayer *layer = list->data;
          gint       x0, y0, x1, y1;

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

345
          gimp_item_get_offset (GIMP_ITEM (layer), &x0, &y0);
346 347
          x1 = x0 + gimp_item_get_width  (GIMP_ITEM (layer));
          y1 = y0 + gimp_item_get_height (GIMP_ITEM (layer));
348 349 350 351

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

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

355 356 357 358
          align_tool->selected_objects =
            g_list_append (align_tool->selected_objects, layer);

          g_signal_connect (layer, "removed",
359
                            G_CALLBACK (gimp_align_tool_object_removed),
360
                            align_tool);
361
        }
362 363

      g_list_free (all_layers);
364 365
    }

366
  for (i = 0; i < ALIGN_OPTIONS_N_BUTTONS; i++)
367
    {
368 369 370
      if (options->button[i])
        gtk_widget_set_sensitive (options->button[i],
                                  align_tool->selected_objects != NULL);
371
    }
372

373 374
  align_tool->x2 = align_tool->x1;
  align_tool->y2 = align_tool->y1;
375 376 377 378 379

  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
}

static void
380 381 382 383 384
gimp_align_tool_motion (GimpTool         *tool,
                        const GimpCoords *coords,
                        guint32           time,
                        GdkModifierType   state,
                        GimpDisplay      *display)
385 386 387 388 389
{
  GimpAlignTool *align_tool = GIMP_ALIGN_TOOL (tool);

  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));

390 391
  align_tool->x2 = coords->x;
  align_tool->y2 = coords->y;
392

393 394 395
  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
}

396 397 398 399 400 401 402 403 404 405
static gboolean
gimp_align_tool_key_press (GimpTool    *tool,
                           GdkEventKey *kevent,
                           GimpDisplay *display)
{
  if (display == tool->display)
    {
      switch (kevent->keyval)
        {
        case GDK_KEY_Escape:
406
          gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
407 408 409 410 411 412 413 414 415 416
          return TRUE;

        default:
          break;
        }
    }

  return FALSE;
}

417
static void
418 419 420 421 422
gimp_align_tool_oper_update (GimpTool         *tool,
                             const GimpCoords *coords,
                             GdkModifierType   state,
                             gboolean          proximity,
                             GimpDisplay      *display)
423
{
424 425 426 427
  GimpAlignTool    *align_tool    = GIMP_ALIGN_TOOL (tool);
  GimpDisplayShell *shell         = gimp_display_get_shell (display);
  GimpImage        *image         = gimp_display_get_image (display);
  gint              snap_distance = display->config->snap_distance;
428 429
  gboolean          add;

430 431
  add = ((state & gimp_get_extend_selection_mask ()) &&
         align_tool->selected_objects);
432

433 434 435 436
  if (gimp_image_pick_vectors (image,
                               coords->x, coords->y,
                               FUNSCALEX (shell, snap_distance),
                               FUNSCALEY (shell, snap_distance)))
437
    {
438
      if (add)
439 440 441
        align_tool->function = ALIGN_TOOL_ADD_PATH;
      else
        align_tool->function = ALIGN_TOOL_PICK_PATH;
442 443
    }
  else if (gimp_display_shell_get_show_guides (shell) &&
444
           gimp_image_pick_guide (image,
445 446 447
                                  coords->x, coords->y,
                                  FUNSCALEX (shell, snap_distance),
                                  FUNSCALEY (shell, snap_distance)))
448
    {
449
      if (add)
450 451 452
        align_tool->function = ALIGN_TOOL_ADD_GUIDE;
      else
        align_tool->function = ALIGN_TOOL_PICK_GUIDE;
453
    }
454
  else if (gimp_image_pick_layer_by_bounds (image, coords->x, coords->y))
455
    {
456 457
      if (add)
        align_tool->function = ALIGN_TOOL_ADD_LAYER;
458
      else
459 460 461 462 463
        align_tool->function = ALIGN_TOOL_PICK_LAYER;
    }
  else
    {
      align_tool->function = ALIGN_TOOL_IDLE;
464 465
    }

466 467 468 469
  gimp_align_tool_status_update (tool, display, state, proximity);
}

static void
470 471 472 473
gimp_align_tool_cursor_update (GimpTool         *tool,
                               const GimpCoords *coords,
                               GdkModifierType   state,
                               GimpDisplay      *display)
474 475 476 477 478
{
  GimpAlignTool      *align_tool  = GIMP_ALIGN_TOOL (tool);
  GimpToolCursorType  tool_cursor = GIMP_TOOL_CURSOR_NONE;
  GimpCursorModifier  modifier    = GIMP_CURSOR_MODIFIER_NONE;

479
  /* always add '+' when Shift is pressed, even if nothing is selected */
480
  if (state & gimp_get_extend_selection_mask ())
481
    modifier = GIMP_CURSOR_MODIFIER_PLUS;
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508

  switch (align_tool->function)
    {
    case ALIGN_TOOL_IDLE:
      tool_cursor = GIMP_TOOL_CURSOR_RECT_SELECT;
      break;

    case ALIGN_TOOL_PICK_LAYER:
    case ALIGN_TOOL_ADD_LAYER:
      tool_cursor = GIMP_TOOL_CURSOR_HAND;
      break;

    case ALIGN_TOOL_PICK_GUIDE:
    case ALIGN_TOOL_ADD_GUIDE:
      tool_cursor = GIMP_TOOL_CURSOR_MOVE;
      break;

    case ALIGN_TOOL_PICK_PATH:
    case ALIGN_TOOL_ADD_PATH:
      tool_cursor = GIMP_TOOL_CURSOR_PATHS;
      break;

    case ALIGN_TOOL_DRAG_BOX:
      break;
    }

  gimp_tool_control_set_cursor          (tool->control, GIMP_CURSOR_MOUSE);
509 510 511
  gimp_tool_control_set_tool_cursor     (tool->control, tool_cursor);
  gimp_tool_control_set_cursor_modifier (tool->control, modifier);

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

515 516 517 518 519 520
static void
gimp_align_tool_status_update (GimpTool        *tool,
                               GimpDisplay     *display,
                               GdkModifierType  state,
                               gboolean         proximity)
{
521 522 523 524
  GimpAlignTool   *align_tool = GIMP_ALIGN_TOOL (tool);
  GdkModifierType  extend_mask;

  extend_mask = gimp_get_extend_selection_mask ();
525 526 527 528 529

  gimp_tool_pop_status (tool, display);

  if (proximity)
    {
530
      gchar *status = NULL;
531 532 533 534

      if (! align_tool->selected_objects)
        {
          /* no need to suggest Shift if nothing is selected */
535
          state |= extend_mask;
536 537 538 539 540 541 542 543
        }

      switch (align_tool->function)
        {
        case ALIGN_TOOL_IDLE:
          status = gimp_suggest_modifiers (_("Click on a layer, path or guide, "
                                             "or Click-Drag to pick several "
                                             "layers"),
544
                                           extend_mask & ~state,
545 546 547 548 549 550
                                           NULL, NULL, NULL);
          break;

        case ALIGN_TOOL_PICK_LAYER:
          status = gimp_suggest_modifiers (_("Click to pick this layer as "
                                             "first item"),
551
                                           extend_mask & ~state,
552 553 554 555
                                           NULL, NULL, NULL);
          break;

        case ALIGN_TOOL_ADD_LAYER:
556
          status = g_strdup (_("Click to add this layer to the list"));
557 558 559 560 561
          break;

        case ALIGN_TOOL_PICK_GUIDE:
          status = gimp_suggest_modifiers (_("Click to pick this guide as "
                                             "first item"),
562
                                           extend_mask & ~state,
563 564 565 566
                                           NULL, NULL, NULL);
          break;

        case ALIGN_TOOL_ADD_GUIDE:
567
          status = g_strdup (_("Click to add this guide to the list"));
568 569 570 571 572
          break;

        case ALIGN_TOOL_PICK_PATH:
          status = gimp_suggest_modifiers (_("Click to pick this path as "
                                             "first item"),
573
                                           extend_mask & ~state,
574 575 576 577
                                           NULL, NULL, NULL);
          break;

        case ALIGN_TOOL_ADD_PATH:
578
          status = g_strdup (_("Click to add this path to the list"));
579 580 581 582 583 584 585
          break;

        case ALIGN_TOOL_DRAG_BOX:
          break;
        }

      if (status)
586 587 588 589
        {
          gimp_tool_push_status (tool, display, "%s", status);
          g_free (status);
        }
590 591 592
    }
}

593 594 595
static void
gimp_align_tool_draw (GimpDrawTool *draw_tool)
{
596 597 598 599 600
  GimpAlignTool *align_tool = GIMP_ALIGN_TOOL (draw_tool);
  GList         *list;
  gint           x, y, w, h;

  /* draw rubber-band rectangle */
601 602 603 604
  x = MIN (align_tool->x2, align_tool->x1);
  y = MIN (align_tool->y2, align_tool->y1);
  w = MAX (align_tool->x2, align_tool->x1) - x;
  h = MAX (align_tool->y2, align_tool->y1) - y;
605

606
  gimp_draw_tool_add_rectangle (draw_tool, FALSE, x, y, w, h);
607

608 609
  for (list = align_tool->selected_objects;
       list;
610
       list = g_list_next (list))
611
    {
612 613
      if (GIMP_IS_ITEM (list->data))
        {
614
          GimpItem *item = list->data;
615
          gint      off_x, off_y;
616

617
          gimp_item_bounds (item, &x, &y, &w, &h);
618

619 620 621
          gimp_item_get_offset (item, &off_x, &off_y);
          x += off_x;
          y += off_y;
622

623
          gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
624 625 626
                                     x, y,
                                     GIMP_TOOL_HANDLE_SIZE_SMALL,
                                     GIMP_TOOL_HANDLE_SIZE_SMALL,
627
                                     GIMP_HANDLE_ANCHOR_NORTH_WEST);
628
          gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
629 630 631
                                     x + w, y,
                                     GIMP_TOOL_HANDLE_SIZE_SMALL,
                                     GIMP_TOOL_HANDLE_SIZE_SMALL,
632
                                     GIMP_HANDLE_ANCHOR_NORTH_EAST);
633
          gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
634 635 636
                                     x, y + h,
                                     GIMP_TOOL_HANDLE_SIZE_SMALL,
                                     GIMP_TOOL_HANDLE_SIZE_SMALL,
637
                                     GIMP_HANDLE_ANCHOR_SOUTH_WEST);
638
          gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
639 640 641
                                     x + w, y + h,
                                     GIMP_TOOL_HANDLE_SIZE_SMALL,
                                     GIMP_TOOL_HANDLE_SIZE_SMALL,
642
                                     GIMP_HANDLE_ANCHOR_SOUTH_EAST);
643 644 645
        }
      else if (GIMP_IS_GUIDE (list->data))
        {
646
          GimpGuide *guide = list->data;
647
          GimpImage *image = gimp_display_get_image (GIMP_TOOL (draw_tool)->display);
648 649 650 651 652 653 654 655
          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);
656
              gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
657 658 659
                                         x, h,
                                         GIMP_TOOL_HANDLE_SIZE_SMALL,
                                         GIMP_TOOL_HANDLE_SIZE_SMALL,
660
                                         GIMP_HANDLE_ANCHOR_SOUTH);
661
              gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
662 663 664
                                         x, 0,
                                         GIMP_TOOL_HANDLE_SIZE_SMALL,
                                         GIMP_TOOL_HANDLE_SIZE_SMALL,
665
                                         GIMP_HANDLE_ANCHOR_NORTH);
666 667 668 669 670
              break;

            case GIMP_ORIENTATION_HORIZONTAL:
              y = gimp_guide_get_position (guide);
              w = gimp_image_get_width (image);
671
              gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
672 673 674
                                         w, y,
                                         GIMP_TOOL_HANDLE_SIZE_SMALL,
                                         GIMP_TOOL_HANDLE_SIZE_SMALL,
675
                                         GIMP_HANDLE_ANCHOR_EAST);
676
              gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
677 678 679
                                         0, y,
                                         GIMP_TOOL_HANDLE_SIZE_SMALL,
                                         GIMP_TOOL_HANDLE_SIZE_SMALL,
680
                                         GIMP_HANDLE_ANCHOR_WEST);
681 682 683 684 685 686
              break;

            default:
              break;
            }
        }
687 688 689 690
    }
}

static void
691 692
gimp_align_tool_align (GimpAlignTool     *align_tool,
                       GimpAlignmentType  align_type)
693
{
694 695 696 697 698 699 700 701 702
  GimpAlignOptions *options = GIMP_ALIGN_TOOL_GET_OPTIONS (align_tool);
  GimpImage        *image;
  GObject          *reference_object = NULL;
  GList            *list;
  gint              offset = 0;

  /* if nothing is selected, just return */
  if (! align_tool->selected_objects)
    return;
703

704
  image  = gimp_display_get_image (GIMP_TOOL (align_tool)->display);
705

706
  switch (align_type)
707 708 709 710 711 712 713 714 715
    {
    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;
716

717 718 719
    case GIMP_ARRANGE_LEFT:
    case GIMP_ARRANGE_HCENTER:
    case GIMP_ARRANGE_RIGHT:
720 721 722 723
    case GIMP_ARRANGE_HFILL:
      offset = options->offset_x;
      break;

724 725 726
    case GIMP_ARRANGE_TOP:
    case GIMP_ARRANGE_VCENTER:
    case GIMP_ARRANGE_BOTTOM:
727
    case GIMP_ARRANGE_VFILL:
728
      offset = options->offset_y;
729 730
      break;
    }
731

732
  /* if only one object is selected, use the image as reference
733 734
   * if multiple objects are selected, use the first one as reference if
   * "set_reference" is TRUE, otherwise use NULL.
735
   */
736 737 738

  list = align_tool->selected_objects;

739
  switch (options->align_reference)
740
    {
741
    case GIMP_ALIGN_REFERENCE_IMAGE:
742
      reference_object = G_OBJECT (image);
743 744 745
      break;

    case GIMP_ALIGN_REFERENCE_FIRST:
746
      if (g_list_length (list) == 1)
747
        {
748
          reference_object = G_OBJECT (image);
749 750 751
        }
      else
        {
752 753
          if (align_tool->set_reference)
            {
754 755
              reference_object = G_OBJECT (list->data);
              list = g_list_next (list);
756 757 758 759 760 761 762 763 764
            }
          else
            {
              reference_object = NULL;
            }
        }
      break;

    case GIMP_ALIGN_REFERENCE_SELECTION:
765
      reference_object = G_OBJECT (gimp_image_get_mask (image));
766 767 768
      break;

    case GIMP_ALIGN_REFERENCE_ACTIVE_LAYER:
769
      reference_object = G_OBJECT (gimp_image_get_active_layer (image));
770 771 772
      break;

    case GIMP_ALIGN_REFERENCE_ACTIVE_CHANNEL:
773
      reference_object = G_OBJECT (gimp_image_get_active_channel (image));
774 775 776
      break;

    case GIMP_ALIGN_REFERENCE_ACTIVE_PATH:
777
      reference_object = G_OBJECT (gimp_image_get_active_vectors (image));
778
      break;
779
    }
780

781 782 783
  if (! reference_object)
    return;

784
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (align_tool));
785

786
  gimp_image_arrange_objects (image, list,
787
                              align_type,
788
                              reference_object,
789
                              align_type,
790
                              offset);
791 792

  gimp_draw_tool_resume (GIMP_DRAW_TOOL (align_tool));
793 794

  gimp_image_flush (image);
795 796
}

797
static void
798 799
gimp_align_tool_object_removed (GObject       *object,
                                GimpAlignTool *align_tool)
800 801 802
{
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (align_tool));

803 804
  if (align_tool->selected_objects)
    g_signal_handlers_disconnect_by_func (object,
805
                                          gimp_align_tool_object_removed,
806
                                          align_tool);
807

808 809
  align_tool->selected_objects = g_list_remove (align_tool->selected_objects,
                                                object);
810 811 812 813 814

  gimp_draw_tool_resume (GIMP_DRAW_TOOL (align_tool));
}

static void
815
gimp_align_tool_clear_selected (GimpAlignTool *align_tool)
816
{
817
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (align_tool));
818

819
  while (align_tool->selected_objects)
820 821
    gimp_align_tool_object_removed (align_tool->selected_objects->data,
                                    align_tool);
822

823
  gimp_draw_tool_resume (GIMP_DRAW_TOOL (align_tool));
824
}