mirror of
https://github.com/fadden/6502bench.git
synced 2025-01-23 04:30:48 +00:00
Port project loader code
Created a file to hold the non-WPF parts of ProjectView.cs. Pulled some code related to project loading into it. Created a few related dialogs.
This commit is contained in:
parent
c976b92f34
commit
ce27ae720e
@ -26,5 +26,7 @@ namespace SourceGenWPF {
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application {
|
||||
public static readonly CommonUtil.Version ProgramVersion =
|
||||
new CommonUtil.Version(1, 2, 0, CommonUtil.Version.PreRelType.Alpha, 1);
|
||||
}
|
||||
}
|
||||
|
779
SourceGenWPF/MainController.cs
Normal file
779
SourceGenWPF/MainController.cs
Normal file
@ -0,0 +1,779 @@
|
||||
/*
|
||||
* 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.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
|
||||
using Asm65;
|
||||
using CommonUtil;
|
||||
using SourceGenWPF.ProjWin;
|
||||
using System.Web.Script.Serialization;
|
||||
|
||||
namespace SourceGenWPF {
|
||||
/// <summary>
|
||||
/// This class manages user interaction. The goal is for this to be relatively
|
||||
/// GUI-toolkit-agnostic, with all the WPF stuff tucked into the code-behind files. An
|
||||
/// instance of this class is created by MainWindow when the app starts.
|
||||
///
|
||||
/// There is some Windows-specific stuff, like MessageBox and OpenFileDialog.
|
||||
/// </summary>
|
||||
public class MainController {
|
||||
#region Project state
|
||||
|
||||
// Currently open project, or null if none.
|
||||
private DisasmProject mProject;
|
||||
|
||||
// Pathname to 65xx data file.
|
||||
private string mDataPathName;
|
||||
|
||||
// Pathname of .dis65 file. This will be empty for a new project.
|
||||
private string mProjectPathName;
|
||||
|
||||
#if false
|
||||
/// <summary>
|
||||
/// Symbol subset, used to supply data to the symbol ListView. Initialized with
|
||||
/// an empty symbol table.
|
||||
/// </summary>
|
||||
private SymbolTableSubset mSymbolSubset;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Current code list view selection. The length will match the DisplayList Count.
|
||||
///
|
||||
/// A simple foreach through codeListView.SelectedIndices on a 500K-line data set
|
||||
/// takes about 2.5 seconds on a fast Win10 x64 machine. Fortunately the control
|
||||
/// notifies us of changes to the selection, so we can track it ourselves.
|
||||
/// </summary>
|
||||
private VirtualListViewSelection mCodeViewSelection = new VirtualListViewSelection();
|
||||
|
||||
/// <summary>
|
||||
/// Data backing the codeListView.
|
||||
/// </summary>
|
||||
private DisplayList mDisplayList;
|
||||
|
||||
#endregion Project state
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// List of recently-opened projects.
|
||||
/// </summary>
|
||||
private List<string> mRecentProjectPaths = new List<string>(MAX_RECENT_PROJECTS);
|
||||
public const int MAX_RECENT_PROJECTS = 6;
|
||||
|
||||
/// <summary>
|
||||
/// Activity log generated by the code and data analyzers. Displayed in window.
|
||||
/// </summary>
|
||||
private DebugLog mGenerationLog;
|
||||
|
||||
/// <summary>
|
||||
/// Timing data generated during analysis.
|
||||
/// </summary>
|
||||
TaskTimer mReanalysisTimer = new TaskTimer();
|
||||
|
||||
/// <summary>
|
||||
/// Stack for navigate forward/backward.
|
||||
/// </summary>
|
||||
private NavStack mNavStack = new NavStack();
|
||||
|
||||
/// <summary>
|
||||
/// Output format configuration.
|
||||
/// </summary>
|
||||
private Formatter.FormatConfig mFormatterConfig;
|
||||
|
||||
/// <summary>
|
||||
/// Output format controller.
|
||||
///
|
||||
/// This is shared with the DisplayList.
|
||||
/// </summary>
|
||||
private Formatter mOutputFormatter;
|
||||
|
||||
/// <summary>
|
||||
/// Pseudo-op names.
|
||||
///
|
||||
/// This is shared with the DisplayList.
|
||||
/// </summary>
|
||||
private PseudoOp.PseudoOpNames mPseudoOpNames;
|
||||
|
||||
/// <summary>
|
||||
/// String we most recently searched for.
|
||||
/// </summary>
|
||||
private string mFindString = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Initial start point of most recent search.
|
||||
/// </summary>
|
||||
private int mFindStartIndex = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Used to highlight the line that is the target of the selected line.
|
||||
/// </summary>
|
||||
private int mTargetHighlightIndex = -1;
|
||||
|
||||
/// <summary>
|
||||
/// CPU definition used when the Formatter was created. If the CPU choice or
|
||||
/// inclusion of undocumented opcodes changes, we need to wipe the formatter.
|
||||
/// </summary>
|
||||
private CpuDef mOutputFormatterCpuDef;
|
||||
|
||||
/// <summary>
|
||||
/// Instruction description object. Used for Info window.
|
||||
/// </summary>
|
||||
private OpDescription mOpDesc = OpDescription.GetOpDescription(null);
|
||||
|
||||
/// <summary>
|
||||
/// If true, plugins will execute in the main application's AppDomain instead of
|
||||
/// the sandbox.
|
||||
/// </summary>
|
||||
private bool mUseMainAppDomainForPlugins = false;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the named project is at the top of the list. If it's elsewhere
|
||||
/// in the list, move it to the top. Excess items are removed.
|
||||
/// </summary>
|
||||
/// <param name="projectPath"></param>
|
||||
private void UpdateRecentProjectList(string projectPath) {
|
||||
if (string.IsNullOrEmpty(projectPath)) {
|
||||
// This can happen if you create a new project, then close the window
|
||||
// without having saved it.
|
||||
return;
|
||||
}
|
||||
int index = mRecentProjectPaths.IndexOf(projectPath);
|
||||
if (index == 0) {
|
||||
// Already in the list, nothing changes. No need to update anything else.
|
||||
return;
|
||||
}
|
||||
if (index > 0) {
|
||||
mRecentProjectPaths.RemoveAt(index);
|
||||
}
|
||||
mRecentProjectPaths.Insert(0, projectPath);
|
||||
|
||||
// Trim the list to the max allowed.
|
||||
while (mRecentProjectPaths.Count > MAX_RECENT_PROJECTS) {
|
||||
Debug.WriteLine("Recent projects: dropping " +
|
||||
mRecentProjectPaths[MAX_RECENT_PROJECTS]);
|
||||
mRecentProjectPaths.RemoveAt(MAX_RECENT_PROJECTS);
|
||||
}
|
||||
|
||||
// Store updated list in app settings. JSON-in-JSON is ugly and inefficient,
|
||||
// but it'll do for now.
|
||||
JavaScriptSerializer ser = new JavaScriptSerializer();
|
||||
string cereal = ser.Serialize(mRecentProjectPaths);
|
||||
AppSettings.Global.SetString(AppSettings.PRVW_RECENT_PROJECT_LIST, cereal);
|
||||
|
||||
#if false
|
||||
UpdateRecentLinks();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public void OpenRecentProject(int projIndex) {
|
||||
//if (DoClose()) {
|
||||
// DoOpenFile(mRecentProjectPaths[projIndex]);
|
||||
//}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles opening an existing project by letting the user select the project file.
|
||||
/// </summary>
|
||||
private void DoOpen() {
|
||||
if (!DoClose()) {
|
||||
return;
|
||||
}
|
||||
|
||||
OpenFileDialog fileDlg = new OpenFileDialog() {
|
||||
Filter = ProjectFile.FILENAME_FILTER + "|" + Res.Strings.FILE_FILTER_ALL,
|
||||
FilterIndex = 1
|
||||
};
|
||||
if (fileDlg.ShowDialog() != true) {
|
||||
return;
|
||||
}
|
||||
|
||||
string projPathName = Path.GetFullPath(fileDlg.FileName);
|
||||
DoOpenFile(projPathName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles opening an existing project, given a pathname to the project file.
|
||||
/// </summary>
|
||||
private void DoOpenFile(string projPathName) {
|
||||
Debug.WriteLine("DoOpenFile: " + projPathName);
|
||||
Debug.Assert(mProject == null);
|
||||
|
||||
if (!File.Exists(projPathName)) {
|
||||
string msg = string.Format(Res.Strings.ERR_FILE_NOT_FOUND_FMT, projPathName);
|
||||
MessageBox.Show(msg, Res.Strings.ERR_FILE_GENERIC_CAPTION,
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
DisasmProject newProject = new DisasmProject();
|
||||
newProject.UseMainAppDomainForPlugins = mUseMainAppDomainForPlugins;
|
||||
|
||||
// Deserialize the project file. I want to do this before loading the data file
|
||||
// in case we decide to store the data file name in the project (e.g. the data
|
||||
// file is a disk image or zip archive, and we need to know which part(s) to
|
||||
// extract).
|
||||
if (!ProjectFile.DeserializeFromFile(projPathName, newProject,
|
||||
out FileLoadReport report)) {
|
||||
// Should probably use a less-busy dialog for something simple like
|
||||
// "permission denied", but the open file dialog handles most simple
|
||||
// stuff directly.
|
||||
ProjectLoadIssues dlg = new ProjectLoadIssues(report.Format(),
|
||||
ProjectLoadIssues.Buttons.Cancel);
|
||||
dlg.ShowDialog();
|
||||
// ignore dlg.DialogResult
|
||||
return;
|
||||
}
|
||||
|
||||
// Now open the data file, generating the pathname by stripping off the ".dis65"
|
||||
// extension. If we can't find the file, show a message box and offer the option to
|
||||
// locate it manually, repeating the process until successful or canceled.
|
||||
const string UNKNOWN_FILE = "UNKNOWN";
|
||||
string dataPathName;
|
||||
if (projPathName.Length <= ProjectFile.FILENAME_EXT.Length) {
|
||||
dataPathName = UNKNOWN_FILE;
|
||||
} else {
|
||||
dataPathName = projPathName.Substring(0,
|
||||
projPathName.Length - ProjectFile.FILENAME_EXT.Length);
|
||||
}
|
||||
byte[] fileData;
|
||||
while ((fileData = FindValidDataFile(ref dataPathName, newProject,
|
||||
out bool cancel)) == null) {
|
||||
if (cancel) {
|
||||
// give up
|
||||
Debug.WriteLine("Abandoning attempt to open project");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If there were warnings, notify the user and give the a chance to cancel.
|
||||
if (report.Count != 0) {
|
||||
ProjectLoadIssues dlg = new ProjectLoadIssues(report.Format(),
|
||||
ProjectLoadIssues.Buttons.ContinueOrCancel);
|
||||
bool? ok = dlg.ShowDialog();
|
||||
|
||||
if (ok != true) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mProject = newProject;
|
||||
mProjectPathName = mProject.ProjectPathName = projPathName;
|
||||
mProject.SetFileData(fileData, Path.GetFileName(dataPathName));
|
||||
FinishPrep();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds and loads the specified data file. The file's length and CRC must match
|
||||
/// the project's expectations.
|
||||
/// </summary>
|
||||
/// <param name="dataPathName">Full path to file.</param>
|
||||
/// <param name="proj">Project object.</param>
|
||||
/// <param name="cancel">Returns true if we want to cancel the attempt.</param>
|
||||
/// <returns></returns>
|
||||
private byte[] FindValidDataFile(ref string dataPathName, DisasmProject proj,
|
||||
out bool cancel) {
|
||||
FileInfo fi = new FileInfo(dataPathName);
|
||||
if (!fi.Exists) {
|
||||
Debug.WriteLine("File '" + dataPathName + "' doesn't exist");
|
||||
dataPathName = ChooseDataFile(dataPathName,
|
||||
Res.Strings.OPEN_DATA_DOESNT_EXIST);
|
||||
cancel = (dataPathName == null);
|
||||
return null;
|
||||
}
|
||||
if (fi.Length != proj.FileDataLength) {
|
||||
Debug.WriteLine("File '" + dataPathName + "' has length=" + fi.Length +
|
||||
", expected " + proj.FileDataLength);
|
||||
dataPathName = ChooseDataFile(dataPathName,
|
||||
string.Format(Res.Strings.OPEN_DATA_WRONG_LENGTH_FMT,
|
||||
fi.Length, proj.FileDataLength));
|
||||
cancel = (dataPathName == null);
|
||||
return null;
|
||||
}
|
||||
byte[] fileData = null;
|
||||
try {
|
||||
fileData = LoadDataFile(dataPathName);
|
||||
} catch (Exception ex) {
|
||||
Debug.WriteLine("File '" + dataPathName + "' failed to load: " + ex.Message);
|
||||
dataPathName = ChooseDataFile(dataPathName,
|
||||
string.Format(Res.Strings.OPEN_DATA_LOAD_FAILED_FMT, ex.Message));
|
||||
cancel = (dataPathName == null);
|
||||
return null;
|
||||
}
|
||||
uint crc = CRC32.OnWholeBuffer(0, fileData);
|
||||
if (crc != proj.FileDataCrc32) {
|
||||
Debug.WriteLine("File '" + dataPathName + "' has CRC32=" + crc +
|
||||
", expected " + proj.FileDataCrc32);
|
||||
// Format the CRC as signed decimal, so that interested parties can
|
||||
// easily replace the value in the .dis65 file.
|
||||
dataPathName = ChooseDataFile(dataPathName,
|
||||
string.Format(Res.Strings.OPEN_DATA_WRONG_CRC_FMT,
|
||||
(int)crc, (int)proj.FileDataCrc32));
|
||||
cancel = (dataPathName == null);
|
||||
return null;
|
||||
}
|
||||
|
||||
cancel = false;
|
||||
return fileData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Displays a "do you want to pick a different file" message, then (on OK) allows the
|
||||
/// user to select a file.
|
||||
/// </summary>
|
||||
/// <param name="origPath">Pathname of original file.</param>
|
||||
/// <param name="errorMsg">Message to display in the message box.</param>
|
||||
/// <returns>Full path of file to open.</returns>
|
||||
private string ChooseDataFile(string origPath, string errorMsg) {
|
||||
DataFileLoadIssue dlg = new DataFileLoadIssue(origPath, errorMsg);
|
||||
bool? ok = dlg.ShowDialog();
|
||||
if (ok != true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
OpenFileDialog fileDlg = new OpenFileDialog() {
|
||||
FileName = Path.GetFileName(origPath),
|
||||
Filter = Res.Strings.FILE_FILTER_ALL
|
||||
};
|
||||
if (fileDlg.ShowDialog() != true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string newPath = Path.GetFullPath(fileDlg.FileName);
|
||||
Debug.WriteLine("User selected data file " + newPath);
|
||||
return newPath;
|
||||
}
|
||||
|
||||
private bool DoSaveAs() {
|
||||
SaveFileDialog fileDlg = new SaveFileDialog() {
|
||||
Filter = ProjectFile.FILENAME_FILTER + "|" + Res.Strings.FILE_FILTER_ALL,
|
||||
FilterIndex = 1,
|
||||
ValidateNames = true,
|
||||
AddExtension = true,
|
||||
FileName = Path.GetFileName(mDataPathName) + ProjectFile.FILENAME_EXT
|
||||
};
|
||||
if (fileDlg.ShowDialog() == true) {
|
||||
string pathName = Path.GetFullPath(fileDlg.FileName);
|
||||
Debug.WriteLine("Project save path: " + pathName);
|
||||
if (DoSave(pathName)) {
|
||||
// Success, record the path name.
|
||||
mProjectPathName = mProject.ProjectPathName = pathName;
|
||||
|
||||
// add it to the title bar
|
||||
#if false
|
||||
UpdateMenuItemsAndTitle();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save the project. If it hasn't been saved before, use save-as behavior instead.
|
||||
private bool DoSave() {
|
||||
if (string.IsNullOrEmpty(mProjectPathName)) {
|
||||
return DoSaveAs();
|
||||
}
|
||||
return DoSave(mProjectPathName);
|
||||
}
|
||||
|
||||
private bool DoSave(string pathName) {
|
||||
Debug.WriteLine("SAVING " + pathName);
|
||||
if (!ProjectFile.SerializeToFile(mProject, pathName, out string errorMessage)) {
|
||||
MessageBox.Show(Res.Strings.ERR_PROJECT_SAVE_FAIL + ": " + errorMessage,
|
||||
Res.Strings.OPERATION_FAILED,
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
mProject.ResetDirtyFlag();
|
||||
#if false
|
||||
// If the debug dialog is visible, update it.
|
||||
if (mShowUndoRedoHistoryDialog != null) {
|
||||
mShowUndoRedoHistoryDialog.BodyText = mProject.DebugGetUndoRedoHistory();
|
||||
}
|
||||
UpdateMenuItemsAndTitle();
|
||||
#endif
|
||||
|
||||
// Update this, in case this was a new project.
|
||||
UpdateRecentProjectList(pathName);
|
||||
|
||||
#if false
|
||||
// Seems like a good time to save this off too.
|
||||
SaveAppSettings();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the project and associated modeless dialogs. Unsaved changes will be
|
||||
/// lost, so if the project has outstanding changes the user will be given the
|
||||
/// opportunity to cancel.
|
||||
/// </summary>
|
||||
/// <returns>True if the project was closed, false if the user chose to cancel.</returns>
|
||||
private bool DoClose() {
|
||||
Debug.WriteLine("ProjectView.DoClose() - dirty=" +
|
||||
(mProject == null ? "N/A" : mProject.IsDirty.ToString()));
|
||||
if (mProject != null && mProject.IsDirty) {
|
||||
DiscardChanges dlg = new DiscardChanges();
|
||||
bool? ok = dlg.ShowDialog();
|
||||
if (ok != true) {
|
||||
return false;
|
||||
} else if (dlg.UserChoice == DiscardChanges.Choice.SaveAndContinue) {
|
||||
if (!DoSave()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if false
|
||||
// Close modeless dialogs that depend on project.
|
||||
if (mShowUndoRedoHistoryDialog != null) {
|
||||
mShowUndoRedoHistoryDialog.Close();
|
||||
}
|
||||
if (mShowAnalysisTimersDialog != null) {
|
||||
mShowAnalysisTimersDialog.Close();
|
||||
}
|
||||
if (mShowAnalyzerOutputDialog != null) {
|
||||
mShowAnalyzerOutputDialog.Close();
|
||||
}
|
||||
if (mHexDumpDialog != null) {
|
||||
mHexDumpDialog.Close();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Discard all project state.
|
||||
if (mProject != null) {
|
||||
mProject.Cleanup();
|
||||
mProject = null;
|
||||
}
|
||||
mDataPathName = null;
|
||||
mProjectPathName = null;
|
||||
#if false
|
||||
mSymbolSubset = new SymbolTableSubset(new SymbolTable());
|
||||
mCodeViewSelection = new VirtualListViewSelection();
|
||||
mDisplayList = null;
|
||||
codeListView.VirtualListSize = 0;
|
||||
//codeListView.Items.Clear();
|
||||
ShowNoProject();
|
||||
InvalidateControls(null);
|
||||
#endif
|
||||
|
||||
mGenerationLog = null;
|
||||
|
||||
// Not necessary, but it lets us check the memory monitor to see if we got
|
||||
// rid of everything.
|
||||
GC.Collect();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#region Project management
|
||||
|
||||
private bool PrepareNewProject(string dataPathName, SystemDef sysDef) {
|
||||
DisasmProject proj = new DisasmProject();
|
||||
mDataPathName = dataPathName;
|
||||
mProjectPathName = string.Empty;
|
||||
byte[] fileData = null;
|
||||
try {
|
||||
fileData = LoadDataFile(dataPathName);
|
||||
} catch (Exception ex) {
|
||||
Debug.WriteLine("PrepareNewProject exception: " + ex);
|
||||
string message = Res.Strings.OPEN_DATA_FAIL_CAPTION;
|
||||
string caption = Res.Strings.OPEN_DATA_FAIL_MESSAGE + ": " + ex.Message;
|
||||
MessageBox.Show(caption, message, MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
return false;
|
||||
}
|
||||
proj.UseMainAppDomainForPlugins = mUseMainAppDomainForPlugins;
|
||||
proj.Initialize(fileData.Length);
|
||||
proj.PrepForNew(fileData, Path.GetFileName(dataPathName));
|
||||
|
||||
proj.LongComments.Add(DisplayList.Line.HEADER_COMMENT_OFFSET,
|
||||
new MultiLineComment("6502bench SourceGen v" + App.ProgramVersion));
|
||||
|
||||
// The system definition provides a set of defaults that can be overridden.
|
||||
// We pull everything of interest out and then discard the object.
|
||||
proj.ApplySystemDef(sysDef);
|
||||
|
||||
mProject = proj;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void FinishPrep() {
|
||||
string messages = mProject.LoadExternalFiles();
|
||||
if (messages.Length != 0) {
|
||||
// ProjectLoadIssues isn't quite the right dialog, but it'll do.
|
||||
ProjectLoadIssues dlg = new ProjectLoadIssues(messages,
|
||||
ProjectLoadIssues.Buttons.Continue);
|
||||
dlg.ShowDialog();
|
||||
}
|
||||
|
||||
mDisplayList = new DisplayList(mProject, mOutputFormatter, mPseudoOpNames);
|
||||
|
||||
// Prep the symbol table subset object. Replace the old one with a new one.
|
||||
//mSymbolSubset = new SymbolTableSubset(mProject.SymbolTable);
|
||||
|
||||
RefreshProject(UndoableChange.ReanalysisScope.CodeAndData);
|
||||
//ShowProject();
|
||||
//InvalidateControls(null);
|
||||
|
||||
// Want to do this after ShowProject() or we see a weird glitch.
|
||||
UpdateRecentProjectList(mProjectPathName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the data file, reading it entirely into memory.
|
||||
///
|
||||
/// All errors are reported as exceptions.
|
||||
/// </summary>
|
||||
/// <param name="dataFileName">Full pathname.</param>
|
||||
/// <returns>Data file contents.</returns>
|
||||
private byte[] LoadDataFile(string dataFileName) {
|
||||
byte[] fileData;
|
||||
|
||||
using (FileStream fs = File.Open(dataFileName, FileMode.Open, FileAccess.Read)) {
|
||||
// Check length; should have been caught earlier.
|
||||
if (fs.Length > DisasmProject.MAX_DATA_FILE_SIZE) {
|
||||
throw new InvalidDataException(
|
||||
string.Format(Res.Strings.OPEN_DATA_TOO_LARGE_FMT,
|
||||
fs.Length / 1024, DisasmProject.MAX_DATA_FILE_SIZE / 1024));
|
||||
} else if (fs.Length == 0) {
|
||||
throw new InvalidDataException(Res.Strings.OPEN_DATA_EMPTY);
|
||||
}
|
||||
fileData = new byte[fs.Length];
|
||||
int actual = fs.Read(fileData, 0, (int)fs.Length);
|
||||
if (actual != fs.Length) {
|
||||
// Not expected -- should be able to read the entire file in one shot.
|
||||
throw new Exception(Res.Strings.OPEN_DATA_PARTIAL_READ);
|
||||
}
|
||||
}
|
||||
|
||||
return fileData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the changes to the project, adds them to the undo stack, and updates
|
||||
/// the display.
|
||||
/// </summary>
|
||||
/// <param name="cs">Set of changes to apply.</param>
|
||||
private void ApplyUndoableChanges(ChangeSet cs) {
|
||||
if (cs.Count == 0) {
|
||||
Debug.WriteLine("ApplyUndoableChanges: change set is empty");
|
||||
}
|
||||
ApplyChanges(cs, false);
|
||||
mProject.PushChangeSet(cs);
|
||||
#if false
|
||||
UpdateMenuItemsAndTitle();
|
||||
|
||||
// If the debug dialog is visible, update it.
|
||||
if (mShowUndoRedoHistoryDialog != null) {
|
||||
mShowUndoRedoHistoryDialog.BodyText = mProject.DebugGetUndoRedoHistory();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the changes to the project, and updates the display.
|
||||
///
|
||||
/// This is called by the undo/redo commands. Don't call this directly from the
|
||||
/// various UI-driven functions, as this does not add the change to the undo stack.
|
||||
/// </summary>
|
||||
/// <param name="cs">Set of changes to apply.</param>
|
||||
/// <param name="backward">If set, undo the changes instead.</param>
|
||||
private void ApplyChanges(ChangeSet cs, bool backward) {
|
||||
mReanalysisTimer.Clear();
|
||||
mReanalysisTimer.StartTask("ProjectView.ApplyChanges()");
|
||||
|
||||
mReanalysisTimer.StartTask("Save selection");
|
||||
#if false
|
||||
int topItem = codeListView.TopItem.Index;
|
||||
#else
|
||||
int topItem = 0;
|
||||
#endif
|
||||
int topOffset = mDisplayList[topItem].FileOffset;
|
||||
DisplayList.SavedSelection savedSel = DisplayList.SavedSelection.Generate(
|
||||
mDisplayList, mCodeViewSelection, topOffset);
|
||||
//savedSel.DebugDump();
|
||||
mReanalysisTimer.EndTask("Save selection");
|
||||
|
||||
mReanalysisTimer.StartTask("Apply changes");
|
||||
UndoableChange.ReanalysisScope needReanalysis = mProject.ApplyChanges(cs, backward,
|
||||
out RangeSet affectedOffsets);
|
||||
mReanalysisTimer.EndTask("Apply changes");
|
||||
|
||||
string refreshTaskStr = "Refresh w/reanalysis=" + needReanalysis;
|
||||
mReanalysisTimer.StartTask(refreshTaskStr);
|
||||
if (needReanalysis != UndoableChange.ReanalysisScope.None) {
|
||||
Debug.WriteLine("Refreshing project (" + needReanalysis + ")");
|
||||
RefreshProject(needReanalysis);
|
||||
} else {
|
||||
Debug.WriteLine("Refreshing " + affectedOffsets.Count + " offsets");
|
||||
RefreshCodeListViewEntries(affectedOffsets);
|
||||
mProject.Validate(); // shouldn't matter w/o reanalysis, but do it anyway
|
||||
}
|
||||
mReanalysisTimer.EndTask(refreshTaskStr);
|
||||
|
||||
VirtualListViewSelection newSel = savedSel.Restore(mDisplayList, out int topIndex);
|
||||
//newSel.DebugDump();
|
||||
|
||||
// Refresh the various windows, and restore the selection.
|
||||
mReanalysisTimer.StartTask("Invalidate controls");
|
||||
#if false
|
||||
InvalidateControls(newSel);
|
||||
#endif
|
||||
mReanalysisTimer.EndTask("Invalidate controls");
|
||||
|
||||
// This apparently has to be done after the EndUpdate, and inside try/catch.
|
||||
// See https://stackoverflow.com/questions/626315/ for notes.
|
||||
try {
|
||||
Debug.WriteLine("Setting TopItem to index=" + topIndex);
|
||||
#if false
|
||||
codeListView.TopItem = codeListView.Items[topIndex];
|
||||
#endif
|
||||
} catch (NullReferenceException) {
|
||||
Debug.WriteLine("Caught an NRE from TopItem");
|
||||
}
|
||||
|
||||
mReanalysisTimer.EndTask("ProjectView.ApplyChanges()");
|
||||
|
||||
//mReanalysisTimer.DumpTimes("ProjectView timers:", mGenerationLog);
|
||||
#if false
|
||||
if (mShowAnalysisTimersDialog != null) {
|
||||
string timerStr = mReanalysisTimer.DumpToString("ProjectView timers:");
|
||||
mShowAnalysisTimersDialog.BodyText = timerStr;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Lines may have moved around. Update the selection highlight. It's important
|
||||
// we do it here, and not down in DoRefreshProject(), because at that point the
|
||||
// ListView's selection index could be referencing a line off the end.
|
||||
#if false
|
||||
UpdateSelectionHighlight();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the project after something of substance has changed. Some
|
||||
/// re-analysis will be done, followed by a complete rebuild of the DisplayList.
|
||||
/// </summary>
|
||||
/// <param name="reanalysisRequired">Indicates whether reanalysis is required, and
|
||||
/// what level.</param>
|
||||
private void RefreshProject(UndoableChange.ReanalysisScope reanalysisRequired) {
|
||||
Debug.Assert(reanalysisRequired != UndoableChange.ReanalysisScope.None);
|
||||
|
||||
// NOTE: my goal is to arrange things so that reanalysis (data-only, and ideally
|
||||
// code+data) takes less than 100ms. With that response time there's no need for
|
||||
// background processing and progress bars. Since we need to do data-only
|
||||
// reanalysis after many common operations, the program becomes unpleasant to
|
||||
// use if we miss this goal, and progress bars won't make it less so.
|
||||
|
||||
// Changing the CPU type or whether undocumented instructions are supported
|
||||
// invalidates the Formatter's mnemonic cache. We can change these values
|
||||
// through undo/redo, so we need to check it here.
|
||||
if (mOutputFormatterCpuDef != mProject.CpuDef) { // reference equality is fine
|
||||
Debug.WriteLine("CpuDef has changed, resetting formatter (now " +
|
||||
mProject.CpuDef + ")");
|
||||
mOutputFormatter = new Formatter(mFormatterConfig);
|
||||
mDisplayList.SetFormatter(mOutputFormatter);
|
||||
mDisplayList.SetPseudoOpNames(mPseudoOpNames);
|
||||
mOutputFormatterCpuDef = mProject.CpuDef;
|
||||
}
|
||||
|
||||
#if false
|
||||
if (mDisplayList.Count > 200000) {
|
||||
string prevStatus = toolStripStatusLabel.Text;
|
||||
|
||||
// The Windows stuff can take 50-100ms, potentially longer than the actual
|
||||
// work, so don't bother unless the file is very large.
|
||||
try {
|
||||
mReanalysisTimer.StartTask("Do Windows stuff");
|
||||
Application.UseWaitCursor = true;
|
||||
Cursor.Current = Cursors.WaitCursor;
|
||||
toolStripStatusLabel.Text = Res.Strings.STATUS_RECALCULATING;
|
||||
Refresh(); // redraw status label
|
||||
mReanalysisTimer.EndTask("Do Windows stuff");
|
||||
|
||||
DoRefreshProject(reanalysisRequired);
|
||||
} finally {
|
||||
Application.UseWaitCursor = false;
|
||||
toolStripStatusLabel.Text = prevStatus;
|
||||
}
|
||||
} else {
|
||||
DoRefreshProject(reanalysisRequired);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (FormatDescriptor.DebugCreateCount != 0) {
|
||||
Debug.WriteLine("FormatDescriptor total=" + FormatDescriptor.DebugCreateCount +
|
||||
" prefab=" + FormatDescriptor.DebugPrefabCount + " (" +
|
||||
(FormatDescriptor.DebugPrefabCount * 100) / FormatDescriptor.DebugCreateCount +
|
||||
"%)");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates all of the specified ListView entries. This is called after minor changes,
|
||||
/// such as editing a comment or renaming a label, that can be handled by regenerating
|
||||
/// selected parts of the DisplayList.
|
||||
/// </summary>
|
||||
/// <param name="offsetSet"></param>
|
||||
private void RefreshCodeListViewEntries(RangeSet offsetSet) {
|
||||
IEnumerator<RangeSet.Range> iter = offsetSet.RangeListIterator;
|
||||
while (iter.MoveNext()) {
|
||||
RangeSet.Range range = iter.Current;
|
||||
mDisplayList.GenerateRange(range.Low, range.High);
|
||||
}
|
||||
}
|
||||
|
||||
private void DoRefreshProject(UndoableChange.ReanalysisScope reanalysisRequired) {
|
||||
if (reanalysisRequired != UndoableChange.ReanalysisScope.DisplayOnly) {
|
||||
mGenerationLog = new CommonUtil.DebugLog();
|
||||
mGenerationLog.SetMinPriority(CommonUtil.DebugLog.Priority.Debug);
|
||||
mGenerationLog.SetShowRelTime(true);
|
||||
|
||||
mReanalysisTimer.StartTask("Call DisasmProject.Analyze()");
|
||||
mProject.Analyze(reanalysisRequired, mGenerationLog, mReanalysisTimer);
|
||||
mReanalysisTimer.EndTask("Call DisasmProject.Analyze()");
|
||||
}
|
||||
|
||||
if (mGenerationLog != null) {
|
||||
//mReanalysisTimer.StartTask("Save _log");
|
||||
//mGenerationLog.WriteToFile(@"C:\Src\WorkBench\SourceGen\TestData\_log.txt");
|
||||
//mReanalysisTimer.EndTask("Save _log");
|
||||
|
||||
#if false
|
||||
if (mShowAnalyzerOutputDialog != null) {
|
||||
mShowAnalyzerOutputDialog.BodyText = mGenerationLog.WriteToString();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
mReanalysisTimer.StartTask("Generate DisplayList");
|
||||
mDisplayList.GenerateAll();
|
||||
mReanalysisTimer.EndTask("Generate DisplayList");
|
||||
}
|
||||
|
||||
#endregion Project management
|
||||
}
|
||||
}
|
26
SourceGenWPF/ProjWin/DataFileLoadIssue.xaml
Normal file
26
SourceGenWPF/ProjWin/DataFileLoadIssue.xaml
Normal file
@ -0,0 +1,26 @@
|
||||
<Window x:Class="SourceGenWPF.ProjWin.DataFileLoadIssue"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:SourceGenWPF.ProjWin"
|
||||
mc:Ignorable="d"
|
||||
Title="Data File Load Issue" ShowInTaskbar="False"
|
||||
Width="570" Height="143" ResizeMode="NoResize" WindowStartupLocation="CenterOwner">
|
||||
<DockPanel Margin="8" LastChildFill="False">
|
||||
<TextBlock DockPanel.Dock="Top">There was an error while loading the data file:</TextBlock>
|
||||
<TextBox Name="pathNameTextBox" DockPanel.Dock="Top" Margin="0,8,0,0" IsReadOnly="True" MaxLines="1" Text="(filename)"/>
|
||||
<TextBlock Name="problemLabel" DockPanel.Dock="Top" Margin="0,8,0,0" Text="(reason)"/>
|
||||
<Grid DockPanel.Dock="Bottom" Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Column="0" Text="Do you want to locate the data file?"/>
|
||||
<Button Grid.Column="1" Content="OK" Width="75" IsDefault="True"/>
|
||||
<Button Grid.Column="2" Content="Cancel" Width="75" Margin="8,0,0,0" IsCancel="True"/>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Window>
|
47
SourceGenWPF/ProjWin/DataFileLoadIssue.xaml.cs
Normal file
47
SourceGenWPF/ProjWin/DataFileLoadIssue.xaml.cs
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2018 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.Windows;
|
||||
|
||||
namespace SourceGenWPF.ProjWin {
|
||||
/// <summary>
|
||||
/// Report a problem encountered while loading a data file.
|
||||
/// </summary>
|
||||
public partial class DataFileLoadIssue : Window {
|
||||
/// <summary>
|
||||
/// Path name of problematic file.
|
||||
/// </summary>
|
||||
private string mPathName;
|
||||
|
||||
/// <summary>
|
||||
/// Message to show in the dialog.
|
||||
/// </summary>
|
||||
private string mMessage;
|
||||
|
||||
|
||||
public DataFileLoadIssue(string pathName, string message) {
|
||||
InitializeComponent();
|
||||
|
||||
mPathName = pathName;
|
||||
mMessage = message;
|
||||
}
|
||||
|
||||
private void DataFileLoadIssue_Load(object sender, EventArgs e) {
|
||||
pathNameTextBox.Text = mPathName;
|
||||
problemLabel.Text = mMessage;
|
||||
}
|
||||
}
|
||||
}
|
19
SourceGenWPF/ProjWin/DiscardChanges.xaml
Normal file
19
SourceGenWPF/ProjWin/DiscardChanges.xaml
Normal file
@ -0,0 +1,19 @@
|
||||
<Window x:Class="SourceGenWPF.ProjWin.DiscardChanges"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:SourceGenWPF.ProjWin"
|
||||
mc:Ignorable="d"
|
||||
Title="Discard Changes?" ShowInTaskbar="False"
|
||||
Width="398" Height="125" ResizeMode="NoResize" WindowStartupLocation="CenterOwner">
|
||||
<StackPanel Margin="8">
|
||||
<TextBlock Text="You have unsaved changes that will be lost if you continue."/>
|
||||
<TextBlock Margin="0,8,0,0" Text="How do you wish to proceed?"/>
|
||||
<StackPanel Margin="0,16,0,0" Orientation="Horizontal">
|
||||
<Button Name="dontSaveButton" Width="120" Content="_Save & Continue" Click="DontSaveButton_Click"/>
|
||||
<Button Name="saveButton" Width="120" Margin="8,0,0,0" Content="_Discard & Continue" Click="SaveButton_Click"/>
|
||||
<Button Name="cancelButton" Width="120" Margin="8,0,0,0" Content="Cancel" IsCancel="True"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Window>
|
52
SourceGenWPF/ProjWin/DiscardChanges.xaml.cs
Normal file
52
SourceGenWPF/ProjWin/DiscardChanges.xaml.cs
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.Windows;
|
||||
|
||||
namespace SourceGenWPF.ProjWin {
|
||||
/// <summary>
|
||||
/// Prompt the user before discarding changes.
|
||||
///
|
||||
/// Dialog result will be false if the user cancels out. Otherwise, the result will be
|
||||
/// true, with the selected option in UserChoice.
|
||||
/// </summary>
|
||||
public partial class DiscardChanges : Window {
|
||||
public enum Choice {
|
||||
Unknown = 0,
|
||||
SaveAndContinue,
|
||||
DiscardAndContinue
|
||||
}
|
||||
public Choice UserChoice { get; private set; }
|
||||
|
||||
public DiscardChanges() {
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// https://stackoverflow.com/questions/817610/wpf-and-initial-focus
|
||||
// FocusManager.FocusedElement={Binding ElementName=cancelButton}"
|
||||
|
||||
private void SaveButton_Click(object sender, RoutedEventArgs e) {
|
||||
UserChoice = Choice.SaveAndContinue;
|
||||
DialogResult = true;
|
||||
}
|
||||
|
||||
private void DontSaveButton_Click(object sender, RoutedEventArgs e) {
|
||||
UserChoice = Choice.DiscardAndContinue;
|
||||
DialogResult = true;
|
||||
}
|
||||
}
|
||||
}
|
@ -30,10 +30,12 @@ limitations under the License.
|
||||
<KeyGesture>Ctrl+Shift+A</KeyGesture>
|
||||
</RoutedUICommand.InputGestures>
|
||||
</RoutedUICommand>
|
||||
<RoutedUICommand x:Key="RecentProject"/>
|
||||
</Window.Resources>
|
||||
|
||||
<Window.CommandBindings>
|
||||
<CommandBinding Command="{StaticResource AssembleCmd}" Executed="AssembleCmd_Executed"/>
|
||||
<CommandBinding Command="{StaticResource RecentProject}" Executed="RecentProject_Executed"/>
|
||||
</Window.CommandBindings>
|
||||
|
||||
<DockPanel>
|
||||
@ -196,8 +198,12 @@ limitations under the License.
|
||||
<StackPanel Grid.Row="1" HorizontalAlignment="Left">
|
||||
<Button Content="Start new project" Width="200" Height="50" Margin="10,30,10,10"/>
|
||||
<Button Content="Open existing project" Width="200" Height="50" Margin="10"/>
|
||||
<Button Content="Recent project #1" Width="200" Height="50" Margin="10"/>
|
||||
<Button Content="Recent project #2" Width="200" Height="50" Margin="10"/>
|
||||
<Button Content="Recent project #1" Width="200" Height="50" Margin="10"
|
||||
CommandParameter="1"
|
||||
Command="{DynamicResource RecentProject}"/>
|
||||
<Button Content="Recent project #2" Width="200" Height="50" Margin="10"
|
||||
CommandParameter="2"
|
||||
Command="{DynamicResource RecentProject}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
|
@ -33,19 +33,32 @@ namespace SourceGenWPF.ProjWin {
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window {
|
||||
public static readonly CommonUtil.Version ProgramVersion =
|
||||
new CommonUtil.Version(1, 2, 0, CommonUtil.Version.PreRelType.Alpha, 1);
|
||||
public string ProgramVersionString {
|
||||
get { return ProgramVersion.ToString(); }
|
||||
get { return App.ProgramVersion.ToString(); }
|
||||
}
|
||||
|
||||
private MainController mUI;
|
||||
|
||||
public MainWindow() {
|
||||
InitializeComponent();
|
||||
|
||||
mUI = new MainController();
|
||||
}
|
||||
|
||||
private void AssembleCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
|
||||
// test
|
||||
Debug.WriteLine("assembling");
|
||||
}
|
||||
|
||||
private void RecentProject_Executed(object sender, ExecutedRoutedEventArgs e) {
|
||||
if (!int.TryParse((string)e.Parameter, out int recentIndex) ||
|
||||
recentIndex < 1 || recentIndex > MainController.MAX_RECENT_PROJECTS) {
|
||||
throw new Exception("Bad parameter: " + e.Parameter);
|
||||
}
|
||||
recentIndex--;
|
||||
|
||||
Debug.WriteLine("Recent project #" + recentIndex);
|
||||
mUI.OpenRecentProject(recentIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
24
SourceGenWPF/ProjWin/ProjectLoadIssue.xaml
Normal file
24
SourceGenWPF/ProjWin/ProjectLoadIssue.xaml
Normal file
@ -0,0 +1,24 @@
|
||||
<Window x:Class="SourceGenWPF.ProjWin.ProjectLoadIssues"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:SourceGenWPF.ProjWin"
|
||||
mc:Ignorable="d"
|
||||
Title="Project Load Issues" ShowInTaskbar="False"
|
||||
Width="570" Height="231" ResizeMode="NoResize" WindowStartupLocation="CenterOwner"
|
||||
Loaded="ProjectLoadIssues_Loaded">
|
||||
<DockPanel LastChildFill="False" Margin="8">
|
||||
<TextBlock DockPanel.Dock="Top">Problems were detected while loading the project file:</TextBlock>
|
||||
<TextBox Name="messageTextBox" DockPanel.Dock="Top" Margin="0,8,0,0" Height="107"
|
||||
IsReadOnly="True" TextWrapping="Wrap" Text=""/>
|
||||
<TextBlock Name="invalidDiscardLabel" DockPanel.Dock="Top" Margin="0,8,0,0" Foreground="Red">
|
||||
Invalid data items will be discarded when you save the project.
|
||||
</TextBlock>
|
||||
<StackPanel DockPanel.Dock="Bottom" Margin="0,8,0,0"
|
||||
Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Name="okButton" Width="75" Content="Continue" IsDefault="True"/>
|
||||
<Button Name="cancelButton" Margin="8,0,0,0" Width="75" Content="Cancel" IsCancel="True"/>
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</Window>
|
65
SourceGenWPF/ProjWin/ProjectLoadIssue.xaml.cs
Normal file
65
SourceGenWPF/ProjWin/ProjectLoadIssue.xaml.cs
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.Windows;
|
||||
|
||||
namespace SourceGenWPF.ProjWin {
|
||||
/// <summary>
|
||||
/// Display errors and warnings generated while attempting to open a project.
|
||||
/// </summary>
|
||||
public partial class ProjectLoadIssues : Window {
|
||||
/// <summary>
|
||||
/// Multi-line message for text box.
|
||||
/// </summary>
|
||||
private string mMessages;
|
||||
|
||||
/// <summary>
|
||||
/// Which buttons are enabled.
|
||||
/// </summary>
|
||||
private Buttons mAllowedButtons;
|
||||
|
||||
public enum Buttons {
|
||||
Unknown = 0, Continue, Cancel, ContinueOrCancel
|
||||
}
|
||||
|
||||
|
||||
public ProjectLoadIssues(string msgs, Buttons allowedButtons) {
|
||||
InitializeComponent();
|
||||
|
||||
mMessages = msgs;
|
||||
mAllowedButtons = allowedButtons;
|
||||
}
|
||||
|
||||
private void ProjectLoadIssues_Loaded(object sender, RoutedEventArgs e) {
|
||||
messageTextBox.Text = mMessages;
|
||||
|
||||
if (mAllowedButtons == Buttons.Cancel) {
|
||||
// Continue not allowed
|
||||
okButton.IsEnabled = false;
|
||||
|
||||
// No point warning them about invalid data if they can't continue.
|
||||
invalidDiscardLabel.Visibility = Visibility.Hidden;
|
||||
}
|
||||
if (mAllowedButtons == Buttons.Continue) {
|
||||
// Cancel not allowed.
|
||||
cancelButton.IsEnabled = false;
|
||||
|
||||
// They're stuck with the problem.
|
||||
invalidDiscardLabel.Visibility = Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
<system:String x:Key="str_ErrBadTypeHint">Type hint not recognized</system:String>
|
||||
<system:String x:Key="str_ErrDuplicateLabelFmt">Removing duplicate label '{0}' (offset +{1:x6})</system:String>
|
||||
<system:String x:Key="str_ErrFileExistsNotDirFmt">The file {0} exists, but is not a directory.</system:String>
|
||||
<system:String x:Key="str_ErrFileGenericCaption">File Error</system:String>
|
||||
<system:String x:Key="str_ErrFileNotFoundFmt">File not found: {0}</system:String>
|
||||
<system:String x:Key="str_ErrFileReadOnlyFmt">Cannot write to read-only file {0}.</system:String>
|
||||
<system:String x:Key="str_ErrInvalidIntValue">Could not convert value to integer</system:String>
|
||||
@ -20,12 +21,24 @@
|
||||
<system:String x:Key="str_ErrNotProjectFile">This does not appear to be a valid .dis65 project file</system:String>
|
||||
<system:String x:Key="str_ErrProjectFileCorrupt">Project file may be corrupt</system:String>
|
||||
<system:String x:Key="str_ErrProjectLoadFail">Unable to load project file</system:String>
|
||||
<system:String x:Key="str_ErrProjectSaveFail">Unable to save project file</system:String>
|
||||
<system:String x:Key="str_FileFilterAll">All files (*.*)|*.*</system:String>
|
||||
<system:String x:Key="str_FileFilterCs">C# Source Files(*.cs)|*.cs</system:String>
|
||||
<system:String x:Key="str_FileFilterDis65">SourceGen projects(*.dis65)|*.dis65</system:String>
|
||||
<system:String x:Key="str_FileFilterSym65">SourceGen symbols (*.sym65)|*.sym65</system:String>
|
||||
<system:String x:Key="str_InitialExtensionScripts">Extension scripts:</system:String>
|
||||
<system:String x:Key="str_InitialParameters">Default settings:</system:String>
|
||||
<system:String x:Key="str_InitialSymbolFiles">Symbol files:</system:String>
|
||||
<system:String x:Key="str_OpenDataDoesntExist">The file doesn't exist.</system:String>
|
||||
<system:String x:Key="str_OpenDataEmpty">File is empty</system:String>
|
||||
<system:String x:Key="str_OpenDataFailCaption">Unable to load data file</system:String>
|
||||
<system:String x:Key="str_OpenDataFailMessage">Unable to load contents of data file</system:String>
|
||||
<system:String x:Key="str_OpenDataLoadFailedFmt">The file could not be opened: {0}.</system:String>
|
||||
<system:String x:Key="str_OpenDataPartialRead">Unable to read the entire file</system:String>
|
||||
<system:String x:Key="str_OpenDataTooLargeFmt">File is too large ({0:N0} KiB, {1:N0} KiB max).</system:String>
|
||||
<system:String x:Key="str_OpenDataWrongLengthFmt">The file is {0:N0} bytes long, but the project expected {1:N0}.</system:String>
|
||||
<system:String x:Key="str_OpenDataWrongCrcFmt">The file has CRC {0}, but the project expected {1}.</system:String>
|
||||
<system:String x:Key="str_OperationFailed">Failed</system:String>
|
||||
<system:String x:Key="str_ProjectFieldComment">comment</system:String>
|
||||
<system:String x:Key="str_ProjectFieldLongComment">long comment</system:String>
|
||||
<system:String x:Key="str_ProjectFieldNote">note</system:String>
|
||||
|
@ -1,4 +1,19 @@
|
||||
using System;
|
||||
/*
|
||||
* 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.Windows;
|
||||
|
||||
namespace SourceGenWPF.Res {
|
||||
@ -23,6 +38,8 @@ namespace SourceGenWPF.Res {
|
||||
(string)Application.Current.FindResource("str_ErrDuplicateLabelFmt");
|
||||
public static string ERR_FILE_EXISTS_NOT_DIR_FMT =
|
||||
(string)Application.Current.FindResource("str_ErrFileExistsNotDirFmt");
|
||||
public static string ERR_FILE_GENERIC_CAPTION =
|
||||
(string)Application.Current.FindResource("str_ErrFileGenericCaption");
|
||||
public static string ERR_FILE_NOT_FOUND_FMT =
|
||||
(string)Application.Current.FindResource("str_ErrFileNotFoundFmt");
|
||||
public static string ERR_FILE_READ_ONLY_FMT =
|
||||
@ -37,6 +54,10 @@ namespace SourceGenWPF.Res {
|
||||
(string)Application.Current.FindResource("str_ErrProjectFileCorrupt");
|
||||
public static string ERR_PROJECT_LOAD_FAIL =
|
||||
(string)Application.Current.FindResource("str_ErrProjectLoadFail");
|
||||
public static string ERR_PROJECT_SAVE_FAIL =
|
||||
(string)Application.Current.FindResource("str_ErrProjectSaveFail");
|
||||
public static string FILE_FILTER_ALL =
|
||||
(string)Application.Current.FindResource("str_FileFilterAll");
|
||||
public static string FILE_FILTER_CS =
|
||||
(string)Application.Current.FindResource("str_FileFilterCs");
|
||||
public static string FILE_FILTER_DIS65 =
|
||||
@ -49,6 +70,26 @@ namespace SourceGenWPF.Res {
|
||||
(string)Application.Current.FindResource("str_InitialParameters");
|
||||
public static string INITIAL_SYMBOL_FILES =
|
||||
(string)Application.Current.FindResource("str_InitialSymbolFiles");
|
||||
public static string OPEN_DATA_DOESNT_EXIST =
|
||||
(string)Application.Current.FindResource("str_OpenDataDoesntExist");
|
||||
public static string OPEN_DATA_EMPTY =
|
||||
(string)Application.Current.FindResource("str_OpenDataEmpty");
|
||||
public static string OPEN_DATA_FAIL_CAPTION =
|
||||
(string)Application.Current.FindResource("str_OpenDataFailCaption");
|
||||
public static string OPEN_DATA_FAIL_MESSAGE =
|
||||
(string)Application.Current.FindResource("str_OpenDataFailMessage");
|
||||
public static string OPEN_DATA_PARTIAL_READ =
|
||||
(string)Application.Current.FindResource("str_OpenDataPartialRead");
|
||||
public static string OPEN_DATA_LOAD_FAILED_FMT =
|
||||
(string)Application.Current.FindResource("str_OpenDataLoadFailedFmt");
|
||||
public static string OPEN_DATA_TOO_LARGE_FMT =
|
||||
(string)Application.Current.FindResource("str_OpenDataTooLargeFmt");
|
||||
public static string OPEN_DATA_WRONG_CRC_FMT =
|
||||
(string)Application.Current.FindResource("str_OpenDataWrongCrcFmt");
|
||||
public static string OPEN_DATA_WRONG_LENGTH_FMT =
|
||||
(string)Application.Current.FindResource("str_OpenDataWrongLengthFmt");
|
||||
public static string OPERATION_FAILED =
|
||||
(string)Application.Current.FindResource("str_OperationFailed");
|
||||
public static string PROJECT_FIELD_COMMENT =
|
||||
(string)Application.Current.FindResource("str_ProjectFieldComment");
|
||||
public static string PROJECT_FIELD_LONG_COMMENT =
|
||||
|
@ -62,6 +62,16 @@
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MainController.cs" />
|
||||
<Compile Include="ProjWin\DataFileLoadIssue.xaml.cs">
|
||||
<DependentUpon>DataFileLoadIssue.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ProjWin\DiscardChanges.xaml.cs">
|
||||
<DependentUpon>DiscardChanges.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ProjWin\ProjectLoadIssue.xaml.cs">
|
||||
<DependentUpon>ProjectLoadIssue.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="PseudoOp.cs" />
|
||||
<Compile Include="Res\Strings.xaml.cs" />
|
||||
<Compile Include="RuntimeDataAccess.cs" />
|
||||
@ -127,10 +137,22 @@
|
||||
<Resource Include="Res\Logo.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="ProjWin\DataFileLoadIssue.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="ProjWin\DiscardChanges.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="ProjWin\MainWindow.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="ProjWin\ProjectLoadIssue.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Res\Strings.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
Loading…
x
Reference in New Issue
Block a user