/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * 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 . */ #include "config.h" #include #include #include #include "libpikamath/pikamath.h" #include "libpikawidgets/pikawidgets.h" #include "tools-types.h" #include "config/pikadisplayconfig.h" #include "core/pika.h" #include "core/pikaguide.h" #include "core/pikaimage.h" #include "core/pikaimage-arrange.h" #include "core/pikaimage-pick-item.h" #include "core/pikaimage-undo.h" #include "core/pikalayer.h" #include "core/pikapickable.h" #include "core/pikapickable-auto-shrink.h" #include "vectors/pikavectors.h" #include "widgets/pikahelp-ids.h" #include "widgets/pikawidgets-utils.h" #include "display/pikacanvasitem.h" #include "display/pikadisplay.h" #include "display/pikadisplayshell.h" #include "display/pikadisplayshell-appearance.h" #include "pikaalignoptions.h" #include "pikaaligntool.h" #include "pikatoolcontrol.h" #include "pika-intl.h" #define EPSILON 3 /* move distance after which we do a box-select */ /* local function prototypes */ static void pika_align_tool_constructed (GObject *object); static void pika_align_tool_control (PikaTool *tool, PikaToolAction action, PikaDisplay *display); static void pika_align_tool_button_press (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonPressType press_type, PikaDisplay *display); static void pika_align_tool_button_release (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonReleaseType release_type, PikaDisplay *display); static void pika_align_tool_motion (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaDisplay *display); static gboolean pika_align_tool_key_press (PikaTool *tool, GdkEventKey *kevent, PikaDisplay *display); static void pika_align_tool_oper_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, gboolean proximity, PikaDisplay *display); static void pika_align_tool_status_update (PikaTool *tool, PikaDisplay *display, GdkModifierType state, gboolean proximity); static void pika_align_tool_cursor_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, PikaDisplay *display); const gchar * pika_align_tool_can_undo (PikaTool *tool, PikaDisplay *display); static gboolean pika_align_tool_undo (PikaTool *tool, PikaDisplay *display); static void pika_align_tool_draw (PikaDrawTool *draw_tool); static void pika_align_tool_redraw (PikaAlignTool *align_tool); static void pika_align_tool_align (PikaAlignTool *align_tool, PikaAlignmentType align_type); static gint pika_align_tool_undo_idle (gpointer data); static void pika_align_tool_display_changed (PikaContext *context, PikaDisplay *display, PikaAlignTool *align_tool); G_DEFINE_TYPE (PikaAlignTool, pika_align_tool, PIKA_TYPE_DRAW_TOOL) #define parent_class pika_align_tool_parent_class void pika_align_tool_register (PikaToolRegisterCallback callback, gpointer data) { (* callback) (PIKA_TYPE_ALIGN_TOOL, PIKA_TYPE_ALIGN_OPTIONS, pika_align_options_gui, 0, "pika-align-tool", _("Align and Distribute"), _("Alignment Tool: Align or arrange layers and other objects"), N_("_Align and Distribute"), "Q", NULL, PIKA_HELP_TOOL_ALIGN, PIKA_ICON_TOOL_ALIGN, data); } static void pika_align_tool_class_init (PikaAlignToolClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); PikaToolClass *tool_class = PIKA_TOOL_CLASS (klass); PikaDrawToolClass *draw_tool_class = PIKA_DRAW_TOOL_CLASS (klass); object_class->constructed = pika_align_tool_constructed; tool_class->control = pika_align_tool_control; tool_class->button_press = pika_align_tool_button_press; tool_class->button_release = pika_align_tool_button_release; tool_class->motion = pika_align_tool_motion; tool_class->key_press = pika_align_tool_key_press; tool_class->oper_update = pika_align_tool_oper_update; tool_class->cursor_update = pika_align_tool_cursor_update; tool_class->can_undo = pika_align_tool_can_undo; tool_class->undo = pika_align_tool_undo; draw_tool_class->draw = pika_align_tool_draw; } static void pika_align_tool_init (PikaAlignTool *align_tool) { PikaTool *tool = PIKA_TOOL (align_tool); pika_tool_control_set_snap_to (tool->control, FALSE); pika_tool_control_set_precision (tool->control, PIKA_CURSOR_PRECISION_PIXEL_BORDER); pika_tool_control_set_tool_cursor (tool->control, PIKA_TOOL_CURSOR_MOVE); align_tool->function = ALIGN_TOOL_REF_IDLE; } static void pika_align_tool_constructed (GObject *object) { PikaAlignTool *align_tool = PIKA_ALIGN_TOOL (object); PikaAlignOptions *options; G_OBJECT_CLASS (parent_class)->constructed (object); options = PIKA_ALIGN_TOOL_GET_OPTIONS (align_tool); g_signal_connect_object (options, "align-button-clicked", G_CALLBACK (pika_align_tool_align), align_tool, G_CONNECT_SWAPPED); g_signal_connect_object (options, "notify::align-reference", G_CALLBACK (pika_align_tool_redraw), align_tool, G_CONNECT_SWAPPED); g_signal_connect_object (options, "notify::align-contents", G_CALLBACK (pika_align_tool_redraw), align_tool, G_CONNECT_SWAPPED); g_signal_connect_object (pika_get_user_context (PIKA_CONTEXT (options)->pika), "display-changed", G_CALLBACK (pika_align_tool_display_changed), align_tool, 0); } static void pika_align_tool_control (PikaTool *tool, PikaToolAction action, PikaDisplay *display) { switch (action) { case PIKA_TOOL_ACTION_PAUSE: case PIKA_TOOL_ACTION_RESUME: break; case PIKA_TOOL_ACTION_HALT: break; case PIKA_TOOL_ACTION_COMMIT: break; } PIKA_TOOL_CLASS (parent_class)->control (tool, action, display); } static void pika_align_tool_button_press (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonPressType press_type, PikaDisplay *display) { PikaAlignTool *align_tool = PIKA_ALIGN_TOOL (tool); pika_draw_tool_pause (PIKA_DRAW_TOOL (tool)); /* If the tool was being used in another image... reset it */ if (display != tool->display) pika_tool_control (tool, PIKA_TOOL_ACTION_HALT, display); tool->display = display; pika_tool_control_activate (tool->control); align_tool->x2 = align_tool->x1 = coords->x; align_tool->y2 = align_tool->y1 = coords->y; if (! pika_draw_tool_is_active (PIKA_DRAW_TOOL (tool))) pika_draw_tool_start (PIKA_DRAW_TOOL (tool), display); pika_draw_tool_resume (PIKA_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 pika_align_tool_button_release (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonReleaseType release_type, PikaDisplay *display) { PikaAlignTool *align_tool = PIKA_ALIGN_TOOL (tool); PikaAlignOptions *options = PIKA_ALIGN_TOOL_GET_OPTIONS (tool); PikaDisplayShell *shell = pika_display_get_shell (display); PikaImage *image = pika_display_get_image (display); GdkModifierType extend_mask; extend_mask = pika_get_extend_selection_mask (); pika_draw_tool_pause (PIKA_DRAW_TOOL (tool)); pika_tool_control_halt (tool->control); if (release_type == PIKA_BUTTON_RELEASE_CANCEL) { align_tool->x2 = align_tool->x1; align_tool->y2 = align_tool->y1; pika_draw_tool_resume (PIKA_DRAW_TOOL (tool)); return; } if (state & GDK_MOD1_MASK) { PikaGuide *guide = NULL; if (pika_display_shell_get_show_guides (shell)) { gint snap_distance = display->config->snap_distance; guide = pika_image_pick_guide (image, coords->x, coords->y, FUNSCALEX (shell, snap_distance), FUNSCALEY (shell, snap_distance)); } pika_align_options_pick_guide (options, guide, (gboolean) state & extend_mask); } else { GObject *object = NULL; /* Check if a layer is fully included in the rubber-band rectangle. * Don't verify for too small rectangles. */ /* FIXME: look for vectors too */ if (hypot (coords->x - align_tool->x1, coords->y - align_tool->y1) > EPSILON) { 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 = pika_image_get_layer_list (image); for (list = all_layers; list; list = g_list_next (list)) { PikaLayer *layer = list->data; gint x0, y0, x1, y1; if (! pika_item_get_visible (PIKA_ITEM (layer))) continue; pika_item_get_offset (PIKA_ITEM (layer), &x0, &y0); x1 = x0 + pika_item_get_width (PIKA_ITEM (layer)); y1 = y0 + pika_item_get_height (PIKA_ITEM (layer)); if (x0 < X0 || y0 < Y0 || x1 > X1 || y1 > Y1) continue; object = G_OBJECT (layer); break; } g_list_free (all_layers); } if (object == NULL) { PikaVectors *vectors; PikaGuide *guide; PikaLayer *layer; GObject *previously_picked; gint snap_distance = display->config->snap_distance; previously_picked = pika_align_options_get_reference (options, FALSE); if ((vectors = pika_image_pick_vectors (image, coords->x, coords->y, FUNSCALEX (shell, snap_distance), FUNSCALEY (shell, snap_distance)))) { object = G_OBJECT (vectors); } else if (pika_display_shell_get_show_guides (shell) && (guide = pika_image_pick_guide (image, coords->x, coords->y, FUNSCALEX (shell, snap_distance), FUNSCALEY (shell, snap_distance)))) { object = G_OBJECT (guide); } else if ((layer = pika_image_pick_layer (image, coords->x, coords->y, previously_picked && PIKA_IS_LAYER (previously_picked)? PIKA_LAYER (previously_picked) : NULL))) { object = G_OBJECT (layer); } } if (object) pika_align_options_pick_reference (options, object); } align_tool->x2 = align_tool->x1; align_tool->y2 = align_tool->y1; pika_draw_tool_resume (PIKA_DRAW_TOOL (tool)); } static void pika_align_tool_motion (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaDisplay *display) { PikaAlignTool *align_tool = PIKA_ALIGN_TOOL (tool); pika_draw_tool_pause (PIKA_DRAW_TOOL (tool)); align_tool->x2 = coords->x; align_tool->y2 = coords->y; pika_draw_tool_resume (PIKA_DRAW_TOOL (tool)); } static gboolean pika_align_tool_key_press (PikaTool *tool, GdkEventKey *kevent, PikaDisplay *display) { if (display == tool->display) { switch (kevent->keyval) { case GDK_KEY_Escape: pika_tool_control (tool, PIKA_TOOL_ACTION_HALT, display); return TRUE; default: break; } } return FALSE; } static void pika_align_tool_oper_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, gboolean proximity, PikaDisplay *display) { PikaAlignTool *align_tool = PIKA_ALIGN_TOOL (tool); PikaAlignOptions *options = PIKA_ALIGN_TOOL_GET_OPTIONS (align_tool); PikaDisplayShell *shell = pika_display_get_shell (display); PikaImage *image = pika_display_get_image (display); gint snap_distance = display->config->snap_distance; state &= pika_get_all_modifiers_mask (); align_tool->function = ALIGN_TOOL_NO_ACTION; if (pika_image_pick_vectors (image, coords->x, coords->y, FUNSCALEX (shell, snap_distance), FUNSCALEY (shell, snap_distance))) { if (options->align_reference == PIKA_ALIGN_REFERENCE_PICK) align_tool->function = ALIGN_TOOL_REF_PICK_PATH; } else if (pika_display_shell_get_show_guides (shell) && pika_image_pick_guide (image, coords->x, coords->y, FUNSCALEX (shell, snap_distance), FUNSCALEY (shell, snap_distance))) { if (state == (pika_get_extend_selection_mask () | GDK_MOD1_MASK)) align_tool->function = ALIGN_TOOL_ALIGN_ADD_GUIDE; else if (state == GDK_MOD1_MASK) align_tool->function = ALIGN_TOOL_ALIGN_PICK_GUIDE; else if (options->align_reference == PIKA_ALIGN_REFERENCE_PICK) align_tool->function = ALIGN_TOOL_REF_PICK_GUIDE; } else if (pika_image_pick_layer_by_bounds (image, coords->x, coords->y)) { if (options->align_reference == PIKA_ALIGN_REFERENCE_PICK) align_tool->function = ALIGN_TOOL_REF_PICK_LAYER; } else { if (state & GDK_MOD1_MASK) align_tool->function = ALIGN_TOOL_ALIGN_IDLE; else if (options->align_reference == PIKA_ALIGN_REFERENCE_PICK) align_tool->function = ALIGN_TOOL_REF_IDLE; } pika_align_tool_status_update (tool, display, state, proximity); if (! pika_draw_tool_is_active (PIKA_DRAW_TOOL (tool))) pika_draw_tool_start (PIKA_DRAW_TOOL (tool), display); } static void pika_align_tool_cursor_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, PikaDisplay *display) { PikaAlignTool *align_tool = PIKA_ALIGN_TOOL (tool); PikaToolCursorType tool_cursor = PIKA_TOOL_CURSOR_NONE; PikaCursorModifier modifier = PIKA_CURSOR_MODIFIER_NONE; switch (align_tool->function) { case ALIGN_TOOL_REF_IDLE: case ALIGN_TOOL_ALIGN_IDLE: tool_cursor = PIKA_TOOL_CURSOR_RECT_SELECT; break; case ALIGN_TOOL_REF_PICK_LAYER: tool_cursor = PIKA_TOOL_CURSOR_HAND; break; case ALIGN_TOOL_ALIGN_ADD_GUIDE: modifier = PIKA_CURSOR_MODIFIER_PLUS; case ALIGN_TOOL_REF_PICK_GUIDE: case ALIGN_TOOL_ALIGN_PICK_GUIDE: tool_cursor = PIKA_TOOL_CURSOR_MOVE; break; case ALIGN_TOOL_REF_PICK_PATH: tool_cursor = PIKA_TOOL_CURSOR_PATHS; break; case ALIGN_TOOL_REF_DRAG_BOX: case ALIGN_TOOL_NO_ACTION: break; } pika_tool_control_set_cursor (tool->control, PIKA_CURSOR_MOUSE); pika_tool_control_set_tool_cursor (tool->control, tool_cursor); pika_tool_control_set_cursor_modifier (tool->control, modifier); PIKA_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display); } const gchar * pika_align_tool_can_undo (PikaTool *tool, PikaDisplay *display) { return _("Arrange Objects"); } static gboolean pika_align_tool_undo (PikaTool *tool, PikaDisplay *display) { g_idle_add ((GSourceFunc) pika_align_tool_undo_idle, (gpointer) tool); return FALSE; } static void pika_align_tool_status_update (PikaTool *tool, PikaDisplay *display, GdkModifierType state, gboolean proximity) { PikaAlignTool *align_tool = PIKA_ALIGN_TOOL (tool); gchar *status = NULL; GdkModifierType extend_mask; extend_mask = pika_get_extend_selection_mask (); pika_tool_pop_status (tool, display); if (proximity) { switch (align_tool->function) { case ALIGN_TOOL_REF_IDLE: status = g_strdup (_("Click on a layer, path or guide, " "or Click-Drag to pick a reference")); break; case ALIGN_TOOL_REF_PICK_LAYER: status = g_strdup (_("Click to pick this layer as reference")); break; case ALIGN_TOOL_REF_PICK_GUIDE: status = pika_suggest_modifiers (_("Click to pick this guide as reference"), GDK_MOD1_MASK & ~state, NULL, NULL, NULL); break; case ALIGN_TOOL_REF_PICK_PATH: status = g_strdup (_("Click to pick this path as reference")); break; case ALIGN_TOOL_REF_DRAG_BOX: break; case ALIGN_TOOL_ALIGN_IDLE: status = g_strdup (_("Click on a guide to add it to objects to align, " "click anywhere else to unselect all guides")); break; case ALIGN_TOOL_ALIGN_PICK_GUIDE: status = pika_suggest_modifiers (_("Click to select this guide for alignment"), extend_mask & ~state, NULL, NULL, NULL); break; case ALIGN_TOOL_ALIGN_ADD_GUIDE: status = g_strdup (_("Click to add this guide to the list of objects to align")); break; case ALIGN_TOOL_NO_ACTION: break; } } if (status) { pika_tool_push_status (tool, display, "%s", status); g_free (status); } } static void pika_align_tool_draw (PikaDrawTool *draw_tool) { PikaAlignTool *align_tool = PIKA_ALIGN_TOOL (draw_tool); PikaAlignOptions *options = PIKA_ALIGN_TOOL_GET_OPTIONS (align_tool); GObject *reference; GList *objects; GList *iter; 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; if (w != 0 && h != 0) pika_draw_tool_add_rectangle (draw_tool, FALSE, x, y, w, h); /* Draw handles on the reference object. */ reference = pika_align_options_get_reference (options, FALSE); if (PIKA_IS_ITEM (reference)) { PikaItem *item = PIKA_ITEM (reference); gint off_x, off_y; pika_item_bounds (item, &x, &y, &w, &h); pika_item_get_offset (item, &off_x, &off_y); x += off_x; y += off_y; if (pika_align_options_align_contents (options)) { gint shrunk_x; gint shrunk_y; if (pika_pickable_auto_shrink (PIKA_PICKABLE (reference), 0, 0, pika_item_get_width (PIKA_ITEM (reference)), pika_item_get_height (PIKA_ITEM (reference)), &shrunk_x, &shrunk_y, &w, &h) == PIKA_AUTO_SHRINK_SHRINK) { x += shrunk_x; y += shrunk_y; } } pika_draw_tool_add_handle (draw_tool, PIKA_HANDLE_FILLED_SQUARE, x, y, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_HANDLE_ANCHOR_NORTH_WEST); pika_draw_tool_add_handle (draw_tool, PIKA_HANDLE_FILLED_SQUARE, x + w, y, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_HANDLE_ANCHOR_NORTH_EAST); pika_draw_tool_add_handle (draw_tool, PIKA_HANDLE_FILLED_SQUARE, x, y + h, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_HANDLE_ANCHOR_SOUTH_WEST); pika_draw_tool_add_handle (draw_tool, PIKA_HANDLE_FILLED_SQUARE, x + w, y + h, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_HANDLE_ANCHOR_SOUTH_EAST); } else if (PIKA_IS_GUIDE (reference)) { PikaGuide *guide = PIKA_GUIDE (reference); PikaImage *image = pika_display_get_image (draw_tool->display); gint x, y; gint w, h; switch (pika_guide_get_orientation (guide)) { case PIKA_ORIENTATION_VERTICAL: x = pika_guide_get_position (guide); h = pika_image_get_height (image); pika_draw_tool_add_handle (draw_tool, PIKA_HANDLE_FILLED_SQUARE, x, h, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_HANDLE_ANCHOR_SOUTH); pika_draw_tool_add_handle (draw_tool, PIKA_HANDLE_FILLED_SQUARE, x, 0, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_HANDLE_ANCHOR_NORTH); break; case PIKA_ORIENTATION_HORIZONTAL: y = pika_guide_get_position (guide); w = pika_image_get_width (image); pika_draw_tool_add_handle (draw_tool, PIKA_HANDLE_FILLED_SQUARE, w, y, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_HANDLE_ANCHOR_EAST); pika_draw_tool_add_handle (draw_tool, PIKA_HANDLE_FILLED_SQUARE, 0, y, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_HANDLE_ANCHOR_WEST); break; default: break; } } else if (PIKA_IS_IMAGE (reference)) { PikaImage *image = PIKA_IMAGE (reference); gint width = pika_image_get_width (image); gint height = pika_image_get_height (image); pika_draw_tool_add_handle (draw_tool, PIKA_HANDLE_FILLED_SQUARE, 0, 0, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_HANDLE_ANCHOR_NORTH_WEST); pika_draw_tool_add_handle (draw_tool, PIKA_HANDLE_FILLED_SQUARE, width, 0, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_HANDLE_ANCHOR_NORTH_EAST); pika_draw_tool_add_handle (draw_tool, PIKA_HANDLE_FILLED_SQUARE, 0, height, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_HANDLE_ANCHOR_SOUTH_WEST); pika_draw_tool_add_handle (draw_tool, PIKA_HANDLE_FILLED_SQUARE, width, height, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_TOOL_HANDLE_SIZE_SMALL, PIKA_HANDLE_ANCHOR_SOUTH_EAST); } /* Highlight picked guides, similarly to how they are highlighted in Move * tool. */ objects = pika_align_options_get_objects (options); for (iter = objects; iter; iter = iter->next) { if (PIKA_IS_GUIDE (iter->data)) { PikaGuide *guide = iter->data; PikaCanvasItem *item; PikaGuideStyle style; style = pika_guide_get_style (guide); item = pika_draw_tool_add_guide (draw_tool, pika_guide_get_orientation (guide), pika_guide_get_position (guide), style); pika_canvas_item_set_highlight (item, TRUE); } } } static void pika_align_tool_redraw (PikaAlignTool *align_tool) { PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (align_tool); pika_draw_tool_pause (draw_tool); if (! pika_draw_tool_is_active (draw_tool) && draw_tool->display) pika_draw_tool_start (draw_tool, draw_tool->display); pika_draw_tool_resume (draw_tool); } static void pika_align_tool_align (PikaAlignTool *align_tool, PikaAlignmentType align_type) { PikaAlignOptions *options = PIKA_ALIGN_TOOL_GET_OPTIONS (align_tool); PikaImage *image; GObject *reference_object = NULL; GList *objects; GList *list; gdouble align_x = 0.0; gdouble align_y = 0.0; /* if nothing is selected, just return */ objects = pika_align_options_get_objects (options); if (! objects) return; image = pika_context_get_image (pika_get_user_context (PIKA_CONTEXT (options)->pika)); list = objects; if (align_type < PIKA_ARRANGE_HFILL) { reference_object = pika_align_options_get_reference (options, TRUE); if (! reference_object) return; } pika_draw_tool_pause (PIKA_DRAW_TOOL (align_tool)); pika_align_options_get_pivot (options, &align_x, &align_y); pika_image_arrange_objects (image, list, align_x, align_y, reference_object, align_type, pika_align_options_align_contents (options)); pika_draw_tool_resume (PIKA_DRAW_TOOL (align_tool)); pika_image_flush (image); g_list_free (objects); } static gint pika_align_tool_undo_idle (gpointer data) { PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (data); PikaDisplay *display = draw_tool->display; /* This makes sure that we properly undraw then redraw the various handles and * highlighted guides after undoing. */ pika_draw_tool_stop (draw_tool); pika_draw_tool_start (draw_tool, display); return FALSE; /* remove idle */ } static void pika_align_tool_display_changed (PikaContext *context, PikaDisplay *display, PikaAlignTool *align_tool) { PikaTool *tool = PIKA_TOOL (align_tool); PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (align_tool); pika_draw_tool_pause (draw_tool); if (display != tool->display) pika_tool_control (tool, PIKA_TOOL_ACTION_HALT, display); tool->display = display; if (! pika_draw_tool_is_active (draw_tool) && display) pika_draw_tool_start (draw_tool, display); pika_draw_tool_resume (draw_tool); }