diff options
Diffstat (limited to 'app/display/gimptoolcompass.c')
-rw-r--r-- | app/display/gimptoolcompass.c | 1218 |
1 files changed, 1218 insertions, 0 deletions
diff --git a/app/display/gimptoolcompass.c b/app/display/gimptoolcompass.c new file mode 100644 index 0000000..3d9045b --- /dev/null +++ b/app/display/gimptoolcompass.c @@ -0,0 +1,1218 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolcompass.c + * Copyright (C) 2017 Michael Natterer <mitch@gimp.org> + * + * Measure tool + * Copyright (C) 1999-2003 Sven Neumann <sven@gimp.org> + * + * 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 "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-utils.h" +#include "core/gimpimage.h" +#include "core/gimpmarshal.h" + +#include "widgets/gimpwidgets-utils.h" + +#include "gimpcanvashandle.h" +#include "gimpcanvasline.h" +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-appearance.h" +#include "gimpdisplayshell-transform.h" +#include "gimpdisplayshell-utils.h" +#include "gimptoolcompass.h" + +#include "gimp-intl.h" + + +#define ARC_RADIUS 30 +#define ARC_GAP (ARC_RADIUS / 2) +#define EPSILON 1e-6 + + +/* possible measure functions */ +typedef enum +{ + CREATING, + ADDING, + MOVING, + MOVING_ALL, + GUIDING, + FINISHED +} CompassFunction; + +enum +{ + PROP_0, + PROP_ORIENTATION, + PROP_N_POINTS, + PROP_X1, + PROP_Y1, + PROP_X2, + PROP_Y2, + PROP_X3, + PROP_Y3, + PROP_PIXEL_ANGLE, + PROP_UNIT_ANGLE, + PROP_EFFECTIVE_ORIENTATION +}; + +enum +{ + CREATE_GUIDES, + LAST_SIGNAL +}; + +struct _GimpToolCompassPrivate +{ + GimpCompassOrientation orientation; + gint n_points; + gint x[3]; + gint y[3]; + + GimpVector2 radius1; + GimpVector2 radius2; + gdouble display_angle; + gdouble pixel_angle; + gdouble unit_angle; + GimpCompassOrientation effective_orientation; + + CompassFunction function; + gdouble mouse_x; + gdouble mouse_y; + gint last_x; + gint last_y; + gint point; + + GimpCanvasItem *line1; + GimpCanvasItem *line2; + GimpCanvasItem *arc; + GimpCanvasItem *arc_line; + GimpCanvasItem *handles[3]; +}; + + +/* local function prototypes */ + +static void gimp_tool_compass_constructed (GObject *object); +static void gimp_tool_compass_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_compass_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_tool_compass_changed (GimpToolWidget *widget); +static gint gimp_tool_compass_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_compass_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type); +static void gimp_tool_compass_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); +static GimpHit gimp_tool_compass_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_compass_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static void gimp_tool_compass_leave_notify (GimpToolWidget *widget); +static void gimp_tool_compass_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state); +static gboolean gimp_tool_compass_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier); + +static gint gimp_tool_compass_get_point (GimpToolCompass *compass, + const GimpCoords *coords); +static void gimp_tool_compass_update_hilight (GimpToolCompass *compass); +static void gimp_tool_compass_update_angle (GimpToolCompass *compass, + GimpCompassOrientation orientation, + gboolean flip); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpToolCompass, gimp_tool_compass, + GIMP_TYPE_TOOL_WIDGET) + +#define parent_class gimp_tool_compass_parent_class + +static guint compass_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_tool_compass_class_init (GimpToolCompassClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass); + + object_class->constructed = gimp_tool_compass_constructed; + object_class->set_property = gimp_tool_compass_set_property; + object_class->get_property = gimp_tool_compass_get_property; + + widget_class->changed = gimp_tool_compass_changed; + widget_class->button_press = gimp_tool_compass_button_press; + widget_class->button_release = gimp_tool_compass_button_release; + widget_class->motion = gimp_tool_compass_motion; + widget_class->hit = gimp_tool_compass_hit; + widget_class->hover = gimp_tool_compass_hover; + widget_class->leave_notify = gimp_tool_compass_leave_notify; + widget_class->motion_modifier = gimp_tool_compass_motion_modifier; + widget_class->get_cursor = gimp_tool_compass_get_cursor; + widget_class->update_on_scale = TRUE; + widget_class->update_on_rotate = TRUE; + + compass_signals[CREATE_GUIDES] = + g_signal_new ("create-guides", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpToolCompassClass, create_guides), + NULL, NULL, + gimp_marshal_VOID__INT_INT_BOOLEAN_BOOLEAN, + G_TYPE_NONE, 4, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + + g_object_class_install_property (object_class, PROP_ORIENTATION, + g_param_spec_enum ("orientation", NULL, NULL, + GIMP_TYPE_COMPASS_ORIENTATION, + GIMP_COMPASS_ORIENTATION_AUTO, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_N_POINTS, + g_param_spec_int ("n-points", NULL, NULL, + 1, 3, 1, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_X1, + g_param_spec_int ("x1", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Y1, + g_param_spec_int ("y1", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_X2, + g_param_spec_int ("x2", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Y2, + g_param_spec_int ("y2", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_X3, + g_param_spec_int ("x3", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_Y3, + g_param_spec_int ("y3", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_PIXEL_ANGLE, + g_param_spec_double ("pixel-angle", NULL, NULL, + -G_PI, G_PI, 0.0, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_UNIT_ANGLE, + g_param_spec_double ("unit-angle", NULL, NULL, + -G_PI, G_PI, 0.0, + GIMP_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_EFFECTIVE_ORIENTATION, + g_param_spec_enum ("effective-orientation", NULL, NULL, + GIMP_TYPE_COMPASS_ORIENTATION, + GIMP_COMPASS_ORIENTATION_AUTO, + GIMP_PARAM_READABLE)); +} + +static void +gimp_tool_compass_init (GimpToolCompass *compass) +{ + compass->private = gimp_tool_compass_get_instance_private (compass); + + compass->private->point = -1; +} + +static void +gimp_tool_compass_constructed (GObject *object) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (object); + GimpToolWidget *widget = GIMP_TOOL_WIDGET (object); + GimpToolCompassPrivate *private = compass->private; + GimpCanvasGroup *stroke_group; + gint i; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + stroke_group = gimp_tool_widget_add_stroke_group (widget); + + gimp_tool_widget_push_group (widget, stroke_group); + + private->line1 = gimp_tool_widget_add_line (widget, + private->x[0], + private->y[0], + private->x[1], + private->y[1]); + + private->line2 = gimp_tool_widget_add_line (widget, + private->x[0], + private->y[0], + private->x[2], + private->y[2]); + + private->arc = gimp_tool_widget_add_handle (widget, + GIMP_HANDLE_CIRCLE, + private->x[0], + private->y[0], + ARC_RADIUS * 2 + 1, + ARC_RADIUS * 2 + 1, + GIMP_HANDLE_ANCHOR_CENTER); + + private->arc_line = gimp_tool_widget_add_line (widget, + private->x[0], + private->y[0], + private->x[0] + 10, + private->y[0]); + + gimp_tool_widget_pop_group (widget); + + for (i = 0; i < 3; i++) + { + private->handles[i] = + gimp_tool_widget_add_handle (widget, + i == 0 ? + GIMP_HANDLE_CIRCLE : GIMP_HANDLE_CROSS, + private->x[i], + private->y[i], + GIMP_CANVAS_HANDLE_SIZE_CROSS, + GIMP_CANVAS_HANDLE_SIZE_CROSS, + GIMP_HANDLE_ANCHOR_CENTER); + } + + gimp_tool_compass_changed (widget); +} + +static void +gimp_tool_compass_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (object); + GimpToolCompassPrivate *private = compass->private; + + switch (property_id) + { + case PROP_ORIENTATION: + private->orientation = g_value_get_enum (value); + break; + case PROP_N_POINTS: + private->n_points = g_value_get_int (value); + break; + case PROP_X1: + private->x[0] = g_value_get_int (value); + break; + case PROP_Y1: + private->y[0] = g_value_get_int (value); + break; + case PROP_X2: + private->x[1] = g_value_get_int (value); + break; + case PROP_Y2: + private->y[1] = g_value_get_int (value); + break; + case PROP_X3: + private->x[2] = g_value_get_int (value); + break; + case PROP_Y3: + private->y[2] = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_compass_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (object); + GimpToolCompassPrivate *private = compass->private; + + switch (property_id) + { + case PROP_ORIENTATION: + g_value_set_enum (value, private->orientation); + break; + case PROP_N_POINTS: + g_value_set_int (value, private->n_points); + break; + case PROP_X1: + g_value_set_int (value, private->x[0]); + break; + case PROP_Y1: + g_value_set_int (value, private->y[0]); + break; + case PROP_X2: + g_value_set_int (value, private->x[1]); + break; + case PROP_Y2: + g_value_set_int (value, private->y[1]); + break; + case PROP_X3: + g_value_set_int (value, private->x[2]); + break; + case PROP_Y3: + g_value_set_int (value, private->y[2]); + break; + case PROP_PIXEL_ANGLE: + g_value_set_double (value, private->pixel_angle); + break; + case PROP_UNIT_ANGLE: + g_value_set_double (value, private->unit_angle); + break; + case PROP_EFFECTIVE_ORIENTATION: + g_value_set_enum (value, private->effective_orientation); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_compass_changed (GimpToolWidget *widget) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + GimpToolCompassPrivate *private = compass->private; + GimpDisplayShell *shell = gimp_tool_widget_get_shell (widget); + gdouble angle1; + gdouble angle2; + gint draw_arc = 0; + gboolean draw_arc_line = FALSE; + gdouble arc_line_display_length; + gdouble arc_line_length; + + gimp_tool_compass_update_angle (compass, private->orientation, FALSE); + + angle1 = -atan2 (private->radius1.y * shell->scale_y, + private->radius1.x * shell->scale_x); + angle2 = -private->display_angle; + + gimp_canvas_line_set (private->line1, + private->x[0], + private->y[0], + private->x[1], + private->y[1]); + gimp_canvas_item_set_visible (private->line1, private->n_points > 1); + if (private->n_points > 1 && + gimp_canvas_item_transform_distance (private->line1, + private->x[0], + private->y[0], + private->x[1], + private->y[1]) > ARC_RADIUS) + { + draw_arc++; + } + + + arc_line_display_length = ARC_RADIUS + + (GIMP_CANVAS_HANDLE_SIZE_CROSS >> 1) + + ARC_GAP; + arc_line_length = arc_line_display_length / + hypot (private->radius2.x * shell->scale_x, + private->radius2.y * shell->scale_y); + + if (private->n_points > 2) + { + gdouble length = gimp_canvas_item_transform_distance (private->line2, + private->x[0], + private->y[0], + private->x[2], + private->y[2]); + + if (length > ARC_RADIUS) + { + draw_arc++; + draw_arc_line = TRUE; + + if (length > arc_line_display_length) + { + gimp_canvas_line_set ( + private->line2, + private->x[0] + private->radius2.x * arc_line_length, + private->y[0] + private->radius2.y * arc_line_length, + private->x[2], + private->y[2]); + gimp_canvas_item_set_visible (private->line2, TRUE); + } + else + { + gimp_canvas_item_set_visible (private->line2, FALSE); + } + } + else + { + gimp_canvas_line_set (private->line2, + private->x[0], + private->y[0], + private->x[2], + private->y[2]); + gimp_canvas_item_set_visible (private->line2, TRUE); + } + } + else + { + gimp_canvas_item_set_visible (private->line2, FALSE); + } + + gimp_canvas_handle_set_position (private->arc, + private->x[0], private->y[0]); + gimp_canvas_handle_set_angles (private->arc, angle1, angle2); + gimp_canvas_item_set_visible (private->arc, + private->n_points > 1 && + draw_arc == private->n_points - 1 && + fabs (angle2) > EPSILON); + + arc_line_length = (ARC_RADIUS + (GIMP_CANVAS_HANDLE_SIZE_CROSS >> 1)) / + hypot (private->radius2.x * shell->scale_x, + private->radius2.y * shell->scale_y); + + gimp_canvas_line_set (private->arc_line, + private->x[0], + private->y[0], + private->x[0] + private->radius2.x * arc_line_length, + private->y[0] + private->radius2.y * arc_line_length); + gimp_canvas_item_set_visible (private->arc_line, + (private->n_points == 2 || draw_arc_line) && + fabs (angle2) > EPSILON); + + gimp_canvas_handle_set_position (private->handles[0], + private->x[0], private->y[0]); + gimp_canvas_item_set_visible (private->handles[0], + private->n_points > 0); + + gimp_canvas_handle_set_position (private->handles[1], + private->x[1], private->y[1]); + gimp_canvas_item_set_visible (private->handles[1], + private->n_points > 1); + + gimp_canvas_handle_set_position (private->handles[2], + private->x[2], private->y[2]); + gimp_canvas_item_set_visible (private->handles[2], + private->n_points > 2); + + gimp_tool_compass_update_hilight (compass); +} + +gint +gimp_tool_compass_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + GimpToolCompassPrivate *private = compass->private; + + private->function = CREATING; + + private->mouse_x = coords->x; + private->mouse_y = coords->y; + + /* if the cursor is in one of the handles, the new function will be + * moving or adding a new point or guide + */ + if (private->point != -1) + { + GdkModifierType extend_mask = gimp_get_extend_selection_mask (); + GdkModifierType toggle_mask = gimp_get_toggle_behavior_mask (); + + if (state & (toggle_mask | GDK_MOD1_MASK)) + { + gboolean create_hguide = (state & toggle_mask); + gboolean create_vguide = (state & GDK_MOD1_MASK); + + g_signal_emit (compass, compass_signals[CREATE_GUIDES], 0, + private->x[private->point], + private->y[private->point], + create_hguide, + create_vguide); + + private->function = GUIDING; + } + else + { + if (private->n_points == 1 || (state & extend_mask)) + private->function = ADDING; + else + private->function = MOVING; + } + } + + /* adding to the middle point makes no sense */ + if (private->point == 0 && + private->function == ADDING && + private->n_points == 3) + { + private->function = MOVING; + } + + /* if the function is still CREATING, we are outside the handles */ + if (private->function == CREATING) + { + if (private->n_points > 1 && (state & GDK_MOD1_MASK)) + { + private->function = MOVING_ALL; + + private->last_x = coords->x; + private->last_y = coords->y; + } + } + + if (private->function == CREATING) + { + /* set the first point and go into ADDING mode */ + g_object_set (compass, + "n-points", 1, + "x1", (gint) (coords->x + 0.5), + "y1", (gint) (coords->y + 0.5), + "x2", 0, + "y2", 0, + "x3", 0, + "y3", 0, + NULL); + + private->point = 0; + private->function = ADDING; + } + + return 1; +} + +void +gimp_tool_compass_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + GimpToolCompassPrivate *private = compass->private; + + private->function = FINISHED; +} + +void +gimp_tool_compass_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + GimpToolCompassPrivate *private = compass->private; + gint new_n_points; + gint new_x[3]; + gint new_y[3]; + gint dx, dy; + gint tmp; + + private->mouse_x = coords->x; + private->mouse_y = coords->y; + + /* A few comments here, because this routine looks quite weird at first ... + * + * The goal is to keep point 0, called the start point, to be + * always the one in the middle or, if there are only two points, + * the one that is fixed. The angle is then always measured at + * this point. + */ + + new_n_points = private->n_points; + new_x[0] = private->x[0]; + new_y[0] = private->y[0]; + new_x[1] = private->x[1]; + new_y[1] = private->y[1]; + new_x[2] = private->x[2]; + new_y[2] = private->y[2]; + + switch (private->function) + { + case ADDING: + switch (private->point) + { + case 0: + /* we are adding to the start point */ + break; + + case 1: + /* we are adding to the end point, make it the new start point */ + new_x[0] = private->x[1]; + new_y[0] = private->y[1]; + + new_x[1] = private->x[0]; + new_y[1] = private->y[0]; + break; + + case 2: + /* we are adding to the third point, make it the new start point */ + new_x[1] = private->x[0]; + new_y[1] = private->y[0]; + new_x[0] = private->x[2]; + new_y[0] = private->y[2]; + break; + + default: + break; + } + + new_n_points = MIN (new_n_points + 1, 3); + + private->point = new_n_points - 1; + private->function = MOVING; + /* don't break here! */ + + case MOVING: + /* if we are moving the start point and only have two, make it + * the end point + */ + if (new_n_points == 2 && private->point == 0) + { + tmp = new_x[0]; + new_x[0] = new_x[1]; + new_x[1] = tmp; + + tmp = new_y[0]; + new_y[0] = new_y[1]; + new_y[1] = tmp; + + private->point = 1; + } + + new_x[private->point] = ROUND (coords->x); + new_y[private->point] = ROUND (coords->y); + + if (state & gimp_get_constrain_behavior_mask ()) + { + gdouble x = new_x[private->point]; + gdouble y = new_y[private->point]; + + gimp_display_shell_constrain_line (gimp_tool_widget_get_shell (widget), + new_x[0], new_y[0], + &x, &y, + GIMP_CONSTRAIN_LINE_15_DEGREES); + + new_x[private->point] = ROUND (x); + new_y[private->point] = ROUND (y); + } + + g_object_set (compass, + "n-points", new_n_points, + "x1", new_x[0], + "y1", new_y[0], + "x2", new_x[1], + "y2", new_y[1], + "x3", new_x[2], + "y3", new_y[2], + NULL); + break; + + case MOVING_ALL: + dx = ROUND (coords->x) - private->last_x; + dy = ROUND (coords->y) - private->last_y; + + g_object_set (compass, + "x1", new_x[0] + dx, + "y1", new_y[0] + dy, + "x2", new_x[1] + dx, + "y2", new_y[1] + dy, + "x3", new_x[2] + dx, + "y3", new_y[2] + dy, + NULL); + + private->last_x = ROUND (coords->x); + private->last_y = ROUND (coords->y); + break; + + default: + break; + } +} + +GimpHit +gimp_tool_compass_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + + if (gimp_tool_compass_get_point (compass, coords) >= 0) + return GIMP_HIT_DIRECT; + else + return GIMP_HIT_INDIRECT; +} + +void +gimp_tool_compass_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + GimpToolCompassPrivate *private = compass->private; + gint point; + + private->mouse_x = coords->x; + private->mouse_y = coords->y; + + point = gimp_tool_compass_get_point (compass, coords); + + if (point >= 0) + { + GdkModifierType extend_mask = gimp_get_extend_selection_mask (); + GdkModifierType toggle_mask = gimp_get_toggle_behavior_mask (); + gchar *status; + + if (state & toggle_mask) + { + if (state & GDK_MOD1_MASK) + { + status = gimp_suggest_modifiers (_("Click to place " + "vertical and " + "horizontal guides"), + 0, + NULL, NULL, NULL); + } + else + { + status = gimp_suggest_modifiers (_("Click to place a " + "horizontal guide"), + GDK_MOD1_MASK & ~state, + NULL, NULL, NULL); + } + } + else if (state & GDK_MOD1_MASK) + { + status = gimp_suggest_modifiers (_("Click to place a " + "vertical guide"), + toggle_mask & ~state, + NULL, NULL, NULL); + } + else if ((state & extend_mask) && + ! ((point == 0) && (private->n_points == 3))) + { + status = gimp_suggest_modifiers (_("Click-Drag to add a " + "new point"), + (toggle_mask | + GDK_MOD1_MASK) & ~state, + NULL, NULL, NULL); + } + else + { + if ((point == 0) && (private->n_points == 3)) + state |= extend_mask; + + status = gimp_suggest_modifiers (_("Click-Drag to move this " + "point"), + (extend_mask | + toggle_mask | + GDK_MOD1_MASK) & ~state, + NULL, NULL, NULL); + } + + gimp_tool_widget_set_status (widget, status); + + g_free (status); + } + else + { + if ((private->n_points > 1) && (state & GDK_MOD1_MASK)) + { + gimp_tool_widget_set_status (widget, + _("Click-Drag to move all points")); + } + else + { + gimp_tool_widget_set_status (widget, NULL); + } + } + + if (point != private->point) + { + private->point = point; + + gimp_tool_compass_update_hilight (compass); + } +} + +void +gimp_tool_compass_leave_notify (GimpToolWidget *widget) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + GimpToolCompassPrivate *private = compass->private; + + if (private->point != -1) + { + private->point = -1; + + gimp_tool_compass_update_hilight (compass); + } + + GIMP_TOOL_WIDGET_CLASS (parent_class)->leave_notify (widget); +} + +static void +gimp_tool_compass_motion_modifier (GimpToolWidget *widget, + GdkModifierType key, + gboolean press, + GdkModifierType state) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + GimpToolCompassPrivate *private = compass->private; + + if (key == gimp_get_constrain_behavior_mask () && + private->function == MOVING) + { + gint new_x[3]; + gint new_y[3]; + gdouble x = private->mouse_x; + gdouble y = private->mouse_y; + + new_x[0] = private->x[0]; + new_y[0] = private->y[0]; + new_x[1] = private->x[1]; + new_y[1] = private->y[1]; + new_x[2] = private->x[2]; + new_y[2] = private->y[2]; + + if (press) + { + gimp_display_shell_constrain_line (gimp_tool_widget_get_shell (widget), + private->x[0], private->y[0], + &x, &y, + GIMP_CONSTRAIN_LINE_15_DEGREES); + } + + new_x[private->point] = ROUND (x); + new_y[private->point] = ROUND (y); + + g_object_set (compass, + "x1", new_x[0], + "y1", new_y[0], + "x2", new_x[1], + "y2", new_y[1], + "x3", new_x[2], + "y3", new_y[2], + NULL); + } +} + +static gboolean +gimp_tool_compass_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *modifier) +{ + GimpToolCompass *compass = GIMP_TOOL_COMPASS (widget); + GimpToolCompassPrivate *private = compass->private; + + if (private->point != -1) + { + GdkModifierType extend_mask = gimp_get_extend_selection_mask (); + GdkModifierType toggle_mask = gimp_get_toggle_behavior_mask (); + + if (state & toggle_mask) + { + if (state & GDK_MOD1_MASK) + { + *cursor = GIMP_CURSOR_CORNER_BOTTOM_RIGHT; + return TRUE; + } + else + { + *cursor = GIMP_CURSOR_SIDE_BOTTOM; + return TRUE; + } + } + else if (state & GDK_MOD1_MASK) + { + *cursor = GIMP_CURSOR_SIDE_RIGHT; + return TRUE; + } + else if ((state & extend_mask) && + ! ((private->point == 0) && + (private->n_points == 3))) + { + *modifier = GIMP_CURSOR_MODIFIER_PLUS; + return TRUE; + } + else + { + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + return TRUE; + } + } + else + { + if ((private->n_points > 1) && (state & GDK_MOD1_MASK)) + { + *modifier = GIMP_CURSOR_MODIFIER_MOVE; + return TRUE; + } + } + + return FALSE; +} + +static gint +gimp_tool_compass_get_point (GimpToolCompass *compass, + const GimpCoords *coords) +{ + GimpToolCompassPrivate *private = compass->private; + gint i; + + for (i = 0; i < private->n_points; i++) + { + if (gimp_canvas_item_hit (private->handles[i], + coords->x, coords->y)) + { + return i; + } + } + + return -1; +} + +static void +gimp_tool_compass_update_hilight (GimpToolCompass *compass) +{ + GimpToolCompassPrivate *private = compass->private; + gint i; + + for (i = 0; i < private->n_points; i++) + { + if (private->handles[i]) + { + gimp_canvas_item_set_highlight (private->handles[i], + private->point == i); + } + } +} + +static void +gimp_tool_compass_update_angle (GimpToolCompass *compass, + GimpCompassOrientation orientation, + gboolean flip) +{ + GimpToolWidget *widget = GIMP_TOOL_WIDGET (compass); + GimpToolCompassPrivate *private = compass->private; + GimpDisplayShell *shell = gimp_tool_widget_get_shell (widget); + GimpImage *image = gimp_display_get_image (shell->display); + GimpVector2 radius1; + GimpVector2 radius2; + gdouble pixel_angle; + gdouble unit_angle; + gdouble xres; + gdouble yres; + + gimp_image_get_resolution (image, &xres, &yres); + + private->radius1.x = private->x[1] - private->x[0]; + private->radius1.y = private->y[1] - private->y[0]; + + if (private->n_points == 3) + { + orientation = GIMP_COMPASS_ORIENTATION_AUTO; + + private->radius2.x = private->x[2] - private->x[0]; + private->radius2.y = private->y[2] - private->y[0]; + } + else + { + gdouble angle = -shell->rotate_angle * G_PI / 180.0; + + if (orientation == GIMP_COMPASS_ORIENTATION_VERTICAL) + angle -= G_PI / 2.0; + + if (flip) + angle += G_PI; + + if (shell->flip_horizontally) + angle = G_PI - angle; + if (shell->flip_vertically) + angle = -angle; + + private->radius2.x = cos (angle); + private->radius2.y = sin (angle); + + if (! shell->dot_for_dot) + { + private->radius2.x *= xres; + private->radius2.y *= yres; + + gimp_vector2_normalize (&private->radius2); + } + } + + radius1 = private->radius1; + radius2 = private->radius2; + + pixel_angle = atan2 (gimp_vector2_cross_product (&radius1, &radius2).x, + gimp_vector2_inner_product (&radius1, &radius2)); + + radius1.x /= xres; + radius1.y /= yres; + + radius2.x /= xres; + radius2.y /= yres; + + unit_angle = atan2 (gimp_vector2_cross_product (&radius1, &radius2).x, + gimp_vector2_inner_product (&radius1, &radius2)); + + if (shell->dot_for_dot) + private->display_angle = pixel_angle; + else + private->display_angle = unit_angle; + + if (private->n_points == 2) + { + if (! flip && fabs (private->display_angle) > G_PI / 2.0 + EPSILON) + { + gimp_tool_compass_update_angle (compass, orientation, TRUE); + + return; + } + else if (orientation == GIMP_COMPASS_ORIENTATION_AUTO) + { + if (fabs (private->display_angle) <= G_PI / 4.0 + EPSILON) + { + orientation = GIMP_COMPASS_ORIENTATION_HORIZONTAL; + } + else + { + gimp_tool_compass_update_angle (compass, + GIMP_COMPASS_ORIENTATION_VERTICAL, + FALSE); + + return; + } + } + } + + if (fabs (pixel_angle - private->pixel_angle) > EPSILON) + { + private->pixel_angle = pixel_angle; + + g_object_notify (G_OBJECT (compass), "pixel-angle"); + } + + if (fabs (unit_angle - private->unit_angle) > EPSILON) + { + private->unit_angle = unit_angle; + + g_object_notify (G_OBJECT (compass), "unit-angle"); + } + + if (orientation != private->effective_orientation) + { + private->effective_orientation = orientation; + + g_object_notify (G_OBJECT (compass), "effective-orientation"); + } +} + + +/* public functions */ + +GimpToolWidget * +gimp_tool_compass_new (GimpDisplayShell *shell, + GimpCompassOrientation orientation, + gint n_points, + gint x1, + gint y1, + gint x2, + gint y2, + gint x3, + gint y3) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_TOOL_COMPASS, + "shell", shell, + "orientation", orientation, + "n-points", n_points, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + NULL); +} |