/* 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-2001 Spencer Kimball, Peter Mattis, and others * * 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 "tools-types.h" #include "core/pikaimage.h" #include "core/pikapickable.h" #include "display/pikacanvas.h" #include "display/pikacanvasarc.h" #include "display/pikacanvasboundary.h" #include "display/pikacanvasgroup.h" #include "display/pikacanvasguide.h" #include "display/pikacanvashandle.h" #include "display/pikacanvasitem-utils.h" #include "display/pikacanvasline.h" #include "display/pikacanvaspen.h" #include "display/pikacanvaspolygon.h" #include "display/pikacanvasrectangle.h" #include "display/pikacanvassamplepoint.h" #include "display/pikacanvastextcursor.h" #include "display/pikacanvastransformpreview.h" #include "display/pikacanvastext.h" #include "display/pikadisplay.h" #include "display/pikadisplayshell.h" #include "display/pikadisplayshell-items.h" #include "display/pikadisplayshell-transform.h" #include "display/pikatoolwidget.h" #include "pikadrawtool.h" #include "pikatoolcontrol.h" #define USE_TIMEOUT #define DRAW_FPS 120 #define DRAW_TIMEOUT (1000 /* milliseconds */ / (2 * DRAW_FPS)) #define MINIMUM_DRAW_INTERVAL (G_TIME_SPAN_SECOND / DRAW_FPS) static void pika_draw_tool_dispose (GObject *object); static gboolean pika_draw_tool_has_display (PikaTool *tool, PikaDisplay *display); static PikaDisplay * pika_draw_tool_has_image (PikaTool *tool, PikaImage *image); static void pika_draw_tool_control (PikaTool *tool, PikaToolAction action, PikaDisplay *display); static gboolean pika_draw_tool_key_press (PikaTool *tool, GdkEventKey *kevent, PikaDisplay *display); static gboolean pika_draw_tool_key_release (PikaTool *tool, GdkEventKey *kevent, PikaDisplay *display); static void pika_draw_tool_modifier_key (PikaTool *tool, GdkModifierType key, gboolean press, GdkModifierType state, PikaDisplay *display); static void pika_draw_tool_active_modifier_key (PikaTool *tool, GdkModifierType key, gboolean press, GdkModifierType state, PikaDisplay *display); static void pika_draw_tool_oper_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, gboolean proximity, PikaDisplay *display); static void pika_draw_tool_cursor_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, PikaDisplay *display); static PikaUIManager * pika_draw_tool_get_popup (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, PikaDisplay *display, const gchar **ui_path); static void pika_draw_tool_widget_status (PikaToolWidget *widget, const gchar *status, PikaTool *tool); static void pika_draw_tool_widget_status_coords (PikaToolWidget *widget, const gchar *title, gdouble x, const gchar *separator, gdouble y, const gchar *help, PikaTool *tool); static void pika_draw_tool_widget_message (PikaToolWidget *widget, const gchar *message, PikaTool *tool); static void pika_draw_tool_widget_snap_offsets (PikaToolWidget *widget, gint offset_x, gint offset_y, gint width, gint height, PikaTool *tool); static void pika_draw_tool_draw (PikaDrawTool *draw_tool); static void pika_draw_tool_undraw (PikaDrawTool *draw_tool); static void pika_draw_tool_real_draw (PikaDrawTool *draw_tool); G_DEFINE_TYPE (PikaDrawTool, pika_draw_tool, PIKA_TYPE_TOOL) #define parent_class pika_draw_tool_parent_class static void pika_draw_tool_class_init (PikaDrawToolClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); PikaToolClass *tool_class = PIKA_TOOL_CLASS (klass); object_class->dispose = pika_draw_tool_dispose; tool_class->has_display = pika_draw_tool_has_display; tool_class->has_image = pika_draw_tool_has_image; tool_class->control = pika_draw_tool_control; tool_class->key_press = pika_draw_tool_key_press; tool_class->key_release = pika_draw_tool_key_release; tool_class->modifier_key = pika_draw_tool_modifier_key; tool_class->active_modifier_key = pika_draw_tool_active_modifier_key; tool_class->oper_update = pika_draw_tool_oper_update; tool_class->cursor_update = pika_draw_tool_cursor_update; tool_class->get_popup = pika_draw_tool_get_popup; klass->draw = pika_draw_tool_real_draw; } static void pika_draw_tool_init (PikaDrawTool *draw_tool) { draw_tool->display = NULL; draw_tool->paused_count = 0; draw_tool->preview = NULL; draw_tool->item = NULL; } static void pika_draw_tool_dispose (GObject *object) { PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (object); if (draw_tool->draw_timeout) { g_source_remove (draw_tool->draw_timeout); draw_tool->draw_timeout = 0; } pika_draw_tool_set_widget (draw_tool, NULL); pika_draw_tool_set_default_status (draw_tool, NULL); G_OBJECT_CLASS (parent_class)->dispose (object); } static gboolean pika_draw_tool_has_display (PikaTool *tool, PikaDisplay *display) { PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (tool); return (display == draw_tool->display || PIKA_TOOL_CLASS (parent_class)->has_display (tool, display)); } static PikaDisplay * pika_draw_tool_has_image (PikaTool *tool, PikaImage *image) { PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (tool); PikaDisplay *display; display = PIKA_TOOL_CLASS (parent_class)->has_image (tool, image); if (! display && draw_tool->display) { if (image && pika_display_get_image (draw_tool->display) == image) display = draw_tool->display; /* NULL image means any display */ if (! image) display = draw_tool->display; } return display; } static void pika_draw_tool_control (PikaTool *tool, PikaToolAction action, PikaDisplay *display) { PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (tool); switch (action) { case PIKA_TOOL_ACTION_PAUSE: case PIKA_TOOL_ACTION_RESUME: break; case PIKA_TOOL_ACTION_HALT: if (pika_draw_tool_is_active (draw_tool)) pika_draw_tool_stop (draw_tool); pika_draw_tool_set_widget (draw_tool, NULL); break; case PIKA_TOOL_ACTION_COMMIT: break; } PIKA_TOOL_CLASS (parent_class)->control (tool, action, display); } static gboolean pika_draw_tool_key_press (PikaTool *tool, GdkEventKey *kevent, PikaDisplay *display) { PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (tool); if (draw_tool->widget && display == draw_tool->display) { return pika_tool_widget_key_press (draw_tool->widget, kevent); } return PIKA_TOOL_CLASS (parent_class)->key_press (tool, kevent, display); } static gboolean pika_draw_tool_key_release (PikaTool *tool, GdkEventKey *kevent, PikaDisplay *display) { PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (tool); if (draw_tool->widget && display == draw_tool->display) { return pika_tool_widget_key_release (draw_tool->widget, kevent); } return PIKA_TOOL_CLASS (parent_class)->key_release (tool, kevent, display); } static void pika_draw_tool_modifier_key (PikaTool *tool, GdkModifierType key, gboolean press, GdkModifierType state, PikaDisplay *display) { PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (tool); if (draw_tool->widget && display == draw_tool->display) { pika_tool_widget_hover_modifier (draw_tool->widget, key, press, state); } PIKA_TOOL_CLASS (parent_class)->modifier_key (tool, key, press, state, display); } static void pika_draw_tool_active_modifier_key (PikaTool *tool, GdkModifierType key, gboolean press, GdkModifierType state, PikaDisplay *display) { PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (tool); if (draw_tool->widget && display == draw_tool->display) { pika_tool_widget_motion_modifier (draw_tool->widget, key, press, state); } PIKA_TOOL_CLASS (parent_class)->active_modifier_key (tool, key, press, state, display); } static void pika_draw_tool_oper_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, gboolean proximity, PikaDisplay *display) { PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (tool); if (draw_tool->widget && display == draw_tool->display) { pika_tool_widget_hover (draw_tool->widget, coords, state, proximity); } else if (proximity && draw_tool->default_status) { pika_tool_replace_status (tool, display, "%s", draw_tool->default_status); } else if (! proximity) { pika_tool_pop_status (tool, display); } else { PIKA_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, proximity, display); } } static void pika_draw_tool_cursor_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, PikaDisplay *display) { PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (tool); if (draw_tool->widget && display == draw_tool->display) { PikaCursorType cursor; PikaToolCursorType tool_cursor; PikaCursorModifier modifier; cursor = pika_tool_control_get_cursor (tool->control); tool_cursor = pika_tool_control_get_tool_cursor (tool->control); modifier = pika_tool_control_get_cursor_modifier (tool->control); if (pika_tool_widget_get_cursor (draw_tool->widget, coords, state, &cursor, &tool_cursor, &modifier)) { pika_tool_set_cursor (tool, display, cursor, tool_cursor, modifier); return; } } PIKA_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display); } static PikaUIManager * pika_draw_tool_get_popup (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, PikaDisplay *display, const gchar **ui_path) { PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (tool); if (draw_tool->widget && display == draw_tool->display) { PikaUIManager *ui_manager; ui_manager = pika_tool_widget_get_popup (draw_tool->widget, coords, state, ui_path); if (ui_manager) return ui_manager; } return PIKA_TOOL_CLASS (parent_class)->get_popup (tool, coords, state, display, ui_path); } static void pika_draw_tool_widget_status (PikaToolWidget *widget, const gchar *status, PikaTool *tool) { PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (tool); if (pika_draw_tool_is_active (draw_tool)) { if (status) pika_tool_replace_status (tool, draw_tool->display, "%s", status); else pika_tool_pop_status (tool, draw_tool->display); } } static void pika_draw_tool_widget_status_coords (PikaToolWidget *widget, const gchar *title, gdouble x, const gchar *separator, gdouble y, const gchar *help, PikaTool *tool) { PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (tool); if (pika_draw_tool_is_active (draw_tool)) { pika_tool_pop_status (tool, draw_tool->display); pika_tool_push_status_coords (tool, draw_tool->display, pika_tool_control_get_precision ( tool->control), title, x, separator, y, help); } } static void pika_draw_tool_widget_message (PikaToolWidget *widget, const gchar *message, PikaTool *tool) { PikaDrawTool *draw_tool = PIKA_DRAW_TOOL (tool); if (pika_draw_tool_is_active (draw_tool)) pika_tool_message_literal (tool, draw_tool->display, message); } static void pika_draw_tool_widget_snap_offsets (PikaToolWidget *widget, gint offset_x, gint offset_y, gint width, gint height, PikaTool *tool) { pika_tool_control_set_snap_offsets (tool->control, offset_x, offset_y, width, height); } #ifdef USE_TIMEOUT static gboolean pika_draw_tool_draw_timeout (PikaDrawTool *draw_tool) { guint64 now = g_get_monotonic_time (); /* keep the timeout running if the last drawing just happened */ if ((now - draw_tool->last_draw_time) <= MINIMUM_DRAW_INTERVAL) return TRUE; draw_tool->draw_timeout = 0; pika_draw_tool_draw (draw_tool); return FALSE; } #endif static void pika_draw_tool_draw (PikaDrawTool *draw_tool) { guint64 now = g_get_monotonic_time (); if (draw_tool->display && draw_tool->paused_count == 0 && (! draw_tool->draw_timeout || (now - draw_tool->last_draw_time) > MINIMUM_DRAW_INTERVAL)) { PikaDisplayShell *shell = pika_display_get_shell (draw_tool->display); if (draw_tool->draw_timeout) { g_source_remove (draw_tool->draw_timeout); draw_tool->draw_timeout = 0; } pika_draw_tool_undraw (draw_tool); PIKA_DRAW_TOOL_GET_CLASS (draw_tool)->draw (draw_tool); if (draw_tool->group_stack) { g_warning ("%s: draw_tool->group_stack not empty after calling " "PikaDrawTool::draw() of %s", G_STRFUNC, g_type_name (G_TYPE_FROM_INSTANCE (draw_tool))); while (draw_tool->group_stack) pika_draw_tool_pop_group (draw_tool); } if (draw_tool->preview) pika_display_shell_add_preview_item (shell, draw_tool->preview); if (draw_tool->item) pika_display_shell_add_tool_item (shell, draw_tool->item); draw_tool->last_draw_time = g_get_monotonic_time (); #if 0 g_printerr ("drawing tool stuff took %f seconds\n", (draw_tool->last_draw_time - now) / 1000000.0); #endif } } static void pika_draw_tool_undraw (PikaDrawTool *draw_tool) { if (draw_tool->display) { PikaDisplayShell *shell = pika_display_get_shell (draw_tool->display); if (draw_tool->preview) { pika_display_shell_remove_preview_item (shell, draw_tool->preview); g_clear_object (&draw_tool->preview); } if (draw_tool->item) { pika_display_shell_remove_tool_item (shell, draw_tool->item); g_clear_object (&draw_tool->item); } } } static void pika_draw_tool_real_draw (PikaDrawTool *draw_tool) { if (draw_tool->widget) { PikaCanvasItem *item = pika_tool_widget_get_item (draw_tool->widget); pika_draw_tool_add_item (draw_tool, item); } } void pika_draw_tool_start (PikaDrawTool *draw_tool, PikaDisplay *display) { g_return_if_fail (PIKA_IS_DRAW_TOOL (draw_tool)); g_return_if_fail (PIKA_IS_DISPLAY (display)); g_return_if_fail (pika_draw_tool_is_active (draw_tool) == FALSE); draw_tool->display = display; pika_draw_tool_draw (draw_tool); } void pika_draw_tool_stop (PikaDrawTool *draw_tool) { g_return_if_fail (PIKA_IS_DRAW_TOOL (draw_tool)); g_return_if_fail (pika_draw_tool_is_active (draw_tool) == TRUE); pika_draw_tool_undraw (draw_tool); if (draw_tool->draw_timeout) { g_source_remove (draw_tool->draw_timeout); draw_tool->draw_timeout = 0; } draw_tool->last_draw_time = 0; draw_tool->display = NULL; } gboolean pika_draw_tool_is_active (PikaDrawTool *draw_tool) { g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), FALSE); return draw_tool->display != NULL; } void pika_draw_tool_pause (PikaDrawTool *draw_tool) { g_return_if_fail (PIKA_IS_DRAW_TOOL (draw_tool)); draw_tool->paused_count++; if (draw_tool->draw_timeout) { g_source_remove (draw_tool->draw_timeout); draw_tool->draw_timeout = 0; } } void pika_draw_tool_resume (PikaDrawTool *draw_tool) { g_return_if_fail (PIKA_IS_DRAW_TOOL (draw_tool)); g_return_if_fail (draw_tool->paused_count > 0); draw_tool->paused_count--; if (draw_tool->paused_count == 0) { #ifdef USE_TIMEOUT /* Don't install the timeout if the draw tool isn't active, so * suspend()/resume() can always be called, and have no side * effect on an inactive tool. See bug #687851. */ if (pika_draw_tool_is_active (draw_tool) && ! draw_tool->draw_timeout) { draw_tool->draw_timeout = gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE, DRAW_TIMEOUT, (GSourceFunc) pika_draw_tool_draw_timeout, draw_tool, NULL); } #endif /* call draw() anyway, it will do nothing if the timeout is * running, but will additionally check the drawing times to * ensure the minimum framerate */ pika_draw_tool_draw (draw_tool); } } /** * pika_draw_tool_calc_distance: * @draw_tool: a #PikaDrawTool * @display: a #PikaDisplay * @x1: start point X in image coordinates * @y1: start point Y in image coordinates * @x2: end point X in image coordinates * @y2: end point Y in image coordinates * * If you just need to compare distances, consider to use * pika_draw_tool_calc_distance_square() instead. * * Returns: the distance between the given points in display coordinates **/ gdouble pika_draw_tool_calc_distance (PikaDrawTool *draw_tool, PikaDisplay *display, gdouble x1, gdouble y1, gdouble x2, gdouble y2) { return sqrt (pika_draw_tool_calc_distance_square (draw_tool, display, x1, y1, x2, y2)); } /** * pika_draw_tool_calc_distance_square: * @draw_tool: a #PikaDrawTool * @display: a #PikaDisplay * @x1: start point X in image coordinates * @y1: start point Y in image coordinates * @x2: end point X in image coordinates * @y2: end point Y in image coordinates * * This function is more effective than pika_draw_tool_calc_distance() * as it doesn't perform a sqrt(). Use this if you just need to compare * distances. * * Returns: the square of the distance between the given points in * display coordinates **/ gdouble pika_draw_tool_calc_distance_square (PikaDrawTool *draw_tool, PikaDisplay *display, gdouble x1, gdouble y1, gdouble x2, gdouble y2) { PikaDisplayShell *shell; gdouble tx1, ty1; gdouble tx2, ty2; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), 0.0); g_return_val_if_fail (PIKA_IS_DISPLAY (display), 0.0); shell = pika_display_get_shell (display); pika_display_shell_transform_xy_f (shell, x1, y1, &tx1, &ty1); pika_display_shell_transform_xy_f (shell, x2, y2, &tx2, &ty2); return SQR (tx2 - tx1) + SQR (ty2 - ty1); } void pika_draw_tool_set_widget (PikaDrawTool *draw_tool, PikaToolWidget *widget) { g_return_if_fail (PIKA_IS_DRAW_TOOL (draw_tool)); g_return_if_fail (widget == NULL || PIKA_IS_TOOL_WIDGET (widget)); if (widget == draw_tool->widget) return; if (draw_tool->widget) { pika_tool_widget_set_focus (draw_tool->widget, FALSE); g_signal_handlers_disconnect_by_func (draw_tool->widget, pika_draw_tool_widget_status, draw_tool); g_signal_handlers_disconnect_by_func (draw_tool->widget, pika_draw_tool_widget_status_coords, draw_tool); g_signal_handlers_disconnect_by_func (draw_tool->widget, pika_draw_tool_widget_message, draw_tool); g_signal_handlers_disconnect_by_func (draw_tool->widget, pika_draw_tool_widget_snap_offsets, draw_tool); if (pika_draw_tool_is_active (draw_tool)) { PikaCanvasItem *item = pika_tool_widget_get_item (draw_tool->widget); pika_draw_tool_remove_item (draw_tool, item); } g_object_unref (draw_tool->widget); } draw_tool->widget = widget; if (draw_tool->widget) { g_object_ref (draw_tool->widget); if (pika_draw_tool_is_active (draw_tool)) { PikaCanvasItem *item = pika_tool_widget_get_item (draw_tool->widget); pika_draw_tool_add_item (draw_tool, item); } g_signal_connect (draw_tool->widget, "status", G_CALLBACK (pika_draw_tool_widget_status), draw_tool); g_signal_connect (draw_tool->widget, "status-coords", G_CALLBACK (pika_draw_tool_widget_status_coords), draw_tool); g_signal_connect (draw_tool->widget, "message", G_CALLBACK (pika_draw_tool_widget_message), draw_tool); g_signal_connect (draw_tool->widget, "snap-offsets", G_CALLBACK (pika_draw_tool_widget_snap_offsets), draw_tool); pika_tool_widget_set_focus (draw_tool->widget, TRUE); } } void pika_draw_tool_set_default_status (PikaDrawTool *draw_tool, const gchar *status) { g_return_if_fail (PIKA_IS_DRAW_TOOL (draw_tool)); if (draw_tool->default_status) g_free (draw_tool->default_status); draw_tool->default_status = g_strdup (status); } void pika_draw_tool_add_preview (PikaDrawTool *draw_tool, PikaCanvasItem *item) { g_return_if_fail (PIKA_IS_DRAW_TOOL (draw_tool)); g_return_if_fail (PIKA_IS_CANVAS_ITEM (item)); if (! draw_tool->preview) draw_tool->preview = pika_canvas_group_new (pika_display_get_shell (draw_tool->display)); pika_canvas_group_add_item (PIKA_CANVAS_GROUP (draw_tool->preview), item); } void pika_draw_tool_remove_preview (PikaDrawTool *draw_tool, PikaCanvasItem *item) { g_return_if_fail (PIKA_IS_DRAW_TOOL (draw_tool)); g_return_if_fail (PIKA_IS_CANVAS_ITEM (item)); g_return_if_fail (draw_tool->preview != NULL); pika_canvas_group_remove_item (PIKA_CANVAS_GROUP (draw_tool->preview), item); } void pika_draw_tool_add_item (PikaDrawTool *draw_tool, PikaCanvasItem *item) { PikaCanvasGroup *group; g_return_if_fail (PIKA_IS_DRAW_TOOL (draw_tool)); g_return_if_fail (PIKA_IS_CANVAS_ITEM (item)); if (! draw_tool->item) draw_tool->item = pika_canvas_group_new (pika_display_get_shell (draw_tool->display)); group = PIKA_CANVAS_GROUP (draw_tool->item); if (draw_tool->group_stack) group = draw_tool->group_stack->data; pika_canvas_group_add_item (group, item); } void pika_draw_tool_remove_item (PikaDrawTool *draw_tool, PikaCanvasItem *item) { g_return_if_fail (PIKA_IS_DRAW_TOOL (draw_tool)); g_return_if_fail (PIKA_IS_CANVAS_ITEM (item)); g_return_if_fail (draw_tool->item != NULL); pika_canvas_group_remove_item (PIKA_CANVAS_GROUP (draw_tool->item), item); } PikaCanvasGroup * pika_draw_tool_add_stroke_group (PikaDrawTool *draw_tool) { PikaCanvasItem *item; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), NULL); item = pika_canvas_group_new (pika_display_get_shell (draw_tool->display)); pika_canvas_group_set_group_stroking (PIKA_CANVAS_GROUP (item), TRUE); pika_draw_tool_add_item (draw_tool, item); g_object_unref (item); return PIKA_CANVAS_GROUP (item); } PikaCanvasGroup * pika_draw_tool_add_fill_group (PikaDrawTool *draw_tool) { PikaCanvasItem *item; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), NULL); item = pika_canvas_group_new (pika_display_get_shell (draw_tool->display)); pika_canvas_group_set_group_filling (PIKA_CANVAS_GROUP (item), TRUE); pika_draw_tool_add_item (draw_tool, item); g_object_unref (item); return PIKA_CANVAS_GROUP (item); } void pika_draw_tool_push_group (PikaDrawTool *draw_tool, PikaCanvasGroup *group) { g_return_if_fail (PIKA_IS_DRAW_TOOL (draw_tool)); g_return_if_fail (PIKA_IS_CANVAS_GROUP (group)); draw_tool->group_stack = g_list_prepend (draw_tool->group_stack, group); } void pika_draw_tool_pop_group (PikaDrawTool *draw_tool) { g_return_if_fail (PIKA_IS_DRAW_TOOL (draw_tool)); g_return_if_fail (draw_tool->group_stack != NULL); draw_tool->group_stack = g_list_remove (draw_tool->group_stack, draw_tool->group_stack->data); } /** * pika_draw_tool_add_line: * @draw_tool: the #PikaDrawTool * @x1: start point X in image coordinates * @y1: start point Y in image coordinates * @x2: end point X in image coordinates * @y2: end point Y in image coordinates * * This function takes image space coordinates and transforms them to * screen window coordinates, then draws a line between the resulting * coordinates. **/ PikaCanvasItem * pika_draw_tool_add_line (PikaDrawTool *draw_tool, gdouble x1, gdouble y1, gdouble x2, gdouble y2) { PikaCanvasItem *item; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), NULL); item = pika_canvas_line_new (pika_display_get_shell (draw_tool->display), x1, y1, x2, y2); pika_draw_tool_add_item (draw_tool, item); g_object_unref (item); return item; } /** * pika_draw_tool_add_guide: * @draw_tool: the #PikaDrawTool * @orientation: the orientation of the guide line * @position: the position of the guide line in image coordinates * * This function draws a guide line across the canvas. **/ PikaCanvasItem * pika_draw_tool_add_guide (PikaDrawTool *draw_tool, PikaOrientationType orientation, gint position, PikaGuideStyle style) { PikaCanvasItem *item; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), NULL); item = pika_canvas_guide_new (pika_display_get_shell (draw_tool->display), orientation, position, style); pika_draw_tool_add_item (draw_tool, item); g_object_unref (item); return item; } /** * pika_draw_tool_add_crosshair: * @draw_tool: the #PikaDrawTool * @position_x: the position of the vertical guide line in image coordinates * @position_y: the position of the horizontal guide line in image coordinates * * This function draws two crossing guide lines across the canvas. **/ PikaCanvasItem * pika_draw_tool_add_crosshair (PikaDrawTool *draw_tool, gint position_x, gint position_y) { PikaCanvasGroup *group; group = pika_draw_tool_add_stroke_group (draw_tool); pika_draw_tool_push_group (draw_tool, group); pika_draw_tool_add_guide (draw_tool, PIKA_ORIENTATION_VERTICAL, position_x, PIKA_GUIDE_STYLE_NONE); pika_draw_tool_add_guide (draw_tool, PIKA_ORIENTATION_HORIZONTAL, position_y, PIKA_GUIDE_STYLE_NONE); pika_draw_tool_pop_group (draw_tool); return PIKA_CANVAS_ITEM (group); } /** * pika_draw_tool_add_sample_point: * @draw_tool: the #PikaDrawTool * @x: X position of the sample point * @y: Y position of the sample point * @index: Index of the sample point * * This function draws a sample point **/ PikaCanvasItem * pika_draw_tool_add_sample_point (PikaDrawTool *draw_tool, gint x, gint y, gint index) { PikaCanvasItem *item; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), NULL); item = pika_canvas_sample_point_new (pika_display_get_shell (draw_tool->display), x, y, index, TRUE); pika_draw_tool_add_item (draw_tool, item); g_object_unref (item); return item; } /** * pika_draw_tool_add_rectangle: * @draw_tool: the #PikaDrawTool * @filled: whether to fill the rectangle * @x: horizontal image coordinate * @y: vertical image coordinate * @width: width in image coordinates * @height: height in image coordinates * * This function takes image space coordinates and transforms them to * screen window coordinates, then draws the resulting rectangle. **/ PikaCanvasItem * pika_draw_tool_add_rectangle (PikaDrawTool *draw_tool, gboolean filled, gdouble x, gdouble y, gdouble width, gdouble height) { PikaCanvasItem *item; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), NULL); item = pika_canvas_rectangle_new (pika_display_get_shell (draw_tool->display), x, y, width, height, filled); pika_draw_tool_add_item (draw_tool, item); g_object_unref (item); return item; } PikaCanvasItem * pika_draw_tool_add_arc (PikaDrawTool *draw_tool, gboolean filled, gdouble x, gdouble y, gdouble width, gdouble height, gdouble start_angle, gdouble slice_angle) { PikaCanvasItem *item; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), NULL); item = pika_canvas_arc_new (pika_display_get_shell (draw_tool->display), x + width / 2.0, y + height / 2.0, width / 2.0, height / 2.0, start_angle, slice_angle, filled); pika_draw_tool_add_item (draw_tool, item); g_object_unref (item); return item; } PikaCanvasItem * pika_draw_tool_add_handle (PikaDrawTool *draw_tool, PikaHandleType type, gdouble x, gdouble y, gint width, gint height, PikaHandleAnchor anchor) { PikaCanvasItem *item; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), NULL); item = pika_canvas_handle_new (pika_display_get_shell (draw_tool->display), type, anchor, x, y, width, height); pika_draw_tool_add_item (draw_tool, item); g_object_unref (item); return item; } PikaCanvasItem * pika_draw_tool_add_lines (PikaDrawTool *draw_tool, const PikaVector2 *points, gint n_points, PikaMatrix3 *transform, gboolean filled) { PikaCanvasItem *item; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), NULL); if (points == NULL || n_points < 2) return NULL; item = pika_canvas_polygon_new (pika_display_get_shell (draw_tool->display), points, n_points, transform, filled); pika_draw_tool_add_item (draw_tool, item); g_object_unref (item); return item; } PikaCanvasItem * pika_draw_tool_add_strokes (PikaDrawTool *draw_tool, const PikaCoords *points, gint n_points, PikaMatrix3 *transform, gboolean filled) { PikaCanvasItem *item; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), NULL); if (points == NULL || n_points < 2) return NULL; item = pika_canvas_polygon_new_from_coords (pika_display_get_shell (draw_tool->display), points, n_points, transform, filled); pika_draw_tool_add_item (draw_tool, item); g_object_unref (item); return item; } PikaCanvasItem * pika_draw_tool_add_pen (PikaDrawTool *draw_tool, const PikaVector2 *points, gint n_points, PikaContext *context, PikaActiveColor color, gint width) { PikaCanvasItem *item; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), NULL); if (points == NULL || n_points < 2) return NULL; item = pika_canvas_pen_new (pika_display_get_shell (draw_tool->display), points, n_points, context, color, width); pika_draw_tool_add_item (draw_tool, item); g_object_unref (item); return item; } /** * pika_draw_tool_add_boundary: * @draw_tool: a #PikaDrawTool * @bound_segs: the sorted brush outline * @n_bound_segs: the number of segments in @bound_segs * @matrix: transform matrix for the boundary * @offset_x: x offset * @offset_y: y offset * * Draw the boundary of the brush that @draw_tool uses. The boundary * should be sorted with sort_boundary(), and @n_bound_segs should * include the sentinel segments inserted by sort_boundary() that * indicate the end of connected segment sequences (groups) . */ PikaCanvasItem * pika_draw_tool_add_boundary (PikaDrawTool *draw_tool, const PikaBoundSeg *bound_segs, gint n_bound_segs, PikaMatrix3 *transform, gdouble offset_x, gdouble offset_y) { PikaCanvasItem *item; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), NULL); g_return_val_if_fail (n_bound_segs > 0, NULL); g_return_val_if_fail (bound_segs != NULL, NULL); item = pika_canvas_boundary_new (pika_display_get_shell (draw_tool->display), bound_segs, n_bound_segs, transform, offset_x, offset_y); pika_draw_tool_add_item (draw_tool, item); g_object_unref (item); return item; } PikaCanvasItem * pika_draw_tool_add_text_cursor (PikaDrawTool *draw_tool, PangoRectangle *cursor, gboolean overwrite, PikaTextDirection direction) { PikaCanvasItem *item; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), NULL); item = pika_canvas_text_cursor_new (pika_display_get_shell (draw_tool->display), cursor, overwrite, direction); pika_draw_tool_add_item (draw_tool, item); g_object_unref (item); return item; } PikaCanvasItem * pika_draw_tool_add_transform_preview (PikaDrawTool *draw_tool, PikaPickable *pickable, const PikaMatrix3 *transform, gdouble x1, gdouble y1, gdouble x2, gdouble y2) { PikaCanvasItem *item; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), NULL); g_return_val_if_fail (PIKA_IS_PICKABLE (pickable), NULL); g_return_val_if_fail (transform != NULL, NULL); item = pika_canvas_transform_preview_new (pika_display_get_shell (draw_tool->display), pickable, transform, x1, y1, x2, y2); pika_draw_tool_add_preview (draw_tool, item); g_object_unref (item); return item; } PikaCanvasItem * pika_draw_tool_add_text (PikaDrawTool *draw_tool, gdouble x, gdouble y, gdouble font_size, gchar *text) { PikaCanvasItem *item; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), NULL); item = pika_canvas_text_new (pika_display_get_shell (draw_tool->display), x, y, font_size, text); pika_draw_tool_add_item (draw_tool, item); g_object_unref (item); return item; } gboolean pika_draw_tool_on_handle (PikaDrawTool *draw_tool, PikaDisplay *display, gdouble x, gdouble y, PikaHandleType type, gdouble handle_x, gdouble handle_y, gint width, gint height, PikaHandleAnchor anchor) { PikaDisplayShell *shell; gdouble tx, ty; gdouble handle_tx, handle_ty; g_return_val_if_fail (PIKA_IS_DRAW_TOOL (draw_tool), FALSE); g_return_val_if_fail (PIKA_IS_DISPLAY (display), FALSE); shell = pika_display_get_shell (display); pika_display_shell_zoom_xy_f (shell, x, y, &tx, &ty); pika_display_shell_zoom_xy_f (shell, handle_x, handle_y, &handle_tx, &handle_ty); switch (type) { case PIKA_HANDLE_SQUARE: case PIKA_HANDLE_FILLED_SQUARE: case PIKA_HANDLE_CROSS: case PIKA_HANDLE_CROSSHAIR: pika_canvas_item_shift_to_north_west (anchor, handle_tx, handle_ty, width, height, &handle_tx, &handle_ty); return (tx == CLAMP (tx, handle_tx, handle_tx + width) && ty == CLAMP (ty, handle_ty, handle_ty + height)); case PIKA_HANDLE_CIRCLE: case PIKA_HANDLE_FILLED_CIRCLE: pika_canvas_item_shift_to_center (anchor, handle_tx, handle_ty, width, height, &handle_tx, &handle_ty); /* FIXME */ if (width != height) width = (width + height) / 2; width /= 2; return ((SQR (handle_tx - tx) + SQR (handle_ty - ty)) < SQR (width)); default: g_warning ("%s: invalid handle type %d", G_STRFUNC, type); break; } return FALSE; }