From 6b29ce98f9055de9310a055cf47ab57640561c37 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Sat, 22 Jun 2019 14:41:09 -0700 Subject: [PATCH] Implement "recents" feature Add the list of recent projects to File > Recent Projects when the sub-menu opens. Make the buttons on the launch panel work correctly. --- SourceGenWPF/MainController.cs | 57 ++++++----- SourceGenWPF/Res/Strings.xaml | 1 + SourceGenWPF/Res/Strings.xaml.cs | 2 + SourceGenWPF/WpfGui/MainWindow.xaml | 35 ++++--- SourceGenWPF/WpfGui/MainWindow.xaml.cs | 132 +++++++++++++++++-------- 5 files changed, 151 insertions(+), 76 deletions(-) diff --git a/SourceGenWPF/MainController.cs b/SourceGenWPF/MainController.cs index 5be114a..6c2eac1 100644 --- a/SourceGenWPF/MainController.cs +++ b/SourceGenWPF/MainController.cs @@ -66,7 +66,7 @@ namespace SourceGenWPF { /// /// List of recently-opened projects. /// - private List mRecentProjectPaths = new List(MAX_RECENT_PROJECTS); + public List RecentProjectPaths = new List(MAX_RECENT_PROJECTS); public const int MAX_RECENT_PROJECTS = 6; /// @@ -195,8 +195,8 @@ namespace SourceGenWPF { #if false UpdateMenuItemsAndTitle(); - UpdateRecentLinks(); #endif + mMainWin.UpdateRecentLinks(); ProcessCommandLine(); } @@ -405,10 +405,12 @@ namespace SourceGenWPF { Debug.WriteLine("Font convert failed: " + ex.Message); } } +#endif // Unpack the recent-project list. UnpackRecentProjectList(); +#if false // Enable the DEBUG menu if configured. bool showDebugMenu = AppSettings.Global.GetBool(AppSettings.DEBUG_MENU_ENABLED, false); if (dEBUGToolStripMenuItem.Visible != showDebugMenu) { @@ -427,6 +429,24 @@ namespace SourceGenWPF { } } + private void UnpackRecentProjectList() { + RecentProjectPaths.Clear(); + + string cereal = AppSettings.Global.GetString( + AppSettings.PRVW_RECENT_PROJECT_LIST, null); + if (string.IsNullOrEmpty(cereal)) { + return; + } + + try { + JavaScriptSerializer ser = new JavaScriptSerializer(); + RecentProjectPaths = ser.Deserialize>(cereal); + } catch (Exception ex) { + Debug.WriteLine("Failed deserializing recent projects: " + ex.Message); + return; + } + } + /// /// 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. @@ -438,32 +458,30 @@ namespace SourceGenWPF { // without having saved it. return; } - int index = mRecentProjectPaths.IndexOf(projectPath); + int index = RecentProjectPaths.IndexOf(projectPath); if (index == 0) { // Already in the list, nothing changes. No need to update anything else. return; } if (index > 0) { - mRecentProjectPaths.RemoveAt(index); + RecentProjectPaths.RemoveAt(index); } - mRecentProjectPaths.Insert(0, projectPath); + RecentProjectPaths.Insert(0, projectPath); // Trim the list to the max allowed. - while (mRecentProjectPaths.Count > MAX_RECENT_PROJECTS) { + while (RecentProjectPaths.Count > MAX_RECENT_PROJECTS) { Debug.WriteLine("Recent projects: dropping " + - mRecentProjectPaths[MAX_RECENT_PROJECTS]); - mRecentProjectPaths.RemoveAt(MAX_RECENT_PROJECTS); + RecentProjectPaths[MAX_RECENT_PROJECTS]); + RecentProjectPaths.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); + string cereal = ser.Serialize(RecentProjectPaths); AppSettings.Global.SetString(AppSettings.PRVW_RECENT_PROJECT_LIST, cereal); -#if false - UpdateRecentLinks(); -#endif + mMainWin.UpdateRecentLinks(); } #endregion Init and settings @@ -761,12 +779,7 @@ namespace SourceGenWPF { if (!CloseProject()) { return; } - //DoOpenFile(mRecentProjectPaths[projIndex]); - if (projIndex == 0) { - DoOpenFile(@"C:\Src\6502bench\EXTRA\ZIPPY#ff2000.dis65"); - } else { - DoOpenFile(@"C:\Src\6502bench\EXTRA\CRYLLAN.MISSION#b30100.dis65"); - } + DoOpenFile(RecentProjectPaths[projIndex]); } /// @@ -1027,7 +1040,7 @@ namespace SourceGenWPF { /// /// True if the project was closed, false if the user chose to cancel. public bool CloseProject() { - Debug.WriteLine("ProjectView.DoClose() - dirty=" + + Debug.WriteLine("CloseProject() - dirty=" + (mProject == null ? "N/A" : mProject.IsDirty.ToString())); if (mProject != null && mProject.IsDirty) { DiscardChanges dlg = new DiscardChanges(mMainWin); @@ -1065,13 +1078,11 @@ namespace SourceGenWPF { mDataPathName = null; mProjectPathName = null; mTargetHighlightIndex = -1; -#if false - mSymbolSubset = new SymbolTableSubset(new SymbolTable()); -#endif + // Clear this to release the memory. mMainWin.CodeDisplayList.Clear(); - mMainWin.InfoPanelContents = String.Empty; + mMainWin.InfoPanelContents = string.Empty; mMainWin.ShowCodeListView = false; mGenerationLog = null; diff --git a/SourceGenWPF/Res/Strings.xaml b/SourceGenWPF/Res/Strings.xaml index d60c87e..5d047f0 100644 --- a/SourceGenWPF/Res/Strings.xaml +++ b/SourceGenWPF/Res/Strings.xaml @@ -73,6 +73,7 @@ limitations under the License. type hint user-defined label This project was created by a newer version of SourceGen. It may contain data that will be lost if the project is edited. + The RuntimeData directory was not found. It should be in the same directory as the executable. RuntimeData Not Found {1} CPU @ {2} MHz diff --git a/SourceGenWPF/Res/Strings.xaml.cs b/SourceGenWPF/Res/Strings.xaml.cs index b607809..62810d9 100644 --- a/SourceGenWPF/Res/Strings.xaml.cs +++ b/SourceGenWPF/Res/Strings.xaml.cs @@ -126,6 +126,8 @@ namespace SourceGenWPF.Res { (string)Application.Current.FindResource("str_ProjectFieldUserLabel"); public static string PROJECT_FROM_NEWER_APP = (string)Application.Current.FindResource("str_ProjectFromNewerApp"); + //public static string RECENT_PROJECT_LINK_FMT = + // (string)Application.Current.FindResource("str_RecentProjectLinkFmt"); public static string RUNTIME_DIR_NOT_FOUND = (string)Application.Current.FindResource("str_RuntimeDirNotFound"); public static string RUNTIME_DIR_NOT_FOUND_CAPTION = diff --git a/SourceGenWPF/WpfGui/MainWindow.xaml b/SourceGenWPF/WpfGui/MainWindow.xaml index 96f3f22..47ae25b 100644 --- a/SourceGenWPF/WpfGui/MainWindow.xaml +++ b/SourceGenWPF/WpfGui/MainWindow.xaml @@ -123,7 +123,7 @@ limitations under the License. Executed="OpenCmd_Executed"/> - @@ -150,7 +150,7 @@ limitations under the License. - + @@ -173,7 +173,7 @@ limitations under the License. - + @@ -351,14 +351,27 @@ limitations under the License. - + diff --git a/SourceGenWPF/WpfGui/MainWindow.xaml.cs b/SourceGenWPF/WpfGui/MainWindow.xaml.cs index 431cc86..59f5dc5 100644 --- a/SourceGenWPF/WpfGui/MainWindow.xaml.cs +++ b/SourceGenWPF/WpfGui/MainWindow.xaml.cs @@ -19,6 +19,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; +using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Windows; @@ -142,20 +143,19 @@ namespace SourceGenWPF.WpfGui { } private void CreateCodeListContextMenu() { - // Find Actions menu. - ItemCollection mainItems = this.appMenu.Items; - MenuItem actionsMenu = null; - foreach (object obj in mainItems) { - if (!(obj is MenuItem)) { - continue; - } - MenuItem mi = (MenuItem)obj; - if (mi.Name.Equals("ActionsMenu")) { - actionsMenu = mi; - break; - } - } - Debug.Assert(actionsMenu != null); + //// Find Actions menu. + //ItemCollection mainItems = this.appMenu.Items; + //foreach (object obj in mainItems) { + // if (!(obj is MenuItem)) { + // continue; + // } + // MenuItem mi = (MenuItem)obj; + // if (mi.Name.Equals("actionsMenu")) { + // actionsMenu = mi; + // break; + // } + //} + //Debug.Assert(actionsMenu != null); // Clone the Actions menu into the codeListView context menu. ContextMenu ctxt = this.codeListView.ContextMenu; @@ -335,6 +335,8 @@ namespace SourceGenWPF.WpfGui { #endregion Window placement + #region Column widths + /// /// Grabs the widths of the columns of the various grids and saves them in the /// global AppSettings. @@ -416,32 +418,7 @@ namespace SourceGenWPF.WpfGui { } } - /// - /// Sets the focus on the code list. - /// - //public void CodeListView_Focus() { - // codeListView.Focus(); - //} - - /// - /// Handles a double-click on the code list. We have to figure out which row and - /// column were clicked, which is not easy in WPF. - /// - private void CodeListView_MouseDoubleClick(object sender, MouseButtonEventArgs e) { - Debug.Assert(sender == codeListView); - - ListViewItem lvi = codeListView.GetClickedItem(e); - if (lvi == null) { - return; - } - DisplayList.FormattedParts parts = (DisplayList.FormattedParts)lvi.Content; - int row = parts.ListIndex; - int col = codeListView.GetClickEventColumn(e); - if (col < 0) { - return; - } - mMainCtrl.HandleCodeListDoubleClick(row, col); - } + #endregion Column widths #region Selection management @@ -658,6 +635,7 @@ namespace SourceGenWPF.WpfGui { #endregion Selection management + #region Can-execute handlers /// @@ -829,8 +807,15 @@ namespace SourceGenWPF.WpfGui { } private void RecentProjectCmd_Executed(object sender, ExecutedRoutedEventArgs e) { - if (!int.TryParse((string)e.Parameter, out int recentIndex) || - recentIndex < 0 || recentIndex >= MainController.MAX_RECENT_PROJECTS) { + int recentIndex; + if (e.Parameter is int) { + recentIndex = (int)e.Parameter; + } else if (e.Parameter is string) { + recentIndex = int.Parse((string)e.Parameter); + } else { + throw new Exception("Bad parameter: " + e.Parameter); + } + if (recentIndex < 0 || recentIndex >= MainController.MAX_RECENT_PROJECTS) { throw new Exception("Bad parameter: " + e.Parameter); } @@ -844,6 +829,69 @@ namespace SourceGenWPF.WpfGui { #endregion Command handlers + #region Misc + + /// + /// Handles a double-click on the code list. We have to figure out which row and + /// column were clicked, which is not easy in WPF. + /// + private void CodeListView_MouseDoubleClick(object sender, MouseButtonEventArgs e) { + Debug.Assert(sender == codeListView); + + ListViewItem lvi = codeListView.GetClickedItem(e); + if (lvi == null) { + return; + } + DisplayList.FormattedParts parts = (DisplayList.FormattedParts)lvi.Content; + int row = parts.ListIndex; + int col = codeListView.GetClickEventColumn(e); + if (col < 0) { + return; + } + mMainCtrl.HandleCodeListDoubleClick(row, col); + } + + private void RecentProjectsMenu_SubmenuOpened(object sender, RoutedEventArgs e) { + MenuItem recents = (MenuItem)sender; + recents.Items.Clear(); + + Debug.WriteLine("COUNT is " + mMainCtrl.RecentProjectPaths.Count); + if (mMainCtrl.RecentProjectPaths.Count == 0) { + MenuItem mi = new MenuItem(); + mi.Header = "(none)"; + recents.Items.Add(mi); + } else { + for (int i = 0; i < mMainCtrl.RecentProjectPaths.Count; i++) { + MenuItem mi = new MenuItem(); + mi.Header = string.Format("{0}: {1}", i + 1, mMainCtrl.RecentProjectPaths[i]); + mi.Command = recentProjectCmd.Command; + mi.CommandParameter = i; + recents.Items.Add(mi); + } + } + } + + public void UpdateRecentLinks() { + List pathList = mMainCtrl.RecentProjectPaths; + + if (pathList.Count >= 1) { + recentProjectName1.Text = Path.GetFileName(pathList[0]); + recentProjectButton1.Visibility = Visibility.Visible; + } else { + recentProjectName1.Text = string.Empty; + recentProjectButton1.Visibility = Visibility.Collapsed; + } + if (pathList.Count >= 2) { + recentProjectName2.Text = Path.GetFileName(pathList[1]); + recentProjectButton2.Visibility = Visibility.Visible; + } else { + recentProjectName2.Text = string.Empty; + recentProjectButton2.Visibility = Visibility.Collapsed; + } + } + + #endregion Misc + #region References panel