Index: app/tools/gimpeditselectiontool.c
===================================================================
--- app/tools/gimpeditselectiontool.c	(revision 24492)
+++ app/tools/gimpeditselectiontool.c	(working copy)
@@ -62,20 +62,43 @@
 #define EDIT_SELECT_SCROLL_LOCK FALSE
 #define ARROW_VELOCITY          25
 
+/**
+ * The number of evenly distributed lines onto which moving will be
+ * constrained when movement constraint is active.
+ */
+#define NBR_OF_SNAP_LINES       4
+
+
+static void    gimp_edit_selection_tool_finalize               (GObject               *object);
+static void    gimp_edit_selection_tool_button_release         (GimpTool              *tool,
+                                                                GimpCoords            *coords,
+                                                                guint32                time,
+                                                                GdkModifierType        state,
+                                                                GimpButtonReleaseType  release_type,
+                                                                GimpDisplay           *display);
+static void    gimp_edit_selection_tool_motion                 (GimpTool              *tool,
+                                                                GimpCoords            *coords,
+                                                                guint32                time,
+                                                                GdkModifierType        state,
+                                                                GimpDisplay           *display);
+static void    gimp_edit_selection_tool_active_modifier_key    (GimpTool              *tool,
+                                                                GdkModifierType        key,
+                                                                gboolean               press,
+                                                                GdkModifierType        state,
+                                                                GimpDisplay           *display);
+static void    gimp_edit_selection_tool_draw                   (GimpDrawTool          *tool);
+
+static void    gimp_edit_selection_tool_init_snap_line_vectors (GimpEditSelectionTool *edit_select);
+static void    gimp_edit_selection_tool_snap_onto_lines        (GimpVector2           *snapped_point,
+                                                                const GimpVector2     *line_dir_vects,
+                                                                const GimpVector2     *line_point,
+                                                                const GimpVector2     *point);
+static gdouble gimp_edit_selection_tool_point_to_line_distance (const GimpVector2     *point,
+                                                                const GimpVector2     *point_on_line,
+                                                                const GimpVector2     *normalized_line_direction,
+                                                                GimpVector2           *closest_line_point);
 
-static void   gimp_edit_selection_tool_button_release (GimpTool              *tool,
-                                                       GimpCoords            *coords,
-                                                       guint32                time,
-                                                       GdkModifierType        state,
-                                                       GimpButtonReleaseType  release_type,
-                                                       GimpDisplay           *display);
-static void   gimp_edit_selection_tool_motion         (GimpTool              *tool,
-                                                       GimpCoords            *coords,
-                                                       guint32                time,
-                                                       GdkModifierType        state,
-                                                       GimpDisplay           *display);
 
