diff --git a/SourceGenWPF/AppSettings.cs b/SourceGenWPF/AppSettings.cs
index 8a03ef7..7a6fcda 100644
--- a/SourceGenWPF/AppSettings.cs
+++ b/SourceGenWPF/AppSettings.cs
@@ -94,7 +94,7 @@ namespace SourceGenWPF {
public const string HEXD_CHAR_CONV = "hexd-char-conv1";
// ASCII chart viewer settings.
- public const string ASCCH_MODE = "ascch-mode";
+ public const string ASCCH_MODE = "ascch-mode1";
// Source generation settings.
public const string SRCGEN_DEFAULT_ASM = "srcgen-default-asm";
diff --git a/SourceGenWPF/MainController.cs b/SourceGenWPF/MainController.cs
index 3a3e50e..dfb96c4 100644
--- a/SourceGenWPF/MainController.cs
+++ b/SourceGenWPF/MainController.cs
@@ -64,10 +64,27 @@ namespace SourceGenWPF {
private MainWindow mMainWin;
///
- /// Hex dump viewer window.
+ /// Hex dump viewer window. This is used for the currently open project.
///
private Tools.WpfGui.HexDumpViewer mHexDumpDialog;
+ ///
+ /// This holds any un-owned Windows that we don't otherwise track. It's used for
+ /// hex dump windows of arbitrary files. We need to close them when the main window
+ /// is closed.
+ ///
+ private List mUnownedWindows = new List();
+
+ ///
+ /// ASCII chart reference window. Not tied to the project.
+ ///
+ private Tools.WpfGui.AsciiChart mAsciiChartDialog;
+
+ ///
+ /// Returns true if the ASCII chart window is currently open.
+ ///
+ public bool IsAsciiChartOpen { get { return mAsciiChartDialog != null; } }
+
///
/// List of recently-opened projects.
///
@@ -1108,7 +1125,30 @@ namespace SourceGenWPF {
/// True if it's okay for the window to close, false to cancel it.
public bool WindowClosing() {
SaveAppSettings();
- return CloseProject();
+ if (!CloseProject()) {
+ return false;
+ }
+
+ // WPF won't exit until all windows are closed, so any unowned windows need
+ // to be cleaned up here.
+ if (mAsciiChartDialog != null) {
+ mAsciiChartDialog.Close();
+ }
+ if (mHexDumpDialog != null) {
+ mHexDumpDialog.Close();
+ }
+ while (mUnownedWindows.Count > 0) {
+ int count = mUnownedWindows.Count;
+ mUnownedWindows[0].Close();
+ if (count == mUnownedWindows.Count) {
+ // Window failed to remove itself; this will cause an infinite loop.
+ // The user will have to close them manually.
+ Debug.Assert(false, "Failed to close window " + mUnownedWindows[0]);
+ break;
+ }
+ }
+
+ return true;
}
///
@@ -2500,18 +2540,26 @@ namespace SourceGenWPF {
return;
}
- // Fire and forget.
- Tools.WpfGui.HexDumpViewer dlg = new Tools.WpfGui.HexDumpViewer(mMainWin,
+ // Create the dialog without an owner, and add it to the "unowned" list.
+ Tools.WpfGui.HexDumpViewer dlg = new Tools.WpfGui.HexDumpViewer(null,
data, mOutputFormatter);
dlg.SetFileName(Path.GetFileName(fileName));
+ dlg.Closing += (sender, e) => {
+ Debug.WriteLine("Window " + dlg + " closed, removing from unowned list");
+ mUnownedWindows.Remove(dlg);
+ };
+ mUnownedWindows.Add(dlg);
dlg.Show();
}
public void ShowHexDump() {
if (mHexDumpDialog == null) {
// Create and show modeless dialog. This one is "always on top" by default,
- // to allow the user to click around to various points.
- mHexDumpDialog = new Tools.WpfGui.HexDumpViewer(mMainWin,
+ // to allow the user to click around to various points. Note that "on top"
+ // means on top of *everything*. We create this without an owner so that,
+ // when it's not on top, it can sit behind the main app window until you
+ // double-click something else.
+ mHexDumpDialog = new Tools.WpfGui.HexDumpViewer(null,
mProject.FileData, mOutputFormatter);
mHexDumpDialog.Closing += (sender, e) => {
Debug.WriteLine("Hex dump dialog closed");
@@ -2633,6 +2681,20 @@ namespace SourceGenWPF {
dlg.ShowDialog();
}
+ public void ToggleAsciiChart() {
+ if (mAsciiChartDialog == null) {
+ // Create without owner so it doesn't have to be in front of main window.
+ mAsciiChartDialog = new Tools.WpfGui.AsciiChart(null);
+ mAsciiChartDialog.Closing += (sender, e) => {
+ Debug.WriteLine("ASCII chart closed");
+ mAsciiChartDialog = null;
+ };
+ mAsciiChartDialog.Show();
+ } else {
+ mAsciiChartDialog.Close();
+ }
+ }
+
public void ToggleDataScan() {
ProjectProperties oldProps = mProject.ProjectProps;
ProjectProperties newProps = new ProjectProperties(oldProps);
diff --git a/SourceGenWPF/SourceGenWPF.csproj b/SourceGenWPF/SourceGenWPF.csproj
index 7844dac..0a2e050 100644
--- a/SourceGenWPF/SourceGenWPF.csproj
+++ b/SourceGenWPF/SourceGenWPF.csproj
@@ -79,6 +79,9 @@
GenTestRunner.xaml
+
+ AsciiChart.xaml
+
AboutBox.xaml
@@ -219,6 +222,10 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
diff --git a/SourceGenWPF/Tools/WpfGui/AsciiChart.xaml b/SourceGenWPF/Tools/WpfGui/AsciiChart.xaml
new file mode 100644
index 0000000..f40e181
--- /dev/null
+++ b/SourceGenWPF/Tools/WpfGui/AsciiChart.xaml
@@ -0,0 +1,45 @@
+
+
+
+
+
+ Standard ASCII
+ High ASCII
+
+
+
+
+
+
+
+
diff --git a/SourceGenWPF/Tools/WpfGui/AsciiChart.xaml.cs b/SourceGenWPF/Tools/WpfGui/AsciiChart.xaml.cs
new file mode 100644
index 0000000..c122a72
--- /dev/null
+++ b/SourceGenWPF/Tools/WpfGui/AsciiChart.xaml.cs
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2019 faddenSoft
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using System;
+using System.Diagnostics;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+
+namespace SourceGenWPF.Tools.WpfGui {
+ ///
+ /// ASCII chart.
+ ///
+ public partial class AsciiChart : Window {
+ public enum ChartMode {
+ Unknown = 0,
+ Standard,
+ High
+ };
+ public class ChartModeItem {
+ public string Name { get; private set; }
+ public ChartMode Mode { get; private set; }
+
+ public ChartModeItem(string name, ChartMode mode) {
+ Name = name;
+ Mode = mode;
+ }
+ }
+ public ChartModeItem[] ChartModeItems { get; private set; }
+
+
+ public AsciiChart(Window owner) {
+ InitializeComponent();
+ Owner = owner;
+ DataContext = this;
+
+ ChartModeItems = new ChartModeItem[] {
+ new ChartModeItem((string)FindResource("str_Standard"), ChartMode.Standard),
+ new ChartModeItem((string)FindResource("str_High"), ChartMode.High),
+ };
+ }
+
+ public void Window_Loaded(object sender, RoutedEventArgs e) {
+ // Restore chart mode setting.
+ ChartMode mode = (ChartMode)AppSettings.Global.GetEnum(
+ AppSettings.ASCCH_MODE, typeof(ChartMode), (int)ChartMode.Standard);
+ int index = 0;
+ for (int i = 0; i < ChartModeItems.Length; i++) {
+ if (ChartModeItems[i].Mode == mode) {
+ index = i;
+ break;
+ }
+ }
+ chartModeComboBox.SelectedIndex = index;
+ // should call UpdateControls via SelectionChanged
+ }
+
+ // Catch ESC key.
+ private void Window_KeyEventHandler(object sender, KeyEventArgs e) {
+ if (e.Key == Key.Escape) {
+ Close();
+ }
+ }
+
+ private void ChartModeComboBox_SelectionChanged(object sender,
+ SelectionChangedEventArgs e) {
+ UpdateControls();
+ }
+
+ private void UpdateControls() {
+ ChartModeItem item = (ChartModeItem)chartModeComboBox.SelectedItem;
+ if (item == null) {
+ // initializing
+ return;
+ }
+
+ AppSettings.Global.SetEnum(AppSettings.ASCCH_MODE, typeof(ChartMode), (int)item.Mode);
+
+ //
+ // Draw box contents.
+ //
+ const string hdr = "Dec Hex Chr";
+ const string div = " | ";
+ const string eol = "\r\n";
+
+ StringBuilder sb = new StringBuilder(
+ (hdr.Length * 4 + div.Length * 3 + eol.Length) * 32);
+ sb.Append(hdr);
+ sb.Append(div);
+ sb.Append(hdr);
+ sb.Append(div);
+ sb.Append(hdr);
+ sb.Append(div);
+ sb.Append(hdr);
+ sb.Append(eol);
+ for (int i = 0; i < 32; i++) {
+ DrawEntry(item.Mode, i, sb);
+ sb.Append(div);
+ DrawEntry(item.Mode, i + 32, sb);
+ sb.Append(div);
+ DrawEntry(item.Mode, i + 64, sb);
+ sb.Append(div);
+ DrawEntry(item.Mode, i + 96, sb);
+ sb.Append(eol);
+ }
+
+ chartTextBox.Text = sb.ToString();
+ chartTextBox.SelectionStart = sb.Length;
+ chartTextBox.SelectionLength = 0;
+ }
+
+ private void DrawEntry(ChartMode mode, int val, StringBuilder sb) {
+ // Format is: Dec Hex Chr
+ int modVal = (mode == ChartMode.High) ? val | 0x80 : val;
+ sb.AppendFormat("{0,3:D} {1,3:X2} ", modVal, modVal);
+ if (val < 0x20) {
+ sb.Append('^');
+ sb.Append((char)(val + 0x40));
+ sb.Append(' ');
+ } else if (val == 0x20) {
+ sb.Append("' '");
+ } else if (val < 0x7f) {
+ sb.Append(' ');
+ sb.Append((char)val);
+ sb.Append(' ');
+ } else {
+ sb.Append("DEL");
+ }
+ }
+ }
+}
diff --git a/SourceGenWPF/Tools/WpfGui/HexDumpViewer.xaml b/SourceGenWPF/Tools/WpfGui/HexDumpViewer.xaml
index a056347..d207f98 100644
--- a/SourceGenWPF/Tools/WpfGui/HexDumpViewer.xaml
+++ b/SourceGenWPF/Tools/WpfGui/HexDumpViewer.xaml
@@ -25,7 +25,7 @@ limitations under the License.
Title="Hex Dump Viewer"
Icon="/SourceGenWPF;component/Res/SourceGenIcon.ico"
Width="542" Height="600" MinWidth="542" MinHeight="180"
- ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
+ ShowInTaskbar="True"
Loaded="Window_Loaded">
diff --git a/SourceGenWPF/WpfGui/MainWindow.xaml b/SourceGenWPF/WpfGui/MainWindow.xaml
index 270744c..a9ef6c3 100644
--- a/SourceGenWPF/WpfGui/MainWindow.xaml
+++ b/SourceGenWPF/WpfGui/MainWindow.xaml
@@ -140,6 +140,7 @@ limitations under the License.
+
Ctrl+D
@@ -241,6 +242,8 @@ limitations under the License.
Executed="ShowFileHexDumpCmd_Executed"/>
+
-