diff --git a/ChangeLog b/ChangeLog index 3f642d4813..afe52547bd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +2005-01-03 Michael Natterer + + Implemented "Snap to Canvas Edges" (fixes bug #152971) and + "Snap to Active Path" (half way done): + + * app/core/gimpimage-snap.[ch]: added boolean snap_to_canvas and + snap_to_vectors parameters (snap_to_vectors works fine when + snapping to a point, but is unimplemented for snapping to a + rectangle). + + * app/display/gimpdisplayshell.[ch] (struct GimpDisplayShell): + added snap_to_canvas and snap_to_vectors booleans. + + * app/display/gimpdisplayshell-appearance.[ch]: added API to + get/set them. + + * app/actions/view-actions.c + * app/actions/view-commands.[ch] + * app/widgets/gimphelp-ids.h: added actions, callbacks and help IDs. + + * menus/image-menu.xml.in: added them to Image->View. + 2005-01-03 Sven Neumann * plug-ins/ifscompose/ifscompose.c: use g_free() to release memory diff --git a/app/actions/view-actions.c b/app/actions/view-actions.c index e3404d7db1..bd4f6acb10 100644 --- a/app/actions/view-actions.c +++ b/app/actions/view-actions.c @@ -157,6 +157,18 @@ static GimpToggleActionEntry view_toggle_actions[] = FALSE, GIMP_HELP_VIEW_SNAP_TO_GRID }, + { "view-snap-to-canvas", NULL, + N_("S_nap to Canvas Edges"), NULL, NULL, + G_CALLBACK (view_snap_to_canvas_cmd_callback), + FALSE, + GIMP_HELP_VIEW_SNAP_TO_CANVAS }, + + { "view-snap-to-vectors", NULL, + N_("Snap to Active Pat_h"), NULL, NULL, + G_CALLBACK (view_snap_to_vectors_cmd_callback), + FALSE, + GIMP_HELP_VIEW_SNAP_TO_VECTORS }, + { "view-show-menubar", NULL, N_("Show _Menubar"), NULL, NULL, G_CALLBACK (view_toggle_menubar_cmd_callback), @@ -514,6 +526,8 @@ view_actions_update (GimpActionGroup *group, SET_ACTIVE ("view-snap-to-guides", gdisp && shell->snap_to_guides); SET_ACTIVE ("view-show-grid", gdisp && options->show_grid); SET_ACTIVE ("view-snap-to-grid", gdisp && shell->snap_to_grid); + SET_ACTIVE ("view-snap-to-canvas", gdisp && shell->snap_to_canvas); + SET_ACTIVE ("view-snap-to-vectors", gdisp && shell->snap_to_vectors); if (gdisp) { diff --git a/app/actions/view-commands.c b/app/actions/view-commands.c index 1ceae4c26a..d7c535d72a 100644 --- a/app/actions/view-commands.c +++ b/app/actions/view-commands.c @@ -526,6 +526,38 @@ view_snap_to_grid_cmd_callback (GtkAction *action, gimp_display_shell_set_snap_to_grid (shell, active); } +void +view_snap_to_canvas_cmd_callback (GtkAction *action, + gpointer data) +{ + GimpDisplay *gdisp; + GimpDisplayShell *shell; + gboolean active; + return_if_no_display (gdisp, data); + + shell = GIMP_DISPLAY_SHELL (gdisp->shell); + + active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); + + gimp_display_shell_set_snap_to_canvas (shell, active); +} + +void +view_snap_to_vectors_cmd_callback (GtkAction *action, + gpointer data) +{ + GimpDisplay *gdisp; + GimpDisplayShell *shell; + gboolean active; + return_if_no_display (gdisp, data); + + shell = GIMP_DISPLAY_SHELL (gdisp->shell); + + active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); + + gimp_display_shell_set_snap_to_vectors (shell, active); +} + void view_padding_color_cmd_callback (GtkAction *action, gint value, diff --git a/app/actions/view-commands.h b/app/actions/view-commands.h index 831052d0f8..1074c0ad15 100644 --- a/app/actions/view-commands.h +++ b/app/actions/view-commands.h @@ -73,6 +73,10 @@ void view_toggle_grid_cmd_callback (GtkAction *action, gpointer data); void view_snap_to_grid_cmd_callback (GtkAction *action, gpointer data); +void view_snap_to_canvas_cmd_callback (GtkAction *action, + gpointer data); +void view_snap_to_vectors_cmd_callback (GtkAction *action, + gpointer data); void view_padding_color_cmd_callback (GtkAction *action, gint value, gpointer data); diff --git a/app/core/gimpimage-snap.c b/app/core/gimpimage-snap.c index 07282827e9..851bc5c8f0 100644 --- a/app/core/gimpimage-snap.c +++ b/app/core/gimpimage-snap.c @@ -28,6 +28,9 @@ #include "gimpimage-guides.h" #include "gimpimage-snap.h" +#include "vectors/gimpstroke.h" +#include "vectors/gimpvectors.h" + #include "gimp-intl.h" @@ -40,7 +43,8 @@ gimp_image_snap_x (GimpImage *gimage, gdouble *tx, gdouble epsilon_x, gboolean snap_to_guides, - gboolean snap_to_grid) + gboolean snap_to_grid, + gboolean snap_to_canvas) { GList *list; GimpGuide *guide; @@ -55,7 +59,7 @@ gimp_image_snap_x (GimpImage *gimage, g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (tx != NULL, FALSE); - if (! snap_to_guides && ! snap_to_grid) + if (! (snap_to_guides || snap_to_grid || snap_to_canvas)) return FALSE; *tx = x; @@ -111,6 +115,27 @@ gimp_image_snap_x (GimpImage *gimage, } } + if (snap_to_canvas) + { + dist = ABS (x); + + if (dist < MIN (epsilon_x, mindist)) + { + mindist = dist; + *tx = 0; + snapped = TRUE; + } + + dist = ABS (gimage->width - x); + + if (dist < MIN (epsilon_x, mindist)) + { + mindist = dist; + *tx = gimage->width; + snapped = TRUE; + } + } + return snapped; } @@ -120,7 +145,8 @@ gimp_image_snap_y (GimpImage *gimage, gdouble *ty, gdouble epsilon_y, gboolean snap_to_guides, - gboolean snap_to_grid) + gboolean snap_to_grid, + gboolean snap_to_canvas) { GList *list; GimpGuide *guide; @@ -135,7 +161,7 @@ gimp_image_snap_y (GimpImage *gimage, g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (ty != NULL, FALSE); - if (! snap_to_guides && ! snap_to_grid) + if (! (snap_to_guides || snap_to_grid || snap_to_canvas)) return FALSE; *ty = y; @@ -191,6 +217,27 @@ gimp_image_snap_y (GimpImage *gimage, } } + if (snap_to_canvas) + { + dist = ABS (y); + + if (dist < MIN (epsilon_y, mindist)) + { + mindist = dist; + *ty = 0; + snapped = TRUE; + } + + dist = ABS (gimage->height - y); + + if (dist < MIN (epsilon_y, mindist)) + { + mindist = dist; + *ty = gimage->height; + snapped = TRUE; + } + } + return snapped; } @@ -203,7 +250,9 @@ gimp_image_snap_point (GimpImage *gimage, gdouble epsilon_x, gdouble epsilon_y, gboolean snap_to_guides, - gboolean snap_to_grid) + gboolean snap_to_grid, + gboolean snap_to_canvas, + gboolean snap_to_vectors) { GList *list; GimpGuide *guide; @@ -219,7 +268,7 @@ gimp_image_snap_point (GimpImage *gimage, g_return_val_if_fail (tx != NULL, FALSE); g_return_val_if_fail (ty != NULL, FALSE); - if (! snap_to_guides && ! snap_to_grid) + if (! (snap_to_guides || snap_to_grid || snap_to_canvas || snap_to_vectors)) return FALSE; *tx = x; @@ -315,6 +364,80 @@ gimp_image_snap_point (GimpImage *gimage, } } + if (snap_to_canvas) + { + dist = ABS (x); + + if (dist < MIN (epsilon_x, minxdist)) + { + minxdist = dist; + *tx = 0; + snapped = TRUE; + } + + dist = ABS (gimage->width - x); + + if (dist < MIN (epsilon_x, minxdist)) + { + minxdist = dist; + *tx = gimage->width; + snapped = TRUE; + } + + dist = ABS (y); + + if (dist < MIN (epsilon_y, minydist)) + { + minydist = dist; + *ty = 0; + snapped = TRUE; + } + + dist = ABS (gimage->height - y); + + if (dist < MIN (epsilon_y, minydist)) + { + minydist = dist; + *ty = gimage->height; + snapped = TRUE; + } + } + + if (snap_to_vectors && gimage->active_vectors != NULL) + { + GimpVectors *vectors = gimp_image_get_active_vectors (gimage); + GimpStroke *stroke = NULL; + GimpCoords coords = { x, y, 0, 0, 0 }; + + while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke))) + { + GimpCoords nearest; + + if (gimp_stroke_nearest_point_get (stroke, &coords, 1.0, + &nearest, + NULL, NULL, NULL) >= 0) + { + dist = ABS (nearest.x - x); + + if (dist < MIN (epsilon_x, minxdist)) + { + minxdist = dist; + *tx = nearest.x; + snapped = TRUE; + } + + dist = ABS (nearest.y - y); + + if (dist < MIN (epsilon_y, minydist)) + { + minydist = dist; + *ty = nearest.y; + snapped = TRUE; + } + } + } + } + return snapped; } @@ -329,7 +452,9 @@ gimp_image_snap_rectangle (GimpImage *gimage, gdouble epsilon_x, gdouble epsilon_y, gboolean snap_to_guides, - gboolean snap_to_grid) + gboolean snap_to_grid, + gboolean snap_to_canvas, + gboolean snap_to_vectors) { gdouble nx1, ny1; gdouble nx2, ny2; @@ -339,28 +464,36 @@ gimp_image_snap_rectangle (GimpImage *gimage, g_return_val_if_fail (tx1 != NULL, FALSE); g_return_val_if_fail (ty1 != NULL, FALSE); - if (! snap_to_guides && ! snap_to_grid) + if (! (snap_to_guides || snap_to_grid || snap_to_canvas || snap_to_vectors)) return FALSE; +#ifdef __GNUC__ +#warning FIXME: implement rectangle snapping to vectors +#endif + *tx1 = x1; *ty1 = y1; snap1 = gimp_image_snap_x (gimage, x1, &nx1, epsilon_x, snap_to_guides, - snap_to_grid); + snap_to_grid, + snap_to_canvas); snap2 = gimp_image_snap_x (gimage, x2, &nx2, epsilon_x, snap_to_guides, - snap_to_grid); + snap_to_grid, + snap_to_canvas); snap3 = gimp_image_snap_y (gimage, y1, &ny1, epsilon_y, snap_to_guides, - snap_to_grid); + snap_to_grid, + snap_to_canvas); snap4 = gimp_image_snap_y (gimage, y2, &ny2, epsilon_y, snap_to_guides, - snap_to_grid); + snap_to_grid, + snap_to_canvas); if (snap1 && snap2) { diff --git a/app/core/gimpimage-snap.h b/app/core/gimpimage-snap.h index 8bbfac06f4..f274bef05d 100644 --- a/app/core/gimpimage-snap.h +++ b/app/core/gimpimage-snap.h @@ -25,13 +25,15 @@ gboolean gimp_image_snap_x (GimpImage *gimage, gdouble *tx, gdouble epsilon_x, gboolean snap_to_guides, - gboolean snap_to_grid); + gboolean snap_to_grid, + gboolean snap_to_canvas); gboolean gimp_image_snap_y (GimpImage *gimage, gdouble y, gdouble *ty, gdouble epsilon_y, gboolean snap_to_guides, - gboolean snap_to_grid); + gboolean snap_to_grid, + gboolean snap_to_canvas); gboolean gimp_image_snap_point (GimpImage *gimage, gdouble x, gdouble y, @@ -40,7 +42,9 @@ gboolean gimp_image_snap_point (GimpImage *gimage, gdouble epsilon_x, gdouble epsilon_y, gboolean snap_to_guides, - gboolean snap_to_grid); + gboolean snap_to_grid, + gboolean snap_to_canvas, + gboolean snap_to_vectors); gboolean gimp_image_snap_rectangle (GimpImage *gimage, gdouble x1, gdouble y1, @@ -51,7 +55,9 @@ gboolean gimp_image_snap_rectangle (GimpImage *gimage, gdouble epsilon_x, gdouble epsilon_y, gboolean snap_to_guides, - gboolean snap_to_grid); + gboolean snap_to_grid, + gboolean snap_to_canvas, + gboolean snap_to_vectors); #endif /* __GIMP_IMAGE_SNAP_H__ */ diff --git a/app/display/gimpdisplayshell-appearance.c b/app/display/gimpdisplayshell-appearance.c index 9540c8261e..19d16a9705 100644 --- a/app/display/gimpdisplayshell-appearance.c +++ b/app/display/gimpdisplayshell-appearance.c @@ -452,6 +452,56 @@ gimp_display_shell_get_snap_to_guides (GimpDisplayShell *shell) return shell->snap_to_guides; } +void +gimp_display_shell_set_snap_to_canvas (GimpDisplayShell *shell, + gboolean snap) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (snap != shell->snap_to_canvas) + { + shell->snap_to_canvas = snap ? TRUE : FALSE; + + SET_ACTIVE (shell->menubar_manager, "view-snap-to-canvas", snap); + + if (IS_ACTIVE_DISPLAY (shell)) + SET_ACTIVE (shell->popup_manager, "view-snap-to-canvas", snap); + } +} + +gboolean +gimp_display_shell_get_snap_to_canvas (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return shell->snap_to_canvas; +} + +void +gimp_display_shell_set_snap_to_vectors (GimpDisplayShell *shell, + gboolean snap) +{ + g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); + + if (snap != shell->snap_to_vectors) + { + shell->snap_to_vectors = snap ? TRUE : FALSE; + + SET_ACTIVE (shell->menubar_manager, "view-snap-to-vectors", snap); + + if (IS_ACTIVE_DISPLAY (shell)) + SET_ACTIVE (shell->popup_manager, "view-snap-to-vectors", snap); + } +} + +gboolean +gimp_display_shell_get_snap_to_vectors (GimpDisplayShell *shell) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); + + return shell->snap_to_vectors; +} + void gimp_display_shell_set_padding (GimpDisplayShell *shell, GimpCanvasPaddingMode padding_mode, diff --git a/app/display/gimpdisplayshell-appearance.h b/app/display/gimpdisplayshell-appearance.h index a3a6eea88d..130bff976d 100644 --- a/app/display/gimpdisplayshell-appearance.h +++ b/app/display/gimpdisplayshell-appearance.h @@ -68,6 +68,14 @@ void gimp_display_shell_set_snap_to_guides (GimpDisplayShell *shell, gboolean snap); gboolean gimp_display_shell_get_snap_to_guides (GimpDisplayShell *shell); +void gimp_display_shell_set_snap_to_canvas (GimpDisplayShell *shell, + gboolean snap); +gboolean gimp_display_shell_get_snap_to_canvas (GimpDisplayShell *shell); + +void gimp_display_shell_set_snap_to_vectors (GimpDisplayShell *shell, + gboolean snap); +gboolean gimp_display_shell_get_snap_to_vectors (GimpDisplayShell *shell); + void gimp_display_shell_set_padding (GimpDisplayShell *shell, GimpCanvasPaddingMode mode, const GimpRGB *color); diff --git a/app/display/gimpdisplayshell.c b/app/display/gimpdisplayshell.c index ad50b5c7a2..0cb906b5fe 100644 --- a/app/display/gimpdisplayshell.c +++ b/app/display/gimpdisplayshell.c @@ -249,6 +249,8 @@ gimp_display_shell_init (GimpDisplayShell *shell) shell->proximity = FALSE; shell->snap_to_guides = TRUE; shell->snap_to_grid = FALSE; + shell->snap_to_canvas = FALSE; + shell->snap_to_vectors = FALSE; shell->select = NULL; @@ -1105,9 +1107,11 @@ gimp_display_shell_snap_coords (GimpDisplayShell *shell, gint snap_width, gint snap_height) { - gboolean snap_to_guides = FALSE; - gboolean snap_to_grid = FALSE; - gboolean snapped = FALSE; + gboolean snap_to_guides = FALSE; + gboolean snap_to_grid = FALSE; + gboolean snap_to_canvas = FALSE; + gboolean snap_to_vectors = FALSE; + gboolean snapped = FALSE; g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE); g_return_val_if_fail (coords != NULL, FALSE); @@ -1115,7 +1119,7 @@ gimp_display_shell_snap_coords (GimpDisplayShell *shell, *snapped_coords = *coords; - if (shell->snap_to_guides && + if (gimp_display_shell_get_snap_to_guides (shell) && shell->gdisp->gimage->guides) { snap_to_guides = TRUE; @@ -1127,7 +1131,15 @@ gimp_display_shell_snap_coords (GimpDisplayShell *shell, snap_to_grid = TRUE; } - if (snap_to_guides || snap_to_grid) + snap_to_canvas = gimp_display_shell_get_snap_to_canvas (shell); + + if (gimp_display_shell_get_snap_to_vectors (shell) && + gimp_image_get_active_vectors (shell->gdisp->gimage)) + { + snap_to_vectors = TRUE; + } + + if (snap_to_guides || snap_to_grid || snap_to_canvas || snap_to_vectors) { gdouble tx, ty; gint snap_distance; @@ -1149,7 +1161,9 @@ gimp_display_shell_snap_coords (GimpDisplayShell *shell, FUNSCALEX (shell, snap_distance), FUNSCALEY (shell, snap_distance), snap_to_guides, - snap_to_grid); + snap_to_grid, + snap_to_canvas, + snap_to_vectors); } else { @@ -1161,7 +1175,9 @@ gimp_display_shell_snap_coords (GimpDisplayShell *shell, FUNSCALEX (shell, snap_distance), FUNSCALEY (shell, snap_distance), snap_to_guides, - snap_to_grid); + snap_to_grid, + snap_to_canvas, + snap_to_vectors); } if (snapped) diff --git a/app/display/gimpdisplayshell.h b/app/display/gimpdisplayshell.h index 966eae67e3..c242625a1e 100644 --- a/app/display/gimpdisplayshell.h +++ b/app/display/gimpdisplayshell.h @@ -93,6 +93,8 @@ struct _GimpDisplayShell gboolean proximity; /* is a device in proximity */ gboolean snap_to_guides; /* should the guides be snapped to? */ gboolean snap_to_grid; /* should the grid be snapped to? */ + gboolean snap_to_canvas; /* should the canvas be snapped to? */ + gboolean snap_to_vectors; /* should the active path be snapped */ Selection *select; /* Selection object */ diff --git a/app/widgets/gimphelp-ids.h b/app/widgets/gimphelp-ids.h index 069f9dd4c1..00638e0f40 100644 --- a/app/widgets/gimphelp-ids.h +++ b/app/widgets/gimphelp-ids.h @@ -82,6 +82,8 @@ #define GIMP_HELP_VIEW_SNAP_TO_GUIDES "gimp-view-snap-to-guides" #define GIMP_HELP_VIEW_SHOW_GRID "gimp-view-show-grid" #define GIMP_HELP_VIEW_SNAP_TO_GRID "gimp-view-snap-to-grid" +#define GIMP_HELP_VIEW_SNAP_TO_CANVAS "gimp-view-snap-to-canvas" +#define GIMP_HELP_VIEW_SNAP_TO_VECTORS "gimp-view-snap-to-vectors" #define GIMP_HELP_VIEW_SHOW_MENUBAR "gimp-view-show-menubar" #define GIMP_HELP_VIEW_SHOW_RULERS "gimp-view-show-rulers" #define GIMP_HELP_VIEW_SHOW_SCROLLBARS "gimp-view-show-scrollbars" diff --git a/menus/image-menu.xml.in b/menus/image-menu.xml.in index d500808d80..8f23c064b6 100644 --- a/menus/image-menu.xml.in +++ b/menus/image-menu.xml.in @@ -241,6 +241,8 @@ + +