From a81c28f1140db20023a13f59531dd7fa19d06afa Mon Sep 17 00:00:00 2001 From: Kurt Date: Sun, 15 Jul 2018 17:48:31 -0700 Subject: [PATCH] Upgrade FolderList to show recent/backups sortable grid like the ReportGrid for pkm, can open/browse individual files --- PKHeX.Core/Saves/SAV1.cs | 2 +- PKHeX.Core/Saves/SaveFile.cs | 3 +- PKHeX.Core/Saves/Storage/Bank7.cs | 2 +- PKHeX.WinForms/MainWindow/Main.cs | 13 +- .../Subforms/SAV_FolderList.Designer.cs | 121 +++++++++++- PKHeX.WinForms/Subforms/SAV_FolderList.cs | 184 +++++++++++++++++- PKHeX.WinForms/Util/PathUtilWindows.cs | 10 +- 7 files changed, 317 insertions(+), 18 deletions(-) diff --git a/PKHeX.Core/Saves/SAV1.cs b/PKHeX.Core/Saves/SAV1.cs index 882497639..11e6fffab 100644 --- a/PKHeX.Core/Saves/SAV1.cs +++ b/PKHeX.Core/Saves/SAV1.cs @@ -264,7 +264,7 @@ namespace PKHeX.Core } private int PlayedTimeOffset => Japanese ? 0x2CA0 : 0x2CED; - protected override string PlayTimeString => !PlayedMaximum ? base.PlayTimeString : $"{base.PlayTimeString} {SaveUtil.CRC16_CCITT(Data):X4}"; + public override string PlayTimeString => !PlayedMaximum ? base.PlayTimeString : $"{base.PlayTimeString} {SaveUtil.CRC16_CCITT(Data):X4}"; public override int PlayedHours { get => Data[PlayedTimeOffset + 0]; diff --git a/PKHeX.Core/Saves/SaveFile.cs b/PKHeX.Core/Saves/SaveFile.cs index a1cc587b0..f7f156dcb 100644 --- a/PKHeX.Core/Saves/SaveFile.cs +++ b/PKHeX.Core/Saves/SaveFile.cs @@ -25,7 +25,7 @@ namespace PKHeX.Core public byte[] Footer { protected get; set; } = new byte[0]; // .dsv public byte[] Header { protected get; set; } = new byte[0]; // .gci public bool Japanese { get; protected set; } - protected virtual string PlayTimeString => $"{PlayedHours}ː{PlayedMinutes:00}ː{PlayedSeconds:00}"; // not : + public virtual string PlayTimeString => $"{PlayedHours}ː{PlayedMinutes:00}ː{PlayedSeconds:00}"; // not : public bool IndeterminateGame => Version == GameVersion.Unknown; public virtual bool IndeterminateSubVersion => false; public abstract string Extension { get; } @@ -392,6 +392,7 @@ namespace PKHeX.Core public virtual int TID { get; set; } public virtual int SID { get; set; } public int TrainerID7 => (int)((uint)(TID | (SID << 16)) % 1000000); + public int TrainerSID7 => (int)((uint)(TID | (SID << 16)) / 1000000); public virtual string OT { get; set; } = "PKHeX"; public virtual int PlayedHours { get; set; } public virtual int PlayedMinutes { get; set; } diff --git a/PKHeX.Core/Saves/Storage/Bank7.cs b/PKHeX.Core/Saves/Storage/Bank7.cs index c04893003..3ef49d2ed 100644 --- a/PKHeX.Core/Saves/Storage/Bank7.cs +++ b/PKHeX.Core/Saves/Storage/Bank7.cs @@ -11,7 +11,7 @@ namespace PKHeX.Core HeldItems = Legal.HeldItems_USUM; } - protected override string PlayTimeString => $"{Year:00}{Month:00}{Day:00}_{Hours:00}ː{Minutes:00}"; + public override string PlayTimeString => $"{Year:00}{Month:00}{Day:00}_{Hours:00}ː{Minutes:00}"; public override string BAKName => $"{FileName} [{PlayTimeString}].bak"; private const int GroupNameSize = 0x20; private const int BankNameSize = 0x24; diff --git a/PKHeX.WinForms/MainWindow/Main.cs b/PKHeX.WinForms/MainWindow/Main.cs index e4031048b..c344f47cf 100644 --- a/PKHeX.WinForms/MainWindow/Main.cs +++ b/PKHeX.WinForms/MainWindow/Main.cs @@ -386,7 +386,18 @@ namespace PKHeX.WinForms C_SAV.SetPKMBoxes(); // refresh C_SAV.UpdateBoxViewers(); } - private void MainMenuFolder(object sender, EventArgs e) => new SAV_FolderList().ShowDialog(); + private void MainMenuFolder(object sender, EventArgs e) + { + var ofType = Application.OpenForms.OfType().FirstOrDefault(); + if (ofType != null) + { + ofType.CenterToForm(this); + ofType.BringToFront(); + } + else + new SAV_FolderList(s => OpenSAV(s, s.FilePath)).Show(); + } + // Misc Options private void ClickShowdownImportPKM(object sender, EventArgs e) { diff --git a/PKHeX.WinForms/Subforms/SAV_FolderList.Designer.cs b/PKHeX.WinForms/Subforms/SAV_FolderList.Designer.cs index 78f9f7ffc..c983bb717 100644 --- a/PKHeX.WinForms/Subforms/SAV_FolderList.Designer.cs +++ b/PKHeX.WinForms/Subforms/SAV_FolderList.Designer.cs @@ -28,26 +28,121 @@ /// private void InitializeComponent() { + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SAV_FolderList)); this.FLP_Buttons = new System.Windows.Forms.FlowLayoutPanel(); + this.TC_Tabs = new System.Windows.Forms.TabControl(); + this.Tab_Recent = new System.Windows.Forms.TabPage(); + this.dgDataRecent = new System.Windows.Forms.DataGridView(); + this.Tab_Backup = new System.Windows.Forms.TabPage(); + this.dgDataBackup = new System.Windows.Forms.DataGridView(); + this.Tab_Folders = new System.Windows.Forms.TabPage(); + this.TC_Tabs.SuspendLayout(); + this.Tab_Recent.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dgDataRecent)).BeginInit(); + this.Tab_Backup.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dgDataBackup)).BeginInit(); + this.Tab_Folders.SuspendLayout(); this.SuspendLayout(); - // + // // FLP_Buttons - // + // this.FLP_Buttons.AutoScroll = true; this.FLP_Buttons.Dock = System.Windows.Forms.DockStyle.Fill; this.FLP_Buttons.Location = new System.Drawing.Point(0, 0); this.FLP_Buttons.Name = "FLP_Buttons"; this.FLP_Buttons.Padding = new System.Windows.Forms.Padding(3); - this.FLP_Buttons.Size = new System.Drawing.Size(304, 161); + this.FLP_Buttons.Size = new System.Drawing.Size(601, 335); this.FLP_Buttons.TabIndex = 0; - // + // + // TC_Tabs + // + this.TC_Tabs.Controls.Add(this.Tab_Recent); + this.TC_Tabs.Controls.Add(this.Tab_Backup); + this.TC_Tabs.Controls.Add(this.Tab_Folders); + this.TC_Tabs.Dock = System.Windows.Forms.DockStyle.Fill; + this.TC_Tabs.Location = new System.Drawing.Point(0, 0); + this.TC_Tabs.Name = "TC_Tabs"; + this.TC_Tabs.SelectedIndex = 0; + this.TC_Tabs.Size = new System.Drawing.Size(609, 361); + this.TC_Tabs.TabIndex = 1; + // + // Tab_Recent + // + this.Tab_Recent.Controls.Add(this.dgDataRecent); + this.Tab_Recent.Location = new System.Drawing.Point(4, 22); + this.Tab_Recent.Name = "Tab_Recent"; + this.Tab_Recent.Size = new System.Drawing.Size(601, 335); + this.Tab_Recent.TabIndex = 1; + this.Tab_Recent.Text = "Recent"; + this.Tab_Recent.UseVisualStyleBackColor = true; + // + // dgDataRecent + // + this.dgDataRecent.AllowUserToAddRows = false; + this.dgDataRecent.AllowUserToDeleteRows = false; + this.dgDataRecent.AllowUserToOrderColumns = true; + this.dgDataRecent.AllowUserToResizeColumns = false; + this.dgDataRecent.AllowUserToResizeRows = false; + dataGridViewCellStyle1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); + this.dgDataRecent.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle1; + this.dgDataRecent.ClipboardCopyMode = System.Windows.Forms.DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText; + this.dgDataRecent.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.DisableResizing; + this.dgDataRecent.Dock = System.Windows.Forms.DockStyle.Fill; + this.dgDataRecent.Location = new System.Drawing.Point(0, 0); + this.dgDataRecent.Margin = new System.Windows.Forms.Padding(0); + this.dgDataRecent.Name = "dgDataRecent"; + this.dgDataRecent.RowHeadersVisible = false; + this.dgDataRecent.Size = new System.Drawing.Size(601, 335); + this.dgDataRecent.TabIndex = 2; + this.dgDataRecent.CellMouseDown += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.DataGridCellMouseDown); + // + // Tab_Backup + // + this.Tab_Backup.Controls.Add(this.dgDataBackup); + this.Tab_Backup.Location = new System.Drawing.Point(4, 22); + this.Tab_Backup.Name = "Tab_Backup"; + this.Tab_Backup.Size = new System.Drawing.Size(601, 335); + this.Tab_Backup.TabIndex = 2; + this.Tab_Backup.Text = "Backups"; + this.Tab_Backup.UseVisualStyleBackColor = true; + // + // dgDataBackup + // + this.dgDataBackup.AllowUserToAddRows = false; + this.dgDataBackup.AllowUserToDeleteRows = false; + this.dgDataBackup.AllowUserToOrderColumns = true; + this.dgDataBackup.AllowUserToResizeColumns = false; + this.dgDataBackup.AllowUserToResizeRows = false; + dataGridViewCellStyle2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); + this.dgDataBackup.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle2; + this.dgDataBackup.ClipboardCopyMode = System.Windows.Forms.DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText; + this.dgDataBackup.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.DisableResizing; + this.dgDataBackup.Dock = System.Windows.Forms.DockStyle.Fill; + this.dgDataBackup.Location = new System.Drawing.Point(0, 0); + this.dgDataBackup.Name = "dgDataBackup"; + this.dgDataBackup.RowHeadersVisible = false; + this.dgDataBackup.Size = new System.Drawing.Size(601, 335); + this.dgDataBackup.TabIndex = 1; + this.dgDataBackup.CellMouseDown += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.DataGridCellMouseDown); + // + // Tab_Folders + // + this.Tab_Folders.Controls.Add(this.FLP_Buttons); + this.Tab_Folders.Location = new System.Drawing.Point(4, 22); + this.Tab_Folders.Name = "Tab_Folders"; + this.Tab_Folders.Size = new System.Drawing.Size(601, 335); + this.Tab_Folders.TabIndex = 0; + this.Tab_Folders.Text = "Folders"; + this.Tab_Folders.UseVisualStyleBackColor = true; + // // SAV_FolderList - // + // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(304, 161); - this.Controls.Add(this.FLP_Buttons); + this.ClientSize = new System.Drawing.Size(609, 361); + this.Controls.Add(this.TC_Tabs); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MaximizeBox = false; @@ -55,6 +150,12 @@ this.Name = "SAV_FolderList"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Folder List"; + this.TC_Tabs.ResumeLayout(false); + this.Tab_Recent.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dgDataRecent)).EndInit(); + this.Tab_Backup.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dgDataBackup)).EndInit(); + this.Tab_Folders.ResumeLayout(false); this.ResumeLayout(false); } @@ -62,5 +163,11 @@ #endregion private System.Windows.Forms.FlowLayoutPanel FLP_Buttons; + private System.Windows.Forms.TabControl TC_Tabs; + private System.Windows.Forms.TabPage Tab_Folders; + private System.Windows.Forms.TabPage Tab_Recent; + private System.Windows.Forms.TabPage Tab_Backup; + private System.Windows.Forms.DataGridView dgDataBackup; + private System.Windows.Forms.DataGridView dgDataRecent; } } \ No newline at end of file diff --git a/PKHeX.WinForms/Subforms/SAV_FolderList.cs b/PKHeX.WinForms/Subforms/SAV_FolderList.cs index fe60d26d7..3274260a5 100644 --- a/PKHeX.WinForms/Subforms/SAV_FolderList.cs +++ b/PKHeX.WinForms/Subforms/SAV_FolderList.cs @@ -1,18 +1,28 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; +using System.Threading.Tasks; using System.Windows.Forms; +using PKHeX.Core; +using PKHeX.WinForms.Properties; using static PKHeX.Core.MessageStrings; namespace PKHeX.WinForms { public partial class SAV_FolderList : Form { - public SAV_FolderList() + private readonly Action OpenSaveFile; + private readonly List Paths; + private readonly SortableBindingList Recent; + private readonly SortableBindingList Backup; + + public SAV_FolderList(Action openSaveFile) { InitializeComponent(); + OpenSaveFile = openSaveFile; // Preprogrammed folders var locs = new List @@ -25,12 +35,24 @@ namespace PKHeX.WinForms addIfExists(CyberGadgetUtil.GetCacheFolder(), "CGSE Cache"); addIfExists(CyberGadgetUtil.GetTempFolder(), "CGSE Temp"); - var paths = locs.GroupBy(z => z.Path).Select(z => z.First()) - .OrderByDescending(z => Directory.Exists(z.Path)); - foreach (var loc in paths) + Paths = locs.GroupBy(z => z.Path).Select(z => z.First()) + .OrderByDescending(z => Directory.Exists(z.Path)).ToList(); + foreach (var loc in Paths) AddButton(loc.DisplayText, loc.Path); + var recent = PathUtilWindows.GetSaveFiles(Paths.Select(z => z.Path).Where(z => z != Main.BackupPath)); + Recent = PopulateData(dgDataRecent, recent); + var backup = PathUtilWindows.GetSaveFiles(Main.BackupPath); + Backup = PopulateData(dgDataBackup, backup); + + dgDataRecent.ContextMenuStrip = GetContextMenu(dgDataRecent); + dgDataBackup.ContextMenuStrip = GetContextMenu(dgDataBackup); + + dgDataRecent.DoubleBuffered(true); + dgDataBackup.DoubleBuffered(true); + WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage); + CenterToParent(); void addIfExists(string path, string text) { @@ -92,7 +114,7 @@ namespace PKHeX.WinForms return paths.Select(z => new CustomFolderPath(z)); } - private struct CustomFolderPath + private class CustomFolderPath { public readonly string Path; public readonly string DisplayText; @@ -121,5 +143,155 @@ namespace PKHeX.WinForms DisplayText = display; } } + + private string GetParentFolderName(SaveFile first) + { + var parent = Paths.Find(z => first.FilePath.StartsWith(z.Path)); + return parent?.DisplayText ?? "???"; + } + + private sealed class SaveList : SortableBindingList { } + private sealed class SavePreview + { + public readonly SaveFile Save; + + public SavePreview(SaveFile sav, string parent) + { + Save = sav; + Folder = parent; + } + + public string OT => Save.OT; + public int G => Save.Generation; + public GameVersion Game => Save.Version; + + public string Played => Save.PlayTimeString.PadLeft(9, '0'); + public string FileTime => new FileInfo(Save.FilePath).LastWriteTime.ToString("yyyy.MM.dd:hh:mm:ss"); + + public int TID => Save.Generation >= 7 ? Save.TrainerID7 : Save.TID; + public int SID => Save.Generation >= 7 ? Save.TrainerSID7 : Save.SID; + + // ReSharper disable once MemberCanBePrivate.Local + // ReSharper disable once UnusedAutoPropertyAccessor.Local + public string Folder { get; } + } + + private ContextMenuStrip GetContextMenu(DataGridView dgv) + { + var mnuOpen = new ToolStripMenuItem + { + Name = "mnuOpen", + Text = "Open", + Image = Resources.open, + }; + mnuOpen.Click += (sender, e) => ClickOpenFile(dgv); + + var mnuBrowseAt = new ToolStripMenuItem + { + Name = "mnuBrowseAt", + Text = "Browse...", + Image = Resources.folder, + }; + mnuBrowseAt.Click += (sender, e) => ClickOpenFolder(dgv); + + ContextMenuStrip mnu = new ContextMenuStrip(); + mnu.Items.Add(mnuOpen); + mnu.Items.Add(mnuBrowseAt); + return mnu; + } + private void ClickOpenFile(DataGridView dgv) + { + var sav = GetSaveFile(dgv); + if (sav == null) + { + WinFormsUtil.Alert(MsgFileLoadFail); + return; + } + + OpenSaveFile(sav.Save); + } + private void ClickOpenFolder(DataGridView dgv) + { + var sav = GetSaveFile(dgv); + if (sav == null) + { + WinFormsUtil.Alert(MsgFileLoadFail); + return; + } + + var path = sav.Save.FilePath; + Process.Start("explorer.exe", $"/select, \"{path}\""); + } + private SavePreview GetSaveFile(DataGridView dgData) + { + var c = dgData.SelectedCells; + if (c.Count != 1) + return null; + + var item = c[0].RowIndex; + var parent = dgData == dgDataRecent ? Recent : Backup; + return parent[item]; + } + + private void DataGridCellMouseDown(object sender, DataGridViewCellMouseEventArgs e) + { + if (e.ColumnIndex == -1 || e.RowIndex == -1 || e.Button != MouseButtons.Right) + return; + + var c = ((DataGridView) sender)[e.ColumnIndex, e.RowIndex]; + c.DataGridView.ClearSelection(); + c.DataGridView.CurrentCell = c; + c.Selected = true; + } + + private SaveList PopulateData(DataGridView dgData, IEnumerable saves) + { + var list = new SaveList(); + + var enumerator = saves.GetEnumerator(); + while (enumerator.Current == null) + { + if (!enumerator.MoveNext()) + return list; + } + + var first = enumerator.Current; + var sav1 = new SavePreview(first, GetParentFolderName(first)); + LoadEntryInitial(dgData, list, sav1); + Task.Run(async () => // load the rest async + { + while (enumerator.MoveNext()) + { + var next = enumerator.Current; + if (next == null) + continue; + var sav = new SavePreview(next, GetParentFolderName(next)); + while (!dgData.IsHandleCreated) + await Task.Delay(100).ConfigureAwait(false); + dgData.Invoke(new Action(() => LoadEntry(dgData, list, sav))); + } + enumerator.Dispose(); + }); + + return list; + } + + private static void LoadEntryInitial(DataGridView dgData, SaveList list, SavePreview sav) + { + list.Add(sav); + dgData.DataSource = list; + dgData.AutoGenerateColumns = true; + for (int i = 0; i < dgData.Columns.Count; i++) + dgData.Columns[i].SortMode = DataGridViewColumnSortMode.Automatic; + dgData.AutoResizeColumns(); // Trigger Resizing + } + + private static void LoadEntry(DataGridView dgData, ICollection list, SavePreview sav) + { + dgData.SuspendLayout(); + list.Add(sav); + dgData.AutoResizeColumns(); + dgData.ResumeLayout(); + } } } diff --git a/PKHeX.WinForms/Util/PathUtilWindows.cs b/PKHeX.WinForms/Util/PathUtilWindows.cs index 41a91cd19..c7cd88527 100644 --- a/PKHeX.WinForms/Util/PathUtilWindows.cs +++ b/PKHeX.WinForms/Util/PathUtilWindows.cs @@ -90,12 +90,20 @@ namespace PKHeX.WinForms return saves.FirstOrDefault(z => z?.ChecksumsValid == true); } + /// /// Gets all detectable save files ordered by most recently saved (by file write time). /// /// Paths to check in addition to the default paths /// Valid save files, if any. - public static IEnumerable GetSaveFiles(params string[] extra) + public static IEnumerable GetSaveFiles(params string[] extra) => GetSaveFiles((IEnumerable)extra); + + /// + /// Gets all detectable save files ordered by most recently saved (by file write time). + /// + /// Paths to check in addition to the default paths + /// Valid save files, if any. + public static IEnumerable GetSaveFiles(IEnumerable extra) { var foldersToCheck = GetFoldersToCheck(extra); var result = GetSaveFilePathsFromFolders(foldersToCheck, out var possiblePaths);