diff options
author | Josip Medved <jmedved@jmedved.com> | 2014-04-24 00:24:55 -0700 |
---|---|---|
committer | Josip Medved <jmedved@jmedved.com> | 2014-04-24 00:24:55 -0700 |
commit | bb5a13f79ff83df93b47188debdf44c7df56e025 (patch) | |
tree | f5aac7930597a2bd50b5f541c6a132637c196212 | |
download | WrtSettings-bb5a13f79ff83df93b47188debdf44c7df56e025.zip WrtSettings-bb5a13f79ff83df93b47188debdf44c7df56e025.tar.gz WrtSettings-bb5a13f79ff83df93b47188debdf44c7df56e025.tar.bz2 |
Initial commit.
24 files changed, 6725 insertions, 0 deletions
diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..b1cfaa8 --- /dev/null +++ b/.hgignore @@ -0,0 +1,35 @@ +syntax: glob
+
+*.bak
+*.csproj.user
+*.csproj.vspscc
+*.ldb
+*.mcs
+*.msi
+*.pdb
+*.rar
+*.SCC
+*.suo
+*.testsettings
+*.tmp
+*.vbproj.user
+*.vbproj.vspscc
+*.vbw
+*.vshost.exe
+*.vsmdi
+*.vspscc
+*.vssscc
+*.zip
+
+*/Bin/*
+*/bin/*
+*/Debug/*
+*/obj/*
+*/TestResults/*
+
+syntax: regexp
+
+^.*[B|b]inaries/.*
+^.*[R|r]eleases/.*
+^.*[B|b]ackup/.*
+^.*Temporary/.*
diff --git a/Setup/License.txt b/Setup/License.txt new file mode 100644 index 0000000..42cdb92 --- /dev/null +++ b/Setup/License.txt @@ -0,0 +1,9 @@ +The MIT License
+
+Copyright (c) 2014 Josip Medved <jmedved@jmedved.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Setup/Publish.cmd b/Setup/Publish.cmd new file mode 100644 index 0000000..0e6caf7 --- /dev/null +++ b/Setup/Publish.cmd @@ -0,0 +1,138 @@ +@ECHO OFF
+
+SET FILE_SETUP=".\WrtSettings.iss"
+SET FILE_SOLUTION="..\Source\WrtSettings.sln"
+SET FILES_EXECUTABLE="..\Binaries\WrtSettings.exe"
+SET FILES_OTHER=
+SET FILES_LICENSE="License.txt"
+
+SET COMPILE_TOOL_1="%PROGRAMFILES(X86)%\Microsoft Visual Studio 12.0\Common7\IDE\devenv.exe"
+SET COMPILE_TOOL_2="%PROGRAMFILES(X86)%\Microsoft Visual Studio 12.0\Common7\IDE\WDExpress.exe"
+SET SETUP_TOOL="%PROGRAMFILES(x86)%\Inno Setup 5\iscc.exe"
+
+SET SIGN_TOOL="%PROGRAMFILES(X86)%\Windows Kits\8.0\bin\x86\signtool.exe"
+SET SIGN_HASH="C02FF227D5EE9F555C13D4C622697DF15C6FF871"
+SET SIGN_TIMESTAMPURL="http://timestamp.comodoca.com/rfc3161"
+
+
+ECHO --- BUILD SOLUTION
+ECHO.
+
+IF EXIST %COMPILE_TOOL_1% (
+ ECHO Using Visual Studio
+ SET COMPILE_TOOL=%COMPILE_TOOL_1%
+) ELSE (
+ IF EXIST %COMPILE_TOOL_2% (
+ ECHO Using Visual Studio Express
+ SET COMPILE_TOOL=%COMPILE_TOOL_2%
+ ) ELSE (
+ ECHO Cannot find Visual Studio!
+ PAUSE && EXIT /B 255
+ )
+)
+
+RMDIR /Q /S "..\Binaries" 2> NUL
+%COMPILE_TOOL% /Build "Release" %FILE_SOLUTION%
+IF ERRORLEVEL 1 PAUSE && EXIT /B %ERRORLEVEL%
+
+ECHO.
+
+
+CERTUTIL -silent -verifystore -user My %SIGN_HASH% > NUL
+IF %ERRORLEVEL%==0 (
+ ECHO --- SIGN SOLUTION
+ ECHO.
+
+ IF [%SIGN_TIMESTAMPURL%]==[] (
+ %SIGN_TOOL% sign /s "My" /sha1 %SIGN_HASH% /v %FILES_EXECUTABLE%
+ ) ELSE (
+ %SIGN_TOOL% sign /s "My" /sha1 %SIGN_HASH% /tr %SIGN_TIMESTAMPURL% /v %FILES_EXECUTABLE%
+ )
+ IF ERRORLEVEL 1 PAUSE && EXIT /B %ERRORLEVEL%
+) ELSE (
+ ECHO --- DID NOT SIGN SOLUTION
+ IF NOT [%SIGN_HASH%]==[] (
+ ECHO.
+ ECHO No certificate with hash %SIGN_HASH%.
+ )
+)
+
+ECHO.
+ECHO.
+
+
+ECHO --- BUILD SETUP
+ECHO.
+
+RMDIR /Q /S ".\Temp" 2> NUL
+CALL %SETUP_TOOL% /O".\Temp" %FILE_SETUP%
+IF ERRORLEVEL 1 PAUSE && EXIT /B %ERRORLEVEL%
+
+FOR /F %%I IN ('DIR ".\Temp\*.exe" /B') DO SET _SETUPEXE=%%I
+ECHO Setup is in file %_SETUPEXE%
+
+ECHO.
+ECHO.
+
+
+ECHO --- RENAME LATEST
+ECHO.
+
+SET _OLDSETUPEXE=%_SETUPEXE%
+SET _SETUPEXE=%_SETUPEXE:000=-LATEST%
+IF NOT %_OLDSETUPEXE%==%_SETUPEXE% (
+ ECHO Renaming %_OLDSETUPEXE% to %_SETUPEXE%
+ MOVE .\Temp\%_OLDSETUPEXE% .\Temp\%_SETUPEXE%
+) ELSE (
+ ECHO No rename needed.
+)
+
+ECHO.
+ECHO.
+
+
+CERTUTIL -silent -verifystore -user My %SIGN_HASH% > NUL
+IF %ERRORLEVEL%==0 (
+ ECHO --- SIGN SETUP
+ ECHO.
+
+ IF [%SIGN_TIMESTAMPURL%]==[] (
+ %SIGN_TOOL% sign /s "My" /sha1 %SIGN_HASH% /v ".\Temp\%_SETUPEXE%"
+ ) ELSE (
+ %SIGN_TOOL% sign /s "My" /sha1 %SIGN_HASH% /tr %SIGN_TIMESTAMPURL% /v ".\Temp\%_SETUPEXE%"
+ )
+ IF ERRORLEVEL 1 PAUSE && EXIT /B %ERRORLEVEL%
+) ELSE (
+ ECHO --- DID NOT SIGN SETUP
+)
+ECHO.
+ECHO.
+
+
+ECHO --- BUILD ZIP
+ECHO.
+
+SET _SETUPZIP=%_SETUPEXE:.exe=.zip%
+ECHO Zipping into %_SETUPZIP%
+"%PROGRAMFILES%\WinRAR\WinRAR.exe" a -afzip -ep -m5 -z%FILES_LICENSE% ".\Temp\%_SETUPZIP%" %FILES_EXECUTABLE% %FILES_OTHER%
+IF ERRORLEVEL 1 PAUSE && EXIT /B %ERRORLEVEL%
+
+ECHO.
+ECHO.
+
+
+ECHO --- RELEASE
+ECHO.
+
+MKDIR "..\Releases" 2> NUL
+MOVE ".\Temp\*.*" "..\Releases\." > NUL
+IF ERRORLEVEL 1 PAUSE && EXIT /B %ERRORLEVEL%
+RMDIR /Q /S ".\Temp"
+
+ECHO.
+
+
+ECHO --- DONE
+ECHO.
+
+PAUSE
diff --git a/Setup/WrtSettings.iss b/Setup/WrtSettings.iss new file mode 100644 index 0000000..59e6d06 --- /dev/null +++ b/Setup/WrtSettings.iss @@ -0,0 +1,68 @@ +#define AppName GetStringFileInfo('..\Binaries\WrtSettings.exe', 'ProductName')
+#define AppVersion GetStringFileInfo('..\Binaries\WrtSettings.exe', 'ProductVersion')
+#define AppFileVersion GetStringFileInfo('..\Binaries\WrtSettings.exe', 'FileVersion')
+#define AppCompany GetStringFileInfo('..\Binaries\WrtSettings.exe', 'CompanyName')
+#define AppCopyright GetStringFileInfo('..\Binaries\WrtSettings.exe', 'LegalCopyright')
+#define AppBase LowerCase(StringChange(AppName, ' ', ''))
+#define AppSetupFile AppBase + StringChange(AppVersion, '.', '')
+#define AppVersionEx StringChange(AppVersion, '0.00', '(latest)')
+
+[Setup]
+AppName={#AppName}
+AppVersion={#AppVersion}
+AppVerName={#AppName} {#AppVersion}
+AppPublisher={#AppCompany}
+AppPublisherURL=http://jmedved.com/{#AppBase}/
+AppCopyright={#AppCopyright}
+VersionInfoProductVersion={#AppVersion}
+VersionInfoProductTextVersion={#AppVersion}
+VersionInfoVersion={#AppFileVersion}
+DefaultDirName={pf}\{#AppCompany}\{#AppName}
+OutputBaseFilename={#AppSetupFile}
+OutputDir=..\Releases
+SourceDir=..\Binaries
+AppId=JosipMedved_WrtSettings
+CloseApplications="yes"
+RestartApplications="no"
+UninstallDisplayIcon={app}\WrtSettings.exe
+AlwaysShowComponentsList=no
+ArchitecturesInstallIn64BitMode=x64
+DisableProgramGroupPage=yes
+MergeDuplicateFiles=yes
+MinVersion=0,6.01.7200
+PrivilegesRequired=admin
+ShowLanguageDialog=no
+SolidCompression=yes
+ChangesAssociations=yes
+DisableWelcomePage=yes
+LicenseFile=..\Setup\License.txt
+
+
+[Messages]
+SetupAppTitle=Setup {#AppName} {#AppVersionEx}
+SetupWindowTitle=Setup {#AppName} {#AppVersionEx}
+BeveledLabel=jmedved.com
+
+
+[Files]
+Source: "WrtSettings.exe"; DestDir: "{app}"; Flags: ignoreversion;
+
+
+[Icons]
+Name: "{userstartmenu}\WRT Settings"; Filename: "{app}\WrtSettings.exe"
+
+
+[Registry]
+Root: HKCU; Subkey: "Software\Josip Medved"; ValueType: none; Flags: uninsdeletekeyifempty;
+Root: HKCU; Subkey: "Software\Josip Medved\WrtSettings"; ValueType: none; Flags: deletekey uninsdeletekey;
+
+
+[Run]
+Filename: "{app}\WrtSettings.exe"; Flags: postinstall nowait skipifsilent runasoriginaluser; Description: "Launch application now";
+
+
+[Code]
+procedure InitializeWizard;
+begin
+ WizardForm.LicenseAcceptedRadio.Checked := True;
+end;
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 Binary files differnew file mode 100644 index 0000000..77784fc --- /dev/null +++ b/Source/WrtSettings/Properties/App.ico 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 Binary files differnew file mode 100644 index 0000000..aaa17e3 --- /dev/null +++ b/Source/WrtSettings/Properties/App.snk 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 |