From 977adb47f2d06fc408eef4133b856a5254347061 Mon Sep 17 00:00:00 2001
From: MaxED <j.maxed@gmail.com>
Date: Fri, 29 Apr 2016 21:38:43 +0000
Subject: [PATCH] Added: holding Control key while switching to/from Visual
 mode will now temporarily disable position synchronization (works only when
 'Synchronize camera position between 2D and 3D modes' Preferences option is
 enabled). Fixed severe lock-up after finishing dragging many
 vertices/linedefs/sectors in Classic modes. Updated documentation
 ("Synchronizing camera position").

---
 .../gzdb/features/all_modes/synch_camera.html |  5 +-
 Source/Core/Controls/ScriptDocumentTab.cs     |  3 +-
 Source/Core/Editing/ClassicMode.cs            |  3 +-
 Source/Core/Geometry/Tools.cs                 |  2 +-
 Source/Core/Map/MapSet.cs                     |  4 +-
 Source/Core/VisualModes/VisualMode.cs         | 48 ++++++++++---------
 .../Core/Windows/PreferencesForm.Designer.cs  |  4 +-
 .../ClassicModes/DragLinedefsMode.cs          |  2 +-
 .../ClassicModes/DragSectorsMode.cs           |  2 +-
 .../ClassicModes/DragVerticesMode.cs          |  2 +-
 .../ClassicModes/EditSelectionMode.cs         |  2 +-
 .../BuilderModes/ClassicModes/ThingsMode.cs   |  6 +--
 .../ErrorChecks/ResultSectorInvalid.cs        |  2 +-
 .../BuilderModes/Resources/Actions.cfg        |  3 +-
 .../VisualModes/BaseVisualMode.cs             |  6 +--
 15 files changed, 49 insertions(+), 45 deletions(-)

diff --git a/Help/gzdb/features/all_modes/synch_camera.html b/Help/gzdb/features/all_modes/synch_camera.html
index 60b8db3cb..2fc50f9a1 100644
--- a/Help/gzdb/features/all_modes/synch_camera.html
+++ b/Help/gzdb/features/all_modes/synch_camera.html
@@ -27,6 +27,9 @@
   <p><img src="synch_cam3.jpg" alt="" width="740" height="515" /></p>
   <p>If you leave Visual mode now, the map will be centered at Visual camera's location:</p>
   <p><img src="synch_cam4.jpg" alt="" width="736" height="512" /></p>
-  <p>If you don't like this behaviour, you can disable it in the <a href="../../../w_preferences.html">Preferences window</a>.</p>
+  <p>If you don't like this behaviour, disable "<b>Synchronize camera position between 2D and 3D modes</b>" <a href="../../../w_preferences.html">Preferences</a> option.</p>
+	<h2>Additional keys:</h2>
+	<p>Holding <b>Shift</b> while switching to/from Visual mode will also synchronize selected map elements.<br />
+	Holding <b>Ctrl</b> while switching to/from Visual mode will temporarily disable camera synchronization (works only when "<b>Synchronize camera position between 2D and 3D modes</b>" <a href="../../../w_preferences.html">Preferences</a> option is enabled).</p>
 </div>
 </body>
diff --git a/Source/Core/Controls/ScriptDocumentTab.cs b/Source/Core/Controls/ScriptDocumentTab.cs
index eb9d076c5..b0ca1aa67 100644
--- a/Source/Core/Controls/ScriptDocumentTab.cs
+++ b/Source/Core/Controls/ScriptDocumentTab.cs
@@ -48,7 +48,6 @@ namespace CodeImp.DoomBuilder.Controls
 		
 		// The script edit control
 		protected readonly ScriptEditorControl editor;
-		private bool preventchanges; //mxd
 		private string title; //mxd
 
 		// Derived classes must set this!
@@ -437,7 +436,7 @@ namespace CodeImp.DoomBuilder.Controls
 		//mxd
 		private void functionbar_DropDown(object sender, EventArgs e) 
 		{
-			if(!preventchanges && editor.IsChanged) panel.ShowErrors(UpdateNavigator());
+			if(editor.IsChanged) panel.ShowErrors(UpdateNavigator());
 		}
 
 		//mxd
