mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2025-01-18 14:31:50 +00:00
heh
This commit is contained in:
parent
59ce633329
commit
4f8cfec1c1
21 changed files with 1093 additions and 541 deletions
BIN
Resources/Icons/Grid3.png
Normal file
BIN
Resources/Icons/Grid3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 145 B |
BIN
Resources/Icons/Grid4.png
Normal file
BIN
Resources/Icons/Grid4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 301 B |
|
@ -45,6 +45,7 @@
|
|||
-->
|
||||
<ItemGroup>
|
||||
<Compile Include="Config\GameConfiguration.cs" />
|
||||
<Compile Include="Config\ProgramConfiguration.cs" />
|
||||
<Compile Include="Config\ThingCategory.cs" />
|
||||
<Compile Include="Config\ThingTypeInfo.cs" />
|
||||
<Compile Include="Controls\ActionAttribute.cs" />
|
||||
|
@ -284,6 +285,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Resources\Builder.ico" />
|
||||
<None Include="Resources\Grid4.png" />
|
||||
<None Include="Resources\Redo.png" />
|
||||
<None Include="Resources\Undo.png" />
|
||||
<None Include="Resources\Grid2.png" />
|
||||
|
|
|
@ -74,7 +74,7 @@ namespace CodeImp.DoomBuilder.Config
|
|||
this.nodebuilder3d = General.Settings.ReadSetting("configurations." + settingskey + ".nodebuilder3d", "");
|
||||
this.testprogram = General.Settings.ReadSetting("configurations." + settingskey + ".testprogram", "");
|
||||
this.testparameters = General.Settings.ReadSetting("configurations." + settingskey + ".testparameters", "");
|
||||
this.resources = new DataLocationList(General.Settings, "configurations." + settingskey + ".resources");
|
||||
this.resources = new DataLocationList(General.Settings.Config, "configurations." + settingskey + ".resources");
|
||||
}
|
||||
|
||||
// Constructor
|
||||
|
@ -102,7 +102,7 @@ namespace CodeImp.DoomBuilder.Config
|
|||
General.Settings.WriteSetting("configurations." + settingskey + ".nodebuilder3d", nodebuilder3d);
|
||||
General.Settings.WriteSetting("configurations." + settingskey + ".testprogram", testprogram);
|
||||
General.Settings.WriteSetting("configurations." + settingskey + ".testparameters", testparameters);
|
||||
resources.WriteToConfig(General.Settings, "configurations." + settingskey + ".resources");
|
||||
resources.WriteToConfig(General.Settings.Config, "configurations." + settingskey + ".resources");
|
||||
}
|
||||
|
||||
// String representation
|
||||
|
|
175
Source/Config/ProgramConfiguration.cs
Normal file
175
Source/Config/ProgramConfiguration.cs
Normal file
|
@ -0,0 +1,175 @@
|
|||
|
||||
#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 CodeImp.DoomBuilder.IO;
|
||||
using CodeImp.DoomBuilder.Data;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Forms;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CodeImp.DoomBuilder.Config
|
||||
{
|
||||
public class ProgramConfiguration
|
||||
{
|
||||
#region ================== Constants
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Variables
|
||||
|
||||
// Original configuration
|
||||
private Configuration cfg;
|
||||
|
||||
// Cached variables
|
||||
private bool blackbrowsers;
|
||||
private float stitchdistance;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Properties
|
||||
|
||||
public Configuration Config { get { return cfg; } }
|
||||
public bool BlackBrowsers { get { return blackbrowsers; } set { blackbrowsers = value; } }
|
||||
public float StitchDistance { get { return stitchdistance; } set { stitchdistance = value; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Constructor / Disposer
|
||||
|
||||
// Constructor
|
||||
public ProgramConfiguration()
|
||||
{
|
||||
// We have no destructor
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Loading / Saving
|
||||
|
||||
// This loads the program configuration
|
||||
public bool Load(string cfgfilepathname, string defaultfilepathname)
|
||||
{
|
||||
// First parse it
|
||||
if(Read(cfgfilepathname, defaultfilepathname))
|
||||
{
|
||||
// Read the cache variables
|
||||
blackbrowsers = cfg.ReadSetting("blackbrowsers", false);
|
||||
stitchdistance = cfg.ReadSetting("stitchdistance", 2.0f);
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Failed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// This saves the program configuration
|
||||
public void Save(string filepathname)
|
||||
{
|
||||
// Write the cache variables
|
||||
cfg.WriteSetting("blackbrowsers", blackbrowsers);
|
||||
cfg.WriteSetting("stitchdistance", stitchdistance);
|
||||
|
||||
// Save settings configuration
|
||||
General.WriteLogLine("Saving program configuration...");
|
||||
cfg.SaveConfiguration(filepathname);
|
||||
}
|
||||
|
||||
// This reads the configuration
|
||||
private bool Read(string cfgfilepathname, string defaultfilepathname)
|
||||
{
|
||||
DialogResult result;
|
||||
|
||||
// Check if no config for this user exists yet
|
||||
if(!File.Exists(cfgfilepathname))
|
||||
{
|
||||
// Copy new configuration
|
||||
General.WriteLogLine("Local user program configuration is missing!");
|
||||
File.Copy(defaultfilepathname, cfgfilepathname);
|
||||
General.WriteLogLine("New program configuration copied for local user");
|
||||
}
|
||||
|
||||
// Load it
|
||||
cfg = new Configuration(cfgfilepathname, true);
|
||||
if(cfg.ErrorResult != 0)
|
||||
{
|
||||
// Error in configuration
|
||||
// Ask user for a new copy
|
||||
result = General.ShowErrorMessage("Error in program configuration near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription, MessageBoxButtons.YesNoCancel);
|
||||
if(result == DialogResult.Yes)
|
||||
{
|
||||
// Remove old configuration and make a new copy
|
||||
General.WriteLogLine("User requested a new copy of the program configuration");
|
||||
File.Delete(cfgfilepathname);
|
||||
File.Copy(defaultfilepathname, cfgfilepathname);
|
||||
General.WriteLogLine("New program configuration copied for local user");
|
||||
|
||||
// Load it
|
||||
cfg = new Configuration(cfgfilepathname, true);
|
||||
if(cfg.ErrorResult != 0)
|
||||
{
|
||||
// Error in configuration
|
||||
General.WriteLogLine("Error in program configuration near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription);
|
||||
General.ShowErrorMessage("Default program configuration is corrupted. Please re-install Doom Builder.", MessageBoxButtons.OK);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if(result == DialogResult.Cancel)
|
||||
{
|
||||
// User requested to cancel startup
|
||||
General.WriteLogLine("User cancelled startup");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Methods
|
||||
|
||||
// ReadSetting
|
||||
public string ReadSetting(string setting, string defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); }
|
||||
public int ReadSetting(string setting, int defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); }
|
||||
public float ReadSetting(string setting, float defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); }
|
||||
public short ReadSetting(string setting, short defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); }
|
||||
public long ReadSetting(string setting, long defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); }
|
||||
public bool ReadSetting(string setting, bool defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); }
|
||||
public byte ReadSetting(string setting, byte defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); }
|
||||
public IDictionary ReadSetting(string setting, IDictionary defaultsetting) { return cfg.ReadSetting(setting, defaultsetting); }
|
||||
|
||||
// WriteSetting
|
||||
public bool WriteSetting(string setting, object settingvalue) { return cfg.WriteSetting(setting, settingvalue); }
|
||||
public bool WriteSetting(string setting, object settingvalue, string pathseperator) { return cfg.WriteSetting(setting, settingvalue, pathseperator); }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -230,11 +230,15 @@ namespace CodeImp.DoomBuilder.Editing
|
|||
// Go for all vertices
|
||||
foreach(Vertex v in General.Map.Map.Vertices)
|
||||
{
|
||||
// Adjust boundaries by vertices
|
||||
if(v.Position.x < left) left = v.Position.x;
|
||||
if(v.Position.x > right) right = v.Position.x;
|
||||
if(v.Position.y < top) top = v.Position.y;
|
||||
if(v.Position.y > bottom) bottom = v.Position.y;
|
||||
// Vertex used?
|
||||
if(v.Linedefs.Count > 0)
|
||||
{
|
||||
// Adjust boundaries by vertices
|
||||
if(v.Position.x < left) left = v.Position.x;
|
||||
if(v.Position.x > right) right = v.Position.x;
|
||||
if(v.Position.y < top) top = v.Position.y;
|
||||
if(v.Position.y > bottom) bottom = v.Position.y;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate width/height
|
||||
|
|
|
@ -57,10 +57,13 @@ namespace CodeImp.DoomBuilder.Editing
|
|||
private List<Vector2D> oldpositions;
|
||||
|
||||
// List of non-selected items
|
||||
private List<Vertex> others;
|
||||
private ICollection<Vertex> unselectedverts;
|
||||
|
||||
// List of unstable lines
|
||||
private ICollection<Linedef> unstablelines;
|
||||
|
||||
// Options
|
||||
private bool snaptogrid; // SHIFT to disable
|
||||
private bool snaptogrid; // SHIFT to toggle
|
||||
private bool snaptonearest; // CTRL to enable
|
||||
|
||||
#endregion
|
||||
|
@ -78,6 +81,8 @@ namespace CodeImp.DoomBuilder.Editing
|
|||
this.dragitem = dragitem;
|
||||
this.dragstartmappos = dragstartmappos;
|
||||
|
||||
Cursor.Current = Cursors.AppStarting;
|
||||
|
||||
// Make old positions list
|
||||
// We will use this as reference to move the vertices, or to move them back on cancel
|
||||
oldpositions = new List<Vector2D>(General.Map.Selection.Vertices.Count);
|
||||
|
@ -88,9 +93,14 @@ namespace CodeImp.DoomBuilder.Editing
|
|||
|
||||
// Make list of non-selected vertices
|
||||
// This will be used for snapping to nearest items
|
||||
others = new List<Vertex>(General.Map.Map.Vertices.Count);
|
||||
foreach(Vertex v in General.Map.Map.Vertices) if(v.Selected == 0) others.Add(v);
|
||||
unselectedverts = General.Map.Map.InvertedCollection(General.Map.Selection.Vertices);
|
||||
|
||||
// Make list of unstable lines only
|
||||
// These will have their length displayed during the drag
|
||||
unstablelines = General.Map.Map.LinedefsFromSelectedVertices(false, true);
|
||||
|
||||
Cursor.Current = Cursors.Default;
|
||||
|
||||
// We have no destructor
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
@ -124,7 +134,7 @@ namespace CodeImp.DoomBuilder.Editing
|
|||
if(snapnearest)
|
||||
{
|
||||
// Find nearest unselected item within selection range
|
||||
nearest = MapSet.NearestVertexSquareRange(others, mousemappos, VerticesMode.VERTEX_HIGHLIGHT_RANGE / renderer.Scale);
|
||||
nearest = MapSet.NearestVertexSquareRange(unselectedverts, mousemappos, VerticesMode.VERTEX_HIGHLIGHT_RANGE / renderer.Scale);
|
||||
if(nearest != null)
|
||||
{
|
||||
// Move the dragged item
|
||||
|
@ -181,6 +191,9 @@ namespace CodeImp.DoomBuilder.Editing
|
|||
// Move geometry back to original position
|
||||
MoveGeometryRelative(new Vector2D(0f, 0f), false, false);
|
||||
|
||||
// If only a single vertex was selected, deselect it now
|
||||
if(General.Map.Selection.Vertices.Count == 1) General.Map.Selection.ClearVertices();
|
||||
|
||||
// Update cached values
|
||||
General.Map.Map.Update();
|
||||
|
||||
|
@ -203,6 +216,11 @@ namespace CodeImp.DoomBuilder.Editing
|
|||
// Disenagaging
|
||||
public override void Disengage()
|
||||
{
|
||||
ICollection<Linedef> movinglines;
|
||||
ICollection<Linedef> fixedlines;
|
||||
int stitches = 0;
|
||||
int stitchundo;
|
||||
|
||||
base.Disengage();
|
||||
Cursor.Current = Cursors.WaitCursor;
|
||||
|
||||
|
@ -212,16 +230,47 @@ namespace CodeImp.DoomBuilder.Editing
|
|||
// Move geometry back to original position
|
||||
MoveGeometryRelative(new Vector2D(0f, 0f), false, false);
|
||||
|
||||
// Make undo
|
||||
// Make undo for the dragging
|
||||
General.Map.UndoRedo.CreateUndo("drag vertices", UndoGroup.None, 0, false);
|
||||
|
||||
// Move selected geometry to final position
|
||||
MoveGeometryRelative(mousemappos - dragstartmappos, snaptogrid, snaptonearest);
|
||||
|
||||
// ===== BEGIN GEOMETRY STITCHING
|
||||
|
||||
// TODO: Merge geometry
|
||||
// Make undo for the stitching
|
||||
stitchundo = General.Map.UndoRedo.CreateUndo("stitch geometry", UndoGroup.None, 0, false);
|
||||
|
||||
// Find lines that moved during the drag
|
||||
movinglines = General.Map.Map.LinedefsFromSelectedVertices(true, true);
|
||||
|
||||
// Find all non-moving lines (inverse of movinglines)
|
||||
fixedlines = General.Map.Map.InvertedCollection(movinglines);
|
||||
|
||||
// Join nearby vertices
|
||||
stitches += MapSet.JoinVertices(unselectedverts, General.Map.Selection.Vertices, true, General.Settings.StitchDistance);
|
||||
|
||||
// Update cached values
|
||||
General.Map.Map.Update();
|
||||
|
||||
// Split moving lines with unselected vertices
|
||||
stitches += MapSet.SplitLinesByVertices(movinglines, unselectedverts, General.Settings.StitchDistance);
|
||||
|
||||
// Split non-moving lines with selected vertices
|
||||
stitches += MapSet.SplitLinesByVertices(fixedlines, General.Map.Selection.Vertices, General.Settings.StitchDistance);
|
||||
|
||||
|
||||
// TODO: Join overlapping lines and remove looped lines
|
||||
|
||||
|
||||
// No stitching done? then withdraw undo
|
||||
if(stitches == 0) General.Map.UndoRedo.WithdrawUndo(stitchundo);
|
||||
|
||||
// ===== END GEOMETRY STITCHING
|
||||
|
||||
// If only a single vertex was selected, deselect it now
|
||||
if(General.Map.Selection.Vertices.Count == 1) General.Map.Selection.ClearVertices();
|
||||
|
||||
// Update cached values
|
||||
General.Map.Map.Update();
|
||||
|
||||
|
@ -260,7 +309,7 @@ namespace CodeImp.DoomBuilder.Editing
|
|||
// This updates the dragging
|
||||
private void Update()
|
||||
{
|
||||
snaptogrid = !General.MainWindow.ShiftState;
|
||||
snaptogrid = General.MainWindow.ShiftState ^ General.MainWindow.SnapToGrid;
|
||||
snaptonearest = General.MainWindow.CtrlState;
|
||||
|
||||
// Move selected geometry
|
||||
|
@ -298,7 +347,7 @@ namespace CodeImp.DoomBuilder.Editing
|
|||
public override void KeyUp(KeyEventArgs e)
|
||||
{
|
||||
base.KeyUp(e);
|
||||
if(snaptogrid != !General.MainWindow.ShiftState) Update();
|
||||
if(snaptogrid != General.MainWindow.ShiftState ^ General.MainWindow.SnapToGrid) Update();
|
||||
if(snaptonearest != General.MainWindow.CtrlState) Update();
|
||||
}
|
||||
|
||||
|
@ -306,7 +355,7 @@ namespace CodeImp.DoomBuilder.Editing
|
|||
public override void KeyDown(KeyEventArgs e)
|
||||
{
|
||||
base.KeyDown(e);
|
||||
if(snaptogrid != !General.MainWindow.ShiftState) Update();
|
||||
if(snaptogrid != General.MainWindow.ShiftState ^ General.MainWindow.SnapToGrid) Update();
|
||||
if(snaptonearest != General.MainWindow.CtrlState) Update();
|
||||
}
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ namespace CodeImp.DoomBuilder
|
|||
// Main objects
|
||||
private static Assembly thisasm;
|
||||
private static MainForm mainwindow;
|
||||
private static Configuration settings;
|
||||
private static ProgramConfiguration settings;
|
||||
private static MapManager map;
|
||||
private static ActionManager actions;
|
||||
private static ColorCollection colors;
|
||||
|
@ -120,7 +120,7 @@ namespace CodeImp.DoomBuilder
|
|||
public static string ConfigsPath { get { return configspath; } }
|
||||
public static string CompilersPath { get { return compilerspath; } }
|
||||
public static MainForm MainWindow { get { return mainwindow; } }
|
||||
public static Configuration Settings { get { return settings; } }
|
||||
public static ProgramConfiguration Settings { get { return settings; } }
|
||||
public static ColorCollection Colors { get { return colors; } }
|
||||
public static List<ConfigurationInfo> Configs { get { return configs; } }
|
||||
public static List<NodebuilderInfo> Nodebuilders { get { return nodebuilders; } }
|
||||
|
@ -404,7 +404,9 @@ namespace CodeImp.DoomBuilder
|
|||
|
||||
// Load configuration
|
||||
General.WriteLogLine("Loading program configuration...");
|
||||
if(LoadProgramConfiguration())
|
||||
settings = new ProgramConfiguration();
|
||||
if(settings.Load(Path.Combine(settingspath, SETTINGS_FILE),
|
||||
Path.Combine(apppath, SETTINGS_FILE)))
|
||||
{
|
||||
// Create action manager
|
||||
actions = new ActionManager();
|
||||
|
@ -440,7 +442,7 @@ namespace CodeImp.DoomBuilder
|
|||
|
||||
// Load color settings
|
||||
General.WriteLogLine("Loading color settings...");
|
||||
colors = new ColorCollection(settings);
|
||||
colors = new ColorCollection(settings.Config);
|
||||
|
||||
// Create application clock
|
||||
General.WriteLogLine("Creating application clock...");
|
||||
|
@ -458,57 +460,6 @@ namespace CodeImp.DoomBuilder
|
|||
}
|
||||
}
|
||||
|
||||
// Program configuration
|
||||
private static bool LoadProgramConfiguration()
|
||||
{
|
||||
DialogResult result;
|
||||
|
||||
// Check if no config for this user exists yet
|
||||
if(!File.Exists(Path.Combine(settingspath, SETTINGS_FILE)))
|
||||
{
|
||||
// Copy new configuration
|
||||
General.WriteLogLine("Local user program configuration is missing!");
|
||||
File.Copy(Path.Combine(apppath, SETTINGS_FILE), Path.Combine(settingspath, SETTINGS_FILE));
|
||||
General.WriteLogLine("New program configuration copied for local user");
|
||||
}
|
||||
|
||||
// Load it
|
||||
settings = new Configuration(Path.Combine(settingspath, SETTINGS_FILE), true);
|
||||
if(settings.ErrorResult != 0)
|
||||
{
|
||||
// Error in configuration
|
||||
// Ask user for a new copy
|
||||
result = ShowErrorMessage("Error in program configuration near line " + settings.ErrorLine + ": " + settings.ErrorDescription, MessageBoxButtons.YesNoCancel);
|
||||
if(result == DialogResult.Yes)
|
||||
{
|
||||
// Remove old configuration and make a new copy
|
||||
General.WriteLogLine("User requested a new copy of the program configuration");
|
||||
File.Delete(Path.Combine(settingspath, SETTINGS_FILE));
|
||||
File.Copy(Path.Combine(apppath, SETTINGS_FILE), Path.Combine(settingspath, SETTINGS_FILE));
|
||||
General.WriteLogLine("New program configuration copied for local user");
|
||||
|
||||
// Load it
|
||||
settings = new Configuration(Path.Combine(settingspath, SETTINGS_FILE), true);
|
||||
if(settings.ErrorResult != 0)
|
||||
{
|
||||
// Error in configuration
|
||||
General.WriteLogLine("Error in program configuration near line " + settings.ErrorLine + ": " + settings.ErrorDescription);
|
||||
ShowErrorMessage("Default program configuration is corrupted. Please re-install Doom Builder.", MessageBoxButtons.OK);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if(result == DialogResult.Cancel)
|
||||
{
|
||||
// User requested to cancel startup
|
||||
General.WriteLogLine("User cancelled startup");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Done
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Terminate
|
||||
|
@ -533,7 +484,7 @@ namespace CodeImp.DoomBuilder
|
|||
Direct3D.Terminate();
|
||||
|
||||
// Save colors
|
||||
colors.SaveColors(settings);
|
||||
colors.SaveColors(settings.Config);
|
||||
|
||||
// Save action controls
|
||||
actions.SaveSettings();
|
||||
|
@ -543,7 +494,7 @@ namespace CodeImp.DoomBuilder
|
|||
|
||||
// Save settings configuration
|
||||
General.WriteLogLine("Saving program configuration...");
|
||||
settings.SaveConfiguration(Path.Combine(settingspath, SETTINGS_FILE));
|
||||
settings.Save(Path.Combine(settingspath, SETTINGS_FILE));
|
||||
|
||||
// Application ends here and now
|
||||
General.WriteLogLine("Termination done");
|
||||
|
|
|
@ -891,6 +891,17 @@ namespace CodeImp.DoomBuilder
|
|||
|
||||
#region ================== Methods
|
||||
|
||||
// This clears the selection
|
||||
[Action("clearselection")]
|
||||
public void ClearSelection()
|
||||
{
|
||||
// Clear selection
|
||||
selection.ClearAll();
|
||||
|
||||
// Redraw
|
||||
General.MainWindow.RedrawDisplay();
|
||||
}
|
||||
|
||||
// This sets a new mapset for editing
|
||||
public void ChangeMapSet(MapSet newmap)
|
||||
{
|
||||
|
|
|
@ -80,7 +80,7 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
public void ApplyColorSettings()
|
||||
{
|
||||
// Force black background?
|
||||
if(General.Settings.ReadSetting("blackbrowsers", false))
|
||||
if(General.Settings.BlackBrowsers)
|
||||
{
|
||||
list.BackColor = Color.Black;
|
||||
list.ForeColor = Color.White;
|
||||
|
|
934
Source/Interface/MainForm.Designer.cs
generated
934
Source/Interface/MainForm.Designer.cs
generated
File diff suppressed because it is too large
Load diff
|
@ -76,6 +76,7 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
public bool AltState { get { return alt; } }
|
||||
public bool MouseInDisplay { get { return mouseinside; } }
|
||||
public RenderTargetControl Display { get { return display; } }
|
||||
public bool SnapToGrid { get { return buttonsnaptogrid.Checked; } }
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -876,6 +877,7 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
itemlinedefsmode.Enabled = (General.Map != null);
|
||||
itemsectorsmode.Enabled = (General.Map != null);
|
||||
itemthingsmode.Enabled = (General.Map != null);
|
||||
itemsnaptogrid.Enabled = (General.Map != null);
|
||||
itemundo.Enabled = (General.Map != null) && (General.Map.UndoRedo.NextUndo != null);
|
||||
itemredo.Enabled = (General.Map != null) && (General.Map.UndoRedo.NextRedo != null);
|
||||
|
||||
|
@ -901,6 +903,15 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
buttonredo.Enabled = itemredo.Enabled;
|
||||
buttonundo.ToolTipText = itemundo.Text;
|
||||
buttonredo.ToolTipText = itemredo.Text;
|
||||
buttonsnaptogrid.Enabled = (General.Map != null);
|
||||
}
|
||||
|
||||
// Action to toggle snap to grid
|
||||
[Action("togglesnap")]
|
||||
public void ToggleSnapToGrid()
|
||||
{
|
||||
buttonsnaptogrid.Checked = !buttonsnaptogrid.Checked;
|
||||
itemsnaptogrid.Checked = buttonsnaptogrid.Checked;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -150,6 +150,18 @@
|
|||
<metadata name="toolStripMenuItem5.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="toolStripSeparator10.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="toolStripMenuItem4.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="toolStripSeparator2.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="toolStripSeparator11.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="menumain.Locked" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
|
@ -162,21 +174,12 @@
|
|||
<metadata name="toolbar.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>121, 17</value>
|
||||
</metadata>
|
||||
<metadata name="toolStripSeparator10.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="statusbar.Locked" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="statusbar.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>207, 17</value>
|
||||
</metadata>
|
||||
<metadata name="toolStripMenuItem4.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="toolStripSeparator2.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="panelinfo.Locked" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
|
|
|
@ -111,7 +111,7 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
colorkeywords.Color = General.Colors.Keywords;
|
||||
colorliterals.Color = General.Colors.Literals;
|
||||
colorconstants.Color = General.Colors.Constants;
|
||||
blackbrowsers.Checked = General.Settings.ReadSetting("blackbrowsers", false);
|
||||
blackbrowsers.Checked = General.Settings.BlackBrowsers;
|
||||
|
||||
// Done
|
||||
allowapplycontrol = true;
|
||||
|
@ -312,7 +312,7 @@ namespace CodeImp.DoomBuilder.Interface
|
|||
General.Colors.Literals = colorliterals.Color;
|
||||
General.Colors.Constants = colorconstants.Color;
|
||||
General.Colors.CreateAssistColors();
|
||||
General.Settings.WriteSetting("blackbrowsers", blackbrowsers.Checked);
|
||||
General.Settings.BlackBrowsers = blackbrowsers.Checked;
|
||||
|
||||
// Close
|
||||
this.DialogResult = DialogResult.OK;
|
||||
|
|
|
@ -158,6 +158,26 @@ namespace CodeImp.DoomBuilder.Map
|
|||
|
||||
#region ================== Management
|
||||
|
||||
// This sets new start vertex
|
||||
public void SetStartVertex(Vertex v)
|
||||
{
|
||||
// Change start
|
||||
start.DetachLinedef(startvertexlistitem);
|
||||
start = v;
|
||||
startvertexlistitem = start.AttachLinedef(this);
|
||||
this.updateneeded = true;
|
||||
}
|
||||
|
||||
// This sets new end vertex
|
||||
public void SetEndVertex(Vertex v)
|
||||
{
|
||||
// Change end
|
||||
end.DetachLinedef(endvertexlistitem);
|
||||
end = v;
|
||||
endvertexlistitem = end.AttachLinedef(this);
|
||||
this.updateneeded = true;
|
||||
}
|
||||
|
||||
// This copies all properties to another line
|
||||
public void CopyPropertiesTo(Linedef l)
|
||||
{
|
||||
|
@ -313,6 +333,36 @@ namespace CodeImp.DoomBuilder.Map
|
|||
// Calculate and return side information
|
||||
return (p.y - v1.y) * (v2.x - v1.x) - (p.x - v1.x) * (v2.y - v1.y);
|
||||
}
|
||||
|
||||
// This splits this line by vertex v
|
||||
// Returns the new line resulting from the split
|
||||
public Linedef Split(Vertex v)
|
||||
{
|
||||
Linedef nl;
|
||||
Sidedef nsd;
|
||||
|
||||
// Copy linedef and change vertices
|
||||
nl = map.CreateLinedef(v, end);
|
||||
CopyPropertiesTo(nl);
|
||||
SetEndVertex(v);
|
||||
|
||||
// Copy front sidedef if exists
|
||||
if(front != null)
|
||||
{
|
||||
nsd = map.CreateSidedef(nl, true, front.Sector);
|
||||
front.CopyPropertiesTo(nsd);
|
||||
}
|
||||
|
||||
// Copy back sidedef if exists
|
||||
if(back != null)
|
||||
{
|
||||
nsd = map.CreateSidedef(nl, false, back.Sector);
|
||||
back.CopyPropertiesTo(nsd);
|
||||
}
|
||||
|
||||
// Return result
|
||||
return nl;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
|
@ -319,6 +319,129 @@ namespace CodeImp.DoomBuilder.Map
|
|||
|
||||
#region ================== Static Tools
|
||||
|
||||
// This joins nearby vertices from two collections. This does NOT join vertices
|
||||
// within the same collection, only if they exist in both collections.
|
||||
// The vertex from the second collection is moved to match the first vertex.
|
||||
// When keepsecond is true, the vertex in the second collection is kept,
|
||||
// otherwise the vertex in the first collection is kept.
|
||||
// Returns the number of joins made
|
||||
public static int JoinVertices(ICollection<Vertex> set1, ICollection<Vertex> set2, bool keepsecond, float joindist)
|
||||
{
|
||||
float joindist2 = joindist * joindist;
|
||||
int joinsdone = 0;
|
||||
bool joined;
|
||||
|
||||
do
|
||||
{
|
||||
// No joins yet
|
||||
joined = false;
|
||||
|
||||
// Go for all vertices in the first set
|
||||
foreach(Vertex v1 in set1)
|
||||
{
|
||||
// Go for all vertices in the second set
|
||||
foreach(Vertex v2 in set2)
|
||||
{
|
||||
// Check if vertices are close enough
|
||||
if(v1.DistanceToSq(v2.Position) <= joindist2)
|
||||
{
|
||||
// Check if not the same vertex
|
||||
if(v1 != v2)
|
||||
{
|
||||
// Move the second vertex to match the first
|
||||
v2.Move(v1.Position);
|
||||
|
||||
// Check which one to keep
|
||||
if(keepsecond)
|
||||
{
|
||||
// Join the first into the second
|
||||
// Second is kept, first is removed
|
||||
v1.Join(v2);
|
||||
set1.Remove(v1);
|
||||
set2.Remove(v1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Join the second into the first
|
||||
// First is kept, second is removed
|
||||
v2.Join(v1);
|
||||
set1.Remove(v2);
|
||||
set2.Remove(v2);
|
||||
}
|
||||
|
||||
// Count the join
|
||||
joinsdone++;
|
||||
joined = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Will have to restart when joined
|
||||
if(joined) break;
|
||||
}
|
||||
}
|
||||
while(joined);
|
||||
|
||||
// Return result
|
||||
return joinsdone;
|
||||
}
|
||||
|
||||
// This splits the given lines with the given vertices
|
||||
// Returns the number of splits made
|
||||
public static int SplitLinesByVertices(ICollection<Linedef> lines, ICollection<Vertex> verts, float splitdist)
|
||||
{
|
||||
float splitdist2 = splitdist * splitdist;
|
||||
int splitsdone = 0;
|
||||
bool splitted;
|
||||
Linedef nl;
|
||||
|
||||
do
|
||||
{
|
||||
// No split yet
|
||||
splitted = false;
|
||||
|
||||
// Go for all the lines
|
||||
foreach(Linedef l in lines)
|
||||
{
|
||||
// Go for all the vertices
|
||||
foreach(Vertex v in verts)
|
||||
{
|
||||
// Check if v is close enough to l for splitting
|
||||
if(l.DistanceToSq(v.Position, true) <= splitdist2)
|
||||
{
|
||||
// Line is not already referencing v?
|
||||
if((l.Start != v) && (l.End != v))
|
||||
{
|
||||
// Split line l with vertex v
|
||||
nl = l.Split(v);
|
||||
|
||||
// Add the new line to the list
|
||||
lines.Add(nl);
|
||||
|
||||
// Both lines must be updated because their new length
|
||||
// is relevant for next iterations!
|
||||
l.Update();
|
||||
nl.Update();
|
||||
|
||||
// Count the split
|
||||
splitsdone++;
|
||||
splitted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Will have to restart when splitted
|
||||
if(splitted) break;
|
||||
}
|
||||
}
|
||||
while(splitted);
|
||||
|
||||
// Return result
|
||||
return splitsdone;
|
||||
}
|
||||
|
||||
// This finds the line closest to the specified position
|
||||
public static Linedef NearestLinedef(ICollection<Linedef> selection, Vector2D pos)
|
||||
{
|
||||
|
@ -460,6 +583,52 @@ namespace CodeImp.DoomBuilder.Map
|
|||
|
||||
#region ================== Tools
|
||||
|
||||
// This makes a list of lines related to vertex selection
|
||||
// A line is unstable when one vertex is selected and the other isn't.
|
||||
public ICollection<Linedef> LinedefsFromSelectedVertices(bool includestable, bool includeunstable)
|
||||
{
|
||||
List<Linedef> list = new List<Linedef>();
|
||||
|
||||
// Go for all lines
|
||||
foreach(Linedef l in linedefs)
|
||||
{
|
||||
// Check if this is to be included
|
||||
if((includestable && ((l.Start.Selected > 0) && (l.End.Selected > 0))) ||
|
||||
(includeunstable && ((l.Start.Selected > 0) || (l.End.Selected > 0))) )
|
||||
{
|
||||
// Add to list
|
||||
list.Add(l);
|
||||
}
|
||||
}
|
||||
|
||||
// Return result
|
||||
return list;
|
||||
}
|
||||
|
||||
// This returns all vertices not in verts collection
|
||||
public ICollection<Vertex> InvertedCollection(ICollection<Vertex> verts)
|
||||
{
|
||||
List<Vertex> list = new List<Vertex>();
|
||||
|
||||
// Go for all vertices
|
||||
foreach(Vertex v in vertices) if(!verts.Contains(v)) list.Add(v);
|
||||
|
||||
// Return result
|
||||
return list;
|
||||
}
|
||||
|
||||
// This returns all linedefs not in lines collection
|
||||
public ICollection<Linedef> InvertedCollection(ICollection<Linedef> lines)
|
||||
{
|
||||
List<Linedef> list = new List<Linedef>();
|
||||
|
||||
// Go for all lines
|
||||
foreach(Linedef l in linedefs) if(!lines.Contains(l)) list.Add(l);
|
||||
|
||||
// Return result
|
||||
return list;
|
||||
}
|
||||
|
||||
// This finds the line closest to the specified position
|
||||
public Linedef NearestLinedef(Vector2D pos) { return MapSet.NearestLinedef(linedefs, pos); }
|
||||
|
||||
|
@ -475,6 +644,37 @@ namespace CodeImp.DoomBuilder.Map
|
|||
// This finds the thing closest to the specified position
|
||||
public Thing NearestThingSquareRange(Vector2D pos, float maxrange) { return MapSet.NearestThingSquareRange(things, pos, maxrange); }
|
||||
|
||||
// This finds the closest unselected linedef that is not connected to the given vertex
|
||||
public Linedef NearestUnselectedUnreferencedLinedef(Vector2D pos, float maxrange, Vertex v, out float distance)
|
||||
{
|
||||
Linedef closest = null;
|
||||
distance = float.MaxValue;
|
||||
float maxrangesq = maxrange * maxrange;
|
||||
float d;
|
||||
|
||||
// Go for all linedefs in selection
|
||||
foreach(Linedef l in linedefs)
|
||||
{
|
||||
// Calculate distance and check if closer than previous find
|
||||
d = l.SafeDistanceToSq(pos, true);
|
||||
if((d <= maxrangesq) && (d < distance))
|
||||
{
|
||||
// Check if not selected
|
||||
|
||||
// Check if linedef is not connected to v
|
||||
if((l.Start != v) && (l.End != v))
|
||||
{
|
||||
// This one is closer
|
||||
closest = l;
|
||||
distance = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return result
|
||||
return closest;
|
||||
}
|
||||
|
||||
// This performs sidedefs compression
|
||||
public void CompressSidedefs()
|
||||
{
|
||||
|
|
|
@ -190,6 +190,36 @@ namespace CodeImp.DoomBuilder.Map
|
|||
this.Move(General.Map.Grid.SnappedToGrid(pos));
|
||||
}
|
||||
|
||||
// This joins another vertex
|
||||
// Which means this vertex is removed and the other is kept!
|
||||
public void Join(Vertex other)
|
||||
{
|
||||
LinkedListNode<Linedef> previous;
|
||||
LinkedListNode<Linedef> current;
|
||||
|
||||
// Go for all lines
|
||||
current = linedefs.Last;
|
||||
while(current != null)
|
||||
{
|
||||
// Get previous
|
||||
previous = current.Previous;
|
||||
|
||||
// Move the start to the other vertex
|
||||
if(current.Value.Start == this)
|
||||
current.Value.SetStartVertex(other);
|
||||
|
||||
// Move the end to the other vertex
|
||||
if(current.Value.End == this)
|
||||
current.Value.SetEndVertex(other);
|
||||
|
||||
// Go back one
|
||||
current = previous;
|
||||
}
|
||||
|
||||
// Remove this vertex
|
||||
this.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
7
Source/Properties/Resources.Designer.cs
generated
7
Source/Properties/Resources.Designer.cs
generated
|
@ -88,6 +88,13 @@ namespace CodeImp.DoomBuilder.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
internal static System.Drawing.Bitmap Grid4 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("Grid4", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
internal static System.Drawing.Bitmap Hourglass {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("Hourglass", resourceCulture);
|
||||
|
|
|
@ -118,6 +118,9 @@
|
|||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="Redo" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Redo.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="Zoom" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Zoom.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
|
@ -142,6 +145,9 @@
|
|||
<data name="NewMap" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\NewMap2.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="File" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\NewMap.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="Undo" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Undo.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
|
@ -157,9 +163,6 @@
|
|||
<data name="Grid2" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Grid2.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="OpenMap" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\OpenMap.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="ThingsMode" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\ThingsMode.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
|
@ -178,10 +181,10 @@
|
|||
<data name="Hourglass" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Hourglass.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="File" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\NewMap.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
<data name="OpenMap" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\OpenMap.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="Redo" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Redo.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
<data name="Grid4" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Grid4.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
</root>
|
|
@ -229,3 +229,21 @@ redo
|
|||
allowmouse = false;
|
||||
allowscroll = false;
|
||||
}
|
||||
|
||||
togglesnap
|
||||
{
|
||||
title = "Edit: Snap to Grid";
|
||||
description = "Toggles snapping to the grid for things and vertices that are being dragged.";
|
||||
allowkeys = true;
|
||||
allowmouse = true;
|
||||
allowscroll = true;
|
||||
}
|
||||
|
||||
clearselection
|
||||
{
|
||||
title = "Edit: Clear Selection";
|
||||
description = "Deselects all selected elements.";
|
||||
allowkeys = true;
|
||||
allowmouse = true;
|
||||
allowscroll = true;
|
||||
}
|
||||
|
|
BIN
Source/Resources/Grid4.png
Normal file
BIN
Source/Resources/Grid4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 301 B |
Loading…
Reference in a new issue