From 6c1dbc0bf3b434ea1aac84d93cd5def670fbab38 Mon Sep 17 00:00:00 2001
From: MaxED <j.maxed@gmail.com>
Date: Wed, 5 Nov 2014 12:32:48 +0000
Subject: [PATCH] Find and Replace mode: "Find Any Texture or Flat", "Find
 Sector Flat" and "Find Sidedef Texture" options now support "*" (match any
 number of characters) and "?" (match any single character) wildcards. "Find
 Any Texture or Flat" is now available any game configuration (previously it
 was disabled when "MixTexturesFlats" game configuration option was disabled,
 now only "Replace" functionality is disabled). "Find Thing Flags" and "Find
 Linedef Flags" can now replace flags. Added "Find Sidedef Flags" and "Find
 Sector Flags" search options.

---
 Source/Core/Config/MatchingTextureSet.cs      |  10 +-
 .../Plugins/BuilderModes/BuilderModes.csproj  |   3 +
 .../ClassicModes/FindReplaceMode.cs           |   2 +-
 .../FindReplace/BaseFindSidedef.cs            |  51 ++++++++
 .../FindReplace/FindAnyTextureFlat.cs         |  68 +++++-----
 .../FindReplace/FindLinedefFlags.cs           |  58 +++++++--
 .../FindReplace/FindLinedefNumber.cs          |  10 +-
 .../FindReplace/FindLinedefSectorRef.cs       |   6 +-
 .../FindReplace/FindLinedefTags.cs            |   6 +-
 .../FindReplace/FindLinedefThingRef.cs        |   6 +-
 .../FindReplace/FindLinedefTypes.cs           |   6 +-
 .../FindReplace/FindReplaceAttribute.cs       |   3 -
 .../FindReplace/FindReplaceType.cs            |  15 ++-
 .../FindReplace/FindSectorBrightness.cs       |   8 +-
 .../FindReplace/FindSectorCeilingHeight.cs    |   8 +-
 .../FindReplace/FindSectorEffect.cs           |   6 +-
 .../FindReplace/FindSectorFlags.cs            | 121 ++++++++++++++++++
 .../FindReplace/FindSectorFlat.cs             |  37 +++---
 .../FindReplace/FindSectorFloorHeight.cs      |   8 +-
 .../FindReplace/FindSectorNumber.cs           |  10 +-
 .../FindReplace/FindSectorTags.cs             |   6 +-
 .../FindReplace/FindSidedefFlags.cs           | 118 +++++++++++++++++
 .../FindReplace/FindSidedefNumber.cs          |  58 +--------
 .../FindReplace/FindSidedefTexture.cs         |  91 +++----------
 .../FindReplace/FindThingAction.cs            |   8 +-
 .../FindReplace/FindThingAngle.cs             |   6 +-
 .../FindReplace/FindThingFlags.cs             |  58 ++++++++-
 .../FindReplace/FindThingNumber.cs            |  10 +-
 .../FindReplace/FindThingSectorRef.cs         |   6 +-
 .../BuilderModes/FindReplace/FindThingTag.cs  |   6 +-
 .../FindReplace/FindThingThingRef.cs          |   6 +-
 .../BuilderModes/FindReplace/FindThingType.cs |   6 +-
 .../FindReplace/FindVertexNumber.cs           |  13 +-
 .../Interface/FindReplaceForm.Designer.cs     |   6 +-
 .../BuilderModes/Interface/FindReplaceForm.cs |  17 ++-
 .../Interface/FindReplaceForm.resx            |   6 +
 36 files changed, 584 insertions(+), 279 deletions(-)
 create mode 100644 Source/Plugins/BuilderModes/FindReplace/BaseFindSidedef.cs
 create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindSectorFlags.cs
 create mode 100644 Source/Plugins/BuilderModes/FindReplace/FindSidedefFlags.cs