-static void   gimp_edit_selection_tool_draw           (GimpDrawTool          *tool);
 
 
 G_DEFINE_TYPE (GimpEditSelectionTool, gimp_edit_selection_tool,
@@ -87,13 +110,17 @@ G_DEFINE_TYPE (GimpEditSelectionTool, gi
 static void
 gimp_edit_selection_tool_class_init (GimpEditSelectionToolClass *klass)
 {
-  GimpToolClass     *tool_class = GIMP_TOOL_CLASS (klass);
-  GimpDrawToolClass *draw_class = GIMP_DRAW_TOOL_CLASS (klass);
+  GObjectClass      *object_class = G_OBJECT_CLASS (klass);
+  GimpToolClass     *tool_class   = GIMP_TOOL_CLASS (klass);
+  GimpDrawToolClass *draw_class   = GIMP_DRAW_TOOL_CLASS (klass);
 
-  tool_class->button_release = gimp_edit_selection_tool_button_release;
-  tool_class->motion         = gimp_edit_selection_tool_motion;
+  object_class->finalize          = gimp_edit_selection_tool_finalize;
 
-  draw_class->draw           = gimp_edit_selection_tool_draw;
+  tool_class->button_release      = gimp_edit_selection_tool_button_release;
+  tool_class->motion              = gimp_edit_selection_tool_motion;
+  tool_class->active_modifier_key = gimp_edit_selection_tool_active_modifier_key;
+
+  draw_class->draw                = gimp_edit_selection_tool_draw;
 }
 
 static void
@@ -111,6 +138,22 @@ gimp_edit_selection_tool_init (GimpEditS
   edit_selection_tool->cumly      = 0;
 
   edit_selection_tool->first_move = TRUE;
+
+  edit_selection_tool->constrain  = FALSE;
+
+  gimp_edit_selection_tool_init_snap_line_vectors (edit_selection_tool);
+}
+
+static void
+gimp_edit_selection_tool_finalize (GObject *object)
+{
+  GimpEditSelectionTool *edit_select;
+
+  edit_select = GIMP_EDIT_SELECTION_TOOL (object);
+
+  g_free (edit_select->snap_line_directions);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
 static void
@@ -207,6 +250,12 @@ gimp_edit_selection_tool_start (GimpTool
   edit_select->x = edit_select->origx = coords->x - off_x;
   edit_select->y = edit_select->origy = coords->y - off_y;
 
+  /* Remember starting point for use in constrained movement */
+  edit_select->startx = coords->x;
+  edit_select->starty = coords->y;
+
+  edit_select->constrain  = FALSE;
+
   switch (edit_select->edit_mode)
     {
     case GIMP_TRANSLATE_MODE_CHANNEL:
@@ -542,6 +591,21 @@ gimp_edit_selection_tool_motion (GimpToo
 
   gimp_item_offsets (active_item, &off_x, &off_y);
 
+  if (edit_select->constrain)
+    {
+      GimpVector2 snapped_point;
+      GimpVector2 line_point = { edit_select->startx, edit_select->starty };
+      GimpVector2 point      = { coords->x,           coords->y           };
+
+      gimp_edit_selection_tool_snap_onto_lines (&snapped_point,
+                                                edit_select->snap_line_directions,
+                                                &line_point,
+                                                &point);
+
+      coords->x = snapped_point.x;
+      coords->y = snapped_point.y;
+    }
+
   motion_x = coords->x - off_x;
   motion_y = coords->y - off_y;
 
@@ -677,6 +741,18 @@ gimp_edit_selection_tool_motion (GimpToo
 }
 
 static void
+gimp_edit_selection_tool_active_modifier_key (GimpTool        *tool,
+                                              GdkModifierType  key,
+                                              gboolean         press,
+                                              GdkModifierType  state,
+                                              GimpDisplay     *display)
+{
+  GimpEditSelectionTool *edit_select = GIMP_EDIT_SELECTION_TOOL (tool);
+
+  edit_select->constrain = state & GDK_CONTROL_MASK ? TRUE : FALSE;
+}
+
+static void
 gimp_edit_selection_tool_draw (GimpDrawTool *draw_tool)
 {
   GimpEditSelectionTool *edit_select = GIMP_EDIT_SELECTION_TOOL (draw_tool);
@@ -872,6 +948,109 @@ gimp_edit_selection_tool_draw (GimpDrawT
   GIMP_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool);
 }
 
+/**
+ * gimp_edit_selection_tool_init_snap_line_vectors:
+ * @edit_select:
+ *
+ * Initializes the array that contains normalized vectors of the
+ * directions on which moving should be constrained.
+ **/
+static void
+gimp_edit_selection_tool_init_snap_line_vectors (GimpEditSelectionTool *edit_select)
+{
+  gint i;
+
+  edit_select->snap_line_directions = g_new0(GimpVector2, NBR_OF_SNAP_LINES);
+
+  for (i = 0; i < NBR_OF_SNAP_LINES; i++)
+    {
+      gdouble angle = i * G_PI / NBR_OF_SNAP_LINES;
+      gimp_vector2_set (&edit_select->snap_line_directions[i],
+                        cos (angle),
+                        sin (angle));
+    }
+}
+
+/**
+ * gimp_edit_selection_tool_snap_onto_lines:
+ * @snapped_point:
+ * @line_dir_vects:
+ * @line_point:
+ * @point:
+ *
+ * Snaps the given point onto the closest line, specified by a set of
+ * normalized direction vectors and the point that connects those
+ * lines.
+ **/
+static void
+gimp_edit_selection_tool_snap_onto_lines (GimpVector2       *snapped_point,
+                                          const GimpVector2 *line_dir_vects,
+                                          const GimpVector2 *line_point,
+                                          const GimpVector2 *point)
+{
+  gdouble shortest_dist_moved = G_MAXDOUBLE;
+  gint    i                   = 0;
+
+  for (i = 0; i < NBR_OF_SNAP_LINES; i++)
+    {
+      GimpVector2 constrained_point;
+      gdouble     dist_moved;
+
+      dist_moved = gimp_edit_selection_tool_point_to_line_distance (point,
+                                                                    line_point,
+                                                                    &line_dir_vects[i],
+                                                                    &constrained_point);
+      if (dist_moved < shortest_dist_moved)
+        {
+          shortest_dist_moved = dist_moved;
+
+          gimp_vector2_set (snapped_point,
+                            constrained_point.x,
+                            constrained_point.y);
+        }
+    }
+}
+
+/**
+ * gimp_edit_selection_tool_point_to_line_distance:
+ * @point:                     The point to calculate the distance for.
+ * @point_on_line:             A point on the line.
+ * @normalized_line_direction: Normalized line direction vector.
+ * @closest_line_point:        Gets set to the point on the line that is
+ *                             closest to @point. May be %NULL.
+ *
+ * Returns: The shortest distance from @point to the line defined by
+ *          @point_on_line and @normalized_line_direction.
+ **/
+static gdouble
+gimp_edit_selection_tool_point_to_line_distance (const GimpVector2 *point,
+                                                 const GimpVector2 *point_on_line,
+                                                 const GimpVector2 *normalized_line_direction,
+                                                 GimpVector2       *closest_line_point)
+{
+  GimpVector2 closest_line_point_dummy;
+  GimpVector2 distance_vector;
+  GimpVector2 a;
+  gdouble     d;
+
+  g_return_val_if_fail (point && point_on_line && normalized_line_direction,
+                        G_MAXDOUBLE);
+
+  if (! closest_line_point)
+    closest_line_point = &closest_line_point_dummy;
+
+  gimp_vector2_sub (&a, point, point_on_line);
+
+  d = gimp_vector2_inner_product (&a, normalized_line_direction);
+
+  *closest_line_point = gimp_vector2_add_val (*point_on_line,
+                                              gimp_vector2_mul_val (*normalized_line_direction, d));
+
+  gimp_vector2_sub (&distance_vector, closest_line_point, point);
+
+  return gimp_vector2_length (&distance_vector);
+}
+
 static gint
 process_event_queue_keys (GdkEventKey *kevent,
                           ... /* GdkKeyType, GdkModifierType, value ... 0 */)
Index: app/tools/gimpeditselectiontool.h
===================================================================
--- app/tools/gimpeditselectiontool.h	(revision 24492)
+++ app/tools/gimpeditselectiontool.h	(working copy)
@@ -53,6 +53,15 @@ struct _GimpEditSelectionTool
   gboolean            first_move;      /*  Don't push undos after the first  */
 
   gboolean            propagate_release;
+
+  gboolean            constrain;       /*  Constrain the movement            */
+  gdouble             startx, starty;  /*  Coords when button was pressed    */
+
+  /**
+   * An array of normalized vectors that defines the lines onto which
+   * moved objects will snap when Ctrl is held down.
+   */
+  GimpVector2        *snap_line_directions;
 };
 
 struct _GimpEditSelectionToolClass
