summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorJosip Medved <jmedved@jmedved.com>2014-04-24 00:24:55 -0700
committerJosip Medved <jmedved@jmedved.com>2014-04-24 00:24:55 -0700
commitbb5a13f79ff83df93b47188debdf44c7df56e025 (patch)
treef5aac7930597a2bd50b5f541c6a132637c196212 /Source
downloadWrtSettings-bb5a13f79ff83df93b47188debdf44c7df56e025.zip
WrtSettings-bb5a13f79ff83df93b47188debdf44c7df56e025.tar.gz
WrtSettings-bb5a13f79ff83df93b47188debdf44c7df56e025.tar.bz2
Initial commit.
Diffstat (limited to 'Source')
-rw-r--r--Source/WrtSettings.sln22
-rw-r--r--Source/WrtSettings/(Medo)/AboutBox [013].cs509
-rw-r--r--Source/WrtSettings/(Medo)/EntryAssembly [003].cs132
-rw-r--r--Source/WrtSettings/(Medo)/ErrorReport [019].cs919
-rw-r--r--Source/WrtSettings/(Medo)/MessageBox [007].cs759
-rw-r--r--Source/WrtSettings/(Medo)/RecentFiles [005].cs345
-rw-r--r--Source/WrtSettings/(Medo)/State [015].cs537
-rw-r--r--Source/WrtSettings/(Medo)/UnhandledCatch [008].cs136
-rw-r--r--Source/WrtSettings/(Medo)/Upgrade [003].cs461
-rw-r--r--Source/WrtSettings/App.cs39
-rw-r--r--Source/WrtSettings/MainForm.Designer.cs252
-rw-r--r--Source/WrtSettings/MainForm.cs436
-rw-r--r--Source/WrtSettings/MainForm.resx1315
-rw-r--r--Source/WrtSettings/Nvram.cs462
-rw-r--r--Source/WrtSettings/Properties/App.icobin0 -> 67664 bytes
-rw-r--r--Source/WrtSettings/Properties/App.manifest33
-rw-r--r--Source/WrtSettings/Properties/App.snkbin0 -> 596 bytes
-rw-r--r--Source/WrtSettings/Properties/AssemblyInfo.cs12
-rw-r--r--Source/WrtSettings/Properties/SolutionInfo.cs8
-rw-r--r--Source/WrtSettings/WrtSettings.csproj98
20 files changed, 6475 insertions, 0 deletions
diff --git a/Source/WrtSettings.sln b/Source/WrtSettings.sln
new file mode 100644
index 0000000..b7f060a
--- /dev/null
+++ b/Source/WrtSettings.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.30110.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WrtSettings", "WrtSettings\WrtSettings.csproj", "{7D43DB74-7DE3-4EE4-8591-D520DD1AB0BF}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {7D43DB74-7DE3-4EE4-8591-D520DD1AB0BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7D43DB74-7DE3-4EE4-8591-D520DD1AB0BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7D43DB74-7DE3-4EE4-8591-D520DD1AB0BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7D43DB74-7DE3-4EE4-8591-D520DD1AB0BF}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Source/WrtSettings/(Medo)/AboutBox [013].cs b/Source/WrtSettings/(Medo)/AboutBox [013].cs
new file mode 100644
index 0000000..f99af24
--- /dev/null
+++ b/Source/WrtSettings/(Medo)/AboutBox [013].cs
@@ -0,0 +1,509 @@
+//Copyright (c) 2008 Josip Medved <jmedved@jmedved.com>
+
+//2008-01-02: New version.
+//2008-01-05: Top line now contains product name.
+//2008-01-22: Changed caption to "About" instead of "About...".
+//2008-01-25: Added product title parameter.
+//2008-04-11: Cleaned code to match FxCop 1.36 beta 2 (NormalizeStringsToUppercase, SpecifyMarshalingForPInvokeStringArguments).
+//2008-11-05: Refactoring (Microsoft.Maintainability : 'AboutBox.ShowDialog(IWin32Window, Uri, string)' has a cyclomatic complexity of 27, Microsoft.Maintainability : 'AboutBox.ShowDialog(IWin32Window, Uri, string)' is coupled with 38 different types from 10 different namespaces.).
+//2008-12-20: Adjusted for high DPI mode.
+//2009-10-25: Adjusted disposing of buttons.
+//2010-11-03: Informational version is used for program name.
+// Content background is now in Window system color.
+//2011-09-01: Added DEBUG sufix for DEBUG builds.
+//2012-03-05: Added padding to buttons.
+//2012-11-24: Suppressing bogus CA5122 warning (http://connect.microsoft.com/VisualStudio/feedback/details/729254/bogus-ca5122-warning-about-p-invoke-declarations-should-not-be-safe-critical).
+
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Windows.Forms;
+using System.Windows.Forms.VisualStyles;
+
+namespace Medo.Windows.Forms {
+
+ /// <summary>
+ /// Simple about form.
+ /// </summary>
+ public static class AboutBox {
+
+ private static readonly object _syncRoot = new object();
+ private static int _titleHeight;
+ private static PaintItem _paintImage;
+ private static PaintItem _paintProduct;
+ private static List<PaintItem> _infoLines;
+
+
+ /// <summary>
+ /// Shows modal dialog.
+ /// </summary>
+ public static DialogResult ShowDialog() {
+ lock (_syncRoot) {
+ return ShowDialog(null, null, null);
+ }
+ }
+
+ /// <summary>
+ /// Shows modal dialog.
+ /// </summary>
+ /// <param name="owner">Any object that implements System.Windows.Forms.IWin32Window that represents the top-level window that will own the modal dialog box.</param>
+ public static DialogResult ShowDialog(IWin32Window owner) {
+ lock (_syncRoot) {
+ return ShowDialog(owner, null, null);
+ }
+ }
+
+ /// <summary>
+ /// Shows modal dialog.
+ /// </summary>
+ /// <param name="owner">Any object that implements System.Windows.Forms.IWin32Window that represents the top-level window that will own the modal dialog box.</param>
+ /// <param name="webpage">URI of program's web page.</param>
+ public static DialogResult ShowDialog(IWin32Window owner, Uri webpage) {
+ return ShowDialog(owner, webpage, null);
+ }
+
+ /// <summary>
+ /// Shows modal dialog.
+ /// </summary>
+ /// <param name="owner">Any object that implements System.Windows.Forms.IWin32Window that represents the top-level window that will own the modal dialog box.</param>
+ /// <param name="webpage">URI of program's web page.</param>
+ /// <param name="productText">Title to use. If null, title will be provided from assembly info.</param>
+ public static DialogResult ShowDialog(IWin32Window owner, Uri webpage, string productText) {
+ lock (_syncRoot) {
+ Assembly assembly = System.Reflection.Assembly.GetEntryAssembly();
+ AssemblyName assemblyName = System.Reflection.Assembly.GetEntryAssembly().GetName();
+
+ if (productText == null) { productText = GetAppProductText(assembly); }
+ string versionText = GetAppTitleText(assembly) + " " + assemblyName.Version.ToString();
+#if DEBUG
+ versionText += " DEBUG";
+#endif
+ string copyrightText = GetAppCopyright(assembly);
+ string applicationPath = Assembly.GetEntryAssembly().Location;
+
+ ShowForm(owner, webpage, productText, versionText, copyrightText, applicationPath);
+
+ return DialogResult.OK;
+ }
+ }
+
+ private static void ShowForm(IWin32Window owner, Uri webpage, string productText, string applicationText, string copyrightText, string applicationPath) {
+ Font productFont = null;
+
+ PaintItem fullName = null;
+ PaintItem dotNetFramework = null;
+ PaintItem osVersion = null;
+ PaintItem copyright = null;
+
+ Button buttonReadme = null;
+ Button buttonClose = null;
+ Button buttonWebPage = null;
+
+ using (Form form = new Form()) {
+ try {
+ form.FormBorderStyle = FormBorderStyle.FixedDialog;
+ form.ShowIcon = false;
+ form.ShowInTaskbar = false;
+ form.MinimizeBox = false;
+ form.MaximizeBox = false;
+ form.AutoSize = false;
+ form.AutoScaleMode = AutoScaleMode.None;
+ form.Text = Resources.Caption;
+
+
+ int imageHeight = 32;
+ int maxRight = 320;
+ int maxBottom = 80;
+ using (Graphics graphics = form.CreateGraphics()) {
+ //icon
+ Bitmap bitmap = GetAppIcon(applicationPath);
+ if (bitmap != null) {
+ _paintImage = new PaintItem(bitmap, new Point(7, 7));
+ }
+
+ //title
+ int imageRight = 7;
+ if (_paintImage != null) {
+ imageRight = _paintImage.Rectangle.Right + 7;
+ }
+ if (_paintImage != null) {
+ imageHeight = _paintImage.Rectangle.Height;
+ }
+ productFont = new Font(SystemFonts.MessageBoxFont.Name, imageHeight, System.Drawing.SystemFonts.MessageBoxFont.Style, System.Drawing.GraphicsUnit.Pixel, System.Drawing.SystemFonts.MessageBoxFont.GdiCharSet);
+ _paintProduct = new PaintItem(productText, productFont, imageRight, 7, imageHeight, VerticalAlignment.Center, graphics);
+
+ _titleHeight = 7 + imageHeight + 7;
+ maxRight = System.Math.Max(maxRight, _paintProduct.Rectangle.Right);
+ maxBottom = System.Math.Max(maxBottom, _titleHeight);
+
+
+ //other stuff
+ _infoLines = new List<PaintItem>();
+
+ fullName = new PaintItem(applicationText, SystemFonts.MessageBoxFont, 7, _titleHeight + 2 + 7, 0, VerticalAlignment.Top, graphics);
+ maxRight = System.Math.Max(maxRight, fullName.Rectangle.Right);
+ maxBottom = System.Math.Max(maxBottom, fullName.Rectangle.Bottom);
+ _infoLines.Add(fullName);
+
+ dotNetFramework = new PaintItem(".NET framework " + Environment.Version.ToString(), SystemFonts.MessageBoxFont, 7, fullName.Rectangle.Bottom, 0, VerticalAlignment.Top, graphics);
+ maxRight = System.Math.Max(maxRight, dotNetFramework.Rectangle.Right);
+ maxBottom = System.Math.Max(maxBottom, dotNetFramework.Rectangle.Bottom);
+ _infoLines.Add(dotNetFramework);
+
+ osVersion = new PaintItem(System.Environment.OSVersion.VersionString, SystemFonts.MessageBoxFont, 7, dotNetFramework.Rectangle.Bottom, 0, VerticalAlignment.Top, graphics);
+ maxRight = System.Math.Max(maxRight, osVersion.Rectangle.Right);
+ maxBottom = System.Math.Max(maxBottom, osVersion.Rectangle.Bottom);
+ _infoLines.Add(osVersion);
+
+ if (copyrightText != null) {
+ copyright = new PaintItem(copyrightText, SystemFonts.MessageBoxFont, 7, osVersion.Rectangle.Bottom + 7, 0, VerticalAlignment.Top, graphics);
+ maxRight = System.Math.Max(maxRight, copyright.Rectangle.Right);
+ maxBottom = System.Math.Max(maxBottom, copyright.Rectangle.Bottom);
+ _infoLines.Add(copyright);
+ }
+ }
+
+ int buttonMinRight = 7;
+
+ //Close button
+ buttonClose = new Button() { Padding = new Padding(3, 1, 3, 1) };
+ buttonClose.Anchor = AnchorStyles.Right | AnchorStyles.Bottom;
+ buttonClose.AutoSize = true;
+ buttonClose.DialogResult = DialogResult.OK;
+ buttonClose.Text = Resources.Close;
+ form.Controls.Add(buttonClose);
+ buttonMinRight += buttonClose.Width + 11;
+
+ //Readme button
+ string readMePath = System.IO.Path.Combine(System.Windows.Forms.Application.StartupPath, "readme.txt");
+ if (System.IO.File.Exists(readMePath)) {
+ buttonReadme = new Button() { Padding = new Padding(3, 1, 3, 1) };
+ buttonReadme.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;
+ buttonReadme.AutoSize = true;
+ buttonReadme.Text = Resources.ReadMe;
+ buttonReadme.Tag = readMePath;
+ buttonReadme.Click += new System.EventHandler(buttonReadme_Click);
+ form.Controls.Add(buttonReadme);
+ buttonMinRight += buttonReadme.Width + 7;
+ }
+
+ //WebPage button
+ if (webpage != null) {
+ buttonWebPage = new Button() { Padding = new Padding(3, 1, 3, 1) };
+ buttonWebPage.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;
+ buttonWebPage.AutoSize = true;
+ buttonWebPage.Text = Resources.WebPage;
+ buttonWebPage.Tag = webpage.ToString();
+ buttonWebPage.Click += new System.EventHandler(buttonWebPage_Click);
+ form.Controls.Add(buttonWebPage);
+ buttonMinRight += buttonWebPage.Width + 7;
+ }
+
+ maxRight = System.Math.Max(maxRight, buttonMinRight);
+
+
+ int borderX = (form.Width - form.ClientRectangle.Width);
+ int borderY = (form.Height - form.ClientRectangle.Height);
+ form.Width = borderX + maxRight + 7;
+ form.Height = borderY + maxBottom + 11 + 11 + buttonClose.Size.Height + 7;
+ if (owner == null) {
+ form.StartPosition = FormStartPosition.CenterScreen;
+ } else {
+ form.StartPosition = FormStartPosition.CenterParent;
+ }
+
+
+ int buttonLeft = form.ClientRectangle.Left + 7;
+
+ if (buttonReadme != null) {
+ buttonReadme.Location = new Point(buttonLeft, form.ClientRectangle.Bottom - buttonClose.Height - 7);
+ buttonLeft += buttonReadme.Width + 7;
+ }
+
+ if (buttonWebPage != null) {
+ buttonWebPage.Location = new Point(buttonLeft, form.ClientRectangle.Bottom - buttonClose.Height - 7);
+ buttonLeft += buttonWebPage.Width + 7;
+ }
+
+ buttonClose.Location = new Point(form.ClientRectangle.Right - buttonClose.Width - 7, form.ClientRectangle.Bottom - buttonClose.Height - 7);
+
+
+
+ form.AcceptButton = buttonClose;
+ form.CancelButton = buttonClose;
+
+ form.Paint += Form_Paint;
+
+ if (owner != null) {
+ Form formOwner = owner as Form;
+ if ((formOwner != null) && (formOwner.TopMost == true)) {
+ form.TopMost = false;
+ form.TopMost = true;
+ }
+ form.ShowDialog(owner);
+ } else {
+ form.ShowDialog();
+ }
+ } finally {
+ if (buttonClose != null) { buttonClose.Dispose(); }
+ if (buttonReadme != null) { buttonReadme.Dispose(); }
+ if (buttonWebPage != null) { buttonWebPage.Dispose(); }
+
+ if (fullName != null) { fullName.Dispose(); }
+ if (dotNetFramework != null) { dotNetFramework.Dispose(); }
+ if (osVersion != null) { osVersion.Dispose(); }
+ if (copyright != null) { copyright.Dispose(); }
+
+ if (_paintImage != null) { _paintImage.Dispose(); }
+ if (_paintProduct != null) { _paintProduct.Dispose(); }
+
+ if (productFont != null) { productFont.Dispose(); }
+ }
+ }
+ }
+
+ private static string GetAppCopyright(Assembly assembly) {
+ object[] copyrightAttributes = assembly.GetCustomAttributes(typeof(AssemblyCopyrightAttribute), true);
+ if ((copyrightAttributes != null) && (copyrightAttributes.Length >= 1)) {
+ return ((AssemblyCopyrightAttribute)copyrightAttributes[copyrightAttributes.Length - 1]).Copyright;
+ }
+ return null;
+ }
+
+ private static string GetAppProductText(Assembly assembly) {
+ string product;
+ object[] productAttributes = assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), true);
+ if ((productAttributes != null) && (productAttributes.Length >= 1)) {
+ product = ((AssemblyProductAttribute)productAttributes[productAttributes.Length - 1]).Product;
+ } else {
+ product = GetAppTitleText(assembly);
+ }
+
+ object[] infoVersionAttributes = assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), true);
+ if ((infoVersionAttributes != null) && (infoVersionAttributes.Length >= 1)) {
+ return product + " " + ((AssemblyInformationalVersionAttribute)infoVersionAttributes[infoVersionAttributes.Length - 1]).InformationalVersion;
+ } else {
+ return product;
+ }
+ }
+
+ private static string GetAppTitleText(Assembly assembly) {
+ object[] titleAttributes = assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), true);
+ if ((titleAttributes != null) && (titleAttributes.Length >= 1)) {
+ return ((AssemblyTitleAttribute)titleAttributes[titleAttributes.Length - 1]).Title;
+ } else {
+ return assembly.GetName().Name;
+ }
+ }
+
+
+ static void buttonWebPage_Click(object sender, System.EventArgs e) {
+ try {
+ string url = (string)((Control)sender).Tag;
+ System.Diagnostics.Process.Start(url);
+ } catch (System.ComponentModel.Win32Exception) { }
+ }
+
+ static void buttonReadme_Click(object sender, System.EventArgs e) {
+ try {
+ string path = (string)((Control)sender).Tag;
+ System.Diagnostics.Process.Start(path);
+ } catch (System.ComponentModel.Win32Exception) { }
+ }
+
+
+ private static void Form_Paint(object sender, System.Windows.Forms.PaintEventArgs e) {
+ lock (_syncRoot) {
+ if (_infoLines != null) {
+ e.Graphics.FillRectangle(SystemBrushes.Window, e.ClipRectangle.Left, e.ClipRectangle.Top, e.ClipRectangle.Width, _infoLines[_infoLines.Count - 1].Rectangle.Bottom + 11);
+ } else {
+ e.Graphics.FillRectangle(SystemBrushes.Window, e.ClipRectangle.Left, e.ClipRectangle.Top, e.ClipRectangle.Width, _paintProduct.Rectangle.Bottom + 11);
+ }
+
+ if (_paintImage != null) { _paintImage.Paint(e.Graphics); }
+ if (_paintProduct != null) { _paintProduct.Paint(e.Graphics); }
+ if (_infoLines != null) {
+ for (int i = 0; i < _infoLines.Count; ++i) {
+ _infoLines[i].Paint(e.Graphics);
+ }
+ }
+ }
+
+ }
+
+ private static Bitmap GetAppIcon(string fileName) {
+ if (!AboutBox.IsRunningOnMono) {
+ System.IntPtr hLibrary = NativeMethods.LoadLibrary(fileName);
+ if (!hLibrary.Equals(System.IntPtr.Zero)) {
+ System.IntPtr hIcon = NativeMethods.LoadIcon(hLibrary, "#32512");
+ if (!hIcon.Equals(System.IntPtr.Zero)) {
+ Bitmap bitmap = System.Drawing.Icon.FromHandle(hIcon).ToBitmap();
+ if (bitmap != null) { return bitmap; }
+ }
+ }
+ }
+ return null;
+ }
+
+
+ private class PaintItem : System.IDisposable {
+
+ public PaintItem(Image image, Point location) {
+ this._image = image;
+ this._location = location;
+ this._rectangle = new Rectangle(location, image.Size);
+ }
+
+ public PaintItem(string title, Font font, int x, int y, int height, System.Windows.Forms.VisualStyles.VerticalAlignment align, Graphics measurementGraphics) {
+ this._text = title;
+ this._font = font;
+ Size size = measurementGraphics.MeasureString(title, font, 600).ToSize();
+ switch (align) {
+ case VerticalAlignment.Top:
+ this._location = new Point(x, y);
+ break;
+ case VerticalAlignment.Center:
+ this._location = new Point(x, y + (height - size.Height) / 2);
+ break;
+ case VerticalAlignment.Bottom:
+ this._location = new Point(x, y + height - size.Height);
+ break;
+ }
+ this._rectangle = new Rectangle(this.Location, size);
+ }
+
+
+ private Image _image;
+ public Image Image {
+ get { return this._image; }
+ }
+
+ private string _text;
+ public string Text {
+ get { return this._text; }
+ }
+
+ private Font _font;
+ public Font Font {
+ get { return this._font; }
+ }
+
+ private Point _location;
+ public Point Location {
+ get { return this._location; }
+ }
+
+ private Rectangle _rectangle;
+ public Rectangle Rectangle {
+ get { return this._rectangle; }
+ }
+
+
+ public void Paint(Graphics graphics) {
+ if (this.Image != null) {
+ graphics.DrawImage(this.Image, this.Rectangle);
+ } else if (this.Text != null) {
+ graphics.DrawString(this.Text, this.Font, SystemBrushes.ControlText, this.Location);
+ }
+ }
+
+
+ #region IDisposable Members
+
+ /// <summary>
+ /// Releases used resources.
+ /// </summary>
+ /// <param name="disposing">True to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool disposing) {
+ if (disposing) {
+ if (this.Image != null) {
+ this.Image.Dispose();
+ this._image = null;
+ }
+ if (this.Font != null) {
+ if (!this.Font.IsSystemFont) {
+ this.Font.Dispose();
+ }
+ this._font = null;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Releases used resources.
+ /// </summary>
+ public void Dispose() {
+ Dispose(true);
+ System.GC.SuppressFinalize(this);
+ }
+
+ #endregion
+
+ }
+
+
+ private static class Resources {
+
+ internal static string Close {
+ get { return GetInCurrentLanguage("Close", "Zatvori"); }
+ }
+
+ internal static string ReadMe {
+ get { return GetInCurrentLanguage("Read me", "Pročitaj me"); }
+ }
+
+ internal static string WebPage {
+ get { return GetInCurrentLanguage("Web page", "Web stranica"); }
+ }
+
+ internal static string Caption {
+ get { return GetInCurrentLanguage("About", "O programu"); }
+ }
+
+
+ private static string GetInCurrentLanguage(string en_US, string hr_HR) {
+ switch (System.Threading.Thread.CurrentThread.CurrentUICulture.Name.ToUpperInvariant()) {
+ case "EN":
+ case "EN-US":
+ case "EN-GB":
+ return en_US;
+
+ case "HR":
+ case "HR-HR":
+ case "HR-BA":
+ return hr_HR;
+
+ default:
+ return en_US;
+ }
+ }
+
+ }
+
+
+ private static bool IsRunningOnMono {
+ get {
+ return (Type.GetType("Mono.Runtime") != null);
+ }
+ }
+
+
+ private static class NativeMethods {
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule", Justification = "Warning is bogus.")]
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
+ static extern internal IntPtr LoadIcon(IntPtr hInstance, string lpIconName);
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule", Justification = "Warning is bogus.")]
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
+ static extern internal IntPtr LoadLibrary(string lpFileName);
+
+ }
+
+ }
+
+}
diff --git a/Source/WrtSettings/(Medo)/EntryAssembly [003].cs b/Source/WrtSettings/(Medo)/EntryAssembly [003].cs
new file mode 100644
index 0000000..e157484
--- /dev/null
+++ b/Source/WrtSettings/(Medo)/EntryAssembly [003].cs
@@ -0,0 +1,132 @@
+//Copyright (c) 2007 Josip Medved <jmedved@jmedved.com>
+
+//2007-12-31: New version.
+//2008-05-22: Added Description.
+//2008-06-06: Added Copyright.
+
+
+using System.Reflection;
+
+namespace Medo.Reflection {
+
+ /// <summary>
+ /// Returns various info about assembly that started process.
+ /// </summary>
+ public static class EntryAssembly {
+
+ private readonly static Assembly Assembly = System.Reflection.Assembly.GetEntryAssembly();
+ //private readonly static AssemblyName AssemblyName = Assembly.GetName();
+
+ /// <summary>
+ /// Gets entry assembly's full name.
+ /// </summary>
+ public static string FullName {
+ get { return System.Reflection.Assembly.GetEntryAssembly().GetName().FullName; }
+ }
+
+ /// <summary>
+ /// Gets entry assembly's application name.
+ /// </summary>
+ public static string Name {
+ get { return System.Reflection.Assembly.GetEntryAssembly().GetName().Name; }
+ }
+
+ /// <summary>
+ /// Gets entry assembly's version.
+ /// </summary>
+ public static System.Version Version {
+ get { return System.Reflection.Assembly.GetEntryAssembly().GetName().Version; }
+ }
+
+ /// <summary>
+ /// Returns entry assembly's version in x.xx format.
+ /// </summary>
+ public static string ShortVersionString {
+ get {
+ System.Version version = EntryAssembly.Version;
+ return version.Major.ToString("0", System.Globalization.CultureInfo.InvariantCulture) + "." + version.Minor.ToString("00", System.Globalization.CultureInfo.InvariantCulture);
+ }
+ }
+
+ /// <summary>
+ /// Returns entry assembly's version in x.xx.xxx.xxxx format.
+ /// </summary>
+ public static string LongVersionString {
+ get {
+ System.Version version = EntryAssembly.Version;
+ return version.Major.ToString("0", System.Globalization.CultureInfo.CurrentCulture) + "." + version.Minor.ToString("00", System.Globalization.CultureInfo.CurrentCulture) + "." + version.Build.ToString("000", System.Globalization.CultureInfo.CurrentCulture) + "." + version.Revision.ToString("0000", System.Globalization.CultureInfo.CurrentCulture);
+ }
+ }
+
+ /// <summary>
+ /// Returns entry assembly's company or null if not found.
+ /// </summary>
+ public static string Company {
+ get{
+ object[] companyAttributes = System.Reflection.Assembly.GetEntryAssembly().GetCustomAttributes(typeof(System.Reflection.AssemblyCompanyAttribute), true);
+ if ((companyAttributes != null) && (companyAttributes.Length >= 1)) {
+ return ((System.Reflection.AssemblyCompanyAttribute)companyAttributes[companyAttributes.Length - 1]).Company;
+ }
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Returns entry assembly's title or name if title is not found.
+ /// </summary>
+ public static string Title {
+ get{
+ object[] titleAttributes = System.Reflection.Assembly.GetEntryAssembly().GetCustomAttributes(typeof(System.Reflection.AssemblyTitleAttribute), true);
+ if ((titleAttributes != null) && (titleAttributes.Length >= 1)) {
+ return ((System.Reflection.AssemblyTitleAttribute)titleAttributes[titleAttributes.Length - 1]).Title;
+ } else {
+ return Name;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Retuns entry's assembly product. If product is not found, title is returned.
+ /// </summary>
+ public static string Product {
+ get{
+ object[] productAttributes = System.Reflection.Assembly.GetEntryAssembly().GetCustomAttributes(typeof(System.Reflection.AssemblyProductAttribute), true);
+ if ((productAttributes != null) && (productAttributes.Length >= 1)) {
+ return ((System.Reflection.AssemblyProductAttribute)productAttributes[productAttributes.Length - 1]).Product;
+ } else {
+ return Title;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Retuns entry's assembly description. If description is not found, empty string is returned.
+ /// </summary>
+ public static string Description {
+ get {
+ object[] descriptionAttributes = System.Reflection.Assembly.GetEntryAssembly().GetCustomAttributes(typeof(System.Reflection.AssemblyDescriptionAttribute), true);
+ if ((descriptionAttributes != null) && (descriptionAttributes.Length >= 1)) {
+ return ((System.Reflection.AssemblyDescriptionAttribute)descriptionAttributes[descriptionAttributes.Length - 1]).Description;
+ } else {
+ return string.Empty;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Retuns entry's assembly copyright. If copyright is not found, empty string is returned.
+ /// </summary>
+ public static string Copyright {
+ get {
+ object[] copyrightAttributes = Assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyCopyrightAttribute), true);
+ if ((copyrightAttributes != null) && (copyrightAttributes.Length >= 1)) {
+ return ((System.Reflection.AssemblyCopyrightAttribute)copyrightAttributes[copyrightAttributes.Length - 1]).Copyright;
+ } else {
+ return string.Empty;
+ }
+ }
+ }
+
+ }
+
+}
diff --git a/Source/WrtSettings/(Medo)/ErrorReport [019].cs b/Source/WrtSettings/(Medo)/ErrorReport [019].cs
new file mode 100644
index 0000000..e46d650
--- /dev/null
+++ b/Source/WrtSettings/(Medo)/ErrorReport [019].cs
@@ -0,0 +1,919 @@
+//Copyright (c) 2008 Josip Medved <jmedved@jmedved.com>
+
+//2008-01-07: First version.
+//2008-01-15: Added overloads for SaveToTemp and ShowDialog.
+//2008-01-17: SendToWeb returns false instead of throwing WebException.
+//2008-01-27: Changed Version format.
+//2008-03-01: Added Product and Time.
+//2008-03-30: Added SaveToEventLog.
+// Fixed mixed English and Croatian messages.
+//2008-04-11: Cleaned code to match FxCop 1.36 beta 2 (NormalizeStringsToUppercase, SpecifyStringComparison).
+//2008-12-17: Changed SendToWeb to PostToWeb.
+//2009-03-30: Refactoring.
+// Added user confirmation form in PostToWeb.
+//2009-04-07: Success check is done with status code.
+//2009-06-26: Added Version and removed EntryAssembly from arguments.
+// Obsoleted PostToWeb, ShowDialog took over functionality.
+// Deleted old obsoleted methods.
+//2009-12-09: Changed source to use only preallocated buffers for writing to log file.
+// Log file name is now ErrorReport.{application}.log (does not contain version info).
+// NameValueCollection is no longer used for passing custom parameters. String array is used instead.
+// If sending of message does not succeed whole message is saved in temp as ErrorReport.{application}.{time}.log.
+//2010-02-13: Send button is disabled if there is neither exception nor message.
+// Log file is now ErrorReport "[{application}].log" and "ErrorReport [{application}] {time}.log".
+//2010-02-13: Added TopMost.
+//2010-03-02: Line wrapping at 72nd character.
+//2010-03-07: Changed Math.* to System.Math.*.
+//2010-10-30: Fixed bug with sending error report.
+//2010-11-06: Graphical update.
+//2012-09-16: Added retry upon send failure.
+
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Drawing;
+using System.Net;
+using System.Reflection;
+using System.Text;
+using System.Windows.Forms;
+
+namespace Medo.Diagnostics {
+
+ /// <summary>
+ /// Creating of error reports.
+ /// This class is thread-safe.
+ /// </summary>
+ public static class ErrorReport {
+
+ private static readonly object _syncRoot = new object();
+ private static readonly StringBuilder _logBuffer = new StringBuilder(8000);
+ private static readonly string _logSeparator = Environment.NewLine + new string('-', 72) + Environment.NewLine + Environment.NewLine;
+ private static readonly string _logFileName;
+
+ private static readonly string _infoProductTitle;
+ private static readonly string _infoProductVersion;
+ private static readonly string _infoAssemblyFullName;
+ private static readonly string _infoOsVersion = System.Environment.OSVersion.ToString();
+ private static readonly string _infoFrameworkVersion = ".NET Framework " + System.Environment.Version.ToString();
+ private static readonly string[] _infoReferencedAssemblies;
+
+
+ /// <summary>
+ /// Setting up of initial variable values in order to avoid setting them once problems (e.g. OutOfMemoryException) occur.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "This cannot be done with in-field assignments.")]
+ static ErrorReport() {
+ var assembly = Assembly.GetEntryAssembly();
+
+ object[] productAttributes = assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), true);
+ if ((productAttributes != null) && (productAttributes.Length >= 1)) {
+ ErrorReport._infoProductTitle = ((AssemblyProductAttribute)productAttributes[productAttributes.Length - 1]).Product;
+ } else {
+ object[] titleAttributes = assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), true);
+ if ((titleAttributes != null) && (titleAttributes.Length >= 1)) {
+ ErrorReport._infoProductTitle = ((AssemblyTitleAttribute)titleAttributes[titleAttributes.Length - 1]).Title;
+ } else {
+ ErrorReport._infoProductTitle = assembly.GetName().Name;
+ }
+ }
+ _infoProductVersion = assembly.GetName().Version.ToString();
+ ErrorReport._logFileName = System.IO.Path.Combine(System.IO.Path.GetTempPath(), string.Format(System.Globalization.CultureInfo.InvariantCulture, "ErrorReport [{0}].log", _infoProductTitle));
+
+ _infoAssemblyFullName = assembly.FullName;
+
+ var listReferencedAssemblies = new List<string>();
+ foreach (var iRefAss in assembly.GetReferencedAssemblies()) {
+ listReferencedAssemblies.Add(iRefAss.ToString());
+ }
+ _infoReferencedAssemblies = listReferencedAssemblies.ToArray();
+ }
+
+
+ /// <summary>
+ /// Writes file with exception details in temp directory.
+ /// Returns true if write succeded.
+ /// </summary>
+ /// <param name="exception">Exception which is processed.</param>
+ public static bool SaveToTemp(Exception exception) {
+ lock (_syncRoot) {
+ return SaveToTemp(exception, null);
+ }
+ }
+
+ /// <summary>
+ /// Writes file with exception details in temp directory.
+ /// Returns true if write succeded.
+ /// </summary>
+ /// <param name="exception">Exception which is processed.</param>
+ /// <param name="additionalInformation">Additional information to be added in log.</param>
+ public static bool SaveToTemp(Exception exception, params string[] additionalInformation) {
+ lock (_syncRoot) {
+ LogBufferFillFromException(exception, additionalInformation);
+ LogBufferSaveToLogFile();
+
+ return true;
+ }
+ }
+
+
+ /// <summary>
+ /// Shows dialog and sends error to support web if user chooses so.
+ /// Returns DialogResult.OK if posting error report succeded.
+ /// </summary>
+ /// <param name="owner">Any object that implements System.Windows.Forms.IWin32Window that represents the top-level window that will own the modal dialog box.</param>
+ /// <param name="exception">Exception which is processed. If exception is null, this is considered feature request.</param>
+ /// <param name="address">Address of form which will receive data. Form should expect POST request with fields "EntryAssembly", "Message" and "Details".</param>
+ /// <param name="additionalInformation">Additional information to be added in log.</param>
+ public static DialogResult ShowDialog(IWin32Window owner, Exception exception, Uri address, params string[] additionalInformation) {
+ lock (_syncRoot) {
+ LogBufferFillFromException(exception, additionalInformation);
+
+ if ((exception != null) && !ErrorReport.DisableAutomaticSaveToTemp) {
+ LogBufferSaveToLogFile();
+ }
+
+ try {
+ if (address != null) { //send to web
+
+ if (exception != null) {
+ if (ShowDialogInform(owner, address) == DialogResult.OK) {
+ string message, email, displayName;
+ if (ShowDialogCollect(owner, exception, out message, out email, out displayName) == DialogResult.OK) {
+ string fullMessage = LogBufferGetStringWithUserInformation(message, displayName, email);
+ while (true) {
+ var result = ShowDialogSend(owner, address, fullMessage, email, displayName);
+ if (result != DialogResult.Retry) {
+ return result;
+ }
+ }
+ }
+ }
+ } else {
+ string message, email, displayName;
+ if (ShowDialogCollect(owner, exception, out message, out email, out displayName) == DialogResult.OK) {
+ string fullMessage = LogBufferGetStringWithUserInformation(message, displayName, email);
+ while (true) {
+ var result = ShowDialogSend(owner, address, fullMessage, email, displayName);
+ if (result != DialogResult.Retry) {
+ return result;
+ }
+ }
+ }
+ }
+
+ } else { //don't send to web
+
+ if (exception != null) {
+ if (ShowDialogInform(owner, address) == DialogResult.OK) {
+ return DialogResult.OK;
+ }
+ } else {
+ return DialogResult.Cancel;
+ }
+
+ }
+
+ } catch (WebException ex) {
+ System.Diagnostics.Debug.WriteLine("W: " + ex.Message + ". {{Medo.Diagnostics.ErrorReport}}");
+ }
+
+ return DialogResult.Cancel;
+ }
+ }
+
+ /// <summary>
+ /// Shows dialog and sends error to support web if user chooses so.
+ /// Returns DialogResult.OK if posting error report succeded.
+ /// </summary>
+ /// <param name="owner">Any object that implements System.Windows.Forms.IWin32Window that represents the top-level window that will own the modal dialog box.</param>
+ /// <param name="exception">Exception which is processed.</param>
+ /// <param name="address">Address of form which will receive data. Form should expect POST request with fields "EntryAssembly", "Message" and "Details".</param>
+ public static DialogResult ShowDialog(IWin32Window owner, Exception exception, Uri address) {
+ lock (_syncRoot) {
+ return ShowDialog(owner, exception, address, null);
+ }
+ }
+
+ /// <summary>
+ /// Shows dialog with exception.
+ /// Returns DialogResult.OK if posting error report succeded.
+ /// </summary>
+ /// <param name="owner">Any object that implements System.Windows.Forms.IWin32Window that represents the top-level window that will own the modal dialog box.</param>
+ /// <param name="exception">Exception which is processed.</param>
+ public static DialogResult ShowDialog(IWin32Window owner, Exception exception) {
+ return ShowDialog(owner, exception, null, null);
+ }
+
+
+ /// <summary>
+ /// Writes file with exception details to EventLog.
+ /// Returns true if write succeded.
+ /// </summary>
+ /// <param name="exception">Exception which is processed.</param>
+ /// <param name="eventLog">EventLog to write to.</param>
+ public static bool SaveToEventLog(Exception exception, EventLog eventLog) {
+ lock (_syncRoot) {
+ return SaveToEventLog(exception, eventLog, 0, null);
+ }
+ }
+
+ /// <summary>
+ /// Writes file with exception details to EventLog.
+ /// Returns true if write succeded.
+ /// </summary>
+ /// <param name="exception">Exception which is processed.</param>
+ /// <param name="eventLog">EventLog to write to.</param>
+ /// <param name="eventId">The application-specific identifier for the event.</param>
+ public static bool SaveToEventLog(Exception exception, EventLog eventLog, int eventId) {
+ lock (_syncRoot) {
+ return SaveToEventLog(exception, eventLog, eventId, null);
+ }
+ }
+
+ /// <summary>
+ /// Writes file with exception details to EventLog.
+ /// Returns true if write succeded.
+ /// </summary>
+ /// <param name="exception">Exception which is processed.</param>
+ /// <param name="eventLog">EventLog to write to.</param>
+ /// <param name="eventId">The application-specific identifier for the event.</param>
+ /// <param name="additionalInformation">Additional information to be added in log.</param>
+ public static bool SaveToEventLog(Exception exception, EventLog eventLog, int eventId, params string[] additionalInformation) {
+ lock (_syncRoot) {
+ LogBufferFillFromException(exception, additionalInformation);
+
+ if ((exception != null) && !ErrorReport.DisableAutomaticSaveToTemp) {
+ LogBufferSaveToLogFile();
+ }
+
+ if (eventLog == null) { return false; }
+
+ eventLog.WriteEntry(LogBufferGetString(), EventLogEntryType.Error, eventId);
+ return true;
+ }
+ }
+
+
+ /// <summary>
+ /// Gets/sets whether report is automatically saved to temporary folder for any reporting method.
+ /// Default is to save all reports to temporary folder before any other action.
+ /// </summary>
+ public static bool DisableAutomaticSaveToTemp { get; set; }
+
+ /// <summary>
+ /// Gets/sets whether window will appear top-most.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "TopMost", Justification = "Spelling is same as Form's TopMost property.")]
+ public static bool TopMost { get; set; }
+
+
+ private static DialogResult ShowDialogInform(IWin32Window owner, Uri address) {
+ var ownerForm = owner as Form;
+
+ using (var form = new Form())
+ using (var label = new Label())
+ using (var sendButton = new Button())
+ using (var closeButton = new Button()) {
+ form.AcceptButton = sendButton;
+ form.CancelButton = closeButton;
+ form.ControlBox = true;
+ form.Font = SystemFonts.MessageBoxFont;
+ form.FormBorderStyle = FormBorderStyle.FixedDialog;
+ if (ownerForm != null) {
+ form.Icon = ownerForm.Icon;
+ form.StartPosition = FormStartPosition.CenterParent;
+ } else {
+ form.Icon = null;
+ form.StartPosition = FormStartPosition.CenterScreen;
+ }
+ form.MaximizeBox = false;
+ form.MinimizeBox = false;
+ form.ShowInTaskbar = false;
+ form.Text = _infoProductTitle;
+ form.TopMost = ErrorReport.TopMost;
+
+ int dluW, dluH;
+ using (var graphics = form.CreateGraphics()) {
+ var fewCharSize = graphics.MeasureString(Resources.MeasurementText, form.Font);
+ dluW = (int)System.Math.Ceiling(fewCharSize.Width / Resources.MeasurementText.Length);
+ dluH = (int)System.Math.Ceiling(fewCharSize.Height);
+ }
+ var dluBorder = dluH / 2;
+
+ form.ClientSize = new Size(System.Math.Min(36 * dluW, Screen.GetWorkingArea(form).Width - 4 * dluH), System.Math.Min(6 * dluH, Screen.GetWorkingArea(form).Height - 4 * dluH));
+
+ //sendButton
+ sendButton.AutoEllipsis = true;
+ sendButton.ClientSize = new Size(dluW * 20, (int)(dluH * 1.5));
+ sendButton.DialogResult = DialogResult.OK;
+ sendButton.Location = new Point(dluBorder, form.ClientRectangle.Bottom - sendButton.Height - dluBorder);
+ sendButton.Text = Resources.GetInCurrentLanguage("Report a bug", "Pošalji prijavu greške");
+
+ //closeButton
+ closeButton.AutoEllipsis = true;
+ closeButton.ClientSize = new Size(dluW * 10, (int)(dluH * 1.5));
+ closeButton.DialogResult = DialogResult.Cancel;
+ closeButton.Location = new Point(form.ClientRectangle.Right - closeButton.Width - dluBorder, form.ClientRectangle.Bottom - closeButton.Height - dluBorder);
+ closeButton.Text = Resources.GetInCurrentLanguage("Close", "Zatvori");
+
+ //label
+ label.AutoEllipsis = true;
+ label.AutoSize = false;
+ label.BackColor = SystemColors.Window;
+ label.ForeColor = SystemColors.WindowText;
+ label.TextAlign = ContentAlignment.TopCenter;
+ label.ClientSize = new Size(form.ClientSize.Width - 14, dluH * 2);
+ label.Location = new Point(0, 0);
+ label.Padding = new Padding(dluBorder);
+ label.Size = new Size(form.ClientSize.Width, form.ClientSize.Height - dluBorder - closeButton.Height - dluBorder);
+ label.Text = Resources.GetInCurrentLanguage("Unexpected error occurred.", "Dogodila se neočekivana greška.");
+
+ form.Controls.Add(label);
+ if (address != null) {
+ form.Controls.Add(sendButton);
+ }
+ form.Controls.Add(closeButton);
+
+
+ return form.ShowDialog(owner);
+ }
+ }
+
+ private static DialogResult ShowDialogCollect(IWin32Window owner, Exception exception, out string message, out string email, out string displayName) {
+ var ownerForm = owner as Form;
+
+ using (var form = new Form())
+ using (var panelContent = new Panel())
+ using (var labelHelp = new Label())
+ using (var labelMessage = new Label())
+ using (var textMessage = new TextBox())
+ using (var labelName = new Label())
+ using (var textName = new TextBox())
+ using (var labelEmail = new Label())
+ using (var textEmail = new TextBox())
+ using (var labelReport = new Label())
+ using (var textReport = new TextBox())
+ using (var sendButton = new Button())
+ using (var cancelButton = new Button()) {
+ form.AcceptButton = sendButton;
+ form.CancelButton = cancelButton;
+ form.ControlBox = true;
+ form.Font = SystemFonts.MessageBoxFont;
+ form.FormBorderStyle = FormBorderStyle.FixedDialog;
+ if (ownerForm != null) {
+ form.Icon = ownerForm.Icon;
+ form.StartPosition = FormStartPosition.CenterParent;
+ } else {
+ form.Icon = null;
+ form.StartPosition = FormStartPosition.CenterScreen;
+ }
+ form.MaximizeBox = false;
+ form.MinimizeBox = false;
+ form.ShowInTaskbar = false;
+ form.Text = _infoProductTitle;
+ form.TopMost = ErrorReport.TopMost;
+
+ int dluW, dluH;
+ using (var graphics = form.CreateGraphics()) {
+ var fewCharSize = graphics.MeasureString(Resources.MeasurementText, form.Font);
+ dluW = (int)System.Math.Ceiling(fewCharSize.Width / Resources.MeasurementText.Length);
+ dluH = (int)System.Math.Ceiling(fewCharSize.Height);
+ }
+ var dluBorder = dluH / 2;
+ var dluSpace = dluH / 3;
+
+ form.ClientSize = new Size(System.Math.Min(64 * dluW, Screen.GetWorkingArea(form).Width - 4 * dluH), System.Math.Min(32 * dluH, Screen.GetWorkingArea(form).Height - 4 * dluH));
+
+ //panel
+ panelContent.BackColor = SystemColors.Window;
+ panelContent.ForeColor = SystemColors.WindowText;
+ panelContent.Location = new Point(0, 0);
+ panelContent.Size = new Size(form.ClientRectangle.Width, form.ClientRectangle.Height - sendButton.Height - dluBorder * 3);
+
+ //labelHelp
+ labelHelp.AutoEllipsis = true;
+ labelHelp.AutoSize = false;
+ labelHelp.BackColor = SystemColors.Window;
+ labelHelp.ForeColor = SystemColors.Highlight;
+ labelHelp.Text = Resources.GetInCurrentLanguage("This report will not contain any personal data not provided by you.", "Ovaj izvještaj neće sadržavati osobne podatke osim onih koje Vi odlučite dati.");
+ labelHelp.ClientSize = new Size(form.ClientSize.Width - dluBorder - dluBorder, dluH * 2);
+ labelHelp.Location = new Point(dluBorder, dluBorder);
+
+ //labelMessage
+ labelMessage.AutoEllipsis = true;
+ labelMessage.AutoSize = false;
+ labelMessage.BackColor = SystemColors.Window;
+ labelMessage.ForeColor = SystemColors.WindowText;
+ if (exception != null) {
+ labelMessage.Text = Resources.GetInCurrentLanguage("What were you doing when error occurred?", "Što ste radili kada se dogodila greška?");
+ } else {
+ labelMessage.Text = Resources.GetInCurrentLanguage("What do you wish to report?", "Što želite prijaviti?");
+ }
+ labelMessage.ClientSize = new Size(form.ClientSize.Width - dluBorder - dluBorder, dluH);
+ labelMessage.Location = new Point(dluBorder, labelHelp.Bottom + dluSpace);
+
+ //textMessage
+ textMessage.BackColor = SystemColors.Window;
+ textMessage.ForeColor = SystemColors.WindowText;
+ textMessage.AcceptsReturn = true;
+ textMessage.Multiline = true;
+ textMessage.ScrollBars = ScrollBars.Vertical;
+ textMessage.Size = new Size(form.ClientRectangle.Width - dluBorder * 2, 3 * dluH);
+ textMessage.Location = new Point(dluBorder, labelMessage.Bottom + dluSpace);
+ if (exception == null) {
+ textMessage.Tag = sendButton;
+ textMessage.TextChanged += new EventHandler(textMessage_TextChanged);
+ }
+ textMessage.PreviewKeyDown += new PreviewKeyDownEventHandler(text_PreviewKeyDown);
+
+ //textEmail
+ textEmail.BackColor = SystemColors.Window;
+ textEmail.ForeColor = SystemColors.WindowText;
+ textEmail.Size = new Size(form.ClientRectangle.Width - dluW * 15 - dluBorder * 2, dluH);
+ textEmail.Location = new Point(form.ClientRectangle.Width - textEmail.Width - dluBorder, textMessage.Bottom + dluSpace);
+ textEmail.PreviewKeyDown += new PreviewKeyDownEventHandler(text_PreviewKeyDown);
+
+ //labelEmail
+ labelEmail.BackColor = SystemColors.Window;
+ labelEmail.ForeColor = SystemColors.WindowText;
+ labelEmail.AutoEllipsis = true;
+ labelEmail.AutoSize = false;
+ labelEmail.ClientSize = new Size(dluW * 14, dluH);
+ labelEmail.Location = new Point(dluBorder, textEmail.Top + (textEmail.Height - labelEmail.Height) / 2);
+ labelEmail.Text = Resources.GetInCurrentLanguage("E-mail (optional):", "E-mail (neobvezno):");
+
+ //textName
+ textName.BackColor = SystemColors.Window;
+ textName.ForeColor = SystemColors.WindowText;
+ textName.Size = new Size(form.ClientRectangle.Width - dluW * 15 - dluBorder * 2, dluH);
+ textName.Location = new Point(form.ClientRectangle.Width - textName.Width - dluBorder, textEmail.Bottom + dluSpace + dluSpace);
+ textName.PreviewKeyDown += new PreviewKeyDownEventHandler(text_PreviewKeyDown);
+
+ //labelName
+ labelName.AutoEllipsis = true;
+ labelName.AutoSize = false;
+ labelName.BackColor = SystemColors.Window;
+ labelName.ForeColor = SystemColors.WindowText;
+ labelName.ClientSize = new Size(dluW * 14, dluH);
+ labelName.Location = new Point(dluBorder, textName.Top + (textName.Height - labelName.Height) / 2);
+ labelName.Text = Resources.GetInCurrentLanguage("Name (optional):", "Ime (neobvezno):");
+
+ //labelReport
+ labelReport.AutoEllipsis = true;
+ labelReport.AutoSize = false;
+ labelReport.BackColor = SystemColors.Window;
+ labelReport.ForeColor = SystemColors.WindowText;
+ labelReport.Text = Resources.GetInCurrentLanguage("Additional data that will be sent:", "Dodatni podaci koji će biti poslani:");
+ labelReport.ClientSize = new Size(form.ClientRectangle.Width - dluBorder * 2, dluH);
+ labelReport.Location = new Point(dluBorder, textName.Bottom + dluBorder);
+
+ //textReport
+ textReport.BackColor = SystemColors.Control;
+ textReport.ForeColor = SystemColors.ControlText;
+ textReport.Font = new Font(FontFamily.GenericMonospace, form.Font.Size * 1F, FontStyle.Regular, form.Font.Unit);
+ textReport.Multiline = true;
+ textReport.ReadOnly = true;
+ textReport.ScrollBars = ScrollBars.Vertical;
+ textReport.Text = LogBufferGetString();
+ textReport.Location = new Point(dluBorder, labelReport.Bottom + dluSpace);
+ textReport.Size = new Size(form.ClientRectangle.Width - dluBorder * 2, panelContent.ClientRectangle.Height - textReport.Top - dluBorder);
+ textReport.PreviewKeyDown += new PreviewKeyDownEventHandler(text_PreviewKeyDown);
+
+ //sendButton
+ sendButton.AutoEllipsis = true;
+ sendButton.ClientSize = new Size(dluW * 20, (int)(dluH * 1.5));
+ sendButton.Enabled = (exception != null);
+ sendButton.DialogResult = DialogResult.OK;
+ sendButton.Location = new Point(dluBorder, form.ClientRectangle.Bottom - sendButton.Height - dluBorder);
+ sendButton.Text = Resources.GetInCurrentLanguage("Report a bug", "Pošalji prijavu greške");
+
+ //cancelButton
+ cancelButton.AutoEllipsis = true;
+ cancelButton.ClientSize = new Size(dluW * 10, (int)(dluH * 1.5));
+ cancelButton.DialogResult = DialogResult.Cancel;
+ cancelButton.Location = new Point(form.ClientRectangle.Right - cancelButton.Width - dluBorder, form.ClientRectangle.Bottom - cancelButton.Height - dluBorder);
+ cancelButton.Text = Resources.GetInCurrentLanguage("Cancel", "Odustani");
+
+ panelContent.Controls.Add(labelHelp);
+ panelContent.Controls.Add(labelMessage);
+ panelContent.Controls.Add(textMessage);
+ panelContent.Controls.Add(labelEmail);
+ panelContent.Controls.Add(textEmail);
+ panelContent.Controls.Add(labelName);
+ panelContent.Controls.Add(textName);
+ panelContent.Controls.Add(labelReport);
+ panelContent.Controls.Add(textReport);
+
+ form.Controls.Add(panelContent);
+ form.Controls.Add(sendButton);
+ form.Controls.Add(cancelButton);
+
+
+ if (form.ShowDialog(owner) == DialogResult.OK) {
+ message = textMessage.Text.Trim();
+ email = textEmail.Text.Trim();
+ displayName = textName.Text.Trim();
+ return DialogResult.OK;
+ } else {
+ message = null;
+ email = null;
+ displayName = null;
+ return DialogResult.Cancel;
+ }
+ }
+ }
+
+ private static void textMessage_TextChanged(object sender, EventArgs e) {
+ var senderTextBox = sender as TextBox;
+ if (senderTextBox != null) {
+ var button = senderTextBox.Tag as Button;
+ if (button != null) {
+ button.Enabled = senderTextBox.Text.Length > 0;
+ }
+ }
+ }
+
+ private static void text_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) {
+ var senderTextBox = sender as TextBox;
+ if (senderTextBox != null) {
+ if (e.KeyData == (Keys.Control | Keys.A)) {
+ senderTextBox.SelectAll();
+ e.IsInputKey = false;
+ }
+ }
+ }
+
+ private static DialogResult ShowDialogSend(IWin32Window owner, Uri address, string message, string email, string displayName) {
+ var ownerForm = owner as Form;
+
+ using (var form = new Form())
+ using (var label = new Label())
+ using (var backgroundWorker = new BackgroundWorker())
+ using (var progressBar = new ProgressBar()) {
+ form.ControlBox = false;
+ form.Font = SystemFonts.MessageBoxFont;
+ form.FormBorderStyle = FormBorderStyle.FixedDialog;
+ if (ownerForm != null) {
+ form.Icon = ownerForm.Icon;
+ form.StartPosition = FormStartPosition.CenterParent;
+ } else {
+ form.Icon = null;
+ form.StartPosition = FormStartPosition.CenterScreen;
+ }
+ form.MaximizeBox = false;
+ form.MinimizeBox = false;
+ form.ShowInTaskbar = false;
+ form.Text = _infoProductTitle;
+ form.TopMost = ErrorReport.TopMost;
+
+ int dluW, dluH;
+ using (var graphics = form.CreateGraphics()) {
+ var fewCharSize = graphics.MeasureString(Resources.MeasurementText, form.Font);
+ dluW = (int)System.Math.Ceiling(fewCharSize.Width / Resources.MeasurementText.Length);
+ dluH = (int)System.Math.Ceiling(fewCharSize.Height);
+ }
+
+ var dluBorder = dluH / 2;
+ var dluSpace = dluH / 3;
+
+ form.ClientSize = new Size(System.Math.Min(36 * dluW, Screen.GetWorkingArea(form).Width - 4 * dluH), System.Math.Min(4 * dluH, Screen.GetWorkingArea(form).Height - 4 * dluH));
+
+ //label
+ label.AutoEllipsis = true;
+ label.TextAlign = ContentAlignment.TopCenter;
+ label.ClientSize = new Size(form.ClientSize.Width - dluBorder * 2, dluH * 2);
+ label.Location = new Point(dluBorder, dluBorder);
+ label.Text = Resources.GetInCurrentLanguage("Sending error report...", "Prijava greške u tijeku...");
+
+ //progressBar
+ progressBar.MarqueeAnimationSpeed = 50;
+ progressBar.Style = ProgressBarStyle.Marquee;
+ progressBar.ClientSize = new Size(form.ClientRectangle.Width - dluBorder * 2, dluH);
+ progressBar.Location = new Point(dluBorder, form.ClientRectangle.Bottom - dluBorder - progressBar.Height);
+
+ //backgroundWorker
+ backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
+ backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
+
+
+ form.Controls.Add(label);
+ form.Controls.Add(progressBar);
+
+ var allFormParameters = new NameValueCollection();
+ allFormParameters.Add("Product", _infoProductTitle);
+ allFormParameters.Add("Version", _infoProductVersion);
+ allFormParameters.Add("Message", message);
+ if (!string.IsNullOrEmpty(email)) { allFormParameters.Add("Email", email); }
+ if (!string.IsNullOrEmpty(displayName)) { allFormParameters.Add("DisplayName", displayName); }
+
+ backgroundWorker.RunWorkerAsync(new object[] { form, address, allFormParameters });
+
+ MessageBoxOptions mbOptions = 0;
+ if (owner == null) { mbOptions |= MessageBoxOptions.ServiceNotification; }
+ if ((ownerForm != null) && (ownerForm.RightToLeft == RightToLeft.Yes)) { mbOptions |= MessageBoxOptions.RightAlign | MessageBoxOptions.RtlReading; }
+ if (form.ShowDialog(owner) == DialogResult.OK) {
+ System.Windows.Forms.MessageBox.Show(owner, Resources.GetInCurrentLanguage("Error report was successfully sent.", "Izvještaj o grešci je uspješno poslan."), _infoProductTitle, MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, mbOptions);
+ return DialogResult.OK;
+ } else {
+ if (!ErrorReport.DisableAutomaticSaveToTemp) {
+ try {
+ string fullLogFileName = System.IO.Path.Combine(System.IO.Path.GetTempPath(), string.Format(System.Globalization.CultureInfo.InvariantCulture, @"ErrorReport [{0}] {1:yyyyMMdd\THHmmss}.log", _infoProductTitle, DateTime.Now));
+ System.IO.File.WriteAllText(fullLogFileName, message);
+ } catch (System.Security.SecurityException) {
+ } catch (System.IO.IOException) { }
+ }
+ if (System.Windows.Forms.MessageBox.Show(owner, Resources.GetInCurrentLanguage("Error report cannot be sent.\nDo you wish to retry?", "Izvještaj o grešci ne može biti poslan.\nŽelite li ponovno pokušati?"), _infoProductTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, mbOptions) == DialogResult.Yes) {
+ return DialogResult.Retry;
+ } else {
+ return DialogResult.Cancel;
+ }
+ }
+ }
+ }
+
+ private static void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) {
+ var transferBag = (object[])e.Argument;
+ var form = (Form)transferBag[0];
+ var address = (Uri)transferBag[1];
+ var allFormParameters = (NameValueCollection)transferBag[2];
+
+
+ try {
+
+ WebRequest request = WebRequest.Create(address);
+ request.Method = "POST";
+ request.Proxy = HttpWebRequest.DefaultWebProxy;
+ request.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
+
+ StringBuilder sbPostData = new StringBuilder();
+ for (int i = 0; i < allFormParameters.Count; ++i) {
+ if (sbPostData.Length > 0) { sbPostData.Append("&"); }
+ sbPostData.Append(UrlEncode(allFormParameters.GetKey(i)) + "=" + UrlEncode(allFormParameters[i]));
+ }
+
+ byte[] byteArray = Encoding.UTF8.GetBytes(sbPostData.ToString());
+ request.ContentType = "application/x-www-form-urlencoded";
+ request.ContentLength = byteArray.Length;
+ using (System.IO.Stream dataStream = request.GetRequestStream()) {
+ dataStream.Write(byteArray, 0, byteArray.Length);
+ }
+
+ using (var response = (HttpWebResponse)request.GetResponse()) {
+ if (response.StatusCode == HttpStatusCode.OK) {
+ using (System.IO.StreamReader reader = new System.IO.StreamReader(response.GetResponseStream())) {
+ string responseFromServer = reader.ReadToEnd();
+ if (responseFromServer.Length == 0) { //no data is outputed in case of real 200 response (instead of 500 wrapped in generic 200 page)
+ e.Result = new object[] { form, DialogResult.OK };
+ } else {
+ e.Result = new object[] { form, DialogResult.Cancel };
+ }
+ }
+ e.Result = new object[] { form, DialogResult.OK };
+ } else {
+ e.Result = new object[] { form, DialogResult.Cancel };
+ }
+ }
+
+ } catch (WebException ex) {
+ System.Diagnostics.Debug.WriteLine("W: " + ex.Message + ". {{Medo.Diagnostics.ErrorReport}}");
+ e.Result = new object[] { form, DialogResult.Cancel };
+ }
+ }
+
+ private static void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
+ var transferBag = (object[])e.Result;
+ var form = (Form)transferBag[0];
+ var result = (DialogResult)transferBag[1];
+
+ form.DialogResult = result;
+ }
+
+ private static void LogBufferSaveToLogFile() {
+ if (System.IO.File.Exists(_logFileName)) {
+ System.IO.File.AppendAllText(_logFileName, _logSeparator);
+ }
+
+ System.IO.File.AppendAllText(_logFileName, LogBufferGetString());
+ }
+
+ private static void LogBufferFillFromException(Exception exception, params string[] additionalInformation) {
+ if (_logBuffer.Length != 0) { _logBuffer.Length = 0; }
+ var isOutOfMemoryException = (exception is OutOfMemoryException);
+
+ AppendLine("Environment", _logBuffer);
+ AppendLine("", _logBuffer);
+ AppendLine(_infoAssemblyFullName, _logBuffer, 1, true);
+ AppendLine(_infoOsVersion, _logBuffer, 1, true);
+ AppendLine(_infoFrameworkVersion, _logBuffer, 1, true);
+ if (isOutOfMemoryException == false) {
+ AppendLine("Local time is " + DateTime.Now.ToString(@"yyyy\-MM\-dd\THH\:mm\:ssK", System.Globalization.CultureInfo.InvariantCulture), _logBuffer, 1, true); //it will fail in OutOfMemory situation
+ }
+
+ if (exception != null) {
+ AppendLine("", _logBuffer);
+ Exception ex = exception;
+ int exLevel = 0;
+ while (ex != null) {
+ AppendLine("", _logBuffer);
+
+ if (exLevel == 0) {
+ AppendLine("Exception", _logBuffer);
+ } else if (exLevel == 1) {
+ AppendLine("Inner exception (1)", _logBuffer);
+ } else if (exLevel == 2) {
+ AppendLine("Inner exception (2)", _logBuffer);
+ } else {
+ AppendLine("Inner exception (...)", _logBuffer);
+ }
+ AppendLine("", _logBuffer);
+ if (isOutOfMemoryException == false) {
+ AppendLine(ex.GetType().ToString(), _logBuffer, 1, true);
+ }
+ AppendLine(ex.Message, _logBuffer, 1, true);
+ if (!string.IsNullOrEmpty(ex.StackTrace)) {
+ AppendLine(ex.StackTrace, _logBuffer, 2, false);
+ }
+
+ ex = ex.InnerException;
+ exLevel += 1;
+ }
+
+ AppendLine("", _logBuffer);
+ AppendLine("", _logBuffer);
+ AppendLine("Referenced assemblies", _logBuffer);
+ AppendLine("", _logBuffer);
+ for (int i = 0; i < _infoReferencedAssemblies.Length; ++i) {
+ AppendLine(_infoReferencedAssemblies[i], _logBuffer, 1, true);
+ }
+ }
+
+ if ((additionalInformation != null) && (additionalInformation.Length > 0)) {
+ AppendLine("", _logBuffer);
+ AppendLine("", _logBuffer);
+ AppendLine("Additional information", _logBuffer);
+ AppendLine("", _logBuffer);
+ for (int i = 0; i < additionalInformation.Length; ++i) {
+ AppendLine(additionalInformation[i], _logBuffer, 1, true);
+ }
+ }
+ }
+
+ private static string LogBufferGetStringWithUserInformation(string message, string name, string email) {
+ var sb = new StringBuilder();
+ if (!string.IsNullOrEmpty(message)) {
+ AppendLine(message, sb);
+ AppendLine("", sb);
+ AppendLine("", sb);
+ }
+ if (!string.IsNullOrEmpty(name) || !string.IsNullOrEmpty(email)) {
+ AppendLine("User information", sb);
+ AppendLine("", sb);
+ if (!string.IsNullOrEmpty(name)) {
+ AppendLine("Name: " + name, sb, 1, true);
+ }
+ if (!string.IsNullOrEmpty(email)) {
+ AppendLine("E-mail: " + email, sb, 1, true);
+ }
+ AppendLine("", sb);
+ AppendLine("", sb);
+ }
+ sb.Append(_logBuffer);
+
+ return sb.ToString();
+ }
+
+ private static string LogBufferGetString() {
+ return _logBuffer.ToString();
+ }
+
+ private static string UrlEncode(string text) {
+ byte[] source = System.Text.UTF8Encoding.UTF8.GetBytes(text);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < source.Length; ++i) {
+ if (((source[i] >= 48) && (source[i] <= 57)) || ((source[i] >= 65) && (source[i] <= 90)) || ((source[i] >= 97) && (source[i] <= 122)) || (source[i] == 45) || (source[i] == 46) || (source[i] == 95) || (source[i] == 126)) { //A-Z a-z - . _ ~
+ sb.Append(System.Convert.ToChar(source[i]));
+ } else {
+ sb.Append("%" + source[i].ToString("X2", System.Globalization.CultureInfo.InvariantCulture));
+ }
+ }
+ return sb.ToString();
+ }
+
+
+ private const int LineLength = 72;
+
+ private static void AppendLine(string input, StringBuilder output) {
+ AppendLine(input, output, 0, false);
+ }
+
+ private static void AppendLine(string input, StringBuilder output, int indentLevel, bool tickO) {
+ if (input == null) { return; }
+ if (input.Length == 0) {
+ output.AppendLine();
+ return;
+ }
+
+ if (tickO) {
+ indentLevel += 1;
+ }
+
+
+ int maxWidth = LineLength - indentLevel * 3;
+ int end = input.Length - 1;
+
+ int firstChar = 0;
+
+ int lastChar;
+ int nextChar;
+ do {
+ if ((end - firstChar) < maxWidth) {
+ lastChar = end;
+ nextChar = end + 1;
+ } else {
+ int nextCrBreak = input.IndexOf('\r', firstChar, maxWidth);
+ int nextLfBreak = input.IndexOf('\n', firstChar, maxWidth);
+ int nextCrLfBreak;
+ if (nextCrBreak == -1) {
+ nextCrLfBreak = nextLfBreak;
+ } else if (nextLfBreak == -1) {
+ nextCrLfBreak = nextCrBreak;
+ } else {
+ nextCrLfBreak = System.Math.Min(nextCrBreak, nextLfBreak);
+ }
+ if ((nextCrLfBreak != -1) && ((nextCrLfBreak - firstChar) <= maxWidth)) {
+ lastChar = nextCrLfBreak - 1;
+ nextChar = lastChar + 2;
+ if (nextChar <= end) {
+ if ((input[nextChar] == '\n') || (input[nextChar] == '\r')) {
+ nextChar += 1;
+ }
+ }
+ } else {
+ int nextSpaceBreak = input.LastIndexOf(' ', firstChar + maxWidth, maxWidth);
+ if ((nextSpaceBreak != -1) && ((nextSpaceBreak - firstChar) <= maxWidth)) {
+ lastChar = nextSpaceBreak;
+ nextChar = lastChar + 1;
+ } else {
+ int nextOtherBreak1 = input.LastIndexOf('-', firstChar + maxWidth, maxWidth);
+ int nextOtherBreak2 = input.LastIndexOf(':', firstChar + maxWidth, maxWidth);
+ int nextOtherBreak3 = input.LastIndexOf('(', firstChar + maxWidth, maxWidth);
+ int nextOtherBreak4 = input.LastIndexOf(',', firstChar + maxWidth, maxWidth);
+ int nextOtherBreak = System.Math.Max(nextOtherBreak1, System.Math.Max(nextOtherBreak2, System.Math.Max(nextOtherBreak3, nextOtherBreak4)));
+ if ((nextOtherBreak != -1) && ((nextOtherBreak - firstChar) <= maxWidth)) {
+ lastChar = nextOtherBreak;
+ nextChar = lastChar + 1;
+ } else {
+ lastChar = firstChar + maxWidth;
+ if (lastChar > end) { lastChar = end; }
+ nextChar = lastChar;
+ }
+ }
+ }
+ }
+
+ if (tickO) {
+ for (int i = 0; i < indentLevel - 1; ++i) { output.Append(" "); }
+ output.Append("o ");
+ tickO = false;
+ } else {
+ for (int i = 0; i < indentLevel; ++i) { output.Append(" "); }
+ }
+ for (int i = firstChar; i <= lastChar; ++i) {
+ output.Append(input[i]);
+ }
+ output.AppendLine();
+
+ firstChar = nextChar;
+ } while (nextChar <= end);
+ }
+
+
+ private static class Resources {
+
+ internal static string MeasurementText { get { return "mMiI"; } }
+
+ internal static string GetInCurrentLanguage(string en_US, string hr_HR) {
+ switch (System.Threading.Thread.CurrentThread.CurrentUICulture.Name.ToUpperInvariant()) {
+ case "EN":
+ case "EN-US":
+ case "EN-GB":
+ return en_US;
+
+ case "HR":
+ case "HR-HR":
+ case "HR-BA":
+ return hr_HR;
+
+ default:
+ return en_US;
+ }
+ }
+
+ }
+
+ }
+
+}
diff --git a/Source/WrtSettings/(Medo)/MessageBox [007].cs b/Source/WrtSettings/(Medo)/MessageBox [007].cs
new file mode 100644
index 0000000..66daca9
--- /dev/null
+++ b/Source/WrtSettings/(Medo)/MessageBox [007].cs
@@ -0,0 +1,759 @@
+//Copyright (c) 2007 Josip Medved <jmedved@jmedved.com>
+
+//2007-12-31: New version.
+//2008-01-03: Added Resources.
+//2008-04-11: Cleaned code to match FxCop 1.36 beta 2 (SpecifyMarshalingForPInvokeStringArguments, NormalizeStringsToUppercase).
+//2008-11-14: Reworked code to use SafeHandle.
+// Fixed ToInt32 call on x64 bit windows.
+//2008-12-01: Deleted methods without owner parameter.
+//2009-07-04: Compatibility with Mono 2.4.
+//2012-11-24: Suppressing bogus CA5122 warning (http://connect.microsoft.com/VisualStudio/feedback/details/729254/bogus-ca5122-warning-about-p-invoke-declarations-should-not-be-safe-critical).
+
+
+using System;
+using System.Runtime.ConstrainedExecution;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace Medo {
+
+ /// <summary>
+ /// Displays a message box that can contain text, buttons, and symbols that inform and instruct the user.
+ /// </summary>
+ public static class MessageBox {
+
+ private readonly static object _syncRoot = new object();
+
+
+ #region With owner
+
+ /// <summary>
+ /// Displays a message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ public static DialogResult ShowDialog(IWin32Window owner, string text) {
+ lock (_syncRoot) {
+ return ShowDialog(owner, text, Resources.DefaultCaption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ public static DialogResult ShowDialog(IWin32Window owner, string text, MessageBoxButtons buttons) {
+ lock (_syncRoot) {
+ return ShowDialog(owner, text, Resources.DefaultCaption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ /// <param name="icon">One of the MessageBoxIcon values that specifies which icon to display in the message box.</param>
+ public static DialogResult ShowDialog(IWin32Window owner, string text, MessageBoxButtons buttons, MessageBoxIcon icon) {
+ lock (_syncRoot) {
+ return ShowDialog(owner, text, Resources.DefaultCaption, buttons, icon, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ /// <param name="icon">One of the MessageBoxIcon values that specifies which icon to display in the message box.</param>
+ /// <param name="defaultButton">One of the MessageBoxDefaultButton values that specifies the default button for the message box.</param>
+ public static DialogResult ShowDialog(IWin32Window owner, string text, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton) {
+ lock (_syncRoot) {
+ return ShowDialog(owner, text, Resources.DefaultCaption, buttons, icon, defaultButton);
+ }
+ }
+
+ /// <summary>
+ /// Displays a message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="caption">The text to display in the title bar of the message box.</param>
+ public static DialogResult ShowDialog(IWin32Window owner, string text, string caption) {
+ lock (_syncRoot) {
+ return ShowDialog(owner, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="caption">The text to display in the title bar of the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ public static DialogResult ShowDialog(IWin32Window owner, string text, string caption, MessageBoxButtons buttons) {
+ lock (_syncRoot) {
+ return ShowDialog(owner, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="caption">The text to display in the title bar of the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ /// <param name="icon">One of the MessageBoxIcon values that specifies which icon to display in the message box.</param>
+ public static DialogResult ShowDialog(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) {
+ lock (_syncRoot) {
+ return ShowDialog(owner, text, caption, buttons, icon, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ #endregion
+
+
+ /// <summary>
+ /// Displays a message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="caption">The text to display in the title bar of the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ /// <param name="icon">One of the MessageBoxIcon values that specifies which icon to display in the message box.</param>
+ /// <param name="defaultButton">One of the MessageBoxDefaultButton values that specifies the default button for the message box.</param>
+ public static DialogResult ShowDialog(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton) {
+ if (!MessageBox.IsRunningOnMono) {
+ lock (_syncRoot) {
+ if (owner != null) {
+ using (CbtHook ch = new CbtHook(owner)) {
+ return (DialogResult)NativeMethods.MessageBox(owner.Handle, text, caption, (uint)buttons | (uint)icon | (uint)defaultButton);
+ }
+ } else {
+ using (CbtHook ch = new CbtHook(null)) {
+ return (DialogResult)NativeMethods.MessageBox(System.IntPtr.Zero, text, caption, (uint)buttons | (uint)icon | (uint)defaultButton);
+ }
+ }
+ } //lock
+ } else { //MONO
+ return System.Windows.Forms.MessageBox.Show(owner, text, caption, buttons, icon, defaultButton, 0);
+ }
+ }
+
+
+ #region ShowInformation
+
+ /// <summary>
+ /// Displays a information message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ public static DialogResult ShowInformation(IWin32Window owner, string text) {
+ lock (_syncRoot) {
+ return ShowInformation(owner, text, Resources.DefaultCaption, MessageBoxButtons.OK, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a information message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ public static DialogResult ShowInformation(IWin32Window owner, string text, MessageBoxButtons buttons) {
+ lock (_syncRoot) {
+ return ShowInformation(owner, text, Resources.DefaultCaption, buttons, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a information message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ /// <param name="defaultButton">One of the MessageBoxDefaultButton values that specifies the default button for the message box.</param>
+ public static DialogResult ShowInformation(IWin32Window owner, string text, MessageBoxButtons buttons, MessageBoxDefaultButton defaultButton) {
+ lock (_syncRoot) {
+ return ShowInformation(owner, text, Resources.DefaultCaption, buttons, defaultButton);
+ }
+ }
+
+ /// <summary>
+ /// Displays a information message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="caption">The text to display in the title bar of the message box.</param>
+ public static DialogResult ShowInformation(IWin32Window owner, string text, string caption) {
+ lock (_syncRoot) {
+ return ShowInformation(owner, text, caption, MessageBoxButtons.OK, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a information message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="caption">The text to display in the title bar of the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ public static DialogResult ShowInformation(IWin32Window owner, string text, string caption, MessageBoxButtons buttons) {
+ lock (_syncRoot) {
+ return ShowInformation(owner, text, caption, buttons, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a information message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="caption">The text to display in the title bar of the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ /// <param name="defaultButton">One of the MessageBoxDefaultButton values that specifies the default button for the message box.</param>
+ public static DialogResult ShowInformation(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxDefaultButton defaultButton) {
+ lock (_syncRoot) {
+ return ShowDialog(owner, text, caption, buttons, MessageBoxIcon.Information, defaultButton);
+ }
+ }
+
+ #endregion
+
+
+ #region ShowWarning
+
+ /// <summary>
+ /// Displays a warning message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ public static DialogResult ShowWarning(IWin32Window owner, string text) {
+ lock (_syncRoot) {
+ return ShowWarning(owner, text, Resources.DefaultCaption, MessageBoxButtons.OK, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a warning message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ public static DialogResult ShowWarning(IWin32Window owner, string text, MessageBoxButtons buttons) {
+ lock (_syncRoot) {
+ return ShowWarning(owner, text, Resources.DefaultCaption, buttons, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a warning message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ /// <param name="defaultButton">One of the MessageBoxDefaultButton values that specifies the default button for the message box.</param>
+ public static DialogResult ShowWarning(IWin32Window owner, string text, MessageBoxButtons buttons, MessageBoxDefaultButton defaultButton) {
+ lock (_syncRoot) {
+ return ShowWarning(owner, text, Resources.DefaultCaption, buttons, defaultButton);
+ }
+ }
+
+ /// <summary>
+ /// Displays a warning message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="caption">The text to display in the title bar of the message box.</param>
+ public static DialogResult ShowWarning(IWin32Window owner, string text, string caption) {
+ lock (_syncRoot) {
+ return ShowWarning(owner, text, caption, MessageBoxButtons.OK, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a warning message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="caption">The text to display in the title bar of the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ public static DialogResult ShowWarning(IWin32Window owner, string text, string caption, MessageBoxButtons buttons) {
+ lock (_syncRoot) {
+ return ShowWarning(owner, text, caption, buttons, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a warning message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="caption">The text to display in the title bar of the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ /// <param name="defaultButton">One of the MessageBoxDefaultButton values that specifies the default button for the message box.</param>
+ public static DialogResult ShowWarning(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxDefaultButton defaultButton) {
+ lock (_syncRoot) {
+ return ShowDialog(owner, text, caption, buttons, MessageBoxIcon.Warning, defaultButton);
+ }
+ }
+
+ #endregion
+
+
+ #region ShowError
+
+ /// <summary>
+ /// Displays a error message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ public static DialogResult ShowError(IWin32Window owner, string text) {
+ lock (_syncRoot) {
+ return ShowError(owner, text, Resources.DefaultCaption, MessageBoxButtons.OK, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a error message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ public static DialogResult ShowError(IWin32Window owner, string text, MessageBoxButtons buttons) {
+ lock (_syncRoot) {
+ return ShowError(owner, text, Resources.DefaultCaption, buttons, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a error message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ /// <param name="defaultButton">One of the MessageBoxDefaultButton values that specifies the default button for the message box.</param>
+ public static DialogResult ShowError(IWin32Window owner, string text, MessageBoxButtons buttons, MessageBoxDefaultButton defaultButton) {
+ lock (_syncRoot) {
+ return ShowError(owner, text, Resources.DefaultCaption, buttons, defaultButton);
+ }
+ }
+
+ /// <summary>
+ /// Displays a error message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="caption">The text to display in the title bar of the message box.</param>
+ public static DialogResult ShowError(IWin32Window owner, string text, string caption) {
+ lock (_syncRoot) {
+ return ShowError(owner, text, caption, MessageBoxButtons.OK, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a error message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="caption">The text to display in the title bar of the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ public static DialogResult ShowError(IWin32Window owner, string text, string caption, MessageBoxButtons buttons) {
+ lock (_syncRoot) {
+ return ShowError(owner, text, caption, buttons, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a error message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="caption">The text to display in the title bar of the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ /// <param name="defaultButton">One of the MessageBoxDefaultButton values that specifies the default button for the message box.</param>
+ public static DialogResult ShowError(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxDefaultButton defaultButton) {
+ lock (_syncRoot) {
+ return ShowDialog(owner, text, caption, buttons, MessageBoxIcon.Error, defaultButton);
+ }
+ }
+
+ #endregion
+
+
+ #region ShowQuestion
+
+ /// <summary>
+ /// Displays a question message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ public static DialogResult ShowQuestion(IWin32Window owner, string text) {
+ lock (_syncRoot) {
+ return ShowQuestion(owner, text, Resources.DefaultCaption, MessageBoxButtons.OK, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a question message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ public static DialogResult ShowQuestion(IWin32Window owner, string text, MessageBoxButtons buttons) {
+ lock (_syncRoot) {
+ return ShowQuestion(owner, text, Resources.DefaultCaption, buttons, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a question message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ /// <param name="defaultButton">One of the MessageBoxDefaultButton values that specifies the default button for the message box.</param>
+ public static DialogResult ShowQuestion(IWin32Window owner, string text, MessageBoxButtons buttons, MessageBoxDefaultButton defaultButton) {
+ lock (_syncRoot) {
+ return ShowQuestion(owner, text, Resources.DefaultCaption, buttons, defaultButton);
+ }
+ }
+
+ /// <summary>
+ /// Displays a question message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="caption">The text to display in the title bar of the message box.</param>
+ public static DialogResult ShowQuestion(IWin32Window owner, string text, string caption) {
+ lock (_syncRoot) {
+ return ShowQuestion(owner, text, caption, MessageBoxButtons.OK, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a question message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="caption">The text to display in the title bar of the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ public static DialogResult ShowQuestion(IWin32Window owner, string text, string caption, MessageBoxButtons buttons) {
+ lock (_syncRoot) {
+ return ShowQuestion(owner, text, caption, buttons, MessageBoxDefaultButton.Button1);
+ }
+ }
+
+ /// <summary>
+ /// Displays a question message box in front of the specified object and with the specified text.
+ /// </summary>
+ /// <param name="owner">An implementation of IWin32Window that will own the modal dialog box.</param>
+ /// <param name="text">The text to display in the message box.</param>
+ /// <param name="caption">The text to display in the title bar of the message box.</param>
+ /// <param name="buttons">One of the MessageBoxButtons values that specifies which buttons to display in the message box.</param>
+ /// <param name="defaultButton">One of the MessageBoxDefaultButton values that specifies the default button for the message box.</param>
+ public static DialogResult ShowQuestion(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxDefaultButton defaultButton) {
+ lock (_syncRoot) {
+ return ShowDialog(owner, text, caption, buttons, MessageBoxIcon.Question, defaultButton);
+ }
+ }
+
+ #endregion
+
+
+ private static class Resources {
+
+ internal static string DefaultCaption {
+ get {
+ System.Reflection.Assembly assembly = System.Reflection.Assembly.GetEntryAssembly();
+
+ string caption;
+ object[] productAttributes = assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyProductAttribute), true);
+ if ((productAttributes != null) && (productAttributes.Length >= 1)) {
+ caption = ((System.Reflection.AssemblyProductAttribute)productAttributes[productAttributes.Length - 1]).Product;
+ } else {
+ object[] titleAttributes = assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyTitleAttribute), true);
+ if ((titleAttributes != null) && (titleAttributes.Length >= 1)) {
+ caption = ((System.Reflection.AssemblyTitleAttribute)titleAttributes[titleAttributes.Length - 1]).Title;
+ } else {
+ caption = assembly.GetName().Name;
+ }
+ }
+
+ return caption;
+ }
+ }
+
+
+ internal static string OK {
+ get { return GetInCurrentLanguage("OK", "U redu"); }
+ }
+
+ internal static string Cancel {
+ get { return GetInCurrentLanguage("Cancel", "Odustani"); }
+ }
+
+ internal static string Abort {
+ get { return GetInCurrentLanguage("&Abort", "P&rekini"); }
+ }
+
+ internal static string Retry {
+ get { return GetInCurrentLanguage("&Retry", "&Ponovi"); }
+ }
+
+ internal static string Ignore {
+ get { return GetInCurrentLanguage("&Ignore", "&Zanemari"); }
+ }
+
+ internal static string Yes {
+ get { return GetInCurrentLanguage("&Yes", "&Da"); }
+ }
+
+ internal static string No {
+ get { return GetInCurrentLanguage("&No", "&Ne"); }
+ }
+
+
+ internal static string ExceptionCbtHookCannotBeRemoved { get { return "CBT Hook cannot be removed."; } }
+
+
+ internal static bool IsTranslatable {
+ get {
+ switch (System.Threading.Thread.CurrentThread.CurrentUICulture.Name.ToUpperInvariant()) {
+ case "EN":
+ case "EN-US":
+ case "EN-GB":
+ case "HR":
+ case "HR-HR":
+ case "HR-BA":
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ }
+
+ private static string GetInCurrentLanguage(string en_US, string hr_HR) {
+ switch (System.Threading.Thread.CurrentThread.CurrentUICulture.Name.ToUpperInvariant()) {
+ case "EN":
+ case "EN-US":
+ case "EN-GB":
+ return en_US;
+
+ case "HR":
+ case "HR-HR":
+ case "HR-BA":
+ return hr_HR;
+
+ default:
+ return en_US;
+ }
+ }
+
+ }
+
+
+ #region Native
+
+ private class CbtHook : System.IDisposable {
+
+ private IWin32Window _owner;
+
+ private NativeMethods.WindowsHookSafeHandle _hook;
+ private NativeMethods.CbtHookProcDelegate _cbtHookProc;
+
+
+ public CbtHook(IWin32Window owner) {
+ this._owner = owner;
+ this._cbtHookProc = new NativeMethods.CbtHookProcDelegate(CbtHookProc);
+ this._hook = NativeMethods.SetWindowsHookEx(NativeMethods.WH_CBT, this._cbtHookProc, System.IntPtr.Zero, NativeMethods.GetCurrentThreadId());
+ System.Diagnostics.Debug.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "I: Created CBT hook (ID={0}). {{Medo.MessageBox}}", this._hook.ToString()));
+ }
+
+ ~CbtHook() {
+ Dispose();
+ }
+
+
+ public System.IntPtr CbtHookProc(int nCode, System.IntPtr wParam, System.IntPtr lParam) {
+ switch (nCode) {
+ case NativeMethods.HCBT_ACTIVATE:
+ System.Diagnostics.Debug.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "I: Dialog HCBT_ACTIVATE (hWnd={0}). {{Medo.MessageBox}}", wParam.ToString()));
+
+ if (this._owner != null) {
+ NativeMethods.RECT rectMessage = new NativeMethods.RECT();
+ NativeMethods.RECT rectOwner = new NativeMethods.RECT();
+ if ((NativeMethods.GetWindowRect(wParam, ref rectMessage)) && (NativeMethods.GetWindowRect(this._owner.Handle, ref rectOwner))) {
+ int widthMessage = rectMessage.right - rectMessage.left;
+ int heightMessage = rectMessage.bottom - rectMessage.top;
+ int widthOwner = rectOwner.right - rectOwner.left;
+ int heightOwner = rectOwner.bottom - rectOwner.top;
+
+ int newLeft = rectOwner.left + (widthOwner - widthMessage) / 2;
+ int newTop = rectOwner.top + (heightOwner - heightMessage) / 2;
+
+ NativeMethods.SetWindowPos(wParam, System.IntPtr.Zero, newLeft, newTop, 0, 0, NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE);
+ }
+ }
+
+ if (Resources.IsTranslatable) {
+ NativeMethods.SetDlgItemText(wParam, NativeMethods.DLG_ID_OK, Resources.OK);
+ NativeMethods.SetDlgItemText(wParam, NativeMethods.DLG_ID_CANCEL, Resources.Cancel);
+ NativeMethods.SetDlgItemText(wParam, NativeMethods.DLG_ID_ABORT, Resources.Abort);
+ NativeMethods.SetDlgItemText(wParam, NativeMethods.DLG_ID_RETRY, Resources.Retry);
+ NativeMethods.SetDlgItemText(wParam, NativeMethods.DLG_ID_IGNORE, Resources.Ignore);
+ NativeMethods.SetDlgItemText(wParam, NativeMethods.DLG_ID_YES, Resources.Yes);
+ NativeMethods.SetDlgItemText(wParam, NativeMethods.DLG_ID_NO, Resources.No);
+ }
+
+ try {
+ return NativeMethods.CallNextHookEx(this._hook, nCode, wParam, lParam);
+ } finally {
+ this.Dispose();
+ }
+ }
+ return NativeMethods.CallNextHookEx(this._hook, nCode, wParam, lParam);
+ }
+
+
+ #region IDisposable Members
+
+ public void Dispose() {
+ //System.GC.KeepAlive(this._cbtHookProc);
+ if (!this._hook.IsClosed) {
+ this._hook.Close();
+ if (this._hook.IsClosed) {
+ System.Diagnostics.Debug.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "I: CBT Hook destroyed (ID={0}). {{Medo.MessageBox}}", this._hook.ToString()));
+ } else {
+ throw new System.InvalidOperationException(Resources.ExceptionCbtHookCannotBeRemoved);
+ }
+ }
+ this._hook.Dispose();
+ //if (!this._hook.Equals(System.IntPtr.Zero)) {
+ // if (NativeMethods.UnhookWindowsHookEx(this._hook)) {
+ // System.Diagnostics.Debug.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "I: Medo.Windows.Forms.MessageBox: CBT Hook destroyed (ID={0}).", this._hook.ToInt32()));
+ // this._hook = System.IntPtr.Zero;
+ // } else {
+ // throw new System.InvalidOperationException(Resources.ExceptionCbtHookCannotBeRemoved);
+ // }
+ //}
+ System.GC.SuppressFinalize(this);
+ }
+
+ #endregion
+
+ }
+
+
+ private static class NativeMethods {
+
+ public const int WH_CBT = 0x5;
+
+ public const int DLG_ID_OK = 0x01;
+ public const int DLG_ID_CANCEL = 0x02;
+ public const int DLG_ID_ABORT = 0x03;
+ public const int DLG_ID_RETRY = 0x04;
+ public const int DLG_ID_IGNORE = 0x05;
+ public const int DLG_ID_YES = 0x06;
+ public const int DLG_ID_NO = 0x07;
+
+ public const int HCBT_ACTIVATE = 0x5;
+
+ public const int SWP_NOSIZE = 0x01;
+ public const int SWP_NOZORDER = 0x04;
+ public const int SWP_NOACTIVATE = 0x10;
+
+
+ [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
+ public struct RECT {
+ public int left;
+ public int top;
+ public int right;
+ public int bottom;
+ }
+
+
+ public class WindowsHookSafeHandle : SafeHandle {
+ public WindowsHookSafeHandle()
+ : base(IntPtr.Zero, true) {
+ }
+
+
+ public override bool IsInvalid {
+ get { return (this.IsClosed) || (base.handle == IntPtr.Zero); }
+ }
+
+ protected override bool ReleaseHandle() {
+ return UnhookWindowsHookEx(this.handle);
+ }
+
+ public override string ToString() {
+ return this.handle.ToString();
+ }
+
+ }
+
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule", Justification = "Warning is bogus.")]
+ [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
+ public static extern System.IntPtr CallNextHookEx(WindowsHookSafeHandle idHook, int nCode, System.IntPtr wParam, System.IntPtr lParam);
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule", Justification = "Warning is bogus.")]
+ [System.Runtime.InteropServices.DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
+ public static extern int GetCurrentThreadId();
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule", Justification = "Warning is bogus.")]
+ [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
+ [return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
+ public static extern bool GetWindowRect(System.IntPtr hWnd, ref RECT lpRect);
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule", Justification = "Warning is bogus.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2205:UseManagedEquivalentsOfWin32Api", Justification = "Managed equivalent does not support all needed features.")]
+ [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern int MessageBox(System.IntPtr hWnd, string lpText, string lpCaption, uint uType);
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule", Justification = "Warning is bogus.")]
+ [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ [return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
+ public static extern bool SetDlgItemText(System.IntPtr hWnd, int nIDDlgItem, string lpString);
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule", Justification = "Warning is bogus.")]
+ [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
+ [return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
+ public static extern bool SetWindowPos(System.IntPtr hWnd, System.IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule", Justification = "Warning is bogus.")]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
+ public static extern WindowsHookSafeHandle SetWindowsHookEx(int idHook, CbtHookProcDelegate lpfn, System.IntPtr hInstance, int threadId);
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule", Justification = "Warning is bogus.")]
+ [ReliabilityContract(Consistency.MayCorruptProcess, Cer.Success)]
+ [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
+ [return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
+ public static extern bool UnhookWindowsHookEx(System.IntPtr idHook);
+
+
+ public delegate System.IntPtr CbtHookProcDelegate(int nCode, System.IntPtr wParam, System.IntPtr lParam);
+
+ }
+
+ #endregion
+
+
+ private static bool IsRunningOnMono {
+ get {
+ return (Type.GetType("Mono.Runtime") != null);
+ }
+ }
+
+ }
+
+}
diff --git a/Source/WrtSettings/(Medo)/RecentFiles [005].cs b/Source/WrtSettings/(Medo)/RecentFiles [005].cs
new file mode 100644
index 0000000..d6137d3
--- /dev/null
+++ b/Source/WrtSettings/(Medo)/RecentFiles [005].cs
@@ -0,0 +1,345 @@
+//Copyright (c) 2009 Josip Medved <jmedved@jmedved.com>
+
+//2009-05-23: New version.
+//2009-07-04: Compatibility with Mono 2.4.
+//2010-10-31: Added option to skip registry writes (NoRegistryWrites).
+//2012-05-31: Refactoring.
+//2012-08-20: Fixed crash when HideFileExt cannot be found in registry.
+
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Reflection;
+using System.Security;
+using Microsoft.Win32;
+
+namespace Medo.Configuration {
+
+ /// <summary>
+ /// Enables loading and saving of files list.
+ /// It is written in State key at HKEY_CURRENT_USER branch withing defined SubKeyPath.
+ /// </summary>
+ public class RecentFiles {
+
+ /// <summary>
+ /// Creates new instance with "Default" as group name and maximum of 16 files.
+ /// </summary>
+ public RecentFiles()
+ : this(16, null) {
+ }
+
+ /// <summary>
+ /// Creates new instance with "Default" as group name.
+ /// </summary>
+ /// <param name="maximumCount">Maximum number of items to load or save.</param>
+ public RecentFiles(int maximumCount)
+ : this(maximumCount, null) {
+ }
+
+ /// <summary>
+ /// Creates new instance.
+ /// </summary>
+ /// <param name="maximumCount">Maximum number of items to load or save.</param>
+ /// <param name="groupName">Name of group. If omitted, "Default" is used.</param>
+ public RecentFiles(int maximumCount, string groupName) {
+ Assembly assembly = Assembly.GetEntryAssembly();
+ if (assembly == null) { assembly = Assembly.GetCallingAssembly(); } //e.g. when running unit tests
+
+ string company = null;
+ object[] companyAttributes = assembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), true);
+ if ((companyAttributes != null) && (companyAttributes.Length >= 1)) {
+ company = ((AssemblyCompanyAttribute)companyAttributes[companyAttributes.Length - 1]).Company;
+ }
+
+ string product = null;
+ object[] productAttributes = assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), true);
+ if ((productAttributes != null) && (productAttributes.Length >= 1)) {
+ product = ((AssemblyProductAttribute)productAttributes[productAttributes.Length - 1]).Product;
+ } else {
+ object[] titleAttributes = assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), true);
+ if ((titleAttributes != null) && (titleAttributes.Length >= 1)) {
+ product = ((AssemblyTitleAttribute)titleAttributes[titleAttributes.Length - 1]).Title;
+ } else {
+ product = assembly.GetName().Name;
+ }
+ }
+
+ string basePath = "Software";
+ if (!string.IsNullOrEmpty(company)) { basePath += "\\" + company; }
+ if (!string.IsNullOrEmpty(product)) { basePath += "\\" + product; }
+
+ this.SubkeyPath = basePath + "\\RecentFiles";
+
+ this.MaximumCount = maximumCount;
+ if (string.IsNullOrEmpty(groupName)) {
+ this.GroupName = "Default";
+ } else {
+ this.GroupName = groupName;
+ }
+
+ this.Load();
+ }
+
+ /// <summary>
+ /// Gets/sets whether settings should be written to registry.
+ /// </summary>
+ public static bool NoRegistryWrites { get; set; }
+
+ /// <summary>
+ /// Gets maximum number of file names to be saved.
+ /// </summary>
+ public int MaximumCount { get; private set; }
+
+ /// <summary>
+ /// Gets number of file names.
+ /// </summary>
+ public int Count {
+ get { return this._items.Count; }
+ }
+
+ /// <summary>
+ /// Group name.
+ /// </summary>
+ public string GroupName { get; private set; }
+
+
+ private List<RecentFile> _items = new List<RecentFile>();
+
+ /// <summary>
+ /// Gets file name at given index.
+ /// </summary>
+ /// <param name="index">Index.</param>
+ public RecentFile this[int index] {
+ get { return this._items[index]; }
+ }
+
+ /// <summary>
+ /// Returns read-only collection of recent files.
+ /// </summary>
+ [Obsolete("Use Items property instead.")]
+ public ReadOnlyCollection<RecentFile> AsReadOnly() {
+ return this._items.AsReadOnly();
+ }
+
+ /// <summary>
+ /// Returns each recent file.
+ /// </summary>
+ public IEnumerable<RecentFile> Items {
+ get {
+ foreach (var item in this._items) {
+ yield return item;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Inserts file name on top of list if one does not exist or moves it to top if one does exist.
+ /// All changes are immediately saved.
+ /// </summary>
+ /// <param name="fileName">File name.</param>
+ public void Push(string fileName) {
+ var item = RecentFile.GetRecentFile(fileName);
+ if (item != null) {
+ _items.Insert(0, item);
+
+ for (int i = _items.Count - 1; i >= 1; --i) { //remove duplicate of it
+ if (_items[i].Equals(fileName)) {
+ this._items.RemoveAt(i);
+ }
+ }
+
+ this.Save();
+ }
+ }
+
+ /// <summary>
+ /// Removes all occurrances of given file.
+ /// All changes are immediately saved.
+ /// </summary>
+ /// <param name="fileName">File name.</param>
+ public void Remove(string fileName) {
+ for (int i = _items.Count - 1; i >= 0; --i) {
+ if (_items[i].Equals(fileName)) {
+ this._items.RemoveAt(i);
+ }
+ }
+ this.Save();
+ }
+
+ /// <summary>
+ /// Removes all files from list.
+ /// All changes are immediately saved.
+ /// </summary>
+ public void Clear() {
+ this._items.Clear();
+ this.Save();
+ }
+
+
+ /// <summary>
+ /// Reloads file list from registry.
+ /// </summary>
+ public void Load() {
+ this._items.Clear();
+ try {
+ using (var rk = Registry.CurrentUser.OpenSubKey(this.SubkeyPath, false)) {
+ if (rk != null) {
+ object valueCU = rk.GetValue(this.GroupName, null);
+ if (valueCU != null) {
+ var valueKind = RegistryValueKind.MultiString;
+ if (!RecentFiles.IsRunningOnMono) { valueKind = rk.GetValueKind(this.GroupName); }
+ if (valueKind == RegistryValueKind.MultiString) {
+ string[] valueArr = valueCU as string[];
+ if (valueArr != null) {
+ for (int i = 0; i < valueArr.Length; ++i) {
+ if (!string.IsNullOrEmpty(valueArr[i])) {
+ var item = RecentFile.GetRecentFile(valueArr[i]);
+ if (item != null) {
+ _items.Add(item);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch (SecurityException) { }
+ }
+
+ /// <summary>
+ /// Saves current list to registry.
+ /// This is automaticaly done on each insert.
+ /// </summary>
+ public void Save() {
+ if (this._items.Count > this.MaximumCount) { this._items.RemoveRange(this.MaximumCount, this._items.Count - this.MaximumCount); }
+
+ string[] fileNames = new string[this._items.Count];
+ for (int i = 0; i < this._items.Count; ++i) {
+ fileNames[i] = this._items[i].FileName;
+ }
+
+ if (RecentFiles.NoRegistryWrites == false) {
+ using (RegistryKey rk = Registry.CurrentUser.CreateSubKey(this.SubkeyPath)) {
+ rk.SetValue(this.GroupName, fileNames, RegistryValueKind.MultiString);
+ }
+ }
+ }
+
+
+
+ /// <summary>
+ /// Gets/sets subkey used for registry storage.
+ /// </summary>
+ private string SubkeyPath { get; set; }
+
+ private static bool IsRunningOnMono {
+ get {
+ return (Type.GetType("Mono.Runtime") != null);
+ }
+ }
+
+ }
+
+
+
+ /// <summary>
+ /// Single recent file
+ /// </summary>
+ public class RecentFile {
+
+ private RecentFile(string fileName, string title) {
+ this.FileName = fileName;
+ this.Title = title;
+ }
+
+ /// <summary>
+ /// Gets full file name.
+ /// </summary>
+ public string FileName { get; private set; }
+
+ /// <summary>
+ /// Gets title of current file.
+ /// </summary>
+ public string Title { get; private set; }
+
+ /// <summary>
+ /// Determines whether the specified object is equal to the current object.
+ /// </summary>
+ /// <param name="obj">The object to compare with the current object.</param>
+ public override bool Equals(object obj) {
+ var other = obj as RecentFile;
+ if (other != null) {
+ return string.Equals(this.FileName, other.FileName, StringComparison.OrdinalIgnoreCase);
+ }
+ var otherString = obj as string;
+ if (otherString != null) {
+ return string.Equals(this.FileName, otherString, StringComparison.OrdinalIgnoreCase);
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Serves as a hash function for a particular type.
+ /// </summary>
+ public override int GetHashCode() {
+ return this.FileName.GetHashCode();
+ }
+
+ /// <summary>
+ /// Returns a System.String that represents the current object.
+ /// </summary>
+ public override string ToString() {
+ return this.Title;
+ }
+
+
+ #region Static
+
+ /// <summary>
+ /// Gets recent file object or null if such object cannot be found.
+ /// </summary>
+ /// <param name="fileName">File name.</param>
+ internal static RecentFile GetRecentFile(string fileName) {
+ try {
+ var title = HideExtension ? Path.GetFileNameWithoutExtension(fileName) : Path.GetFileName(fileName);
+ return new RecentFile(fileName, title);
+ } catch (ArgumentException) {
+ return null;
+ }
+ }
+
+
+ private static bool HideExtension {
+ get {
+ try {
+ using (RegistryKey rk = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced", false)) {
+ if (rk != null) {
+ var valueKind = IsRunningOnMono ? RegistryValueKind.DWord : rk.GetValueKind("HideFileExt");
+ if (valueKind == RegistryValueKind.DWord) {
+ int hideFileExt = (int)(rk.GetValue("HideFileExt", 1));
+ return (hideFileExt != 0);
+ }
+ }
+ }
+ } catch (SecurityException) {
+ } catch (IOException) { //key does not exist
+ }
+ return false;
+ }
+ }
+
+ private static bool IsRunningOnMono {
+ get {
+ return (Type.GetType("Mono.Runtime") != null);
+ }
+ }
+
+
+ #endregion
+
+ }
+
+}
diff --git a/Source/WrtSettings/(Medo)/State [015].cs b/Source/WrtSettings/(Medo)/State [015].cs
new file mode 100644
index 0000000..4a83c2b
--- /dev/null
+++ b/Source/WrtSettings/(Medo)/State [015].cs
@@ -0,0 +1,537 @@
+//Copyright (c) 2007 Josip Medved <jmedved@jmedved.com>
+
+//2007-11-21: Initial version.
+//2007-12-24: Changed SubKeyPath to include full path.
+//2007-12-27: Added Load overloads for multiple controls.
+// Obsoleted Subscribe method.
+//2008-01-02: Calls to MoveSplitterTo method are now checked.
+//2008-01-05: Removed obsoleted methods.
+//2008-01-10: Moved private methods to Helper class.
+//2008-01-31: Fixed bug that caused only first control to be loaded/saved.
+//2008-02-15: Fixed bug with positioning of centered forms.
+//2008-04-11: Cleaned code to match FxCop 1.36 beta 2 (CompoundWordsShouldBeCasedCorrectly).
+//2008-05-11: Windows with fixed borders are no longer resized.
+//2008-07-10: Fixed resize on load when window is maximized.
+//2008-12-27: Added LoadNowAndSaveOnClose method.
+//2009-07-04: Compatibility with Mono 2.4.
+//2010-10-17: Limited all loaded forms to screen's working area.
+// Changed LoadNowAndSaveOnClose to use SetupOnLoadAndClose.
+//2010-10-31: Added option to skip registry writes (NoRegistryWrites).
+
+
+using System.Windows.Forms;
+using System.Collections.Generic;
+using Microsoft.Win32;
+using System.IO;
+using System;
+using System.Security;
+using System.Globalization;
+using System.Drawing;
+
+namespace Medo.Windows.Forms {
+
+ /// <summary>
+ /// Enables storing and loading of windows control's state.
+ /// It is written in State key at HKEY_CURRENT_USER branch withing SubKeyPath of Settings class.
+ /// This class is not thread-safe since it should be called only from one thread - one that has interface.
+ /// </summary>
+ public static class State {
+
+ private static string _subkeyPath;
+ /// <summary>
+ /// Gets/sets subkey used for registry storage.
+ /// </summary>
+ public static string SubkeyPath {
+ get {
+ if (_subkeyPath == null) {
+ System.Reflection.Assembly assembly = System.Reflection.Assembly.GetEntryAssembly();
+
+ string company = null;
+ object[] companyAttributes = assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyCompanyAttribute), true);
+ if ((companyAttributes != null) && (companyAttributes.Length >= 1)) {
+ company = ((System.Reflection.AssemblyCompanyAttribute)companyAttributes[companyAttributes.Length - 1]).Company;
+ }
+
+ string product = null;
+ object[] productAttributes = assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyProductAttribute), true);
+ if ((productAttributes != null) && (productAttributes.Length >= 1)) {
+ product = ((System.Reflection.AssemblyProductAttribute)productAttributes[productAttributes.Length - 1]).Product;
+ } else {
+ object[] titleAttributes = assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyTitleAttribute), true);
+ if ((titleAttributes != null) && (titleAttributes.Length >= 1)) {
+ product = ((System.Reflection.AssemblyTitleAttribute)titleAttributes[titleAttributes.Length - 1]).Title;
+ } else {
+ product = assembly.GetName().Name;
+ }
+ }
+
+ string path = "Software";
+ if (!string.IsNullOrEmpty(company)) { path += "\\" + company; }
+ if (!string.IsNullOrEmpty(product)) { path += "\\" + product; }
+
+ _subkeyPath = path + "\\State";
+ }
+ return _subkeyPath;
+ }
+ set { _subkeyPath = value; }
+ }
+
+ /// <summary>
+ /// Gets/sets whether settings should be written to registry.
+ /// </summary>
+ public static bool NoRegistryWrites { get; set; }
+
+
+ #region Load Save - All
+
+ /// <summary>
+ /// Loads previous state.
+ /// Supported controls are Form, PropertyGrid, ListView and SplitContainer.
+ /// </summary>
+ /// <param name="form">Form on which's FormClosing handler this function will attach. State will not be altered for this parameter.</param>
+ /// <param name="controls">Controls to load and to save.</param>
+ /// <exception cref="System.ArgumentNullException">Form is null.</exception>
+ /// <exception cref="System.NotSupportedException">This control's parents cannot be resolved using Name property.</exception>
+ /// <exception cref="System.ArgumentException">Form already used.</exception>
+ [Obsolete("Use SetupOnLoadAndClose instead.")]
+ public static void LoadNowAndSaveOnClose(Form form, params Control[] controls) {
+ SetupOnLoadAndClose(form, controls);
+ }
+
+ /// <summary>
+ /// Loads previous state.
+ /// Supported controls are Form, PropertyGrid, ListView and SplitContainer.
+ /// </summary>
+ /// <param name="form">Form on which's FormClosing handler this function will attach. State will not be altered for this parameter.</param>
+ /// <param name="controls">Controls to load and to save.</param>
+ /// <exception cref="System.ArgumentNullException">Form is null.</exception>
+ /// <exception cref="System.NotSupportedException">This control's parents cannot be resolved using Name property.</exception>
+ /// <exception cref="System.ArgumentException">Form setup already done.</exception>
+ public static void SetupOnLoadAndClose(Form form, params Control[] controls) {
+ if (form == null) { throw new ArgumentNullException("form", "Form is null."); }
+
+ if (formSetup.ContainsKey(form)) { throw new System.ArgumentException("Form setup already done.", "form"); }
+
+ Load(form);
+ if (controls != null) { Load(controls); }
+
+ formSetup.Add(form, controls);
+ form.Load += new EventHandler(form_Load);
+ form.FormClosed += new FormClosedEventHandler(form_FormClosed);
+ }
+
+ private static Dictionary<Form, Control[]> formSetup = new Dictionary<Form, Control[]>();
+
+ private static void form_Load(object sender, EventArgs e) {
+ var form = sender as Form;
+ if (formSetup.ContainsKey(form)) {
+ Load(form);
+ Load(formSetup[form]);
+ }
+ }
+
+ private static void form_FormClosed(object sender, FormClosedEventArgs e) {
+ var form = sender as Form;
+ if (formSetup.ContainsKey(form)) {
+ Save(form);
+ Save(formSetup[form]);
+ formSetup.Remove(form);
+ }
+ }
+
+ /// <summary>
+ /// Loads previous state.
+ /// Supported controls are Form, PropertyGrid, ListView and SplitContainer.
+ /// </summary>
+ /// <param name="controls">Controls to load.</param>
+ /// <exception cref="System.NotSupportedException">This control's parents cannot be resolved using Name property.</exception>
+ public static void Load(params System.Windows.Forms.Control[] controls) {
+ if (controls == null) { return; }
+ for (int i = 0; i < controls.Length; ++i) {
+ Form form = controls[i] as Form;
+ if (form != null) {
+ Load(form);
+ continue;
+ }
+
+ PropertyGrid propertyGrid = controls[i] as PropertyGrid;
+ if (propertyGrid != null) {
+ Load(propertyGrid);
+ continue;
+ }
+
+ ListView listView = controls[i] as ListView;
+ if (listView != null) {
+ Load(listView);
+ continue;
+ }
+
+ SplitContainer splitContainer = controls[i] as SplitContainer;
+ if (splitContainer != null) {
+ Load(splitContainer);
+ continue;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Saves control's state.
+ /// Supported controls are Form, PropertyGrid, ListView and SplitContainer.
+ /// </summary>
+ /// <param name="controls">Controls to save.</param>
+ /// <exception cref="System.NotSupportedException">This control's parents cannot be resolved using Name property.</exception>
+ public static void Save(params System.Windows.Forms.Control[] controls) {
+ if (controls == null) { return; }
+ for (int i = 0; i < controls.Length; ++i) {
+ Form form = controls[i] as Form;
+ if (form != null) {
+ Save(form);
+ continue;
+ }
+
+ PropertyGrid propertyGrid = controls[i] as PropertyGrid;
+ if (propertyGrid != null) {
+ Save(propertyGrid);
+ continue;
+ }
+
+ ListView listView = controls[i] as ListView;
+ if (listView != null) {
+ Save(listView);
+ continue;
+ }
+
+ SplitContainer splitContainer = controls[i] as SplitContainer;
+ if (splitContainer != null) {
+ Save(splitContainer);
+ continue;
+ }
+ }
+ }
+
+ #endregion
+
+
+ #region Load Save - Form
+
+ /// <summary>
+ /// Saves Form state (Left,Top,Width,Height,WindowState).
+ /// </summary>
+ /// <param name="form">Form.</param>
+ /// <exception cref="System.ArgumentNullException">Form is null.</exception>
+ /// <exception cref="System.NotSupportedException">This control's parents cannot be resolved using Name property.</exception>
+ public static void Save(System.Windows.Forms.Form form) {
+ if (form == null) { throw new ArgumentNullException("form", "Form is null."); }
+
+ string baseValueName = Helper.GetControlPath(form);
+
+ Helper.Write(baseValueName + ".WindowState", System.Convert.ToInt32(form.WindowState, System.Globalization.CultureInfo.InvariantCulture));
+ if (form.WindowState == System.Windows.Forms.FormWindowState.Normal) {
+ Helper.Write(baseValueName + ".Left", form.Bounds.Left);
+ Helper.Write(baseValueName + ".Top", form.Bounds.Top);
+ Helper.Write(baseValueName + ".Width", form.Bounds.Width);
+ Helper.Write(baseValueName + ".Height", form.Bounds.Height);
+ } else {
+ Helper.Write(baseValueName + ".Left", form.RestoreBounds.Left);
+ Helper.Write(baseValueName + ".Top", form.RestoreBounds.Top);
+ Helper.Write(baseValueName + ".Width", form.RestoreBounds.Width);
+ Helper.Write(baseValueName + ".Height", form.RestoreBounds.Height);
+ }
+ }
+
+ /// <summary>
+ /// Loads previous Form state (Left,Top,Width,Height,WindowState).
+ /// If StartupPosition value is Manual, saved settings are used, for other types, it tryes to resemble original behaviour.
+ /// </summary>
+ /// <param name="form">Form.</param>
+ /// <exception cref="System.ArgumentNullException">Form is null.</exception>
+ /// <exception cref="System.NotSupportedException">This control's parents cannot be resolved using Name property.</exception>
+ public static void Load(System.Windows.Forms.Form form) {
+ if (form == null) { throw new ArgumentNullException("form", "Form is null."); }
+
+ string baseValueName = Helper.GetControlPath(form);
+
+ int currWindowState = System.Convert.ToInt32(form.WindowState, System.Globalization.CultureInfo.InvariantCulture);
+ int currLeft, currTop, currWidth, currHeight;
+ if (form.WindowState == System.Windows.Forms.FormWindowState.Normal) {
+ currLeft = form.Bounds.Left;
+ currTop = form.Bounds.Top;
+ currWidth = form.Bounds.Width;
+ currHeight = form.Bounds.Height;
+ } else {
+ currLeft = form.RestoreBounds.Left;
+ currTop = form.RestoreBounds.Top;
+ currWidth = form.RestoreBounds.Width;
+ currHeight = form.RestoreBounds.Height;
+ }
+
+ int newLeft = Helper.Read(baseValueName + ".Left", currLeft);
+ int newTop = Helper.Read(baseValueName + ".Top", currTop);
+ int newWidth = Helper.Read(baseValueName + ".Width", currWidth);
+ int newHeight = Helper.Read(baseValueName + ".Height", currHeight);
+ int newWindowState = Helper.Read(baseValueName + ".WindowState", currWindowState);
+
+ if ((form.FormBorderStyle == FormBorderStyle.Fixed3D) || (form.FormBorderStyle == FormBorderStyle.FixedDialog) || (form.FormBorderStyle == FormBorderStyle.FixedSingle) || (form.FormBorderStyle == FormBorderStyle.FixedToolWindow)) {
+ newWidth = currWidth;
+ newHeight = currHeight;
+ }
+
+ System.Windows.Forms.Screen screen = System.Windows.Forms.Screen.FromRectangle(new System.Drawing.Rectangle(newLeft, newTop, newWidth, newHeight));
+
+ switch (form.StartPosition) {
+
+ case System.Windows.Forms.FormStartPosition.CenterParent: {
+ if (form.Parent != null) {
+ newLeft = form.Parent.Left + (form.Parent.Width - newWidth) / 2;
+ newTop = form.Parent.Top + (form.Parent.Height - newHeight) / 2;
+ } else if (form.Owner != null) {
+ newLeft = form.Owner.Left + (form.Owner.Width - newWidth) / 2;
+ newTop = form.Owner.Top + (form.Owner.Height - newHeight) / 2;
+ } else {
+ newLeft = screen.WorkingArea.Left + (screen.WorkingArea.Width - newWidth) / 2;
+ newTop = screen.WorkingArea.Top + (screen.WorkingArea.Height - newHeight) / 2;
+ }
+ } break;
+
+ case System.Windows.Forms.FormStartPosition.CenterScreen: {
+ newLeft = screen.WorkingArea.Left + (screen.WorkingArea.Width - newWidth) / 2;
+ newTop = screen.WorkingArea.Top + (screen.WorkingArea.Height - newHeight) / 2;
+ } break;
+
+ }
+
+ if (newWidth <= 0) { newWidth = currWidth; }
+ if (newHeight <= 0) { newHeight = currHeight; }
+ if (newWidth > screen.WorkingArea.Width) { newWidth = screen.WorkingArea.Width; }
+ if (newHeight > screen.WorkingArea.Height) { newHeight = screen.WorkingArea.Height; }
+ if (newLeft + newWidth > screen.WorkingArea.Right) { newLeft = screen.WorkingArea.Left + (screen.WorkingArea.Width - newWidth); }
+ if (newTop + newHeight > screen.WorkingArea.Bottom) { newTop = screen.WorkingArea.Top + (screen.WorkingArea.Height - newHeight); }
+ if (newLeft < screen.WorkingArea.Left) { newLeft = screen.WorkingArea.Left; }
+ if (newTop < screen.WorkingArea.Top) { newTop = screen.WorkingArea.Top; }
+
+ form.Location = new Point(newLeft, newTop);
+ form.Size = new Size(newWidth, newHeight);
+
+ if (newWindowState == System.Convert.ToInt32(System.Windows.Forms.FormWindowState.Maximized, System.Globalization.CultureInfo.InvariantCulture)) {
+ form.WindowState = System.Windows.Forms.FormWindowState.Maximized;
+ } //no need for any code - it is already either in normal state or minimized (will be restored to normal).
+ }
+
+ #endregion
+
+ #region Load Save - PropertyGrid
+
+ /// <summary>
+ /// Loads previous PropertyGrid state (LabelWidth, PropertySort).
+ /// </summary>
+ /// <param name="control">PropertyGrid.</param>
+ /// <exception cref="System.ArgumentNullException">Control is null.</exception>
+ /// <exception cref="System.NotSupportedException">This control's parents cannot be resolved using Name property.</exception>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "PropertyGrid is passed because of reflection upon its member.")]
+ public static void Load(PropertyGrid control) {
+ if (control == null) { throw new ArgumentNullException("control", "Control is null."); }
+
+ string baseValueName = Helper.GetControlPath(control);
+
+ try {
+ control.PropertySort = (System.Windows.Forms.PropertySort)(Helper.Read(baseValueName + ".PropertySort", System.Convert.ToInt32(control.PropertySort, System.Globalization.CultureInfo.InvariantCulture)));
+ } catch (System.ComponentModel.InvalidEnumArgumentException) { }
+
+ System.Reflection.FieldInfo fieldGridView = control.GetType().GetField("gridView", System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ object gridViewObject = fieldGridView.GetValue(control);
+ if (gridViewObject != null) {
+ int currentlabelWidth = 0;
+ System.Reflection.PropertyInfo propertyInternalLabelWidth = gridViewObject.GetType().GetProperty("InternalLabelWidth", System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ if (propertyInternalLabelWidth != null) {
+ object val = propertyInternalLabelWidth.GetValue(gridViewObject, null);
+ if (val is int) {
+ currentlabelWidth = (int)val;
+ }
+ }
+ int labelWidth = Helper.Read(baseValueName + ".LabelWidth", currentlabelWidth);
+ if ((labelWidth > 0) && (labelWidth < control.Width)) {
+ System.Reflection.BindingFlags methodMoveSplitterToFlags = System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance;
+ System.Reflection.MethodInfo methodMoveSplitterTo = gridViewObject.GetType().GetMethod("MoveSplitterTo", methodMoveSplitterToFlags);
+ if (methodMoveSplitterTo != null) {
+ methodMoveSplitterTo.Invoke(gridViewObject, methodMoveSplitterToFlags, null, new object[] { labelWidth }, System.Globalization.CultureInfo.CurrentCulture);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Saves PropertyGrid state (LabelWidth).
+ /// </summary>
+ /// <param name="control">PropertyGrid.</param>
+ /// <exception cref="System.ArgumentNullException">Control is null.</exception>
+ /// <exception cref="System.NotSupportedException">This control's parents cannot be resolved using Name property.</exception>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "PropertyGrid is passed because of reflection upon its member.")]
+ public static void Save(System.Windows.Forms.PropertyGrid control) {
+ if (control == null) { throw new ArgumentNullException("control", "Control is null."); }
+
+ string baseValueName = Helper.GetControlPath(control);
+
+ Helper.Write(baseValueName + ".PropertySort", System.Convert.ToInt32(control.PropertySort, System.Globalization.CultureInfo.InvariantCulture));
+
+ System.Reflection.FieldInfo fieldGridView = control.GetType().GetField("gridView", System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ object gridViewObject = fieldGridView.GetValue(control);
+ if (gridViewObject != null) {
+ System.Reflection.PropertyInfo propertyInternalLabelWidth = gridViewObject.GetType().GetProperty("InternalLabelWidth", System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ if (propertyInternalLabelWidth != null) {
+ object val = propertyInternalLabelWidth.GetValue(gridViewObject, null);
+ if (val is int) {
+ Helper.Write(baseValueName + ".LabelWidth", (int)val);
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region Load Save - ListView
+
+ /// <summary>
+ /// Loads previous ListView state (Column header width).
+ /// </summary>
+ /// <param name="control">ListView.</param>
+ /// <exception cref="System.ArgumentNullException">Control is null.</exception>
+ public static void Load(System.Windows.Forms.ListView control) {
+ if (control == null) { throw new ArgumentNullException("control", "Control is null."); }
+
+ string baseValueName = Helper.GetControlPath(control);
+
+ for (int i = 0; i < control.Columns.Count; i++) {
+ int width = Helper.Read(baseValueName + ".ColumnHeaderWidth[" + i.ToString(System.Globalization.CultureInfo.InvariantCulture) + "]", control.Columns[i].Width);
+ if (width > control.ClientRectangle.Width) { width = control.ClientRectangle.Width; }
+ control.Columns[i].Width = width;
+ }
+ }
+
+ /// <summary>
+ /// Saves ListView state (Column header width).
+ /// </summary>
+ /// <param name="control">ListView.</param>
+ /// <exception cref="System.ArgumentNullException">Control is null.</exception>
+ public static void Save(System.Windows.Forms.ListView control) {
+ if (control == null) { throw new ArgumentNullException("control", "Control is null."); }
+
+ string baseValueName = Helper.GetControlPath(control);
+
+ for (int i = 0; i < control.Columns.Count; i++) {
+ Helper.Write(baseValueName + ".ColumnHeaderWidth[" + i.ToString(System.Globalization.CultureInfo.InvariantCulture) + "]", control.Columns[i].Width);
+ }
+ }
+
+ #endregion
+
+ #region Load Save - SplitContainer
+
+ /// <summary>
+ /// Loads previous SplitContainer state.
+ /// </summary>
+ /// <param name="control">SplitContainer.</param>
+ /// <exception cref="System.ArgumentNullException">Control is null.</exception>
+ public static void Load(SplitContainer control) {
+ if (control == null) { throw new ArgumentNullException("control", "Control is null."); }
+
+ string baseValueName = Helper.GetControlPath(control);
+
+ try {
+ control.Orientation = (System.Windows.Forms.Orientation)(Helper.Read(baseValueName + ".Orientation", System.Convert.ToInt32(control.Orientation, System.Globalization.CultureInfo.InvariantCulture)));
+ } catch (System.ComponentModel.InvalidEnumArgumentException) { }
+ try {
+ int distance = Helper.Read(baseValueName + ".SplitterDistance", control.SplitterDistance);
+ try {
+ control.SplitterDistance = distance;
+ } catch (System.ArgumentOutOfRangeException) { }
+ } catch (System.ComponentModel.InvalidEnumArgumentException) { }
+ }
+
+ /// <summary>
+ /// Saves SplitContainer state.
+ /// </summary>
+ /// <param name="control">SplitContainer.</param>
+ /// <exception cref="System.ArgumentNullException">Control is null.</exception>
+ public static void Save(System.Windows.Forms.SplitContainer control) {
+ if (control == null) { throw new ArgumentNullException("control", "Control is null."); }
+
+ string baseValueName = Helper.GetControlPath(control);
+
+ Helper.Write(baseValueName + ".Orientation", System.Convert.ToInt32(control.Orientation, System.Globalization.CultureInfo.InvariantCulture));
+ Helper.Write(baseValueName + ".SplitterDistance", control.SplitterDistance);
+ }
+
+ #endregion
+
+
+ private static class Helper {
+
+ internal static void Write(string valueName, int value) {
+ if (State.NoRegistryWrites == false) {
+ try {
+ if (State.SubkeyPath.Length == 0) { return; }
+ using (RegistryKey rk = Registry.CurrentUser.CreateSubKey(State.SubkeyPath)) {
+ if (rk != null) {
+ rk.SetValue(valueName, value, RegistryValueKind.DWord);
+ }
+ }
+ } catch (IOException) { //key is deleted.
+ } catch (UnauthorizedAccessException) { } //key is write protected.
+ }
+ }
+
+ internal static int Read(string valueName, int defaultValue) {
+ try {
+ using (RegistryKey rk = Registry.CurrentUser.OpenSubKey(State.SubkeyPath, false)) {
+ if (rk != null) {
+ object value = rk.GetValue(valueName, null);
+ if (value == null) { return defaultValue; }
+ var valueKind = RegistryValueKind.DWord;
+ if (!State.Helper.IsRunningOnMono) { valueKind = rk.GetValueKind(valueName); }
+ if ((value != null) && (valueKind == RegistryValueKind.DWord)) {
+ return (int)value;
+ }
+ }
+ }
+ } catch (SecurityException) { }
+ return defaultValue;
+ }
+
+
+ internal static string GetControlPath(System.Windows.Forms.Control control) {
+ System.Text.StringBuilder sbPath = new System.Text.StringBuilder();
+
+ System.Windows.Forms.Control currControl = control;
+ while (true) {
+ System.Windows.Forms.Control parentControl = currControl.Parent;
+
+ if (parentControl == null) {
+ if (sbPath.Length > 0) { sbPath.Insert(0, "."); }
+ sbPath.Insert(0, currControl.GetType().FullName);
+ break;
+ } else {
+ if (string.IsNullOrEmpty(currControl.Name)) {
+ //throw new System.NotSupportedException("This control's parents cannot be resolved using Name property.");
+ } else {
+ if (sbPath.Length > 0) { sbPath.Insert(0, "."); }
+ sbPath.Insert(0, currControl.Name);
+ }
+ }
+
+ currControl = parentControl;
+ }
+
+ return sbPath.ToString();
+ }
+
+ private static bool IsRunningOnMono {
+ get {
+ return (Type.GetType("Mono.Runtime") != null);
+ }
+ }
+
+ }
+
+ }
+
+}
diff --git a/Source/WrtSettings/(Medo)/UnhandledCatch [008].cs b/Source/WrtSettings/(Medo)/UnhandledCatch [008].cs
new file mode 100644
index 0000000..5bfdbdb
--- /dev/null
+++ b/Source/WrtSettings/(Medo)/UnhandledCatch [008].cs
@@ -0,0 +1,136 @@
+//Copyright (c) 2007 Josip Medved <jmedved@jmedved.com>
+
+//2007-12-30: New version.
+//2008-01-02: Added support for inner exceptions.
+// Added thread-safe locking.
+//2008-01-03: Added Resources.
+//2008-01-06: System.Environment.Exit returns E_UNEXPECTED (0x8000ffff).
+//2008-01-13: Changed default mode to ThrowException.
+// Uses FailFast to exit application.
+//2009-03-31: Changed FailFast to optional in order to avoid WER messages.
+//2010-11-07: Compatible with Mono (ignoring FailFast).
+//2010-11-22: Changed default exception mode to CatchException.
+
+
+using System;
+using System.Threading;
+
+namespace Medo.Application {
+
+ /// <summary>
+ /// Handling of unhandled errors.
+ /// This class is thread-safe.
+ /// </summary>
+ public static class UnhandledCatch {
+
+ /// <summary>
+ /// Occurs when an exception is not caught.
+ /// </summary>
+ public static event EventHandler<ThreadExceptionEventArgs> ThreadException;
+
+ private static readonly object SyncRoot = new object();
+
+ /// <summary>
+ /// Initializes handlers for unhandled exception.
+ /// </summary>
+ public static void Attach() {
+ lock (SyncRoot) {
+ Attach(System.Windows.Forms.UnhandledExceptionMode.CatchException, true);
+ }
+ }
+
+ /// <summary>
+ /// Initializes handlers for unhandled exception.
+ /// </summary>
+ /// <param name="mode">Defines where a Windows Forms application should send unhandled exceptions.</param>
+ public static void Attach(System.Windows.Forms.UnhandledExceptionMode mode) {
+ lock (SyncRoot) {
+ Attach(mode, true);
+ }
+ }
+
+ /// <summary>
+ /// Initializes handlers for unhandled exception.
+ /// </summary>
+ /// <param name="mode">Defines where a Windows Forms application should send unhandled exceptions.</param>
+ /// <param name="threadScope">True to set the thread exception mode.</param>
+ public static void Attach(System.Windows.Forms.UnhandledExceptionMode mode, bool threadScope) {
+ lock (SyncRoot) {
+ System.Windows.Forms.Application.SetUnhandledExceptionMode(mode, threadScope);
+ System.Windows.Forms.Application.ThreadException += Application_ThreadException;
+ System.AppDomain.CurrentDomain.UnhandledException += AppDomain_UnhandledException;
+ }
+ }
+
+ /// <summary>
+ /// Initializes handlers for unhandled exception.
+ /// </summary>
+ /// <param name="mode">Defines where a Windows Forms application should send unhandled exceptions.</param>
+ /// <param name="threadScope">True to set the thread exception mode.</param>
+ /// <param name="useFailFast">When true, FailFast will be used to stop application. If false, standard Environment.Exit will be used instead.</param>
+ [Obsolete("Use UseFailFast property instead.")]
+ public static void Attach(System.Windows.Forms.UnhandledExceptionMode mode, bool threadScope, bool useFailFast) {
+ lock (SyncRoot) {
+ UnhandledCatch.UseFailFast = useFailFast;
+ System.Windows.Forms.Application.SetUnhandledExceptionMode(mode, threadScope);
+ System.Windows.Forms.Application.ThreadException += Application_ThreadException;
+ System.AppDomain.CurrentDomain.UnhandledException += AppDomain_UnhandledException;
+ }
+ }
+
+ private static bool _useFailFast;
+ /// <summary>
+ /// Gets/sets whether to use FailFast terminating application.
+ /// Under Mono this property always remains false.
+ /// </summary>
+ public static bool UseFailFast {
+ get { lock (SyncRoot) { return _useFailFast; } }
+ set { lock (SyncRoot) { _useFailFast = value && !IsRunningOnMono; } }
+ }
+
+ private static void AppDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e) {
+ lock (SyncRoot) {
+ Process(e.ExceptionObject as System.Exception);
+ }
+ }
+
+ private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) {
+ lock (SyncRoot) {
+ Process(e.Exception);
+ }
+ }
+
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "General exceptions are catched on purpose because this is handler for unhandled exceptions.")]
+ private static void Process(System.Exception exception) {
+ lock (SyncRoot) {
+ System.Environment.ExitCode = unchecked((int)0x8000ffff); //E_UNEXPECTED(0x8000ffff)
+
+ if (exception != null) {
+ System.Diagnostics.Trace.TraceError(exception.ToString() + " {Medo.Application.UnhandledCatch}");
+
+ System.Windows.Forms.Application.ThreadException -= Application_ThreadException;
+ System.AppDomain.CurrentDomain.UnhandledException -= AppDomain_UnhandledException;
+
+ if (ThreadException != null) { ThreadException(null, new ThreadExceptionEventArgs(exception)); }
+ }
+
+ System.Diagnostics.Trace.TraceError("Exit(E_UNEXPECTED): Unhandled exception has occurred. {Medo.Application.UnhandledCatch}");
+
+ if (UnhandledCatch.UseFailFast) {
+ System.Environment.FailFast(exception.Message);
+ } else {
+ System.Environment.Exit(unchecked((int)0x8000ffff)); //E_UNEXPECTED(0x8000ffff)
+ }
+ }
+ }
+
+ private static bool IsRunningOnMono {
+ get {
+ return (Type.GetType("Mono.Runtime") != null);
+ }
+ }
+
+ }
+
+}
diff --git a/Source/WrtSettings/(Medo)/Upgrade [003].cs b/Source/WrtSettings/(Medo)/Upgrade [003].cs
new file mode 100644
index 0000000..ae4e573
--- /dev/null
+++ b/Source/WrtSettings/(Medo)/Upgrade [003].cs
@@ -0,0 +1,461 @@
+//Copyright (c) 2012 Josip Medved <jmedved@jmedved.com>
+
+//2012-03-05: Initial version.
+//2012-03-13: UI adjustments.
+//2013-12-28: Message box adjustments.
+
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Drawing;
+using System.Globalization;
+using System.IO;
+using System.Net;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Windows.Forms;
+
+namespace Medo.Services {
+
+ /// <summary>
+ /// Handles upgrade procedures.
+ /// </summary>
+ public static class Upgrade {
+
+ /// <summary>
+ /// Returns upgrade file if there is one or null if there is no upgrade.
+ /// </summary>
+ /// <param name="serviceUri">Service URI (e.g. http://jmedved.com/upgrade/).</param>
+ /// <exception cref="System.ArgumentNullException">Argument cannot be null (serviceUri).</exception>
+ /// <exception cref="System.InvalidOperationException">Unexpected answer from upgrade server. -or- Cannot contact upgrade server.</exception>
+ public static UpgradeFile GetUpgradeFile(Uri serviceUri) {
+ return GetUpgradeFile(serviceUri, Assembly.GetEntryAssembly());
+ }
+
+ /// <summary>
+ /// Returns upgrade file if there is one or null if there is no upgrade.
+ /// </summary>
+ /// <param name="serviceUri">Service URI (e.g. http://jmedved.com/upgrade/).</param>
+ /// <param name="assembly">Assembly.</param>
+ /// <exception cref="System.ArgumentNullException">Argument cannot be null (serviceUri).</exception>
+ /// <exception cref="System.InvalidOperationException">Unexpected answer from upgrade server. -or- Cannot contact upgrade server.</exception>
+ public static UpgradeFile GetUpgradeFile(Uri serviceUri, Assembly assembly) {
+ if (serviceUri == null) { throw new ArgumentNullException("serviceUri", "Argument cannot be null."); }
+ if (assembly == null) { assembly = Assembly.GetEntryAssembly(); }
+ var url = new StringBuilder();
+ url.Append(serviceUri.AbsoluteUri.EndsWith("/", StringComparison.OrdinalIgnoreCase) ? serviceUri.AbsoluteUri : serviceUri.AbsoluteUri + "/");
+ foreach (var ch in GetProduct(assembly)) {
+ if (char.IsLetterOrDigit(ch)) {
+ url.Append(char.ToLowerInvariant(ch));
+ }
+ }
+ url.Append("/");
+ url.Append(assembly.GetName().Version.ToString());
+ url.Append("/");
+ return GetUpgradeFileFromURL(url.ToString());
+ }
+
+
+ /// <summary>
+ /// Shows Upgrade dialog.
+ /// </summary>
+ /// <param name="owner">Shows the form as a modal dialog box with the specified owner.</param>
+ /// <param name="serviceUri">Service URI (e.g. http://jmedved.com/upgrade/).</param>
+ /// <exception cref="System.ArgumentNullException">Argument cannot be null (serviceUri).</exception>
+ public static DialogResult ShowDialog(IWin32Window owner, Uri serviceUri) {
+ return ShowDialog(owner, serviceUri, Assembly.GetEntryAssembly());
+ }
+
+ /// <summary>
+ /// Shows Upgrade dialog.
+ /// </summary>
+ /// <param name="owner">Shows the form as a modal dialog box with the specified owner.</param>
+ /// <param name="serviceUri">Service URI (e.g. http://jmedved.com/upgrade/).</param>
+ /// <param name="assembly">Assembly.</param>
+ /// <exception cref="System.ArgumentNullException">Argument cannot be null (serviceUri).</exception>
+ public static DialogResult ShowDialog(IWin32Window owner, Uri serviceUri, Assembly assembly) {
+ if (serviceUri == null) { throw new ArgumentNullException("serviceUri", "Argument cannot be null."); }
+ if (assembly == null) { assembly = Assembly.GetEntryAssembly(); }
+ using (var frm = new UpgradeForm(serviceUri, assembly)) {
+ if (owner != null) {
+ frm.ShowInTaskbar = false;
+ frm.StartPosition = FormStartPosition.CenterParent;
+ return frm.ShowDialog(owner);
+ } else {
+ frm.ShowInTaskbar = true;
+ frm.StartPosition = FormStartPosition.CenterScreen;
+ return frm.ShowDialog();
+ }
+ }
+ }
+
+
+ private static UpgradeFile GetUpgradeFileFromURL(string url) {
+ try {
+ HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
+ request.AllowAutoRedirect = false;
+ request.Method = "HEAD";
+ request.Proxy = HttpWebRequest.DefaultWebProxy;
+ request.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
+ using (var response = (HttpWebResponse)request.GetResponse()) {
+ switch (response.StatusCode) {
+ case HttpStatusCode.Gone: return null; //no upgrade
+ case HttpStatusCode.Forbidden: return null; //no upgrade (old code)
+ case HttpStatusCode.SeeOther: return new UpgradeFile(new Uri(response.Headers["Location"])); //upgrade at Location
+ default: throw new InvalidOperationException("Unexpected answer from upgrade server (" + response.StatusCode.ToString() + " " + response.StatusDescription + ").");
+ }
+ }
+ } catch (InvalidOperationException ex) {
+ var webEx = ex as WebException;
+ if (webEx != null) {
+ var response = webEx.Response as HttpWebResponse;
+ if (response != null) {
+ switch (response.StatusCode) {
+ case HttpStatusCode.Gone: return null; //no upgrade
+ case HttpStatusCode.Forbidden: return null; //no upgrade (old code)
+ case HttpStatusCode.SeeOther: return new UpgradeFile(new Uri(response.Headers["Location"])); //upgrade at Location
+ default: throw new InvalidOperationException("Unexpected answer from upgrade server (" + response.StatusCode.ToString() + " " + response.StatusDescription + ").", ex);
+ }
+ }
+ }
+ throw;
+ } catch (Exception ex) {
+ throw new InvalidOperationException("Cannot contact upgrade server.", ex);
+ }
+ }
+
+ private static string GetProduct(Assembly assembly) {
+ object[] productAttributes = assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), true);
+ if ((productAttributes != null) && (productAttributes.Length >= 1)) {
+ return ((AssemblyProductAttribute)productAttributes[productAttributes.Length - 1]).Product;
+ } else {
+ object[] titleAttributes = assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), true);
+ if ((titleAttributes != null) && (titleAttributes.Length >= 1)) {
+ return ((AssemblyTitleAttribute)titleAttributes[titleAttributes.Length - 1]).Title;
+ } else {
+ return assembly.GetName().Name;
+ }
+ }
+ }
+
+
+ #region Form
+
+ private class UpgradeForm : Form {
+
+ private readonly Uri ServiceUri;
+ private readonly Assembly Assembly;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "All controls are disposed in Form's Dispose method.")]
+ internal UpgradeForm(Uri serviceUri, Assembly assembly) {
+ this.ServiceUri = serviceUri;
+ this.Assembly = assembly;
+
+ this.CancelButton = btnCancel;
+ this.AutoSize = true;
+ this.AutoScaleMode = AutoScaleMode.Font;
+ this.Font = SystemFonts.MessageBoxFont;
+ this.FormBorderStyle = FormBorderStyle.FixedDialog;
+ this.MinimizeBox = false;
+ this.MaximizeBox = false;
+ this.ShowIcon = false;
+ this.Text = string.Format(CultureInfo.CurrentUICulture, Resources.Caption, GetProduct(assembly));
+
+ this.FormClosing += new FormClosingEventHandler(Form_FormClosing);
+
+ this.Controls.Add(prgProgress);
+ this.Controls.Add(lblStatus);
+ this.Controls.Add(btnUpgrade);
+ this.Controls.Add(btnDownload);
+ this.Controls.Add(btnCancel);
+
+ var width = 7 + btnUpgrade.Width + 7 + btnDownload.Width + 5 * 7 + btnCancel.Width + 7;
+ var height = 7 + prgProgress.Height + 7 + lblStatus.Height + 21 + btnCancel.Height + 7;
+ this.ClientSize = new Size(width, height);
+
+ prgProgress.Location = new Point(7, 7);
+ prgProgress.Width = width - 14;
+ lblStatus.Location = new Point(7, prgProgress.Bottom + 7);
+
+ btnUpgrade.Location = new Point(7, this.ClientSize.Height - btnUpgrade.Height - 7);
+ btnDownload.Location = new Point(btnUpgrade.Right + 7, this.ClientSize.Height - btnDownload.Height - 7);
+ btnCancel.Location = new Point(this.ClientSize.Width - btnCancel.Width - 7, this.ClientSize.Height - btnCancel.Height - 7);
+
+
+ bwCheck.DoWork += new DoWorkEventHandler(bwCheck_DoWork);
+ bwCheck.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwCheck_RunWorkerCompleted);
+ bwCheck.RunWorkerAsync();
+
+ btnUpgrade.Click += new EventHandler(btnUpgrade_Click);
+ btnDownload.Click += new EventHandler(btnDownload_Click);
+
+ bwDownload.DoWork += new DoWorkEventHandler(bwDownload_DoWork);
+ bwDownload.ProgressChanged += new ProgressChangedEventHandler(bwDownload_ProgressChanged);
+ bwDownload.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwDownload_RunWorkerCompleted);
+ }
+
+
+ void Form_FormClosing(object sender, FormClosingEventArgs e) {
+ prgProgress.Style = ProgressBarStyle.Continuous;
+ prgProgress.Value = 0;
+ lblStatus.Text = Resources.StatusCancelling;
+ btnCancel.Enabled = false;
+ if (this.bwCheck.IsBusy) { this.bwCheck.CancelAsync(); }
+ if (this.bwDownload.IsBusy) { this.bwDownload.CancelAsync(); }
+ e.Cancel = (this.bwCheck.IsBusy) || (this.bwDownload.IsBusy);
+ }
+
+
+ ProgressBar prgProgress = new ProgressBar() { Height = (int)(SystemInformation.HorizontalScrollBarHeight * 1.5), Style = ProgressBarStyle.Marquee };
+ Label lblStatus = new Label() { AutoSize = true, Text = Resources.StatusChecking };
+ Button btnCancel = new Button() { AutoSize = true, DialogResult = DialogResult.Cancel, Padding = new Padding(3, 1, 3, 1), Text = Resources.Cancel };
+ Button btnUpgrade = new Button() { AutoSize = true, Padding = new Padding(3, 1, 3, 1), Text = Resources.Upgrade, Visible = false };
+ Button btnDownload = new Button() { AutoSize = true, Padding = new Padding(3, 1, 3, 1), Text = Resources.Download, Visible = false };
+
+ BackgroundWorker bwCheck = new BackgroundWorker() { WorkerSupportsCancellation = true };
+ BackgroundWorker bwDownload = new BackgroundWorker() { WorkerSupportsCancellation = true, WorkerReportsProgress = true };
+
+ UpgradeFile UpgradeFile = null;
+
+
+ void bwCheck_DoWork(object sender, DoWorkEventArgs e) {
+ e.Result = Medo.Services.Upgrade.GetUpgradeFile(this.ServiceUri, this.Assembly);
+ e.Cancel = bwCheck.CancellationPending;
+ }
+
+ void bwCheck_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
+ if (e.Cancelled == false) {
+ if (e.Error == null) {
+ this.UpgradeFile = e.Result as UpgradeFile;
+ prgProgress.Style = ProgressBarStyle.Continuous;
+ if (this.UpgradeFile != null) {
+ lblStatus.Text = Resources.StatusUpgradeIsAvailable;
+ btnUpgrade.Visible = true;
+ btnDownload.Visible = true;
+ btnUpgrade.Focus();
+ } else {
+ lblStatus.Text = Resources.StatusUpgradeIsNotAvailable;
+ btnCancel.DialogResult = DialogResult.OK;
+ btnCancel.Text = Resources.Close;
+ }
+ } else {
+ System.Windows.Forms.MessageBox.Show(this, Resources.ErrorCannotCheck + "\n\n" + e.Error.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
+ this.DialogResult = DialogResult.Cancel;
+ }
+ } else {
+ this.DialogResult = DialogResult.Cancel;
+ }
+ }
+
+ void btnUpgrade_Click(object sender, EventArgs e) {
+ lblStatus.Text = Resources.StatusDownloading;
+ btnUpgrade.Enabled = false;
+ btnDownload.Visible = false;
+ bwDownload.RunWorkerAsync();
+ }
+
+ void btnDownload_Click(object sender, EventArgs e) {
+ lblStatus.Text = Resources.StatusDownloading;
+ btnUpgrade.Visible = false;
+ btnDownload.Enabled = false;
+ bwDownload.RunWorkerAsync();
+ }
+
+
+ void bwDownload_DoWork(object sender, DoWorkEventArgs e) {
+ var buffer = new byte[1024];
+ using (var stream = this.UpgradeFile.GetStream()) {
+ stream.ReadTimeout = 5000;
+ using (var bytes = new MemoryStream(1024 * 1024)) {
+ while (bwDownload.CancellationPending == false) {
+ bwDownload.ReportProgress((int)(bytes.Length * 100 / this.UpgradeFile.StreamLength));
+ var read = stream.Read(buffer, 0, buffer.Length);
+ if (read > 0) {
+ bytes.Write(buffer, 0, read);
+ } else {
+ break;
+ }
+ }
+ if (bwDownload.CancellationPending) {
+ e.Cancel = true;
+ } else {
+ if (bytes.Length != this.UpgradeFile.StreamLength) { throw new InvalidOperationException("Content length mismatch."); }
+ e.Result = bytes.ToArray();
+ }
+ }
+ }
+ }
+
+ void bwDownload_ProgressChanged(object sender, ProgressChangedEventArgs e) {
+ prgProgress.Value = e.ProgressPercentage;
+ var text = e.UserState as string;
+ if (text != null) { lblStatus.Text = text; }
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "SaveFileDialog is disposed in using.")]
+ void bwDownload_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
+ if (e.Cancelled == false) {
+ if (e.Error == null) {
+ this.btnCancel.Enabled = false;
+ var bytes = e.Result as byte[];
+ var isUpgrade = btnUpgrade.Visible;
+ if (isUpgrade) {
+ try {
+ var fileName = Path.Combine(Path.GetTempPath(), this.UpgradeFile.FileName);
+ File.WriteAllBytes(fileName, bytes);
+ Process.Start(fileName);
+ System.Windows.Forms.Application.Exit();
+ this.DialogResult = DialogResult.OK;
+ } catch (Win32Exception ex) {
+ System.Windows.Forms.MessageBox.Show(this, Resources.ErrorCannotUpgrade + "\n\n" + ex.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
+ this.DialogResult = DialogResult.Cancel;
+ }
+ } else {
+ var filter = string.Format(CultureInfo.CurrentUICulture, Resources.Filter, new FileInfo(this.UpgradeFile.FileName).Extension);
+ using (var frm = new SaveFileDialog() { AddExtension = false, CheckPathExists = true, FileName = this.UpgradeFile.FileName, InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop), Filter = filter }) {
+ if (frm.ShowDialog(this) == DialogResult.OK) {
+ File.WriteAllBytes(frm.FileName, bytes);
+ this.DialogResult = DialogResult.OK;
+ } else {
+ this.DialogResult = DialogResult.Cancel;
+ }
+ }
+ }
+ } else {
+ System.Windows.Forms.MessageBox.Show(this, Resources.ErrorCannotDownload + "\n\n" + e.Error.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
+ this.DialogResult = DialogResult.Cancel;
+ }
+ } else {
+ this.DialogResult = DialogResult.Cancel;
+ }
+ }
+
+
+ protected override void Dispose(bool disposing) {
+ if (disposing) {
+ foreach (Control iControl in this.Controls) {
+ iControl.Dispose();
+ }
+ this.bwCheck.Dispose();
+ this.bwDownload.Dispose();
+ this.Controls.Clear();
+ }
+ base.Dispose(disposing);
+ }
+
+ }
+
+ #endregion
+
+
+
+ private static class Resources {
+
+ internal static string Caption { get { return GetInCurrentLanguage("Upgrade {0}", "{0} nadogradnja"); } }
+ internal static string Cancel { get { return GetInCurrentLanguage("Cancel", "Odustani"); } }
+ internal static string Close { get { return GetInCurrentLanguage("Close", "Zatvori"); } }
+ internal static string Upgrade { get { return GetInCurrentLanguage("Upgrade", "Nadogradi"); } }
+ internal static string Download { get { return GetInCurrentLanguage("Download", "Preuzmi"); } }
+ internal static string ErrorCannotCheck { get { return GetInCurrentLanguage("Cannot check for upgrade.", "Nije mogue provjeriti nadogradnju."); } }
+ internal static string ErrorCannotUpgrade { get { return GetInCurrentLanguage("Cannot upgrade.", "Nadogradnja nije mogua."); } }
+ internal static string ErrorCannotDownload { get { return GetInCurrentLanguage("Cannot download upgrade.", "Nije mogue preuzeti nadogradnju."); } }
+ internal static string StatusChecking { get { return GetInCurrentLanguage("Checking for upgrade...", "Provjera nadogradnje u tijeku..."); } }
+ internal static string StatusCancelling { get { return GetInCurrentLanguage("Cancelling...", "Odustajanje u tijeku..."); } }
+ internal static string StatusDownloading { get { return GetInCurrentLanguage("Download in progress...", "Preuzimanje u tijeku..."); } }
+ internal static string StatusUpgradeIsAvailable { get { return GetInCurrentLanguage("Upgrade is available.", "Nadogradnja je dostupna."); } }
+ internal static string StatusUpgradeIsNotAvailable { get { return GetInCurrentLanguage("Upgrade is not available.", "Nadogradnja nije dostupna."); } }
+ internal static string Filter { get { return GetInCurrentLanguage("Download|*{0}|All files|*.*", "Preuzimanje|*{0}|Sve datoteke|*.*"); } }
+
+
+ private static string GetInCurrentLanguage(string en_US, string hr_HR) {
+ switch (Thread.CurrentThread.CurrentUICulture.Name.ToUpperInvariant()) {
+ case "EN":
+ case "EN-US":
+ case "EN-GB":
+ return en_US;
+
+ case "HR":
+ case "HR-HR":
+ case "HR-BA":
+ return hr_HR;
+
+ default:
+ return en_US;
+ }
+ }
+
+ }
+
+ }
+
+
+
+
+ /// <summary>
+ /// Handles upgrade file operations.
+ /// </summary>
+ public sealed class UpgradeFile {
+
+ internal UpgradeFile(Uri uri) {
+ this.Uri = uri;
+ }
+
+
+ /// <summary>
+ /// Gets upgrade URI.
+ /// </summary>
+ public Uri Uri { get; private set; }
+
+ /// <summary>
+ /// Gets file name.
+ /// </summary>
+ public string FileName { get { return this.Uri.Segments[this.Uri.Segments.Length - 1]; } }
+
+ /// <summary>
+ /// Returns content stream.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Method is appropriate since it can produce side effects and each call produces different result.")]
+ public Stream GetStream() {
+ var request = (HttpWebRequest)HttpWebRequest.Create(this.Uri);
+ request.AllowAutoRedirect = true;
+ request.Method = "GET";
+ request.Proxy = HttpWebRequest.DefaultWebProxy;
+ request.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
+ var response = (HttpWebResponse)request.GetResponse();
+ this.StreamLength = response.ContentLength;
+ return response.GetResponseStream();
+ }
+
+ /// <summary>
+ /// Gets length of stream.
+ /// Valid only once stream has been read.
+ /// </summary>
+ internal long? StreamLength { get; private set; }
+
+ /// <summary>
+ /// Retrieves whole file.
+ /// </summary>
+ /// <exception cref="System.InvalidOperationException">Content length mismatch.</exception>
+ public byte[] GetBytes() {
+ var buffer = new byte[1024];
+ using (var stream = this.GetStream()) {
+ stream.ReadTimeout = 5000;
+ using (var bytes = new MemoryStream(1024 * 1024)) {
+ while (true) {
+ var read = stream.Read(buffer, 0, buffer.Length);
+ if (read > 0) {
+ bytes.Write(buffer, 0, read);
+ } else {
+ break;
+ }
+ }
+ if (bytes.Length != this.StreamLength) { throw new InvalidOperationException("Content length mismatch."); }
+ return bytes.ToArray();
+ }
+ }
+ }
+
+ }
+}
diff --git a/Source/WrtSettings/App.cs b/Source/WrtSettings/App.cs
new file mode 100644
index 0000000..21442bd
--- /dev/null
+++ b/Source/WrtSettings/App.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Security.AccessControl;
+using System.Security.Principal;
+using System.Threading;
+using System.Windows.Forms;
+
+namespace WrtSettings {
+
+ internal static class App {
+
+ [STAThread]
+ static void Main() {
+ bool createdNew;
+ var mutexSecurity = new MutexSecurity();
+ mutexSecurity.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow));
+ using (var setupMutex = new Mutex(false, @"Global\JosipMedved_WrtSettings", out createdNew, mutexSecurity)) {
+ System.Windows.Forms.Application.EnableVisualStyles();
+ System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
+
+ Medo.Application.UnhandledCatch.ThreadException += new EventHandler<ThreadExceptionEventArgs>(UnhandledCatch_ThreadException);
+ Medo.Application.UnhandledCatch.Attach();
+
+ Application.Run(new MainForm());
+ }
+ }
+
+
+
+ private static void UnhandledCatch_ThreadException(object sender, ThreadExceptionEventArgs e) {
+#if !DEBUG
+ Medo.Diagnostics.ErrorReport.ShowDialog(null, e.Exception, new Uri("http://jmedved.com/feedback/"));
+#else
+ throw e.Exception;
+#endif
+ }
+
+ }
+}
diff --git a/Source/WrtSettings/MainForm.Designer.cs b/Source/WrtSettings/MainForm.Designer.cs
new file mode 100644
index 0000000..8c04095
--- /dev/null
+++ b/Source/WrtSettings/MainForm.Designer.cs
@@ -0,0 +1,252 @@
+namespace WrtSettings {
+ partial class MainForm {
+ /// <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() {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
+ this.mnu = new System.Windows.Forms.ToolStrip();
+ this.mnuOpenRoot = new System.Windows.Forms.ToolStripSplitButton();
+ this.mnuOpen = new System.Windows.Forms.ToolStripMenuItem();
+ this.mnuOpenRecentSeparator = new System.Windows.Forms.ToolStripSeparator();
+ this.mnuSaveRoot = new System.Windows.Forms.ToolStripSplitButton();
+ this.mnuSave = new System.Windows.Forms.ToolStripMenuItem();
+ this.mnuSaveAs = new System.Windows.Forms.ToolStripMenuItem();
+ this.mnuApp = new System.Windows.Forms.ToolStripDropDownButton();
+ this.mnuAppFeedback = new System.Windows.Forms.ToolStripMenuItem();
+ this.mnuAppUpgrade = new System.Windows.Forms.ToolStripMenuItem();
+ this.mnuAppDonate = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator();
+ this.mnuAppAbout = new System.Windows.Forms.ToolStripMenuItem();
+ this.grid = new System.Windows.Forms.DataGridView();
+ this.grid_colKey = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.grid_colValue = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.mnu.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.grid)).BeginInit();
+ this.SuspendLayout();
+ //
+ // mnu
+ //
+ this.mnu.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
+ this.mnu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.mnuOpenRoot,
+ this.mnuSaveRoot,
+ this.mnuApp});
+ this.mnu.Location = new System.Drawing.Point(0, 0);
+ this.mnu.Name = "mnu";
+ this.mnu.Padding = new System.Windows.Forms.Padding(1, 0, 1, 0);
+ this.mnu.Size = new System.Drawing.Size(442, 25);
+ this.mnu.TabIndex = 0;
+ //
+ // mnuOpenRoot
+ //
+ this.mnuOpenRoot.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.mnuOpenRoot.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.mnuOpen,
+ this.mnuOpenRecentSeparator});
+ this.mnuOpenRoot.Image = ((System.Drawing.Image)(resources.GetObject("mnuOpenRoot.Image")));
+ this.mnuOpenRoot.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.mnuOpenRoot.Name = "mnuOpenRoot";
+ this.mnuOpenRoot.Size = new System.Drawing.Size(32, 22);
+ this.mnuOpenRoot.Text = "Open";
+ this.mnuOpenRoot.ToolTipText = "Open (Ctrl+O)";
+ this.mnuOpenRoot.ButtonClick += new System.EventHandler(this.mnuOpen_Click);
+ this.mnuOpenRoot.DropDownOpening += new System.EventHandler(this.mnuOpenRoot_DropDownOpening);
+ //
+ // mnuOpen
+ //
+ this.mnuOpen.Name = "mnuOpen";
+ this.mnuOpen.ShortcutKeyDisplayString = "Ctrl+O";
+ this.mnuOpen.Size = new System.Drawing.Size(167, 24);
+ this.mnuOpen.Text = "&Open";
+ this.mnuOpen.Click += new System.EventHandler(this.mnuOpen_Click);
+ //
+ // mnuOpenRecentSeparator
+ //
+ this.mnuOpenRecentSeparator.Name = "mnuOpenRecentSeparator";
+ this.mnuOpenRecentSeparator.Size = new System.Drawing.Size(164, 6);
+ //
+ // mnuSaveRoot
+ //
+ this.mnuSaveRoot.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.mnuSaveRoot.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.mnuSave,
+ this.mnuSaveAs});
+ this.mnuSaveRoot.Enabled = false;
+ this.mnuSaveRoot.Image = ((System.Drawing.Image)(resources.GetObject("mnuSaveRoot.Image")));
+ this.mnuSaveRoot.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.mnuSaveRoot.Name = "mnuSaveRoot";
+ this.mnuSaveRoot.Size = new System.Drawing.Size(32, 22);
+ this.mnuSaveRoot.Text = "Save";
+ this.mnuSaveRoot.ToolTipText = "Save (Ctrl+S)";
+ this.mnuSaveRoot.ButtonClick += new System.EventHandler(this.mnuSave_Click);
+ //
+ // mnuSave
+ //
+ this.mnuSave.Name = "mnuSave";
+ this.mnuSave.ShortcutKeyDisplayString = "Ctrl+S";
+ this.mnuSave.Size = new System.Drawing.Size(159, 24);
+ this.mnuSave.Text = "&Save";
+ this.mnuSave.Click += new System.EventHandler(this.mnuSave_Click);
+ //
+ // mnuSaveAs
+ //
+ this.mnuSaveAs.Name = "mnuSaveAs";
+ this.mnuSaveAs.Size = new System.Drawing.Size(159, 24);
+ this.mnuSaveAs.Text = "Save &as";
+ this.mnuSaveAs.Click += new System.EventHandler(this.mnuSaveAs_Click);
+ //
+ // mnuApp
+ //
+ this.mnuApp.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
+ this.mnuApp.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.mnuApp.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.mnuAppFeedback,
+ this.mnuAppUpgrade,
+ this.mnuAppDonate,
+ this.toolStripMenuItem2,
+ this.mnuAppAbout});
+ this.mnuApp.Image = ((System.Drawing.Image)(resources.GetObject("mnuApp.Image")));
+ this.mnuApp.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.mnuApp.Name = "mnuApp";
+ this.mnuApp.Size = new System.Drawing.Size(29, 22);
+ this.mnuApp.Text = "Application";
+ //
+ // mnuAppFeedback
+ //
+ this.mnuAppFeedback.Name = "mnuAppFeedback";
+ this.mnuAppFeedback.Size = new System.Drawing.Size(200, 24);
+ this.mnuAppFeedback.Text = "Send &feedback";
+ this.mnuAppFeedback.Click += new System.EventHandler(this.mnuAppFeedback_Click);
+ //
+ // mnuAppUpgrade
+ //
+ this.mnuAppUpgrade.Name = "mnuAppUpgrade";
+ this.mnuAppUpgrade.Size = new System.Drawing.Size(200, 24);
+ this.mnuAppUpgrade.Text = "Check for &upgrade";
+ this.mnuAppUpgrade.Click += new System.EventHandler(this.mnuAppUpgrade_Click);
+ //
+ // mnuAppDonate
+ //
+ this.mnuAppDonate.Name = "mnuAppDonate";
+ this.mnuAppDonate.Size = new System.Drawing.Size(200, 24);
+ this.mnuAppDonate.Text = "&Donate";
+ this.mnuAppDonate.Click += new System.EventHandler(this.mnuAppDonate_Click);
+ //
+ // toolStripMenuItem2
+ //
+ this.toolStripMenuItem2.Name = "toolStripMenuItem2";
+ this.toolStripMenuItem2.Size = new System.Drawing.Size(197, 6);
+ //
+ // mnuAppAbout
+ //
+ this.mnuAppAbout.Name = "mnuAppAbout";
+ this.mnuAppAbout.Size = new System.Drawing.Size(200, 24);
+ this.mnuAppAbout.Text = "&About";
+ this.mnuAppAbout.Click += new System.EventHandler(this.mnuAppAbout_Click);
+ //
+ // grid
+ //
+ this.grid.AllowUserToResizeRows = false;
+ this.grid.BackgroundColor = System.Drawing.SystemColors.Control;
+ this.grid.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.grid.ClipboardCopyMode = System.Windows.Forms.DataGridViewClipboardCopyMode.Disable;
+ this.grid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+ this.grid.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+ this.grid_colKey,
+ this.grid_colValue});
+ this.grid.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.grid.Location = new System.Drawing.Point(0, 25);
+ this.grid.Name = "grid";
+ this.grid.RowTemplate.Height = 24;
+ this.grid.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect;
+ this.grid.Size = new System.Drawing.Size(442, 368);
+ this.grid.TabIndex = 1;
+ this.grid.Visible = false;
+ this.grid.CellBeginEdit += new System.Windows.Forms.DataGridViewCellCancelEventHandler(this.grid_CellBeginEdit);
+ this.grid.CellParsing += new System.Windows.Forms.DataGridViewCellParsingEventHandler(this.grid_CellParsing);
+ this.grid.CellValidating += new System.Windows.Forms.DataGridViewCellValidatingEventHandler(this.grid_CellValidating);
+ this.grid.KeyDown += new System.Windows.Forms.KeyEventHandler(this.grid_KeyDown);
+ //
+ // grid_colKey
+ //
+ this.grid_colKey.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
+ this.grid_colKey.Frozen = true;
+ this.grid_colKey.HeaderText = "Key";
+ this.grid_colKey.MinimumWidth = 120;
+ this.grid_colKey.Name = "grid_colKey";
+ this.grid_colKey.Width = 180;
+ //
+ // grid_colValue
+ //
+ this.grid_colValue.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+ this.grid_colValue.DividerWidth = 1;
+ this.grid_colValue.HeaderText = "Value";
+ this.grid_colValue.MinimumWidth = 60;
+ this.grid_colValue.Name = "grid_colValue";
+ this.grid_colValue.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+ //
+ // MainForm
+ //
+ this.AllowDrop = true;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(442, 393);
+ this.Controls.Add(this.grid);
+ this.Controls.Add(this.mnu);
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.KeyPreview = true;
+ this.MinimumSize = new System.Drawing.Size(400, 320);
+ this.Name = "MainForm";
+ this.Text = "WRT Settings";
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form_FormClosing);
+ this.DragDrop += new System.Windows.Forms.DragEventHandler(this.Form_DragDrop);
+ this.DragOver += new System.Windows.Forms.DragEventHandler(this.Form_DragOver);
+ this.mnu.ResumeLayout(false);
+ this.mnu.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.grid)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.ToolStrip mnu;
+ private System.Windows.Forms.ToolStripDropDownButton mnuApp;
+ private System.Windows.Forms.ToolStripSplitButton mnuOpenRoot;
+ private System.Windows.Forms.ToolStripMenuItem mnuOpen;
+ private System.Windows.Forms.ToolStripSeparator mnuOpenRecentSeparator;
+ private System.Windows.Forms.ToolStripSplitButton mnuSaveRoot;
+ private System.Windows.Forms.ToolStripMenuItem mnuSave;
+ private System.Windows.Forms.ToolStripMenuItem mnuSaveAs;
+ private System.Windows.Forms.ToolStripMenuItem mnuAppFeedback;
+ private System.Windows.Forms.ToolStripMenuItem mnuAppUpgrade;
+ private System.Windows.Forms.ToolStripMenuItem mnuAppDonate;
+ private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2;
+ private System.Windows.Forms.ToolStripMenuItem mnuAppAbout;
+ private System.Windows.Forms.DataGridView grid;
+ private System.Windows.Forms.DataGridViewTextBoxColumn grid_colKey;
+ private System.Windows.Forms.DataGridViewTextBoxColumn grid_colValue;
+ }
+}
+
diff --git a/Source/WrtSettings/MainForm.cs b/Source/WrtSettings/MainForm.cs
new file mode 100644
index 0000000..80cd8fa
--- /dev/null
+++ b/Source/WrtSettings/MainForm.cs
@@ -0,0 +1,436 @@
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Drawing;
+using System.Globalization;
+using System.IO;
+using System.Text;
+using System.Windows.Forms;
+
+namespace WrtSettings {
+ internal partial class MainForm : Form {
+
+ public MainForm() {
+ InitializeComponent();
+ this.Font = SystemFonts.MessageBoxFont;
+ Medo.Windows.Forms.State.SetupOnLoadAndClose(this);
+ }
+
+
+ #region InitializeComponent
+
+ private bool SuppressMenuKey = false;
+
+ protected override bool ProcessDialogKey(Keys keyData) {
+ if (((keyData & Keys.Alt) == Keys.Alt) && (keyData != (Keys.Alt | Keys.Menu))) { this.SuppressMenuKey = true; }
+
+ switch (keyData) {
+
+ case Keys.F10:
+ ToggleMenu();
+ return true;
+
+ case Keys.Control | Keys.O:
+ mnuOpenRoot.PerformButtonClick();
+ return true;
+
+ case Keys.Alt | Keys.O:
+ mnuOpenRoot.ShowDropDown();
+ return true;
+
+ case Keys.Control | Keys.S:
+ mnuSaveRoot.PerformButtonClick();
+ return true;
+
+ case Keys.Alt | Keys.S:
+ mnuSaveRoot.ShowDropDown();
+ return true;
+
+ case Keys.F1:
+ mnuApp.ShowDropDown();
+ mnuAppAbout.Select();
+ return true;
+
+ case Keys.Control | Keys.C:
+ //TODO
+ return true;
+
+ case Keys.Control | Keys.A:
+ grid.SelectAll();
+ return true;
+
+ }
+
+ return base.ProcessDialogKey(keyData);
+ }
+
+ protected override void OnKeyDown(KeyEventArgs e) {
+ if (e.KeyData == Keys.Menu) {
+ if (this.SuppressMenuKey) { this.SuppressMenuKey = false; return; }
+ ToggleMenu();
+ e.Handled = true;
+ e.SuppressKeyPress = true;
+ } else {
+ base.OnKeyDown(e);
+ }
+ }
+
+ protected override void OnKeyUp(KeyEventArgs e) {
+ if (e.KeyData == Keys.Menu) {
+ if (this.SuppressMenuKey) { this.SuppressMenuKey = false; return; }
+ ToggleMenu();
+ e.Handled = true;
+ e.SuppressKeyPress = true;
+ } else {
+ base.OnKeyUp(e);
+ }
+ }
+
+
+ private void ToggleMenu() {
+ if (mnu.ContainsFocus) {
+ grid.Select();
+ } else {
+ mnu.Select();
+ mnu.Items[0].Select();
+ }
+ }
+
+ #endregion
+
+
+ #region Form
+
+ private void Form_FormClosing(object sender, FormClosingEventArgs e) {
+ if (!HasSavedModifications()) { e.Cancel = true; }
+
+ }
+
+ #endregion
+
+
+ #region Drag&Drop
+
+ private void Form_DragOver(object sender, DragEventArgs e) {
+ if (GetFileName(e.Data) != null) {
+ e.Effect = DragDropEffects.Link;
+ } else {
+ e.Effect = DragDropEffects.None;
+ }
+ }
+
+ private void Form_DragDrop(object sender, DragEventArgs e) {
+ var fileName = GetFileName(e.Data);
+ if (fileName != null) {
+ //TODO: Open file
+ }
+ }
+
+
+ private static string GetFileName(IDataObject data) {
+ if (data.GetDataPresent("FileDrop")) {
+ var fileNames = data.GetData("FileDrop") as string[];
+ if ((fileNames != null) && (fileNames.Length == 1)) {
+ return fileNames[0];
+ }
+ }
+ return null;
+ }
+
+ #endregion
+
+
+ #region Grid
+
+ private void grid_KeyDown(object sender, KeyEventArgs e) {
+ switch (e.KeyData) {
+ case Keys.Control | Keys.C:
+ var sb = new StringBuilder();
+ foreach (DataGridViewRow row in grid.Rows) {
+ if (row.Selected && !row.IsNewRow) {
+ sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "{0}={1}", row.Cells[0].Value, row.Cells[1].Value));
+ }
+ }
+ Clipboard.Clear();
+ Clipboard.SetText(sb.ToString());
+ e.Handled = true;
+ break;
+ }
+ }
+
+ private void grid_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) {
+ Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "CellBeginEdit: C{0} R{1}", e.ColumnIndex, e.RowIndex));
+
+ var row = grid.Rows[e.RowIndex];
+ if (row.IsNewRow) {
+ if ((e.ColumnIndex != 0) && (string.IsNullOrEmpty(row.Cells[0].Value as string))) { //cannot edit value if there is no key
+ e.Cancel = true;
+ }
+ }
+ }
+
+ private void grid_CellParsing(object sender, DataGridViewCellParsingEventArgs e) {
+ var oldText = grid.Rows[e.RowIndex].Cells[e.ColumnIndex].Value as string;
+ var newText = e.Value.ToString();
+
+ Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "CellParsing: C{0} R{1}: '{2}' -> '{3}'", e.ColumnIndex, e.RowIndex, oldText, newText));
+
+ e.ParsingApplied = true;
+
+ try {
+ oldText = Nvram.DecodeText((oldText ?? "").Trim());
+ newText = Nvram.DecodeText(newText.Trim());
+
+ if (e.ColumnIndex == 0) { //key
+
+ if (newText.Length == 0) {
+ throw new FormatException("Key must be at least one character!");
+ }
+
+ if (newText.Contains("=")) {
+ throw new FormatException("Key cannot contain equals (=) character!");
+ }
+
+ foreach (var ch in newText) {
+ var value = Encoding.ASCII.GetBytes(new char[] { ch })[0];
+ if ((value < 32) || (value > 127)) {
+ throw new FormatException("Key must be in ASCII 32-127 range!");
+ }
+ }
+
+ if (!oldText.Equals(newText, StringComparison.Ordinal)) {
+ if (this.Document.Variables.ContainsKey(newText)) {
+ throw new FormatException("Cannot have duplicate key!");
+ }
+ if (oldText.Length > 0) { //not a new key
+ this.Document.Variables.Remove(oldText);
+ }
+
+ var oldValue = (grid.Rows[e.RowIndex].Cells[1].Value as string) ?? "";
+ this.Document.Variables.Add(newText, oldValue);
+ this.HasChanged = true;
+ }
+
+ } else { //value
+
+ var oldKey = (grid.Rows[e.RowIndex].Cells[0].Value as string) ?? "";
+ this.Document.Variables[oldKey] = newText;
+ this.HasChanged = true;
+
+ }
+
+ e.Value = Nvram.EncodeText(newText);
+ } catch (FormatException ex) {
+ Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "CellParsing: C{0} R{1}: {2}", e.ColumnIndex, e.RowIndex, ex.Message));
+ e.Value = oldText;
+ }
+ }
+
+ private void grid_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) {
+ var oldText = grid.Rows[e.RowIndex].Cells[e.ColumnIndex].Value as string;
+ var newText = e.FormattedValue.ToString();
+
+ Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "CellValidating: C{0} R{1}: '{2}' -> '{3}'", e.ColumnIndex, e.RowIndex, oldText, newText));
+
+ if (e.ColumnIndex == 0) {
+ if (string.IsNullOrEmpty(oldText) && (this.Document.Variables.ContainsKey(newText))) { //don't add new key as a duplicate
+ e.Cancel = true;
+ }
+ }
+ }
+
+ #endregion
+
+
+ #region Document
+
+ private Nvram _document;
+ private Nvram Document {
+ get { return this._document; }
+ set {
+ this._document = value;
+ this._hasChanged = false;
+ if (this.Document != null) {
+ mnuSaveRoot.Enabled = true;
+ grid.Visible = false;
+ grid.CancelEdit();
+ grid.Rows.Clear();
+ foreach (var pair in this.Document.Variables) {
+ var row = new string[] { Nvram.EncodeText(pair.Key), Nvram.EncodeText(pair.Value) };
+ grid.Rows.Add(row);
+ }
+ grid.Sort(grid_colKey, ListSortDirection.Ascending);
+ grid.Visible = true;
+ grid.Refresh();
+ }
+ UpdateTitle();
+ }
+ }
+
+ private bool _hasChanged;
+ private bool HasChanged {
+ get { return this._hasChanged; }
+ set {
+ if (this._hasChanged != value) {
+ this._hasChanged = value;
+ UpdateTitle();
+ }
+ }
+ }
+
+ #endregion
+
+
+ #region Menu
+
+ private void mnuOpenRoot_DropDownOpening(object sender, EventArgs e) {
+ var startAt = mnuOpenRoot.DropDownItems.IndexOf(mnuOpenRecentSeparator);
+ for (int i = mnuOpenRoot.DropDownItems.Count - 1; i > startAt; i--) {
+ mnuOpenRoot.DropDownItems.RemoveAt(i);
+ }
+
+ mnuOpenRecentSeparator.Visible = false;
+
+ foreach (var item in this.Recent.Items) {
+ if (File.Exists(item.FileName)) {
+ var menuItem = new ToolStripMenuItem(item.Title, null, mnuOpenRecent_Click) { Tag = item.FileName };
+ mnuOpenRoot.DropDownItems.Add(menuItem);
+ mnuOpenRecentSeparator.Visible = true;
+ }
+ }
+ }
+
+ private void mnuOpen_Click(object sender, EventArgs e) {
+ if (!HasSavedModifications()) { return; }
+
+ using (var frm = new OpenFileDialog() { Filter = "Auto-detect configuration|*.cfg;*.txt|AsusWRT configuration|*.cfg|Tomato configuration|*.cfg|Text file|*.txt" }) {
+ if (frm.ShowDialog(this) == DialogResult.OK) {
+ try {
+ switch (frm.FilterIndex) {
+ case 2: this.Document = new Nvram(frm.FileName, NvramFormat.AsuswrtVersion1 | NvramFormat.AsuswrtVersion2); break;
+ case 3: this.Document = new Nvram(frm.FileName, NvramFormat.Tomato); break;
+ case 4: this.Document = new Nvram(frm.FileName, NvramFormat.Text); break;
+ default: this.Document = new Nvram(frm.FileName, NvramFormat.All); break;
+ }
+ this.Recent.Push(this.Document.FileName);
+ grid.Select();
+ } catch (FormatException ex) {
+ Medo.MessageBox.ShowError(this, "Cannot open file!\n\n" + ex.Message);
+ }
+ }
+ }
+ }
+
+ private void mnuOpenRecent_Click(object sender, EventArgs e) {
+ if (!HasSavedModifications()) { return; }
+
+ var fileName = (string)(((ToolStripMenuItem)sender).Tag);
+ try {
+ this.Document = new Nvram(fileName, NvramFormat.All);
+ this.Recent.Push(fileName);
+ grid.Select();
+ } catch (FormatException ex) {
+ if (Medo.MessageBox.ShowError(this, "Cannot open file!\n\n" + ex.Message + "\n\nDo you want to remove it from recent files list?", MessageBoxButtons.YesNo) == DialogResult.Yes) {
+ this.Recent.Remove(fileName);
+ }
+ }
+ }
+
+ private bool HasSavedModifications() {
+ if (this.HasChanged) {
+ switch (Medo.MessageBox.ShowQuestion(this, "File has been changed. Do you want to save it?", MessageBoxButtons.YesNoCancel)) {
+ case DialogResult.Yes:
+ mnuSave_Click(null, null);
+ return !this.HasChanged;
+
+ case DialogResult.No: return true;
+ default: return false;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ private void mnuSave_Click(object sender, EventArgs e) {
+ try {
+ this.Document.Save(this.Document.FileName);
+ this.HasChanged = false;
+ this.Recent.Push(this.Document.FileName);
+ } catch (InvalidOperationException ex) {
+ Medo.MessageBox.ShowError(this, "Cannot save file!\n\n" + ex.Message);
+ }
+ grid.Select();
+ UpdateTitle();
+ }
+
+ private void mnuSaveAs_Click(object sender, EventArgs e) {
+ using (var frm = new SaveFileDialog() { Filter = "AsusWRT configuration (v1)|*.cfg|AsusWRT configuration (v2)|*.cfg|Tomato configuration|*.cfg|Text file|*.txt" }) {
+ switch (this.Document.Format) {
+ case NvramFormat.AsuswrtVersion1: frm.FilterIndex = 1; break;
+ case NvramFormat.AsuswrtVersion2: frm.FilterIndex = 2; break;
+ case NvramFormat.Tomato: frm.FilterIndex = 3; break;
+ case NvramFormat.Text: frm.FilterIndex = 4; break;
+ }
+ if (frm.ShowDialog(this) == DialogResult.OK) {
+ switch (frm.FilterIndex) {
+ case 1: this.Document.Format = NvramFormat.AsuswrtVersion1; break;
+ case 2: this.Document.Format = NvramFormat.AsuswrtVersion2; break;
+ case 3: this.Document.Format = NvramFormat.Tomato; break;
+ case 4: this.Document.Format = NvramFormat.Text; break;
+ }
+ try {
+ this.Document.Save(frm.FileName);
+ this.HasChanged = false;
+ this.Recent.Push(this.Document.FileName);
+ } catch (InvalidOperationException ex) {
+ Medo.MessageBox.ShowError(this, "Cannot save file!\n\n" + ex.Message);
+ }
+ grid.Select();
+ UpdateTitle();
+ }
+ }
+ }
+
+
+ private void mnuAppFeedback_Click(object sender, EventArgs e) {
+ Medo.Diagnostics.ErrorReport.ShowDialog(this, null, new Uri("http://jmedved.com/feedback/"));
+ }
+
+ private void mnuAppUpgrade_Click(object sender, EventArgs e) {
+ Medo.Services.Upgrade.ShowDialog(this, new Uri("http://jmedved.com/upgrade/"));
+ }
+
+ private void mnuAppDonate_Click(object sender, EventArgs e) {
+ Process.Start("http://www.jmedved.com/donate/");
+ }
+
+ private void mnuAppAbout_Click(object sender, EventArgs e) {
+ Medo.Windows.Forms.AboutBox.ShowDialog(this, new Uri("http://www.jmedved.com/asuswrtsettings/"));
+ }
+
+ #endregion
+
+
+ #region File
+
+ #endregion
+
+
+ #region Helpers
+
+ private readonly Medo.Configuration.RecentFiles Recent = new Medo.Configuration.RecentFiles();
+
+ private void UpdateTitle() {
+ string newText;
+ if (this.Document == null) {
+ newText = (this.HasChanged ? "* " : "") + Medo.Reflection.EntryAssembly.Product;
+ } else {
+ newText = Path.GetFileNameWithoutExtension(this.Document.FileName) + (this.HasChanged ? "*" : "") + " - " + Medo.Reflection.EntryAssembly.Product;
+ }
+ if (this.Text != newText) { this.Text = newText; }
+ }
+
+ #endregion
+
+ }
+}
diff --git a/Source/WrtSettings/MainForm.resx b/Source/WrtSettings/MainForm.resx
new file mode 100644
index 0000000..eafdbab
--- /dev/null
+++ b/Source/WrtSettings/MainForm.resx
@@ -0,0 +1,1315 @@
+<?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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <metadata name="mnu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <value>17, 17</value>
+ </metadata>
+ <assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+ <data name="mnuOpenRoot.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAJ1SURBVDhPvZJZSNRRHEb/Dz30kkVmJEKSJWKBGBhSPYgY
+ Ua6UKyipYblRZijmuDTjrrPoqFMq2ow6jTpaWYphhVAoEqHlQompWVBUiqmhkttpHEWyfAiCPvjgcu89
+ hx+XK/yXZBZXu0iUOq7lVpKt0pMk1SSvHf1dQuJUeEfKg1bWlxIrzNIMsvgMtW9wXDFCgzbf31B+b0tt
+ ttxIb5JIUYmHT7Qcr8gCo4DBXg1zk/XG/pjQsTCmRl8pp1GbnrbGbIh3lAKvCCXu4UUI9VUKZidqNsBL
+ n0tY6I+l5lYu1aVZqIvTKCtI5aZMRFFOAmue1dRqpMx+1azDywaYQRH0h2/aPwS3y3P4/qlsHW5tkBr7
+ UJ9Hky6He9VZ6NXp6MokVKquU6FMplQhQiVNXJVVl2Ux/b7QOPZIu5jGGgVLIxIYzdjYd4a94SQYiIPX
+ 0fTU+RET6lklqFXpTA3lwgcZ92vldD6SrV76dfRXwdDlw2KHG3OPnXlbZUfYOaepK2Eeh4RypZhvbySM
+ dqTQqJOxPCyGvohVsOe8AQxgsdOTubaTTLccZ7zhMJJIawJcTAuMb1CiSGG8N5E7WindzwyTDMQaBBeg
+ O4jl52eYf3qamVYnJhod+FJrS1fmDgLdrHCw2WZjFKjyRPS1pXJXmwdDqfDSMO4LX+bb3Zl54sxkkyNj
+ ejs+ag7QLzNFHL4f+4PmzUZ4JYXZCdRX5tL/IIqufGG9nbIttOVspTltO3XJZqgTLLhx1ZJA133LjvaW
+ rmu4IORnxJMvvkh2YghJl/2JOe9JqN8Jzp46issxO47YWWNrZcFe813s3mnCHlOTjf/g3yIIPwHVOuT9
+ xNmtFAAAAABJRU5ErkJggg==
+</value>
+ </data>
+ <data name="mnuSaveRoot.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAIzSURBVDhPrZLrS9NRGMd//0LvexP0orc1NYZrsRr8yjWU
+ ZcOwIrr9wltpuswLKZamUcsLZCtcOmm1moSTiryAKWIii6VoZeaKpji8ohPB4Nt5zn55sqIX0QPPm8P5
+ fM55LtJ/iefPIqD0tS6hxbuIh+45NDWGcdcxhbrar7DfDOJaxThKSz6gsGAEeblDOH8uABWXJAK7OoHa
+ mlX8LVZW12BK6cLhlKc8VVySvE8WuKCqcl69+nsQHJ5bwW7LC2g0CqyHPELgfjDLBaUlk+r1jfED/hRa
+ QIzZh4JLEzhoaRYCZ8M0F1y0fVQRET/Db8fC2CZ7kZM9iqREpxDU3w5xQVbmkIpF41e4LxDCZoMbijII
+ s9khBDXVX7hAOTOoon+GOwc+Y1N8I44cfYWEhDohuF41gY52sFECx0++hvVYL+82NYxqpm/Ty1G4G8nW
+ l5BluxBcKRtDmw9sB4Bm1zc292VUlM/wplLDqOaM9MA6fCCpFUZjpRAUFY7C8wi471xjixOBKTV7Q1pO
+ 2JCaVhyFExm83wODoUwIbHnDDAaqb0X4ywSxY8ToZPjfT2N4fAZK7lXsNbVwWLfHBb2+WAhoLR13sP5t
+ EhB8Nv8G3gVnEZxaxIXLdsQaXAxuQuyue9DF5wtBRvobVvcSh7caPdiyQ8bjth742vvQ0T2A3n4/9pmt
+ 0OgbEMfg7dp6aLU5QpCZ5k8+faqHrydtGC0JzZlGRd2mhlHN9ColwTvjsoTg30OSvgNCVjq+glI0hgAA
+ AABJRU5ErkJggg==
+</value>
+ </data>
+ <data name="mnuApp.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAHtSURBVDhPxZHNT9JhHMD9Ozja1iEuXfwjOrd16Nq9Q+vg
+ obUx24rcaoomKaHADwjBV8SXgEQYICACQSTRppk5CV/KZsrLj0/+ntaQah3s0Gd79ny/z5599n1p+2dG
+ JsJYZ+LYPAlxS+4YlukopskIw+MhnrmWGBpd5OlzPwM2L/3SAn2WeXTmOYTAMr3Meei4cuOHwDoTEw9y
+ o0GlWkOj0XBSqYk3BV+kwGwwz6Qvh2M2jXkyydvN/abgZwWyLJNMJrl5q1Pcf6NFoMxAoVwuc/Xadfb2
+ 9rhw8ZLIFaZe5nDOZ07nsorBlaDPGiVT/NQUGJxL4qNWq0WlUqFWq8VR8rPU6jJHxzUODitEMttNgd7u
+ Fx/UlzuQJEnELpeL9vZ2EVvdKYxjCZ7Yl3lsCvNgKMiL6EZToKzlT8hyg5NqncOjKuWDY7ZKRxQ/fCH7
+ bp8xf7EpUPapEIgX8YbX8ATyjHuz2D1pRiZW0Dti9JgjaA1BugYWudPjx+TO/y5QONvnzu43Nra/8mb9
+ M6tru4QzJXzxj7hDm/Q7XrUK6qfl/rrrwdE4OilCtzHEPX2Auzo/nY+83H64QLcp2SpQ+kwVSsRyOwRT
+ W/hi7/GE1kWvtrkCw1Ov0Tuz9NrS3Deu0DUYbxUoyXmOEPxn2tq+A1vJaj6PPYobAAAAAElFTkSuQmCC
+</value>
+ </data>
+ <metadata name="grid_colKey.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <value>True</value>
+ </metadata>
+ <metadata name="grid_colValue.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <value>True</value>
+ </metadata>
+ <metadata name="grid_colKey.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <value>True</value>
+ </metadata>
+ <metadata name="grid_colValue.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <value>True</value>
+ </metadata>
+ <data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>
+ AAABAAoAMDAQAAEABABoBgAApgAAACAgEAABAAQA6AIAAA4HAAAQEBAAAQAEACgBAAD2CQAAMDAAAAEA
+ CACoDgAAHgsAACAgAAABAAgAqAgAAMYZAAAQEAAAAQAIAGgFAABuIgAAAAAAAAEAIADCpQAA1icAADAw
+ AAABACAAqCUAAJjNAAAgIAAAAQAgAKgQAABA8wAAEBAAAAEAIABoBAAA6AMBACgAAAAwAAAAYAAAAAEA
+ BAAAAAAAgAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACAAAAAgIAAgAAAAIAAgACAgAAAgICAAMDA
+ wAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAB3d3d3d3d3d3d3d3d3d3d3d3d3d3cAAAeIiIiIiIiIiIiIiIiIiIiIiI
+ iIiIhwAAeP//////////////////////////hwAAeP///////////////4//+P/4/4//hwAAeP//+IiI
+ //+P/4/4//+P//j///+PhwAAeP//+Pho/4//////+P//j//4+P//hwAAeP//+PbG////j/j///j//4//
+ //j/hwAAeP//+Gbmf///d4d3d4d3eHd3+P//hwAAeP//98eMaP//eHiHh3iHh4eH//j/hwAAeP//+I/+
+ bv+P//j///j/j/+P+P//hwAAeP//////x4j////4+P//+P////j/hwAAeP//////jm//+P////+P//j4
+ +P//hwAAeP//iIiI9+b///+P/4//j/////j/hwAAeP//+Pho/36I/////////4+P+P//hwAAeP//+PbG
+ /46Pd3d3d3d3eHh3//j/hwAAeP//+Gbmf///eHiHiIeIeHeH////hwAAeP//9seMaP////+P+P/4+P//
+ j4+PhwAAeP///4/35v//j/////j///j/////hwAAeP//////bI////////////+Pj/j/hwAAeP//////
+ jm///4/4+P/4+P//////hwAAeP//iIiI+Ob4//////////+P+Pj/hwAAeP//+Pho+OePd3d4d3d3h3h4
+ ////hwAAeP//+PbG/46PeIh3iHiHeHeH+P+PhwAAf///+Gbmf////4////+P//+P////hwAAeP//98eM
+ aP//////j///+P//j4//hwAAf/+P/4/+bv/4/////4/4//j///+P9wAAeP//////x4j///j////////4
+ /4//hwAAeP////+Pjm//////////////////hwAAf////4//9+f/////+P+P+P+P+P+P9wAAeP//////
+ /+aPj//4////////////hwAAf//4/////4jv////////////j/j/hwAAeP////////////////+P+P+P
+ ////9wAAeIiIiIiIiIiIiIiIiIiIiIiIiIiIhwAAf4iPiIiIiIiIiIiIiIj4j4j4iIiI9wAAeP+I+Pj4
+ +Pj4+Pj4+PiPiHePd/h3hwAAB3d3d3d3d3d3d3d3d3d3d3d3d3d3cAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////AAD///////8AAP//
+ /////wAA////////AAD///////8AAP///////wAA////////AACAAAAAAB8AAAAAAAAADwAAAAAAAAAP
+ AAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAA
+ AAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAP
+ AAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAA
+ AAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAP
+ AAAAAAAAAA8AAAAAAAAADwAAgAAAAAAfAAD///////8AAP///////wAA////////AAD///////8AAP//
+ /////wAAKAAAACAAAABAAAAAAQAEAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIAAAACA
+ gACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP///wAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAh3d3d3d3
+ d3d3d3d3d3d4AH//////////////////9wB/////////////+P/4//cAf//4iI//j/j4+P/4//j3AH//
+ +IaP//////////j/9wB///hs6P/4//j/j4//+PcAf//36Gj4d3d3d3d3+P/3AH////+Gj///////////
+ 9wB///j4jn////j/+Pj4+PcAf//494/o+P//+P/////3AH//+IyPjv//j///+P+P9wB///ZuaP93d3d3
+ d3f///cAf//4j27/////////j/j3AH////+Mj/+P//j/j/+P9wB///j4+Oj///j///+P//cAf//49o/n
+ /////4////j3AH//+Gzvj3d3d3d3d///9wB///boaP/////////4//cAf////3z//4/4//j///j3AH//
+ //+Of///////+P+P9wB/////+Oj////4//////cAf/////+O////////j//3AHiIiIiIiIiIiIiIiIiI
+ hwB/j4iPiIiIiIiPiIeHh4cAB3d3d3d4d4d4d3d3d3d4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////////////8AAAADAAAAAwAAAAMAAAADAAAAAwAA
+ AAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAA
+ AAMAAAADAAAAAwAAAAOAAAAD////////////////KAAAABAAAAAgAAAAAQAEAAAAAACAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8A
+ AAD/AP8A//8AAP///wB3d4d3eHd3h3/////////3f/j4j//4/4h/+PaP+P//93/4Zu/3h3f3f/foyP//
+ //d///92/4/493///45////3f///+Oj4//d/////jv/493iIiIiIiIiIiPj4+Ph4f3d3h3d3eHh3dwAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAD//wAA//8AAP//AAAoAAAAMAAAAGAAAAABAAgAAAAAAAAJAAAAAAAAAAAAAAAB
+ AAAAAQAAAAAAALNtCwC1bAoAtnYPALlxCwC6dQoAq2saALh0EACyeSgAtngpALh9NACReWgAwYcLAMaO
+ HADFiCEAzJkkAMWKNwDNoSEA1q46ALWBQQC8kl8Ay5paAMybWwDUr0QA2LJBANq2SwDbuFYA3LhWANm4
+ XADGmGAAyJphAN7BbQDhx3wA4sh9AIiEgQCmkJIAppGUAKiSlACplJcAqpWYALuigwC7ooQA2riAANy6
+ ggDBq5EAwqySAMWzoADFtKEA3sGcAODDngDhxJ4A48qdAOXMnwDRxr0A59W+AOnWvwDu4boA7+O8AL6/
+ wADLyckAzMnJAOrYwQDn2cgA6NvJAOncygDg2dMA7+XNAPHnzgDz6M8A7+TWAPHm1wDy5tkA9e7lAPXv
+ 6AD28ecA9vHpAPXx7AD39e4A+PLrAPj06gD48+8A8fHxAPTz8gDz9PMA9PTzAPPz9AD08/QA8/T0APX1
+ 9QD59vEA+Pf3APf49wD6+PEA+Pj3APf3+AD49/gA9/j4APn5+QD8+/sA+/z7APz8+wD7+/wA/Pv8APv8
+ /AD9/v4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIi
+ IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAACI1NTU1NTU1NTU1NTU1NTU1NTU1NTU1NTU1
+ NTU1NTU1NTU1NTU1NTU1NTUiAAAAACI1ZWVlZWVlZWVlW2VaW1paWlpaWlpaWlpaUVpRWlFaUVpRUVFR
+ UVFRZTUiAAAAACI1ZWVlZWVhYVtlWlphYVpaWlpaUVpRWlFRUVpRUVFRUVFRUVFRUVFRZTUiAAAAACI1
+ ZWVlZWU8PDw8O2VaYVpaWlpaWlFaUVFaUVFRWlFRUVFRUVFRUVFRZTUiAAAAACI1ZWVlZWU8ZT4TKFth
+ YVtaWlpRV1daWlFaUVpRUVFRUVFRUVFRUVFRZTUiAAAAACI1ZWVlZWU8UQgFBlpaYVtaWldgV2BaWlFR
+ WlFRUVpRWlFRUVFRUVFRZTUiAAAAACI1ZWVlZWUuCAIHBx1hYWFbVycmJiYjJiYmJiYmJiMmIyYmJlFR
+ UVFRZTUiAAAAACI1ZWVlZWUUAgosBwU2YWFlYSUmJiMmJiYjIyYjJiMjIyMjI1FRUVFRZTUiAAAAACI1
+ ZWVlZWVEMEhlFQwQYWFaWl9aWlpaWlFaUVFRUVFRUVFRUVFRUVFRZTUiAAAAACI1ZWVlZWVlZWVlSA4O
+ KmFhYWFaWlpRWlpRWlpRW1FRW1FRUVFRUVFRZTUiAAAAACI1ZWVlZWVlZWVlYTMREUJhYWFaWlpaWlFX
+ WlFRW1pRUVFaUVFRUVFRZTUiAAAAACI1ZWVlZWU8Ozw8PGEaEhdIYWFbWlpaUWBaUWBaUVFXW1FRUVFR
+ UVFRZTUiAAAAACI1ZWVlZWU8ZUATKGVRGRgfYWFhWldXYFdXV1pRYFFbUVFRUVFRUVFRZTUiAAAAACI1
+ ZWVlZWU8TQgFBk1lOBogYSUlJiYnJiYmJiYmJiYjJiYmJlFRUVFRZTUiAAAAACI1ZWVlZWUvCQUHBx1l
+ ZWVlYSUlJiMmIyYmJiYjJiMmJiMjI1FaUVFRZTUiAAAAACI1ZWVlZWUdAQosBwU3ZWVhZWFhW2BXZVpa
+ WlFaUVFRUVFRUVFRUVpRZTUiAAAAACI1ZWVlZWVGMlFlHQwQZWVhZWFhZWFlWlpaWlFaW1taUVdXV1dR
+ UVFRZTUiAAAAACI1ZWVlZWVlZWVlSA4OKmFlYWFhYWFaWlpaWlpRWlFaV1dRV1FbUVFaZTUiAAAAACI1
+ ZWVlZWVlZWVlZTQREUNlZWFlYWFbYFdXV2BgUWBRYFpRW1FRWlFRZTUiAAAAACI1ZWVlZWU8PDw8PGUa
+ EhdIZWVhYWFlYWVlV2BbYFtbUVtRW1pRUVFRZTUiAAAAACI1ZWVlZWU8ZUITKGVhGRgfZSUlJSUmJiMm
+ JSYmJiYmJiYmJlFRW1FRZTUiAAAAACI1ZWVlZWU8WggHBmFhOBshZSUnJyUlJSclJyYmJiYmJiYmJlFb
+ UVpRZTUiAAAAACJBZWVlZWUvCQEFBx1hZWVhZWVlZWVlZWVaZVpaWlpRWlFRUVFbUVFXZUEiAAAAACJB
+ ZWVlZWUdAQosBwU9ZWVlZWVlZWVlWmVaWlpaWlpaUVtbWlFaUVdaZUEiAAAAACJBZWVlZWVGMk1lHQwQ
+ ZWVlZWVlYWFhZVplWldXV1paWlFbWlFRYFFRZUEiAAAAACJBZWVlZWVlZWVlSA0NK2VlZWVlZWFhYWVa
+ YGVlW1paV2BaUVtaUVtaZUEiAAAAACJBZWVlZWVlZWVlZTQREURlZWVhZWVlYWFlWmFaYFpgYFpaWlFb
+ WlFRZUEiAAAAACJBZWVlZWVlZWVlZWUaEhlNYWVlYWVlYWFhYWFhW1taWlpRYFdRW1FgZUEiAAAAACJB
+ ZWVlZWVlZWVlZWVhGRgfZWVlZWVlYWVhZWFhYVtbWltgWmBaUWBaZUEiAAAAACJBZWVlZWVlZWVlZWVl
+ ORshZWVlZWVlYWVhYWFhYWFbW1paWlFgUVdRZUEiAAAAACJBZWVlZWVlZWVlZWVlZWVlZWVlZWVhZWFh
+ YWFhYWFhWlpaUWBaW1pRZUEiAAAAACJBOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6
+ Ojo6OkEiAAAAACJBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEiAAAAACJB
+ QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBCwtBQQsLQUELC0EiAAAAAAAiIiIiIiIiIiIiIiIi
+ IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////wAA////////AAD///////8AAP//
+ /////wAA////////AAD///////8AAP///////wAAgAAAAAAfAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAP
+ AAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAA
+ AAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAP
+ AAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAA
+ AAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAAAAP
+ AAAAAAAAAA8AAIAAAAAAHwAA////////AAD///////8AAP///////wAA////////AAD///////8AACgA
+ AAAgAAAAQAAAAAEACAAAAAAAAAQAAAAAAAAAAAAAAAEAAAABAAAAAAAAr24CALRwAAC3fQcAtnoNALl+
+ CgCzdhAAsXkUALd9IgCReWgAlXx/ALmACQC5ghsAu4UqALGAMQC/izgAw5EWAMicGwDBjiMAxJAgAMyj
+ IQDTsTAA0rA3ANW2NwDYujsAuo9IALuVXAC0lWkAv6R7AMmZQQDBkkgAw5tbAM6jTwDUskQA2LleAMqm
+ bwDNqGsAzKhvAM+rcgDTsHEA17RzANzBUADbwFsA3cNpAN/HagDeyHAA4858AJV8gACIhIEAi4eFAIuJ
+ hwCNiYYAjoqIAI+MiQCRjowAsa6tAMiuiQDJtpoA07iQAMK0pQDewpgA4cmKAOXSiwDm1JEA6dmfANbF
+ rwDZxagA1sy/AN7PugDlz6cA696lAObQrwDg0LcA6NW3AOvcugDEwcAAy8nHAMzKyADNzMoAzdjNAN7X
+ 0QDY0tYA1NzTANbf1QDb1dkA2tvZAOnZwwDh2csA6d7NAObe2ADv5sQA7OHPAPLpyQDx6c4A7uPQAO/n
+ 3ADy5dEA9e3cAPXw2gDk2uIA49ziAOfo5QDr5+oA6urqAPLr4QD28+kA+PLrAPHx8QD08/MA8/TzAPT0
+ 8wDz8/QA9PP0APP09AD19fUA+vfzAPj39wD3+PcA+vj2APf3+AD49/gA9/j4APn5+QD8+/sA+/z7APv7
+ /AD8+/wA+/z8AP7+/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLMDAwMDAwMDAw
+ MDAwMDAwMDM1MzAwMDAwMDAwMDcAADB7e3t7e3V7dXV1dXV1dXV1dWt1dWtrdWtra2t2MAAAMHt7e3l7
+ dXl5dXV1dXVrdWtra2tra2tra2tra3YwAAAwe3t7e1FPO1N3d3V1a3VrdWt1a2tra2tra2trczAAADB7
+ e3t5U0gHOnt1d3V1dWt1a3VrdWtra2tra2tzMAAAMHt7e3scAQwNaHh4dXV1cHdra2tra3Vra2tra3Yw
+ AAAwe3t7eyYkQQsne3UvLy8vLy8vLy8vCgpra2trdjAAADB7e3t7e3t7JxBKeHh1dXV1a3h1a3Vra2tr
+ a2t2MAAAMHt7e3trZmVeFiFpeHh3dXVrd2t1a3VrdWtrdXMwAAAwe3t7e1RXG0RdGC17dXh1dXB4dWtr
+ a2trdWtrczAAADB7e3t7QyYCI3tGPnh4eHdzd2t4dWt4dWtra2tzMAAAMHt7e3saBhkEVnt7Ly8vLy8v
+ Ly8vLy8Ka2trdXMwAAAwe3t7e0dJXxIddnt7e3l4d3N3dWt4a3VrdWtreDAAADB7e3t7e3t7RRE9e3l7
+ eXt3eHV3d3Vra3dra3VzMAAAMHt7e3tkZFRbKxZae3t1dnt1c3d1dXN3a3Nra3gwAAAwe3t7e1VeDkJ2
+ KSp7eXt5dnd3dXVrd2t4a3dreDAAADB7e3t7OQgFHnZiXAovCi8vLy8vLy8vL3N3a3dzMAAANXt7e3sf
+ DzgDPHt7e3t7e3t5e3V4c3h3dWt3a3swAAA1e3t7e2p2eyATYXt7e3t5e3l4eHd3d3V1a3d1czAAADV7
+ e3t7e3t7YBQie3t7e3t7e3l7dXN3dWt3a2t4MAAANXt7e3t7e3t7RhY/e3t7e3l7eXh4d3V1d3V3dXMw
+ AAA1e3t7e3t7e3t7Lix7e3t7e3t7eXt1dXN3dWt4czAAADVOTk5OTk5OTk5NTU5OTkxMTk5OTk5OTExM
+ TExMMAAANVlQUFBQUFBQUFBQUFBQUFBQUFBQUFAJUAlQCVAwAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAw
+ MDAwMDAwMEsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////////
+ //8AAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAA
+ AAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAOAAAAD////////////////KAAAABAA
+ AAAgAAAAAQAIAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAEAAAAAAACReWgAt4MHALmECgC3gyIAsIQxAL+Q
+ OADEliAAzKkhANW7NwDAlUcAw55bAM2nTgDXvFwA3sppAOLRfACXko8AqpaZAKuYmgDIsIgAyLeaANvC
+ lQDk1Y8A6NqdANbFpgDNzMoA1tHUANra2ADn3cwA6N/MAODZ0wDw5dAA8OrXAOLa4QDk2eIA+PTqAPHx
+ 8QDz9PMA9vTyAPPz9AD08/QA9fX1APn38gD49/cA9/f4APj3+AD5+fkA/Pz7AP7+/gAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wAQEBAQEBAQEBAQEBAQEBAQEDAwMDAp
+ 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
+ R4PlTQD3X/f8XgD4H+97z3c/Fwxt2qZt2le1Xdv4gR4A+MgnP//csLJpm7Zpz+t2jUeCbdqmbdqLtW0A
+ YNM27Y9xkyUAA8Cb3/jQ15CVTdu0TfsKNgbQ5v91o9pnTv8uEiDclf8O8JxmVjdt0zbta9QYwL/Ln88A
+ HOe/F2LgDZLRjwG8PP8dYrNE2LRNezG0FsAifz4EcArgHMCl3wVgAMv8wxIpAtgAwKZt2gu/tUg2fQXg
+ EgkMlgDY5wDkpFOY8W+WAJu2aS/85u17gQQCSwBtGQG0SBGAGP8GADZt0174TZJ+AgLL/DncBiMGP8TG
+ +Ddt015sjRGBIN/2FBvBvH+LDQhs2qa9GFrtftXa8U3btE3btE3btE3btE3btE3btE3btE3btE3btE3b
+ tE3btE3btE3btE3btE3btE3btE3btE3btE3btE3btE3btE3btE3btE3btE3btE3btE3btE3btE3btE3b
+ tE3btE3btE3btE3btE3btE3btE3btE3btE3btE3btE3btE3btE3btE3btE3btE177tv/A2F/Fw0eR0qc
+ AAAAAElFTkSuQmCCKAAAADAAAABgAAAAAQAgAAAAAACAJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAF
+ AAAACwAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAO
+ AAAADgoEBRAKBAUQCgQFEAoEBRAKBAUQCgQFEAoEBRAKBAUQCgQFEAoEBRAKBAUQCgQFEAoEBRAKBAUQ
+ CgQFEAoEBRAKBAUQCgQFEAAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAKAAAAAwAAAAAAAAAA
+ AAAAAAAAAAUAAAAVAAAAJQAAACoAAAAqAAAAKgAAACoAAAAqAAAAKgAAACoAAAAqAAAAKgAAACoAAAAq
+ AAAAKgAAACoAAAAqAAAAKgUCAi0FAgItBQICLQUCAi0FAgItBQICLQUCAi0FAgItBQICLQUCAi0FAgIt
+ BQICLQUCAi0FAgItBQICLQUCAi0FAgItBQICLQAAACoAAAAqAAAAKgAAACoAAAAqAAAAKgAAACoAAAAj
+ AAAAEQAAAAMAAAAAAAAAAHh0ckWIhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/
+ iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/
+ iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/
+ iISB/4iEgf8AAAA0AAAAIwAAAAoAAAAAAAAAAIiEgf/Rxr3/0ca9/9HGvf/Rxr3/0ca9/9HGvf/Rxr3/
+ 0ca9/9HGvf/Rxr3/0ca9/9HGvf/Rxr3/0ca9/9HGvf/Rxr3/0ca9/9HGvf/Rxr3/0ca9/9HGvf/Rxr3/
+ 0ca9/9HGvf/Rxr3/0ca9/9HGvf/Rxr3/0ca9/9HGvf/Rxr3/0ca9/9HGvf/Rxr3/0ca9/9HGvf/Rxr3/
+ 0ca9/9HGvf/Rxr3/0ca9/9HGvf+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3/+/v8//z7/P/7+/v/
+ +/r7//r7+//6+vr/+vr6//n5+v/5+fn/+Pn4//j4+P/4+Pf/9/j3//f39//29vf/9vb2//b29v/19fb/
+ 9fX1//T19P/09PT/9PT0//Tz9P/z9PP/8/Pz//Py8v/y8vL/8vPz//Ly8v/y8fL/8fHy//Hx8f/x8fH/
+ 8PHw//Hx8P/w8PD/8PDw//Dw8P/w8PD//f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3/
+ /Pz8//v8+//7+/v/+/v7//r6+v/7+/r/+fr6//n5+f/5+fn/+fn5//n4+P/4+Pj/+Pj3//f39//39/f/
+ 9/b2//b39v/29fb/9fX1//X19P/19fX/9PX0//Tz9P/08/T/8/Pz//Pz8//z8/L/8vLy//Ly8v/y8fL/
+ 8fLy//Hx8f/x8PH/8fHx//Dw8P/w8fD/8PDw//Dw8P/w8PD//f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAA
+ AAAAAIiEgf/Rxr3//Pz9//z8+//8+/z//Pz8//v7+//Lycn/y8nJ/8vJyf/Lycn/y8nJ//j4+f/4+Pj/
+ +Pj3//f39//39/f/9/f3//f29v/29vb/9fb2//X19f/19PX/9PX0//T09P/09PT/9PP0//Pz8//z8/P/
+ 8vPy//Ly8v/y8vL/8vHx//Hx8f/x8fH/8fHx//Dx8f/w8fD/8PDw//Dw8P/w8PD//f7+/9HGvf+IhIH/
+ AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3//Pz9//z8/P/8+/z//Pz8//v7+//Mycn/+vr6/+fZyP+1gUH/
+ u6KD//j5+f/4+Pn/+Pj4//j4+P/39/f/9/f3//f39//29vb/9fb1//b19f/19fX/9fX1//T09P/09PT/
+ 9PTz//Pz8//z8/P/8vPz//Pz8v/y8vL/8vLy//Ly8f/x8fH/8fHx//Hx8f/x8PH/8PDw//Hw8P/w8PD/
+ /f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3//P39//z9/f/8/Pz//Pv8//v7+//Mycn/
+ 9fDs/7J5KP+6dQr/q2sa//bz8f/5+Pj/+fj5//j4+P/49/f/9/f3//f39//29/f/9vb2//b29f/19fX/
+ 9fX1//X19f/09PT/9PPz//P09P/z8/P/8vPy//Ly8v/z8/L/8vLy//Ly8v/y8fH/8fHx//Dx8f/x8fD/
+ 8PHw//Dx8f/w8PD//f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3//fz8//38/P/8/Pz/
+ /Pv7//v8+//Fs6D/tngp/7NtC/+5dRH/uXEL/8aYYP/5+fn/+Pj5//n4+P/4+Pj/9/f3/6iSlf+okpX/
+ qJKV/6iSlf+okpX/qJKV/6iSlP+okpT/qJKU/6iSlP+okpT/p5KU/6eSlP+nkpT/p5KU/6eSlP+nkpT/
+ p5GU//Hx8f/x8fD/8PHx//Dw8P/x8fD//f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3/
+ /f39//38/f/8/fz//Pz8//z8/P+8kl//tWwK/7h9NP/Bq5H/uHQQ/7Z2D//n1b7/+fn5//n5+P/4+Pj/
+ +Pj3/6iSlf+okpX/qJKV/6iSlf+okpX/qJKV/6iSlf+okpT/qJKV/6iSlP+okpT/p5KU/6eSlP+nkpT/
+ p5KU/6eSlP+nkpT/p5GU//Lx8f/x8fH/8fHx//Hx8f/w8PD//f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAA
+ AAAAAIiEgf/Rxr3//f39//39/f/8/fz//fz9//z8/P/v5Nb/3sGc//Xv6P/7+vv/y5pa/8GHC//Fijf/
+ +fn5//n5+f/5+Pj/+Pf4//j3+P/49/f/9vf2//b29//29vb/9vX2//X19f/19fX/9PT1//T09P/09PT/
+ 8/Pz//Pz8//z8/P/8vPy//Pz8v/y8vL/8fLy//Hx8v/x8vH/8fHx//Dx8f/w8fD//f7+/9HGvf+IhIH/
+ AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3//f3+//39/f/8/Pz//fz9//z8/P/7+/z//Pz7//v7+//7+vv/
+ 9e7k/8WIIf/Gjhz/2riA//n5+f/5+Pj/+Pj4//j49//49/j/9/f3//f29//29vb/9vb2//b19f/29fb/
+ 9fX1//X19f/09PT/8/T0//P09P/z8/P/8/Pz//Lz8v/z8vP/8vLx//Hx8v/x8fH/8fHx//Dx8f/x8fH/
+ /f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3//f3+//39/v/9/f3//f39//38/f/8/Pz/
+ /Pz8//v8+//7+/v/+vr6/+PKnf/NoSH/zJkk/+/lzf/5+fn/+Pn5//j4+P/49/j/9/f3//f29//39vf/
+ 9vb2//X29v/19vX/9fX1//X19P/09PT/9PT0//Tz9P/z9PP/8/Pz//Lz8v/z8vL/8vHy//Ly8f/y8fH/
+ 8fHx//Hw8f/x8fH//f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3//v7+//39/f/9/f3/
+ /f39//39/f/Mysr/zMrK/8zKyf/Mycn/zMnJ//r7+v/ZuFz/1q46/9SvRP/18uf/+Pn5//n4+P/4+Pj/
+ +Pf3//f39//39/f/9vb3//b29v/29fX/9fX1//X19f/09fT/9PT0//Tz9P/z9PT/8/Pz//Pz8//y8/P/
+ 8vLy//Hy8v/y8vL/8vHx//Hy8f/x8fD//f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3/
+ /f7+//7+/f/9/v3//f39//38/P/Mysr//Pz8/+jbyf+2gUH/u6KD//r6+v/39e7/2rZL/9iyQf/ewW3/
+ +Pn5//j5+P/4+Pj/+Pf3//f3+P/39/f/9vf2//b29v/29fX/9fX1//X19f/19fX/9fX0//T09P/09PT/
+ 8/Pz//Pz8//y8vP/8vPy//Ly8v/y8vL/8vLy//Hx8f/x8fH//f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAA
+ AAAAAIiEgf/Rxr3//v7+//79/f/9/f3//f39//39/f/Mysr/9vPt/7N5KP+6dQr/q2sa//j18v/6+vv/
+ 7eG6/9u4Vv/hx3z/+fn5/6mTlv+ok5b/qJOW/6iSlv+okpX/qJKV/6eSlP+nkpT/p5KU/6eRlP+nkZT/
+ ppGU/6aRk/+mkZP/ppCT/6aQk/+mkJP/ppCS//Ly8//y8vL/8vLx//Lx8f/x8fH//f7+/9HGvf+IhIH/
+ AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3//v7///7+/v/+/v7//f7+//39/f/FtKH/tnkp/7NtC/+5dRH/
+ uXEL/8eZYf/7+/v/+vr7//r6+v/6+vr/+fn6/6iSlf+okpX/qJKV/6iSlf+okpX/qJKV/6iSlf+okpX/
+ qJKV/6iSlf+okpX/qJKV/6iSlP+okpT/qJKU/6eSlP+nkpT/p5KU//Py8v/z8vL/8vLy//Hy8f/x8vL/
+ /f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3//v/+//7+/v/9/f3//f7+//39/f+8k1//
+ tWwK/7h+NP/CrJL/uHQQ/7Z2D//p1r//+/v7//r6+//6+vr/+vn6//n5+f/5+fj/+Pn4//j5+P/4+Pf/
+ 9/j3//f39//29vb/9vb3//b29f/29vb/9fX1//T19P/19PT/9PT0//P08//z8/P/8/Pz//Py8v/z8vL/
+ 8/Ly//Ly8f/y8fL//f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3//v7+//7+///+/v7/
+ /v3+//3+/v/x5tf/4MOe//bw6v/8/Pz/y5tb/8GHC//Fijf/+vv7//r6+//6+vr/+vr6//n5+f/5+fn/
+ +Pn5//j5+P/4+Pj/+Pj3//f3+P/39/f/9vb2//b29v/19fX/9fb2//X19f/19PX/9PT1//P09P/09PP/
+ 8/Pz//Pz8v/y8vP/8vLz//Ly8v/y8vL//f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3/
+ /v////7//v/+/v7//v7+//39/f/+/v3//f39//z8/P/8/fz/9u/m/8WIIv/Gjhz/27mB//v6+//6+/v/
+ +vr6//n6+f/5+fr/+fn5//n5+P/4+Pn/9/j4//j39//39/f/9/b3//b29v/29vb/9vX2//X29f/19PX/
+ 9PX1//T09P/z8/T/8/Tz//Pz8//z8/P/8vPy//Ly8v/y8vH//f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAA
+ AAAAAIiEgf/Rxr3///////7//v///v7//v7+//7+/v/9/f7//f39//39/f/9/fz//Pz9/+XMn//NoSH/
+ zJkk//Hnzv/6+/v/+vr7//r6+v/6+vr/+fr5//n5+f/5+Pj/+Pj4//f3+P/4+Pj/9/f3//b39v/29vf/
+ 9fb2//X29v/19fX/9fT0//T09P/09PT/9PTz//Pz8//z8/P/8/Ly//Ly8v/y8vL//f7+/9HGvf+IhIH/
+ AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3//v////7+/v/+/v7//v7+//7+/v/Mysr/zMrK/8zKyv/Mysr/
+ zMrK//z8/P/auV3/1q46/9WvRP/28+n/+/r6//r6+//6+vr/+fn5//n5+f/5+fn/+fn4//j4+P/39/f/
+ 9/f3//b39//29/b/9vb2//b29f/19vX/9fX1//X19P/09PT/9PT0//Tz9P/08/P/8/Pz//Pz8v/y8vL/
+ /f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3///////7///////7//v7+//7+/v/Mysr/
+ /v3+/+ncyv+2gkL/u6KE//z8/f/69/D/2rZL/9iyQf/fwW7/+/r7/6iSlf+okpX/qJKV/6iSlf+okpX/
+ qJKV/6iSlf+okpX/qJKV/6iSlf+okpX/qJKV/6iSlf+okpX/qJKV/6iSlf+okpT/qJKU//P09P/08/P/
+ 8/Pz//Lz8//y8vL//f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/Rxr3//////////v///v7/
+ /v7+//7+/v/Mysr/+PPv/7N5Kf+6dQr/q2sa//r28//8/Pz/7+K7/9y4Vv/ix3z/+/v7/6uWmf+rlpn/
+ q5aY/6uVmP+qlpj/qpSY/6qUl/+qlJf/qpSX/6mUl/+plJf/qZSX/6iTlv+ok5b/qJOW/6iTlv+nk5X/
+ qJKV//T09P/09PP/8/Pz//Pz8//z8/P//f7+/9HGvf+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/g2dP/
+ ///////////+/////v///////v/FtKH/t3kp/7NtC/+5dRH/uXEL/8iaYf/8/f3//Pz8//z8+//8/Pv/
+ /Pv7//v7+//6+vv/+vr6//r6+f/5+fr/+fn5//j4+f/5+Pj/+Pj4//f49//39/f/9vf3//b29v/29vb/
+ 9fb1//X29f/19fX/9fT0//T09P/09PP/8/T0//Pz9P/z8/P//f7+/+DZ0/+IhIH/AAAAKgAAAA4AAAAA
+ AAAAAIiEgf/g2dP///////////////////7//////v+8k1//tWwK/7h+NP/CrJL/uHQQ/7Z2D//q2MH/
+ /Pz8//z8/P/8+/z/+/v7//v7+//7+/v/+vv6//r6+v/5+fr/+vn5//n5+f/5+Pn/+Pj4//j3+P/49/f/
+ 9/f3//f29//29vb/9vb2//b29f/19fX/9PX1//T09f/09PT/8/T0//P08//z8/P//f7+/+DZ0/+IhIH/
+ AAAAKgAAAA4AAAAAAAAAAIiEgf/g2dP///////////////////7////+///y5tn/4cSe//jy6//+/f7/
+ zJtb/8GHC//Gizf//f39//z9/f/8/Pz//Pz8//v8+//7+/v/+vr7//v6+v/5+vn/+fn6//r5+f/5+Pn/
+ +Pj4//j4+P/3+Pj/9/f4//f39//39/f/9vb2//X29f/29vb/9fX1//T09f/09PT/9PT0//Pz9P/z8/P/
+ /f7+/+DZ0/+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/g2dP////////////////////////////+/v//
+ /v7///7+/v/+/v7/9/Dn/8WIIv/Gjhz/3LqC//z9/f/8/P3//Pz8//v8/P/8+/v/+/v6//r6+v/6+vr/
+ +vr5//n6+v/5+fn/+Pn4//n5+P/3+Pj/+Pj3//f39//29/f/9vb3//b29v/29fX/9fX1//X19P/19PT/
+ 9PT0//Tz9P/09PT//f7+/+DZ0/+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/g2dP/////////////////
+ ///////////+/////v////7+/v/+/v7//v7+/+XNn//NoSH/zJkk//Poz//8/Pz//Pz8//z7+//8+/v/
+ +/v7//v7+//6+/v/+vr6//r5+f/6+fn/+fj5//n5+P/4+Pj/+Pj4//f39//39/f/9/f3//b29v/29vb/
+ 9fX1//X19f/19fX/9fT0//P09P/z8/P//f7+/+DZ0/+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/g2dP/
+ ///////////////////////+//////7///7+//7+///+/v///v7+//39/f/auV3/1q46/9WvRf/49Or/
+ /Pz8//z8+//8+/v/+/v7//v7+//7+vv/+vr6//n6+v/6+fn/+fn5//j5+f/5+Pj/+Pj4//j49//39/f/
+ 9/f2//f39//29vb/9fb2//X29f/19fX/9fX1//T09P/z9PT//f7+/+DZ0/+IhIH/AAAAKgAAAA4AAAAA
+ AAAAAIiEgf/g2dP//////////////////////////////v////////7+///+/v7//v7+//7+/v/6+PH/
+ 2rZL/9iyQf/fwm7//f38//z9/P/7/Pz/+/v8//v7+//7+/v/+vr6//r6+v/6+fr/+fr6//n5+f/4+fj/
+ +Pj4//j4+P/3+Pf/9/f3//b39v/39vf/9vb2//X29v/19fX/9fX0//X09P/09PT//f7+/+DZ0/+IhIH/
+ AAAAKgAAAA4AAAAAAAAAAIiEgf/g2dP////////////////////////////////////+/////v/+//7/
+ /v7+//7+/v/9/v7/7+O8/9y4Vv/iyH3//Pz9//z8/f/8/Pz//Pz8//z8+//7+/v/+/v6//v7+v/6+vr/
+ +vn5//r5+f/5+fn/+Pn4//j4+P/4+Pj/9/j3//f39v/29vb/9vb2//b29f/29fX/9fX1//T19P/09fT/
+ /f7+/+DZ0/+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/g2dP/////////////////////////////////
+ ///////+///+/v///v7////+///9/v7//v7+//39/v/9/f3//fz9//z8/P/8/Pz/+/z8//z8+//7+/v/
+ +/v7//v7+//6+vr/+vr6//r6+f/5+fn/+fn5//j4+P/4+Pf/9/f3//f39//39/f/9vb2//b19v/29fX/
+ 9vb1//X19P/09PX//f7+/+DZ0/+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/g2dP/vr/A/76/wP++v8D/
+ vr/A/76/wP++v8D/vr/A/76/wP++v8D/vr/A/76/wP++v8D/vr/A/76/wP++v8D/vr/A/76/wP++v8D/
+ vr/A/76/wP++v8D/vr/A/76/wP++v8D/vr/A/76/wP++v8D/vr/A/76/wP++v8D/vr/A/76/wP++v8D/
+ vr/A/76/wP++v8D/vr/A/76/wP++v8D/vr/A/+DZ0/+IhIH/AAAAKgAAAA4AAAAAAAAAAIiEgf/g2dP/
+ 4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/
+ 4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/
+ 4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/4NnT/+DZ0/+IhIH/AAAAJgAAAAwAAAAA
+ AAAAAIiEgf/g2dP/4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/
+ 4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/
+ 4NnT/+DZ0//g2dP/kXlo/5F5aP/g2dP/4NnT/5F5aP+ReWj/4NnT/+DZ0/+ReWj/kXlo/+DZ0/+IhIH/
+ AAAAFwAAAAYAAAAAAAAAAIeDgGyIhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/
+ iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/
+ iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/
+ iISB/4iEgf9/fHlyAAAABgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////wAA////////AAD///////8AAP///////wAA
+ ////////AAAAAAAAAAcAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAA
+ AAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAwAA
+ AAAAAAADAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAA
+ AAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAwAA
+ AAAAAAADAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAA
+ AAMAAAAAAAAAAwAA////////AAD///////8AAP///////wAA////////AAD///////8AACgAAAAgAAAA
+ QAAAAAEAIAAAAAAAgBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAYAAAAL
+ AAAADQAAAA0AAAANAAAADQAAAA0AAAANAAAADQAAAA0AAAANAAAADQAAAA0AAAANAAAADQAAAA0AAAAN
+ AAAADQAAAA0AAAANAAAADQAAAA0AAAANAAAADQAAAA0AAAANAAAADQAAAA0AAAALAAAABgAAAAIAAAAG
+ AAAAFgAAACMAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAm
+ AAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACYAAAAmAAAAJgAAACMAAAAW
+ AAAABouGhIGFgX73iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/
+ iISB/4iEgf+KhoP/jImG/4+Mif+Piof/i4eF/4iEgv+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/
+ eXVzlAAAACMAAAALiISB//z7+//7+/r/+vr6//r6+f/5+vn/+fn4//j4+P/3+Pj/9/f3//f39//29vb/
+ 9vb2//X19f/09fX/9PT1//T08//08/P/8/Lz//Ly8//x8vL/8fHx//Lx8f/x8fH/8fHx//Dx8P/w8PD/
+ 8PDw//v59v+IhIH/AAAAJgAAAA2IhIH/+/z7//v7+//6+/v/+/r6//n5+f/5+fn/+Pn5//j4+P/39/f/
+ 9/f3//b29//29vb/9fX2//X19f/19fX/9PT0//Pz8//z8/P/8/Lz//Ly8v/x8vL/8fLx//Hx8f/x8fH/
+ 8fHx//Dw8P/w8PD/+/n2/4iEgf8AAAAmAAAADYiEgf/8+/v/+/z7//v7+//7+vr/2NLX/83Yzf/CtKX/
+ 1t/V//f3+P/39/j/9/f3//b29v/19vb/9fX2//X19f/09PT/9PT0//P09P/z8/L/8vLz//Py8v/y8vL/
+ 8fHx//Hx8P/x8fH/8fDw//Dw8P/7+fb/iISB/wAAACYAAAANiISB//z8/P/8+/z/+/v7//r7+v/U3NP/
+ 4NC3/7F5FP/TuJD/+Pj4//j4+P/39/j/9vf3//f29v/29fX/9fX1//X09f/09PT/9PT0//Pz8//z8/P/
+ 8vLy//Ly8v/y8vL/8vHy//Hx8f/x8PH/8PDw//v59v+IhIH/AAAAJgAAAA2IhIH//f39//z8/P/7/Pv/
+ /Pv7/7+ke/+vbgL/uYIb/7uFKv/y6+H/+fj4//j4+P/39/f/9/b3//b29v/19fb/9fX1//T09P/09PT/
+ 8/T0//Pz8//z8/P/8vLy//Ly8v/y8vL/8fHx//Dx8f/x8fD/+/n2/4iEgf8AAAAmAAAADYiEgf/9/f3/
+ /f39//z8/P/7+/v/z6ty/82oa//Wxa//uYAJ/9Owcf/4+fn/+Pj4/5Z8gP+WfID/lnyA/5Z8gP+WfID/
+ lXyA/5V8gP+VfID/lXx//5V8f/+VfH//lXx///Ly8v/x8vH/8fHx//Dx8f/7+fb/iISB/wAAACYAAAAN
+ iISB//39/f/9/f3//Pz9//z8/P/8+/v/+/v7//v6+v/XtHP/w5EW/+vcuv/4+Pj/+Pj4//f49//39/b/
+ 9/b2//X29v/19fX/9fX1//T19P/z9PT/9PTz//Lz8v/y8/L/8vLy//Hy8v/x8fH/8fHx//v59v+IhIH/
+ AAAAJgAAAA2IhIH//f39//z9/f/9/f3//Pz9/+rq6v/r5+r/5+jl/+7j0P/SsDf/1LJE//bz6f/4+Pn/
+ +Pf4//f3+P/29/f/9/b2//X29f/19fX/9PT0//T09P/09PT/8/Pz//Lz8//y8/P/8vLy//Lx8v/x8vL/
+ +/n2/4iEgf8AAAAmAAAADYiEgf/9/f7//f39//39/f/8/P3/29XZ/+HZy/+0lWn/3s+6//Hpzv/Yujv/
+ 3shw//j4+P/4+Pj/+Pj4//f39//29vb/9vb2//b19v/19fX/9PT0//T09P/08/T/8/Pz//Pz8v/y8vL/
+ 8fLy//Hx8f/7+fb/iISB/wAAACYAAAANiISB//7+/f/+/f3//f39//z9/f/WzL//zKhv/7RwAP/Kpm//
+ +/v6/+vepf/l0ov/+fn5//j4+P/4+Pj/9/j4//f29v/29/b/9fb2//X19f/09fX/9PX0//T09P/z9PP/
+ 8vPz//Py8//y8vL/8fLy//v59v+IhIH/AAAAJgAAAA2IhIH//v7+//7+/v/+/v3//f39/7uVXP+zdhD/
+ uo9I/7Z6Df/p2cP/+/r6//r6+v+WfID/lnyA/5Z8gP+WfID/lnyA/5Z8gP+WfID/lXyA/5V8gP+VfID/
+ lXyA/5V8f//z8/T/8/Pz//Lz8//y8vL/+/n2/4iEgf8AAAAmAAAADYiEgf/+/v7//v7+//3+/v/+/f3/
+ 5tCv/+jVt//v59z/wY4j/8mZQf/5+Pf/+vr7//r6+v/5+fn/+fn5//j4+P/3+Pj/9/f3//b29v/29vb/
+ 9fX2//X19f/09fX/9PT0//Tz9P/z8/P/8/Lz//Py8//7+fb/iISB/wAAACYAAAANiISB///+/v/+/v7/
+ /v7+//7+/f/9/f3//f39//38/P/lz6f/yJwb/+HJiv/7+/r/+vr6//n6+f/5+fn/+fj4//j4+P/4+Pf/
+ 9/f3//b29//19vb/9vb1//X19P/09PT/9PT0//T08//z8/P/8vLz//v59v+IhIH/AAAAJgAAAA2IhIH/
+ //////7+///+/v7//f7+/+Ta4v/j3OL/2NPV/+zhz//dw2n/07Ew/+/mxP/6+vr/+vn6//n5+f/5+fj/
+ +fj5//j4+P/49/f/9vb3//b39v/29fb/9fX1//X19f/19PT/9PT0//Tz8//z8vP/+/n2/4iEgf8AAAAm
+ AAAADYiEgf////////7///7////+/v7/2tvZ/+nezf+xgDH/2cWo//v59v/cwVD/28Bb//r7+//6+vr/
+ +fr5//n5+f/5+fj/+Pj4//f4+P/39/f/9/f3//b29v/19vX/9fX1//X19P/09PX/8/T0//Pz8//7+fb/
+ iISB/wAAACYAAAANiISB//////////7///7+//7+/v/Jtpr/t30i/7l+Cv/Bkkj/+vj1//Xw2v/y6cn/
+ ln2A/5Z9gP+WfID/lnyA/5Z8gP+WfID/lnyA/5Z8gP+WfID/lnyA/5Z8gP+VfID/9fX1//T19f/09PT/
+ 8/T0//v59v+IhIH/AAAAJgAAAA2KhoT2/////////////////////8ObW/+/izj/yK6J/7d9B//ewpj/
+ /fz8//v7/P/7+/v/+/v7//r7+v/6+vr/+fn6//n5+f/5+fj/+Pj4//f3+P/39/b/9vb3//X29v/19fX/
+ 9PX1//T09P/08/T/+/n2/4iEgf8AAAAmAAAADYqGhPb///////////////////7/+PLr//r38//+/v3/
+ zqNP/8SQIP/17dz//Pz8//v8+//7+/v/+/r6//r6+v/6+fr/+fn5//n4+f/4+Pj/9/f4//f39//39/f/
+ 9vb3//X29f/19fX/9fT0//T19P/7+fb/iISB/wAAACYAAAANioaE9v/////////////////////+////
+ /v7+//7+/f/y5dH/zKMh/9i5Xv/8/P3//Pz8//z7+//7+/v/+/v7//r6+v/6+vn/+fn5//n4+f/4+Pj/
+ +Pj3//f39//39/f/9vb2//X19f/19fX/9PT1//v59v+IhIH/AAAAJgAAAA2KhoT2////////////////
+ //////////////7//v/+//79/f/p2Z//1bY3/+bUkf/9/fz//Pz8//v8/P/7+/v/+/r6//r6+v/5+vn/
+ +fj5//j4+P/4+Pj/9/j3//b39v/29vf/9vX1//X19f/09fX/+/n2/4iEgf8AAAAmAAAADYuGhPb/////
+ //////////////////7//////v/+/////v7+//39/v/jznz/38dq//39/f/8/Pz//Pv8//z7+//7+/r/
+ +vr7//r6+f/5+vn/+fn5//j5+P/3+Pf/+Pf3//f39v/39vb/9vb1//X19f/7+fb/iISB/wAAACYAAAAN
+ joqI9svJx//Lycf/y8nH/8vJx//Lycf/y8nH/8vJx//Lycf/y8nH/8vJx//Lycf/y8nH/8vJx//Lycf/
+ y8nH/8vJx//Mysn/zcvK/83Myv/NzMr/zcvJ/8zKyP/Mysj/y8nH/8vJx//Lycf/y8nH/8vJx/+IhIH/
+ AAAAIwAAAAuLiYf+5t7Y/9/X0f/e19H/3tfR/97X0f/e19H/3tfR/97X0f/e19H/3tfR/97X0f/e19H/
+ 3tfR/97X0f/e19H/3tfR/97X0f/e19H/3tfR/97X0f/e19H/3tfR/5F5aP/e19H/kXlo/97X0f+ReWj/
+ 3tfR/4iEgf8AAAAWAAAABpGMiXyIhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/
+ iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/iISB/4iEgf+IhIH/
+ iISB/4iEgf+IhIH/i4aEgQAAAAYAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8AAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////////8oAAAA
+ EAAAACAAAAABACAAAAAAAEAEAAAAAAAAAAAAAAAAAAAAAAAAl5KP/5eSj/+Xko//l5KP/5eSj/+Xko//
+ l5KP/5eSj/+Xko//l5KP/5eSj/+Xko//l5KP/5eSj/+Xko//l5KP/5eSj//+/f7//fz9//v7+//6+vn/
+ +Pn4//f39//19vb/9fT0//Pz8//y8vL/8fHx//Dw8P/w8PD/8PDw/5eSj/+Xko///v7+//39/f/k2eL/
+ 4trh/9bR1P/o38z/9/f3//X19f/09PT/8vLz//Hx8f/x8fH/8PDw//Dw8P+Xko//l5KP///+///+/v7/
+ 2trY/+fdzP+whDH/1sWm//f3+P/29vX/9fT0//Pz9P/y8vL/8fHx//Hw8f/w8PD/l5KP/5eSj///////
+ /v7+/8i3mv+3gyL/uYQK/8CVR//29PL/9vf2/6uYmv+rl5r/qpea/6qXmf+qlpn/8PDw/5eSj/+Xko//
+ /////////v/Dnlv/v5A4/8iwiP+3gwf/28KV//j3+P/29/b/9fX1//Pz9P/y8vP/8vHx//Dw8f+Xko//
+ l5KP////////////+PTq//n38v/9/Pz/zadO/8SWIP/w6tf/9/f3//b19f/09PT/8/Pz//Ly8v/x8fH/
+ l5KP/5eSj/////////////7//v/+/v7//f39//Dl0P/MqSH/17xc//f3+P/29/b/9fX1//Tz9P/z8vL/
+ 8fHy/5eSj/+Xko///////////////////v/+//7+/v/8/P3/6Nqd/9W7N//k1Y//+Pf3//b29v/09fX/
+ 8/Tz//Lz8v+Xko//l5KP//////////////////7//v/+/v7//f39//z8+//i0Xz/3spp//j4+P/39vf/
+ 9fX1//T09P/z8/P/l5KP/5eSj//NzMr/zczK/83Myv/NzMr/zczK/83Myv/NzMr/zczK/83Myv/NzMr/
+ zczK/83Myv/NzMr/zczK/5eSj/+Xko//4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/4NnT/+DZ0//g2dP/
+ kXlo/+DZ0/+ReWj/4NnT/5F5aP+Xko//l5KP/5eSj/+Xko//l5KP/5eSj/+Xko//l5KP/5eSj/+Xko//
+ l5KP/5eSj/+Xko//l5KP/5eSj/+Xko//l5KP/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAA//8AAP//AAA=
+</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/Source/WrtSettings/Nvram.cs b/Source/WrtSettings/Nvram.cs
new file mode 100644
index 0000000..5db1d64
--- /dev/null
+++ b/Source/WrtSettings/Nvram.cs
@@ -0,0 +1,462 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.IO.Compression;
+using System.Text;
+
+namespace WrtSettings {
+ internal class Nvram {
+
+ public Nvram(string fileName, NvramFormat format) {
+ try {
+ byte[] buffer;
+ using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) {
+ buffer = new byte[(int)stream.Length];
+ stream.Read(buffer, 0, buffer.Length);
+ }
+
+ this.Variables = new Dictionary<string, string>();
+
+ if (((format & NvramFormat.AsuswrtVersion1) != 0) && TryParseAsuswrtVersion1(buffer)) {
+ } else if (((format & NvramFormat.AsuswrtVersion2) != 0) && TryParseAsuswrtVersion2(buffer)) {
+ } else if (((format & NvramFormat.Tomato) != 0) && TryParseTomato(buffer)) {
+ } else if (((format & NvramFormat.Text) != 0) && TryParseText(buffer)) {
+ } else {
+ throw new FormatException("Unrecognized format.");
+ }
+ this.FileName = fileName;
+ } catch (FormatException) {
+ throw;
+ } catch (Exception ex) {
+ throw new FormatException(ex.Message);
+ }
+ }
+
+
+ public string FileName { get; private set; }
+ public NvramFormat Format { get; set; }
+ public IDictionary<String, String> Variables { get; private set; }
+
+
+ public void Save(string fileName) {
+ try {
+ byte[] buffer;
+
+ switch (this.Format) {
+ case NvramFormat.AsuswrtVersion1: buffer = GetBytesForAsuswrtVersion1(); break;
+ case NvramFormat.AsuswrtVersion2: buffer = GetBytesForAsuswrtVersion2(); break;
+ case NvramFormat.Tomato: buffer = GetBytesForTomato(); break;
+ case NvramFormat.Text: buffer = GetBytesForText(); break;
+ default: throw new InvalidOperationException("Unsupported format!");
+ }
+
+ using (var stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.Read)) {
+ stream.Write(buffer, 0, buffer.Length);
+ }
+ this.FileName = fileName;
+ } catch (InvalidOperationException) {
+ throw;
+ } catch (Exception ex) {
+ throw new InvalidOperationException(ex.Message);
+ }
+ }
+
+
+ #region Open
+
+ private bool TryParseAsuswrtVersion1(byte[] buffer) {
+ if (buffer.Length < 8) {
+ Debug.WriteLine("NVRAM: Asuswrt 1: File is too small!");
+ return false;
+ }
+
+ string header = ToString(buffer, 0, 4);
+ if (!header.Equals("HDR1")) {
+ Debug.WriteLine("NVRAM: Asuswrt 1: Unknown header '" + header + "'!");
+ return false;
+ }
+
+ var len = ToInt32(buffer, 4);
+ if (len > (buffer.Length - 8)) {
+ Debug.WriteLine("NVRAM: Asuswrt 1: length mismatch!");
+ return false;
+ }
+
+ //decode variables
+ var lastStart = 8;
+ for (int i = 8; i < len + 8; i++) {
+ if (buffer[i] == 0x00) {
+ if (i != lastStart) { //skip empty ones
+ var variable = Encoding.ASCII.GetString(buffer, lastStart, i - lastStart);
+ Debug.WriteLine("NVRAM: Asuswrt 1: " + variable);
+ var parts = variable.Split(new char[] { '=' }, 2);
+ if (parts.Length == 2) {
+ this.Variables.Add(parts[0], parts[1]);
+ } else {
+ Debug.WriteLine("NVRAM: Asuswrt 1: Cannot parse '" + variable + "'!");
+ return false;
+ }
+ }
+ lastStart = i + 1;
+ }
+ }
+
+ this.Format = NvramFormat.AsuswrtVersion1;
+ return true;
+ }
+
+ private bool TryParseAsuswrtVersion2(byte[] buffer) {
+ if (buffer.Length < 8) {
+ Debug.WriteLine("NVRAM: Asuswrt 2: File is too small!");
+ return false;
+ }
+
+ string header = ToString(buffer, 0, 4);
+ if (!header.Equals("HDR2")) {
+ Debug.WriteLine("NVRAM: Asuswrt 2: Unknown header '" + header + "'!");
+ return false;
+ }
+
+ var len = ToInt24(buffer, 4);
+ if (len > (buffer.Length - 8)) {
+ Debug.WriteLine("NVRAM: Asuswrt 2: Length mismatch!");
+ return false;
+ }
+
+ //decoding stupid "encryption"
+ var random = buffer[7];
+ for (int i = 8; i < len + 8; i++) {
+ if (buffer[i] > (0xFD - 0x01)) {
+ buffer[i] = 0x00;
+ } else {
+ buffer[i] = (byte)((0xFF + random - buffer[i]) % 256);
+ }
+ }
+
+ //decode variables
+ var lastStart = 8;
+ for (int i = 8; i < len + 8; i++) {
+ if (buffer[i] == 0x00) {
+ if (i != lastStart) { //skip empty ones
+ var variable = Encoding.ASCII.GetString(buffer, lastStart, i - lastStart);
+ Debug.WriteLine("NVRAM: Asuswrt 2: " + variable);
+ var parts = variable.Split(new char[] { '=' }, 2);
+ if (parts.Length == 2) {
+ this.Variables.Add(parts[0], parts[1]);
+ } else {
+ Debug.WriteLine("NVRAM: Asuswrt 2: Cannot parse '" + variable + "'!");
+ return false;
+ }
+ }
+ lastStart = i + 1;
+ }
+ }
+
+ this.Format = NvramFormat.AsuswrtVersion2;
+ return true;
+ }
+
+ private bool TryParseTomato(byte[] buffer) {
+ try {
+ using (var msIn = new MemoryStream(buffer) { Position = 0 })
+ using (var msOut = new MemoryStream())
+ using (var gzStream = new GZipStream(msIn, CompressionMode.Decompress)) {
+ var midBuffer = new byte[65536];
+ int read;
+ while ((read = gzStream.Read(midBuffer, 0, midBuffer.Length)) > 0) {
+ msOut.Write(midBuffer, 0, read);
+ }
+ buffer = msOut.ToArray();
+ }
+ } catch (InvalidDataException ex) {
+ Debug.WriteLine("NVRAM: Tomato: Cannot ungzip (" + ex.Message + ")!");
+ return false;
+ }
+
+ string header = ToString(buffer, 0, 4);
+ if (!header.Equals("TCF1")) {
+ Debug.WriteLine("NVRAM: Tomato: Unknown header '" + header + "'!");
+ return false;
+ }
+
+ //decode variables
+ var lastStart = 8;
+ for (int i = 8; i < buffer.Length; i++) {
+ if (buffer[i] == 0x00) {
+ if (i != lastStart) { //skip empty ones
+ var variable = Encoding.ASCII.GetString(buffer, lastStart, i - lastStart);
+ Debug.WriteLine("NVRAM: Tomato: " + variable);
+ var parts = variable.Split(new char[] { '=' }, 2);
+ if (parts.Length == 2) {
+ this.Variables.Add(parts[0], parts[1]);
+ } else {
+ Debug.WriteLine("NVRAM: Tomato: Cannot parse '" + variable + "'!");
+ return false;
+ }
+ }
+ lastStart = i + 1;
+ }
+ }
+
+ var hardwareType = ToInt32(buffer, 4);
+ this.Variables.Add(".HardwareType", hardwareType.ToString(CultureInfo.InvariantCulture));
+
+ this.Format = NvramFormat.Tomato;
+ return true;
+ }
+
+ private bool TryParseText(byte[] buffer) {
+ var content = Encoding.ASCII.GetString(buffer);
+ var lines = content.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
+
+ foreach (var variable in lines) {
+ Debug.WriteLine("NVRAM: Text: " + variable);
+ var parts = variable.Split(new char[] { '=' }, 2);
+ if (parts.Length == 2) {
+ this.Variables.Add(DecodeText(parts[0]), DecodeText(parts[1]));
+ } else {
+ Debug.WriteLine("NVRAM: Text: Cannot parse '" + variable + "'!");
+ return false;
+ }
+ }
+
+ this.Format = NvramFormat.Text;
+ return true;
+ }
+
+ #endregion
+
+
+ #region Save
+
+ private byte[] GetBytesForAsuswrtVersion1() {
+ var buffer = new List<Byte>();
+
+ foreach (var pair in this.Variables) {
+ buffer.AddRange(Encoding.ASCII.GetBytes(pair.Key + "=" + pair.Value + "\0"));
+ }
+
+ //length has to be multiple of 1024
+ var len = buffer.Count + (1024 - buffer.Count % 1024);
+
+ //padding
+ var padding = new byte[len - buffer.Count];
+ buffer.AddRange(padding);
+
+ //insert header in reverse order
+ buffer.InsertRange(0, FromInt32(len));
+ buffer.InsertRange(0, Encoding.ASCII.GetBytes("HDR1"));
+
+ return buffer.ToArray();
+ }
+
+ private Random Random = new Random();
+
+ private byte[] GetBytesForAsuswrtVersion2() {
+ var buffer = new List<Byte>();
+
+ var rand = (byte)Random.Next(0, 256);
+
+ foreach (var pair in this.Variables) {
+ buffer.AddRange(Encoding.ASCII.GetBytes(pair.Key + "=" + pair.Value + "\0"));
+ }
+
+ //length has to be multiple of 1024
+ var len = buffer.Count + (1024 - buffer.Count % 1024);
+
+ //padding
+ var padding = new byte[len - buffer.Count];
+ buffer.AddRange(padding);
+
+ //do stupid "encryption"
+ for (int i = 0; i < buffer.Count; i++) {
+ if (buffer[i] == 0x00) {
+ buffer[i] = (byte)(0xFD + Random.Next(0, 3));
+ } else {
+ buffer[i] = (byte)((0xFF - buffer[i] + rand) % 256);
+ }
+ }
+
+ //insert header in reverse order
+ buffer.Insert(0, rand);
+ buffer.InsertRange(0, FromInt24(len));
+ buffer.InsertRange(0, Encoding.ASCII.GetBytes("HDR2"));
+
+ return buffer.ToArray();
+ }
+
+ private byte[] GetBytesForTomato() {
+ var buffer = new List<Byte>();
+
+ if (!this.Variables.ContainsKey(".HardwareType")) {
+ throw new InvalidOperationException("Data format requires hardware type to be defined (.HardwareType)!");
+ }
+
+ var hardwareTypeText = this.Variables[".HardwareType"];
+ int hardwareType;
+ if (!int.TryParse(hardwareTypeText, NumberStyles.Integer, CultureInfo.InvariantCulture, out hardwareType)) {
+ throw new InvalidOperationException("Data format requires hardware type to be defined (.HardwareType) as an integer!");
+ }
+
+ buffer.AddRange(Encoding.ASCII.GetBytes("TCF1"));
+ buffer.AddRange(FromInt32(hardwareType));
+
+ foreach (var pair in this.Variables) {
+ if (pair.Key.Equals(".HardwareType", StringComparison.Ordinal)) { continue; } //skip virtual keys
+ buffer.AddRange(Encoding.ASCII.GetBytes(pair.Key + "=" + pair.Value + "\0"));
+ }
+
+ using (var msOut = new MemoryStream()) {
+ using (var gzStream = new GZipStream(msOut, CompressionMode.Compress)) {
+ gzStream.Write(buffer.ToArray(), 0, buffer.Count);
+ } //must close stream before returning array (or flush)
+ return msOut.ToArray();
+ }
+ }
+
+ private byte[] GetBytesForText() {
+ var list = new List<string>();
+ foreach (var pair in this.Variables) {
+ list.Add(string.Format(CultureInfo.InvariantCulture, "{0}={1}", EncodeText(pair.Key), EncodeText(pair.Value)));
+ }
+ list.Sort();
+
+ var sb = new StringBuilder();
+ foreach (var item in list) {
+ sb.AppendLine(item);
+ }
+
+ return Encoding.ASCII.GetBytes(sb.ToString());
+ }
+
+ #endregion
+
+
+ #region Helpers
+
+ private static int ToInt24(byte[] buffer, int offset) {
+ var bufferAlt = new byte[4];
+ Buffer.BlockCopy(buffer, offset, bufferAlt, 0, 3);
+ return BitConverter.ToInt32(bufferAlt, 0);
+ }
+
+ private static byte[] FromInt24(int value) {
+ var buffer = BitConverter.GetBytes(value);
+ return new byte[] { buffer[0], buffer[1], buffer[2] };
+ }
+
+
+ private static int ToInt32(byte[] buffer, int offset) {
+ return BitConverter.ToInt32(buffer, offset);
+ }
+
+ private static byte[] FromInt32(int value) {
+ return BitConverter.GetBytes(value);
+ }
+
+
+ private static string ToString(byte[] buffer, int offset, int count) {
+ return Encoding.ASCII.GetString(buffer, offset, count);
+ }
+
+
+ internal static string EncodeText(string text) {
+ var sb = new StringBuilder();
+ foreach (var ch in text) {
+ if (ch == '\n') {
+ sb.Append(@"\n");
+ } else if (ch == '\r') {
+ sb.Append(@"\r");
+ } else if (ch == '\t') {
+ sb.Append(@"\t");
+ } else if (ch == '\b') {
+ sb.Append(@"\b");
+ } else if (ch == '\f') {
+ sb.Append(@"\f");
+ } else if (ch == '\\') {
+ sb.Append(@"\\");
+ } else {
+ var value = Encoding.ASCII.GetBytes(new char[] { ch })[0];
+ if ((value < 32) || (value > 127)) {
+ sb.AppendFormat(CultureInfo.InvariantCulture, @"\x{0:X2}", value);
+ } else {
+ sb.Append(ch);
+ }
+ }
+ }
+ return sb.ToString();
+ }
+
+ internal static string DecodeText(string text) {
+ var sb = new StringBuilder();
+ var sbHex = new StringBuilder();
+
+ var state = DTState.Text;
+ foreach (var ch in text) {
+ switch (state) {
+ case DTState.Text: {
+ if (ch == '\\') {
+ state = DTState.Escape;
+ } else {
+ sb.Append(ch);
+ }
+ } break;
+
+ case DTState.Escape: {
+ switch (ch) {
+ case 'n': sb.Append("\n"); state = DTState.Text; break;
+ case 'r': sb.Append("\r"); state = DTState.Text; break;
+ case 't': sb.Append("\t"); state = DTState.Text; break;
+ case 'b': sb.Append("\b"); state = DTState.Text; break;
+ case 'f': sb.Append("\f"); state = DTState.Text; break;
+ case '\\': sb.Append(@"\"); state = DTState.Text; break;
+ case 'x': state = DTState.EscapeHex; break;
+ default: throw new FormatException("Invalid escape sequence.");
+ }
+ } break;
+
+ case DTState.EscapeHex: {
+ sbHex.Append(ch);
+ if (sbHex.Length == 2) {
+ var hex = sbHex.ToString();
+ sbHex.Length = 0;
+ byte value;
+ if (!byte.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out value)) {
+ throw new FormatException("Invalid hexadecimal escape sequence.");
+ }
+ sb.Append(Encoding.ASCII.GetString(new byte[] { value }));
+ state = DTState.Text;
+ }
+ } break;
+ }
+ }
+
+ if (state == DTState.Text) {
+ return sb.ToString();
+ } else {
+ throw new FormatException("Invalid character sequence.");
+ }
+ }
+
+ private enum DTState {
+ Text,
+ Escape,
+ EscapeHex
+ }
+
+ #endregion
+
+ }
+
+
+ [Flags]
+ internal enum NvramFormat {
+ AsuswrtVersion1 = 1,
+ AsuswrtVersion2 = 2,
+ Tomato = 4,
+ Text = 0x40000000,
+ All = 0x7FFFFFFF,
+ }
+}
diff --git a/Source/WrtSettings/Properties/App.ico b/Source/WrtSettings/Properties/App.ico
new file mode 100644
index 0000000..77784fc
--- /dev/null
+++ b/Source/WrtSettings/Properties/App.ico
Binary files differ
diff --git a/Source/WrtSettings/Properties/App.manifest b/Source/WrtSettings/Properties/App.manifest
new file mode 100644
index 0000000..5ee4d58
--- /dev/null
+++ b/Source/WrtSettings/Properties/App.manifest
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+
+ <dependency xmlns="urn:schemas-microsoft-com:asm.v2">
+ <dependentAssembly>
+ <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
+ </dependentAssembly>
+ </dependency>
+
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+ <requestedExecutionLevel level="asInvoker" uiAccess="false" />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
+ <windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
+ <dpiAware>true</dpiAware>
+ </windowsSettings>
+ </application>
+
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
+ </application>
+ </compatibility>
+
+</assembly>
diff --git a/Source/WrtSettings/Properties/App.snk b/Source/WrtSettings/Properties/App.snk
new file mode 100644
index 0000000..aaa17e3
--- /dev/null
+++ b/Source/WrtSettings/Properties/App.snk
Binary files differ
diff --git a/Source/WrtSettings/Properties/AssemblyInfo.cs b/Source/WrtSettings/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..f355636
--- /dev/null
+++ b/Source/WrtSettings/Properties/AssemblyInfo.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("WrtSettings")]
+[assembly: AssemblyDescription("Handling nvram setting files for WRT router family.")]
+
+[assembly: CLSCompliant(true)]
+[assembly: ComVisible(false)]
+[assembly: NeutralResourcesLanguage("en-US")]
diff --git a/Source/WrtSettings/Properties/SolutionInfo.cs b/Source/WrtSettings/Properties/SolutionInfo.cs
new file mode 100644
index 0000000..18b5786
--- /dev/null
+++ b/Source/WrtSettings/Properties/SolutionInfo.cs
@@ -0,0 +1,8 @@
+using System.Reflection;
+
+[assembly: AssemblyProduct("WRT Settings")]
+[assembly: AssemblyCompany("Josip Medved")]
+[assembly: AssemblyCopyright("Copyright (c) 2014 Josip Medved <jmedved@jmedved.com>")]
+
+[assembly: AssemblyVersion("0.00.*")]
+[assembly: AssemblyInformationalVersion("0.00")]
diff --git a/Source/WrtSettings/WrtSettings.csproj b/Source/WrtSettings/WrtSettings.csproj
new file mode 100644
index 0000000..2d98ec1
--- /dev/null
+++ b/Source/WrtSettings/WrtSettings.csproj
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{7D43DB74-7DE3-4EE4-8591-D520DD1AB0BF}</ProjectGuid>
+ <OutputType>WinExe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>WrtSettings</RootNamespace>
+ <AssemblyName>WrtSettings</AssemblyName>
+ <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>..\..\Binaries\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <UseVSHostingProcess>false</UseVSHostingProcess>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>..\..\Binaries\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <UseVSHostingProcess>false</UseVSHostingProcess>
+ </PropertyGroup>
+ <PropertyGroup>
+ <StartupObject>WrtSettings.App</StartupObject>
+ </PropertyGroup>
+ <PropertyGroup>
+ <SignAssembly>true</SignAssembly>
+ </PropertyGroup>
+ <PropertyGroup>
+ <DelaySign>false</DelaySign>
+ </PropertyGroup>
+ <PropertyGroup>
+ <AssemblyOriginatorKeyFile>Properties\App.snk</AssemblyOriginatorKeyFile>
+ </PropertyGroup>
+ <PropertyGroup>
+ <ApplicationManifest>Properties\App.manifest</ApplicationManifest>
+ </PropertyGroup>
+ <PropertyGroup>
+ <ApplicationIcon>Properties\App.ico</ApplicationIcon>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Drawing" />
+ <Reference Include="System.Windows.Forms" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="%28Medo%29\AboutBox [013].cs" />
+ <Compile Include="%28Medo%29\EntryAssembly [003].cs" />
+ <Compile Include="%28Medo%29\ErrorReport [019].cs" />
+ <Compile Include="%28Medo%29\MessageBox [007].cs" />
+ <Compile Include="%28Medo%29\RecentFiles [005].cs" />
+ <Compile Include="%28Medo%29\State [015].cs" />
+ <Compile Include="%28Medo%29\UnhandledCatch [008].cs" />
+ <Compile Include="%28Medo%29\Upgrade [003].cs" />
+ <Compile Include="MainForm.cs">
+ <SubType>Form</SubType>
+ </Compile>
+ <Compile Include="MainForm.Designer.cs">
+ <DependentUpon>MainForm.cs</DependentUpon>
+ </Compile>
+ <Compile Include="App.cs" />
+ <Compile Include="Nvram.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Properties\SolutionInfo.cs" />
+ <None Include="Properties\App.manifest" />
+ <None Include="Properties\App.snk" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="Properties\App.ico" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="MainForm.resx">
+ <DependentUpon>MainForm.cs</DependentUpon>
+ </EmbeddedResource>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file