From 89413d11e46a71649fad6045ea9eb4e006b08149 Mon Sep 17 00:00:00 2001
From: Andy McFadden
Date: Sat, 28 Dec 2019 17:14:29 -0800
Subject: [PATCH] Add file concatenation tool
Select a list of files and save it. File lengths and CRCs are
shown for reference.
---
CommonUtil/CRC32.cs | 50 ++--
CommonUtil/FileUtil.cs | 2 +-
SourceGen/MainController.cs | 142 +++++-----
SourceGen/RuntimeData/Help/index.html | 2 +
SourceGen/RuntimeData/Help/tools.html | 7 +
SourceGen/Sandbox/PluginDllCache.cs | 8 +-
SourceGen/SourceGen.csproj | 7 +
SourceGen/Tools/WpfGui/FileConcatenator.xaml | 100 +++++++
.../Tools/WpfGui/FileConcatenator.xaml.cs | 244 ++++++++++++++++++
.../Tools/WpfGui/InstructionChart.xaml.cs | 2 +
.../WpfGui/EditProjectProperties.xaml.cs | 6 +-
SourceGen/WpfGui/EditVisualizationSet.xaml.cs | 19 +-
SourceGen/WpfGui/MainWindow.xaml | 12 +-
SourceGen/WpfGui/MainWindow.xaml.cs | 4 +
14 files changed, 492 insertions(+), 113 deletions(-)
create mode 100644 SourceGen/Tools/WpfGui/FileConcatenator.xaml
create mode 100644 SourceGen/Tools/WpfGui/FileConcatenator.xaml.cs
diff --git a/CommonUtil/CRC32.cs b/CommonUtil/CRC32.cs
index c492bb7..d45c690 100644
--- a/CommonUtil/CRC32.cs
+++ b/CommonUtil/CRC32.cs
@@ -47,13 +47,13 @@ namespace CommonUtil {
}
///
- /// Computes a CRC on part of a buffer of data.
+ /// Computes a CRC-32 on part of a buffer of data.
///
- /// Previously computed CRC value. Initially zero.
+ /// Previously computed CRC value. Use zero as initial value.
/// Data to compute CRC on.
/// Start offset within buffer.
/// Number of bytes to process.
- /// New CRC value.
+ /// Computed CRC value.
public static uint OnBuffer(uint crc, byte[] buffer, int offset, int count) {
crc = crc ^ INVERT;
while (count-- != 0) {
@@ -64,45 +64,37 @@ namespace CommonUtil {
}
///
- /// Computes a CRC on a buffer of data.
+ /// Computes a CRC-32 on a buffer of data.
///
/// Previously computed CRC value. Initially zero.
/// Data to compute CRC on.
- /// New CRC value.
+ /// Computed CRC value.
public static uint OnWholeBuffer(uint crc, byte[] buffer) {
return OnBuffer(crc, buffer, 0, buffer.Length);
}
///
- /// Computes a CRC on an entire file.
+ /// Computes a CRC-32 on an entire file. An exception will be thrown on file errors.
///
/// Full path to file to open.
- /// Receives the CRC.
- /// True on success, false on file error.
- public static bool OnWholeFile(string pathName, out uint ocrc) {
- try {
- using (FileStream fs = File.Open(pathName, FileMode.Open, FileAccess.Read)) {
- byte[] buffer = new byte[8192];
- uint crc = 0;
- long remain = fs.Length;
- while (remain != 0) {
- int toRead = (remain < buffer.Length) ? (int)remain : buffer.Length;
- int actual = fs.Read(buffer, 0, toRead);
- if (toRead != actual) {
- throw new IOException("Expected " + toRead + ", got " + actual);
- }
-
- crc = OnBuffer(crc, buffer, 0, toRead);
- remain -= toRead;
+ /// Computed CRC value.
+ public static uint OnWholeFile(string pathName) {
+ using (FileStream fs = File.Open(pathName, FileMode.Open, FileAccess.Read)) {
+ byte[] buffer = new byte[8192];
+ uint crc = 0;
+ long remain = fs.Length;
+ while (remain != 0) {
+ int toRead = (remain < buffer.Length) ? (int)remain : buffer.Length;
+ int actual = fs.Read(buffer, 0, toRead);
+ if (toRead != actual) {
+ throw new IOException("Expected " + toRead + ", got " + actual);
}
- ocrc = crc;
- return true;
+ crc = OnBuffer(crc, buffer, 0, toRead);
+ remain -= toRead;
}
- } catch (IOException ioe) {
- Debug.WriteLine("Fail: " + ioe);
- ocrc = 0;
- return false;
+
+ return crc;
}
}
}
diff --git a/CommonUtil/FileUtil.cs b/CommonUtil/FileUtil.cs
index 3cb0289..823b84a 100644
--- a/CommonUtil/FileUtil.cs
+++ b/CommonUtil/FileUtil.cs
@@ -125,7 +125,7 @@ namespace CommonUtil {
/// File of interest.
/// File to compare dates with.
/// True if dstFile is missing or older than srcFile.
- public static bool FileMissingOrOlder(string dstFile, string srcFile) {
+ public static bool IsFileMissingOrOlder(string dstFile, string srcFile) {
FileInfo fid = new FileInfo(dstFile);
if (!fid.Exists) {
return true; // not there
diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs
index 61a82bf..ad00dcf 100644
--- a/SourceGen/MainController.cs
+++ b/SourceGen/MainController.cs
@@ -3117,44 +3117,6 @@ namespace SourceGen {
return -1;
}
- public void ShowFileHexDump() {
- OpenFileDialog fileDlg = new OpenFileDialog() {
- Filter = Res.Strings.FILE_FILTER_ALL,
- FilterIndex = 1
- };
- if (fileDlg.ShowDialog() != true) {
- return;
- }
- string fileName = fileDlg.FileName;
- FileInfo fi = new FileInfo(fileName);
- if (fi.Length > Tools.WpfGui.HexDumpViewer.MAX_LENGTH) {
- string msg = string.Format(Res.Strings.OPEN_DATA_TOO_LARGE_FMT,
- fi.Length / 1024, Tools.WpfGui.HexDumpViewer.MAX_LENGTH / 1024);
- MessageBox.Show(msg, Res.Strings.OPEN_DATA_FAIL_CAPTION,
- MessageBoxButton.OK, MessageBoxImage.Error);
- return;
- }
- byte[] data;
- try {
- data = File.ReadAllBytes(fileName);
- } catch (Exception ex) {
- // not expecting this to happen
- MessageBox.Show(ex.Message);
- return;
- }
-
- // 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,
@@ -3283,34 +3245,6 @@ namespace SourceGen {
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 ToggleInstructionChart() {
- if (mInstructionChartDialog == null) {
- // Create without owner so it doesn't have to be in front of main window.
- mInstructionChartDialog = new Tools.WpfGui.InstructionChart(null,mOutputFormatter);
- mInstructionChartDialog.Closing += (sender, e) => {
- Debug.WriteLine("Instruction chart closed");
- mInstructionChartDialog = null;
- };
- mInstructionChartDialog.Show();
- } else {
- mInstructionChartDialog.Close();
- }
- }
-
public void ToggleDataScan() {
ProjectProperties oldProps = mProject.ProjectProps;
ProjectProperties newProps = new ProjectProperties(oldProps);
@@ -4017,6 +3951,82 @@ namespace SourceGen {
#endregion Info panel
+ #region Tools
+
+ 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 ToggleInstructionChart() {
+ if (mInstructionChartDialog == null) {
+ // Create without owner so it doesn't have to be in front of main window.
+ mInstructionChartDialog = new Tools.WpfGui.InstructionChart(null, mOutputFormatter);
+ mInstructionChartDialog.Closing += (sender, e) => {
+ Debug.WriteLine("Instruction chart closed");
+ mInstructionChartDialog = null;
+ };
+ mInstructionChartDialog.Show();
+ } else {
+ mInstructionChartDialog.Close();
+ }
+ }
+
+ public void ShowFileHexDump() {
+ OpenFileDialog fileDlg = new OpenFileDialog() {
+ Filter = Res.Strings.FILE_FILTER_ALL,
+ FilterIndex = 1
+ };
+ if (fileDlg.ShowDialog() != true) {
+ return;
+ }
+ string fileName = fileDlg.FileName;
+ FileInfo fi = new FileInfo(fileName);
+ if (fi.Length > Tools.WpfGui.HexDumpViewer.MAX_LENGTH) {
+ string msg = string.Format(Res.Strings.OPEN_DATA_TOO_LARGE_FMT,
+ fi.Length / 1024, Tools.WpfGui.HexDumpViewer.MAX_LENGTH / 1024);
+ MessageBox.Show(msg, Res.Strings.OPEN_DATA_FAIL_CAPTION,
+ MessageBoxButton.OK, MessageBoxImage.Error);
+ return;
+ }
+ byte[] data;
+ try {
+ data = File.ReadAllBytes(fileName);
+ } catch (Exception ex) {
+ // not expecting this to happen
+ MessageBox.Show(ex.Message);
+ return;
+ }
+
+ // 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 ConcatenateFiles() {
+ Tools.WpfGui.FileConcatenator concat =
+ new Tools.WpfGui.FileConcatenator(this.mMainWin);
+ concat.ShowDialog();
+ }
+
+ #endregion Tools
+
#region Debug features
public void Debug_ExtensionScriptInfo() {
diff --git a/SourceGen/RuntimeData/Help/index.html b/SourceGen/RuntimeData/Help/index.html
index 16980bd..b74081c 100644
--- a/SourceGen/RuntimeData/Help/index.html
+++ b/SourceGen/RuntimeData/Help/index.html
@@ -138,6 +138,7 @@ and 65816 code. The official web site is
Tools
@@ -175,6 +176,7 @@ and 65816 code. The official web site is
Tutorial #2: Advanced Features
Tutorial #3: Address Table Formatting
Tutorial #4: Extension Scripts
+ Tutorial #5: Visualizations
diff --git a/SourceGen/RuntimeData/Help/tools.html b/SourceGen/RuntimeData/Help/tools.html
index 6a95891..bd9d58c 100644
--- a/SourceGen/RuntimeData/Help/tools.html
+++ b/SourceGen/RuntimeData/Help/tools.html
@@ -49,6 +49,13 @@ default this box is checked when displaying project data, and not checked for
external files.
+
+
+The File Concatenator combines multiple files into a single file.
+Select the files to add, arrange them in the proper order, then hit
+"Save".
+
+
This opens a window with the ASCII character set. Each character is
diff --git a/SourceGen/Sandbox/PluginDllCache.cs b/SourceGen/Sandbox/PluginDllCache.cs
index 2d9ec7f..93ad26c 100644
--- a/SourceGen/Sandbox/PluginDllCache.cs
+++ b/SourceGen/Sandbox/PluginDllCache.cs
@@ -104,7 +104,7 @@ namespace SourceGen.Sandbox {
/// Destination directory.
private static void CopyIfNewer(string srcDll, string dstDir) {
string dstFile = Path.Combine(dstDir, Path.GetFileName(srcDll));
- if (FileUtil.FileMissingOrOlder(dstFile, srcDll)) {
+ if (FileUtil.IsFileMissingOrOlder(dstFile, srcDll)) {
Debug.WriteLine("Copying " + srcDll + " to " + dstFile);
File.Copy(srcDll, dstFile, true);
}
@@ -155,10 +155,10 @@ namespace SourceGen.Sandbox {
// the DLLs the app uses, not the copy the plugins use, but earlier we made sure
// that they were the same. This test doesn't handle the case where the DLLs
// get rolled back, but that's probably not interesting for us.)
- bool needCompile = FileUtil.FileMissingOrOlder(destPathName, srcPathName) ||
- FileUtil.FileMissingOrOlder(destPathName,
+ bool needCompile = FileUtil.IsFileMissingOrOlder(destPathName, srcPathName) ||
+ FileUtil.IsFileMissingOrOlder(destPathName,
typeof(PluginCommon.PluginManager).Assembly.Location) ||
- FileUtil.FileMissingOrOlder(destPathName,
+ FileUtil.IsFileMissingOrOlder(destPathName,
typeof(CommonUtil.CRC32).Assembly.Location);
if (needCompile) {
Debug.WriteLine("Compiling " + srcPathName + " to " + destPathName);
diff --git a/SourceGen/SourceGen.csproj b/SourceGen/SourceGen.csproj
index e0b3755..bf54898 100644
--- a/SourceGen/SourceGen.csproj
+++ b/SourceGen/SourceGen.csproj
@@ -86,6 +86,9 @@
+
+ FileConcatenator.xaml
+
InstructionChart.xaml
@@ -262,6 +265,10 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
MSBuild:Compile
Designer
diff --git a/SourceGen/Tools/WpfGui/FileConcatenator.xaml b/SourceGen/Tools/WpfGui/FileConcatenator.xaml
new file mode 100644
index 0000000..7ef7ceb
--- /dev/null
+++ b/SourceGen/Tools/WpfGui/FileConcatenator.xaml
@@ -0,0 +1,100 @@
+
+
+
+
+
+ File Access Error
+ Unable to access file: {0}
+ CRC failed
+ Select File
+
+ Success
+ All files were concatenated successfully.
Total length is {0}, overall CRC-32 is {1}.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SourceGen/Tools/WpfGui/FileConcatenator.xaml.cs b/SourceGen/Tools/WpfGui/FileConcatenator.xaml.cs
new file mode 100644
index 0000000..483ce84
--- /dev/null
+++ b/SourceGen/Tools/WpfGui/FileConcatenator.xaml.cs
@@ -0,0 +1,244 @@
+/*
+ * 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 Microsoft.Win32;
+using System;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Windows;
+
+using CommonUtil;
+using System.Windows.Controls;
+
+namespace SourceGen.Tools.WpfGui {
+ ///
+ /// File concatenation tool.
+ ///
+ public partial class FileConcatenator : Window, INotifyPropertyChanged {
+ //
+ // Every action causes a selection change, so we don't explicitly call an "update
+ // controls" function.
+ //
+
+ public bool IsRemoveEnabled {
+ get { return mIsRemoveEnabled; }
+ set { mIsRemoveEnabled = value; OnPropertyChanged(); }
+ }
+ private bool mIsRemoveEnabled;
+
+ public bool IsUpEnabled {
+ get { return mIsUpEnabled; }
+ set { mIsUpEnabled = value; OnPropertyChanged(); }
+ }
+ private bool mIsUpEnabled;
+
+ public bool IsDownEnabled {
+ get { return mIsDownEnabled; }
+ set { mIsDownEnabled = value; OnPropertyChanged(); }
+ }
+ private bool mIsDownEnabled;
+
+ public bool IsSaveEnabled {
+ get { return mIsSaveEnabled; }
+ set { mIsSaveEnabled = value; OnPropertyChanged(); }
+ }
+ private bool mIsSaveEnabled;
+
+
+ ///
+ /// Item for main list.
+ ///
+ public class ConcatItem {
+ public string PathName { get; private set; }
+ public string FileName { get; private set; }
+ public long Length { get; private set; }
+ public string Crc32 { get; private set; }
+
+ public ConcatItem(string pathName, long length, uint crc32) {
+ PathName = pathName;
+ Length = length;
+ Crc32 = crc32.ToString("x8");
+
+ FileName = Path.GetFileName(pathName);
+ }
+ }
+
+ public ObservableCollection ConcatItems { get; private set; } =
+ new ObservableCollection();
+
+ // INotifyPropertyChanged implementation
+ public event PropertyChangedEventHandler PropertyChanged;
+ private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+
+ ///
+ /// Constructor.
+ ///
+ /// Parent window.
+ public FileConcatenator(Window owner) {
+ InitializeComponent();
+ Owner = owner;
+ DataContext = this;
+
+ // all "enabled" flags are initially false
+ }
+
+ private void ConcatGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) {
+ bool isItemSelected = (concatGrid.SelectedItem != null);
+ IsRemoveEnabled = isItemSelected;
+ IsUpEnabled = isItemSelected && concatGrid.SelectedIndex != 0;
+ IsDownEnabled = isItemSelected && concatGrid.SelectedIndex != ConcatItems.Count - 1;
+
+ IsSaveEnabled = ConcatItems.Count != 0;
+ }
+
+ private void AddButton_Click(object sender, RoutedEventArgs e) {
+ OpenFileDialog fileDlg = new OpenFileDialog() {
+ Filter = Res.Strings.FILE_FILTER_ALL,
+ FilterIndex = 0,
+ Title = (string)FindResource("str_SelectFileTitle")
+ };
+ if (fileDlg.ShowDialog() != true) {
+ return;
+ }
+
+ try {
+ string pathName = Path.GetFullPath(fileDlg.FileName);
+ long length = new FileInfo(pathName).Length;
+ uint crc32 = CRC32.OnWholeFile(pathName);
+ ConcatItems.Add(new ConcatItem(pathName, length, crc32));
+ concatGrid.SelectedIndex = ConcatItems.Count - 1;
+ } catch (Exception ex) {
+ string caption = (string)FindResource("str_FileAccessFailedCaption");
+ string fmt = (string)FindResource("str_FileAccessFailedFmt");
+ MessageBox.Show(string.Format(fmt, ex.Message), caption,
+ MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ private void RemoveButton_Click(object sender, RoutedEventArgs e) {
+ int index = concatGrid.SelectedIndex;
+ ConcatItems.RemoveAt(index);
+
+ // Keep selection at same index, unless we just removed the item at the end.
+ if (index == ConcatItems.Count) {
+ index--;
+ }
+ if (index >= 0) {
+ concatGrid.SelectedIndex = index;
+ }
+ }
+
+ private void UpButton_Click(object sender, RoutedEventArgs e) {
+ ConcatItem item = (ConcatItem)concatGrid.SelectedItem;
+ int index = concatGrid.SelectedIndex;
+ Debug.Assert(index > 0);
+ ConcatItems.RemoveAt(index);
+ ConcatItems.Insert(index - 1, item);
+ concatGrid.SelectedIndex = index - 1;
+ }
+
+ private void DownButton_Click(object sender, RoutedEventArgs e) {
+ ConcatItem item = (ConcatItem)concatGrid.SelectedItem;
+ int index = concatGrid.SelectedIndex;
+ Debug.Assert(index >= 0 && index < ConcatItems.Count - 1);
+ ConcatItems.RemoveAt(index);
+ ConcatItems.Insert(index + 1, item);
+ concatGrid.SelectedIndex = index + 1;
+ }
+
+ private void SaveButton_Click(object sender, RoutedEventArgs e) {
+ SaveFileDialog fileDlg = new SaveFileDialog() {
+ Filter = Res.Strings.FILE_FILTER_ALL,
+ FilterIndex = 0,
+ ValidateNames = true,
+ FileName = "concat.bin"
+ };
+ if (fileDlg.ShowDialog() != true) {
+ return;
+ }
+ string pathName = Path.GetFullPath(fileDlg.FileName);
+ Debug.WriteLine("OUTPUT TO " + pathName);
+
+ // Create the new file, and copy each file to it in order. We compute a total
+ // CRC for the file contents as we go. (It's possible to combine the CRCs we already
+ // have with some math; see e.g. https://stackoverflow.com/a/44061990/294248 .)
+ uint totalCrc;
+ long totalLen;
+ try {
+ using (FileStream stream = new FileStream(pathName, FileMode.Create)) {
+ totalCrc = CopyAllFiles(stream);
+ }
+ totalLen = new FileInfo(pathName).Length;
+ } catch (Exception ex) {
+ string ecaption = (string)FindResource("str_FileAccessFailedCaption");
+ string efmt = (string)FindResource("str_FileAccessFailedFmt");
+ MessageBox.Show(string.Format(efmt, ex.Message), ecaption,
+ MessageBoxButton.OK, MessageBoxImage.Error);
+ return;
+ }
+
+ // TODO(someday): make all data available on the clipboard
+
+ string caption = (string)FindResource("str_SuccessCaption");
+ string fmt = (string)FindResource("str_SuccessMsgFmt");
+ MessageBox.Show(string.Format(fmt, totalLen, "0x" + totalCrc.ToString("x8")),
+ caption, MessageBoxButton.OK, MessageBoxImage.None);
+ DialogResult = true;
+ }
+
+ private uint CopyAllFiles(Stream outStream) {
+ byte[] buffer = new byte[256 * 1024];
+ uint crc32 = 0;
+
+ foreach (ConcatItem item in ConcatItems) {
+ CopyFileToStreamWithCrc32(item.PathName, outStream, buffer, ref crc32);
+ }
+
+ return crc32;
+ }
+
+ ///
+ /// Copies a file on disk to a Stream, computing a CRC-32 on the data. Throws an
+ /// exception on file error.
+ ///
+ /// Full path to input file.
+ /// Output data stream.
+ /// Data buffer, to avoid repeated allocations.
+ /// CRC-32, initially zero.
+ public static void CopyFileToStreamWithCrc32(string inputPath, Stream outStream,
+ byte[] buffer, ref uint crc) {
+ using (FileStream inStream = new FileStream(inputPath, FileMode.Open,
+ FileAccess.Read)) {
+ while (true) {
+ int actual = inStream.Read(buffer, 0, buffer.Length);
+ if (actual == 0) {
+ // end of stream
+ return;
+ }
+
+ crc = CRC32.OnBuffer(crc, buffer, 0, actual);
+
+ outStream.Write(buffer, 0, actual);
+ }
+ }
+ }
+ }
+}
diff --git a/SourceGen/Tools/WpfGui/InstructionChart.xaml.cs b/SourceGen/Tools/WpfGui/InstructionChart.xaml.cs
index 93aeed9..3cacb86 100644
--- a/SourceGen/Tools/WpfGui/InstructionChart.xaml.cs
+++ b/SourceGen/Tools/WpfGui/InstructionChart.xaml.cs
@@ -87,6 +87,8 @@ namespace SourceGen.Tools.WpfGui {
///
/// Constructor.
///
+ /// Parent window.
+ /// Text formatter.
public InstructionChart(Window owner, Formatter formatter) {
InitializeComponent();
Owner = owner;
diff --git a/SourceGen/WpfGui/EditProjectProperties.xaml.cs b/SourceGen/WpfGui/EditProjectProperties.xaml.cs
index 011a7f4..86cd712 100644
--- a/SourceGen/WpfGui/EditProjectProperties.xaml.cs
+++ b/SourceGen/WpfGui/EditProjectProperties.xaml.cs
@@ -831,7 +831,7 @@ namespace SourceGen.WpfGui {
}
}
- private void SymbolFileUpButton_Click(object sender, EventArgs e) {
+ private void SymbolFileUpButton_Click(object sender, RoutedEventArgs e) {
Debug.Assert(symbolFilesListBox.SelectedItems.Count == 1);
int selIndex = symbolFilesListBox.SelectedIndex;
Debug.Assert(selIndex > 0);
@@ -839,7 +839,7 @@ namespace SourceGen.WpfGui {
MoveSingleItem(selIndex, symbolFilesListBox.SelectedItem, -1);
}
- private void SymbolFileDownButton_Click(object sender, EventArgs e) {
+ private void SymbolFileDownButton_Click(object sender, RoutedEventArgs e) {
Debug.Assert(symbolFilesListBox.SelectedItems.Count == 1);
int selIndex = symbolFilesListBox.SelectedIndex;
Debug.Assert(selIndex < symbolFilesListBox.Items.Count - 1);
@@ -862,7 +862,7 @@ namespace SourceGen.WpfGui {
UpdateControls();
}
- private void SymbolFileRemoveButton_Click(object sender, EventArgs e) {
+ private void SymbolFileRemoveButton_Click(object sender, RoutedEventArgs e) {
Debug.Assert(symbolFilesListBox.SelectedItems.Count > 0);
for (int i = symbolFilesListBox.SelectedItems.Count - 1; i >= 0; i--) {
string selItem = (string)symbolFilesListBox.SelectedItems[i];
diff --git a/SourceGen/WpfGui/EditVisualizationSet.xaml.cs b/SourceGen/WpfGui/EditVisualizationSet.xaml.cs
index 9b72f34..841cb26 100644
--- a/SourceGen/WpfGui/EditVisualizationSet.xaml.cs
+++ b/SourceGen/WpfGui/EditVisualizationSet.xaml.cs
@@ -71,6 +71,11 @@ namespace SourceGen.WpfGui {
// this can't change while the dialog is open, so don't need OnPropertyChanged
}
+ //
+ // Every action causes a selection change, so we don't explicitly call an "update
+ // controls" function.
+ //
+
public bool IsEditEnabled {
get { return mIsEditEnabled; }
set { mIsEditEnabled = value; OnPropertyChanged(); }
@@ -229,8 +234,10 @@ namespace SourceGen.WpfGui {
private void RemoveButton_Click(object sender, RoutedEventArgs e) {
Visualization item = (Visualization)visualizationGrid.SelectedItem;
- int index = VisualizationList.IndexOf(item);
- VisualizationList.Remove(item);
+ int index = visualizationGrid.SelectedIndex;
+ VisualizationList.RemoveAt(index);
+
+ // Keep selection at same index, unless we just removed the item at the end.
if (index == VisualizationList.Count) {
index--;
}
@@ -271,18 +278,18 @@ namespace SourceGen.WpfGui {
private void UpButton_Click(object sender, RoutedEventArgs e) {
Visualization item = (Visualization)visualizationGrid.SelectedItem;
- int index = VisualizationList.IndexOf(item);
+ int index = visualizationGrid.SelectedIndex;
Debug.Assert(index > 0);
- VisualizationList.Remove(item);
+ VisualizationList.RemoveAt(index);
VisualizationList.Insert(index - 1, item);
visualizationGrid.SelectedIndex = index - 1;
}
private void DownButton_Click(object sender, RoutedEventArgs e) {
Visualization item = (Visualization)visualizationGrid.SelectedItem;
- int index = VisualizationList.IndexOf(item);
+ int index = visualizationGrid.SelectedIndex;
Debug.Assert(index >= 0 && index < VisualizationList.Count - 1);
- VisualizationList.Remove(item);
+ VisualizationList.RemoveAt(index);
VisualizationList.Insert(index + 1, item);
visualizationGrid.SelectedIndex = index + 1;
}
diff --git a/SourceGen/WpfGui/MainWindow.xaml b/SourceGen/WpfGui/MainWindow.xaml
index 4efd755..d598aef 100644
--- a/SourceGen/WpfGui/MainWindow.xaml
+++ b/SourceGen/WpfGui/MainWindow.xaml
@@ -63,6 +63,7 @@ limitations under the License.
+
@@ -161,7 +162,7 @@ limitations under the License.
Ctrl+A
-
+
@@ -203,6 +204,8 @@ limitations under the License.
CanExecute="IsProjectOpen" Executed="AssembleCmd_Executed"/>
+
-
-
+
+
+
diff --git a/SourceGen/WpfGui/MainWindow.xaml.cs b/SourceGen/WpfGui/MainWindow.xaml.cs
index 756b749..ea02be8 100644
--- a/SourceGen/WpfGui/MainWindow.xaml.cs
+++ b/SourceGen/WpfGui/MainWindow.xaml.cs
@@ -1131,6 +1131,10 @@ namespace SourceGen.WpfGui {
}
}
+ private void ConcatenateFilesCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
+ mMainCtrl.ConcatenateFiles();
+ }
+
private void CreateLocalVariableTableCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
mMainCtrl.CreateLocalVariableTable();
}