diff --git a/Source/BuilderModes/BuilderModes.csproj b/Source/BuilderModes/BuilderModes.csproj index c3639f42..9796c004 100644 --- a/Source/BuilderModes/BuilderModes.csproj +++ b/Source/BuilderModes/BuilderModes.csproj @@ -38,6 +38,7 @@ + diff --git a/Source/BuilderModes/LinedefsMode/DragLinedefsMode.cs b/Source/BuilderModes/LinedefsMode/DragLinedefsMode.cs index a7fe5b84..6085b719 100644 --- a/Source/BuilderModes/LinedefsMode/DragLinedefsMode.cs +++ b/Source/BuilderModes/LinedefsMode/DragLinedefsMode.cs @@ -62,7 +62,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing #region ================== Constructor / Disposer // Constructor to start dragging immediately - public DragLinedefsMode(EditMode basemode, Linedef dragitem, Vector2D dragstartmappos) + public DragLinedefsMode(EditMode basemode, Vector2D dragstartmappos) { // Get the nearest vertex for snapping Vertex nearest = MapSet.NearestVertex(General.Map.Map.GetVerticesFromLinesSelection(true), dragstartmappos); diff --git a/Source/BuilderModes/LinedefsMode/LinedefsMode.cs b/Source/BuilderModes/LinedefsMode/LinedefsMode.cs index 93fa6097..f0470696 100644 --- a/Source/BuilderModes/LinedefsMode/LinedefsMode.cs +++ b/Source/BuilderModes/LinedefsMode/LinedefsMode.cs @@ -323,7 +323,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing } // Start dragging the selection - General.Map.ChangeMode(new DragLinedefsMode(new LinedefsMode(), highlighted, mousedownmappos)); + General.Map.ChangeMode(new DragLinedefsMode(new LinedefsMode(), mousedownmappos)); } } } diff --git a/Source/BuilderModes/SectorsMode/DragSectorsMode.cs b/Source/BuilderModes/SectorsMode/DragSectorsMode.cs index b01a064d..637a3ae1 100644 --- a/Source/BuilderModes/SectorsMode/DragSectorsMode.cs +++ b/Source/BuilderModes/SectorsMode/DragSectorsMode.cs @@ -63,7 +63,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing #region ================== Constructor / Disposer // Constructor to start dragging immediately - public DragSectorsMode(EditMode basemode, Sector dragitem, Vector2D dragstartmappos) + public DragSectorsMode(EditMode basemode, Vector2D dragstartmappos) { // Get the nearest vertex for snapping Vertex nearest = MapSet.NearestVertex(General.Map.Map.GetVerticesFromLinesSelection(true), dragstartmappos); diff --git a/Source/BuilderModes/SectorsMode/SectorsMode.cs b/Source/BuilderModes/SectorsMode/SectorsMode.cs index 72dc39ca..5901d99b 100644 --- a/Source/BuilderModes/SectorsMode/SectorsMode.cs +++ b/Source/BuilderModes/SectorsMode/SectorsMode.cs @@ -368,7 +368,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing } // Start dragging the selection - General.Map.ChangeMode(new DragSectorsMode(new SectorsMode(), highlighted, mousedownmappos)); + General.Map.ChangeMode(new DragSectorsMode(new SectorsMode(), mousedownmappos)); } } } diff --git a/Source/BuilderModes/Shared/DragGeometryMode.cs b/Source/BuilderModes/Shared/DragGeometryMode.cs index b4b269db..5e89c651 100644 --- a/Source/BuilderModes/Shared/DragGeometryMode.cs +++ b/Source/BuilderModes/Shared/DragGeometryMode.cs @@ -241,7 +241,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing MoveGeometryRelative(new Vector2D(0f, 0f), false, false); // Make undo for the dragging - General.Map.UndoRedo.CreateUndo("drag vertices", UndoGroup.None, 0, false); + General.Map.UndoRedo.CreateUndo("drag vertices", UndoGroup.None, 0); // Move selected geometry to final position MoveGeometryRelative(mousemappos - dragstartmappos, snaptogrid, snaptonearest); diff --git a/Source/BuilderModes/ThingsMode/DragThingsMode.cs b/Source/BuilderModes/ThingsMode/DragThingsMode.cs new file mode 100644 index 00000000..c2fdeb01 --- /dev/null +++ b/Source/BuilderModes/ThingsMode/DragThingsMode.cs @@ -0,0 +1,369 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Interface; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using System.Drawing; +using CodeImp.DoomBuilder.Editing; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes.Editing +{ + // No action or button for this mode, it is automatic. + // 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] + + public sealed class DragThingsMode : ClassicMode + { + #region ================== Constants + + #endregion + + #region ================== Variables + + // Mode to return to + private EditMode basemode; + + // Mouse position on map where dragging started + private Vector2D dragstartmappos; + + // Item used as reference for snapping to the grid + protected Thing dragitem; + private Vector2D dragitemposition; + + // List of old thing positions + private List oldpositions; + + // List of selected items + private ICollection selectedthings; + + // List of non-selected items + private ICollection unselectedthings; + + // Keep track of view changes + private float lastoffsetx; + private float lastoffsety; + private float lastscale; + + // Options + private bool snaptogrid; // SHIFT to toggle + private bool snaptonearest; // CTRL to enable + + #endregion + + #region ================== Properties + + // Just keep the base mode button checked + public override string EditModeButtonName { get { return basemode.GetType().Name; } } + + #endregion + + #region ================== Constructor / Disposer + + // Constructor to start dragging immediately + public DragThingsMode(EditMode basemode, Vector2D dragstartmappos) + { + // Initialize + this.dragstartmappos = dragstartmappos; + this.basemode = basemode; + + Cursor.Current = Cursors.AppStarting; + + // Get the nearest thing for snapping + dragitem = MapSet.NearestThing(General.Map.Map.GetThingsSelection(true), dragstartmappos); + + // Get selected things + selectedthings = General.Map.Map.GetThingsSelection(true); + unselectedthings = General.Map.Map.GetThingsSelection(false); + + // Make old positions list + // We will use this as reference to move the vertices, or to move them back on cancel + oldpositions = new List(selectedthings.Count); + foreach(Thing t in selectedthings) oldpositions.Add(t.Position); + + // Also keep old position of the dragged item + dragitemposition = dragitem.Position; + + // Keep view information + lastoffsetx = renderer.OffsetX; + lastoffsety = renderer.OffsetY; + lastscale = renderer.Scale; + + Cursor.Current = Cursors.Default; + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if(!isdisposed) + { + // Clean up + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This moves the selected things relatively + // Returns true when things has actually moved + private bool MoveThingsRelative(Vector2D offset, bool snapgrid, bool snapnearest) + { + Vector2D oldpos = dragitem.Position; + Thing nearest; + int i = 0; + + // Snap to nearest? + if(snapnearest) + { + // Find nearest unselected item within selection range + nearest = MapSet.NearestThingSquareRange(unselectedthings, mousemappos, ThingsMode.THING_HIGHLIGHT_RANGE / renderer.Scale); + if(nearest != null) + { + // Move the dragged item + dragitem.Move((Vector2D)nearest.Position); + + // Adjust the offset + offset = (Vector2D)nearest.Position - dragitemposition; + + // Do not snap to grid! + snapgrid = false; + } + } + + // Snap to grid? + if(snapgrid) + { + // Move the dragged item + dragitem.Move(dragitemposition + offset); + + // Snap item to grid + dragitem.SnapToGrid(); + + // Adjust the offset + offset += (Vector2D)dragitem.Position - (dragitemposition + offset); + } + + // Drag item moved? + if(!snapgrid || ((Vector2D)dragitem.Position != oldpos)) + { + // Move selected geometry + foreach(Thing t in selectedthings) + { + // Move vertex from old position relative to the + // mouse position change since drag start + t.Move(oldpositions[i] + offset); + + // Next + i++; + } + + // Moved + return true; + } + else + { + // No changes + return false; + } + } + + // This redraws the display + public unsafe override void RedrawDisplay() + { + bool viewchanged = CheckViewChanged(); + + // Start with a clear display + if(renderer.Start(viewchanged, true)) + { + if(viewchanged) + { + // Render lines and vertices + renderer.RenderLinedefSet(General.Map.Map.Linedefs); + renderer.RenderVerticesSet(General.Map.Map.Vertices); + } + + // Render things + renderer.SetThingsRenderOrder(true); + renderer.RenderThingSet(unselectedthings); + renderer.RenderThingSet(selectedthings); + + // Draw the dragged item highlighted + // This is important to know, because this item is used + // for snapping to the grid and snapping to nearest items + renderer.RenderThing(dragitem, General.Colors.Highlight); + + // Done + renderer.Finish(); + } + } + + // Cancelled + public override void Cancel() + { + // Move geometry back to original position + MoveThingsRelative(new Vector2D(0f, 0f), false, false); + + // If only a single vertex was selected, deselect it now + if(selectedthings.Count == 1) General.Map.Map.ClearSelectedThings(); + + // Update cached values + General.Map.Map.Update(); + + // Cancel base class + base.Cancel(); + + // Return to vertices mode + General.Map.ChangeMode(basemode); + } + + // Mode engages + public override void Engage() + { + base.Engage(); + } + + // Disenagaging + public override void Disengage() + { + base.Disengage(); + Cursor.Current = Cursors.AppStarting; + + // When not cancelled + if(!cancelled) + { + // Move geometry back to original position + MoveThingsRelative(new Vector2D(0f, 0f), false, false); + + // Make undo for the dragging + General.Map.UndoRedo.CreateUndo("drag things", UndoGroup.None, 0); + + // Move selected geometry to final position + MoveThingsRelative(mousemappos - dragstartmappos, snaptogrid, snaptonearest); + + // Update cached values + General.Map.Map.Update(); + + // Map is changed + General.Map.IsChanged = true; + } + + // Hide highlight info + General.Interface.HideInfo(); + + // Done + Cursor.Current = Cursors.Default; + } + + // This checks if the view offset/zoom changed and updates the check + protected bool CheckViewChanged() + { + bool viewchanged = false; + + // View changed? + if(renderer.OffsetX != lastoffsetx) viewchanged = true; + if(renderer.OffsetY != lastoffsety) viewchanged = true; + if(renderer.Scale != lastscale) viewchanged = true; + + // Keep view information + lastoffsetx = renderer.OffsetX; + lastoffsety = renderer.OffsetY; + lastscale = renderer.Scale; + + // Return result + return viewchanged; + } + + // This updates the dragging + private void Update() + { + snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid; + snaptonearest = General.Interface.CtrlState; + + // Move selected geometry + if(MoveThingsRelative(mousemappos - dragstartmappos, snaptogrid, snaptonearest)) + { + // Update cached values + //General.Map.Map.Update(true, false); + General.Map.Map.Update(); + + // Redraw + General.Interface.RedrawDisplay(); + } + } + + // Mouse moving + public override void MouseMove(MouseEventArgs e) + { + base.MouseMove(e); + Update(); + } + + // Mouse button released + public override void MouseUp(MouseEventArgs e) + { + base.MouseUp(e); + + // Is the editing button released? + if(e.Button == EditMode.EDIT_BUTTON) + { + // Just return to vertices mode, geometry will be merged on disengage. + General.Map.ChangeMode(basemode); + } + } + + // When a key is released + public override void KeyUp(KeyEventArgs e) + { + base.KeyUp(e); + if(snaptogrid != General.Interface.ShiftState ^ General.Interface.SnapToGrid) Update(); + if(snaptonearest != General.Interface.CtrlState) Update(); + } + + // When a key is pressed + public override void KeyDown(KeyEventArgs e) + { + base.KeyDown(e); + if(snaptogrid != General.Interface.ShiftState ^ General.Interface.SnapToGrid) Update(); + if(snaptonearest != General.Interface.CtrlState) Update(); + } + + #endregion + } +} diff --git a/Source/BuilderModes/ThingsMode/ThingsMode.cs b/Source/BuilderModes/ThingsMode/ThingsMode.cs index 710c0b1a..88a8cfdd 100644 --- a/Source/BuilderModes/ThingsMode/ThingsMode.cs +++ b/Source/BuilderModes/ThingsMode/ThingsMode.cs @@ -44,7 +44,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing { #region ================== Constants - protected const float THING_HIGHLIGHT_RANGE = 10f; + public const float THING_HIGHLIGHT_RANGE = 10f; #endregion @@ -254,6 +254,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing if(selected.Count > 0) { // Show thing edit dialog + // TODO // When a single thing was selected, deselect it now if(selected.Count == 1) General.Map.Map.ClearSelectedThings(); @@ -264,6 +265,36 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing } } } + + // Mouse wants to drag + protected override void DragStart(MouseEventArgs e) + { + base.DragStart(e); + + // Which button is used? + if(e.Button == EditMode.SELECT_BUTTON) + { + // Make selection + + } + else if(e.Button == EditMode.EDIT_BUTTON) + { + // Anything highlighted? + if((highlighted != null) && !highlighted.IsDisposed) + { + // Highlighted item not selected? + if(!highlighted.Selected) + { + // Select only this sector for dragging + General.Map.Map.ClearSelectedThings(); + highlighted.Selected = true; + } + + // Start dragging the selection + General.Map.ChangeMode(new DragThingsMode(new ThingsMode(), mousedownmappos)); + } + } + } #endregion } diff --git a/Source/Editing/UndoManager.cs b/Source/Editing/UndoManager.cs index ea50cde3..5daac5d0 100644 --- a/Source/Editing/UndoManager.cs +++ b/Source/Editing/UndoManager.cs @@ -142,7 +142,7 @@ namespace CodeImp.DoomBuilder.Editing #region ================== Public Methods // This makes an undo and returns the unique ticket id - public int CreateUndo(string description, UndoGroup group, int grouptag, bool allow3dchange) + public int CreateUndo(string description, UndoGroup group, int grouptag) { UndoSnapshot u; @@ -155,7 +155,7 @@ namespace CodeImp.DoomBuilder.Editing if(++ticketid == int.MaxValue) ticketid = 1; // Make a snapshot - u = new UndoSnapshot(description, allow3dchange, General.Map.Map.Clone(), ticketid); + u = new UndoSnapshot(description, General.Map.Map.Clone(), ticketid); // Put it on the stack undos.Insert(0, u); diff --git a/Source/Editing/UndoSnapshot.cs b/Source/Editing/UndoSnapshot.cs index 8e3c87e4..3682539a 100644 --- a/Source/Editing/UndoSnapshot.cs +++ b/Source/Editing/UndoSnapshot.cs @@ -39,15 +39,13 @@ namespace CodeImp.DoomBuilder.Editing { public MapSet map; public string description; - public bool allow3dchange; // True when allowed to change in 3D mode public int ticketid; // For safe withdrawing // Constructor - public UndoSnapshot(string description, bool allow3dchange, MapSet map, int ticketid) + public UndoSnapshot(string description, MapSet map, int ticketid) { this.ticketid = ticketid; this.description = description; - this.allow3dchange = allow3dchange; this.map = map; } @@ -56,7 +54,6 @@ namespace CodeImp.DoomBuilder.Editing { this.ticketid = info.ticketid; this.description = info.description; - this.allow3dchange = info.allow3dchange; this.map = map; } } diff --git a/Source/Interface/LinedefEditForm.cs b/Source/Interface/LinedefEditForm.cs index 1a45d5e9..990f0605 100644 --- a/Source/Interface/LinedefEditForm.cs +++ b/Source/Interface/LinedefEditForm.cs @@ -237,7 +237,7 @@ namespace CodeImp.DoomBuilder.Interface // Make undo if(lines.Count > 1) undodesc = lines.Count + " linedefs"; - General.Map.UndoRedo.CreateUndo("edit " + undodesc, UndoGroup.None, 0, false); + General.Map.UndoRedo.CreateUndo("edit " + undodesc, UndoGroup.None, 0); // Go for all the lines foreach(Linedef l in lines) diff --git a/Source/Interface/MainForm.cs b/Source/Interface/MainForm.cs index 9f4a6e38..50b45b25 100644 --- a/Source/Interface/MainForm.cs +++ b/Source/Interface/MainForm.cs @@ -314,8 +314,6 @@ namespace CodeImp.DoomBuilder.Interface // This updates the status icon internal void UpdateStatusIcon() { - if(IsDisposed) return; - // From another thread? if(statusbar.InvokeRequired) { diff --git a/Source/Interface/SectorEditForm.cs b/Source/Interface/SectorEditForm.cs index 9e1e1143..914e2ede 100644 --- a/Source/Interface/SectorEditForm.cs +++ b/Source/Interface/SectorEditForm.cs @@ -113,7 +113,7 @@ namespace CodeImp.DoomBuilder.Interface // Make undo if(sectors.Count > 1) undodesc = sectors.Count + " sectors"; - General.Map.UndoRedo.CreateUndo("edit " + undodesc, UndoGroup.None, 0, false); + General.Map.UndoRedo.CreateUndo("edit " + undodesc, UndoGroup.None, 0); // Go for all sectors foreach(Sector s in sectors) diff --git a/Source/Map/MapSet.cs b/Source/Map/MapSet.cs index 684c4475..64b9b295 100644 --- a/Source/Map/MapSet.cs +++ b/Source/Map/MapSet.cs @@ -619,7 +619,7 @@ namespace CodeImp.DoomBuilder.Map if(General.MainWindow.AutoMerge) { // Make undo for the stitching - stitchundo = General.Map.UndoRedo.CreateUndo("stitch geometry", UndoGroup.None, 0, false); + stitchundo = General.Map.UndoRedo.CreateUndo("stitch geometry", UndoGroup.None, 0); // Find lines that moved during the drag movinglines = LinedefsFromSelectedVertices(false, true, true); @@ -968,6 +968,30 @@ namespace CodeImp.DoomBuilder.Map return closest; } + // This finds the thing closest to the specified position + public static Thing NearestThing(ICollection selection, Vector2D pos) + { + Thing closest = null; + float distance = float.MaxValue; + float d; + + // Go for all things in selection + foreach(Thing t in selection) + { + // Calculate distance and check if closer than previous find + d = t.DistanceToSq(pos); + if(d < distance) + { + // This one is closer + closest = t; + distance = d; + } + } + + // Return result + return closest; + } + // This finds the vertex closest to the specified position public static Vertex NearestVertexSquareRange(ICollection selection, Vector2D pos, float maxrange) { diff --git a/Source/Map/Thing.cs b/Source/Map/Thing.cs index c6076e4b..fa25b873 100644 --- a/Source/Map/Thing.cs +++ b/Source/Map/Thing.cs @@ -224,6 +224,16 @@ namespace CodeImp.DoomBuilder.Map #region ================== Changes + // This moves the thing + // NOTE: This does not update sector! (call DetermineSector) + public void Move(Vector2D newpos) + { + // Change position + this.x = (int)newpos.x; + this.y = (int)newpos.y; + this.pos = new Vector3D(newpos.x, newpos.y, zoffset); + } + // This moves the thing // NOTE: This does not update sector! (call DetermineSector) public void Move(int x, int y, int zoffset) @@ -289,6 +299,13 @@ namespace CodeImp.DoomBuilder.Map #region ================== Methods + // This snaps the vertex to the grid + public void SnapToGrid() + { + // Calculate nearest grid coordinates + this.Move(General.Map.Grid.SnappedToGrid((Vector2D)pos)); + } + // This returns the distance from given coordinates public float DistanceToSq(Vector2D p) {