diff --git a/SourceGenWPF/MainController.cs b/SourceGenWPF/MainController.cs index 14b8380..80e909f 100644 --- a/SourceGenWPF/MainController.cs +++ b/SourceGenWPF/MainController.cs @@ -606,18 +606,41 @@ namespace SourceGenWPF { return true; } +#if false + private class FinishPrepProgress : WorkProgress.IWorker { + public string ExtMessages { get; private set; } + private MainController mMainCtrl; + + public FinishPrepProgress(MainController mainCtrl) { + mMainCtrl = mainCtrl; + } + public object DoWork(BackgroundWorker worker) { + string messages = mMainCtrl.mProject.LoadExternalFiles(); + mMainCtrl.DoRefreshProject(UndoableChange.ReanalysisScope.CodeAndData); + return messages; + } + + public void RunWorkerCompleted(object results) { + ExtMessages = (string)results; + } + } +#endif + private void FinishPrep() { + CodeLineList = new LineListGen(mProject, mMainWin.CodeDisplayList, + mOutputFormatter, mPseudoOpNames); + string messages = mProject.LoadExternalFiles(); if (messages.Length != 0) { - // ProjectLoadIssues isn't quite the right dialog, but it'll do. + // ProjectLoadIssues isn't quite the right dialog, but it'll do. This is + // purely informative; no decision needs to be made. ProjectLoadIssues dlg = new ProjectLoadIssues(mMainWin, messages, ProjectLoadIssues.Buttons.Continue); dlg.ShowDialog(); } - CodeLineList = new LineListGen(mProject, mMainWin.CodeDisplayList, - mOutputFormatter, mPseudoOpNames); - + // Ideally we'd call DoRefreshProject (and LoadExternalFiles) from a progress + // dialog, but we're not allowed to update the DisplayList from a different thread. RefreshProject(UndoableChange.ReanalysisScope.CodeAndData); // Populate the Symbols list. @@ -753,6 +776,20 @@ namespace SourceGenWPF { UpdateSelectionHighlight(); } + /// + /// 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. + /// + /// + private void RefreshCodeListViewEntries(RangeSet offsetSet) { + IEnumerator iter = offsetSet.RangeListIterator; + while (iter.MoveNext()) { + RangeSet.Range range = iter.Current; + CodeLineList.GenerateRange(range.Low, range.High); + } + } + /// /// Refreshes the project after something of substance has changed. Some /// re-analysis will be done, followed by a complete rebuild of the DisplayList. @@ -768,6 +805,47 @@ namespace SourceGenWPF { // 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. + if (mProject.FileDataLength > 65536) { + try { + Mouse.OverrideCursor = Cursors.Wait; + DoRefreshProject(reanalysisRequired); + } finally { + Mouse.OverrideCursor = null; + } + } else { + DoRefreshProject(reanalysisRequired); + } + + if (mGenerationLog != null) { + //mReanalysisTimer.StartTask("Save _log"); + //mGenerationLog.WriteToFile(@"C:\Src\WorkBench\SourceGen\TestData\_log.txt"); + //mReanalysisTimer.EndTask("Save _log"); + + if (mShowAnalyzerOutputDialog != null) { + mShowAnalyzerOutputDialog.DisplayText = mGenerationLog.WriteToString(); + } + } + + if (FormatDescriptor.DebugCreateCount != 0) { + Debug.WriteLine("FormatDescriptor total=" + FormatDescriptor.DebugCreateCount + + " prefab=" + FormatDescriptor.DebugPrefabCount + " (" + + (FormatDescriptor.DebugPrefabCount * 100) / FormatDescriptor.DebugCreateCount + + "%)"); + } + } + + /// + /// Refreshes the project after something of substance has changed. + /// + /// + /// Ideally from this point on we can run on a background thread. The tricky part + /// is the close relationship between LineListGen and DisplayList -- we can't update + /// DisplayList from a background thread. Until that's fixed, putting up a "working..." + /// dialog or other UI will be awkward. + /// + /// Indicates whether reanalysis is required, and + /// what level. + private void DoRefreshProject(UndoableChange.ReanalysisScope reanalysisRequired) { // 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. @@ -780,40 +858,6 @@ namespace SourceGenWPF { mOutputFormatterCpuDef = mProject.CpuDef; } - if (CodeLineList.Count > 40000) { - try { - Mouse.OverrideCursor = Cursors.Wait; - DoRefreshProject(reanalysisRequired); - } finally { - Mouse.OverrideCursor = null; - } - } else { - DoRefreshProject(reanalysisRequired); - } - - if (FormatDescriptor.DebugCreateCount != 0) { - Debug.WriteLine("FormatDescriptor total=" + FormatDescriptor.DebugCreateCount + - " prefab=" + FormatDescriptor.DebugPrefabCount + " (" + - (FormatDescriptor.DebugPrefabCount * 100) / FormatDescriptor.DebugCreateCount + - "%)"); - } - } - - /// - /// 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. - /// - /// - private void RefreshCodeListViewEntries(RangeSet offsetSet) { - IEnumerator iter = offsetSet.RangeListIterator; - while (iter.MoveNext()) { - RangeSet.Range range = iter.Current; - CodeLineList.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); @@ -824,16 +868,6 @@ namespace SourceGenWPF { 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 (mShowAnalyzerOutputDialog != null) { - mShowAnalyzerOutputDialog.DisplayText = mGenerationLog.WriteToString(); - } - } - mReanalysisTimer.StartTask("Generate DisplayList"); CodeLineList.GenerateAll(); mReanalysisTimer.EndTask("Generate DisplayList"); @@ -903,13 +937,14 @@ namespace SourceGenWPF { } /// - /// Handles opening an existing project, given a pathname to the project file. + /// Handles opening an existing project, given a full pathname to the project file. /// private void DoOpenFile(string projPathName) { Debug.WriteLine("DoOpenFile: " + projPathName); Debug.Assert(mProject == null); if (!File.Exists(projPathName)) { + // Should only happen for projects in "recents". 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); @@ -1188,11 +1223,8 @@ namespace SourceGenWPF { mProjectPathName = null; mTargetHighlightIndex = -1; - // Clear this to release the memory. - mMainWin.CodeDisplayList.Clear(); - - mMainWin.InfoPanelContents = string.Empty; mMainWin.ShowCodeListView = false; + mMainWin.ProjectClosing(); mGenerationLog = null; diff --git a/SourceGenWPF/WpfGui/MainWindow.xaml.cs b/SourceGenWPF/WpfGui/MainWindow.xaml.cs index 0b987b1..18cf433 100644 --- a/SourceGenWPF/WpfGui/MainWindow.xaml.cs +++ b/SourceGenWPF/WpfGui/MainWindow.xaml.cs @@ -388,6 +388,22 @@ namespace SourceGenWPF.WpfGui { } } + /// + /// Cleans up state when MainController decides to close the project. + /// + public void ProjectClosing() { + // Clear this to release the memory. + CodeDisplayList.Clear(); + + InfoPanelContents = string.Empty; + + // If you open a new project while one is already open, the ListView apparently + // doesn't reset certain state, possibly because it's never asked to draw after + // the list is cleared. This results in the new project being open at the same + // line as the previous project. This is a little weird, so we reset it here. + CodeListView_SetTopIndex(0); + } + /// /// Catch mouse-down events so we can treat the fourth mouse button as "back". /// diff --git a/SourceGenWPF/WpfGui/WorkProgress.xaml.cs b/SourceGenWPF/WpfGui/WorkProgress.xaml.cs index f744405..bbe91fa 100644 --- a/SourceGenWPF/WpfGui/WorkProgress.xaml.cs +++ b/SourceGenWPF/WpfGui/WorkProgress.xaml.cs @@ -126,7 +126,7 @@ namespace SourceGenWPF.WpfGui { DialogResult = false; } } else if (e.Error != null) { - // Unexpected -- shell command execution shouldn't throw exceptions. + // Unexpected; success/failure should be passed through e.Result. MessageBox.Show(e.Error.ToString(), Res.Strings.OPERATION_FAILED, MessageBoxButton.OK, MessageBoxImage.Error); DialogResult = false;