From 6f255e18669e1caf6837386e498551165557892c Mon Sep 17 00:00:00 2001 From: codeimp Date: Thu, 8 May 2008 16:39:14 +0000 Subject: [PATCH] - introduced "volatile" editing modes - automated exclusive mouse mode management - fixed bug with actions that never ended when a dialog appears --- .../ClassicModes/DragLinedefsMode.cs | 4 +- .../ClassicModes/DragSectorsMode.cs | 4 +- .../ClassicModes/DragThingsMode.cs | 4 +- .../ClassicModes/DragVerticesMode.cs | 4 +- .../ClassicModes/DrawGeometryMode.cs | 15 ++- Source/Controls/ActionManager.cs | 36 +++++-- Source/Editing/EditMode.cs | 19 ++++ Source/Editing/EditModeAttribute.cs | 8 ++ Source/Editing/UndoManager.cs | 6 ++ Source/General/General.cs | 53 ++++++++-- Source/General/MapManager.cs | 3 + Source/Interface/IMainForm.cs | 3 + Source/Interface/MainForm.Designer.cs | 2 + Source/Interface/MainForm.cs | 99 ++++++++++++++++--- Source/Map/Linedef.cs | 8 -- 15 files changed, 228 insertions(+), 40 deletions(-) diff --git a/Source/BuilderModes/ClassicModes/DragLinedefsMode.cs b/Source/BuilderModes/ClassicModes/DragLinedefsMode.cs index e87f6774..450f04f3 100644 --- a/Source/BuilderModes/ClassicModes/DragLinedefsMode.cs +++ b/Source/BuilderModes/ClassicModes/DragLinedefsMode.cs @@ -40,8 +40,10 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing // The EditMode attribute does not have to be specified unless the // mode must be activated by class name rather than direct instance. // In that case, just specifying the attribute like this is enough: - [EditMode] + // [EditMode] + [EditMode(Volatile = true)] + public sealed class DragLinedefsMode : DragGeometryMode { #region ================== Constants diff --git a/Source/BuilderModes/ClassicModes/DragSectorsMode.cs b/Source/BuilderModes/ClassicModes/DragSectorsMode.cs index a2c4fd86..1bb90f9a 100644 --- a/Source/BuilderModes/ClassicModes/DragSectorsMode.cs +++ b/Source/BuilderModes/ClassicModes/DragSectorsMode.cs @@ -40,7 +40,9 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing // The EditMode attribute does not have to be specified unless the // mode must be activated by class name rather than direct instance. // In that case, just specifying the attribute like this is enough: - [EditMode] + // [EditMode] + + [EditMode(Volatile = true)] public sealed class DragSectorsMode : DragGeometryMode { diff --git a/Source/BuilderModes/ClassicModes/DragThingsMode.cs b/Source/BuilderModes/ClassicModes/DragThingsMode.cs index 2e84e343..ffecda67 100644 --- a/Source/BuilderModes/ClassicModes/DragThingsMode.cs +++ b/Source/BuilderModes/ClassicModes/DragThingsMode.cs @@ -40,7 +40,9 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing // The EditMode attribute does not have to be specified unless the // mode must be activated by class name rather than direct instance. // In that case, just specifying the attribute like this is enough: - [EditMode] + // [EditMode] + + [EditMode(Volatile = true)] public sealed class DragThingsMode : ClassicMode { diff --git a/Source/BuilderModes/ClassicModes/DragVerticesMode.cs b/Source/BuilderModes/ClassicModes/DragVerticesMode.cs index 10f0b1ed..7de671dc 100644 --- a/Source/BuilderModes/ClassicModes/DragVerticesMode.cs +++ b/Source/BuilderModes/ClassicModes/DragVerticesMode.cs @@ -40,7 +40,9 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing // The EditMode attribute does not have to be specified unless the // mode must be activated by class name rather than direct instance. // In that case, just specifying the attribute like this is enough: - [EditMode] + // [EditMode] + + [EditMode(Volatile = true)] public sealed class DragVerticesMode : DragGeometryMode { diff --git a/Source/BuilderModes/ClassicModes/DrawGeometryMode.cs b/Source/BuilderModes/ClassicModes/DrawGeometryMode.cs index f6ecc0ce..65185b62 100644 --- a/Source/BuilderModes/ClassicModes/DrawGeometryMode.cs +++ b/Source/BuilderModes/ClassicModes/DrawGeometryMode.cs @@ -37,7 +37,7 @@ using CodeImp.DoomBuilder.Controls; namespace CodeImp.DoomBuilder.BuilderModes.Editing { - [EditMode(SwitchAction = "drawlinesmode")] + [EditMode(SwitchAction = "drawlinesmode", Volatile = true)] public class DrawGeometryMode : ClassicMode { @@ -733,6 +733,19 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing { points.Add(GetCurrentPosition()); Update(); + + // Check if point stitches with the first + if((points.Count > 1) && (points[points.Count - 1].stitch)) + { + Vector2D p1 = points[0].pos; + Vector2D p2 = points[points.Count - 1].pos; + Vector2D delta = p1 - p2; + if((Math.Abs(delta.x) <= 0.001f) && (Math.Abs(delta.y) <= 0.001f)) + { + // Finish drawing + FinishDraw(); + } + } } } diff --git a/Source/Controls/ActionManager.cs b/Source/Controls/ActionManager.cs index b81b060e..814a91b2 100644 --- a/Source/Controls/ActionManager.cs +++ b/Source/Controls/ActionManager.cs @@ -422,13 +422,15 @@ namespace CodeImp.DoomBuilder.Controls if((strippedkey == (int)Keys.ShiftKey) || (strippedkey == (int)Keys.ControlKey)) key = strippedkey; bool repeat = pressedkeys.Contains(strippedkey); - // Invoke event - BeginActionByKey(key, repeat); - Action[] acts = GetActionsByKey(key); - foreach(Action a in acts) if(!activeactions.Contains(a)) activeactions.Add(a); - // Update pressed keys if(!repeat) pressedkeys.Add(strippedkey); + + // Add action to active list + Action[] acts = GetActionsByKey(key); + foreach(Action a in acts) if(!activeactions.Contains(a)) activeactions.Add(a); + + // Invoke actions + BeginActionByKey(key, repeat); } // This notifies a key has been released @@ -443,6 +445,16 @@ namespace CodeImp.DoomBuilder.Controls // End actions that no longer match EndActiveActions(); } + + // This releases all pressed keys + public void ReleaseAllKeys() + { + // Clear pressed keys + pressedkeys.Clear(); + + // End actions + EndActiveActions(); + } // This updates the modifiers public void UpdateModifiers(int mods) @@ -461,10 +473,18 @@ namespace CodeImp.DoomBuilder.Controls foreach(KeyValuePair a in actions) { // This action is associated with this key? - if(a.Value.KeyMatches(key) && (a.Value.Repeat || !repeated)) + if(a.Value.KeyMatches(key)) { - // Invoke action - a.Value.Begin(); + // Allowed to repeat? + if(a.Value.Repeat || !repeated) + { + // Invoke action + a.Value.Begin(); + } + else + { + //General.WriteLogLine("Action \"" + a.Value.Name + "\" failed because it does not support repeating activation!"); + } } } } diff --git a/Source/Editing/EditMode.cs b/Source/Editing/EditMode.cs index 124ed078..c417a19a 100644 --- a/Source/Editing/EditMode.cs +++ b/Source/Editing/EditMode.cs @@ -49,6 +49,9 @@ namespace CodeImp.DoomBuilder.Editing #region ================== Variables + // Attributes + private EditModeAttribute attributes; + // Disposing protected bool isdisposed = false; @@ -58,6 +61,8 @@ namespace CodeImp.DoomBuilder.Editing public bool IsDisposed { get { return isdisposed; } } + public EditModeAttribute Attributes { get { return attributes; } } + // Unless overriden, this returns the name of this mode // for checking the appropriate button on the toolbar. public virtual string EditModeButtonName { get { return GetType().Name; } } @@ -71,6 +76,20 @@ namespace CodeImp.DoomBuilder.Editing /// public EditMode() { + // Fetch attributes + object[] attrs = this.GetType().GetCustomAttributes(true); + foreach(object a in attrs) + { + if(a is EditModeAttribute) + { + attributes = (EditModeAttribute)a; + break; + } + } + + // No attributes found? + if(attributes == null) throw new Exception("Editing mode \"" + this.GetType().Name + "\" is missing EditMode attributes!"); + // We have no destructor GC.SuppressFinalize(this); } diff --git a/Source/Editing/EditModeAttribute.cs b/Source/Editing/EditModeAttribute.cs index 8c098fa0..b530d9b5 100644 --- a/Source/Editing/EditModeAttribute.cs +++ b/Source/Editing/EditModeAttribute.cs @@ -42,6 +42,7 @@ namespace CodeImp.DoomBuilder.Editing private string buttondesc; private int buttonorder; private bool configspecific; + private bool isvolatile; #endregion @@ -78,6 +79,13 @@ namespace CodeImp.DoomBuilder.Editing /// public bool ConfigSpecific { get { return configspecific; } set { configspecific = value; } } + /// + /// When set to true, this mode is cancelled when core actions like + /// undo and save are performed. The editing mode should then return to + /// a non-volatile mode. + /// + public bool Volatile { get { return isvolatile; } set { isvolatile = value; } } + #endregion #region ================== Constructor / Disposer diff --git a/Source/Editing/UndoManager.cs b/Source/Editing/UndoManager.cs index da9e1286..57a9e6b0 100644 --- a/Source/Editing/UndoManager.cs +++ b/Source/Editing/UndoManager.cs @@ -209,6 +209,9 @@ namespace CodeImp.DoomBuilder.Editing // Anything to undo? if(undos.Count > 0) { + // Cancel volatile mode, if any + General.CancelVolatileMode(); + // Get undo snapshot u = undos[0]; undos.RemoveAt(0); @@ -245,6 +248,9 @@ namespace CodeImp.DoomBuilder.Editing // Anything to redo? if(redos.Count > 0) { + // Cancel volatile mode, if any + General.CancelVolatileMode(); + // Get redo snapshot r = redos[0]; redos.RemoveAt(0); diff --git a/Source/General/General.cs b/Source/General/General.cs index ad7604d9..06845ea5 100644 --- a/Source/General/General.cs +++ b/Source/General/General.cs @@ -543,13 +543,23 @@ namespace CodeImp.DoomBuilder #region ================== Management + // This cancels a volatile mode + internal static void CancelVolatileMode() + { + if((map != null) && (map.Mode != null) && map.Mode.Attributes.Volatile) + map.Mode.Cancel(); + } + // This creates a new map [BeginAction("newmap")] internal static void NewMap() { MapOptions newoptions = new MapOptions(); MapOptionsForm optionswindow; - + + // Cancel volatile mode, if any + General.CancelVolatileMode(); + // Ask the user to save changes (if any) if(General.AskSaveMap()) { @@ -596,6 +606,9 @@ namespace CodeImp.DoomBuilder [BeginAction("closemap")] internal static void CloseMap() { + // Cancel volatile mode, if any + General.CancelVolatileMode(); + // Ask the user to save changes (if any) if(General.AskSaveMap()) { @@ -625,7 +638,10 @@ namespace CodeImp.DoomBuilder internal static void OpenMap() { OpenFileDialog openfile; - + + // Cancel volatile mode, if any + General.CancelVolatileMode(); + // Open map file dialog openfile = new OpenFileDialog(); openfile.Filter = "Doom WAD Files (*.wad)|*.wad"; @@ -649,6 +665,9 @@ namespace CodeImp.DoomBuilder { OpenMapOptionsForm openmapwindow; + // Cancel volatile mode, if any + General.CancelVolatileMode(); + // Ask the user to save changes (if any) if(General.AskSaveMap()) { @@ -693,14 +712,21 @@ namespace CodeImp.DoomBuilder } // This saves the current map + // Returns tre when saved, false when cancelled or failed [BeginAction("savemap")] - internal static void SaveMap() + internal static void ActionSaveMap() { SaveMap(); } + internal static bool SaveMap() { + bool result = false; + + // Cancel volatile mode, if any + General.CancelVolatileMode(); + // Check if a wad file is known if(map.FilePathName == "") { // Call to SaveMapAs - SaveMapAs(); + result = SaveMapAs(); } else { @@ -713,6 +739,7 @@ namespace CodeImp.DoomBuilder { // Add recent file mainwindow.AddRecentFile(map.FilePathName); + result = true; } // All done @@ -720,14 +747,23 @@ namespace CodeImp.DoomBuilder mainwindow.DisplayReady(); Cursor.Current = Cursors.Default; } + + return result; } + // This saves the current map as a different file + // Returns tre when saved, false when cancelled or failed [BeginAction("savemapas")] - internal static void SaveMapAs() + internal static void ActionSaveMapAs() { SaveMapAs(); } + internal static bool SaveMapAs() { SaveFileDialog savefile; + bool result = false; + // Cancel volatile mode, if any + General.CancelVolatileMode(); + // Show save as dialog savefile = new SaveFileDialog(); savefile.Filter = "Doom WAD Files (*.wad)|*.wad"; @@ -747,6 +783,7 @@ namespace CodeImp.DoomBuilder { // Add recent file mainwindow.AddRecentFile(map.FilePathName); + result = true; } // All done @@ -754,6 +791,8 @@ namespace CodeImp.DoomBuilder mainwindow.DisplayReady(); Cursor.Current = Cursors.Default; } + + return result; } // This asks to save the map if needed @@ -769,8 +808,8 @@ namespace CodeImp.DoomBuilder result = MessageBox.Show(mainwindow, "Do you want to save changes to " + map.FileTitle + " (" + map.Options.CurrentName + ")?", Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); if(result == DialogResult.Yes) { - // TODO: Save map - + // Save map and return true on success + return SaveMap(); } else if(result == DialogResult.Cancel) { diff --git a/Source/General/MapManager.cs b/Source/General/MapManager.cs index f2e16576..1729252e 100644 --- a/Source/General/MapManager.cs +++ b/Source/General/MapManager.cs @@ -951,6 +951,9 @@ namespace CodeImp.DoomBuilder [BeginAction("mapoptions")] internal void ShowMapOptions() { + // Cancel volatile mode, if any + General.CancelVolatileMode(); + // Show map options dialog MapOptionsForm optionsform = new MapOptionsForm(options); if(optionsform.ShowDialog(General.MainWindow) == DialogResult.OK) diff --git a/Source/Interface/IMainForm.cs b/Source/Interface/IMainForm.cs index 9874f63f..d32d6db8 100644 --- a/Source/Interface/IMainForm.cs +++ b/Source/Interface/IMainForm.cs @@ -48,6 +48,7 @@ namespace CodeImp.DoomBuilder.Interface bool MouseInDisplay { get; } bool AutoMerge { get; } bool SnapToGrid { get; } + bool MouseExclusive { get; } // Methods void DisplayReady(); @@ -65,6 +66,8 @@ namespace CodeImp.DoomBuilder.Interface void SetProcessorState(bool on); void StartExclusiveMouseInput(); void StopExclusiveMouseInput(); + void BreakExclusiveMouseInput(); + void ResumeExclusiveMouseInput(); bool CheckActionActive(Assembly assembly, string actionname); } } diff --git a/Source/Interface/MainForm.Designer.cs b/Source/Interface/MainForm.Designer.cs index 564e9d2c..fc1697b7 100644 --- a/Source/Interface/MainForm.Designer.cs +++ b/Source/Interface/MainForm.Designer.cs @@ -923,7 +923,9 @@ namespace CodeImp.DoomBuilder.Interface this.Name = "MainForm"; this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; this.Text = "Doom Builder"; + this.Deactivate += new System.EventHandler(this.MainForm_Deactivate); this.Resize += new System.EventHandler(this.MainForm_Resize); + this.Activated += new System.EventHandler(this.MainForm_Activated); this.Move += new System.EventHandler(this.MainForm_Move); this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.MainForm_KeyUp); diff --git a/Source/Interface/MainForm.cs b/Source/Interface/MainForm.cs index 5a471feb..6b553e34 100644 --- a/Source/Interface/MainForm.cs +++ b/Source/Interface/MainForm.cs @@ -67,6 +67,8 @@ namespace CodeImp.DoomBuilder.Interface private bool shift, ctrl, alt; private MouseInput mouseinput; private Rectangle originalclip; + private bool mouseexclusive; + private int mouseexclusivebreaklevel; // Recent files private ToolStripMenuItem[] recentitems; @@ -85,7 +87,8 @@ namespace CodeImp.DoomBuilder.Interface internal RenderTargetControl Display { get { return display; } } public bool SnapToGrid { get { return buttonsnaptogrid.Checked; } } public bool AutoMerge { get { return buttonautomerge.Checked; } } - + public bool MouseExclusive { get { return mouseexclusive; } } + #endregion #region ================== Constructor / Disposer @@ -184,6 +187,23 @@ namespace CodeImp.DoomBuilder.Interface lastsize = this.Size; } } + + // Window receives focus + private void MainForm_Activated(object sender, EventArgs e) + { + // Resume any exclusive mouse input + ResumeExclusiveMouseInput(); + } + + // Window loses focus + private void MainForm_Deactivate(object sender, EventArgs e) + { + // Release all pressed keys + General.Actions.ReleaseAllKeys(); + + // Stop exclusive mouse input + BreakExclusiveMouseInput(); + } // Window is moved private void MainForm_Move(object sender, EventArgs e) @@ -749,14 +769,12 @@ namespace CodeImp.DoomBuilder.Interface return General.Actions.CheckActionActive(assembly, actionname); } - // This requests exclusive mouse input - public void StartExclusiveMouseInput() + // This is a tool to lock the mouse in exclusive mode + private void StartMouseExclusive() { - // Only when not already in exclusive mode + // Not already locked? if(mouseinput == null) { - General.WriteLogLine("Starting exclusive mouse input mode..."); - // Start special input device mouseinput = new MouseInput(this); @@ -766,15 +784,13 @@ namespace CodeImp.DoomBuilder.Interface Cursor.Hide(); } } - - // This stops exclusive mouse input - public void StopExclusiveMouseInput() + + // This is a tool to unlock the mouse + private void StopMouseExclusive() { - // Only when in exclusive mode + // Locked? if(mouseinput != null) { - General.WriteLogLine("Stopping exclusive mouse input mode..."); - // Stop special input device mouseinput.Dispose(); mouseinput = null; @@ -784,6 +800,65 @@ namespace CodeImp.DoomBuilder.Interface Cursor.Show(); } } + + // This requests exclusive mouse input + public void StartExclusiveMouseInput() + { + // Only when not already in exclusive mode + if(!mouseexclusive) + { + General.WriteLogLine("Starting exclusive mouse input mode..."); + + // Start special input device + StartMouseExclusive(); + mouseexclusive = true; + mouseexclusivebreaklevel = 0; + } + } + + // This stops exclusive mouse input + public void StopExclusiveMouseInput() + { + // Only when in exclusive mode + if(mouseexclusive) + { + General.WriteLogLine("Stopping exclusive mouse input mode..."); + + // Stop special input device + StopMouseExclusive(); + mouseexclusive = false; + mouseexclusivebreaklevel = 0; + } + } + + // This temporarely breaks exclusive mode and counts the break level + public void BreakExclusiveMouseInput() + { + // Only when in exclusive mode + if(mouseexclusive) + { + // Stop special input device + StopMouseExclusive(); + + // Count the break level + mouseexclusivebreaklevel++; + } + } + + // This resumes exclusive mode from a break when all breaks have been called to resume + public void ResumeExclusiveMouseInput() + { + // Only when in exclusive mode + if(mouseexclusive && (mouseexclusivebreaklevel > 0)) + { + // Decrease break level + mouseexclusivebreaklevel--; + + // All break levels resumed? Then lock the mouse again. + if(mouseexclusivebreaklevel == 0) + StartMouseExclusive(); + } + } // When the mouse wheel is changed protected override void OnMouseWheel(MouseEventArgs e) diff --git a/Source/Map/Linedef.cs b/Source/Map/Linedef.cs index 23411907..45a2e5ac 100644 --- a/Source/Map/Linedef.cs +++ b/Source/Map/Linedef.cs @@ -377,14 +377,6 @@ namespace CodeImp.DoomBuilder.Map public List GetGridIntersections() { List coords = new List(); - /* - float startx = start.Position.x; - float endx = end.Position.x; - float starty = start.Position.y; - float endy = end.Position.y; - float dirx = (float)Math.Sign(endx - startx); - float diry = (float)Math.Sign(endy - starty); - */ Vector2D v = new Vector2D(); float gx, gy, minx, maxx, miny, maxy; bool reversex, reversey;