|
@@ -5,23 +5,62 @@ namespace PicView.Core.Navigation;
|
|
|
/// <summary>
|
|
|
/// Manages the history of recently accessed files.
|
|
|
/// </summary>
|
|
|
-public class FileHistory
|
|
|
+public static class FileHistory
|
|
|
{
|
|
|
- private readonly List<string> _fileHistory;
|
|
|
- public const short MaxCount = 15;
|
|
|
- private readonly string _fileLocation;
|
|
|
+ private const int MaxHistoryEntries = 15;
|
|
|
+ private static readonly List<string> Entries = new(MaxHistoryEntries);
|
|
|
+ private static string? _fileLocation;
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Initializes a new instance of the <see cref="FileHistory"/> class.
|
|
|
+ /// Gets the number of entries in the file history
|
|
|
/// </summary>
|
|
|
- public FileHistory()
|
|
|
+ public static int Count => Entries.Count;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets all history entries
|
|
|
+ /// </summary>
|
|
|
+ public static IReadOnlyList<string> AllEntries => Entries.AsReadOnly();
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets the current index position in history
|
|
|
+ /// </summary>
|
|
|
+ public static int CurrentIndex
|
|
|
+ {
|
|
|
+ get;
|
|
|
+ private set => field = Math.Clamp(value, -1, Entries.Count - 1);
|
|
|
+ } = -1;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Indicates whether there is a previous entry available in history (older entry)
|
|
|
+ /// </summary>
|
|
|
+ public static bool HasPrevious => CurrentIndex > 0;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Indicates whether there is a next entry available in history (newer entry)
|
|
|
+ /// </summary>
|
|
|
+ public static bool HasNext => CurrentIndex < Entries.Count - 1 && Entries.Count > 0;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the current entry at the current index
|
|
|
+ /// </summary>
|
|
|
+ public static string? CurrentEntry => CurrentIndex >= 0 && CurrentIndex < Entries.Count ? Entries[CurrentIndex] : null;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Initializes the file history by loading entries from the history file
|
|
|
+ /// </summary>
|
|
|
+ public static void Initialize()
|
|
|
{
|
|
|
- _fileHistory ??= [];
|
|
|
_fileLocation = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config/recent.txt");
|
|
|
try
|
|
|
{
|
|
|
if (!File.Exists(_fileLocation))
|
|
|
{
|
|
|
+ var directory = Path.GetDirectoryName(_fileLocation);
|
|
|
+ if (directory != null && !Directory.Exists(directory))
|
|
|
+ {
|
|
|
+ Directory.CreateDirectory(directory);
|
|
|
+ }
|
|
|
+
|
|
|
using var fs = File.Create(_fileLocation);
|
|
|
fs.Seek(0, SeekOrigin.Begin);
|
|
|
}
|
|
@@ -42,102 +81,172 @@ public class FileHistory
|
|
|
Trace.WriteLine($"{nameof(FileHistory)} exception, \n{e.Message}");
|
|
|
#endif
|
|
|
}
|
|
|
- ReadFromFile();
|
|
|
+ LoadFromFile();
|
|
|
+ CurrentIndex = Entries.Count > 0 ? Entries.Count - 1 : -1; // Set to most recent entry
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Reads the file history from the .txt file.
|
|
|
+ /// Adds an entry to the history
|
|
|
/// </summary>
|
|
|
- /// <returns>An empty string if successful, otherwise an error message.</returns>
|
|
|
- public string ReadFromFile()
|
|
|
+ public static void Add(string path)
|
|
|
{
|
|
|
- _fileHistory.Clear();
|
|
|
- try
|
|
|
+ if (string.IsNullOrWhiteSpace(path))
|
|
|
+ return;
|
|
|
+
|
|
|
+ // Check if the entry already exists
|
|
|
+ var existingIndex = Entries.IndexOf(path);
|
|
|
+
|
|
|
+ if (existingIndex >= 0)
|
|
|
{
|
|
|
- using var reader = new StreamReader(_fileLocation);
|
|
|
- while (reader.Peek() >= 0)
|
|
|
- {
|
|
|
- _fileHistory.Add(reader.ReadLine());
|
|
|
- }
|
|
|
+ // If entry already exists, just update current index to point to it
|
|
|
+ CurrentIndex = existingIndex;
|
|
|
+ return;
|
|
|
}
|
|
|
- catch (Exception e)
|
|
|
+
|
|
|
+ // Trim the list if it will exceed the maximum size
|
|
|
+ if (Entries.Count >= MaxHistoryEntries)
|
|
|
{
|
|
|
-#if DEBUG
|
|
|
- Trace.WriteLine($"{nameof(FileHistory)}: {nameof(ReadFromFile)} exception,\n{e.Message}");
|
|
|
-#endif
|
|
|
- return e.Message;
|
|
|
+ // Remove oldest entry (at beginning)
|
|
|
+ Entries.RemoveAt(0);
|
|
|
+ // Adjust current index since we removed an item
|
|
|
+ if (CurrentIndex > 0)
|
|
|
+ {
|
|
|
+ CurrentIndex--;
|
|
|
+ }
|
|
|
}
|
|
|
- return string.Empty;
|
|
|
+
|
|
|
+ // Add to the end of the list (newest entry)
|
|
|
+ Entries.Add(path);
|
|
|
+
|
|
|
+ // Set the current index to the newly added item (last position)
|
|
|
+ CurrentIndex = Entries.Count - 1;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Writes the file history to the .txt file.
|
|
|
+ /// Gets the next entry in history (newer entry)
|
|
|
/// </summary>
|
|
|
- /// <returns>An empty string if successful, otherwise an error message.</returns>
|
|
|
- public string WriteToFile()
|
|
|
+ /// <returns>The next entry in history, or null if there is no next entry</returns>
|
|
|
+ public static string? GetNextEntry()
|
|
|
{
|
|
|
- try
|
|
|
- {
|
|
|
- using var writer = new StreamWriter(_fileLocation);
|
|
|
- foreach (var item in _fileHistory)
|
|
|
- {
|
|
|
- writer.WriteLine(item);
|
|
|
- }
|
|
|
- }
|
|
|
- catch (Exception e)
|
|
|
- {
|
|
|
- return e.Message;
|
|
|
- }
|
|
|
- return string.Empty;
|
|
|
+ if (!HasNext)
|
|
|
+ return null;
|
|
|
+
|
|
|
+ CurrentIndex++;
|
|
|
+ return CurrentEntry;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Adds a file to the history.
|
|
|
+ /// Gets the previous entry in history (older entry)
|
|
|
/// </summary>
|
|
|
- /// <param name="fileName">The name of the file to add to the history.</param>
|
|
|
- public void Add(string fileName)
|
|
|
+ /// <returns>The previous entry in history, or null if there is no previous entry</returns>
|
|
|
+ public static string? GetPreviousEntry()
|
|
|
{
|
|
|
- try
|
|
|
- {
|
|
|
- if (string.IsNullOrWhiteSpace(fileName) || _fileHistory.Exists(e => e is not null && e.EndsWith(fileName)))
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
+ if (!HasPrevious)
|
|
|
+ return null;
|
|
|
+
|
|
|
+ CurrentIndex--;
|
|
|
+ return CurrentEntry;
|
|
|
+ }
|
|
|
|
|
|
- if (_fileHistory.Count >= MaxCount)
|
|
|
- {
|
|
|
- _fileHistory.RemoveAt(0);
|
|
|
- }
|
|
|
+ /// <summary>
|
|
|
+ /// Gets an entry at the specified index
|
|
|
+ /// </summary>
|
|
|
+ public static string? GetEntry(int index)
|
|
|
+ {
|
|
|
+ if (index < 0 || index >= Entries.Count)
|
|
|
+ return null;
|
|
|
|
|
|
- _fileHistory.Add(fileName);
|
|
|
- }
|
|
|
- catch (Exception e)
|
|
|
- {
|
|
|
-#if DEBUG
|
|
|
- Trace.WriteLine($"{nameof(FileHistory)}: {nameof(Add)} exception,\n{e.Message}");
|
|
|
-#endif
|
|
|
- }
|
|
|
+ return Entries[index];
|
|
|
}
|
|
|
|
|
|
- public void Remove(string fileName)
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the first entry in history (oldest)
|
|
|
+ /// </summary>
|
|
|
+ public static string? GetFirstEntry() => Entries.Count > 0 ? Entries[0] : null;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the last entry in history (newest)
|
|
|
+ /// </summary>
|
|
|
+ public static string? GetLastEntry() => Entries.Count > 0 ? Entries[^1] : null;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Tries to find an entry that matches or contains the given string
|
|
|
+ /// </summary>
|
|
|
+ public static string? GetEntryByString(string searchString)
|
|
|
{
|
|
|
- try
|
|
|
+ if (string.IsNullOrWhiteSpace(searchString))
|
|
|
+ return null;
|
|
|
+
|
|
|
+ // First try exact match
|
|
|
+ var exactMatch = Entries.FirstOrDefault(e =>
|
|
|
+ string.Equals(e, searchString, StringComparison.OrdinalIgnoreCase));
|
|
|
+
|
|
|
+ if (exactMatch != null)
|
|
|
+ return exactMatch;
|
|
|
+
|
|
|
+ // Then try contains
|
|
|
+ return Entries.FirstOrDefault(e =>
|
|
|
+ e.Contains(searchString, StringComparison.OrdinalIgnoreCase));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Clears all history entries
|
|
|
+ /// </summary>
|
|
|
+ public static void Clear()
|
|
|
+ {
|
|
|
+ Entries.Clear();
|
|
|
+ CurrentIndex = -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Removes a specific entry from history
|
|
|
+ /// </summary>
|
|
|
+ public static bool Remove(string path)
|
|
|
+ {
|
|
|
+ var index = Entries.IndexOf(path);
|
|
|
+ if (index < 0)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ Entries.RemoveAt(index);
|
|
|
+
|
|
|
+ // Adjust current index if necessary
|
|
|
+ if (index <= CurrentIndex)
|
|
|
{
|
|
|
- if (string.IsNullOrWhiteSpace(fileName))
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
- _fileHistory.Remove(fileName);
|
|
|
+ CurrentIndex = Math.Max(-1, CurrentIndex - 1);
|
|
|
}
|
|
|
- catch (Exception e)
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Removes an entry at the specified index
|
|
|
+ /// </summary>
|
|
|
+ public static bool RemoveAt(int index)
|
|
|
+ {
|
|
|
+ if (index < 0 || index >= Entries.Count)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ Entries.RemoveAt(index);
|
|
|
+
|
|
|
+ // Adjust current index if necessary
|
|
|
+ if (index <= CurrentIndex)
|
|
|
{
|
|
|
-#if DEBUG
|
|
|
- Trace.WriteLine($"{nameof(FileHistory)}: {nameof(Remove)} exception,\n{e.Message}");
|
|
|
-#endif
|
|
|
+ CurrentIndex = Math.Max(-1, CurrentIndex - 1);
|
|
|
}
|
|
|
+
|
|
|
+ return true;
|
|
|
}
|
|
|
-
|
|
|
- public void Rename(string oldName, string newName)
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Renames a file in the history, replacing the old entry with the new one.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="oldName">The old name to be replaced.</param>
|
|
|
+ /// <param name="newName">The new name that will replace the old one.</param>
|
|
|
+ /// <remarks>
|
|
|
+ /// This method is case-insensitive and will replace the first entry that matches the old name.
|
|
|
+ /// If no matching entry is found, this method does nothing.
|
|
|
+ /// </remarks>
|
|
|
+ public static void Rename(string oldName, string newName)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
@@ -145,12 +254,15 @@ public class FileHistory
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
- var index = _fileHistory.IndexOf(oldName);
|
|
|
- if (index < 0)
|
|
|
+
|
|
|
+ var entry = GetEntryByString(oldName);
|
|
|
+ if (string.IsNullOrWhiteSpace(entry) || !Entries.Contains(entry))
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
- _fileHistory[index] = newName;
|
|
|
+
|
|
|
+ var index = Entries.IndexOf(entry);
|
|
|
+ Entries[index] = newName;
|
|
|
}
|
|
|
catch (Exception e)
|
|
|
{
|
|
@@ -159,96 +271,62 @@ public class FileHistory
|
|
|
#endif
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- public int GetCount() => _fileHistory.Count;
|
|
|
-
|
|
|
- public bool Contains(string name) => !string.IsNullOrWhiteSpace(name) && _fileHistory.Contains(name);
|
|
|
-
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Gets the last file in the history.
|
|
|
+ /// Saves the history to the history file
|
|
|
/// </summary>
|
|
|
- /// <returns>The last file entry or null if the history is empty.</returns>
|
|
|
- public string? GetLastFile() => _fileHistory.Count > 0 ? _fileHistory[^1] : null;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Gets the first file in the history.
|
|
|
- /// </summary>
|
|
|
- /// <returns>The first file entry or null if the history is empty.</returns>
|
|
|
- public string? GetFirstFile() => _fileHistory.Count > 0 ? _fileHistory[0] : null;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Gets the file entry at the specified index.
|
|
|
- /// </summary>
|
|
|
- /// <param name="index">The index of the file entry to retrieve.</param>
|
|
|
- /// <returns>The file entry at the specified index or null if the history is empty.</returns>
|
|
|
- public string? GetEntryAt(int index)
|
|
|
+ public static void SaveToFile()
|
|
|
{
|
|
|
- if (_fileHistory.Count == 0)
|
|
|
- {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- if (index < 0)
|
|
|
- {
|
|
|
- return _fileHistory[0];
|
|
|
- }
|
|
|
-
|
|
|
- return index >= _fileHistory.Count ? _fileHistory[^1] : _fileHistory[index];
|
|
|
- }
|
|
|
-
|
|
|
- public string? GetNextEntry(bool looping, int index)
|
|
|
- {
|
|
|
- if (_fileHistory.Count <= 0)
|
|
|
- {
|
|
|
- return GetLastFile();
|
|
|
- }
|
|
|
-
|
|
|
try
|
|
|
{
|
|
|
- var foundIndex = _fileHistory.IndexOf(_fileHistory[index]);
|
|
|
-
|
|
|
- if (looping)
|
|
|
+ if (_fileLocation == null)
|
|
|
+ return;
|
|
|
+
|
|
|
+ // Create directory if it doesn't exist
|
|
|
+ var directory = Path.GetDirectoryName(_fileLocation);
|
|
|
+ if (directory != null && !Directory.Exists(directory))
|
|
|
{
|
|
|
- return GetEntryAt((foundIndex + 1 + _fileHistory.Count) % _fileHistory.Count);
|
|
|
+ Directory.CreateDirectory(directory);
|
|
|
}
|
|
|
|
|
|
- foundIndex++;
|
|
|
- return foundIndex >= MaxCount ? null : GetEntryAt(foundIndex);
|
|
|
+ // Write all entries to file
|
|
|
+ File.WriteAllLines(_fileLocation, Entries);
|
|
|
}
|
|
|
- catch (Exception e)
|
|
|
+ catch (Exception ex)
|
|
|
{
|
|
|
#if DEBUG
|
|
|
- Trace.WriteLine($"{nameof(FileHistory)}: {nameof(GetNextEntry)} exception,\n{e.Message}");
|
|
|
+ // Log error but don't throw - this is not critical functionality
|
|
|
+ Debug.WriteLine($"Error saving file history: {ex.Message}");
|
|
|
#endif
|
|
|
- return null;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- public string? GetPreviousEntry(bool looping, int index)
|
|
|
- {
|
|
|
- if (_fileHistory.Count <= 0)
|
|
|
- {
|
|
|
- return GetFirstFile();
|
|
|
- }
|
|
|
|
|
|
- try
|
|
|
- {
|
|
|
- var foundIndex = _fileHistory.IndexOf(_fileHistory[index]);
|
|
|
- if (looping)
|
|
|
+ /// <summary>
|
|
|
+ /// Loads the history from the history file
|
|
|
+ /// </summary>
|
|
|
+ private static void LoadFromFile()
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ if (_fileLocation == null || !File.Exists(_fileLocation))
|
|
|
{
|
|
|
- return GetEntryAt((foundIndex - 1 + _fileHistory.Count) % _fileHistory.Count);
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- foundIndex--;
|
|
|
- return foundIndex < 0 ? null : GetEntryAt(foundIndex);
|
|
|
+ var lines = File.ReadAllLines(_fileLocation);
|
|
|
+ foreach (var line in lines)
|
|
|
+ {
|
|
|
+ if (!string.IsNullOrWhiteSpace(line) && Entries.Count < MaxHistoryEntries)
|
|
|
+ {
|
|
|
+ Entries.Add(line);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- catch (Exception e)
|
|
|
+ catch (Exception ex)
|
|
|
{
|
|
|
#if DEBUG
|
|
|
- Trace.WriteLine($"{nameof(FileHistory)}: {nameof(GetPreviousEntry)} exception,\n{e.Message}");
|
|
|
+ // Log error but don't throw - we can start with an empty history
|
|
|
+ Debug.WriteLine($"Error loading file history: {ex.Message}");
|
|
|
#endif
|
|
|
- return null;
|
|
|
}
|
|
|
}
|
|
|
}
|