diff --git a/Source/Core/Editing/ClassicMode.cs b/Source/Core/Editing/ClassicMode.cs
index 6442946e3..2063fd09b 100644
--- a/Source/Core/Editing/ClassicMode.cs
+++ b/Source/Core/Editing/ClassicMode.cs
@@ -653,7 +653,8 @@ namespace CodeImp.DoomBuilder.Editing
 			if(renderer.StartOverlay(true))
 			{
 				//mxd. Center 2d view on camera position in 3d view
-				if(General.Settings.GZSynchCameras && General.Editing.PreviousMode != null && General.Editing.PreviousMode.IsSubclassOf(typeof (VisualMode)))
+				if(General.Settings.GZSynchCameras && !General.Interface.CtrlState 
+					&& General.Editing.PreviousMode != null && General.Editing.PreviousMode.IsSubclassOf(typeof(VisualMode)))
 				{
 					Vector2D campos = new Vector2D(General.Map.VisualCamera.Position.x, General.Map.VisualCamera.Position.y);
 					renderer2d.PositionView(campos.x, campos.y);
diff --git a/Source/Core/Geometry/Tools.cs b/Source/Core/Geometry/Tools.cs
index 9a4bf3951..4cd256afe 100644
--- a/Source/Core/Geometry/Tools.cs
+++ b/Source/Core/Geometry/Tools.cs
@@ -2254,7 +2254,7 @@ namespace CodeImp.DoomBuilder.Geometry
 		}
 
 		//mxd. Try to create/remove/reassign outer sidedefs. Selected linedefs and verts are marked
-		public static void AdjustOuterSidedefs(HashSet<Sector> selectedsectors, ICollection<Linedef> selectedlines)
+		public static void AdjustOuterSidedefs(HashSet<Sector> selectedsectors, HashSet<Linedef> selectedlines)
 		{
 			HashSet<Sidedef> outersides = new HashSet<Sidedef>();
 			HashSet<Linedef> singlesidedlines = new HashSet<Linedef>();
diff --git a/Source/Core/Map/MapSet.cs b/Source/Core/Map/MapSet.cs
index fe86c109e..f3aa7fa0f 100644
--- a/Source/Core/Map/MapSet.cs
+++ b/Source/Core/Map/MapSet.cs
@@ -2588,7 +2588,7 @@ namespace CodeImp.DoomBuilder.Map
 		}
 
 		/// <summary>mxd. This finds the line closest to the specified position excluding given list of linedefs.</summary>
-		public Linedef NearestLinedef(Vector2D pos, ICollection<Linedef> linesToExclude) 
+		public Linedef NearestLinedef(Vector2D pos, HashSet<Linedef> linesToExclude) 
 		{
 			Linedef closest = null;
 			float distance = float.MaxValue;
@@ -2596,7 +2596,7 @@ namespace CodeImp.DoomBuilder.Map
 			// Go for all linedefs in selection
 			foreach(Linedef l in linedefs) 
 			{
-				if(linesToExclude.Contains(l))	continue;
+				if(linesToExclude.Contains(l)) continue;
 				// Calculate distance and check if closer than previous find
 				float d = l.SafeDistanceToSq(pos, true);
 				if(d < distance) 
diff --git a/Source/Core/VisualModes/VisualMode.cs b/Source/Core/VisualModes/VisualMode.cs
index c9c70de05..0373721d9 100644
--- a/Source/Core/VisualModes/VisualMode.cs
+++ b/Source/Core/VisualModes/VisualMode.cs
@@ -170,33 +170,37 @@ namespace CodeImp.DoomBuilder.VisualModes
 			//mxd. Synch camera position to cursor position or center of the screen in 2d-mode
 			if(General.Settings.GZSynchCameras) 
 			{
-				//If initial position is inside or nearby a sector - adjust camera.z accordingly
-				float posz = General.Map.VisualCamera.Position.z;
-				Sector nearestsector = General.Map.Map.GetSectorByCoordinates(initialcameraposition, blockmap);
-
-				if(nearestsector == null)
+				// Keep previous camera position if Control is held and camera was previously moved in Visual mode
+				if(!General.Interface.CtrlState || General.Map.VisualCamera.Position.GetLengthSq() == 0)
 				{
-					Linedef nearestline = MapSet.NearestLinedef(General.Map.Map.Linedefs, initialcameraposition);
-					if(nearestline != null) 
+					//If initial position is inside or nearby a sector - adjust camera.z accordingly
+					float posz = General.Map.VisualCamera.Position.z;
+					Sector nearestsector = General.Map.Map.GetSectorByCoordinates(initialcameraposition, blockmap);
+
+					if(nearestsector == null)
 					{
-						float side = nearestline.SideOfLine(initialcameraposition);
-						Sidedef nearestside = (side < 0.0f ? nearestline.Front : nearestline.Back) ?? (side < 0.0f ? nearestline.Back : nearestline.Front);
-						if(nearestside != null) nearestsector = nearestside.Sector;
+						Linedef nearestline = MapSet.NearestLinedef(General.Map.Map.Linedefs, initialcameraposition);
+						if(nearestline != null)
+						{
+							float side = nearestline.SideOfLine(initialcameraposition);
+							Sidedef nearestside = (side < 0.0f ? nearestline.Front : nearestline.Back) ?? (side < 0.0f ? nearestline.Back : nearestline.Front);
+							if(nearestside != null) nearestsector = nearestside.Sector;
+						}
 					}
-				}
 
-				if(nearestsector != null) 
-				{
-					int sectorheight = nearestsector.CeilHeight - nearestsector.FloorHeight;
-					if(sectorheight < 41)
-						posz = nearestsector.FloorHeight + Math.Max(16, sectorheight / 2);
-					else if(General.Map.VisualCamera.Position.z < nearestsector.FloorHeight + 41) 
-						posz = nearestsector.FloorHeight + 41; // same as in doom
-					else if(General.Map.VisualCamera.Position.z > nearestsector.CeilHeight) 
-						posz = nearestsector.CeilHeight - 4;
-				}
+					if(nearestsector != null)
+					{
+						int sectorheight = nearestsector.CeilHeight - nearestsector.FloorHeight;
+						if(sectorheight < 41)
+							posz = nearestsector.FloorHeight + Math.Max(16, sectorheight / 2);
+						else if(General.Map.VisualCamera.Position.z < nearestsector.FloorHeight + 41)
+							posz = nearestsector.FloorHeight + 41; // same as in doom
+						else if(General.Map.VisualCamera.Position.z > nearestsector.CeilHeight)
+							posz = nearestsector.CeilHeight - 4;
+					}
 
-				General.Map.VisualCamera.Position = new Vector3D(initialcameraposition.x, initialcameraposition.y, posz);
+					General.Map.VisualCamera.Position = new Vector3D(initialcameraposition.x, initialcameraposition.y, posz);
+				}
 			} 
 			else 
 			{
diff --git a/Source/Core/Windows/PreferencesForm.Designer.cs b/Source/Core/Windows/PreferencesForm.Designer.cs
index 74e550679..492753e34 100644
--- a/Source/Core/Windows/PreferencesForm.Designer.cs
+++ b/Source/Core/Windows/PreferencesForm.Designer.cs
@@ -470,9 +470,9 @@ namespace CodeImp.DoomBuilder.Windows
 			this.cbSynchCameras.AutoSize = true;
 			this.cbSynchCameras.Location = new System.Drawing.Point(16, 371);
 			this.cbSynchCameras.Name = "cbSynchCameras";
-			this.cbSynchCameras.Size = new System.Drawing.Size(260, 17);
+			this.cbSynchCameras.Size = new System.Drawing.Size(294, 17);
 			this.cbSynchCameras.TabIndex = 12;
-			this.cbSynchCameras.Text = "Sync camera position between 2D and 3D modes";
+			this.cbSynchCameras.Text = "Synchronize camera position between 2D and 3D modes";
 			this.cbSynchCameras.UseVisualStyleBackColor = true;
 			// 
 			// showtexturesizes
diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragLinedefsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragLinedefsMode.cs
index ea9739179..1670d26e2 100644
--- a/Source/Plugins/BuilderModes/ClassicModes/DragLinedefsMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/DragLinedefsMode.cs
@@ -119,7 +119,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					HashSet<Sector> toadjust = General.Map.Map.GetUnselectedSectorsFromLinedefs(selectedlines);
 
 					// Process outer sidedefs
-					Tools.AdjustOuterSidedefs(toadjust, selectedlines);
+					Tools.AdjustOuterSidedefs(toadjust, new HashSet<Linedef>(selectedlines));
 				}
 
 				// If only a single linedef was selected, deselect it now
diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragSectorsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragSectorsMode.cs
index 1bbcec61d..4235b5f8b 100644
--- a/Source/Plugins/BuilderModes/ClassicModes/DragSectorsMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/DragSectorsMode.cs
@@ -128,7 +128,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					toadjust.UnionWith(General.Map.Map.GetUnselectedSectorsFromLinedefs(selectedlines));
 
 					// Process outer sidedefs
-					Tools.AdjustOuterSidedefs(toadjust, selectedlines);
+					Tools.AdjustOuterSidedefs(toadjust, new HashSet<Linedef>(selectedlines));
 				}
 
 				// If only a single sector was selected, deselect it now
diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragVerticesMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragVerticesMode.cs
index a455326da..59f6a963b 100644
--- a/Source/Plugins/BuilderModes/ClassicModes/DragVerticesMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/DragVerticesMode.cs
@@ -103,7 +103,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 				{
 					// Add sectors, which have all their linedefs selected
 					// (otherwise those would be destroyed after moving the selection)
-					ICollection<Linedef> selectedlines = General.Map.Map.LinedefsFromMarkedVertices(false, true, false);
+					HashSet<Linedef> selectedlines = new HashSet<Linedef>(General.Map.Map.LinedefsFromMarkedVertices(false, true, false));
 					HashSet<Sector> toadjust = General.Map.Map.GetUnselectedSectorsFromLinedefs(selectedlines);
 
 					// Process outer sidedefs
diff --git a/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs b/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs
index 6d57ac5d3..1d8c63c01 100644
--- a/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs
@@ -1485,7 +1485,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					// Update outer sides of the selection
 					HashSet<Sector> affectedsectors = new HashSet<Sector>(General.Map.Map.GetSelectedSectors(true));
 					affectedsectors.UnionWith(General.Map.Map.GetUnselectedSectorsFromLinedefs(selectedlines));
-					Tools.AdjustOuterSidedefs(affectedsectors, selectedlines);
+					Tools.AdjustOuterSidedefs(affectedsectors, new HashSet<Linedef>(selectedlines));
 				}
 
 				// Stitch geometry
diff --git a/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs
index 34ece983c..de1892363 100644
--- a/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs
@@ -1428,11 +1428,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			}
 
 			//align things
