diff --git a/Source/Plugins/BuilderModes/Resources/Actions.cfg b/Source/Plugins/BuilderModes/Resources/Actions.cfg
index 34f1c03915376067befb1b601a951fe3206c9f4a..f5328e9f57cef3c1d24395ca4de8a6f282815bbb 100755
--- a/Source/Plugins/BuilderModes/Resources/Actions.cfg
+++ b/Source/Plugins/BuilderModes/Resources/Actions.cfg
@@ -1414,4 +1414,16 @@ togglevisualslopepicking
 	allowkeys = true;
 	allowmouse = true;
 	allowscroll = false;
+	default = 65623; // Shift-W
+}
+
+slopebetweenhandles
+{
+	title = "Slope Between Handles";
+	category = "visual";
+	description = "Slopes the selected floors and ceilings between the selected slope handles.";
+	allowkeys = true;
+	allowmouse = true;
+	allowscroll = false;
+	default = 131142; // Ctrl-F
 }
\ No newline at end of file
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
index fd51b8ea9ebb78981f974f709ac69273db24e433..5a1b4ff73cdf5a5bfac8a381128d2474ce822ce1 100755
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
@@ -2080,6 +2080,32 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 			return verts;
 		}
+
+		// This returns all selected slope handles, no doubles
+		private List<VisualSidedefSlope> GetSelectedSlopeHandles()
+		{
+			HashSet<VisualSidedefSlope> added = new HashSet<VisualSidedefSlope>();
+			List<VisualSidedefSlope> handles = new List<VisualSidedefSlope>();
+
+			foreach(IVisualEventReceiver i in selectedobjects)
+			{
+				VisualSidedefSlope handle = i as VisualSidedefSlope;
+				if(handle != null && !added.Contains(handle))
+				{
+					handles.Add(handle);
+					added.Add(handle);
+				}
+			}
+
+			// Add highlight?
+			if((selectedobjects.Count == 0) && (target.picked is VisualSidedefSlope))
+			{
+				VisualSidedefSlope handle = (VisualSidedefSlope)target.picked;
+				if (!added.Contains(handle)) handles.Add(handle);
+			}
+
+			return handles;
+		}
 		
 		// This returns the IVisualEventReceiver on which the action must be performed
 		private IVisualEventReceiver GetTargetEventReceiver(bool targetonly)
@@ -3980,6 +4006,43 @@ namespace CodeImp.DoomBuilder.BuilderModes
 				pickingmode = PickingMode.Default;
 		}
 
+		[BeginAction("slopebetweenhandles")]
+		public void SlopeBetweenHandles()
+		{
+			List<VisualSidedefSlope> handles = GetSelectedSlopeHandles();
+			if(handles.Count != 2)
+			{
+				General.Interface.DisplayStatus(StatusType.Warning, "You need to have exactly two slope handles selected to slope between them.");
+				return;
+			}
+
+			List<IVisualEventReceiver> selectedsectors = GetSelectedObjects(true, false, false, false, false);
+			if(selectedsectors.Count == 0)
+			{
+				General.Interface.DisplayStatus(StatusType.Warning, "You need to select floors or ceilings to slope between slope handles.");
+				return;
+			}
+
+			General.Map.UndoRedo.CreateUndo("Slope between slope handles");
+
+			// Create the new plane
+			Vector3D p1 = new Vector3D(handles[0].Sidedef.Line.Start.Position, handles[0].Level.plane.GetZ(handles[0].Sidedef.Line.Start.Position));
+			Vector3D p2 = new Vector3D(handles[0].Sidedef.Line.End.Position, handles[0].Level.plane.GetZ(handles[0].Sidedef.Line.End.Position));
+			Vector3D p3 = new Vector3D(handles[1].Sidedef.Line.Line.GetCoordinatesAt(0.5f), handles[1].Level.plane.GetZ(handles[1].Sidedef.Line.Line.GetCoordinatesAt(0.5f)));
+			Plane plane = new Plane(p1, p2, p3, true);
+
+			// Apply slope
+			foreach (BaseVisualGeometrySector bvgs in selectedsectors)
+			{
+				VisualSidedefSlope.ApplySlope(bvgs.Level, plane, this);
+				bvgs.Sector.UpdateSectorGeometry(true);
+			}
+
+			UpdateChangedObjects();
+
+			General.Interface.DisplayStatus(StatusType.Action, "Sloped between slope handles.");
+		}
+
 		#endregion
 
 		#region ================== Texture Alignment
diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualSidedefSlope.cs b/Source/Plugins/BuilderModes/VisualModes/VisualSidedefSlope.cs
index c66607272a81cbf517cc528de5bae683d441b614..ce0778531213f6afddc2914a21486975ba944920 100644
--- a/Source/Plugins/BuilderModes/VisualModes/VisualSidedefSlope.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/VisualSidedefSlope.cs
@@ -213,6 +213,54 @@ namespace CodeImp.DoomBuilder.VisualModes
 			return handle;
 		}
 
+		public static void ApplySlope(SectorLevel level, Plane plane, BaseVisualMode mode)
+		{
+			bool applytoceiling = false;
+
+			Vector2D center = new Vector2D(level.sector.BBox.X + level.sector.BBox.Width / 2,
+											   level.sector.BBox.Y + level.sector.BBox.Height / 2);
+
+			if (level.extrafloor)
+			{
+				// The top side of 3D floors is the ceiling of the sector, but it's a "floor" in UDB, so the
+				// ceiling of the control sector has to be modified
+				if (level.type == SectorLevelType.Floor)
+					applytoceiling = true;
+			}
+			else
+			{
+				if (level.type == SectorLevelType.Ceiling)
+					applytoceiling = true;
+			}
+
+			if (applytoceiling)
+			{
+				Plane downplane = plane.GetInverted();
+				level.sector.CeilSlope = downplane.Normal;
+				level.sector.CeilSlopeOffset = downplane.Offset;
+				level.sector.CeilHeight = (int)new Plane(level.sector.CeilSlope, level.sector.CeilSlopeOffset).GetZ(center);
+			}
+			else
+			{
+				level.sector.FloorSlope = plane.Normal;
+				level.sector.FloorSlopeOffset = plane.Offset;
+				level.sector.FloorHeight = (int)new Plane(level.sector.FloorSlope, level.sector.FloorSlopeOffset).GetZ(center);
+			}
+
+			// Rebuild sector
+			BaseVisualSector vs;
+			if (mode.VisualSectorExists(level.sector))
+			{
+				vs = (BaseVisualSector)mode.GetVisualSector(level.sector);
+			}
+			else
+			{
+				vs = mode.CreateBaseVisualSector(level.sector);
+			}
+
+			if (vs != null) vs.UpdateSectorGeometry(true);
+		}
+
 		#endregion
 
 		#region ================== Events
@@ -274,51 +322,7 @@ namespace CodeImp.DoomBuilder.VisualModes
 
 			// Apply slope to surfaces
 			foreach (SectorLevel l in levels)
-			{
-				bool applytoceiling = false;
-				Vector2D center = new Vector2D(l.sector.BBox.X + l.sector.BBox.Width / 2,
-												   l.sector.BBox.Y + l.sector.BBox.Height / 2);
-				
-				if(l.extrafloor)
-				{
-					// The top side of 3D floors is the ceiling of the sector, but it's a "floor" in UDB, so the
-					// ceiling of the control sector has to be modified
-					if (l.type == SectorLevelType.Floor)
-						applytoceiling = true;
-				}
-				else
-				{
-					if (l.type == SectorLevelType.Ceiling)
-						applytoceiling = true;
-				}
-
-				if (applytoceiling)
-				{
-					Plane downplane = plane.GetInverted();
-					l.sector.CeilSlope = downplane.Normal;
-					l.sector.CeilSlopeOffset = downplane.Offset;
-					l.sector.CeilHeight = (int)new Plane(l.sector.CeilSlope, l.sector.CeilSlopeOffset).GetZ(center);
-				}
-				else
-				{
-					l.sector.FloorSlope = plane.Normal;
-					l.sector.FloorSlopeOffset = plane.Offset;
-					l.sector.FloorHeight = (int)new Plane(l.sector.FloorSlope, l.sector.FloorSlopeOffset).GetZ(center);
-				}
-
-				// Rebuild sector
-				BaseVisualSector vs;
-				if (mode.VisualSectorExists(l.sector))
-				{
-					vs = (BaseVisualSector)mode.GetVisualSector(l.sector);
-				}
-				else
-				{
-					vs = mode.CreateBaseVisualSector(l.sector);
-				}
-
-				if (vs != null) vs.UpdateSectorGeometry(true);
-			}
+				ApplySlope(l, plane, mode);
 
 			mode.SetActionResult("Changed slope.");
 		}