diff --git a/Source/Core/Config/MatchingTextureSet.cs b/Source/Core/Config/MatchingTextureSet.cs
index 5ebfc25a6..41cebca31 100644
--- a/Source/Core/Config/MatchingTextureSet.cs
+++ b/Source/Core/Config/MatchingTextureSet.cs
@@ -26,7 +26,7 @@ using System.Text.RegularExpressions;
 
 namespace CodeImp.DoomBuilder.Config
 {
-	internal sealed class MatchingTextureSet : TextureSet, IFilledTextureSet, IComparable<MatchingTextureSet>
+	public sealed class MatchingTextureSet : TextureSet, IFilledTextureSet, IComparable<MatchingTextureSet>
 	{
 		#region ================== Variables
 		
@@ -58,7 +58,7 @@ namespace CodeImp.DoomBuilder.Config
 		}
 		
 		// Texture set from defined set
-		public MatchingTextureSet(DefinedTextureSet definedset)
+		internal MatchingTextureSet(DefinedTextureSet definedset)
 		{
 			// Copy the name
 			this.name = definedset.Name;
@@ -177,6 +177,12 @@ namespace CodeImp.DoomBuilder.Config
 			return regex.IsMatch(image.Name.ToUpperInvariant());
 		}
 
+		// This only checks if the given texture name is a match (mxd)
+		public bool IsMatch(string texturename) 
+		{
+			return regex.IsMatch(texturename.ToUpperInvariant());
+		}
+
 		// This compares it for sorting
 		public int CompareTo(MatchingTextureSet other)
 		{
diff --git a/Source/Plugins/BuilderModes/BuilderModes.csproj b/Source/Plugins/BuilderModes/BuilderModes.csproj
index a3ffaa65e..de4d31d4c 100644
--- a/Source/Plugins/BuilderModes/BuilderModes.csproj
+++ b/Source/Plugins/BuilderModes/BuilderModes.csproj
@@ -254,11 +254,14 @@
     <Compile Include="ErrorChecks\ResultVertexOverlappingVertex.cs" />
     <Compile Include="FindReplace\BaseFindLinedef.cs" />
     <Compile Include="FindReplace\BaseFindSector.cs" />
+    <Compile Include="FindReplace\BaseFindSidedef.cs" />
     <Compile Include="FindReplace\BaseFindThing.cs" />
     <Compile Include="FindReplace\FindLinedefFlags.cs" />
     <Compile Include="FindReplace\FindSectorBrightness.cs" />
     <Compile Include="FindReplace\FindSectorCeilingHeight.cs" />
+    <Compile Include="FindReplace\FindSectorFlags.cs" />
     <Compile Include="FindReplace\FindSectorFloorHeight.cs" />
+    <Compile Include="FindReplace\FindSidedefFlags.cs" />
     <Compile Include="FindReplace\FindThingAngle.cs" />
     <Compile Include="FindReplace\FindAnyTextureFlat.cs" />
     <Compile Include="FindReplace\FindThingFlags.cs" />
diff --git a/Source/Plugins/BuilderModes/ClassicModes/FindReplaceMode.cs b/Source/Plugins/BuilderModes/ClassicModes/FindReplaceMode.cs
index 94400d50e..d11c68621 100644
--- a/Source/Plugins/BuilderModes/ClassicModes/FindReplaceMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/FindReplaceMode.cs
@@ -25,7 +25,7 @@ using CodeImp.DoomBuilder.Editing;
 
 namespace CodeImp.DoomBuilder.BuilderModes
 {
-	[EditMode(DisplayName = "Find & Replace Mode",
+	[EditMode(DisplayName = "Find and Replace Mode",
 			  SwitchAction = "findmode",
 			  ButtonImage = "FindMode.png",
 			  ButtonOrder = 100,
diff --git a/Source/Plugins/BuilderModes/FindReplace/BaseFindSidedef.cs b/Source/Plugins/BuilderModes/FindReplace/BaseFindSidedef.cs
new file mode 100644
index 000000000..60abeeefc
--- /dev/null
+++ b/Source/Plugins/BuilderModes/FindReplace/BaseFindSidedef.cs
@@ -0,0 +1,51 @@
+#region ================== Namespaces
+
+using System.Collections.Generic;
+using CodeImp.DoomBuilder.Map;
+using CodeImp.DoomBuilder.Rendering;
+
+#endregion
+
+namespace CodeImp.DoomBuilder.BuilderModes
+{
+	//mxd. Encapsulates boring stuff
+	internal class BaseFindSidedef : FindReplaceType
+	{
+		#region ================== Methods
+
+		// This is called when a specific object is selected from the list
+		public override void ObjectSelected(FindReplaceObject[] selection) 
+		{
+			if (selection.Length == 1)
+			{
+				ZoomToSelection(selection);
+				General.Interface.ShowLinedefInfo(selection[0].Sidedef.Line);
+			}
+			else
+			{
+				General.Interface.HideInfo();
+			}
+
+			General.Map.Map.ClearAllSelected();
+			foreach(FindReplaceObject obj in selection) obj.Sidedef.Line.Selected = true;
+		}
+
+		// Render selection
+		public override void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection) 
+		{
+			foreach(FindReplaceObject o in selection) {
+				renderer.PlotLinedef(o.Sidedef.Line, General.Colors.Selection);
+			}
+		}
+
+		// Edit objects
+		public override void EditObjects(FindReplaceObject[] selection) 
+		{
+			List<Linedef> linedefs = new List<Linedef>(selection.Length);
+			foreach(FindReplaceObject o in selection) linedefs.Add(o.Sidedef.Line);
+			General.Interface.ShowEditLinedefs(linedefs);
+		}
+
+		#endregion
+	}
+}
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindAnyTextureFlat.cs b/Source/Plugins/BuilderModes/FindReplace/FindAnyTextureFlat.cs
index 524b5e3d3..8c17907fe 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindAnyTextureFlat.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindAnyTextureFlat.cs
@@ -17,8 +17,9 @@
 #region ================== Namespaces
 
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
 using System.Windows.Forms;
-using CodeImp.DoomBuilder.IO;
+using CodeImp.DoomBuilder.Config;
 using CodeImp.DoomBuilder.Map;
 using CodeImp.DoomBuilder.Rendering;
 using System.Drawing;
@@ -50,13 +51,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		#region ================== Methods
 
-		// This is called to test if the item should be displayed
-		public override bool DetermineVisiblity()
+		//mxd. 
+		public override bool CanReplace()
 		{
 			return General.Map.Config.MixTexturesFlats;
 		}
 
-
 		// This is called when the browse button is pressed
 		public override string Browse(string initialvalue)
 		{
@@ -67,25 +67,20 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
-			if(replacewith != null)
+			if(replace && (string.IsNullOrEmpty(replacewith) || replacewith.Length > 8))
 			{
-				// If it cannot be interpreted, set replacewith to null (not replacing at all)
-				if(replacewith.Length < 0) replacewith = null; //mxd. WUT?
-				if(replacewith.Length > 8) replacewith = null;
-				if(replacewith == null)
-				{
-					MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error);
-					return objs.ToArray();
-				}
+				MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error);
+				return objs.ToArray();
 			}
 
 			// Interpret the find
-			long longfind = Lump.MakeLongName(value.Trim());
+			bool isregex = (value.IndexOf('*') != -1 || value.IndexOf('?') != -1); //mxd
+			MatchingTextureSet set = new MatchingTextureSet(new Collection<string> { value.Trim() }); //mxd
 			
 			// Where to search?
 			ICollection<Sector> seclist = withinselection ? General.Map.Map.GetSelectedSectors(true) : General.Map.Map.Sectors;
@@ -95,18 +90,18 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			foreach(Sector s in seclist)
 			{
 				// Flat matches?
-				if(s.LongCeilTexture == longfind)
+				if(set.IsMatch(s.CeilTexture))
 				{
 					// Replace and add to list
-					if(replacewith != null) s.SetCeilTexture(replacewith);
-					objs.Add(new FindReplaceObject(s, "Sector " + s.Index + " (ceiling)"));
+					if(replace) s.SetCeilTexture(replacewith);
+					objs.Add(new FindReplaceObject(s, "Sector " + s.Index + " (ceiling)" + (isregex ? " - " + s.CeilTexture : null)));
 				}
 
-				if(s.LongFloorTexture == longfind)
+				if(set.IsMatch(s.FloorTexture))
 				{
 					// Replace and add to list
-					if(replacewith != null) s.SetFloorTexture(replacewith);
-					objs.Add(new FindReplaceObject(s, "Sector " + s.Index + " (floor)"));
+					if(replace) s.SetFloorTexture(replacewith);
+					objs.Add(new FindReplaceObject(s, "Sector " + s.Index + " (floor)" + (isregex ? " - " + s.FloorTexture : null)));
 				}
 			}
 			
@@ -115,30 +110,31 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			{
 				string side = sd.IsFront ? "front" : "back";
 				
-				if(sd.LongHighTexture == longfind)
+				if(set.IsMatch(sd.HighTexture))
 				{
 					// Replace and add to list
-					if(replacewith != null) sd.SetTextureHigh(replacewith);
-					objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", high)"));
+					if(replace) sd.SetTextureHigh(replacewith);
+					objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", high)" + (isregex ? " - " + sd.HighTexture : null)));
 				}
 				
-				if(sd.LongMiddleTexture == longfind)
+				if(set.IsMatch(sd.MiddleTexture))
 				{
 					// Replace and add to list
-					if(replacewith != null) sd.SetTextureMid(replacewith);
-					objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", middle)"));
+					if(replace) sd.SetTextureMid(replacewith);
+					objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", middle)" + (isregex ? " - " + sd.MiddleTexture : null)));
 				}
 				
-				if(sd.LongLowTexture == longfind)
+				if(set.IsMatch(sd.LowTexture))
 				{
 					// Replace and add to list
-					if(replacewith != null) sd.SetTextureLow(replacewith);
-					objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", low)"));
+					if(replace) sd.SetTextureLow(replacewith);
+					objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", low)" + (isregex ? " - " + sd.LowTexture : null)));
 				}
 			}
 			
 			// When replacing, make sure we keep track of used textures
