mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2025-01-19 06:51:09 +00:00
UDBScript asynchronous execution (#684)
Script run by UDBScript are now executed asynchronously
This commit is contained in:
parent
12f32e2bc6
commit
e2374102ee
13 changed files with 801 additions and 115 deletions
|
@ -144,6 +144,7 @@ namespace CodeImp.DoomBuilder
|
|||
internal const int WM_UIACTION = WM_USER + 1;
|
||||
internal const int WM_SYSCOMMAND = 0x112;
|
||||
internal const int WM_MOUSEHWHEEL = 0x020E; // [ZZ]
|
||||
internal const int WM_MOUSEWHEEL = 0x20A;
|
||||
internal const int SC_KEYMENU = 0xF100;
|
||||
internal const int CB_SETITEMHEIGHT = 0x153;
|
||||
//internal const int CB_SHOWDROPDOWN = 0x14F;
|
||||
|
|
|
@ -101,6 +101,9 @@ namespace CodeImp.DoomBuilder.Map
|
|||
// Statics
|
||||
private static long emptylongname;
|
||||
private static UniValue virtualsectorvalue;
|
||||
|
||||
// Concurrency
|
||||
private bool issafetoaccess;
|
||||
|
||||
// Disposing
|
||||
private bool isdisposed;
|
||||
|
@ -169,6 +172,13 @@ namespace CodeImp.DoomBuilder.Map
|
|||
|
||||
internal List<UniversalEntry> UnknownUDMFData { get { return unknownudmfdata; } set { unknownudmfdata = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// If it's safe to access (either reading or modifying) the map data. May only be read or set from the UI thread to
|
||||
/// avoid racing conditions. Code that wants to access the map data on on a timer or in another thread must honor
|
||||
/// this setting to avoid exceptions
|
||||
/// </summary>
|
||||
public bool IsSafeToAccess { get { return issafetoaccess; } set { issafetoaccess = value; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Constructor / Disposer
|
||||
|
@ -190,6 +200,8 @@ namespace CodeImp.DoomBuilder.Map
|
|||
lastsectorindex = 0;
|
||||
autoremove = true;
|
||||
unknownudmfdata = new List<UniversalEntry>();
|
||||
|
||||
issafetoaccess = true;
|
||||
|
||||
// We have no destructor
|
||||
GC.SuppressFinalize(this);
|
||||
|
@ -213,6 +225,8 @@ namespace CodeImp.DoomBuilder.Map
|
|||
autoremove = true;
|
||||
unknownudmfdata = new List<UniversalEntry>();
|
||||
|
||||
issafetoaccess = true;
|
||||
|
||||
// Deserialize
|
||||
Deserialize(stream);
|
||||
|
||||
|
@ -1077,9 +1091,14 @@ namespace CodeImp.DoomBuilder.Map
|
|||
// Update all sectors
|
||||
if(dosectors)
|
||||
{
|
||||
foreach(Sector s in sectors) s.Triangulate();
|
||||
foreach (Sector s in sectors)
|
||||
{
|
||||
s.Triangulate();
|
||||
s.UpdateBBox();
|
||||
}
|
||||
|
||||
General.Map.CRenderer2D.Surfaces.AllocateBuffers();
|
||||
foreach(Sector s in sectors) s.CreateSurfaces();
|
||||
foreach (Sector s in sectors) s.CreateSurfaces();
|
||||
General.Map.CRenderer2D.Surfaces.UnlockBuffers();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ namespace CodeImp.DoomBuilder.Windows
|
|||
bool IsActiveWindow { get; }
|
||||
string ActiveDockerTabName { get; } //mxd
|
||||
RenderTargetControl Display { get; }
|
||||
int ProcessingCount { get; }
|
||||
|
||||
//mxd. Events
|
||||
event EventHandler OnEditFormValuesChanged;
|
||||
|
|
|
@ -180,6 +180,7 @@ namespace CodeImp.DoomBuilder.Windows
|
|||
public StatusInfo Status { get { return status; } }
|
||||
public static Size ScaledIconSize = new Size(16, 16); //mxd
|
||||
public static SizeF DPIScaler = new SizeF(1.0f, 1.0f); //mxd
|
||||
public int ProcessingCount { get { return processingcount; } }
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -1352,22 +1353,26 @@ namespace CodeImp.DoomBuilder.Windows
|
|||
if(alt) mod |= (int)Keys.Alt;
|
||||
if(shift) mod |= (int)Keys.Shift;
|
||||
if(ctrl) mod |= (int)Keys.Control;
|
||||
|
||||
// Scrollwheel up?
|
||||
if(e.Delta > 0)
|
||||
|
||||
// Only send key events when the main window can be focused (i.e. no modal dialogs are open)
|
||||
if (CanFocus)
|
||||
{
|
||||
// Invoke actions for scrollwheel
|
||||
//for(int i = 0; i < e.Delta; i += 120)
|
||||
General.Actions.KeyPressed((int)SpecialKeys.MScrollUp | mod);
|
||||
General.Actions.KeyReleased((int)SpecialKeys.MScrollUp | mod);
|
||||
}
|
||||
// Scrollwheel down?
|
||||
else if(e.Delta < 0)
|
||||
{
|
||||
// Invoke actions for scrollwheel
|
||||
//for(int i = 0; i > e.Delta; i -= 120)
|
||||
General.Actions.KeyPressed((int)SpecialKeys.MScrollDown | mod);
|
||||
General.Actions.KeyReleased((int)SpecialKeys.MScrollDown | mod);
|
||||
// Scrollwheel up?
|
||||
if (e.Delta > 0)
|
||||
{
|
||||
// Invoke actions for scrollwheel
|
||||
//for(int i = 0; i < e.Delta; i += 120)
|
||||
General.Actions.KeyPressed((int)SpecialKeys.MScrollUp | mod);
|
||||
General.Actions.KeyReleased((int)SpecialKeys.MScrollUp | mod);
|
||||
}
|
||||
// Scrollwheel down?
|
||||
else if (e.Delta < 0)
|
||||
{
|
||||
// Invoke actions for scrollwheel
|
||||
//for(int i = 0; i > e.Delta; i -= 120)
|
||||
General.Actions.KeyPressed((int)SpecialKeys.MScrollDown | mod);
|
||||
General.Actions.KeyReleased((int)SpecialKeys.MScrollDown | mod);
|
||||
}
|
||||
}
|
||||
|
||||
// Let the base know
|
||||
|
@ -4394,7 +4399,7 @@ namespace CodeImp.DoomBuilder.Windows
|
|||
OnMouseHWheel(delta);
|
||||
m.Result = new IntPtr(delta);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
// Let the base handle the message
|
||||
base.WndProc(ref m);
|
||||
|
|
|
@ -159,7 +159,7 @@ namespace CodeImp.DoomBuilder.CommentsPanel
|
|||
// This finds all comments and updates the list
|
||||
public void UpdateList()
|
||||
{
|
||||
if(!preventupdate)
|
||||
if(!preventupdate && General.Map.Map.IsSafeToAccess)
|
||||
{
|
||||
// Update vertices
|
||||
Dictionary<string, CommentInfo> newcomments = new Dictionary<string, CommentInfo>(StringComparer.Ordinal);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#region ================== Namespaces
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CodeImp.DoomBuilder.BuilderModes;
|
||||
|
@ -41,6 +42,7 @@ namespace CodeImp.DoomBuilder.UDBScript.Wrapper
|
|||
|
||||
private MapSet map;
|
||||
private VisualCameraWrapper visualcamera;
|
||||
private Vector2D mousemappos;
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -86,10 +88,7 @@ namespace CodeImp.DoomBuilder.UDBScript.Wrapper
|
|||
{
|
||||
get
|
||||
{
|
||||
if (General.Editing.Mode is ClassicMode)
|
||||
return ((ClassicMode)General.Editing.Mode).MouseMapPos;
|
||||
else
|
||||
return ((VisualMode)General.Editing.Mode).GetHitPosition();
|
||||
return mousemappos;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,6 +111,11 @@ namespace CodeImp.DoomBuilder.UDBScript.Wrapper
|
|||
{
|
||||
map = General.Map.Map;
|
||||
visualcamera = new VisualCameraWrapper();
|
||||
|
||||
if (General.Editing.Mode is ClassicMode)
|
||||
mousemappos = ((ClassicMode)General.Editing.Mode).MouseMapPos;
|
||||
else
|
||||
mousemappos = ((VisualMode)General.Editing.Mode).GetHitPosition();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -416,8 +420,8 @@ namespace CodeImp.DoomBuilder.UDBScript.Wrapper
|
|||
// Snap to map format accuracy
|
||||
General.Map.Map.SnapAllToAccuracy();
|
||||
|
||||
// Update map
|
||||
General.Map.Map.Update();
|
||||
// Update map. This has to run on the UI thread
|
||||
BuilderPlug.Me.ScriptRunnerForm.RunAction(() => General.Map.Map.Update());
|
||||
|
||||
// Update textures
|
||||
General.Map.Data.UpdateUsedTextures();
|
||||
|
@ -1178,7 +1182,7 @@ namespace CodeImp.DoomBuilder.UDBScript.Wrapper
|
|||
sectors[i].Join(first);
|
||||
|
||||
// Update
|
||||
General.Map.Map.Update();
|
||||
BuilderPlug.Me.ScriptRunnerForm.RunAction(() => General.Map.Map.Update());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -97,6 +97,7 @@ namespace CodeImp.DoomBuilder.UDBScript
|
|||
private Dictionary<int, ScriptInfo> scriptslots;
|
||||
private string editorexepath;
|
||||
private PreferencesForm preferencesform;
|
||||
private ScriptRunnerForm scriptrunnerform;
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -108,6 +109,7 @@ namespace CodeImp.DoomBuilder.UDBScript
|
|||
internal ScriptRunner ScriptRunner { get { return scriptrunner; } }
|
||||
internal ScriptDirectoryStructure ScriptDirectoryStructure { get { return scriptdirectorystructure; } }
|
||||
internal string EditorExePath { get { return editorexepath; } }
|
||||
public ScriptRunnerForm ScriptRunnerForm { get { return scriptrunnerform; } }
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -138,6 +140,8 @@ namespace CodeImp.DoomBuilder.UDBScript
|
|||
|
||||
editorexepath = General.Settings.ReadPluginSetting("externaleditor", string.Empty);
|
||||
|
||||
scriptrunnerform = new ScriptRunnerForm();
|
||||
|
||||
FindEditor();
|
||||
}
|
||||
|
||||
|
@ -617,7 +621,7 @@ namespace CodeImp.DoomBuilder.UDBScript
|
|||
return;
|
||||
|
||||
scriptrunner = new ScriptRunner(currentscript);
|
||||
scriptrunner.Run();
|
||||
scriptrunnerform.ShowDialog();
|
||||
}
|
||||
|
||||
[BeginAction("udbscriptexecuteslot1")]
|
||||
|
@ -665,7 +669,7 @@ namespace CodeImp.DoomBuilder.UDBScript
|
|||
if (scriptslots.ContainsKey(slot) && scriptslots[slot] != null)
|
||||
{
|
||||
scriptrunner = new ScriptRunner(scriptslots[slot]);
|
||||
scriptrunner.Run();
|
||||
scriptrunnerform.ShowDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
37
Source/Plugins/UDBScript/ProgressInfo.cs
Normal file
37
Source/Plugins/UDBScript/ProgressInfo.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeImp.DoomBuilder.UDBScript
|
||||
{
|
||||
class ProgressInfo
|
||||
{
|
||||
IProgress<int> progress;
|
||||
IProgress<string> status;
|
||||
IProgress<string> _log;
|
||||
|
||||
public ProgressInfo(IProgress<int> progress, IProgress<string> status, IProgress<string> log)
|
||||
{
|
||||
this.progress = progress;
|
||||
this.status = status;
|
||||
_log = log;
|
||||
}
|
||||
|
||||
public void setProgress(int p)
|
||||
{
|
||||
progress.Report(p);
|
||||
}
|
||||
|
||||
public void setStatus(string s)
|
||||
{
|
||||
status.Report(s);
|
||||
}
|
||||
|
||||
public void log(string s)
|
||||
{
|
||||
_log.Report(s);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using CodeImp.DoomBuilder.Map;
|
||||
using CodeImp.DoomBuilder.Windows;
|
||||
|
@ -47,6 +48,7 @@ namespace CodeImp.DoomBuilder.UDBScript
|
|||
private ScriptInfo scriptinfo;
|
||||
Engine engine;
|
||||
Stopwatch stopwatch;
|
||||
int oldprocessingcount;
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -84,16 +86,18 @@ namespace CodeImp.DoomBuilder.UDBScript
|
|||
/// <param name="message">Message to show</param>
|
||||
public void ShowMessage(object message)
|
||||
{
|
||||
if (message == null)
|
||||
message = string.Empty;
|
||||
BuilderPlug.Me.ScriptRunnerForm.InvokePaused(new Action(() => {
|
||||
if (message == null)
|
||||
message = string.Empty;
|
||||
|
||||
stopwatch.Stop();
|
||||
MessageForm mf = new MessageForm("OK", null, message.ToString());
|
||||
DialogResult result = mf.ShowDialog();
|
||||
stopwatch.Start();
|
||||
stopwatch.Stop();
|
||||
MessageForm mf = new MessageForm("OK", null, message.ToString());
|
||||
DialogResult result = mf.ShowDialog();
|
||||
stopwatch.Start();
|
||||
|
||||
if (result == DialogResult.Abort)
|
||||
throw new UserScriptAbortException();
|
||||
if (result == DialogResult.Abort)
|
||||
throw new UserScriptAbortException();
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -103,18 +107,21 @@ namespace CodeImp.DoomBuilder.UDBScript
|
|||
/// <returns>true if "Yes" was clicked, false if "No" was clicked</returns>
|
||||
public bool ShowMessageYesNo(object message)
|
||||
{
|
||||
if (message == null)
|
||||
message = string.Empty;
|
||||
return (bool)BuilderPlug.Me.ScriptRunnerForm.InvokePaused(new Func<bool>(() =>
|
||||
{
|
||||
if (message == null)
|
||||
message = string.Empty;
|
||||
|
||||
stopwatch.Stop();
|
||||
MessageForm mf = new MessageForm("Yes", "No", message.ToString());
|
||||
DialogResult result = mf.ShowDialog();
|
||||
stopwatch.Start();
|
||||
stopwatch.Stop();
|
||||
MessageForm mf = new MessageForm("Yes", "No", message.ToString());
|
||||
DialogResult result = mf.ShowDialog();
|
||||
stopwatch.Start();
|
||||
|
||||
if (result == DialogResult.Abort)
|
||||
throw new UserScriptAbortException();
|
||||
if (result == DialogResult.Abort)
|
||||
throw new UserScriptAbortException();
|
||||
|
||||
return result == DialogResult.OK ? true : false;
|
||||
return result == DialogResult.OK ? true : false;
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -166,17 +173,16 @@ namespace CodeImp.DoomBuilder.UDBScript
|
|||
ParserOptions po = new ParserOptions(file.Remove(0, General.AppPath.Length));
|
||||
engine.Execute(File.ReadAllText(file), po);
|
||||
}
|
||||
catch (Esprima.ParserException e)
|
||||
catch (ParserException e)
|
||||
{
|
||||
MessageBox.Show("There was an error while loading the library " + file + ":\n\n" + e.Message, "Script error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Jint.Runtime.JavaScriptException e)
|
||||
catch (JavaScriptException e)
|
||||
{
|
||||
if (e.Error.Type != Jint.Runtime.Types.String)
|
||||
{
|
||||
//MessageBox.Show("There is an error in the script in line " + e.LineNumber + ":\n\n" + e.Message + "\n\n" + e.StackTrace, "Script error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
UDBScriptErrorForm sef = new UDBScriptErrorForm(e.Message, e.StackTrace);
|
||||
sef.ShowDialog();
|
||||
}
|
||||
|
@ -191,16 +197,74 @@ namespace CodeImp.DoomBuilder.UDBScript
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the script
|
||||
/// Handles the different exceptions we're expecting, and withdraws the undo snapshot if necessary.
|
||||
/// </summary>
|
||||
public void Run()
|
||||
/// <param name="e">The exception to handle</param>
|
||||
public void HandleExceptions(Exception e)
|
||||
{
|
||||
bool abort = false;
|
||||
|
||||
if(e is UserScriptAbortException)
|
||||
{
|
||||
General.Interface.DisplayStatus(StatusType.Warning, "Script aborted");
|
||||
abort = true;
|
||||
}
|
||||
else if(e is ParserException)
|
||||
{
|
||||
MessageBox.Show("There is an error while parsing the script:\n\n" + e.Message, "Script error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
abort = true;
|
||||
}
|
||||
else if(e is JavaScriptException)
|
||||
{
|
||||
if (((JavaScriptException)e).Error.Type != Jint.Runtime.Types.String)
|
||||
{
|
||||
UDBScriptErrorForm sef = new UDBScriptErrorForm(e.Message, e.StackTrace);
|
||||
sef.ShowDialog();
|
||||
}
|
||||
else
|
||||
General.Interface.DisplayStatus(StatusType.Warning, e.Message); // We get here if "throw" is used in a script
|
||||
|
||||
abort = true;
|
||||
}
|
||||
else if(e is ExitScriptException)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.Message))
|
||||
General.Interface.DisplayStatus(StatusType.Ready, e.Message);
|
||||
}
|
||||
else if(e is DieScriptException)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.Message))
|
||||
General.Interface.DisplayStatus(StatusType.Warning, e.Message);
|
||||
|
||||
abort = true;
|
||||
}
|
||||
else if(e is ExecutionCanceledException)
|
||||
{
|
||||
abort = true;
|
||||
}
|
||||
else // Catch anything else we didn't think about
|
||||
{
|
||||
UDBScriptErrorForm sef = new UDBScriptErrorForm(e.Message, e.StackTrace);
|
||||
sef.ShowDialog();
|
||||
|
||||
abort = true;
|
||||
}
|
||||
|
||||
if (abort)
|
||||
General.Map.UndoRedo.WithdrawUndo();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets everything up for running the script. This has to be done on the UI thread.
|
||||
/// </summary>
|
||||
/// <param name="cancellationtoken">Cancellation token to cancel the running script</param>
|
||||
public void PreRun(CancellationToken cancellationtoken)
|
||||
{
|
||||
string importlibraryerrors;
|
||||
bool abort = false;
|
||||
|
||||
// If the script requires a higher version of UDBScript than this version ask the user if they want
|
||||
// to execute it anyways. Remember the choice for this session if "yes" was selected.
|
||||
if(scriptinfo.Version > BuilderPlug.UDB_SCRIPT_VERSION && !scriptinfo.IgnoreVersion)
|
||||
if (scriptinfo.Version > BuilderPlug.UDB_SCRIPT_VERSION && !scriptinfo.IgnoreVersion)
|
||||
{
|
||||
if (MessageBox.Show("The script requires a higher version of the feature set than this version of UDBScript supports. Executing this script might fail\n\nRequired feature version: " + scriptinfo.Version + "\nUDBScript feature version: " + BuilderPlug.UDB_SCRIPT_VERSION + "\n\nExecute anyway?", "UDBScript feature version too low", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No)
|
||||
return;
|
||||
|
@ -208,22 +272,19 @@ namespace CodeImp.DoomBuilder.UDBScript
|
|||
scriptinfo.IgnoreVersion = true;
|
||||
}
|
||||
|
||||
// Read the current script file
|
||||
string script = File.ReadAllText(scriptinfo.ScriptFile);
|
||||
|
||||
// Make sure the option value gets saved if an option is currently being edited
|
||||
BuilderPlug.Me.EndOptionEdit();
|
||||
General.Interface.Focus();
|
||||
|
||||
|
||||
// Set engine options
|
||||
Options options = new Options();
|
||||
options.Constraint(new RuntimeConstraint(stopwatch));
|
||||
options.CancellationToken(cancellationtoken);
|
||||
options.AllowOperatorOverloading();
|
||||
options.SetTypeResolver(new TypeResolver {
|
||||
options.SetTypeResolver(new TypeResolver
|
||||
{
|
||||
MemberFilter = member => member.Name != nameof(GetType)
|
||||
});
|
||||
|
||||
|
||||
// Create the script engine
|
||||
engine = new Engine(options);
|
||||
engine.SetValue("showMessage", new Action<object>(ShowMessage));
|
||||
|
@ -260,65 +321,41 @@ namespace CodeImp.DoomBuilder.UDBScript
|
|||
// Tell the mode that a script is about to be run
|
||||
General.Editing.Mode.OnScriptRunBegin();
|
||||
|
||||
General.Map.UndoRedo.CreateUndo("Run script " + scriptinfo.Name);
|
||||
General.Map.Map.ClearAllMarks(false);
|
||||
|
||||
General.Map.Map.IsSafeToAccess = false;
|
||||
|
||||
// Disable all processing. Has to be done as many times as it was enabled.
|
||||
// Save old value since after running the script we need to enable it as many times
|
||||
oldprocessingcount = General.Interface.ProcessingCount;
|
||||
for (int i = 0; i < oldprocessingcount; i++)
|
||||
General.Interface.DisableProcessing();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the script
|
||||
/// </summary>
|
||||
public void Run(IProgress<int> progress, IProgress<string> status, IProgress<string> log)
|
||||
{
|
||||
engine.SetValue("ProgressInfo", new ProgressInfo(progress, status, log));
|
||||
// Read the current script file
|
||||
string script = File.ReadAllText(scriptinfo.ScriptFile);
|
||||
|
||||
// Run the script file
|
||||
try
|
||||
{
|
||||
General.Map.UndoRedo.CreateUndo("Run script " + scriptinfo.Name);
|
||||
General.Map.Map.ClearAllMarks(false);
|
||||
ParserOptions po = new ParserOptions(scriptinfo.ScriptFile.Remove(0, General.AppPath.Length));
|
||||
|
||||
ParserOptions po = new ParserOptions(scriptinfo.ScriptFile.Remove(0, General.AppPath.Length));
|
||||
stopwatch.Start();
|
||||
engine.Execute(script, po);
|
||||
stopwatch.Stop();
|
||||
}
|
||||
|
||||
stopwatch.Start();
|
||||
engine.Execute(script, po);
|
||||
stopwatch.Stop();
|
||||
}
|
||||
catch (UserScriptAbortException)
|
||||
{
|
||||
General.Interface.DisplayStatus(StatusType.Warning, "Script aborted");
|
||||
abort = true;
|
||||
}
|
||||
catch (ParserException e)
|
||||
{
|
||||
MessageBox.Show("There is an error while parsing the script:\n\n" + e.Message, "Script error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
abort = true;
|
||||
}
|
||||
catch (Jint.Runtime.JavaScriptException e)
|
||||
{
|
||||
if (e.Error.Type != Jint.Runtime.Types.String)
|
||||
{
|
||||
//MessageBox.Show("There is an error in the script in line " + e.LineNumber + ":\n\n" + e.Message + "\n\n" + e.StackTrace, "Script error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
UDBScriptErrorForm sef = new UDBScriptErrorForm(e.Message, e.StackTrace);
|
||||
sef.ShowDialog();
|
||||
}
|
||||
else
|
||||
General.Interface.DisplayStatus(StatusType.Warning, e.Message); // We get here if "throw" is used in a script
|
||||
|
||||
abort = true;
|
||||
}
|
||||
catch(ExitScriptException e)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.Message))
|
||||
General.Interface.DisplayStatus(StatusType.Ready, e.Message);
|
||||
}
|
||||
catch(DieScriptException e)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.Message))
|
||||
General.Interface.DisplayStatus(StatusType.Warning, e.Message);
|
||||
|
||||
abort = true;
|
||||
}
|
||||
catch (Exception e) // Catch anything else we didn't think about
|
||||
{
|
||||
UDBScriptErrorForm sef = new UDBScriptErrorForm(e.Message, e.StackTrace);
|
||||
sef.ShowDialog();
|
||||
|
||||
abort = true;
|
||||
}
|
||||
|
||||
if (abort)
|
||||
{
|
||||
General.Map.UndoRedo.WithdrawUndo();
|
||||
}
|
||||
/// <summary>
|
||||
/// Cleanups and updates after the script stopped running. Has to be called from the UI thread.
|
||||
/// </summary>
|
||||
public void PostRun()
|
||||
{
|
||||
General.Map.Map.IsSafeToAccess = true;
|
||||
|
||||
// Do some updates
|
||||
General.Map.Map.Update();
|
||||
|
@ -327,6 +364,10 @@ namespace CodeImp.DoomBuilder.UDBScript
|
|||
|
||||
// Tell the mode that running the script ended
|
||||
General.Editing.Mode.OnScriptRunEnd();
|
||||
|
||||
// Enable processing again, if required
|
||||
for (int i = 0; i < oldprocessingcount; i++)
|
||||
General.Interface.EnableProcessing();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -93,6 +93,7 @@
|
|||
<Compile Include="Controls\ScriptOptionsControl.Designer.cs">
|
||||
<DependentUpon>ScriptOptionsControl.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ProgressInfo.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
|
@ -123,6 +124,12 @@
|
|||
<Compile Include="Windows\QueryOptionsForm.Designer.cs">
|
||||
<DependentUpon>QueryOptionsForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\ScriptRunnerForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Windows\ScriptRunnerForm.Designer.cs">
|
||||
<DependentUpon>ScriptRunnerForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Windows\UDBScriptErrorForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
|
@ -162,6 +169,9 @@
|
|||
<EmbeddedResource Include="Windows\QueryOptionsForm.resx">
|
||||
<DependentUpon>QueryOptionsForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Windows\ScriptRunnerForm.resx">
|
||||
<DependentUpon>ScriptRunnerForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Windows\UDBScriptErrorForm.resx">
|
||||
<DependentUpon>UDBScriptErrorForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
|
|
110
Source/Plugins/UDBScript/Windows/ScriptRunnerForm.Designer.cs
generated
Normal file
110
Source/Plugins/UDBScript/Windows/ScriptRunnerForm.Designer.cs
generated
Normal file
|
@ -0,0 +1,110 @@
|
|||
namespace CodeImp.DoomBuilder.UDBScript
|
||||
{
|
||||
partial class ScriptRunnerForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.progressbar = new System.Windows.Forms.ProgressBar();
|
||||
this.lbStatus = new System.Windows.Forms.Label();
|
||||
this.btnAction = new System.Windows.Forms.Button();
|
||||
this.tbLog = new System.Windows.Forms.TextBox();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// progressbar
|
||||
//
|
||||
this.progressbar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.progressbar.Location = new System.Drawing.Point(12, 25);
|
||||
this.progressbar.Name = "progressbar";
|
||||
this.progressbar.Size = new System.Drawing.Size(419, 23);
|
||||
this.progressbar.Step = 1;
|
||||
this.progressbar.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
|
||||
this.progressbar.TabIndex = 0;
|
||||
//
|
||||
// lbStatus
|
||||
//
|
||||
this.lbStatus.AutoSize = true;
|
||||
this.lbStatus.Location = new System.Drawing.Point(12, 9);
|
||||
this.lbStatus.Name = "lbStatus";
|
||||
this.lbStatus.Size = new System.Drawing.Size(84, 13);
|
||||
this.lbStatus.TabIndex = 1;
|
||||
this.lbStatus.Text = "Running script...";
|
||||
//
|
||||
// btnAction
|
||||
//
|
||||
this.btnAction.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnAction.Location = new System.Drawing.Point(437, 25);
|
||||
this.btnAction.Name = "btnAction";
|
||||
this.btnAction.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnAction.TabIndex = 2;
|
||||
this.btnAction.Text = "Cancel";
|
||||
this.btnAction.UseVisualStyleBackColor = true;
|
||||
this.btnAction.Click += new System.EventHandler(this.btnAction_Click);
|
||||
//
|
||||
// tbLog
|
||||
//
|
||||
this.tbLog.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.tbLog.Location = new System.Drawing.Point(12, 54);
|
||||
this.tbLog.Multiline = true;
|
||||
this.tbLog.Name = "tbLog";
|
||||
this.tbLog.ReadOnly = true;
|
||||
this.tbLog.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||
this.tbLog.Size = new System.Drawing.Size(500, 118);
|
||||
this.tbLog.TabIndex = 3;
|
||||
//
|
||||
// ScriptRunnerForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(524, 184);
|
||||
this.ControlBox = false;
|
||||
this.Controls.Add(this.tbLog);
|
||||
this.Controls.Add(this.btnAction);
|
||||
this.Controls.Add(this.lbStatus);
|
||||
this.Controls.Add(this.progressbar);
|
||||
this.MinimumSize = new System.Drawing.Size(540, 200);
|
||||
this.Name = "ScriptRunnerForm";
|
||||
this.ShowIcon = false;
|
||||
this.Text = "Running script";
|
||||
this.WindowState = System.Windows.Forms.FormWindowState.Minimized;
|
||||
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.ScriptRunnerForm_FormClosed);
|
||||
this.Shown += new System.EventHandler(this.ScriptRunnerForm_Shown);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.ProgressBar progressbar;
|
||||
private System.Windows.Forms.Label lbStatus;
|
||||
private System.Windows.Forms.Button btnAction;
|
||||
private System.Windows.Forms.TextBox tbLog;
|
||||
}
|
||||
}
|
334
Source/Plugins/UDBScript/Windows/ScriptRunnerForm.cs
Normal file
334
Source/Plugins/UDBScript/Windows/ScriptRunnerForm.cs
Normal file
|
@ -0,0 +1,334 @@
|
|||
#region ================== Copyright (c) 2022 Boris Iwanski
|
||||
|
||||
/*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
*
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
*
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program.If not, see<http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Namespaces
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using CodeImp.DoomBuilder.Windows;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CodeImp.DoomBuilder.UDBScript
|
||||
{
|
||||
public partial class ScriptRunnerForm : DelayedForm
|
||||
{
|
||||
#region ================== Constants
|
||||
|
||||
/// <summary>
|
||||
/// How long a script is allowed to run until the form is made visible.
|
||||
/// </summary>
|
||||
const int RUNTIME_THRESHOLD = 1000;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Variables
|
||||
|
||||
/// <summary>
|
||||
/// Cancellation token for stopping the script.
|
||||
/// </summary>
|
||||
CancellationTokenSource cancellationtokensource;
|
||||
|
||||
/// <summary>
|
||||
/// If script is currently executed or not.
|
||||
/// </summary>
|
||||
bool running;
|
||||
|
||||
/// <summary>
|
||||
/// How many milliseconds the script has been running
|
||||
/// </summary>
|
||||
double runningseconds;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the form should be automatically closed when the script is finished.
|
||||
/// </summary>
|
||||
bool autoclose;
|
||||
|
||||
/// <summary>
|
||||
/// Stopwatch used to determine how long the script is already running.
|
||||
/// </summary>
|
||||
Stopwatch stopwatch;
|
||||
|
||||
/// <summary>
|
||||
/// Timer for making the form visible when the script is running for too long.
|
||||
/// </summary>
|
||||
System.Windows.Forms.Timer timer;
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Methods
|
||||
|
||||
public ScriptRunnerForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes a method, but stops the timer before running the code, and starting the timer after the code ran.
|
||||
/// </summary>
|
||||
/// <param name="method">Method to invoke</param>
|
||||
/// <returns>Return value of the method</returns>
|
||||
public object InvokePaused(Delegate method)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
return Invoke(new Action(() => InvokePaused(method)));
|
||||
}
|
||||
else
|
||||
{
|
||||
stopwatch.Stop();
|
||||
object result = Invoke(method);
|
||||
stopwatch.Start();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes a method.
|
||||
/// </summary>
|
||||
/// <param name="action">Method to invoke</param>
|
||||
public void RunAction(Action action)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
Invoke(action);
|
||||
else
|
||||
action();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of the progress bar (in the range from 0 to 100).
|
||||
/// </summary>
|
||||
/// <param name="value">Value of the progress bar (in the range from 0 to 100)</param>
|
||||
private void SetProgress(int value)
|
||||
{
|
||||
if (progressbar.Style != ProgressBarStyle.Continuous)
|
||||
progressbar.Style = ProgressBarStyle.Continuous;
|
||||
|
||||
// Do some trickery to remove the movement of the progress bar, since it can
|
||||
// otherwise screw with how much the progress bar is filled
|
||||
if (progressbar.Value != value)
|
||||
{
|
||||
if (value > progressbar.Maximum)
|
||||
value = progressbar.Maximum;
|
||||
else if (value < progressbar.Minimum)
|
||||
value = progressbar.Minimum;
|
||||
|
||||
if (progressbar.Maximum == value)
|
||||
{
|
||||
progressbar.Value = value;
|
||||
progressbar.Value = value - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
progressbar.Value = value + 1;
|
||||
}
|
||||
progressbar.Value = value;
|
||||
}
|
||||
|
||||
// Make the form visible so that the user can actually see the progress bar
|
||||
MakeVisible();
|
||||
}
|
||||
|
||||
private void SetProgressStatus(string status)
|
||||
{
|
||||
lbStatus.Text = status;
|
||||
}
|
||||
|
||||
private void Log(string text)
|
||||
{
|
||||
// If there's something in the log we don't want to automatically
|
||||
// close the form, otherwise the user could not read the contents
|
||||
autoclose = false;
|
||||
|
||||
// Since we don't want to have a useless line at the end of the textbox
|
||||
// we add a new line before adding the new text (unless there's no text
|
||||
// at all yet, then we don't add a new line
|
||||
if (!string.IsNullOrEmpty(tbLog.Text))
|
||||
tbLog.AppendText(Environment.NewLine);
|
||||
|
||||
// Add the new text
|
||||
tbLog.AppendText(text);
|
||||
|
||||
// Make the form visible so that the user can actually see the status bar
|
||||
MakeVisible();
|
||||
}
|
||||
|
||||
private async void RunScript(CancellationToken cancellationtoken)
|
||||
{
|
||||
// Callbacks for setting the progress bar, status text, and adding log lines from the script
|
||||
Progress<int> progress = new Progress<int>(SetProgress);
|
||||
Progress<string> status = new Progress<string>(SetProgressStatus);
|
||||
Progress<string> log = new Progress<string>(Log);
|
||||
|
||||
running = true;
|
||||
|
||||
// Prepare running the script
|
||||
BuilderPlug.Me.ScriptRunner.PreRun(cancellationtoken);
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Run(() => BuilderPlug.Me.ScriptRunner.Run(progress, status, log));
|
||||
stopwatch.Stop();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
stopwatch.Stop();
|
||||
BuilderPlug.Me.ScriptRunner.HandleExceptions(ex);
|
||||
}
|
||||
|
||||
// Clean up and update after running the script
|
||||
BuilderPlug.Me.ScriptRunner.PostRun();
|
||||
|
||||
running = false;
|
||||
|
||||
btnAction.Text = "Close";
|
||||
btnAction.Enabled = true;
|
||||
|
||||
if (autoclose)
|
||||
{
|
||||
MakeInvisible();
|
||||
//Hide();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes the form visible.
|
||||
/// </summary>
|
||||
private void MakeVisible()
|
||||
{
|
||||
Opacity = 1.0;
|
||||
btnAction.Enabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes the form invisible.
|
||||
/// </summary>
|
||||
private void MakeInvisible()
|
||||
{
|
||||
Opacity = 0.0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Events
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the currently running script, or closes the form if no script is running.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender</param>
|
||||
/// <param name="e">Event arguments</param>
|
||||
private void btnAction_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (running)
|
||||
{
|
||||
btnAction.Enabled = false;
|
||||
cancellationtokensource.Cancel();
|
||||
}
|
||||
else
|
||||
{
|
||||
MakeInvisible();
|
||||
//Hide();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets everything up for running the script, and then immediately runs the script.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender</param>
|
||||
/// <param name="e">Event arguments</param>
|
||||
private void ScriptRunnerForm_Shown(object sender, EventArgs e)
|
||||
{
|
||||
cancellationtokensource = new CancellationTokenSource();
|
||||
autoclose = true;
|
||||
runningseconds = 0;
|
||||
|
||||
progressbar.Value = 0;
|
||||
progressbar.Style = ProgressBarStyle.Marquee;
|
||||
|
||||
Text = "Running script";
|
||||
lbStatus.Text = "Running script...";
|
||||
|
||||
btnAction.Text = "Cancel";
|
||||
// Disable the button because it could otherwise be pressed while the form is invisible.
|
||||
// It'll be enabled as soon as the form is made visible
|
||||
btnAction.Enabled = false;
|
||||
|
||||
tbLog.Clear();
|
||||
|
||||
// The timer ticks ever 100ms. The method it runs checks how long the script is running
|
||||
// and makes the form visible if the runtime threshold has been reached
|
||||
timer = new System.Windows.Forms.Timer();
|
||||
timer.Interval = 100;
|
||||
timer.Tick += timerShow_Tick;
|
||||
timer.Start();
|
||||
|
||||
// This stopwatch is used to measure how long the script has been running
|
||||
stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
// Start running the script
|
||||
RunScript(cancellationtokensource.Token);
|
||||
}
|
||||
|
||||
protected override void OnLoad(EventArgs e)
|
||||
{
|
||||
base.OnLoad(e);
|
||||
|
||||
MakeInvisible();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes the form visible if the runtime threshold has been reached. Shows the elapsed time the script is running.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender</param>
|
||||
/// <param name="e">Event arguments</param>
|
||||
private void timerShow_Tick(object sender, EventArgs e)
|
||||
{
|
||||
if (Opacity == 0.0 && stopwatch.ElapsedMilliseconds > 1000)
|
||||
{
|
||||
MakeVisible();
|
||||
}
|
||||
|
||||
double newrunningsecods = Math.Floor(stopwatch.Elapsed.TotalSeconds);
|
||||
|
||||
if(newrunningsecods > runningseconds)
|
||||
{
|
||||
runningseconds = newrunningsecods;
|
||||
Text = "Running script (" + string.Format("{0:D2}:{1:D2}:{2:D2}", stopwatch.Elapsed.Hours, stopwatch.Elapsed.Minutes, stopwatch.Elapsed.Seconds) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
private void ScriptRunnerForm_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
timer.Stop();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
120
Source/Plugins/UDBScript/Windows/ScriptRunnerForm.resx
Normal file
120
Source/Plugins/UDBScript/Windows/ScriptRunnerForm.resx
Normal file
|
@ -0,0 +1,120 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
Loading…
Reference in a new issue