mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-11-23 20:32:34 +00:00
1374 lines
40 KiB
C#
1374 lines
40 KiB
C#
|
|
#region ================== Copyright (c) 2007 Pascal vd Heiden
|
|
|
|
/*
|
|
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
|
|
* This program is released under GNU General Public License
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#endregion
|
|
|
|
#region ================== Namespaces
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using CodeImp.DoomBuilder.Windows;
|
|
using CodeImp.DoomBuilder.IO;
|
|
using CodeImp.DoomBuilder.Map;
|
|
using CodeImp.DoomBuilder.Geometry;
|
|
using System.Runtime.InteropServices;
|
|
using CodeImp.DoomBuilder.Actions;
|
|
using System.Diagnostics;
|
|
using CodeImp.DoomBuilder.Rendering;
|
|
using CodeImp.DoomBuilder.Config;
|
|
using SlimDX.Direct3D9;
|
|
using System.Drawing;
|
|
using CodeImp.DoomBuilder.Plugins;
|
|
using CodeImp.DoomBuilder.Types;
|
|
using System.Collections.ObjectModel;
|
|
using System.Threading;
|
|
|
|
#endregion
|
|
|
|
namespace CodeImp.DoomBuilder
|
|
{
|
|
public static class General
|
|
{
|
|
#region ================== API Declarations
|
|
|
|
[DllImport("user32.dll")]
|
|
internal static extern bool LockWindowUpdate(IntPtr hwnd);
|
|
|
|
[DllImport("kernel32.dll", EntryPoint = "RtlZeroMemory", SetLastError = false)]
|
|
internal static extern void ZeroMemory(IntPtr dest, int size);
|
|
|
|
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
|
|
internal static extern unsafe void CopyMemory(void* dst, void* src, UIntPtr length);
|
|
|
|
[DllImport("user32.dll", EntryPoint = "SendMessage", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
|
|
internal static extern int SendMessage(IntPtr hwnd, uint Msg, int wParam, int lParam);
|
|
|
|
[DllImport("user32.dll", SetLastError = true)]
|
|
internal static extern bool MessageBeep(MessageBeepType type);
|
|
|
|
[DllImport("kernel32.dll")]
|
|
internal extern static IntPtr LoadLibrary(string filename);
|
|
|
|
[DllImport("kernel32.dll")]
|
|
internal extern static bool FreeLibrary(IntPtr moduleptr);
|
|
|
|
[DllImport("user32.dll")]
|
|
internal static extern IntPtr CreateWindowEx(uint exstyle, string classname, string windowname, uint style,
|
|
int x, int y, int width, int height, IntPtr parentptr, int menu,
|
|
IntPtr instanceptr, string param);
|
|
|
|
[DllImport("user32.dll")]
|
|
internal static extern bool DestroyWindow(IntPtr windowptr);
|
|
|
|
[DllImport("user32.dll")]
|
|
internal static extern int SetWindowPos(IntPtr windowptr, int insertafterptr, int x, int y, int cx, int cy, int flags);
|
|
|
|
#endregion
|
|
|
|
#region ================== Constants
|
|
|
|
// SendMessage API
|
|
internal const int WM_USER = 0x400;
|
|
internal const int CB_SETITEMHEIGHT = 0x153;
|
|
internal const int EM_GETSCROLLPOS = WM_USER + 221;
|
|
internal const int EM_SETSCROLLPOS = WM_USER + 222;
|
|
|
|
// Files and Folders
|
|
private const string SETTINGS_FILE = "Builder.cfg";
|
|
private const string SETTINGS_DIR = "Doom Builder";
|
|
private const string LOG_FILE = "Builder.log";
|
|
private const string GAME_CONFIGS_DIR = "Configurations";
|
|
private const string COMPILERS_DIR = "Compilers";
|
|
private const string PLUGINS_DIR = "Plugins";
|
|
private const string SETUP_DIR = "Setup";
|
|
|
|
#endregion
|
|
|
|
#region ================== Variables
|
|
|
|
// Files and Folders
|
|
private static string apppath;
|
|
private static string setuppath;
|
|
private static string settingspath;
|
|
private static string logfile;
|
|
private static string temppath;
|
|
private static string configspath;
|
|
private static string compilerspath;
|
|
private static string pluginspath;
|
|
|
|
// Main objects
|
|
private static Assembly thisasm;
|
|
private static MainForm mainwindow;
|
|
private static ProgramConfiguration settings;
|
|
private static MapManager map;
|
|
private static ActionManager actions;
|
|
private static PluginManager plugins;
|
|
private static ColorCollection colors;
|
|
private static TypesManager types;
|
|
private static Clock clock;
|
|
|
|
// Configurations
|
|
private static List<ConfigurationInfo> configs;
|
|
private static List<CompilerInfo> compilers;
|
|
private static List<NodebuilderInfo> nodebuilders;
|
|
|
|
// States
|
|
private static bool debugbuild;
|
|
|
|
// Command line arguments
|
|
private static string[] cmdargs;
|
|
private static string autoloadfile = null;
|
|
private static string autoloadmap = null;
|
|
private static string autoloadconfig = null;
|
|
private static bool delaymainwindow;
|
|
|
|
#endregion
|
|
|
|
#region ================== Properties
|
|
|
|
internal static Assembly ThisAssembly { get { return thisasm; } }
|
|
public static string AppPath { get { return apppath; } }
|
|
public static string TempPath { get { return temppath; } }
|
|
public static string ConfigsPath { get { return configspath; } }
|
|
public static string CompilersPath { get { return compilerspath; } }
|
|
public static string PluginsPath { get { return pluginspath; } }
|
|
public static ICollection<string> CommandArgs { get { return Array.AsReadOnly<string>(cmdargs); } }
|
|
internal static MainForm MainWindow { get { return mainwindow; } }
|
|
public static IMainForm Interface { get { return mainwindow; } }
|
|
public static ProgramConfiguration Settings { get { return settings; } }
|
|
public static ColorCollection Colors { get { return colors; } }
|
|
internal static List<ConfigurationInfo> Configs { get { return configs; } }
|
|
internal static List<NodebuilderInfo> Nodebuilders { get { return nodebuilders; } }
|
|
internal static List<CompilerInfo> Compilers { get { return compilers; } }
|
|
public static MapManager Map { get { return map; } }
|
|
internal static ActionManager Actions { get { return actions; } }
|
|
internal static PluginManager Plugins { get { return plugins; } }
|
|
public static Clock Clock { get { return clock; } }
|
|
public static bool DebugBuild { get { return debugbuild; } }
|
|
internal static TypesManager Types { get { return types; } }
|
|
public static string AutoLoadFile { get { return autoloadfile; } }
|
|
public static string AutoLoadMap { get { return autoloadmap; } }
|
|
public static string AutoLoadConfig { get { return autoloadconfig; } }
|
|
public static bool DelayMainWindow { get { return delaymainwindow; } }
|
|
|
|
#endregion
|
|
|
|
#region ================== Configurations
|
|
|
|
// This returns the game configuration info by filename
|
|
internal static ConfigurationInfo GetConfigurationInfo(string filename)
|
|
{
|
|
// Go for all config infos
|
|
foreach(ConfigurationInfo ci in configs)
|
|
{
|
|
// Check if filename matches
|
|
if(string.Compare(Path.GetFileNameWithoutExtension(ci.Filename),
|
|
Path.GetFileNameWithoutExtension(filename), true) == 0)
|
|
{
|
|
// Return this info
|
|
return ci;
|
|
}
|
|
}
|
|
|
|
// None found
|
|
return null;
|
|
}
|
|
|
|
// This loads and returns a game configuration
|
|
internal static Configuration LoadGameConfiguration(string filename)
|
|
{
|
|
Configuration cfg;
|
|
|
|
// Make the full filepathname
|
|
string filepathname = Path.Combine(configspath, filename);
|
|
|
|
// Load configuration
|
|
try
|
|
{
|
|
// Try loading the configuration
|
|
cfg = new Configuration(filepathname, true);
|
|
|
|
// Check for erors
|
|
if(cfg.ErrorResult != 0)
|
|
{
|
|
// Error in configuration
|
|
ShowErrorMessage("Unable to load the game configuration file \"" + filename + "\".\n" +
|
|
"Error near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription, MessageBoxButtons.OK);
|
|
return null;
|
|
}
|
|
// Check if this is a Doom Builder 2 config
|
|
else if(cfg.ReadSetting("type", "") != "Doom Builder 2 Game Configuration")
|
|
{
|
|
// Old configuration
|
|
ShowErrorMessage("Unable to load the game configuration file \"" + filename + "\".\n" +
|
|
"This configuration is not a Doom Builder 2 game configuration.", MessageBoxButtons.OK);
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
// The following code was used to convert the linedef types of DB1 type
|
|
// configurations into proper categorized structures for DB2.
|
|
// I keep this code here in the repository because if it failed, I might
|
|
// need this again to convert from scratch.
|
|
/*
|
|
GameConfiguration gcfg = new GameConfiguration(cfg);
|
|
Configuration newcfg = new Configuration();
|
|
newcfg.NewConfiguration(true);
|
|
bool doommap = (gcfg.FormatInterface == "DoomMapSetIO");
|
|
|
|
foreach(LinedefActionInfo a in gcfg.SortedLinedefActions)
|
|
{
|
|
string catkey = a.Category.ToLowerInvariant().Trim();
|
|
string cattitle = a.Category;
|
|
string linekey = a.Index.ToString(CultureInfo.InvariantCulture);
|
|
string linetitle = a.Name;
|
|
string lineprefix = a.Prefix;
|
|
if(catkey.Length == 0) { catkey = "misc"; cattitle = ""; }
|
|
if(cattitle.Length > 0) newcfg.WriteSetting("linedeftypes." + catkey + ".title", cattitle);
|
|
newcfg.WriteSetting("linedeftypes." + catkey + "." + linekey + ".title", linetitle);
|
|
if(doommap) newcfg.WriteSetting("linedeftypes." + catkey + "." + linekey + ".prefix", lineprefix);
|
|
|
|
if(!doommap)
|
|
{
|
|
for(int i = 0; i < 5; i++)
|
|
{
|
|
if(a.ArgUsed[i])
|
|
{
|
|
newcfg.WriteSetting("linedeftypes." + catkey + "." + linekey + ".arg" + i.ToString(CultureInfo.InvariantCulture) + ".title", a.ArgTitle[i]);
|
|
if(a.ArgTagType[i] != TagType.None) newcfg.WriteSetting("linedeftypes." + catkey + "." + linekey + ".arg" + i.ToString(CultureInfo.InvariantCulture) + ".tag", (int)a.ArgTagType[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
newcfg.SaveConfiguration(Path.Combine(configspath, "_" + filename));
|
|
*/
|
|
|
|
// Return config
|
|
return cfg;
|
|
}
|
|
}
|
|
catch(Exception)
|
|
{
|
|
// Unable to load configuration
|
|
ShowErrorMessage("Unable to load the game configuration file \"" + filename + "\".", MessageBoxButtons.OK);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// This loads all game configurations
|
|
private static void LoadAllGameConfigurations()
|
|
{
|
|
Configuration cfg;
|
|
string[] filenames;
|
|
string name, fullfilename;
|
|
|
|
// Display status
|
|
mainwindow.DisplayStatus("Loading game configurations...");
|
|
|
|
// Make array
|
|
configs = new List<ConfigurationInfo>();
|
|
|
|
// Go for all cfg files in the configurations directory
|
|
filenames = Directory.GetFiles(configspath, "*.cfg", SearchOption.TopDirectoryOnly);
|
|
foreach(string filepath in filenames)
|
|
{
|
|
// Check if it can be loaded
|
|
cfg = LoadGameConfiguration(Path.GetFileName(filepath));
|
|
if(cfg != null)
|
|
{
|
|
fullfilename = Path.GetFileName(filepath);
|
|
ConfigurationInfo cfginfo = new ConfigurationInfo(cfg, fullfilename);
|
|
|
|
// Add to lists
|
|
General.WriteLogLine("Registered game configuration '" + cfginfo.Name + "' from '" + fullfilename + "'");
|
|
configs.Add(cfginfo);
|
|
}
|
|
}
|
|
|
|
// Sort the list
|
|
configs.Sort();
|
|
}
|
|
|
|
// This loads all nodebuilder configurations
|
|
private static void LoadAllNodebuilderConfigurations()
|
|
{
|
|
Configuration cfg;
|
|
IDictionary builderslist;
|
|
string[] filenames;
|
|
|
|
// Display status
|
|
mainwindow.DisplayStatus("Loading nodebuilder configurations...");
|
|
|
|
// Make array
|
|
nodebuilders = new List<NodebuilderInfo>();
|
|
|
|
// Go for all cfg files in the compilers directory
|
|
filenames = Directory.GetFiles(compilerspath, "*.cfg", SearchOption.TopDirectoryOnly);
|
|
foreach(string filepath in filenames)
|
|
{
|
|
try
|
|
{
|
|
// Try loading the configuration
|
|
cfg = new Configuration(filepath, true);
|
|
|
|
// Check for erors
|
|
if(cfg.ErrorResult != 0)
|
|
{
|
|
// Error in configuration
|
|
ShowErrorMessage("Unable to load the compiler configuration file \"" + Path.GetFileName(filepath) + "\".\n" +
|
|
"Error near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription, MessageBoxButtons.OK);
|
|
}
|
|
else
|
|
{
|
|
// Get structures
|
|
builderslist = cfg.ReadSetting("nodebuilders", new Hashtable());
|
|
foreach(DictionaryEntry de in builderslist)
|
|
{
|
|
// Check if this is a structure
|
|
if(de.Value is IDictionary)
|
|
{
|
|
try
|
|
{
|
|
// Make nodebuilder info
|
|
nodebuilders.Add(new NodebuilderInfo(Path.GetFileName(filepath), de.Key.ToString(), cfg));
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
// Unable to load configuration
|
|
ShowErrorMessage("Unable to load the nodebuilder configuration '" + de.Key.ToString() + "' from \"" + Path.GetFileName(filepath) + "\". Error: " + e.Message, MessageBoxButtons.OK);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch(Exception)
|
|
{
|
|
// Unable to load configuration
|
|
ShowErrorMessage("Unable to load the compiler configuration file \"" + Path.GetFileName(filepath) + "\".", MessageBoxButtons.OK);
|
|
}
|
|
}
|
|
|
|
// Sort the list
|
|
nodebuilders.Sort();
|
|
}
|
|
|
|
// This loads all compiler configurations
|
|
private static void LoadAllCompilerConfigurations()
|
|
{
|
|
Configuration cfg;
|
|
IDictionary compilerslist;
|
|
string[] filenames;
|
|
|
|
// Display status
|
|
mainwindow.DisplayStatus("Loading compiler configurations...");
|
|
|
|
// Make array
|
|
compilers = new List<CompilerInfo>();
|
|
|
|
// Go for all cfg files in the compilers directory
|
|
filenames = Directory.GetFiles(compilerspath, "*.cfg", SearchOption.TopDirectoryOnly);
|
|
foreach(string filepath in filenames)
|
|
{
|
|
try
|
|
{
|
|
// Try loading the configuration
|
|
cfg = new Configuration(filepath, true);
|
|
|
|
// Check for erors
|
|
if(cfg.ErrorResult != 0)
|
|
{
|
|
// Error in configuration
|
|
ShowErrorMessage("Unable to load the compiler configuration file \"" + Path.GetFileName(filepath) + "\".\n" +
|
|
"Error near line " + cfg.ErrorLine + ": " + cfg.ErrorDescription, MessageBoxButtons.OK);
|
|
}
|
|
else
|
|
{
|
|
// Get structures
|
|
compilerslist = cfg.ReadSetting("compilers", new Hashtable());
|
|
foreach(DictionaryEntry de in compilerslist)
|
|
{
|
|
// Check if this is a structure
|
|
if(de.Value is IDictionary)
|
|
{
|
|
// Make compiler info
|
|
compilers.Add(new CompilerInfo(Path.GetFileName(filepath), de.Key.ToString(), cfg));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch(Exception)
|
|
{
|
|
// Unable to load configuration
|
|
ShowErrorMessage("Unable to load the compiler configuration file \"" + Path.GetFileName(filepath) + "\".", MessageBoxButtons.OK);
|
|
}
|
|
}
|
|
}
|
|
|
|
// This returns a nodebuilder by name
|
|
internal static NodebuilderInfo GetNodebuilderByName(string name)
|
|
{
|
|
// Go for all nodebuilders
|
|
foreach(NodebuilderInfo n in nodebuilders)
|
|
{
|
|
// Name matches?
|
|
if(n.Name == name) return n;
|
|
}
|
|
|
|
// Cannot find that nodebuilder
|
|
return null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Startup
|
|
|
|
// Main program entry
|
|
[STAThread]
|
|
internal static void Main(string[] args)
|
|
{
|
|
Uri localpath;
|
|
Version thisversion;
|
|
|
|
// Determine states
|
|
#if DEBUG
|
|
debugbuild = true;
|
|
#else
|
|
debugbuild = false;
|
|
#endif
|
|
|
|
// Enable OS visual styles
|
|
Application.EnableVisualStyles();
|
|
Application.DoEvents(); // This must be here to work around a .NET bug
|
|
|
|
// Hook to DLL loading failure event
|
|
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
|
|
|
|
// Set current thread name
|
|
Thread.CurrentThread.Name = "Main Application";
|
|
|
|
// Get a reference to this assembly
|
|
thisasm = Assembly.GetExecutingAssembly();
|
|
thisversion = thisasm.GetName().Version;
|
|
|
|
// Find application path
|
|
localpath = new Uri(Path.GetDirectoryName(thisasm.GetName().CodeBase));
|
|
apppath = Uri.UnescapeDataString(localpath.AbsolutePath);
|
|
|
|
// Setup directories
|
|
temppath = Path.GetTempPath();
|
|
setuppath = Path.Combine(apppath, SETUP_DIR);
|
|
settingspath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), SETTINGS_DIR);
|
|
configspath = Path.Combine(apppath, GAME_CONFIGS_DIR);
|
|
compilerspath = Path.Combine(apppath, COMPILERS_DIR);
|
|
pluginspath = Path.Combine(apppath, PLUGINS_DIR);
|
|
logfile = Path.Combine(settingspath, LOG_FILE);
|
|
|
|
// Make program settings directory if missing
|
|
if(!Directory.Exists(settingspath)) Directory.CreateDirectory(settingspath);
|
|
|
|
// Remove the previous log file and start logging
|
|
if(File.Exists(logfile)) File.Delete(logfile);
|
|
General.WriteLogLine("Doom Builder " + thisversion.Major + "." + thisversion.Minor + " startup");
|
|
General.WriteLogLine("Application path: " + apppath);
|
|
General.WriteLogLine("Temporary path: " + temppath);
|
|
General.WriteLogLine("Local settings path: " + settingspath);
|
|
General.WriteLogLine("Configurations path: " + configspath);
|
|
General.WriteLogLine("Compilers path: " + compilerspath);
|
|
General.WriteLogLine("Plugins path: " + pluginspath);
|
|
General.WriteLogLine("Command-line arguments: " + args.Length);
|
|
for(int i = 0; i < args.Length; i++)
|
|
General.WriteLogLine("Argument " + i + ": \"" + args[i] + "\"");
|
|
|
|
// Parse command-line arguments
|
|
ParseCommandLineArgs(args);
|
|
|
|
// Load configuration
|
|
General.WriteLogLine("Loading program configuration...");
|
|
settings = new ProgramConfiguration();
|
|
if(settings.Load(Path.Combine(settingspath, SETTINGS_FILE),
|
|
Path.Combine(apppath, SETTINGS_FILE)))
|
|
{
|
|
// Create action manager
|
|
actions = new ActionManager();
|
|
|
|
// Bind static methods to actions
|
|
General.Actions.BindMethods(typeof(General));
|
|
|
|
// Initialize static classes
|
|
MapSet.Initialize();
|
|
|
|
// Create main window
|
|
General.WriteLogLine("Loading main interface window...");
|
|
mainwindow = new MainForm();
|
|
mainwindow.UpdateInterface();
|
|
|
|
if(!delaymainwindow)
|
|
{
|
|
// Show main window
|
|
General.WriteLogLine("Showing main interface window...");
|
|
mainwindow.Show();
|
|
mainwindow.Update();
|
|
}
|
|
|
|
// Start Direct3D
|
|
General.WriteLogLine("Starting Direct3D graphics driver...");
|
|
try { D3DDevice.Startup(); }
|
|
catch(Direct3D9NotFoundException) { AskDownloadDirectX(); return; }
|
|
catch(Direct3DX9NotFoundException) { AskDownloadDirectX(); return; }
|
|
|
|
// Load plugin manager
|
|
General.WriteLogLine("Loading plugins...");
|
|
plugins = new PluginManager();
|
|
plugins.LoadAllPlugins();
|
|
|
|
// Now that all settings have been combined (core & plugins) apply the defaults
|
|
General.WriteLogLine("Applying configuration settings...");
|
|
actions.ApplyDefaultShortcutKeys();
|
|
|
|
// Load game configurations
|
|
General.WriteLogLine("Loading game configurations...");
|
|
LoadAllGameConfigurations();
|
|
|
|
// Load compiler configurations
|
|
General.WriteLogLine("Loading compiler configurations...");
|
|
LoadAllCompilerConfigurations();
|
|
|
|
// Load nodebuilder configurations
|
|
General.WriteLogLine("Loading nodebuilder configurations...");
|
|
LoadAllNodebuilderConfigurations();
|
|
|
|
// Load color settings
|
|
General.WriteLogLine("Loading color settings...");
|
|
colors = new ColorCollection(settings.Config);
|
|
|
|
// Create application clock
|
|
General.WriteLogLine("Creating application clock...");
|
|
clock = new Clock();
|
|
|
|
// Create types manager
|
|
General.WriteLogLine("Creating types manager...");
|
|
types = new TypesManager();
|
|
|
|
// Do auto map loading when window is delayed
|
|
if(delaymainwindow)
|
|
mainwindow.PerformAutoMapLoading();
|
|
|
|
// Run application from the main window
|
|
General.WriteLogLine("Startup done");
|
|
mainwindow.DisplayReady();
|
|
Application.Run(mainwindow);
|
|
}
|
|
else
|
|
{
|
|
// Terminate
|
|
Terminate(false);
|
|
}
|
|
}
|
|
|
|
// This handles DLL linking errors
|
|
private static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
|
|
{
|
|
// Check if SlimDX failed loading
|
|
if(args.Name.Contains("SlimDX")) AskDownloadDirectX();
|
|
|
|
// Return null
|
|
return null;
|
|
}
|
|
|
|
// This asks the user to download DirectX
|
|
private static void AskDownloadDirectX()
|
|
{
|
|
// Cancel loading map from command-line parameters, if any.
|
|
// This causes problems, because when the window is shown, the map will
|
|
// be loaded and DirectX is initialized (which we seem to be missing)
|
|
autoloadfile = null;
|
|
|
|
// Ask the user to download DirectX
|
|
if(MessageBox.Show("This application requires the latest version of Microsoft DirectX installed on your computer." + Environment.NewLine +
|
|
"Do you want to install and/or update Microsoft DirectX now?", "DirectX Error", System.Windows.Forms.MessageBoxButtons.YesNo,
|
|
System.Windows.Forms.MessageBoxIcon.Exclamation) == System.Windows.Forms.DialogResult.Yes)
|
|
{
|
|
// Open DX web setup
|
|
//System.Diagnostics.Process.Start("http://www.microsoft.com/downloads/details.aspx?FamilyId=2DA43D38-DB71-4C1B-BC6A-9B6652CD92A3").WaitForExit(1000);
|
|
System.Diagnostics.Process.Start(Path.Combine(setuppath, "dxwebsetup.exe")).WaitForExit(1000);
|
|
}
|
|
|
|
// End program here
|
|
Terminate(false);
|
|
}
|
|
|
|
// This parses the command line arguments
|
|
private static void ParseCommandLineArgs(string[] args)
|
|
{
|
|
// Keep a copy
|
|
cmdargs = args;
|
|
|
|
// Make a queue so we can parse the values from left to right
|
|
Queue<string> argslist = new Queue<string>(args);
|
|
|
|
// Parse list
|
|
while(argslist.Count > 0)
|
|
{
|
|
// Get next arg
|
|
string curarg = argslist.Dequeue();
|
|
|
|
// Delay window?
|
|
if(string.Compare(curarg, "-DELAYWINDOW", true) == 0)
|
|
{
|
|
// Delay showing the main window
|
|
delaymainwindow = true;
|
|
}
|
|
// Map name info?
|
|
else if(string.Compare(curarg, "-MAP", true) == 0)
|
|
{
|
|
// Store next arg as map name information
|
|
autoloadmap = argslist.Dequeue();
|
|
}
|
|
// Config name info?
|
|
else if((string.Compare(curarg, "-CFG", true) == 0) ||
|
|
(string.Compare(curarg, "-CONFIG", true) == 0))
|
|
{
|
|
// Store next arg as config filename information
|
|
autoloadconfig = argslist.Dequeue();
|
|
}
|
|
// Every other arg
|
|
else
|
|
{
|
|
// No command to load file yet?
|
|
if(autoloadfile == null)
|
|
{
|
|
// Check if this is a file we can load
|
|
if(File.Exists(curarg))
|
|
{
|
|
// Load this file!
|
|
autoloadfile = curarg.Trim();
|
|
}
|
|
else
|
|
{
|
|
// Note in the log that we cannot find this file
|
|
General.WriteLogLine("WARNING: Cannot find the specified file \"" + curarg + "\"");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Terminate
|
|
|
|
// This is for plugins to use
|
|
public static void Exit(bool properexit)
|
|
{
|
|
// Plugin wants to exit nicely?
|
|
if(properexit)
|
|
{
|
|
// Close dialog forms first
|
|
while((Form.ActiveForm != mainwindow) && (Form.ActiveForm != null))
|
|
Form.ActiveForm.Close();
|
|
|
|
// Close main window
|
|
mainwindow.Close();
|
|
}
|
|
else
|
|
{
|
|
// Terminate, no questions asked
|
|
Terminate(true);
|
|
}
|
|
}
|
|
|
|
// This terminates the program
|
|
internal static void Terminate(bool properexit)
|
|
{
|
|
// Terminate properly?
|
|
if(properexit)
|
|
{
|
|
General.WriteLogLine("Termination requested");
|
|
|
|
// Unbind static methods from actions
|
|
General.Actions.UnbindMethods(typeof(General));
|
|
|
|
// Save colors
|
|
colors.SaveColors(settings.Config);
|
|
|
|
// Save action controls
|
|
actions.SaveSettings();
|
|
|
|
// Save game configuration settings
|
|
foreach(ConfigurationInfo ci in configs) ci.SaveSettings();
|
|
|
|
// Save settings configuration
|
|
General.WriteLogLine("Saving program configuration...");
|
|
settings.Save(Path.Combine(settingspath, SETTINGS_FILE));
|
|
|
|
// Clean up
|
|
if(map != null) map.Dispose(); map = null;
|
|
if(mainwindow != null) mainwindow.Dispose();
|
|
if(actions != null) actions.Dispose();
|
|
if(clock != null) clock.Dispose();
|
|
if(plugins != null) plugins.Dispose();
|
|
if(types != null) types.Dispose();
|
|
try { D3DDevice.Terminate(); } catch(Exception) { }
|
|
|
|
// Application ends here and now
|
|
General.WriteLogLine("Termination done");
|
|
Application.Exit();
|
|
}
|
|
else
|
|
{
|
|
// Just end now
|
|
General.WriteLogLine("Immediate program termination");
|
|
Application.Exit();
|
|
}
|
|
|
|
// Die.
|
|
Process.GetCurrentProcess().Kill();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Management
|
|
|
|
// This cancels a volatile mode, as if the user presses cancel
|
|
public static bool CancelVolatileMode()
|
|
{
|
|
// Volatile mode?
|
|
if((map != null) & (map.Mode != null) && map.Mode.Attributes.Volatile)
|
|
{
|
|
// Cancel
|
|
map.Mode.OnCancel();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Mode is not volatile
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// This disengages a volatile mode, leaving the choice to cancel or accept to the editing mode
|
|
public static bool DisengageVolatileMode()
|
|
{
|
|
// Volatile mode?
|
|
if((map != null) && (map.Mode != null) && map.Mode.Attributes.Volatile)
|
|
{
|
|
// Change back to normal mode
|
|
map.ChangeMode(map.PreviousStableMode.Name);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Mode is not volatile
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// This creates a new map
|
|
[BeginAction("newmap")]
|
|
internal static void NewMap()
|
|
{
|
|
MapOptions newoptions = new MapOptions();
|
|
MapOptionsForm optionswindow;
|
|
|
|
// Cancel volatile mode, if any
|
|
General.DisengageVolatileMode();
|
|
|
|
// Ask the user to save changes (if any)
|
|
if(General.AskSaveMap())
|
|
{
|
|
// Open map options dialog
|
|
optionswindow = new MapOptionsForm(newoptions);
|
|
optionswindow.IsForNewMap = true;
|
|
if(optionswindow.ShowDialog(mainwindow) == DialogResult.OK)
|
|
{
|
|
// Display status
|
|
mainwindow.DisplayStatus("Creating new map...");
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
|
|
// Let the plugins know
|
|
plugins.OnMapNewBegin();
|
|
|
|
// Clear the display
|
|
mainwindow.ClearDisplay();
|
|
|
|
// Trash the current map, if any
|
|
if(map != null) map.Dispose();
|
|
|
|
// Create map manager with given options
|
|
map = new MapManager();
|
|
if(map.InitializeNewMap(newoptions))
|
|
{
|
|
// Done
|
|
}
|
|
else
|
|
{
|
|
// Unable to create map manager
|
|
map.Dispose();
|
|
map = null;
|
|
|
|
// Show splash logo on display
|
|
mainwindow.ShowSplashDisplay();
|
|
}
|
|
|
|
// Let the plugins know
|
|
plugins.OnMapNewEnd();
|
|
|
|
// All done
|
|
mainwindow.RedrawDisplay();
|
|
mainwindow.UpdateInterface();
|
|
mainwindow.HideInfo();
|
|
mainwindow.DisplayReady();
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
}
|
|
}
|
|
|
|
// This closes the current map
|
|
[BeginAction("closemap")]
|
|
internal static void ActionCloseMap() { CloseMap(); }
|
|
internal static bool CloseMap()
|
|
{
|
|
// Cancel volatile mode, if any
|
|
General.DisengageVolatileMode();
|
|
|
|
// Ask the user to save changes (if any)
|
|
if(General.AskSaveMap())
|
|
{
|
|
// Display status
|
|
mainwindow.DisplayStatus("Closing map...");
|
|
General.WriteLogLine("Unloading map...");
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
|
|
// Trash the current map
|
|
if(map != null) map.Dispose();
|
|
map = null;
|
|
|
|
// Show splash logo on display
|
|
mainwindow.ShowSplashDisplay();
|
|
|
|
// Done
|
|
Cursor.Current = Cursors.Default;
|
|
mainwindow.RedrawDisplay();
|
|
mainwindow.HideInfo();
|
|
mainwindow.UpdateInterface();
|
|
mainwindow.DisplayReady();
|
|
General.WriteLogLine("Map unload done");
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// User cancelled
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// This loads a map from file
|
|
[BeginAction("openmap")]
|
|
internal static void OpenMap()
|
|
{
|
|
OpenFileDialog openfile;
|
|
|
|
// Cancel volatile mode, if any
|
|
General.DisengageVolatileMode();
|
|
|
|
// Open map file dialog
|
|
openfile = new OpenFileDialog();
|
|
openfile.Filter = "Doom WAD Files (*.wad)|*.wad";
|
|
openfile.Title = "Open Map";
|
|
openfile.AddExtension = false;
|
|
openfile.CheckFileExists = true;
|
|
openfile.Multiselect = false;
|
|
openfile.ValidateNames = true;
|
|
if(openfile.ShowDialog(mainwindow) == DialogResult.OK)
|
|
{
|
|
// Update main window
|
|
mainwindow.Update();
|
|
|
|
// Open map file
|
|
OpenMapFile(openfile.FileName);
|
|
}
|
|
}
|
|
|
|
// This opens the specified file
|
|
internal static void OpenMapFile(string filename)
|
|
{
|
|
OpenMapOptionsForm openmapwindow;
|
|
|
|
// Cancel volatile mode, if any
|
|
General.DisengageVolatileMode();
|
|
|
|
// Ask the user to save changes (if any)
|
|
if(General.AskSaveMap())
|
|
{
|
|
// Open map options dialog
|
|
openmapwindow = new OpenMapOptionsForm(filename);
|
|
if(openmapwindow.ShowDialog(mainwindow) == DialogResult.OK)
|
|
OpenMapFileWithOptions(filename, openmapwindow.Options);
|
|
}
|
|
}
|
|
|
|
// This opens the specified file without dialog
|
|
internal static void OpenMapFileWithOptions(string filename, MapOptions options)
|
|
{
|
|
// Display status
|
|
mainwindow.DisplayStatus("Opening map file...");
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
|
|
// Let the plugins know
|
|
plugins.OnMapOpenBegin();
|
|
|
|
// Clear the display
|
|
mainwindow.ClearDisplay();
|
|
|
|
// Trash the current map, if any
|
|
if(map != null) map.Dispose();
|
|
|
|
// Create map manager with given options
|
|
map = new MapManager();
|
|
if(map.InitializeOpenMap(filename, options))
|
|
{
|
|
// Add recent file
|
|
mainwindow.AddRecentFile(filename);
|
|
}
|
|
else
|
|
{
|
|
// Unable to create map manager
|
|
map.Dispose();
|
|
map = null;
|
|
|
|
// Show splash logo on display
|
|
mainwindow.ShowSplashDisplay();
|
|
}
|
|
|
|
// Let the plugins know
|
|
plugins.OnMapOpenEnd();
|
|
|
|
// All done
|
|
mainwindow.RedrawDisplay();
|
|
mainwindow.UpdateInterface();
|
|
mainwindow.HideInfo();
|
|
mainwindow.DisplayReady();
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
|
|
// This saves the current map
|
|
// Returns tre when saved, false when cancelled or failed
|
|
[BeginAction("savemap")]
|
|
internal static void ActionSaveMap() { SaveMap(); }
|
|
internal static bool SaveMap()
|
|
{
|
|
bool result = false;
|
|
|
|
// Cancel volatile mode, if any
|
|
General.DisengageVolatileMode();
|
|
|
|
// Check if a wad file is known
|
|
if(map.FilePathName == "")
|
|
{
|
|
// Call to SaveMapAs
|
|
result = SaveMapAs();
|
|
}
|
|
else
|
|
{
|
|
// Display status
|
|
mainwindow.DisplayStatus("Saving map file...");
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
|
|
// Save the map
|
|
if(map.SaveMap(map.FilePathName, MapManager.SAVE_NORMAL))
|
|
{
|
|
// Add recent file
|
|
mainwindow.AddRecentFile(map.FilePathName);
|
|
result = true;
|
|
}
|
|
|
|
// All done
|
|
mainwindow.UpdateInterface();
|
|
mainwindow.DisplayReady();
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
// This saves the current map as a different file
|
|
// Returns tre when saved, false when cancelled or failed
|
|
[BeginAction("savemapas")]
|
|
internal static void ActionSaveMapAs() { SaveMapAs(); }
|
|
internal static bool SaveMapAs()
|
|
{
|
|
SaveFileDialog savefile;
|
|
bool result = false;
|
|
|
|
// Cancel volatile mode, if any
|
|
General.DisengageVolatileMode();
|
|
|
|
// Show save as dialog
|
|
savefile = new SaveFileDialog();
|
|
savefile.Filter = "Doom WAD Files (*.wad)|*.wad";
|
|
savefile.Title = "Save Map As";
|
|
savefile.AddExtension = true;
|
|
savefile.CheckPathExists = true;
|
|
savefile.OverwritePrompt = true;
|
|
savefile.ValidateNames = true;
|
|
if(savefile.ShowDialog(mainwindow) == DialogResult.OK)
|
|
{
|
|
// Display status
|
|
mainwindow.DisplayStatus("Saving map file...");
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
|
|
// Save the map
|
|
if(map.SaveMap(savefile.FileName, MapManager.SAVE_AS))
|
|
{
|
|
// Add recent file
|
|
mainwindow.AddRecentFile(map.FilePathName);
|
|
result = true;
|
|
}
|
|
|
|
// All done
|
|
mainwindow.UpdateInterface();
|
|
mainwindow.DisplayReady();
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
// This saves the current map as a different file
|
|
// Returns tre when saved, false when cancelled or failed
|
|
[BeginAction("savemapinto")]
|
|
internal static void ActionSaveMapInto() { SaveMapInto(); }
|
|
internal static bool SaveMapInto()
|
|
{
|
|
SaveFileDialog savefile;
|
|
bool result = false;
|
|
|
|
// Cancel volatile mode, if any
|
|
General.DisengageVolatileMode();
|
|
|
|
// Show save as dialog
|
|
savefile = new SaveFileDialog();
|
|
savefile.Filter = "Doom WAD Files (*.wad)|*.wad";
|
|
savefile.Title = "Save Map Into";
|
|
savefile.AddExtension = true;
|
|
savefile.CheckPathExists = true;
|
|
savefile.OverwritePrompt = false;
|
|
savefile.ValidateNames = true;
|
|
if(savefile.ShowDialog(mainwindow) == DialogResult.OK)
|
|
{
|
|
// Display status
|
|
mainwindow.DisplayStatus("Saving map file...");
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
|
|
// Save the map
|
|
if(map.SaveMap(savefile.FileName, MapManager.SAVE_INTO))
|
|
{
|
|
// Add recent file
|
|
mainwindow.AddRecentFile(map.FilePathName);
|
|
result = true;
|
|
}
|
|
|
|
// All done
|
|
mainwindow.UpdateInterface();
|
|
mainwindow.DisplayReady();
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// This asks to save the map if needed
|
|
// Returns false when action was cancelled
|
|
internal static bool AskSaveMap()
|
|
{
|
|
DialogResult result;
|
|
|
|
// Map open and not saved?
|
|
if((map != null) && map.IsChanged)
|
|
{
|
|
// Ask to save changes
|
|
result = MessageBox.Show(mainwindow, "Do you want to save changes to " + map.FileTitle + " (" + map.Options.CurrentName + ")?", Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
|
|
if(result == DialogResult.Yes)
|
|
{
|
|
// Save map and return true on success
|
|
return SaveMap();
|
|
}
|
|
else if(result == DialogResult.Cancel)
|
|
{
|
|
// Abort
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Continue
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Debug
|
|
|
|
// This shows a major failure
|
|
public static void Fail(string message, string detailedmessage)
|
|
{
|
|
Debug.Fail(message, detailedmessage);
|
|
}
|
|
|
|
// This outputs log information
|
|
public static void WriteLogLine(string line)
|
|
{
|
|
// Output to console
|
|
Console.WriteLine(line);
|
|
|
|
// Write to log file
|
|
try { File.AppendAllText(logfile, line + Environment.NewLine); }
|
|
catch(Exception) { }
|
|
}
|
|
|
|
// This outputs log information
|
|
public static void WriteLog(string text)
|
|
{
|
|
// Output to console
|
|
Console.Write(text);
|
|
|
|
// Write to log file
|
|
try { File.AppendAllText(logfile, text); }
|
|
catch(Exception) { }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Tools
|
|
|
|
// This returns an element from a collection by index
|
|
public static T GetByIndex<T>(ICollection<T> collection, int index)
|
|
{
|
|
IEnumerator<T> e = collection.GetEnumerator();
|
|
for(int i = -1; i < index; i++) e.MoveNext();
|
|
return e.Current;
|
|
}
|
|
|
|
// This returns the next power of 2
|
|
public static int NextPowerOf2(int v)
|
|
{
|
|
int p = 0;
|
|
|
|
// Continue increasing until higher than v
|
|
while(Math.Pow(2, p) < v) p++;
|
|
|
|
// Return power
|
|
return (int)Math.Pow(2, p);
|
|
}
|
|
|
|
// Convert bool to integer
|
|
internal static int Bool2Int(bool v)
|
|
{
|
|
if(v) return 1; else return 0;
|
|
}
|
|
|
|
// Convert integer to bool
|
|
internal static bool Int2Bool(int v)
|
|
{
|
|
return (v != 0);
|
|
}
|
|
|
|
// This shows a message and logs the message
|
|
public static DialogResult ShowErrorMessage(string message, MessageBoxButtons buttons)
|
|
{
|
|
Cursor oldcursor;
|
|
DialogResult result;
|
|
|
|
// Log the message
|
|
WriteLogLine(message);
|
|
|
|
// Use normal cursor
|
|
oldcursor = Cursor.Current;
|
|
Cursor.Current = Cursors.Default;
|
|
|
|
// Show message
|
|
result = MessageBox.Show(Form.ActiveForm, message, Application.ProductName, buttons, MessageBoxIcon.Error);
|
|
|
|
// Restore old cursor
|
|
Cursor.Current = oldcursor;
|
|
|
|
// Return result
|
|
return result;
|
|
}
|
|
|
|
// This shows a message and logs the message
|
|
public static DialogResult ShowWarningMessage(string message, MessageBoxButtons buttons)
|
|
{
|
|
return ShowWarningMessage(message, buttons, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
// This shows a message and logs the message
|
|
public static DialogResult ShowWarningMessage(string message, MessageBoxButtons buttons, MessageBoxDefaultButton defaultbutton)
|
|
{
|
|
Cursor oldcursor;
|
|
DialogResult result;
|
|
|
|
// Log the message
|
|
WriteLogLine(message);
|
|
|
|
// Use normal cursor
|
|
oldcursor = Cursor.Current;
|
|
Cursor.Current = Cursors.Default;
|
|
|
|
// Show message
|
|
result = MessageBox.Show(Form.ActiveForm, message, Application.ProductName, buttons, MessageBoxIcon.Warning, defaultbutton);
|
|
|
|
// Restore old cursor
|
|
Cursor.Current = oldcursor;
|
|
|
|
// Return result
|
|
return result;
|
|
}
|
|
|
|
// This returns a unique temp filename
|
|
internal static string MakeTempFilename(string tempdir)
|
|
{
|
|
return MakeTempFilename(tempdir, "tmp");
|
|
}
|
|
|
|
// This returns a unique temp filename
|
|
internal static string MakeTempFilename(string tempdir, string extension)
|
|
{
|
|
string filename;
|
|
string chars = "abcdefghijklmnopqrstuvwxyz1234567890";
|
|
Random rnd = new Random();
|
|
int i;
|
|
|
|
do
|
|
{
|
|
// Generate a filename
|
|
filename = "";
|
|
for(i = 0; i < 8; i++) filename += chars[rnd.Next(chars.Length)];
|
|
filename = Path.Combine(tempdir, filename + "." + extension);
|
|
}
|
|
// Continue while file is not unique
|
|
while(File.Exists(filename) || Directory.Exists(filename));
|
|
|
|
// Return the filename
|
|
return filename;
|
|
}
|
|
|
|
// This returns a unique temp directory name
|
|
internal static string MakeTempDirname()
|
|
{
|
|
string dirname;
|
|
string chars = "abcdefghijklmnopqrstuvwxyz1234567890";
|
|
Random rnd = new Random();
|
|
int i;
|
|
|
|
do
|
|
{
|
|
// Generate a filename
|
|
dirname = "";
|
|
for(i = 0; i < 8; i++) dirname += chars[rnd.Next(chars.Length)];
|
|
dirname = Path.Combine(temppath, dirname);
|
|
}
|
|
// Continue while file is not unique
|
|
while(File.Exists(dirname) || Directory.Exists(dirname));
|
|
|
|
// Return the filename
|
|
return dirname;
|
|
}
|
|
|
|
// This shows an image in a panel either zoomed or centered depending on size
|
|
public static void DisplayZoomedImage(Panel panel, Image image)
|
|
{
|
|
// Set the image
|
|
panel.BackgroundImage = image;
|
|
|
|
// Image not null?
|
|
if(image != null)
|
|
{
|
|
// Small enough to fit in panel?
|
|
if((image.Size.Width < panel.ClientRectangle.Width) &&
|
|
(image.Size.Height < panel.ClientRectangle.Height))
|
|
{
|
|
// Display centered
|
|
panel.BackgroundImageLayout = ImageLayout.Center;
|
|
}
|
|
else
|
|
{
|
|
// Display zoomed
|
|
panel.BackgroundImageLayout = ImageLayout.Zoom;
|
|
}
|
|
}
|
|
}
|
|
|
|
// This calculates the new rectangle when one is scaled into another keeping aspect ratio
|
|
public static RectangleF MakeZoomedRect(Size source, RectangleF target)
|
|
{
|
|
return MakeZoomedRect(new SizeF((int)source.Width, (int)source.Height), target);
|
|
}
|
|
|
|
// This calculates the new rectangle when one is scaled into another keeping aspect ratio
|
|
public static RectangleF MakeZoomedRect(Size source, Rectangle target)
|
|
{
|
|
return MakeZoomedRect(new SizeF((int)source.Width, (int)source.Height),
|
|
new RectangleF((int)target.Left, (int)target.Top, (int)target.Width, (int)target.Height));
|
|
}
|
|
|
|
// This calculates the new rectangle when one is scaled into another keeping aspect ratio
|
|
public static RectangleF MakeZoomedRect(SizeF source, RectangleF target)
|
|
{
|
|
float scale;
|
|
|
|
// Image fits?
|
|
if((source.Width <= target.Width) &&
|
|
(source.Height <= target.Height))
|
|
{
|
|
// Just center
|
|
scale = 1.0f;
|
|
}
|
|
// Image is wider than tall?
|
|
else if((source.Width - target.Width) > (source.Height - target.Height))
|
|
{
|
|
// Scale down by width
|
|
scale = target.Width / source.Width;
|
|
}
|
|
else
|
|
{
|
|
// Scale down by height
|
|
scale = target.Height / source.Height;
|
|
}
|
|
|
|
// Return centered and scaled
|
|
return new RectangleF(target.Left + (target.Width - source.Width * scale) * 0.5f,
|
|
target.Top + (target.Height - source.Height * scale) * 0.5f,
|
|
source.Width * scale, source.Height * scale);
|
|
}
|
|
|
|
#endregion
|
|
|
|
[BeginAction("testaction")]
|
|
internal static void TestAction()
|
|
{
|
|
ScriptEditTestForm t = new ScriptEditTestForm();
|
|
t.ShowDialog(mainwindow);
|
|
t.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|