-			if(replacewith != null) {
+			if(replace)
+			{
 				General.Map.Data.UpdateUsedTextures();
 				General.Map.Map.Update(); //mxd. And don't forget to update the view itself
 				General.Map.IsChanged = true;
@@ -191,14 +187,14 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		}
 
 		//mxd
-		public override void RenderOverlaySelection(IRenderer2D renderer, FindReplaceObject[] selection) {
+		public override void RenderOverlaySelection(IRenderer2D renderer, FindReplaceObject[] selection) 
+		{
 			if(!BuilderPlug.Me.UseHighlight) return;
 
 			int color = General.Colors.Selection.WithAlpha(64).ToInt();
-			foreach(FindReplaceObject o in selection) {
-				if(o.Object is Sector) {
-					renderer.RenderHighlight(o.Sector.FlatVertices, color);
-				}
+			foreach(FindReplaceObject o in selection) 
+			{
+				if(o.Object is Sector) renderer.RenderHighlight(o.Sector.FlatVertices, color);
 			}
 		}
 
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindLinedefFlags.cs b/Source/Plugins/BuilderModes/FindReplace/FindLinedefFlags.cs
index b8545af6f..0122f10e0 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindLinedefFlags.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindLinedefFlags.cs
@@ -16,18 +16,19 @@
 
 #region ================== Namespaces
 
+using System;
 using System.Collections.Generic;
-using System.Windows.Forms;
-using CodeImp.DoomBuilder.Windows;
-using CodeImp.DoomBuilder.Map;
 using System.Drawing;
+using System.Windows.Forms;
 using CodeImp.DoomBuilder.Config;
+using CodeImp.DoomBuilder.Map;
+using CodeImp.DoomBuilder.Windows;
 
 #endregion
 
 namespace CodeImp.DoomBuilder.BuilderModes
 {
-	[FindReplace("Linedef Flags", BrowseButton = true, Replacable = false)]
+	[FindReplace("Linedef Flags", BrowseButton = true)]
 	internal class FindLinedefFlags : BaseFindLinedef
 	{
 		#region ================== Constants
@@ -56,29 +57,56 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			return FlagsForm.ShowDialog(Form.ActiveForm, initialvalue, General.Map.Config.LinedefFlags);
 		}
 
-
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Where to search?
 			ICollection<Linedef> list = withinselection ? General.Map.Map.GetSelectedLinedefs(true) : General.Map.Map.Linedefs;
 
+			// Find what? (mxd)
+			List<string> findflagslist = new List<string>();
+			foreach(string flag in value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) 
+			{
+				string f = flag.Trim();
+				if(General.Map.Config.LinedefFlags.ContainsKey(f)) findflagslist.Add(f);
+			}
+			if(findflagslist.Count == 0) 
+			{
+				MessageBox.Show("Invalid value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error);
+				return objs.ToArray();
+			}
+
+			// Replace with what? (mxd)
+			List<string> replaceflagslist = new List<string>();
+			if(replace) 
+			{
+				string[] replaceflags = replacewith.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+				foreach(string flag in replaceflags) 
+				{
+					string f = flag.Trim();
+					if (!General.Map.Config.LinedefFlags.ContainsKey(f))
+					{
+						MessageBox.Show("Invalid replace value '" + f + "' for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error);
+						return objs.ToArray();
+					}
+					replaceflagslist.Add(f);
+				}
+			}
+
 			// Go for all linedefs
 			foreach (Linedef l in list)
 			{
 				bool match = true;
 
 				// Parse the value string...
-				foreach (string s in value.Split(','))
+				foreach(string flag in findflagslist)
 				{
-					string str = s.Trim();
-
 					// ... and check if the flags don't match
-					if (General.Map.Config.LinedefFlags.ContainsKey(str) && !l.IsFlagSet(str))
+					if(!l.IsFlagSet(flag))
 					{
 						match = false;
 						break;
@@ -88,6 +116,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
 				// Flags matches?
 				if (match)
 				{
+					// Replace flags (mxd)
+					if(replace) 
+					{
+						// Clear all flags
+						l.ClearFlags();
+
+						// Set new flags
+						foreach(string flag in replaceflagslist) l.SetFlag(flag, true);
+					}
+					
 					// Add to list
 					LinedefActionInfo info = General.Map.Config.GetLinedefActionInfo(l.Action);
 					if (!info.IsNull)
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindLinedefNumber.cs b/Source/Plugins/BuilderModes/FindReplace/FindLinedefNumber.cs
index c373a49a8..cc2188124 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindLinedefNumber.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindLinedefNumber.cs
@@ -24,7 +24,7 @@ using CodeImp.DoomBuilder.Config;
 
 namespace CodeImp.DoomBuilder.BuilderModes
 {
-	[FindReplace("Linedef Index", BrowseButton = false, Replacable = false)]
+	[FindReplace("Linedef Index", BrowseButton = false)]
 	internal class FindLinedefNumber : BaseFindLinedef
 	{
 		#region ================== Constants
@@ -45,10 +45,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		#region ================== Methods
 
+		//mxd
+		public override bool CanReplace() 
+		{
+			return false;
+		}
+
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindLinedefSectorRef.cs b/Source/Plugins/BuilderModes/FindReplace/FindLinedefSectorRef.cs
index 98dc2e20e..973de0792 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindLinedefSectorRef.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindLinedefSectorRef.cs
@@ -56,13 +56,13 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
 			int replacetag = 0;
-			if(replacewith != null)
+			if(replace)
 			{
 				// If it cannot be interpreted, set replacewith to null (not replacing at all)
 				if(!int.TryParse(replacewith, out replacetag)) replacewith = null;
@@ -99,7 +99,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 								if(l.Args[i] == tag)
 								{
 									// Replace
-									if(replacewith != null) l.Args[i] = replacetag;
+									if(replace) l.Args[i] = replacetag;
 									addline = true;
 								}
 							}
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindLinedefTags.cs b/Source/Plugins/BuilderModes/FindReplace/FindLinedefTags.cs
index 03c78bb89..dbfaedaa5 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindLinedefTags.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindLinedefTags.cs
@@ -55,13 +55,13 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
 			int replacetag = 0;
-			if(replacewith != null)
+			if(replace)
 			{
 				// If it cannot be interpreted, set replacewith to null (not replacing at all)
 				if(!int.TryParse(replacewith, out replacetag)) replacewith = null;
@@ -88,7 +88,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					if(l.Tag == tag)
 					{
 						// Replace
-						if(replacewith != null) l.Tag = replacetag;
+						if(replace) l.Tag = replacetag;
 
 						// Add to list
 						LinedefActionInfo info = General.Map.Config.GetLinedefActionInfo(l.Action);
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindLinedefThingRef.cs b/Source/Plugins/BuilderModes/FindReplace/FindLinedefThingRef.cs
index c48441902..e14bffa29 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindLinedefThingRef.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindLinedefThingRef.cs
@@ -56,13 +56,13 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
 			int replacetag = 0;
-			if(replacewith != null)
+			if(replace)
 			{
 				// If it cannot be interpreted, set replacewith to null (not replacing at all)
 				if(!int.TryParse(replacewith, out replacetag)) replacewith = null;
@@ -99,7 +99,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 								if(l.Args[i] == tag)
 								{
 									// Replace
-									if(replacewith != null) l.Args[i] = replacetag;
+									if(replace) l.Args[i] = replacetag;
 									addline = true;
 								}
 							}
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindLinedefTypes.cs b/Source/Plugins/BuilderModes/FindReplace/FindLinedefTypes.cs
index 19bc54d9b..2ffa9daf4 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindLinedefTypes.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindLinedefTypes.cs
@@ -83,13 +83,13 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
 			int replaceaction = 0;
-			if(replacewith != null)
+			if(replace)
 			{
 				// If it cannot be interpreted, set replacewith to null (not replacing at all)
 				if(!int.TryParse(replacewith, out replaceaction)) replacewith = null;
@@ -162,7 +162,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 						if (match) {
 							// Replace
-							if (replacewith != null) l.Action = replaceaction;
+							if(replace) l.Action = replaceaction;
 
 							// Add to list
 							LinedefActionInfo info = General.Map.Config.GetLinedefActionInfo(l.Action);
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindReplaceAttribute.cs b/Source/Plugins/BuilderModes/FindReplace/FindReplaceAttribute.cs
index 81f0f54fa..b33c50fb0 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindReplaceAttribute.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindReplaceAttribute.cs
@@ -29,7 +29,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		private string displayname;
 		private bool browsebutton;
-		private bool replacable;
 
 		#endregion
 
@@ -37,7 +36,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		public string DisplayName { get { return displayname; } set { displayname = value; } }
 		public bool BrowseButton { get { return browsebutton; } set { browsebutton = value; } }
-		public bool Replacable { get { return replacable; } set { replacable = value; } }
 
 		#endregion
 
@@ -48,7 +46,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		{
 			// Initialize
 			this.displayname = displayname;
-			this.replacable = true;
 		}
 
 		#endregion
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindReplaceType.cs b/Source/Plugins/BuilderModes/FindReplace/FindReplaceType.cs
index 6843c4e12..1795b5a67 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindReplaceType.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindReplaceType.cs
@@ -35,7 +35,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		#region ================== Variables
 
-		protected FindReplaceAttribute attribs;
+		protected readonly FindReplaceAttribute attribs;
 		
 		#endregion
 
@@ -58,11 +58,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			attribs = (FindReplaceAttribute)attrs[0];
 		}
 
-		// Destructor
-		~FindReplaceType()
-		{
-		}
-
 		#endregion
 
 		#region ================== Methods
@@ -72,6 +67,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		{
 			return true;
 		}
+
+		// This is called to test if replacing is supported (mxd)
+		public virtual bool CanReplace() 
+		{
+			return true;
+		}
 		
 		// This is called when the browse button is pressed
 		public virtual string Browse(string initialvalue)
@@ -82,7 +83,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Must return a list of items to show in the results list
 		// replacewith is null when not replacing
-		public virtual FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public virtual FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			return new FindReplaceObject[0];
 		}
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSectorBrightness.cs b/Source/Plugins/BuilderModes/FindReplace/FindSectorBrightness.cs
index 0afcd7a17..a88bd569e 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindSectorBrightness.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindSectorBrightness.cs
@@ -16,12 +16,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) {
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection) {
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
 			int replacebrightness = 0;
-			if(replacewith != null) {
+			if(replace) {
 				// If it cannot be interpreted, set replacewith to null (not replacing at all)
 				if(!int.TryParse(replacewith, out replacebrightness)) replacewith = null;
 				if(replacebrightness < 0) replacewith = null;
@@ -43,7 +43,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					// Brightness matches?
 					if(s.Brightness == brightness) {
 						// Replace
-						if(replacewith != null) s.Brightness = replacebrightness;
+						if(replace) s.Brightness = replacebrightness;
 
 						objs.Add(new FindReplaceObject(s, "Sector " + s.Index));
 					}
@@ -51,7 +51,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			}
 
 			//refresh map
-			if(replacewith != null) {
+			if(replace) {
 				General.Map.Map.Update();
 				General.Map.IsChanged = true;
 			}
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSectorCeilingHeight.cs b/Source/Plugins/BuilderModes/FindReplace/FindSectorCeilingHeight.cs
index 76509ce76..c9abe80aa 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindSectorCeilingHeight.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindSectorCeilingHeight.cs
@@ -16,12 +16,12 @@ namespace CodeImp.DoomBuilder.BuilderModes.FindReplace
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) {
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection) {
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
 			int replaceheight = 0;
-			if(replacewith != null) {
+			if(replace) {
 				// If it cannot be interpreted, set replacewith to null (not replacing at all)
 				if(!int.TryParse(replacewith, out replaceheight)) replacewith = null;
 				if(replacewith == null) {
@@ -41,7 +41,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.FindReplace
 					// Height matches?
 					if(s.CeilHeight == height) {
 						// Replace
-						if(replacewith != null) s.CeilHeight = replaceheight;
+						if(replace) s.CeilHeight = replaceheight;
 
 						objs.Add(new FindReplaceObject(s, "Sector " + s.Index));
 					}
@@ -49,7 +49,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.FindReplace
 			}
 
 			//refresh map
-			if(replacewith != null) {
+			if(replace) {
 				General.Map.Map.Update();
 				General.Map.IsChanged = true;
 			}
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSectorEffect.cs b/Source/Plugins/BuilderModes/FindReplace/FindSectorEffect.cs
index 7cf32ac05..9172140cf 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindSectorEffect.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindSectorEffect.cs
@@ -81,13 +81,13 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
 			int replaceeffect = 0;
-			if(replacewith != null)
+			if(replace)
 			{
 				// If it cannot be interpreted, set replacewith to null (not replacing at all)
 				if(!int.TryParse(replacewith, out replaceeffect)) replacewith = null;
@@ -117,7 +117,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					if(s.Effect == effect || BitsMatch(s.Effect, expectedbits))
 					{
 						// Replace
-						if(replacewith != null) s.Effect = replaceeffect;
+						if(replace) s.Effect = replaceeffect;
 						
 						SectorEffectInfo info = General.Map.Config.GetSectorEffectInfo(s.Effect);
 						if(!info.IsNull)
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSectorFlags.cs b/Source/Plugins/BuilderModes/FindReplace/FindSectorFlags.cs
new file mode 100644
index 000000000..64320f062
--- /dev/null
+++ b/Source/Plugins/BuilderModes/FindReplace/FindSectorFlags.cs
@@ -0,0 +1,121 @@
+#region ================== Namespaces
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+using CodeImp.DoomBuilder.Config;
+using CodeImp.DoomBuilder.Map;
+using CodeImp.DoomBuilder.Windows;
+
+#endregion
+
+namespace CodeImp.DoomBuilder.BuilderModes
+{
+	[FindReplace("Sector Flags", BrowseButton = true)]
+	internal class FindSectorFlags : BaseFindSector
+	{
+		#region ================== Properties
+
+		public override Image BrowseImage { get { return Properties.Resources.List; } }
+
+		#endregion
+
+		#region ================== Methods
+
+		// This is called to test if the item should be displayed
+		public override bool DetermineVisiblity() 
+		{
+			return General.Map.Config.SectorFlags.Count > 0;
+		}
+
+		// This is called when the browse button is pressed
+		public override string Browse(string initialvalue)
+		{
+			return FlagsForm.ShowDialog(Form.ActiveForm, initialvalue, General.Map.Config.SectorFlags);
+		}
+
+		// This is called to perform a search (and replace)
+		// Returns a list of items to show in the results list
+		// replacewith is null when not replacing
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
+		{
+			List<FindReplaceObject> objs = new List<FindReplaceObject>();
+
+			// Where to search?
+			ICollection<Sector> list = withinselection ? General.Map.Map.GetSelectedSectors(true) : General.Map.Map.Sectors;
+
+			// Find what? (mxd)
+			List<string> findflagslist = new List<string>();
+			foreach(string flag in value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) 
+			{
+				string f = flag.Trim();
+				if(General.Map.Config.SectorFlags.ContainsKey(f)) findflagslist.Add(f);
+			}
+			if(findflagslist.Count == 0) 
+			{
+				MessageBox.Show("Invalid value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error);
+				return objs.ToArray();
+			}
+
+			// Replace with what? (mxd)
+			List<string> replaceflagslist = new List<string>();
+			if(replace) 
+			{
+				string[] replaceflags = replacewith.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+				foreach(string flag in replaceflags) 
+				{
+					string f = flag.Trim();
+					if (!General.Map.Config.SectorFlags.ContainsKey(f))
+					{
+						MessageBox.Show("Invalid replace value '" + f + "' for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error);
+						return objs.ToArray();
+					}
+					replaceflagslist.Add(f);
+				}
+			}
+
+			// Go for all linedefs
+			foreach (Sector s in list)
+			{
+				bool match = true;
+
+				// Parse the value string...
+				foreach(string flag in findflagslist)
+				{
+					// ... and check if the flags don't match
+					if(!s.IsFlagSet(flag))
+					{
+						match = false;
+						break;
+					}
+				}
+
+				// Flags matches?
+				if (match)
+				{
+					// Replace flags (mxd)
+					if(replace) 
+					{
+						// Clear all flags
+						s.ClearFlags();
+
+						// Set new flags
+						foreach(string flag in replaceflagslist) s.SetFlag(flag, true);
+					}
+					
+					// Add to list
+					SectorEffectInfo info = General.Map.Config.GetSectorEffectInfo(s.Effect);
+					if(!info.IsNull)
+						objs.Add(new FindReplaceObject(s, "Sector " + s.Index + " (" + info.Title + ")"));
+					else
+						objs.Add(new FindReplaceObject(s, "Sector " + s.Index));
+				}
+			}
+
+			return objs.ToArray();
+		}
+
+		#endregion
+	}
+}
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSectorFlat.cs b/Source/Plugins/BuilderModes/FindReplace/FindSectorFlat.cs
index e900278e3..fa39cb188 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindSectorFlat.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindSectorFlat.cs
@@ -17,8 +17,9 @@
 #region ================== Namespaces
 
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
 using System.Windows.Forms;
-using CodeImp.DoomBuilder.IO;
+using CodeImp.DoomBuilder.Config;
 using CodeImp.DoomBuilder.Map;
 using System.Drawing;
 
@@ -59,25 +60,20 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
-			if(replacewith != null)
+			if(replace && (string.IsNullOrEmpty(replacewith) || replacewith.Length > 8)) 
 			{
-				// If it cannot be interpreted, set replacewith to null (not replacing at all)
-				if(replacewith.Length < 0) replacewith = null;
-				if(replacewith.Length > 8) replacewith = null;
-				if(replacewith == null)
-				{
-					MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error);
-					return objs.ToArray();
-				}
+				MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error);
+				return objs.ToArray();
 			}
 			
 			// Interpret the find
-			long longfind = Lump.MakeLongName(value.Trim());
+			bool isregex = (value.IndexOf('*') != -1 || value.IndexOf('?') != -1); //mxd
+			MatchingTextureSet set = new MatchingTextureSet(new Collection<string> { value.Trim() }); //mxd
 
 			// Where to search?
 			ICollection<Sector> list = withinselection ? General.Map.Map.GetSelectedSectors(true) : General.Map.Map.Sectors;
@@ -86,23 +82,24 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			foreach(Sector s in list)
 			{
 				// Flat matches?
-				if(s.LongCeilTexture == longfind)
+				if(set.IsMatch(s.CeilTexture)) 
 				{
 					// Replace and add to list
-					if(replacewith != null) s.SetCeilTexture(replacewith);
-					objs.Add(new FindReplaceObject(s, "Sector " + s.Index + " (ceiling)"));
+					if(replace) s.SetCeilTexture(replacewith);
+					objs.Add(new FindReplaceObject(s, "Sector " + s.Index + " (ceiling)" + (isregex ? " - " + s.CeilTexture : null)));
 				}
-				
-				if(s.LongFloorTexture == longfind)
+
+				if(set.IsMatch(s.FloorTexture)) 
 				{
 					// Replace and add to list
-					if(replacewith != null) s.SetFloorTexture(replacewith);
-					objs.Add(new FindReplaceObject(s, "Sector " + s.Index + " (floor)"));
+					if(replace) s.SetFloorTexture(replacewith);
+					objs.Add(new FindReplaceObject(s, "Sector " + s.Index + " (floor)" + (isregex ? " - " + s.FloorTexture : null)));
 				}
 			}
 			
 			// When replacing, make sure we keep track of used textures
-			if(replacewith != null) {
+			if(replace) 
+			{
 				General.Map.Data.UpdateUsedTextures();
 				General.Map.Map.Update(); //mxd. And don't forget to update the view itself
 				General.Map.IsChanged = true;
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSectorFloorHeight.cs b/Source/Plugins/BuilderModes/FindReplace/FindSectorFloorHeight.cs
index b2f92449c..415d6940b 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindSectorFloorHeight.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindSectorFloorHeight.cs
@@ -16,12 +16,12 @@ namespace CodeImp.DoomBuilder.BuilderModes.FindReplace
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection) {
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection) {
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
 			int replaceheight = 0;
-			if(replacewith != null) {
+			if(replace) {
 				// If it cannot be interpreted, set replacewith to null (not replacing at all)
 				if(!int.TryParse(replacewith, out replaceheight)) replacewith = null;
 				if(replacewith == null) {
@@ -41,7 +41,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.FindReplace
 					// Height matches?
 					if(s.FloorHeight == height) {
 						// Replace
-						if(replacewith != null) s.FloorHeight = replaceheight;
+						if(replace) s.FloorHeight = replaceheight;
 
 						objs.Add(new FindReplaceObject(s, "Sector " + s.Index));
 					}
@@ -49,7 +49,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.FindReplace
 			}
 
 			//refresh map
-			if(replacewith != null) {
+			if(replace) {
 				General.Map.Map.Update();
 				General.Map.IsChanged = true;
 			}
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSectorNumber.cs b/Source/Plugins/BuilderModes/FindReplace/FindSectorNumber.cs
index fbfaed896..8ad83dc9c 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindSectorNumber.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindSectorNumber.cs
@@ -24,7 +24,7 @@ using CodeImp.DoomBuilder.Config;
 
 namespace CodeImp.DoomBuilder.BuilderModes
 {
-	[FindReplace("Sector Index", BrowseButton = false, Replacable = false)]
+	[FindReplace("Sector Index", BrowseButton = false)]
 	internal class FindSectorNumber : BaseFindSector
 	{
 		#region ================== Constants
@@ -45,10 +45,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		#region ================== Methods
 
+		//mxd
+		public override bool CanReplace() 
+		{
+			return false;
+		}
+
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSectorTags.cs b/Source/Plugins/BuilderModes/FindReplace/FindSectorTags.cs
index 2440f4926..4e38ea7c9 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindSectorTags.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindSectorTags.cs
@@ -49,13 +49,13 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
 			int replacetag = 0;
-			if(replacewith != null)
+			if(replace)
 			{
 				// If it cannot be interpreted, set replacewith to null (not replacing at all)
 				if(!int.TryParse(replacewith, out replacetag)) replacewith = null;
@@ -82,7 +82,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					if(s.Tag == tag)
 					{
 						// Replace
-						if(replacewith != null) s.Tag = replacetag;
+						if(replace) s.Tag = replacetag;
 						
 						SectorEffectInfo info = General.Map.Config.GetSectorEffectInfo(s.Effect);
 						if(!info.IsNull)
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSidedefFlags.cs b/Source/Plugins/BuilderModes/FindReplace/FindSidedefFlags.cs
new file mode 100644
index 000000000..15a42df8b
--- /dev/null
+++ b/Source/Plugins/BuilderModes/FindReplace/FindSidedefFlags.cs
@@ -0,0 +1,118 @@
+#region ================== Namespaces
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+using CodeImp.DoomBuilder.Map;
+using CodeImp.DoomBuilder.Windows;
+
+#endregion
+
+namespace CodeImp.DoomBuilder.BuilderModes
+{
+	[FindReplace("Sidedef Flags", BrowseButton = true)]
+	internal class FindSidedefFlags : BaseFindSidedef
+	{
+		#region ================== Properties
+
+		public override Image BrowseImage { get { return Properties.Resources.List; } }
+
+		#endregion
+
+		#region ================== Methods
+
+		// This is called to test if the item should be displayed
+		public override bool DetermineVisiblity() 
+		{
+			return General.Map.Config.SidedefFlags.Count > 0;
+		}
+
+		// This is called when the browse button is pressed
+		public override string Browse(string initialvalue) 
+		{
+			return FlagsForm.ShowDialog(Form.ActiveForm, initialvalue, General.Map.Config.SidedefFlags);
+		}
+
+		// This is called to perform a search (and replace)
+		// Returns a list of items to show in the results list
+		// replacewith is null when not replacing
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection) 
+		{
+			List<FindReplaceObject> objs = new List<FindReplaceObject>();
+
+			// Where to search?
+			ICollection<Sidedef> list = withinselection ? General.Map.Map.GetSidedefsFromSelectedLinedefs(true) : General.Map.Map.Sidedefs;
+
+			// Find what? (mxd)
+			List<string> findflagslist = new List<string>();
+			foreach(string flag in value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) 
+			{
+				string f = flag.Trim();
+				if(General.Map.Config.SidedefFlags.ContainsKey(f)) findflagslist.Add(f);
+			}
+			if(findflagslist.Count == 0) 
+			{
+				MessageBox.Show("Invalid value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error);
+				return objs.ToArray();
+			}
+
+			// Replace with what? (mxd)
+			List<string> replaceflagslist = new List<string>();
+			if(replace) 
+			{
+				string[] replaceflags = replacewith.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+				foreach(string flag in replaceflags) 
+				{
+					string f = flag.Trim();
+					if (!General.Map.Config.SidedefFlags.ContainsKey(f))
+					{
+						MessageBox.Show("Invalid replace value '" + f + "' for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error);
+						return objs.ToArray();
+					}
+					replaceflagslist.Add(f);
+				}
+			}
+
+			// Go for all linedefs
+			foreach(Sidedef sd in list) 
+			{
+				bool match = true;
+
+				// Parse the value string...
+				foreach(string flag in findflagslist) 
+				{
+					// ... and check if the flags don't match
+					if(!sd.IsFlagSet(flag)) 
+					{
+						match = false;
+						break;
+					}
+				}
+
+				// Flags matches?
+				if(match) 
+				{
+					string side = sd.IsFront ? "front" : "back";
+					
+					// Replace flags (mxd)
+					if(replace) 
+					{
+						// Clear all flags
+						sd.ClearFlags();
+
+						// Set new flags
+						foreach(string flag in replaceflagslist) sd.SetFlag(flag, true);
+					}
+
+					// Add to list
+					objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ")"));
+				}
+			}
+
+			return objs.ToArray();
+		}
+
+		#endregion
+	}
+}
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSidedefNumber.cs b/Source/Plugins/BuilderModes/FindReplace/FindSidedefNumber.cs
index 9a36d26ce..cbab6033e 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindSidedefNumber.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindSidedefNumber.cs
@@ -18,14 +18,13 @@
 
 using System.Collections.Generic;
 using CodeImp.DoomBuilder.Map;
-using CodeImp.DoomBuilder.Rendering;
 
 #endregion
 
 namespace CodeImp.DoomBuilder.BuilderModes
 {
-	[FindReplace("Sidedef Index", BrowseButton = false, Replacable = false)]
-	internal class FindSidedefNumber : FindReplaceType
+	[FindReplace("Sidedef Index", BrowseButton = false)]
+	internal class FindSidedefNumber : BaseFindSidedef
 	{
 		#region ================== Constants
 
@@ -41,33 +40,20 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		#region ================== Constructor / Destructor
 
-		// Constructor
-		public FindSidedefNumber()
-		{
-			// Initialize
-
-		}
-
-		// Destructor
-		~FindSidedefNumber()
-		{
-		}
-
 		#endregion
 
 		#region ================== Methods
 
-		// This is called when the browse button is pressed
-		public override string Browse(string initialvalue)
+		//mxd
+		public override bool CanReplace() 
 		{
-			return "";
+			return false;
 		}
 
-
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
@@ -82,38 +68,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			return objs.ToArray();
 		}
 
-		// This is called when a specific object is selected from the list
-		public override void ObjectSelected(FindReplaceObject[] selection)
-		{
-			if(selection.Length == 1)
-			{
-				ZoomToSelection(selection);
-				General.Interface.ShowLinedefInfo(selection[0].Sidedef.Line);
-			}
-			else
-				General.Interface.HideInfo();
-
-			General.Map.Map.ClearAllSelected();
-			foreach(FindReplaceObject obj in selection) obj.Sidedef.Line.Selected = true;
-		}
-
-		// Render selection
-		public override void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection)
-		{
-			foreach(FindReplaceObject o in selection)
-			{
-				renderer.PlotLinedef(o.Sidedef.Line, General.Colors.Selection);
-			}
-		}
-
-		// Edit objects
-		public override void EditObjects(FindReplaceObject[] selection)
-		{
-			List<Linedef> linedefs = new List<Linedef>(selection.Length);
-			foreach(FindReplaceObject o in selection) linedefs.Add(o.Sidedef.Line);
-			General.Interface.ShowEditLinedefs(linedefs);
-		}
-
 		#endregion
 	}
 }
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindSidedefTexture.cs b/Source/Plugins/BuilderModes/FindReplace/FindSidedefTexture.cs
index 3f9870ff7..faf1e56f1 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindSidedefTexture.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindSidedefTexture.cs
@@ -17,10 +17,10 @@
 #region ================== Namespaces
 
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
 using System.Windows.Forms;
-using CodeImp.DoomBuilder.IO;
+using CodeImp.DoomBuilder.Config;
 using CodeImp.DoomBuilder.Map;
-using CodeImp.DoomBuilder.Rendering;
 using System.Drawing;
 
 #endregion
@@ -28,7 +28,7 @@ using System.Drawing;
 namespace CodeImp.DoomBuilder.BuilderModes
 {
 	[FindReplace("Sidedef Texture", BrowseButton = true)]
-	internal class FindSidedefTexture : FindReplaceType
+	internal class FindSidedefTexture : BaseFindSidedef
 	{
 		#region ================== Constants
 
@@ -46,18 +46,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		#region ================== Constructor / Destructor
 
-		// Constructor
-		public FindSidedefTexture()
-		{
-			// Initialize
-
-		}
-
-		// Destructor
-		~FindSidedefTexture()
-		{
-		}
-
 		#endregion
 
 		#region ================== Methods
@@ -72,94 +60,57 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
-			if(replacewith != null)
+			if(replace && (string.IsNullOrEmpty(replacewith) || replacewith.Length > 8)) 
 			{
-				// If it cannot be interpreted, set replacewith to null (not replacing at all)
-				if(replacewith.Length < 0) replacewith = null;
-				if(replacewith.Length > 8) replacewith = null;
-				if(replacewith == null)
-				{
-					MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error);
-					return objs.ToArray();
-				}
+				MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error);
+				return objs.ToArray();
 			}
 			
 			// Interpret the find
-			long longfind = Lump.MakeLongName(value.Trim());
+			bool isregex = (value.IndexOf('*') != -1 || value.IndexOf('?') != -1); //mxd
+			MatchingTextureSet set = new MatchingTextureSet(new Collection<string> { value.Trim() }); //mxd
 
 			// Where to search?
-			ICollection<Sidedef> list = withinselection ? General.Map.Map.GetSidedefsFromSelectedLinedefs(true) : General.Map.Map.Sidedefs;
+			ICollection<Sidedef> sidelist = withinselection ? General.Map.Map.GetSidedefsFromSelectedLinedefs(true) : General.Map.Map.Sidedefs;
 
 			// Go for all sidedefs
-			foreach(Sidedef sd in list)
+			foreach(Sidedef sd in sidelist) 
 			{
 				string side = sd.IsFront ? "front" : "back";
 				
-				if(sd.LongHighTexture == longfind)
+				if(set.IsMatch(sd.HighTexture)) 
 				{
 					// Replace and add to list
-					if(replacewith != null) sd.SetTextureHigh(replacewith);
-					objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", high)"));
+					if(replace) sd.SetTextureHigh(replacewith);
+					objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", high)" + (isregex ? " - " + sd.HighTexture : null)));
 				}
 				
-				if(sd.LongMiddleTexture == longfind)
+				if(set.IsMatch(sd.MiddleTexture)) 
 				{
 					// Replace and add to list
-					if(replacewith != null) sd.SetTextureMid(replacewith);
-					objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", middle)"));
+					if(replace) sd.SetTextureMid(replacewith);
+					objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", middle)" + (isregex ? " - " + sd.MiddleTexture : null)));
 				}
 				
-				if(sd.LongLowTexture == longfind)
+				if(set.IsMatch(sd.LowTexture)) 
 				{
 					// Replace and add to list
-					if(replacewith != null) sd.SetTextureLow(replacewith);
-					objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", low)"));
+					if(replace) sd.SetTextureLow(replacewith);
+					objs.Add(new FindReplaceObject(sd, "Sidedef " + sd.Index + " (" + side + ", low)" + (isregex ? " - " + sd.LowTexture : null)));
 				}
 			}
 			
 			// When replacing, make sure we keep track of used textures
-			if(replacewith != null) General.Map.Data.UpdateUsedTextures();
+			if(replace) General.Map.Data.UpdateUsedTextures();
 			
 			return objs.ToArray();
 		}
 
-		// This is called when a specific object is selected from the list
-		public override void ObjectSelected(FindReplaceObject[] selection)
-		{
-			if(selection.Length == 1)
-			{
-				ZoomToSelection(selection);
-				General.Interface.ShowLinedefInfo(selection[0].Sidedef.Line);
-			}
-			else
-				General.Interface.HideInfo();
-
-			General.Map.Map.ClearAllSelected();
-			foreach(FindReplaceObject obj in selection) obj.Sidedef.Line.Selected = true;
-		}
-
-		// Render selection
-		public override void PlotSelection(IRenderer2D renderer, FindReplaceObject[] selection)
-		{
-			foreach(FindReplaceObject o in selection)
-			{
-				renderer.PlotLinedef(o.Sidedef.Line, General.Colors.Selection);
-			}
-		}
-
-		// Edit objects
-		public override void EditObjects(FindReplaceObject[] selection)
-		{
-			List<Linedef> linedefs = new List<Linedef>(selection.Length);
-			foreach(FindReplaceObject o in selection) linedefs.Add(o.Sidedef.Line);
-			General.Interface.ShowEditLinedefs(linedefs);
-		}
-
 		#endregion
 	}
 }
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindThingAction.cs b/Source/Plugins/BuilderModes/FindReplace/FindThingAction.cs
index a5ef596a2..f07242c69 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindThingAction.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindThingAction.cs
@@ -72,13 +72,13 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
 			int replaceaction = 0;
-			if(replacewith != null)
+			if(replace)
 			{
 				// If it cannot be interpreted, set replacewith to null (not replacing at all)
 				if(!int.TryParse(replacewith, out replaceaction)) replacewith = null;
@@ -105,8 +105,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			//useful for locating enemies that trigger a certain script
 			//
 			//Since the Thing object does not contain a reference to arg0str, this search cannot match named scripts
-			
-
 			if (int.TryParse(parts[0], out findaction))
 			{
 				//parse the arg value out
@@ -153,7 +151,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 						if (match)
 						{
 							// Replace
-							if (replacewith != null) t.Action = replaceaction;
+							if (replace) t.Action = replaceaction;
 
 							// Add to list
 							ThingTypeInfo ti = General.Map.Data.GetThingInfo(t.Type);
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindThingAngle.cs b/Source/Plugins/BuilderModes/FindReplace/FindThingAngle.cs
index 74e42e920..effc65e80 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindThingAngle.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindThingAngle.cs
@@ -65,13 +65,13 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
 			int replaceangle = 0;
-			if(replacewith != null)
+			if(replace)
 			{
 				// If it cannot be interpreted, set replacewith to null (not replacing at all)
 				if(!int.TryParse(replacewith, out replaceangle)) replacewith = null;
@@ -96,7 +96,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					if(Angle2D.RealToDoom(t.Angle) == angle)
 					{
 						// Replace
-						if(replacewith != null) t.Rotate(Angle2D.DoomToReal(replaceangle));
+						if(replace) t.Rotate(Angle2D.DoomToReal(replaceangle));
 
 						// Add to list
 						ThingTypeInfo ti = General.Map.Data.GetThingInfo(t.Type);
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindThingFlags.cs b/Source/Plugins/BuilderModes/FindReplace/FindThingFlags.cs
index a3f8fbe42..277f26643 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindThingFlags.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindThingFlags.cs
@@ -16,6 +16,7 @@
 
 #region ================== Namespaces
 
+using System;
 using System.Collections.Generic;
 using System.Windows.Forms;
 using CodeImp.DoomBuilder.Windows;
@@ -28,7 +29,7 @@ using CodeImp.DoomBuilder.Config;
 
 namespace CodeImp.DoomBuilder.BuilderModes
 {
-	[FindReplace("Thing Flags", BrowseButton = true, Replacable = false)]
+	[FindReplace("Thing Flags", BrowseButton = true)]
 	internal class FindThingFlag : BaseFindThing
 	{
 		#region ================== Constants
@@ -52,35 +53,68 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		#region ================== Methods
 
+		// This is called to test if the item should be displayed
+		public override bool DetermineVisiblity() 
+		{
+			return General.Map.Config.ThingFlags.Count > 0;
+		}
+
 		// This is called when the browse button is pressed
 		public override string Browse(string initialvalue)
 		{
 			return FlagsForm.ShowDialog(Form.ActiveForm, initialvalue, General.Map.Config.ThingFlags);
 		}
 
-
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Where to search?
 			ICollection<Thing> list = withinselection ? General.Map.Map.GetSelectedThings(true) : General.Map.Map.Things;
 
+			// Find what? (mxd)
+			List<string> findflagslist = new List<string>();
+			foreach(string flag in value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
+			{
+				string f = flag.Trim();
+				if(General.Map.Config.ThingFlags.ContainsKey(f)) findflagslist.Add(f);
+			}
+			if(findflagslist.Count == 0) 
+			{
+				MessageBox.Show("Invalid value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error);
+				return objs.ToArray();
+			}
+
+			// Replace with what? (mxd)
+			List<string> replaceflagslist = new List<string>();
+			if(replace)
+			{
+				string[] replaceflags = replacewith.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+				foreach(string flag in replaceflags) 
+				{
+					string f = flag.Trim();
+					if (!General.Map.Config.ThingFlags.ContainsKey(f))
+					{
+						MessageBox.Show("Invalid replace value '" + f + "' for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error);
+						return objs.ToArray();
+					}
+					replaceflagslist.Add(f);
+				}
+			}
+
 			// Go for all things
 			foreach (Thing t in list)
 			{
 				bool match = true;
 
 				// Parse the value string...
-				foreach (string s in value.Split(','))
+				foreach(string flag in findflagslist)
 				{
-					string str = s.Trim();
-
 					// ... and check if the flags don't match
-					if (General.Map.Config.ThingFlags.ContainsKey(str) && !t.IsFlagSet(str))
+					if(!t.IsFlagSet(flag))
 					{
 						match = false;
 						break;
@@ -90,6 +124,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
 				// Match?
 				if (match)
 				{
+					// Replace flags (mxd)
+					if(replace)
+					{
+						// Clear all flags
+						t.ClearFlags();
+
+						// Set new flags
+						foreach(string flag in replaceflagslist) t.SetFlag(flag, true);
+					}
+					
 					// Add to list
 					ThingTypeInfo ti = General.Map.Data.GetThingInfo(t.Type);
 					objs.Add(new FindReplaceObject(t, "Thing " + t.Index + " (" + ti.Title + ")"));
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindThingNumber.cs b/Source/Plugins/BuilderModes/FindReplace/FindThingNumber.cs
index 62da21ac1..9ded73a31 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindThingNumber.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindThingNumber.cs
@@ -25,7 +25,7 @@ using CodeImp.DoomBuilder.Config;
 
 namespace CodeImp.DoomBuilder.BuilderModes
 {
-	[FindReplace("Thing Index", BrowseButton = false, Replacable = false)]
+	[FindReplace("Thing Index", BrowseButton = false)]
 	internal class FindThingNumber : BaseFindThing
 	{
 		#region ================== Constants
@@ -48,10 +48,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		#region ================== Methods
 
+		//mxd
+		public override bool CanReplace() 
+		{
+			return false;
+		}
+
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindThingSectorRef.cs b/Source/Plugins/BuilderModes/FindReplace/FindThingSectorRef.cs
index ece1d4e3a..bd8d15569 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindThingSectorRef.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindThingSectorRef.cs
@@ -59,13 +59,13 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
 			int replacetag = 0;
-			if(replacewith != null)
+			if(replace)
 			{
 				// If it cannot be interpreted, set replacewith to null (not replacing at all)
 				if(!int.TryParse(replacewith, out replacetag)) replacewith = null;
@@ -102,7 +102,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 								if(t.Args[i] == tag)
 								{
 									// Replace
-									if(replacewith != null) t.Args[i] = replacetag;
+									if(replace) t.Args[i] = replacetag;
 									addthing = true;
 								}
 							}
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindThingTag.cs b/Source/Plugins/BuilderModes/FindReplace/FindThingTag.cs
index 7bdca71b6..f633722b2 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindThingTag.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindThingTag.cs
@@ -59,13 +59,13 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
 			int replacetag = 0;
-			if(replacewith != null)
+			if(replace)
 			{
 				// If it cannot be interpreted, set replacewith to null (not replacing at all)
 				if(!int.TryParse(replacewith, out replacetag)) replacewith = null;
@@ -92,7 +92,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					if(t.Tag == tag)
 					{
 						// Replace
-						if(replacewith != null) t.Tag = replacetag;
+						if(replace) t.Tag = replacetag;
 
 						// Add to list
 						ThingTypeInfo ti = General.Map.Data.GetThingInfo(t.Type);
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindThingThingRef.cs b/Source/Plugins/BuilderModes/FindReplace/FindThingThingRef.cs
index 89f21909c..e051d5a92 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindThingThingRef.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindThingThingRef.cs
@@ -59,13 +59,13 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
 			int replacetag = 0;
-			if(replacewith != null)
+			if(replace)
 			{
 				// If it cannot be interpreted, set replacewith to null (not replacing at all)
 				if(!int.TryParse(replacewith, out replacetag)) replacewith = null;
@@ -102,7 +102,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 								if(t.Args[i] == tag)
 								{
 									// Replace
-									if(replacewith != null) t.Args[i] = replacetag;
+									if(replace) t.Args[i] = replacetag;
 									addthing = true;
 								}
 							}
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindThingType.cs b/Source/Plugins/BuilderModes/FindReplace/FindThingType.cs
index fcc1031e5..ebb51f26e 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindThingType.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindThingType.cs
@@ -65,13 +65,13 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
 			// Interpret the replacement
 			int replacetype = 0;
-			if(replacewith != null)
+			if(replace)
 			{
 				// If it cannot be interpreted, set replacewith to null (not replacing at all)
 				if(!int.TryParse(replacewith, out replacetype)) replacewith = null;
@@ -98,7 +98,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					if(t.Type == findtype)
 					{
 						// Replace
-						if(replacewith != null)
+						if(replace)
 						{
 							t.Type = replacetype;
 							t.UpdateConfiguration();
diff --git a/Source/Plugins/BuilderModes/FindReplace/FindVertexNumber.cs b/Source/Plugins/BuilderModes/FindReplace/FindVertexNumber.cs
index f5b7f6136..7300ea49c 100644
--- a/Source/Plugins/BuilderModes/FindReplace/FindVertexNumber.cs
+++ b/Source/Plugins/BuilderModes/FindReplace/FindVertexNumber.cs
@@ -24,7 +24,7 @@ using CodeImp.DoomBuilder.Rendering;
 
 namespace CodeImp.DoomBuilder.BuilderModes
 {
-	[FindReplace("Vertex Index", BrowseButton = false, Replacable = false)]
+	[FindReplace("Vertex Index", BrowseButton = false)]
 	internal class FindVertexNumber : FindReplaceType
 	{
 		#region ================== Constants
@@ -57,17 +57,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		#region ================== Methods
 
-		// This is called when the browse button is pressed
-		/*public override string Browse(string initialvalue)
+		//mxd
+		public override bool CanReplace() 
 		{
-			return "";
-		}*/
-
+			return false;
+		}
 
 		// This is called to perform a search (and replace)
 		// Returns a list of items to show in the results list
 		// replacewith is null when not replacing
-		public override FindReplaceObject[] Find(string value, bool withinselection, string replacewith, bool keepselection)
+		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
 		{
 			List<FindReplaceObject> objs = new List<FindReplaceObject>();
 
diff --git a/Source/Plugins/BuilderModes/Interface/FindReplaceForm.Designer.cs b/Source/Plugins/BuilderModes/Interface/FindReplaceForm.Designer.cs
index 90f49407a..b6cc783e9 100644
--- a/Source/Plugins/BuilderModes/Interface/FindReplaceForm.Designer.cs
+++ b/Source/Plugins/BuilderModes/Interface/FindReplaceForm.Designer.cs
@@ -96,6 +96,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			this.findinput.Name = "findinput";
 			this.findinput.Size = new System.Drawing.Size(106, 20);
 			this.findinput.TabIndex = 1;
+			this.findinput.TextChanged += new System.EventHandler(this.findinput_TextChanged);
 			// 
 			// browsefind
 			// 
@@ -103,7 +104,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			this.browsefind.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.treeview;
 			this.browsefind.Location = new System.Drawing.Point(212, 46);
 			this.browsefind.Name = "browsefind";
-			this.browsefind.Padding = new System.Windows.Forms.Padding(0, 0, 1, 3);
 			this.browsefind.Size = new System.Drawing.Size(28, 25);
 			this.browsefind.TabIndex = 2;
 			this.browsefind.UseVisualStyleBackColor = true;
@@ -125,7 +125,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			this.browsereplace.Image = global::CodeImp.DoomBuilder.BuilderModes.Properties.Resources.treeview;
 			this.browsereplace.Location = new System.Drawing.Point(203, 23);
 			this.browsereplace.Name = "browsereplace";
-			this.browsereplace.Padding = new System.Windows.Forms.Padding(0, 0, 1, 3);
 			this.browsereplace.Size = new System.Drawing.Size(28, 25);
 			this.browsereplace.TabIndex = 1;
 			this.browsereplace.UseVisualStyleBackColor = true;
@@ -140,6 +139,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			// 
 			// findbutton
 			// 
+			this.findbutton.Enabled = false;
 			this.findbutton.Location = new System.Drawing.Point(273, 12);
 			this.findbutton.Name = "findbutton";
 			this.findbutton.Size = new System.Drawing.Size(74, 25);
@@ -268,7 +268,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			this.MaximizeBox = false;
 			this.MinimizeBox = false;
 			this.Name = "FindReplaceForm";
-			this.Opacity = 0;
+			this.Opacity = 1;
 			this.ShowIcon = false;
 			this.ShowInTaskbar = false;
 			this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
diff --git a/Source/Plugins/BuilderModes/Interface/FindReplaceForm.cs b/Source/Plugins/BuilderModes/Interface/FindReplaceForm.cs
index f27222837..a5f2e4b5f 100644
--- a/Source/Plugins/BuilderModes/Interface/FindReplaceForm.cs
+++ b/Source/Plugins/BuilderModes/Interface/FindReplaceForm.cs
@@ -124,8 +124,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			browsefind.Image = newfinder.BrowseImage;
 			browsereplace.Enabled = newfinder.Attributes.BrowseButton;
 			browsereplace.Image = newfinder.BrowseImage;
-			if(!newfinder.Attributes.Replacable) doreplace.Checked = false;
-			doreplace.Enabled = newfinder.Attributes.Replacable;
+			if(!newfinder.CanReplace()) doreplace.Checked = false;
+			doreplace.Enabled = newfinder.CanReplace();
 		}
 		
 		// Browse find button clicked
@@ -140,6 +140,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			replaceinput.Text = newfinder.Browse(replaceinput.Text);
 		}
 
+		//mxd
+		private void findinput_TextChanged(object sender, EventArgs e)
+		{
+			findbutton.Enabled = !string.IsNullOrEmpty(findinput.Text);
+		}
+
 		// Find / Replace clicked
 		private void findbutton_Click(object sender, EventArgs e)
 		{
@@ -164,16 +170,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			{
 				General.Map.UndoRedo.CreateUndo("Replace " + searchtypes.SelectedItem);
 
-				resultslist.Items.AddRange(finder.Find(findinput.Text, withinselection.Checked, replaceinput.Text, false));
+				resultslist.Items.AddRange(finder.Find(findinput.Text, withinselection.Checked, true, replaceinput.Text, false));
 				resultscount.Text = resultslist.Items.Count + " items found and replaced.";
 
 				// Withdraw the undo step if nothing was replaced
-				if (resultslist.Items.Count <= 0)
+				if (resultslist.Items.Count < 1)
 					General.Map.UndoRedo.WithdrawUndo();
 			}
 			else
 			{
-				resultslist.Items.AddRange(finder.Find(findinput.Text, withinselection.Checked, null, false));
+				resultslist.Items.AddRange(finder.Find(findinput.Text, withinselection.Checked, false, string.Empty, false));
 				resultscount.Text = resultslist.Items.Count + " items found.";
 			}
 			
@@ -407,5 +413,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		}
 		
 		#endregion
+
 	}
 }
\ No newline at end of file
diff --git a/Source/Plugins/BuilderModes/Interface/FindReplaceForm.resx b/Source/Plugins/BuilderModes/Interface/FindReplaceForm.resx
index 294cffb50..46afa4acd 100644
--- a/Source/Plugins/BuilderModes/Interface/FindReplaceForm.resx
+++ b/Source/Plugins/BuilderModes/Interface/FindReplaceForm.resx
@@ -171,6 +171,12 @@
   <metadata name="editbutton.Locked" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>True</value>
   </metadata>
+  <metadata name="deletebutton.Locked" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="editbutton.Locked" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
   <metadata name="groupreplace.Locked" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>True</value>
   </metadata>
-- 
GitLab