From 06e73045b47c63757bbf24e4ce35ad02f508d5e6 Mon Sep 17 00:00:00 2001 From: Chris Lovett Date: Tue, 15 Oct 2024 16:18:01 -0700 Subject: [PATCH] - Fix issue #425: "No byte order mark on save" option throws stream closed exception add unit test - Apply dark mode to window titlebar --- src/Application/Application.csproj | 1 + src/Application/FormMain.cs | 24 +++++++++++ src/Application/Win32Helpers.cs | 67 ++++++++++++++++++++++++++++++ src/Model/XmlCache.cs | 9 ++-- src/UnitTests/UnitTest1.cs | 39 +++++++++++++++++ src/Updates/Updates.xml | 4 ++ src/Version/Version.cs | 2 + 7 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 src/Application/Win32Helpers.cs diff --git a/src/Application/Application.csproj b/src/Application/Application.csproj index bcdd3a58..559b62d9 100644 --- a/src/Application/Application.csproj +++ b/src/Application/Application.csproj @@ -174,6 +174,7 @@ + FormGotoLine.cs diff --git a/src/Application/FormMain.cs b/src/Application/FormMain.cs index fe86c4a9..e742a374 100644 --- a/src/Application/FormMain.cs +++ b/src/Application/FormMain.cs @@ -1753,6 +1753,9 @@ protected virtual void OnSettingsChanged(object sender, string name) this._analytics.SetEnabled(this._settings.GetBoolean("AllowAnalytics", false)); } break; + case "Theme": + OnThemeChanged(); + break; } this._delayedActions.StartDelayedAction("DelaySaveSettings", @@ -1760,6 +1763,27 @@ protected virtual void OnSettingsChanged(object sender, string name) TimeSpan.FromSeconds(1)); } + private void OnThemeChanged() + { + if ((ColorTheme)this._settings["Theme"] == ColorTheme.Light) + { + ThemeAllControls(this, false); + } + else + { + ThemeAllControls(this, true); + } + } + + private void ThemeAllControls(Control parent, bool darkMode) + { + Win32Helpers.UseImmersiveDarkMode(this.Handle, darkMode); + foreach (Control control in parent.Controls) + { + ThemeAllControls(control, darkMode); + } + } + public void SaveErrors(string filename) { this._taskList.Save(filename); diff --git a/src/Application/Win32Helpers.cs b/src/Application/Win32Helpers.cs new file mode 100644 index 00000000..2c7248d1 --- /dev/null +++ b/src/Application/Win32Helpers.cs @@ -0,0 +1,67 @@ +using System; +using System.Runtime.InteropServices; + +namespace XmlNotepad +{ + internal enum DWMWINDOWATTRIBUTE : uint + { + DWMWA_NCRENDERING_ENABLED, + DWMWA_NCRENDERING_POLICY, + DWMWA_TRANSITIONS_FORCEDISABLED, + DWMWA_ALLOW_NCPAINT, + DWMWA_CAPTION_BUTTON_BOUNDS, + DWMWA_NONCLIENT_RTL_LAYOUT, + DWMWA_FORCE_ICONIC_REPRESENTATION, + DWMWA_FLIP3D_POLICY, + DWMWA_EXTENDED_FRAME_BOUNDS, + DWMWA_HAS_ICONIC_BITMAP, + DWMWA_DISALLOW_PEEK, + DWMWA_EXCLUDED_FROM_PEEK, + DWMWA_CLOAK, + DWMWA_CLOAKED, + DWMWA_FREEZE_REPRESENTATION, + DWMWA_PASSIVE_UPDATE_MODE, + DWMWA_USE_HOSTBACKDROPBRUSH, + DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19, + DWMWA_USE_IMMERSIVE_DARK_MODE = 20, + DWMWA_WINDOW_CORNER_PREFERENCE = 33, + DWMWA_BORDER_COLOR, + DWMWA_CAPTION_COLOR, + DWMWA_TEXT_COLOR, + DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, + DWMWA_SYSTEMBACKDROP_TYPE, + DWMWA_LAST + }; + + + internal static class Win32Helpers + { + [DllImport("Dwmapi")] + static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE dwAttribute, ref int attrValue, uint cbAttribute); + + internal static bool UseImmersiveDarkMode(IntPtr handle, bool enabled) + { + if (IsWindows10OrGreater(17763)) + { + var attribute = DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1; + if (IsWindows10OrGreater(18985)) + { + attribute = DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE; + } + + int useImmersiveDarkMode = enabled ? 1 : 0; + return DwmSetWindowAttribute(handle, attribute, + ref useImmersiveDarkMode, sizeof(int)) == 0; + } + + return false; + } + + private static bool IsWindows10OrGreater(int build = -1) + { + return Environment.OSVersion.Version.Major >= 10 && + Environment.OSVersion.Version.Build >= build; + } + + } +} diff --git a/src/Model/XmlCache.cs b/src/Model/XmlCache.cs index 7bbbf426..a269bb6b 100644 --- a/src/Model/XmlCache.cs +++ b/src/Model/XmlCache.cs @@ -393,7 +393,7 @@ public void SaveCopy(string filename) Settings settings = (Settings)this._site.GetService(typeof(Settings)); if (settings != null) { - noBom = (bool)settings["NoByteOrderMark"]; + noBom = settings.GetBoolean("NoByteOrderMark", false); if (noBom) { // then we must have an XML declaration with an encoding attribute. @@ -414,10 +414,11 @@ public void SaveCopy(string filename) EncodingHelpers.InitializeWriterSettings(w, this._site); _doc.Save(w); } - ms.Seek(0, SeekOrigin.Begin); - - EncodingHelpers.WriteFileWithoutBOM(ms, filename); + using (var stm = new MemoryStream(ms.ToArray())) + { + EncodingHelpers.WriteFileWithoutBOM(stm, filename); + } } else { diff --git a/src/UnitTests/UnitTest1.cs b/src/UnitTests/UnitTest1.cs index c558bbee..738ede2c 100644 --- a/src/UnitTests/UnitTest1.cs +++ b/src/UnitTests/UnitTest1.cs @@ -3099,9 +3099,48 @@ public void TestChangeTo() this.SaveAndCompare("out.xml", "test8.xml"); } + [TestMethod] + [Timeout(TestMethodTimeout)] + public void TestNoByteOrderMark() + { + SetNoByteOrderMark(true); + Trace.WriteLine("TestNoByteOrderMark=========================================================="); + string testFile = _testDir + "UnitTests\\test8.xml"; + var w = this.LaunchNotepad(testFile); + Sleep(1000); + string outFile = Save("out.xml"); + AssertUtf8Bom(testFile, true); + AssertUtf8Bom(outFile, false); + + // test we can load it + XmlDocument doc = new XmlDocument(); + doc.Load(outFile); + } //================================================================================== + + void SetNoByteOrderMark(bool value) + { + this.testSettings["NoByteOrderMark"] = value; + this.testSettings.Save(this.testSettings.FileName); + } + + private void AssertUtf8Bom(string filename, bool expected) + { + var data = File.ReadAllBytes(filename); + bool isUtf8 = data.Length > 3 && data[0] == 0xef && data[1] == 0xBB && data[2] == 0xBF; + if (isUtf8 && !expected) + { + throw new ApplicationException("Byte order mark is present in {filename} but should not be"); + } + else if (!isUtf8 && expected) + { + throw new ApplicationException("Byte order mark is not present in {filename} but should be"); + } + } + + private void SaveAndCompare(string outname, string compareWith) { string outFile = Save(outname); diff --git a/src/Updates/Updates.xml b/src/Updates/Updates.xml index eef0e37d..eb40b39d 100644 --- a/src/Updates/Updates.xml +++ b/src/Updates/Updates.xml @@ -9,6 +9,10 @@ https://github.com/microsoft/XmlNotepad/blob/master/src/Updates/Updates.xml 1.00:00:00 + + Fix issue #425: "No byte order mark on save" option throws stream closed exception add unit test. + Apply dark mode to window titlebar. + Fix issue #418: check setttings-file for "read-only". Fix issue #398: XmlNotepad detects xml-errors at empty nillable xsd:date Elements, but xml is correct diff --git a/src/Version/Version.cs b/src/Version/Version.cs index 70fb28e9..628ba1b6 100644 --- a/src/Version/Version.cs +++ b/src/Version/Version.cs @@ -15,5 +15,7 @@ // Build Number // Revision // +// This is the Master version number from which UpdateVersions will propagate to +// Package.appxmanifest, Product.wxs, Bundle.wxs and Application.csproj. [assembly: AssemblyVersion("2.9.0.14")] [assembly: AssemblyFileVersion("2.9.0.14")]