summaryrefslogtreecommitdiffstats
path: root/app/tools/gimpaligntool.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/tools/gimpaligntool.c')
-rw-r--r--app/tools/gimpaligntool.c824
1 files changed, 824 insertions, 0 deletions
diff --git a/app/tools/gimpaligntool.c b/app/tools/gimpaligntool.c
new file mode 100644
index 0000000..4698993
--- /dev/null
+++ b/app/tools/gimpaligntool.c
@@ -0,0 +1,824 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "libgimpmath/gimpmath.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-pick-item.h"
+#include "core/gimpimage-undo.h"
+#include "core/gimplayer.h"
+
+#include "vectors/gimpvectors.h"
+
+#include "widgets/gimphelp-ids.h"
+#include "widgets/gimpwidgets-utils.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"
+
+
+#define EPSILON 3 /* move distance after which we do a box-select */
+
+
+/* local function prototypes */
+
+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);
+
+
+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"),
+ _("Alignment Tool: Align or arrange layers and other objects"),
+ N_("_Align"), "Q",
+ NULL, GIMP_HELP_TOOL_ALIGN,
+ GIMP_ICON_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->constructed = gimp_align_tool_constructed;
+
+ 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->key_press = gimp_align_tool_key_press;
+ tool_class->oper_update = gimp_align_tool_oper_update;
+ 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);
+
+ gimp_tool_control_set_snap_to (tool->control, FALSE);
+ gimp_tool_control_set_precision (tool->control,
+ GIMP_CURSOR_PRECISION_PIXEL_BORDER);
+ gimp_tool_control_set_tool_cursor (tool->control, GIMP_TOOL_CURSOR_MOVE);
+
+ align_tool->function = ALIGN_TOOL_IDLE;
+}
+
+static void
+gimp_align_tool_constructed (GObject *object)
+{
+ GimpAlignTool *align_tool = GIMP_ALIGN_TOOL (object);
+ GimpAlignOptions *options;
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ options = GIMP_ALIGN_TOOL_GET_OPTIONS (align_tool);
+
+ g_signal_connect_object (options, "align-button-clicked",
+ G_CALLBACK (gimp_align_tool_align),
+ align_tool, G_CONNECT_SWAPPED);
+}
+
+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:
+ gimp_align_tool_clear_selected (align_tool);
+ break;
+
+ case GIMP_TOOL_ACTION_COMMIT:
+ break;
+ }
+
+ GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
+}
+
+static void
+gimp_align_tool_button_press (GimpTool *tool,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state,
+ GimpButtonPressType press_type,
+ 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)
+ gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
+
+ tool->display = display;
+
+ gimp_tool_control_activate (tool->control);
+
+ align_tool->x2 = align_tool->x1 = coords->x;
+ align_tool->y2 = align_tool->y1 = 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,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state,
+ GimpButtonReleaseType release_type,
+ GimpDisplay *display)
+{
+ GimpAlignTool *align_tool = GIMP_ALIGN_TOOL (tool);
+ GimpAlignOptions *options = GIMP_ALIGN_TOOL_GET_OPTIONS (tool);
+ GimpDisplayShell *shell = gimp_display_get_shell (display);
+ GObject *object = NULL;
+ GimpImage *image = gimp_display_get_image (display);
+ GdkModifierType extend_mask;
+ gint i;
+
+ extend_mask = gimp_get_extend_selection_mask ();
+
+ gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
+
+ gimp_tool_control_halt (tool->control);
+
+ if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
+ {
+ align_tool->x2 = align_tool->x1;
+ align_tool->y2 = align_tool->y1;
+
+ gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
+ return;
+ }
+
+ if (! (state & extend_mask)) /* start a new list */
+ {
+ gimp_align_tool_clear_selected (align_tool);
+ align_tool->set_reference = FALSE;
+ }
+
+ /* 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->x1,
+ coords->y - align_tool->y1) < EPSILON)
+ {
+ GimpVectors *vectors;
+ GimpGuide *guide;
+ GimpLayer *layer;
+ gint snap_distance = display->config->snap_distance;
+
+ if ((vectors = gimp_image_pick_vectors (image,
+ coords->x, coords->y,
+ FUNSCALEX (shell, snap_distance),
+ FUNSCALEY (shell, snap_distance))))
+ {
+ object = G_OBJECT (vectors);
+ }
+ else if (gimp_display_shell_get_show_guides (shell) &&
+ (guide = gimp_image_pick_guide (image,
+ coords->x, coords->y,
+ FUNSCALEX (shell, snap_distance),
+ FUNSCALEY (shell, snap_distance))))
+ {
+ object = G_OBJECT (guide);
+ }
+ else if ((layer = gimp_image_pick_layer_by_bounds (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 (gimp_align_tool_object_removed),
+ align_tool);
+
+ /* if an object has been selected using unmodified click,
+ * it should be used as the reference
+ */
+ if (! (state & extend_mask))
+ align_tool->set_reference = TRUE;
+ }
+ }
+ }
+ else /* FIXME: look for vectors too */
+ {
+ 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);
+ GList *all_layers;
+ GList *list;
+
+ all_layers = gimp_image_get_layer_list (image);
+
+ for (list = all_layers; 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_get_offset (GIMP_ITEM (layer), &x0, &y0);
+ x1 = x0 + gimp_item_get_width (GIMP_ITEM (layer));
+ y1 = y0 + gimp_item_get_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 (gimp_align_tool_object_removed),
+ align_tool);
+ }
+
+ g_list_free (all_layers);
+ }
+
+ for (i = 0; i < ALIGN_OPTIONS_N_BUTTONS; i++)
+ {
+ if (options->button[i])
+ gtk_widget_set_sensitive (options->button[i],
+ align_tool->selected_objects != NULL);
+ }
+
+ align_tool->x2 = align_tool->x1;
+ align_tool->y2 = align_tool->y1;
+
+ gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
+}
+
+static void
+gimp_align_tool_motion (GimpTool *tool,
+ const 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->x2 = coords->x;
+ align_tool->y2 = coords->y;
+
+ gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
+}
+
+static gboolean
+gimp_align_tool_key_press (GimpTool *tool,
+ GdkEventKey *kevent,
+ GimpDisplay *display)
+{
+ if (display == tool->display)
+ {
+ switch (kevent->keyval)
+ {
+ case GDK_KEY_Escape:
+ gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
+ return TRUE;
+
+ default:
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_align_tool_oper_update (GimpTool *tool,
+ const GimpCoords *coords,
+ GdkModifierType state,
+ gboolean proximity,
+ GimpDisplay *display)
+{
+ 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;
+ gboolean add;
+
+ add = ((state & gimp_get_extend_selection_mask ()) &&
+ align_tool->selected_objects);
+
+ if (gimp_image_pick_vectors (image,
+ coords->x, coords->y,
+ FUNSCALEX (shell, snap_distance),
+ FUNSCALEY (shell, snap_distance)))
+ {
+ if (add)
+ align_tool->function = ALIGN_TOOL_ADD_PATH;
+ else
+ align_tool->function = ALIGN_TOOL_PICK_PATH;
+ }
+ else if (gimp_display_shell_get_show_guides (shell) &&
+ gimp_image_pick_guide (image,
+ coords->x, coords->y,
+ FUNSCALEX (shell, snap_distance),
+ FUNSCALEY (shell, snap_distance)))
+ {
+ if (add)
+ align_tool->function = ALIGN_TOOL_ADD_GUIDE;
+ else
+ align_tool->function = ALIGN_TOOL_PICK_GUIDE;
+ }
+ else if (gimp_image_pick_layer_by_bounds (image, coords->x, coords->y))
+ {
+ if (add)
+ align_tool->function = ALIGN_TOOL_ADD_LAYER;
+ else
+ align_tool->function = ALIGN_TOOL_PICK_LAYER;
+ }
+ else
+ {
+ align_tool->function = ALIGN_TOOL_IDLE;
+ }
+
+ gimp_align_tool_status_update (tool, display, state, proximity);
+}
+
+static void
+gimp_align_tool_cursor_update (GimpTool *tool,
+ const GimpCoords *coords,
+ GdkModifierType state,
+ GimpDisplay *display)
+{
+ GimpAlignTool *align_tool = GIMP_ALIGN_TOOL (tool);
+ GimpToolCursorType tool_cursor = GIMP_TOOL_CURSOR_NONE;
+ GimpCursorModifier modifier = GIMP_CURSOR_MODIFIER_NONE;
+
+ /* always add '+' when Shift is pressed, even if nothing is selected */
+ if (state & gimp_get_extend_selection_mask ())
+ modifier = GIMP_CURSOR_MODIFIER_PLUS;
+
+ 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);
+ 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_status_update (GimpTool *tool,
+ GimpDisplay *display,
+ GdkModifierType state,
+ gboolean proximity)
+{
+ GimpAlignTool *align_tool = GIMP_ALIGN_TOOL (tool);
+ GdkModifierType extend_mask;
+
+ extend_mask = gimp_get_extend_selection_mask ();
+
+ gimp_tool_pop_status (tool, display);
+
+ if (proximity)
+ {
+ gchar *status = NULL;
+
+ if (! align_tool->selected_objects)
+ {
+ /* no need to suggest Shift if nothing is selected */
+ state |= extend_mask;
+ }
+
+ 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"),
+ extend_mask & ~state,
+ NULL, NULL, NULL);
+ break;
+
+ case ALIGN_TOOL_PICK_LAYER:
+ status = gimp_suggest_modifiers (_("Click to pick this layer as "
+ "first item"),
+ extend_mask & ~state,
+ NULL, NULL, NULL);
+ break;
+
+ case ALIGN_TOOL_ADD_LAYER:
+ status = g_strdup (_("Click to add this layer to the list"));
+ break;
+
+ case ALIGN_TOOL_PICK_GUIDE:
+ status = gimp_suggest_modifiers (_("Click to pick this guide as "
+ "first item"),
+ extend_mask & ~state,
+ NULL, NULL, NULL);
+ break;
+
+ case ALIGN_TOOL_ADD_GUIDE:
+ status = g_strdup (_("Click to add this guide to the list"));
+ break;
+
+ case ALIGN_TOOL_PICK_PATH:
+ status = gimp_suggest_modifiers (_("Click to pick this path as "
+ "first item"),
+ extend_mask & ~state,
+ NULL, NULL, NULL);
+ break;
+
+ case ALIGN_TOOL_ADD_PATH:
+ status = g_strdup (_("Click to add this path to the list"));
+ break;
+
+ case ALIGN_TOOL_DRAG_BOX:
+ break;
+ }
+
+ if (status)
+ {
+ gimp_tool_push_status (tool, display, "%s", status);
+ g_free (status);
+ }
+ }
+}
+
+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->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;
+
+ gimp_draw_tool_add_rectangle (draw_tool, FALSE, x, y, w, h);
+
+ for (list = align_tool->selected_objects;
+ list;
+ list = g_list_next (list))
+ {
+ if (GIMP_IS_ITEM (list->data))
+ {
+ GimpItem *item = list->data;
+ gint off_x, off_y;
+
+ gimp_item_bounds (item, &x, &y, &w, &h);
+
+ gimp_item_get_offset (item, &off_x, &off_y);
+ x += off_x;
+ y += off_y;
+
+ gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
+ x, y,
+ GIMP_TOOL_HANDLE_SIZE_SMALL,
+ GIMP_TOOL_HANDLE_SIZE_SMALL,
+ GIMP_HANDLE_ANCHOR_NORTH_WEST);
+ gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
+ x + w, y,
+ GIMP_TOOL_HANDLE_SIZE_SMALL,
+ GIMP_TOOL_HANDLE_SIZE_SMALL,
+ GIMP_HANDLE_ANCHOR_NORTH_EAST);
+ gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
+ x, y + h,
+ GIMP_TOOL_HANDLE_SIZE_SMALL,
+ GIMP_TOOL_HANDLE_SIZE_SMALL,
+ GIMP_HANDLE_ANCHOR_SOUTH_WEST);
+ gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
+ x + w, y + h,
+ GIMP_TOOL_HANDLE_SIZE_SMALL,
+ GIMP_TOOL_HANDLE_SIZE_SMALL,
+ GIMP_HANDLE_ANCHOR_SOUTH_EAST);
+ }
+ else if (GIMP_IS_GUIDE (list->data))
+ {
+ GimpGuide *guide = list->data;
+ GimpImage *image = gimp_display_get_image (GIMP_TOOL (draw_tool)->display);
+ 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_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
+ x, h,
+ GIMP_TOOL_HANDLE_SIZE_SMALL,
+ GIMP_TOOL_HANDLE_SIZE_SMALL,
+ GIMP_HANDLE_ANCHOR_SOUTH);
+ gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
+ x, 0,
+ GIMP_TOOL_HANDLE_SIZE_SMALL,
+ GIMP_TOOL_HANDLE_SIZE_SMALL,
+ GIMP_HANDLE_ANCHOR_NORTH);
+ break;
+
+ case GIMP_ORIENTATION_HORIZONTAL:
+ y = gimp_guide_get_position (guide);
+ w = gimp_image_get_width (image);
+ gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
+ w, y,
+ GIMP_TOOL_HANDLE_SIZE_SMALL,
+ GIMP_TOOL_HANDLE_SIZE_SMALL,
+ GIMP_HANDLE_ANCHOR_EAST);
+ gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE,
+ 0, y,
+ GIMP_TOOL_HANDLE_SIZE_SMALL,
+ GIMP_TOOL_HANDLE_SIZE_SMALL,
+ GIMP_HANDLE_ANCHOR_WEST);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
+
+static void
+gimp_align_tool_align (GimpAlignTool *align_tool,
+ GimpAlignmentType align_type)
+{
+ 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;
+
+ image = gimp_display_get_image (GIMP_TOOL (align_tool)->display);
+
+ switch (align_type)
+ {
+ 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_HFILL:
+ offset = options->offset_x;
+ break;
+
+ case GIMP_ARRANGE_TOP:
+ case GIMP_ARRANGE_VCENTER:
+ case GIMP_ARRANGE_BOTTOM:
+ case GIMP_ARRANGE_VFILL:
+ offset = options->offset_y;
+ break;
+ }
+
+ /* 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 (options->align_reference)
+ {
+ case GIMP_ALIGN_REFERENCE_IMAGE:
+ reference_object = G_OBJECT (image);
+ break;
+
+ case GIMP_ALIGN_REFERENCE_FIRST:
+ if (g_list_length (list) == 1)
+ {
+ reference_object = G_OBJECT (image);
+ }
+ else
+ {
+ if (align_tool->set_reference)
+ {
+ reference_object = G_OBJECT (list->data);
+ list = g_list_next (list);
+ }
+ else
+ {
+ reference_object = NULL;
+ }
+ }
+ break;
+
+ case GIMP_ALIGN_REFERENCE_SELECTION:
+ reference_object = G_OBJECT (gimp_image_get_mask (image));
+ break;
+
+ case GIMP_ALIGN_REFERENCE_ACTIVE_LAYER:
+ reference_object = G_OBJECT (gimp_image_get_active_layer (image));
+ break;
+
+ case GIMP_ALIGN_REFERENCE_ACTIVE_CHANNEL:
+ reference_object = G_OBJECT (gimp_image_get_active_channel (image));
+ break;
+
+ case GIMP_ALIGN_REFERENCE_ACTIVE_PATH:
+ reference_object = G_OBJECT (gimp_image_get_active_vectors (image));
+ break;
+ }
+
+ if (! reference_object)
+ return;
+
+ gimp_draw_tool_pause (GIMP_DRAW_TOOL (align_tool));
+
+ gimp_image_arrange_objects (image, list,
+ align_type,
+ reference_object,
+ align_type,
+ offset);
+
+ gimp_draw_tool_resume (GIMP_DRAW_TOOL (align_tool));
+
+ gimp_image_flush (image);
+}
+
+static void
+gimp_align_tool_object_removed (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,
+ gimp_align_tool_object_removed,
+ 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
+gimp_align_tool_clear_selected (GimpAlignTool *align_tool)
+{
+ gimp_draw_tool_pause (GIMP_DRAW_TOOL (align_tool));
+
+ while (align_tool->selected_objects)
+ gimp_align_tool_object_removed (align_tool->selected_objects->data,
+ align_tool);
+
+ gimp_draw_tool_resume (GIMP_DRAW_TOOL (align_tool));
+}