-			int thingsCount = General.Map.Map.Things.Count;
-
 			foreach(Thing t in toAlign) 
 			{
-				List<Linedef> excludedLines = new List<Linedef>();
+				HashSet<Linedef> excludedLines = new HashSet<Linedef>();
 				bool aligned;
 
 				do
@@ -1443,7 +1441,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					if(!aligned) 
 					{
 						excludedLines.Add(l);
-						if(excludedLines.Count == thingsCount) 
+						if(excludedLines.Count == General.Map.Map.Linedefs.Count) 
 						{
 							ThingTypeInfo tti = General.Map.Data.GetThingInfo(t.Type);
 							General.ErrorLogger.Add(ErrorType.Warning, "Unable to align " + tti.Title + " (index " + t.Index + ") to any linedef!");
diff --git a/Source/Plugins/BuilderModes/ErrorChecks/ResultSectorInvalid.cs b/Source/Plugins/BuilderModes/ErrorChecks/ResultSectorInvalid.cs
index 736a11bac..efe98c1aa 100644
--- a/Source/Plugins/BuilderModes/ErrorChecks/ResultSectorInvalid.cs
+++ b/Source/Plugins/BuilderModes/ErrorChecks/ResultSectorInvalid.cs
@@ -100,7 +100,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 				if(!merged)
 				{
-					List<Linedef> sectorlines = new List<Linedef>(sector.Sidedefs.Count);
+					HashSet<Linedef> sectorlines = new HashSet<Linedef>();
 					foreach(Sidedef side in sector.Sidedefs) sectorlines.Add(side.Line);
 
 					if(sectorlines.Count > 0)
diff --git a/Source/Plugins/BuilderModes/Resources/Actions.cfg b/Source/Plugins/BuilderModes/Resources/Actions.cfg
index 04ca21885..fe2d12b8a 100644
--- a/Source/Plugins/BuilderModes/Resources/Actions.cfg
+++ b/Source/Plugins/BuilderModes/Resources/Actions.cfg
@@ -1266,11 +1266,12 @@ gzdbvisualmode
 {
 	title = "GZDB Visual Mode";
 	category = "modes";
-	description = "Switches to the (G)ZDoom visual editing mode. Hold Shift key to invert 'Synhcronise selection between Visual and Classic modes' setting.";
+	description = "Switches to the (G)ZDoom visual editing mode. Hold Shift key to invert 'Synhcronise selection between Visual and Classic modes' setting. Hold Control key to disable position synchronization when 'Synchronize camera position between 2D and 3D modes' Preferences option is enabled.";
 	allowkeys = true;
 	allowmouse = true;
 	allowscroll = true;
 	disregardshift = true;
+	disregardcontrol = true;
 	default = 81; //Q
 }
 
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
index 5842c1dde..f283da49d 100644
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
@@ -3365,11 +3365,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			}
 
 			//align things
-			int thingsCount = General.Map.Map.Things.Count;
-
 			foreach(Thing t in things) 
 			{
-				List<Linedef> excludedLines = new List<Linedef>();
+				HashSet<Linedef> excludedLines = new HashSet<Linedef>();
 				bool aligned;
 
 				do 
@@ -3381,7 +3379,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					{
 						excludedLines.Add(l);
 
-						if(excludedLines.Count == thingsCount) 
+						if(excludedLines.Count == General.Map.Map.Linedefs.Count) 
 						{
 							ThingTypeInfo tti = General.Map.Data.GetThingInfo(t.Type);
 							General.ErrorLogger.Add(ErrorType.Warning, "Unable to align " + tti.Title + " (index " + t.Index + ") to any linedef!");
-- 
GitLab