UltimateZoneBuilder/Source/Core/Controls/ArgumentsControl.cs
MaxED bfd57379bb Added, Map Analysis mode: added "Check unknown ACS scripts" error check.
Added, Map Analysis mode: added "Edit Thing..." option to "Unknown Thing" and "Obsolete Thing" error check results.
Added: a warning is now displayed when a thing has both voxel and model attached.
Changed, Thing/Linedef Info panels: unknown ACS script names/numbers are now shown in red.
Fixed, Edit Things window, UDMF: unneeded undo was created when opening the window.
Re-fixed: in some cases invalid sectors were created after dragging map elements when using "Merge Dragged Geometry" and "Replace with Dragged Geometry" drag modes, when at least one of dragged linedef was facing into an enclosed void area (previous fix entirely disabled the associated logic).
2016-09-12 14:08:45 +00:00

501 lines
14 KiB
C#

#region ================== Namespaces
using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.GZBuilder.Data;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Types;
#endregion
namespace CodeImp.DoomBuilder.Controls
{
public partial class ArgumentsControl : UserControl
{
#region ================== Native stuff
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam);
private const int WM_SETREDRAW = 11;
#endregion
#region ================== Enums
private enum ArgZeroMode
{
DEFAULT,
SCRIPT_NUMBER,
SCRIPT_NAME,
}
#endregion
#region ================== Variables
private string arg0str;
private bool havearg0str;
private int action;
private ArgumentInfo[] arginfo;
private ArgZeroMode argzeromode;
#endregion
#region ================== Constructor
public ArgumentsControl()
{
InitializeComponent();
// Only when running (this.DesignMode won't do when not this, but one of parent controls is in design mode)
if(LicenseManager.UsageMode != LicenseUsageMode.Designtime)
{
//mxd. Setup script numbers
scriptnumbers.Location = new Point(arg0.Location.X, arg0.Location.Y + 2);
foreach(ScriptItem si in General.Map.NumberedScripts.Values)
scriptnumbers.Items.Add(new ColoredComboBoxItem(si, si.IsInclude ? SystemColors.HotTrack : SystemColors.WindowText));
scriptnumbers.DropDownWidth = Tools.GetDropDownWidth(scriptnumbers);
//mxd. Setup script names
if(General.Map.UDMF)
{
scriptnames.Location = scriptnumbers.Location;
foreach(ScriptItem nsi in General.Map.NamedScripts.Values)
scriptnames.Items.Add(new ColoredComboBoxItem(nsi, nsi.IsInclude ? SystemColors.HotTrack : SystemColors.WindowText));
scriptnames.DropDownWidth = Tools.GetDropDownWidth(scriptnames);
}
else
{
scriptnames.Visible = false;
cbuseargstr.Visible = false;
}
}
}
#endregion
#region ================== Setup
public void SetValue(Linedef l, bool first)
{
SetValue(l.Fields, l.Args, first);
}
public void SetValue(Thing t, bool first)
{
SetValue(t.Fields, t.Args, first);
}
private void SetValue(UniFields fields, int[] args, bool first)
{
if(first)
{
if(General.Map.UDMF)
{
arg0str = fields.GetValue("arg0str", string.Empty);
havearg0str = !string.IsNullOrEmpty(arg0str);
}
// Update arguments
arg0.SetValue(args[0]);
arg1.SetValue(args[1]);
arg2.SetValue(args[2]);
arg3.SetValue(args[3]);
arg4.SetValue(args[4]);
}
else
{
if(General.Map.UDMF)
{
if(arg0str != fields.GetValue("arg0str", string.Empty))
{
havearg0str = true;
arg0str = string.Empty;
}
}
// Update arguments
if(!string.IsNullOrEmpty(arg0.Text) && args[0] != arg0.GetResult(int.MinValue)) arg0.ClearValue();
if(!string.IsNullOrEmpty(arg1.Text) && args[1] != arg1.GetResult(int.MinValue)) arg1.ClearValue();
if(!string.IsNullOrEmpty(arg2.Text) && args[2] != arg2.GetResult(int.MinValue)) arg2.ClearValue();
if(!string.IsNullOrEmpty(arg3.Text) && args[3] != arg3.GetResult(int.MinValue)) arg3.ClearValue();
if(!string.IsNullOrEmpty(arg4.Text) && args[4] != arg4.GetResult(int.MinValue)) arg4.ClearValue();
}
}
#endregion
#region ================== Apply
public void Apply(Linedef l, int step)
{
//mxd. Script name/number handling
// We can't rely on control visibility here, because all controlls will be invisible if ArgumentsControl is invisible
// (for example, when a different tab is selected)
switch(argzeromode)
{
// Apply arg0str
case ArgZeroMode.SCRIPT_NAME:
if(!string.IsNullOrEmpty(scriptnames.Text))
l.Fields["arg0str"] = new UniValue(UniversalType.String, scriptnames.Text);
break;
// Apply script number
case ArgZeroMode.SCRIPT_NUMBER:
if(!string.IsNullOrEmpty(scriptnumbers.Text))
{
if(scriptnumbers.SelectedItem != null)
l.Args[0] = ((ScriptItem)((ColoredComboBoxItem)scriptnumbers.SelectedItem).Value).Index;
else if(!int.TryParse(scriptnumbers.Text.Trim(), out l.Args[0]))
l.Args[0] = 0;
if(l.Fields.ContainsKey("arg0str")) l.Fields.Remove("arg0str");
}
break;
// Apply classic arg
case ArgZeroMode.DEFAULT:
l.Args[0] = arg0.GetResult(l.Args[0], step);
if(l.Fields.ContainsKey("arg0str")) l.Fields.Remove("arg0str");
break;
default: throw new NotImplementedException("Unknown ArgZeroMode");
}
// Apply the rest of args
l.Args[1] = arg1.GetResult(l.Args[1], step);
l.Args[2] = arg2.GetResult(l.Args[2], step);
l.Args[3] = arg3.GetResult(l.Args[3], step);
l.Args[4] = arg4.GetResult(l.Args[4], step);
}
public void Apply(Thing t, int step)
{
//mxd. Script name/number handling
// We can't rely on control visibility here, because all controlls will be invisible if ArgumentsControl is invisible
// (for example, when a different tab is selected)
switch(argzeromode)
{
// Apply arg0str
case ArgZeroMode.SCRIPT_NAME:
if(!string.IsNullOrEmpty(scriptnames.Text))
t.Fields["arg0str"] = new UniValue(UniversalType.String, scriptnames.Text);
break;
// Apply script number
case ArgZeroMode.SCRIPT_NUMBER:
if(!string.IsNullOrEmpty(scriptnumbers.Text))
{
if(scriptnumbers.SelectedItem != null)
t.Args[0] = ((ScriptItem)((ColoredComboBoxItem)scriptnumbers.SelectedItem).Value).Index;
else if(!int.TryParse(scriptnumbers.Text.Trim(), out t.Args[0]))
t.Args[0] = 0;
if(t.Fields.ContainsKey("arg0str")) t.Fields.Remove("arg0str");
}
break;
// Apply classic arg
case ArgZeroMode.DEFAULT:
t.Args[0] = arg0.GetResult(t.Args[0], step);
if(t.Fields.ContainsKey("arg0str")) t.Fields.Remove("arg0str");
break;
default: throw new NotImplementedException("Unknown ArgZeroMode");
}
// Apply the rest of args
t.Args[1] = arg1.GetResult(t.Args[1], step);
t.Args[2] = arg2.GetResult(t.Args[2], step);
t.Args[3] = arg3.GetResult(t.Args[3], step);
t.Args[4] = arg4.GetResult(t.Args[4], step);
}
#endregion
#region ================== Update
public void UpdateAction(int action, bool setuponly)
{
UpdateAction(action, setuponly, null);
}
public void UpdateAction(int action, bool setuponly, ThingTypeInfo info)
{
// Update arguments
int showaction = 0;
ArgumentInfo[] oldarginfo = (arginfo != null ? (ArgumentInfo[])arginfo.Clone() : null); //mxd
// Only when action type is known
if(General.Map.Config.LinedefActions.ContainsKey(action)) showaction = action;
// Update argument infos
if((showaction == 0) && (info != null)) arginfo = info.Args;
else arginfo = General.Map.Config.LinedefActions[showaction].Args;
// Don't update action args when thing type is changed
if(info != null && showaction != 0 && this.action == showaction) return;
//mxd. Don't update action args when old and new argument infos match
if(arginfo != null && oldarginfo != null && ArgumentInfosMatch(arginfo, oldarginfo)) return;
// Change the argument descriptions
this.BeginUpdate();
UpdateArgument(arg0, arg0label, arginfo[0]);
UpdateArgument(arg1, arg1label, arginfo[1]);
UpdateArgument(arg2, arg2label, arginfo[2]);
UpdateArgument(arg3, arg3label, arginfo[3]);
UpdateArgument(arg4, arg4label, arginfo[4]);
if(!setuponly)
{
// Apply action's or thing's default arguments
if(showaction != 0 || info != null)
{
arg0.SetDefaultValue();
arg1.SetDefaultValue();
arg2.SetDefaultValue();
arg3.SetDefaultValue();
arg4.SetDefaultValue();
}
else //or set them to 0
{
arg0.SetValue(0);
arg1.SetValue(0);
arg2.SetValue(0);
arg3.SetValue(0);
arg4.SetValue(0);
}
}
// Store current action
this.action = showaction;
this.EndUpdate();
}
public void UpdateScriptControls()
{
// Update script-specific stuff
if(Array.IndexOf(GZBuilder.GZGeneral.ACS_SPECIALS, action) != -1)
{
// Update script controls visibility
bool shownamedscripts = (General.Map.UDMF && havearg0str);
cbuseargstr.Visible = General.Map.UDMF;
cbuseargstr.Checked = shownamedscripts;
scriptnames.Visible = shownamedscripts;
scriptnumbers.Visible = !shownamedscripts;
// Update named script name
if(shownamedscripts)
{
argzeromode = ArgZeroMode.SCRIPT_NAME;
if(General.Map.NamedScripts.ContainsKey(arg0str))
{
scriptnames.SelectedText = arg0str;
UpdateScriptArguments(General.Map.NamedScripts[arg0str]);
}
else
{
// Unknown script name
scriptnames.Text = arg0str;
arg0label.Text = "Script Name:";
}
}
else
{
// Update numbered script name
argzeromode = ArgZeroMode.SCRIPT_NUMBER;
int a0 = arg0.GetResult(0);
if(General.Map.NumberedScripts.ContainsKey(a0))
{
int i = 0;
foreach(ScriptItem item in General.Map.NumberedScripts.Values)
{
if(item.Index == a0)
{
scriptnumbers.SelectedIndex = i;
UpdateScriptArguments(item);
break;
}
i++;
}
}
else
{
// Unknown script number...
scriptnumbers.Text = a0.ToString();
}
}
}
else
{
cbuseargstr.Visible = false;
scriptnames.Visible = false;
scriptnumbers.Visible = false;
cbuseargstr.Checked = false;
argzeromode = ArgZeroMode.DEFAULT;
}
arg0.Visible = (argzeromode == ArgZeroMode.DEFAULT);
}
private void UpdateArgument(ArgumentBox arg, Label label, ArgumentInfo info)
{
// Update labels
label.Text = info.Title + ":";
label.Enabled = info.Used;
arg.ForeColor = (label.Enabled ? SystemColors.WindowText : SystemColors.GrayText);
arg.Setup(info);
// Update tooltip
UpdateToolTip(label, info);
}
private void UpdateToolTip(Label label, ArgumentInfo info)
{
if(info.Used && !string.IsNullOrEmpty(info.ToolTip))
{
tooltip.SetToolTip(label, info.ToolTip);
label.Font = new Font(label.Font, FontStyle.Underline);
label.ForeColor = SystemColors.HotTrack;
}
else
{
tooltip.SetToolTip(label, null);
label.Font = new Font(label.Font, FontStyle.Regular);
label.ForeColor = SystemColors.WindowText;
}
}
private void UpdateScriptArguments(ScriptItem item)
{
Label[] labels = { arg0label, arg1label, arg2label, arg3label, arg4label };
ArgumentBox[] args = { arg0, arg1, arg2, arg3, arg4 };
if(item != null)
{
string[] argnames = item.GetArgumentsDescriptions(action);
for(int i = 0; i < labels.Length; i++)
{
if(!string.IsNullOrEmpty(argnames[i]))
{
labels[i].Text = argnames[i] + ":";
labels[i].Enabled = true;
labels[i].Font = new Font(labels[i].Font, FontStyle.Regular);
labels[i].ForeColor = SystemColors.WindowText;
}
else
{
labels[i].Text = arginfo[i].Title + ":";
labels[i].Enabled = arginfo[i].Used;
UpdateToolTip(labels[i], arginfo[i]);
}
args[i].ForeColor = (labels[i].Enabled ? SystemColors.WindowText : SystemColors.GrayText);
}
}
else
{
for(int i = 0; i < labels.Length; i++)
{
labels[i].Text = arginfo[i].Title + ":";
labels[i].Enabled = arginfo[i].Used;
UpdateToolTip(labels[i], arginfo[i]);
args[i].ForeColor = (labels[i].Enabled ? SystemColors.WindowText : SystemColors.GrayText);
}
}
}
//mxd
private static bool ArgumentInfosMatch(ArgumentInfo[] info1, ArgumentInfo[] info2)
{
if(info1.Length != info2.Length) return false;
bool haveusedargs = false; // Arguments should still be reset if all arguments are unused
for(int i = 0; i < info1.Length; i++)
{
if(info1[i].Used != info1[2].Used || info1[i].Type != info1[2].Type
|| info1[i].Title.ToUpperInvariant() != info2[i].Title.ToUpperInvariant())
return false;
haveusedargs |= (info1[i].Used || info1[2].Used);
}
return haveusedargs;
}
#endregion
#region ================== Redraw control
private void BeginUpdate()
{
SendMessage(this.Parent.Handle, WM_SETREDRAW, false, 0);
}
private void EndUpdate()
{
SendMessage(this.Parent.Handle, WM_SETREDRAW, true, 0);
this.Parent.Refresh();
}
#endregion
#region ================== Events
private void cbuseargstr_CheckedChanged(object sender, EventArgs e)
{
if(!cbuseargstr.Visible) return;
scriptnames.Visible = cbuseargstr.Checked;
scriptnumbers.Visible = !cbuseargstr.Checked;
arg0label.Text = (cbuseargstr.Checked ? "Script Name:" : "Script Number:");
argzeromode = (cbuseargstr.Checked ? ArgZeroMode.SCRIPT_NAME : ArgZeroMode.SCRIPT_NUMBER);
}
private void scriptnumbers_TextChanged(object sender, EventArgs e)
{
if(string.IsNullOrEmpty(scriptnumbers.Text)) return;
ScriptItem item = null;
if(scriptnumbers.SelectedIndex != -1)
{
item = ((ScriptItem)((ColoredComboBoxItem)scriptnumbers.SelectedItem).Value);
}
else
{
int scriptindex;
if(int.TryParse(scriptnumbers.Text, out scriptindex) && General.Map.NumberedScripts.ContainsKey(scriptindex))
item = General.Map.NumberedScripts[scriptindex];
}
UpdateScriptArguments(item);
}
private void scriptnames_TextChanged(object sender, EventArgs e)
{
if(string.IsNullOrEmpty(scriptnames.Text)) return;
ScriptItem item = null;
if(scriptnames.SelectedIndex != -1)
{
item = ((ScriptItem)((ColoredComboBoxItem)scriptnames.SelectedItem).Value);
}
else
{
string scriptname = scriptnames.Text.Trim().ToLowerInvariant();
if(General.Map.NamedScripts.ContainsKey(scriptname))
item = General.Map.NamedScripts[scriptname];
}
UpdateScriptArguments(item);
}
#endregion
}
}