[Feature] Refresh root folder on change of contents (#330, #399, #400), version 1.2.9.20

This commit is contained in:
Markus Hofknecht 2022-06-17 14:38:09 +02:00
parent bcb537d1ee
commit 7c253fa28a
13 changed files with 771 additions and 644 deletions

View file

@ -30,6 +30,8 @@ namespace SystemTrayMenu.Business
private readonly DgvMouseRow dgvMouseRow = new();
private readonly WaitToLoadMenu waitToOpenMenu = new();
private readonly KeyboardInput keyboardInput;
private readonly List<FileSystemWatcher> watchers = new();
private readonly List<FileSystemEventArgs> watcherHistory = new();
private readonly Timer timerShowProcessStartedAsLoadingIcon = new();
private readonly Timer timerStillActiveCheck = new();
private readonly WaitLeave waitLeave = new(Properties.Settings.Default.TimeUntilCloses);
@ -43,12 +45,12 @@ namespace SystemTrayMenu.Business
private int dragSwipeScrollingStartRowIndex = -1;
private bool isDraggingSwipeScrolling;
private bool isDragSwipeScrolled;
private bool hideSubmenuDuringRefreshSearch;
public Menus()
{
workerMainMenu.WorkerSupportsCancellation = true;
workerMainMenu.DoWork += LoadMenu;
workerMainMenu.RunWorkerCompleted += LoadMainMenuCompleted;
void LoadMainMenuCompleted(object sender, RunWorkerCompletedEventArgs e)
{
@ -90,8 +92,8 @@ namespace SystemTrayMenu.Business
{
workerMainMenu.DoWork -= LoadMenu;
menus[0] = Create(menuData, Path.GetFileName(Config.Path));
menus[0].HandleCreated += (s, e) => ExecuteWatcherHistory();
IconReader.MainPreload = false;
if (showMenuAfterMainPreload)
{
AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); });
@ -135,8 +137,8 @@ namespace SystemTrayMenu.Business
Config.SetFolderByUser();
AppRestart.ByConfigChange();
break;
case MenuDataValidity.AbortedOrUnknown:
Log.Info("MenuDataValidity.AbortedOrUnknown");
case MenuDataValidity.Undefined:
Log.Info($"{nameof(MenuDataValidity)}.{nameof(MenuDataValidity.Undefined)}");
break;
default:
break;
@ -162,23 +164,21 @@ namespace SystemTrayMenu.Business
void StartLoadMenu(RowData rowData)
{
if (menus[0].IsUsable &&
(menus[rowData.MenuLevel + 1] == null ||
menus[rowData.MenuLevel + 1].Tag as RowData != rowData))
(menus[rowData.Level + 1] == null ||
menus[rowData.Level + 1].Tag as RowData != rowData))
{
CreateAndShowLoadingMenu(rowData);
void CreateAndShowLoadingMenu(RowData rowData)
{
MenuData menuDataLoading = new()
MenuData menuDataLoading = new(rowData.Level + 1)
{
RowDatas = new List<RowData>(),
Validity = MenuDataValidity.Valid,
Level = rowData.MenuLevel + 1,
};
Menu menuLoading = Create(menuDataLoading, Path.GetFileName(rowData.TargetFilePathOrig));
Menu menuLoading = Create(menuDataLoading, Path.GetFileName(rowData.Path));
menuLoading.IsLoadingMenu = true;
AdjustMenusSizeAndLocation();
menus[rowData.MenuLevel + 1] = menuLoading;
menus[rowData.Level + 1] = menuLoading;
menuLoading.Tag = menuDataLoading.RowDataParent = rowData;
menuDataLoading.RowDataParent.SubMenu = menuLoading;
menuLoading.SetTypeLoading();
@ -216,7 +216,7 @@ namespace SystemTrayMenu.Business
closedLoadingMenu = true;
}
if (menuData.Validity != MenuDataValidity.AbortedOrUnknown &&
if (menuData.Validity != MenuDataValidity.Undefined &&
menus[0].IsUsable)
{
Menu menu = Create(menuData);
@ -270,20 +270,6 @@ namespace SystemTrayMenu.Business
}
waitToOpenMenu.MouseEnterOk += MouseEnterOk;
void MouseEnterOk(DataGridView dgv, int rowIndex)
{
if (menus[0].IsUsable)
{
if (keyboardInput.InUse)
{
keyboardInput.ClearIsSelectedByKey();
keyboardInput.InUse = false;
}
keyboardInput.Select(dgv, rowIndex, false);
}
}
dgvMouseRow.RowMouseEnter += waitToOpenMenu.MouseEnter;
dgvMouseRow.RowMouseLeave += waitToOpenMenu.MouseLeave;
dgvMouseRow.RowMouseLeave += Dgv_RowMouseLeave;
@ -316,6 +302,37 @@ namespace SystemTrayMenu.Business
}
waitLeave.LeaveTriggered += FadeHalfOrOutIfNeeded;
CreateWatcher(Config.Path, false);
foreach (var pathAndFlags in MenusHelpers.GetAddionalPathsForMainMenu())
{
CreateWatcher(pathAndFlags.Path, pathAndFlags.Recursive);
}
void CreateWatcher(string path, bool recursiv)
{
try
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = path;
watcher.NotifyFilter = NotifyFilters.Attributes |
NotifyFilters.DirectoryName |
NotifyFilters.FileName |
NotifyFilters.LastWrite;
watcher.Filter = "*.*";
watcher.Created += WatcherProcessItem;
watcher.Deleted += WatcherProcessItem;
watcher.Renamed += WatcherRenamed;
watcher.Changed += WatcherChanged;
watcher.IncludeSubdirectories = recursiv;
watcher.EnableRaisingEvents = true;
watchers.Add(watcher);
}
catch (Exception ex)
{
Log.Warn($"Failed to {nameof(CreateWatcher)}: {path}", ex);
}
}
}
internal event EventHandlerEmpty LoadStarted;
@ -349,237 +366,44 @@ namespace SystemTrayMenu.Business
IconReader.Dispose();
DisposeMenu(menus[0]);
dgvMouseRow.Dispose();
foreach (FileSystemWatcher watcher in watchers)
{
watcher.Created -= WatcherProcessItem;
watcher.Deleted -= WatcherProcessItem;
watcher.Renamed -= WatcherRenamed;
watcher.Changed -= WatcherChanged;
watcher.Dispose();
}
}
internal static MenuData GetData(BackgroundWorker worker, string path, int level)
{
MenuData menuData = new()
MenuData menuData = new(level);
if (worker?.CancellationPending == true || string.IsNullOrEmpty(path))
{
RowDatas = new List<RowData>(),
Validity = MenuDataValidity.AbortedOrUnknown,
Level = level,
};
string[] directoriesToAddToMainMenu = Array.Empty<string>();
string[] filesToAddToMainMenu = Array.Empty<string>();
if (level == 0)
{
AddFoldersToMainMenu(ref directoriesToAddToMainMenu, ref filesToAddToMainMenu);
return menuData;
}
if (!worker.CancellationPending)
MenusHelpers.GetItemsForMainMenu(worker, path, ref menuData);
if (worker?.CancellationPending == true)
{
string[] directories = Array.Empty<string>();
bool isSharedDirectory = false;
try
{
if (string.IsNullOrEmpty(path))
{
Log.Info($"path is null or empty");
}
else if (FileLnk.IsNetworkRoot(path))
{
isSharedDirectory = true;
directories = GetDirectoriesInNetworkLocation(path);
static string[] GetDirectoriesInNetworkLocation(string networkLocationRootPath)
{
List<string> directories = new();
Process cmd = new();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = true;
cmd.StartInfo.UseShellExecute = false;
cmd.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
cmd.Start();
cmd.StandardInput.WriteLine($"net view {networkLocationRootPath}");
cmd.StandardInput.Flush();
cmd.StandardInput.Close();
string output = cmd.StandardOutput.ReadToEnd();
cmd.WaitForExit();
cmd.Close();
bool resolvedSomething = false;
List<string> lines = output
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).ToList();
if (lines.Count > 8)
{
foreach (string line in lines.Skip(6).SkipLast(2))
{
int indexOfFirstSpace = line.IndexOf(" ", StringComparison.InvariantCulture);
if (indexOfFirstSpace > 0)
{
string directory = Path.Combine(
networkLocationRootPath,
line[..indexOfFirstSpace]);
directories.Add(directory);
resolvedSomething = true;
}
}
}
if (!resolvedSomething)
{
Log.Info($"Could not resolve network root folder: {networkLocationRootPath} , output:{output}");
}
return directories.ToArray();
}
}
else
{
directories = Directory.GetDirectories(path);
directories = directories.Concat(directoriesToAddToMainMenu).ToArray();
}
if (Properties.Settings.Default.SortByTypeAndNameWindowsExplorerSort)
{
Array.Sort(directories, new WindowsExplorerSort());
}
}
catch (UnauthorizedAccessException ex)
{
Log.Warn($"path:'{path}'", ex);
menuData.Validity = MenuDataValidity.NoAccess;
}
catch (Exception ex)
{
Log.Warn($"path:'{path}'", ex);
}
foreach (string directoryWithIllegalCharacters in directories)
{
if (worker != null && worker.CancellationPending)
{
break;
}
// https://github.com/Hofknecht/SystemTrayMenu/issues/171
string directory = directoryWithIllegalCharacters.Replace("\x00", string.Empty);
bool hiddenEntry = false;
if (!isSharedDirectory &&
FolderOptions.IsHidden(directory, ref hiddenEntry))
{
continue;
}
RowData rowData = ReadRowData(directory, false, true);
rowData.HiddenEntry = hiddenEntry;
string resolvedLnkPath = string.Empty;
rowData.ReadIconOrResolveLinkAndReadIcon(true, ref resolvedLnkPath, level);
rowData.MenuLevel = level;
menuData.RowDatas.Add(rowData);
}
if (Properties.Settings.Default.SortByTypeAndDate)
{
menuData.RowDatas = menuData.RowDatas.OrderBy(x => x.FileInfo.LastWriteTime).Reverse().ToList();
}
return menuData;
}
if (!worker.CancellationPending)
MenusHelpers.GetAddionalItemsForMainMenu(ref menuData);
if (worker?.CancellationPending == true)
{
string[] files = Array.Empty<string>();
List<RowData> rowDatasFiles = new List<RowData>();
try
{
if (string.IsNullOrEmpty(path))
{
Log.Info($"path is null or empty");
}
else if (!FileLnk.IsNetworkRoot(path))
{
files = DirectoryBySearchPattern.GetFiles(path, Config.SearchPattern);
files = files.Concat(filesToAddToMainMenu).ToArray();
}
if (Properties.Settings.Default.SortByTypeAndNameWindowsExplorerSort)
{
Array.Sort(files, new WindowsExplorerSort());
}
}
catch (UnauthorizedAccessException ex)
{
Log.Warn($"path:'{path}'", ex);
menuData.Validity = MenuDataValidity.NoAccess;
}
catch (Exception ex)
{
Log.Warn($"path:'{path}'", ex);
}
foreach (string fileWithIllegalCharacters in files)
{
if (worker != null && worker.CancellationPending)
{
break;
}
// https://github.com/Hofknecht/SystemTrayMenu/issues/171
string file = fileWithIllegalCharacters.Replace("\x00", string.Empty);
bool hiddenEntry = false;
if (FolderOptions.IsHidden(file, ref hiddenEntry))
{
continue;
}
RowData rowData = ReadRowData(file, false, false);
rowData.HiddenEntry = hiddenEntry;
if (Properties.Settings.Default.ShowOnlyAsSearchResult)
{
rowData.ShowOnlyWhenSearch = filesToAddToMainMenu.Contains(fileWithIllegalCharacters);
}
string resolvedLnkPath = string.Empty;
if (rowData.ReadIconOrResolveLinkAndReadIcon(false, ref resolvedLnkPath, level))
{
rowData = ReadRowData(resolvedLnkPath, true, true, rowData);
rowData.HiddenEntry = hiddenEntry;
}
rowDatasFiles.Add(rowData);
}
if (Properties.Settings.Default.SortByTypeAndDate)
{
rowDatasFiles = rowDatasFiles.OrderBy(x => x.FileInfo.LastWriteTime).Reverse().ToList();
}
menuData.RowDatas = menuData.RowDatas.Concat(rowDatasFiles).ToList();
return menuData;
}
if (!worker.CancellationPending)
MenusHelpers.ReadHiddenAndReadIcons(worker, ref menuData);
if (worker?.CancellationPending == true)
{
if (menuData.Validity == MenuDataValidity.AbortedOrUnknown)
{
if (menuData.RowDatas.Count == 0)
{
menuData.Validity = MenuDataValidity.Empty;
}
else
{
if (Properties.Settings.Default.SortByName)
{
menuData.RowDatas = menuData.RowDatas.OrderBy(x => x.Text).ToList();
}
else if (Properties.Settings.Default.SortByDate)
{
menuData.RowDatas = menuData.RowDatas.OrderBy(x => x.FileInfo.LastWriteTime).Reverse().ToList();
}
menuData.Validity = MenuDataValidity.Valid;
}
}
return menuData;
}
MenusHelpers.CheckIfValid(ref menuData);
MenusHelpers.SortItemsWhenValid(ref menuData);
return menuData;
}
@ -726,8 +550,8 @@ namespace SystemTrayMenu.Business
RowData rowData = eDoWork.Argument as RowData;
if (rowData != null)
{
path = rowData.TargetFilePath;
level = rowData.MenuLevel + 1;
path = rowData.ResolvedPath;
level = rowData.Level + 1;
}
if (Properties.Settings.Default.GenerateShortcutsToDrives)
@ -740,120 +564,6 @@ namespace SystemTrayMenu.Business
eDoWork.Result = menuData;
}
private static void AddFoldersToMainMenu(ref string[] directoriesToAddToMainMenu, ref string[] filesToAddToMainMenu)
{
string pathAddToMainMenu = string.Empty;
bool recursive = false;
try
{
foreach (string pathAndRecursivString in Properties.Settings.Default.PathsAddToMainMenu.Split(@"|"))
{
if (string.IsNullOrEmpty(pathAndRecursivString))
{
continue;
}
pathAddToMainMenu = pathAndRecursivString.Split("recursiv:")[0].Trim();
recursive = pathAndRecursivString.Split("recursiv:")[1].StartsWith("True");
bool onlyFiles = pathAndRecursivString.Split("onlyFiles:")[1].StartsWith("True");
string[] directoriesToConcat = Array.Empty<string>();
string[] filesToAddToConcat = Array.Empty<string>();
if (recursive)
{
GetDirectoriesAndFilesRecursive(ref directoriesToConcat, ref filesToAddToConcat, pathAddToMainMenu);
}
else
{
directoriesToConcat = Directory.GetDirectories(pathAddToMainMenu);
filesToAddToConcat = DirectoryBySearchPattern.GetFiles(pathAddToMainMenu, Config.SearchPattern);
}
if (!onlyFiles)
{
directoriesToAddToMainMenu = directoriesToAddToMainMenu.Concat(directoriesToConcat).ToArray();
}
filesToAddToMainMenu = filesToAddToMainMenu.Concat(filesToAddToConcat).ToArray();
}
}
catch (Exception ex)
{
Log.Warn($"path:'{pathAddToMainMenu}' recursiv:{recursive}", ex);
}
}
private static void GetDirectoriesAndFilesRecursive(ref string[] directoriesToConcat, ref string[] filesToAddToConcat, string pathAddToMainMenu)
{
try
{
string[] directories = Directory.GetDirectories(pathAddToMainMenu);
try
{
filesToAddToConcat = filesToAddToConcat.Concat(DirectoryBySearchPattern.GetFiles(pathAddToMainMenu, Config.SearchPattern)).ToArray();
}
catch (Exception ex)
{
Log.Warn($"GetDirectoriesAndFilesRecursive path:'{pathAddToMainMenu}'", ex);
}
foreach (string directory in directories)
{
GetDirectoriesAndFilesRecursive(ref directoriesToConcat, ref filesToAddToConcat, directory);
}
directoriesToConcat = directoriesToConcat.Concat(directories).ToArray();
}
catch (Exception ex)
{
Log.Warn($"GetDirectoriesAndFilesRecursive path:'{pathAddToMainMenu}'", ex);
}
}
private static RowData ReadRowData(string fileName, bool isResolvedLnk, bool containsMenu, RowData rowData = null)
{
if (rowData == null)
{
rowData = new RowData();
}
rowData.ContainsMenu = containsMenu;
rowData.IsResolvedLnk = isResolvedLnk;
try
{
FileInfo fileInfo = new FileInfo(fileName);
rowData.TargetFilePath = fileInfo.FullName;
if (!isResolvedLnk)
{
rowData.FileInfo = fileInfo;
if (string.IsNullOrEmpty(fileInfo.Name))
{
string path = fileInfo.FullName;
int directoryNameBegin = path.LastIndexOf(@"\", StringComparison.InvariantCulture) + 1;
rowData.SetText(path[directoryNameBegin..]);
}
else if (!rowData.ContainsMenu && Config.IsHideFileExtension())
{
rowData.SetText(Path.GetFileNameWithoutExtension(fileInfo.Name));
}
else
{
rowData.SetText(fileInfo.Name);
}
rowData.TargetFilePathOrig = fileInfo.FullName;
}
}
catch (Exception ex)
{
Log.Warn($"fileName:'{fileName}'", ex);
}
return rowData;
}
private static void OpenFolder(string pathToFolder = "")
{
string path = pathToFolder;
@ -879,6 +589,52 @@ namespace SystemTrayMenu.Business
}
}
private static void AddItemsToMenu(List<RowData> data, Menu menu, out int foldersCount, out int filesCount)
{
foldersCount = 0;
filesCount = 0;
DataGridView dgv = menu.GetDataGridView();
DataTable dataTable = new();
dataTable.Columns.Add(dgv.Columns[0].Name, typeof(Icon));
dataTable.Columns.Add(dgv.Columns[1].Name, typeof(string));
dataTable.Columns.Add("data", typeof(RowData));
dataTable.Columns.Add("SortIndex");
foreach (RowData rowData in data)
{
if (!(rowData.IsAddionalItem && Properties.Settings.Default.ShowOnlyAsSearchResult))
{
if (rowData.ContainsMenu)
{
foldersCount++;
}
else
{
filesCount++;
}
}
rowData.SetData(rowData, dataTable);
}
dgv.DataSource = dataTable;
dgv.Columns["data"].Visible = false;
dgv.Columns["SortIndex"].Visible = false;
string columnSortIndex = "SortIndex";
foreach (DataRow row in dataTable.Rows)
{
RowData rowData = (RowData)row[2];
if (rowData.IsAddionalItem && Properties.Settings.Default.ShowOnlyAsSearchResult)
{
row[columnSortIndex] = 99;
}
else
{
row[columnSortIndex] = 0;
}
}
}
private bool IsActive()
{
bool IsShellContextMenuOpen()
@ -916,8 +672,8 @@ namespace SystemTrayMenu.Business
string path = Config.Path;
if (title == null)
{
title = Path.GetFileName(menuData.RowDataParent.TargetFilePath);
path = menuData.RowDataParent.TargetFilePath;
title = Path.GetFileName(menuData.RowDataParent.ResolvedPath);
path = menuData.RowDataParent.ResolvedPath;
}
if (string.IsNullOrEmpty(title))
@ -975,51 +731,6 @@ namespace SystemTrayMenu.Business
menu.VisibleChanged += MenuVisibleChanged;
AddItemsToMenu(menuData.RowDatas, menu, out int foldersCount, out int filesCount);
static void AddItemsToMenu(List<RowData> data, Menu menu, out int foldersCount, out int filesCount)
{
foldersCount = 0;
filesCount = 0;
DataGridView dgv = menu.GetDataGridView();
DataTable dataTable = new();
dataTable.Columns.Add(dgv.Columns[0].Name, typeof(Icon));
dataTable.Columns.Add(dgv.Columns[1].Name, typeof(string));
dataTable.Columns.Add("data", typeof(RowData));
dataTable.Columns.Add("SortIndex");
foreach (RowData rowData in data)
{
if (!rowData.ShowOnlyWhenSearch)
{
if (rowData.ContainsMenu)
{
foldersCount++;
}
else
{
filesCount++;
}
}
rowData.SetData(rowData, dataTable);
}
dgv.DataSource = dataTable;
dgv.Columns["data"].Visible = false;
dgv.Columns["SortIndex"].Visible = false;
string columnSortIndex = "SortIndex";
foreach (DataRow row in dataTable.Rows)
{
RowData rowData = (RowData)row[2];
if (rowData.ShowOnlyWhenSearch)
{
row[columnSortIndex] = 99;
}
else
{
row[columnSortIndex] = 0;
}
}
}
DataGridView dgv = menu.GetDataGridView();
dgv.CellMouseEnter += dgvMouseRow.CellMouseEnter;
@ -1137,6 +848,7 @@ namespace SystemTrayMenu.Business
if (hitTestInfo.RowIndex > -1 &&
hitTestInfo.RowIndex < dgv.Rows.Count)
{
MouseEnterOk(dgv, hitTestInfo.RowIndex, true);
RowData rowData = (RowData)dgv.Rows[hitTestInfo.RowIndex].Cells[2].Value;
rowData.MouseDown(dgv, e);
InvalidateRowIfIndexInRange(dgv, hitTestInfo.RowIndex);
@ -1178,6 +890,25 @@ namespace SystemTrayMenu.Business
isDragSwipeScrolled = false;
}
private void MouseEnterOk(DataGridView dgv, int rowIndex)
{
MouseEnterOk(dgv, rowIndex, false);
}
private void MouseEnterOk(DataGridView dgv, int rowIndex, bool refreshView)
{
if (menus[0].IsUsable)
{
if (keyboardInput.InUse)
{
keyboardInput.ClearIsSelectedByKey();
keyboardInput.InUse = false;
}
keyboardInput.Select(dgv, rowIndex, refreshView);
}
}
private void Dgv_RowMouseLeave(object sender, DataGridViewCellEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
@ -1189,7 +920,7 @@ namespace SystemTrayMenu.Business
{
lastMouseDownRowIndex = -1;
RowData rowData = (RowData)dgv.Rows[e.RowIndex].Cells[2].Value;
string[] files = new string[] { rowData.TargetFilePathOrig };
string[] files = new string[] { rowData.Path };
// Update position raises move event which prevent DoDragDrop blocking UI when mouse not moved
Cursor.Position = new Point(Cursor.Position.X, Cursor.Position.Y);
@ -1332,7 +1063,7 @@ namespace SystemTrayMenu.Business
{
timerShowProcessStartedAsLoadingIcon.Tick -= Tick;
timerShowProcessStartedAsLoadingIcon.Stop();
row.Cells[0].Value = rowData.ReadLoadedIcon();
row.Cells[0].Value = rowData.ReadIcon(false);
waitingForReactivate = false;
}
@ -1538,6 +1269,7 @@ namespace SystemTrayMenu.Business
{
searchTextChanging = true;
keyboardInput.SearchTextChanging();
waitToOpenMenu.MouseActive = false;
}
private void Menu_SearchTextChanged(object sender, bool isSearchStringEmpty)
@ -1551,11 +1283,126 @@ namespace SystemTrayMenu.Business
if (menu.Level + 1 < menus.Length)
{
Menu menuToClose = menus[menu.Level + 1];
if (menuToClose != null)
if (menuToClose != null && hideSubmenuDuringRefreshSearch)
{
HideOldMenu(menuToClose);
}
}
}
private void ExecuteWatcherHistory()
{
foreach (var fileSystemEventArgs in watcherHistory)
{
WatcherProcessItem(watchers, fileSystemEventArgs);
}
watcherHistory.Clear();
}
private void WatcherRenamed(object sender, RenamedEventArgs e)
{
WatcherProcessItem(sender, new FileSystemEventArgs(WatcherChangeTypes.Deleted, Path.GetDirectoryName(e.OldFullPath), e.OldName));
WatcherProcessItem(sender, new FileSystemEventArgs(WatcherChangeTypes.Created, Path.GetDirectoryName(e.FullPath), e.Name));
}
private void WatcherChanged(object sender, FileSystemEventArgs e)
{
WatcherProcessItem(sender, new FileSystemEventArgs(WatcherChangeTypes.Deleted, Path.GetDirectoryName(e.FullPath), e.Name));
WatcherProcessItem(sender, new FileSystemEventArgs(WatcherChangeTypes.Created, Path.GetDirectoryName(e.FullPath), e.Name));
}
private void WatcherProcessItem(object sender, FileSystemEventArgs e)
{
if (menus[0] == null || !menus[0].IsHandleCreated)
{
watcherHistory.Add(e);
return;
}
if (e.ChangeType == WatcherChangeTypes.Deleted)
{
menus[0].Invoke(() => DeleteItem(e));
}
else if (e.ChangeType == WatcherChangeTypes.Created)
{
menus[0].Invoke(() => CreateItem(e));
}
}
private void DeleteItem(FileSystemEventArgs e)
{
try
{
DataRow rowToRemove = null;
DataGridView dgv = menus[0].GetDataGridView();
DataTable dataTable = (DataTable)dgv.DataSource;
foreach (DataRow row in dataTable.Rows)
{
RowData rowData = (RowData)row[2];
if (rowData.Path == e.FullPath)
{
IconReader.RemoveIconFromCache(rowData.Path);
rowToRemove = row;
}
}
if (rowToRemove != null)
{
dataTable.Rows.Remove(rowToRemove);
}
keyboardInput.ClearIsSelectedByKey();
dgv.DataSource = dataTable;
hideSubmenuDuringRefreshSearch = false;
menus[0].RefreshSearchText();
hideSubmenuDuringRefreshSearch = true;
}
catch (Exception ex)
{
Log.Warn($"Failed to {nameof(DeleteItem)}: {e.FullPath}", ex);
}
}
private void CreateItem(FileSystemEventArgs e)
{
try
{
FileAttributes attr = File.GetAttributes(e.FullPath);
bool isFolder = (attr & FileAttributes.Directory) == FileAttributes.Directory;
bool isAddionalItem = Path.GetDirectoryName(e.FullPath) != Config.Path;
RowData rowData = new(isFolder, isAddionalItem, false, 0, e.FullPath);
if (FolderOptions.IsHidden(rowData))
{
return;
}
rowData.ReadIcon(true);
List<RowData> rowDatas = new();
rowDatas.Add(rowData);
DataTable dataTable = (DataTable)menus[0].GetDataGridView().DataSource;
foreach (DataRow row in dataTable.Rows)
{
rowDatas.Add((RowData)row[2]);
}
rowDatas = MenusHelpers.SortItems(rowDatas);
keyboardInput.ClearIsSelectedByKey();
AddItemsToMenu(rowDatas, menus[0], out _, out _);
hideSubmenuDuringRefreshSearch = false;
menus[0].RefreshSearchText();
hideSubmenuDuringRefreshSearch = true;
menus[0].TimerUpdateIconsStart();
}
catch (Exception ex)
{
Log.Warn($"Failed to {nameof(CreateItem)}: {e.FullPath}", ex);
}
}
}
}

265
Business/MenusHelpers.cs Normal file
View file

@ -0,0 +1,265 @@
// <copyright file="MenusHelpers.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace SystemTrayMenu.Business
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using SystemTrayMenu.DataClasses;
using SystemTrayMenu.Helper;
using SystemTrayMenu.Utilities;
internal static class MenusHelpers
{
internal static void GetItemsForMainMenu(BackgroundWorker worker, string path, ref MenuData menuData)
{
menuData.IsNetworkRoot = FileLnk.IsNetworkRoot(path);
if (menuData.IsNetworkRoot)
{
GetNetworkRootDirectories(path, ref menuData);
}
else
{
GetDirectories(worker, path, ref menuData);
GetFiles(worker, path, ref menuData);
}
}
internal static void GetAddionalItemsForMainMenu(ref MenuData menuData)
{
if (menuData.Level != 0)
{
return;
}
foreach (var path in GetAddionalPathsForMainMenu())
{
GetDirectoriesAndFilesRecursive(ref menuData, path.Path, path.OnlyFiles, path.Recursive);
}
}
internal static IEnumerable<(string Path, bool Recursive, bool OnlyFiles)> GetAddionalPathsForMainMenu()
{
foreach (string pathAndRecursivString in Properties.Settings.Default.PathsAddToMainMenu.Split(@"|"))
{
if (string.IsNullOrEmpty(pathAndRecursivString))
{
continue;
}
string pathAddForMainMenu = pathAndRecursivString.Split("recursiv:")[0].Trim();
bool recursive = pathAndRecursivString.Split("recursiv:")[1].StartsWith("True");
bool onlyFiles = pathAndRecursivString.Split("onlyFiles:")[1].StartsWith("True");
yield return (Path: pathAddForMainMenu, Recursive: recursive, OnlyFiles: onlyFiles);
}
}
internal static void ReadHiddenAndReadIcons(BackgroundWorker worker, ref MenuData menuData)
{
List<RowData> rowDatasToRemove = new();
foreach (RowData rowData in menuData.RowDatas)
{
if (worker?.CancellationPending == true)
{
return;
}
if (!menuData.IsNetworkRoot && FolderOptions.IsHidden(rowData))
{
rowDatasToRemove.Add(rowData);
continue;
}
rowData.ReadIcon(true);
}
menuData.RowDatas = menuData.RowDatas.Except(rowDatasToRemove).ToList();
}
internal static void CheckIfValid(ref MenuData menuData)
{
if (menuData.Validity == MenuDataValidity.Undefined)
{
if (menuData.RowDatas.Count == 0)
{
menuData.Validity = MenuDataValidity.Empty;
}
else
{
menuData.Validity = MenuDataValidity.Valid;
}
}
}
internal static void SortItemsWhenValid(ref MenuData menuData)
{
if (menuData.Validity != MenuDataValidity.Valid)
{
return;
}
menuData.RowDatas = SortItems(menuData.RowDatas);
}
internal static List<RowData> SortItems(List<RowData> rowDatas)
{
if (Properties.Settings.Default.SortByTypeAndNameWindowsExplorerSort)
{
rowDatas = rowDatas.OrderByDescending(x => x.IsFolder)
.ThenBy(x => x.Text, new WindowsExplorerSort()).ToList();
}
else if (Properties.Settings.Default.SortByTypeAndDate)
{
rowDatas = rowDatas.OrderByDescending(x => x.IsFolder)
.ThenByDescending(x => x.FileInfo.LastWriteTime).ToList();
}
else if (Properties.Settings.Default.SortByName)
{
rowDatas = rowDatas.OrderBy(x => x.Text).ToList();
}
else if (Properties.Settings.Default.SortByDate)
{
rowDatas = rowDatas.OrderByDescending(x => x.FileInfo.LastWriteTime).ToList();
}
return rowDatas;
}
private static void GetNetworkRootDirectories(string path, ref MenuData menuData)
{
Process cmd = new();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = true;
cmd.StartInfo.UseShellExecute = false;
cmd.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
try
{
bool resolvedSomething = false;
cmd.Start();
cmd.StandardInput.WriteLine($"net view {path}");
cmd.StandardInput.Flush();
cmd.StandardInput.Close();
string output = cmd.StandardOutput.ReadToEnd();
cmd.WaitForExit();
cmd.Close();
List<string> lines = output
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).ToList();
if (lines.Count > 8)
{
foreach (string line in lines.Skip(6).SkipLast(2))
{
int indexOfFirstSpace = line.IndexOf(" ", StringComparison.InvariantCulture);
if (indexOfFirstSpace > 0)
{
string directory = Path.Combine(path, line[..indexOfFirstSpace]);
menuData.RowDatas.Add(new RowData(true, false, true, menuData.Level, directory));
resolvedSomething = true;
}
}
}
if (!resolvedSomething)
{
Log.Info($"Could not resolve network root folder: {path} , output:{output}");
}
}
catch (Exception ex)
{
Log.Warn($"path:'{path}'", ex);
if (ex is UnauthorizedAccessException)
{
menuData.Validity = MenuDataValidity.NoAccess;
}
}
}
private static void GetDirectories(BackgroundWorker worker, string path, ref MenuData menuData)
{
try
{
foreach (var directory in Directory.GetDirectories(path))
{
if (worker?.CancellationPending == true)
{
return;
}
menuData.RowDatas.Add(new RowData(true, false, false, menuData.Level, directory));
}
}
catch (Exception ex)
{
Log.Warn($"path:'{path}'", ex);
if (ex is UnauthorizedAccessException)
{
menuData.Validity = MenuDataValidity.NoAccess;
}
}
}
private static void GetFiles(BackgroundWorker worker, string path, ref MenuData menuData)
{
try
{
foreach (string file in DirectoryBySearchPattern.GetFiles(path, Config.SearchPattern))
{
if (worker?.CancellationPending == true)
{
return;
}
menuData.RowDatas.Add(new RowData(false, false, false, menuData.Level, file));
}
}
catch (Exception ex)
{
Log.Warn($"path:'{path}'", ex);
if (ex is UnauthorizedAccessException)
{
menuData.Validity = MenuDataValidity.NoAccess;
}
}
}
private static void GetDirectoriesAndFilesRecursive(
ref MenuData menuData,
string path,
bool onlyFiles,
bool recursiv)
{
try
{
foreach (string file in DirectoryBySearchPattern.GetFiles(path, Config.SearchPattern))
{
menuData.RowDatas.Add(new RowData(false, true, false, menuData.Level, file));
}
foreach (string directory in Directory.GetDirectories(path))
{
if (!onlyFiles)
{
menuData.RowDatas.Add(new RowData(true, true, false, menuData.Level, directory));
}
if (recursiv)
{
GetDirectoriesAndFilesRecursive(ref menuData, directory, onlyFiles, recursiv);
}
}
}
catch (Exception ex)
{
Log.Warn($"GetDirectoriesAndFilesRecursive path:'{path}'", ex);
}
}
}
}

View file

@ -168,17 +168,17 @@ namespace SystemTrayMenu.Handler
{
RowData rowData = (RowData)dgv.Rows[rowIndex].Cells[2].Value;
Menu menu = (Menu)dgv.FindForm();
rowData.MenuLevel = menu.Level;
rowData.Level = menu.Level;
if (rowData.ContainsMenu)
{
CloseMenu.Invoke(rowData.MenuLevel + 2);
CloseMenu.Invoke(rowData.Level + 2);
}
CloseMenu.Invoke(rowData.MenuLevel + 1);
CloseMenu.Invoke(rowData.Level + 1);
if (!rowData.IsContextMenuOpen &&
rowData.ContainsMenu &&
rowData.MenuLevel + 1 < MenuDefines.MenusMax)
rowData.Level + 1 < MenuDefines.MenusMax)
{
StartLoadMenu.Invoke(rowData);
}

View file

@ -14,12 +14,12 @@ namespace SystemTrayMenu
using SystemTrayMenu.Properties;
using SystemTrayMenu.UserInterface.FolderBrowseDialog;
using SystemTrayMenu.Utilities;
using static SystemTrayMenu.Utilities.IconReader;
public static class Config
{
private static readonly Icon SystemTrayMenu = Properties.Resources.SystemTrayMenu;
private static readonly Icon IconFromRootFolder = IconReader.GetIconSTA(
Path, Path, false, IconReader.IconSize.Small, IconReader.FolderType.Closed);
private static readonly Icon IconRootFolder = GetIconSTA(Path, Path, false, IconSize.Small, true);
private static bool readDarkModeDone;
private static bool isDarkMode;
@ -71,7 +71,7 @@ namespace SystemTrayMenu
{
if (Settings.Default.UseIconFromRootFolder)
{
return IconFromRootFolder;
return IconRootFolder;
}
else
{
@ -174,7 +174,7 @@ namespace SystemTrayMenu
/// <summary>
/// Read the OS setting whether HideFileExt enabled.
/// </summary>
/// <returns>true = Dark mode; false = Light mode.</returns>
/// <returns>isHideFileExtension.</returns>
internal static bool IsHideFileExtension()
{
if (!readHideFileExtdone)

View file

@ -8,7 +8,7 @@ namespace SystemTrayMenu.DataClasses
internal enum MenuDataValidity
{
AbortedOrUnknown,
Undefined,
Valid,
Empty,
NoAccess,
@ -16,9 +16,23 @@ namespace SystemTrayMenu.DataClasses
internal struct MenuData
{
internal List<RowData> RowDatas;
internal MenuDataValidity Validity;
internal int Level;
internal RowData RowDataParent;
public MenuData(int level)
{
RowDatas = new List<RowData>();
Validity = MenuDataValidity.Undefined;
Level = level;
RowDataParent = null;
IsNetworkRoot = false;
}
internal List<RowData> RowDatas { get; set; }
internal MenuDataValidity Validity { get; set; }
internal int Level { get; }
internal RowData RowDataParent { get; set; }
internal bool IsNetworkRoot { get; set; }
}
}

View file

@ -10,22 +10,116 @@ namespace SystemTrayMenu.DataClasses
using System.IO;
using System.Windows.Forms;
using SystemTrayMenu.Utilities;
using static SystemTrayMenu.Utilities.IconReader;
using Menu = SystemTrayMenu.UserInterface.Menu;
internal class RowData
{
private static readonly Icon White50PercentageIcon = Properties.Resources.White50Percentage;
private static readonly Icon NotFoundIcon = Properties.Resources.NotFound;
private static DateTime contextMenuClosed;
private Icon icon;
/// <summary>
/// Initializes a new instance of the <see cref="RowData"/> class.
/// empty dummy.
/// </summary>
internal RowData()
{
}
internal string Text { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="RowData"/> class.
/// (Related replace "\x00" see #171.)
/// </summary>
/// <param name="isFolder">Flag if file or folder.</param>
/// <param name="isAddionalItem">Flag if addional item, from other folder than root folder.</param>
/// <param name="isNetworkRoot">Flag if resolved from network root folder.</param>
/// <param name="level">The number of the menu level.</param>
/// <param name="path">Path to item.</param>
internal RowData(bool isFolder, bool isAddionalItem, bool isNetworkRoot, int level, string path)
{
IsFolder = isFolder;
IsAddionalItem = isAddionalItem;
IsNetworkRoot = isNetworkRoot;
Level = level;
internal FileInfo FileInfo { get; set; }
try
{
FileInfo = new FileInfo(path.Replace("\x00", string.Empty));
Path = FileInfo.FullName;
FileExtension = System.IO.Path.GetExtension(Path);
IsLink = FileExtension.Equals(".lnk", StringComparison.InvariantCultureIgnoreCase);
if (IsLink)
{
ResolvedPath = FileLnk.GetResolvedFileName(Path, out bool isLinkToFolder);
IsLinkToFolder = isLinkToFolder || FileLnk.IsNetworkRoot(ResolvedPath);
ShowOverlay = Properties.Settings.Default.ShowLinkOverlay;
Text = System.IO.Path.GetFileNameWithoutExtension(Path);
if (string.IsNullOrEmpty(ResolvedPath))
{
Log.Info($"Resolved path is empty: '{Path}'");
ResolvedPath = Path;
}
}
else
{
ResolvedPath = Path;
if (string.IsNullOrEmpty(FileInfo.Name))
{
int nameBegin = FileInfo.FullName.LastIndexOf(@"\", StringComparison.InvariantCulture) + 1;
Text = FileInfo.FullName[nameBegin..];
}
else if (FileExtension.Equals(".url", StringComparison.InvariantCultureIgnoreCase) ||
FileExtension.Equals(".appref-ms", StringComparison.InvariantCultureIgnoreCase))
{
ShowOverlay = Properties.Settings.Default.ShowLinkOverlay;
Text = System.IO.Path.GetFileNameWithoutExtension(FileInfo.Name);
}
else if (!IsFolder && Config.IsHideFileExtension())
{
Text = System.IO.Path.GetFileNameWithoutExtension(FileInfo.Name);
}
else
{
Text = FileInfo.Name;
}
}
ContainsMenu = IsFolder || IsLinkToFolder;
IsMainMenu = Level == 0;
}
catch (Exception ex)
{
Log.Warn($"path:'{path}'", ex);
}
}
internal FileInfo FileInfo { get; }
internal string Path { get; }
internal bool IsFolder { get; }
internal bool IsAddionalItem { get; }
internal bool IsNetworkRoot { get; }
internal int Level { get; set; }
internal string FileExtension { get; }
internal bool IsLink { get; }
internal string ResolvedPath { get; }
internal bool IsLinkToFolder { get; }
internal bool ShowOverlay { get; }
internal string Text { get; }
internal bool ContainsMenu { get; }
internal bool IsMainMenu { get; }
internal Menu SubMenu { get; set; }
@ -35,35 +129,16 @@ namespace SystemTrayMenu.DataClasses
internal bool IsSelected { get; set; }
internal bool ContainsMenu { get; set; }
internal bool IsContextMenuOpen { get; set; }
internal bool IsResolvedLnk { get; set; }
internal bool HiddenEntry { get; set; }
internal bool ShowOnlyWhenSearch { get; set; }
internal string TargetFilePath { get; set; }
internal string TargetFilePathOrig { get; set; }
internal int RowIndex { get; set; }
internal int MenuLevel { get; set; }
internal bool IconLoading { get; set; }
internal string FilePathIcon { get; set; }
internal bool ProcessStarted { get; set; }
internal void SetText(string text)
{
this.Text = text;
}
internal void SetData(RowData data, DataTable dataTable)
{
DataRow row = dataTable.Rows.Add();
@ -71,7 +146,7 @@ namespace SystemTrayMenu.DataClasses
if (HiddenEntry)
{
row[0] = IconReader.AddIconOverlay(data.icon, White50PercentageIcon);
row[0] = AddIconOverlay(data.icon, Properties.Resources.White50Percentage);
}
else
{
@ -82,65 +157,32 @@ namespace SystemTrayMenu.DataClasses
row[2] = data;
}
internal bool ReadIconOrResolveLinkAndReadIcon(bool isDirectory, ref string resolvedLnkPath, int level)
internal Icon ReadIcon(bool updateIconInBackground)
{
bool isLnkDirectory = false;
if (string.IsNullOrEmpty(TargetFilePath))
if (IsFolder || IsLinkToFolder)
{
Log.Info($"TargetFilePath from {resolvedLnkPath} empty");
}
else if (isDirectory)
{
icon = IconReader.GetFolderIconWithCache(
TargetFilePathOrig,
IconReader.FolderType.Closed,
false,
true,
level == 0,
out bool loading);
icon = GetFolderIconWithCache(Path, ShowOverlay, updateIconInBackground, IsMainMenu, out bool loading);
IconLoading = loading;
}
else
{
bool handled = false;
bool showOverlay = false;
string fileExtension = Path.GetExtension(TargetFilePath);
if (fileExtension.Equals(".lnk", StringComparison.InvariantCultureIgnoreCase))
{
handled = ResolveLinkAndReadIcon(level, ref isLnkDirectory, ref resolvedLnkPath);
showOverlay = Properties.Settings.Default.ShowLinkOverlay;
}
else if (fileExtension.Equals(".url", StringComparison.InvariantCultureIgnoreCase))
{
SetText($"{Text[0..^4]}");
showOverlay = Properties.Settings.Default.ShowLinkOverlay;
}
else if (fileExtension.Equals(".appref-ms", StringComparison.InvariantCultureIgnoreCase))
{
showOverlay = Properties.Settings.Default.ShowLinkOverlay;
}
if (!handled)
{
icon = IconReader.GetFileIconWithCache(
TargetFilePathOrig,
TargetFilePath,
showOverlay,
true,
level == 0,
out bool loading);
IconLoading = loading;
}
icon = GetFileIconWithCache(Path, ResolvedPath, ShowOverlay, updateIconInBackground, IsMainMenu, out bool loading);
IconLoading = loading;
}
if (icon == null)
if (!IconLoading)
{
icon = NotFoundIcon;
if (icon == null)
{
icon = Properties.Resources.NotFound;
}
else if (HiddenEntry)
{
icon = AddIconOverlay(icon, Properties.Resources.White50Percentage);
}
}
return isLnkDirectory;
return icon;
}
internal void MouseDown(DataGridView dgv, MouseEventArgs e)
@ -167,14 +209,22 @@ namespace SystemTrayMenu.DataClasses
if (ContainsMenu)
{
DirectoryInfo[] dir = new DirectoryInfo[1];
dir[0] = new DirectoryInfo(TargetFilePathOrig);
dir[0] = new DirectoryInfo(Path);
ctxMnu.ShowContextMenu(dir, point);
// Triggers filewatcher change event
string parentFolder = System.IO.Path.GetDirectoryName(Path);
Directory.GetFiles(parentFolder);
}
else
{
FileInfo[] arrFI = new FileInfo[1];
arrFI[0] = new FileInfo(TargetFilePathOrig);
arrFI[0] = FileInfo;
ctxMnu.ShowContextMenu(arrFI, point);
// Triggers filewatcher change event
string parentFolder = System.IO.Path.GetDirectoryName(Path);
Directory.GetFiles(parentFolder);
}
IsContextMenuOpen = false;
@ -195,7 +245,7 @@ namespace SystemTrayMenu.DataClasses
if (Properties.Settings.Default.OpenDirectoryWithOneClick &&
ContainsMenu && (e == null || e.Button == MouseButtons.Left))
{
Log.ProcessStart(TargetFilePath);
Log.ProcessStart(Path);
if (!Properties.Settings.Default.StaysOpenWhenItemClicked)
{
toCloseByDoubleClick = true;
@ -216,7 +266,7 @@ namespace SystemTrayMenu.DataClasses
if (!Properties.Settings.Default.OpenDirectoryWithOneClick &&
ContainsMenu && (e == null || e.Button == MouseButtons.Left))
{
Log.ProcessStart(TargetFilePath);
Log.ProcessStart(Path);
if (!Properties.Settings.Default.StaysOpenWhenItemClicked)
{
toCloseByDoubleClick = true;
@ -224,100 +274,19 @@ namespace SystemTrayMenu.DataClasses
}
}
internal Icon ReadLoadedIcon()
{
if (ContainsMenu)
{
icon = IconReader.GetFolderIconWithCache(
TargetFilePathOrig,
IconReader.FolderType.Closed,
false,
false,
MenuLevel == 0,
out bool loading);
IconLoading = loading;
}
else
{
bool showOverlay = false;
string fileExtension = Path.GetExtension(TargetFilePathOrig);
if (fileExtension == ".lnk" || fileExtension == ".url" || fileExtension == ".appref-ms")
{
showOverlay = Properties.Settings.Default.ShowLinkOverlay;
}
icon = IconReader.GetFileIconWithCache(
TargetFilePathOrig,
TargetFilePath,
showOverlay,
false,
MenuLevel == 0,
out bool loading);
IconLoading = loading;
}
if (!IconLoading && icon == null)
{
icon = NotFoundIcon;
}
if (HiddenEntry)
{
icon = IconReader.AddIconOverlay(icon, White50PercentageIcon);
}
return icon;
}
private void OpenItem(MouseEventArgs e, ref bool toCloseByOpenItem)
{
if (!ContainsMenu &&
(e == null || e.Button == MouseButtons.Left))
{
ProcessStarted = true;
string workingDirectory = Path.GetDirectoryName(TargetFilePath);
Log.ProcessStart(TargetFilePathOrig, string.Empty, false, workingDirectory, true);
string workingDirectory = System.IO.Path.GetDirectoryName(ResolvedPath);
Log.ProcessStart(Path, string.Empty, false, workingDirectory, true);
if (!Properties.Settings.Default.StaysOpenWhenItemClicked)
{
toCloseByOpenItem = true;
}
}
}
private bool ResolveLinkAndReadIcon(int level, ref bool isLnkDirectory, ref string resolvedLnkPath)
{
bool handled = false;
resolvedLnkPath = FileLnk.GetResolvedFileName(TargetFilePath, out bool isFolder);
if (string.IsNullOrEmpty(resolvedLnkPath))
{
// Log.Info($"Could not resolve *.LNK '{TargetFilePath}'");
}
else if (isFolder)
{
icon = IconReader.GetFolderIconWithCache(
TargetFilePathOrig,
IconReader.FolderType.Open,
Properties.Settings.Default.ShowLinkOverlay,
true,
level == 0,
out bool loading);
IconLoading = loading;
handled = true;
isLnkDirectory = true;
}
else if (FileLnk.IsNetworkRoot(resolvedLnkPath))
{
isLnkDirectory = true;
}
else
{
TargetFilePath = resolvedLnkPath;
}
SetText(Path.GetFileNameWithoutExtension(TargetFilePathOrig));
return handled;
}
}
}

View file

@ -40,7 +40,7 @@ namespace SystemTrayMenu.Helper
RowData rowData = (RowData)menu.Tag;
if (rowData != null)
{
path = rowData.TargetFilePath;
path = rowData.ResolvedPath;
}
else
{

View file

@ -39,5 +39,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.2.9.19")]
[assembly: AssemblyFileVersion("1.2.9.19")]
[assembly: AssemblyVersion("1.2.9.20")]
[assembly: AssemblyFileVersion("1.2.9.20")]

View file

@ -18,6 +18,7 @@ namespace SystemTrayMenu.UserInterface
internal partial class Menu : Form
{
private const int CornerRadius = 20;
private const string RowFilterShowAll = "[SortIndex] LIKE '%0%'";
private readonly Fading fading = new();
private bool isShowing;
private bool directionToRight;
@ -234,7 +235,22 @@ namespace SystemTrayMenu.UserInterface
internal void ResetSearchText()
{
textBoxSearch.Text = string.Empty;
dgv.FirstDisplayedScrollingRowIndex = 0;
if (dgv.Rows.Count > 0)
{
dgv.FirstDisplayedScrollingRowIndex = 0;
}
AdjustScrollbar();
}
internal void RefreshSearchText()
{
TextBoxSearch_TextChanged(textBoxSearch, null);
if (dgv.Rows.Count > 0)
{
dgv.FirstDisplayedScrollingRowIndex = 0;
}
AdjustScrollbar();
}
@ -421,6 +437,11 @@ namespace SystemTrayMenu.UserInterface
}
}
internal void TimerUpdateIconsStart()
{
timerUpdateIcons.Start();
}
/// <summary>
/// Update the position and size of the menu.
/// </summary>
@ -757,7 +778,8 @@ namespace SystemTrayMenu.UserInterface
ScrollbarVisible = false;
}
if (string.IsNullOrEmpty(data.DefaultView.RowFilter))
if (string.IsNullOrEmpty(textBoxSearch.Text) &&
dgv.Height != dgvHeightNew)
{
dgv.Height = dgvHeightNew;
}
@ -765,13 +787,13 @@ namespace SystemTrayMenu.UserInterface
private void AdjustDataGridViewWidth()
{
DataGridViewExtensions.FastAutoSizeColumns(dgv);
if (dgv.Columns[1].Width < 60)
if (!string.IsNullOrEmpty(textBoxSearch.Text))
{
dgv.Columns[1].Width = 60;
return;
}
DataGridViewExtensions.FastAutoSizeColumns(dgv);
int widthIcon = dgv.Columns[0].Width;
int widthText = dgv.Columns[1].Width;
int widthScrollbar = 0;
@ -780,14 +802,23 @@ namespace SystemTrayMenu.UserInterface
widthScrollbar = customScrollbar.Width;
}
if (tableLayoutPanelBottom.Width > (widthIcon + widthText + widthScrollbar))
using Graphics gfx = labelTitle.CreateGraphics();
gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
int withTitle = (int)(gfx.MeasureString(
labelTitle.Text + "___",
dgv.RowTemplate.DefaultCellStyle.Font).Width + 0.5);
if (withTitle > (widthIcon + widthText + widthScrollbar))
{
dgv.Width = tableLayoutPanelBottom.Width - widthScrollbar;
dgv.Columns[1].Width = tableLayoutPanelBottom.Width - widthIcon - widthScrollbar;
tableLayoutPanelDgvAndScrollbar.MinimumSize = new Size(withTitle, 0);
dgv.Width = withTitle - widthScrollbar;
dgv.Columns[1].Width = dgv.Width - widthIcon;
}
else
{
tableLayoutPanelDgvAndScrollbar.MinimumSize = new Size(widthIcon + widthText + widthScrollbar, 0);
dgv.Width = widthIcon + widthText;
dgv.Columns[1].Width = dgv.Width - widthIcon;
}
tableLayoutPanelSearch.MinimumSize = new Size(dgv.Width + widthScrollbar, 0);
@ -867,7 +898,7 @@ namespace SystemTrayMenu.UserInterface
if (Properties.Settings.Default.ShowOnlyAsSearchResult &&
isSearchStringEmpty)
{
data.DefaultView.RowFilter = "[SortIndex] LIKE '%0%'";
data.DefaultView.RowFilter = RowFilterShowAll;
}
else
{
@ -893,8 +924,7 @@ namespace SystemTrayMenu.UserInterface
foreach (DataRow row in data.Rows)
{
RowData rowData = (RowData)row[2];
if (Properties.Settings.Default.ShowOnlyAsSearchResult &&
rowData.ShowOnlyWhenSearch)
if (rowData.IsAddionalItem && Properties.Settings.Default.ShowOnlyAsSearchResult)
{
row[columnSortIndex] = 99;
}
@ -934,7 +964,8 @@ namespace SystemTrayMenu.UserInterface
{
RowData rowData = (RowData)row.Cells[2].Value;
if (!isSearchStringEmpty || !rowData.ShowOnlyWhenSearch)
if (!isSearchStringEmpty ||
!(rowData.IsAddionalItem && Properties.Settings.Default.ShowOnlyAsSearchResult))
{
rowData.RowIndex = row.Index;
@ -1113,7 +1144,7 @@ namespace SystemTrayMenu.UserInterface
if (rowData.IconLoading)
{
iconsToUpdate++;
row.Cells[0].Value = rowData.ReadLoadedIcon();
row.Cells[0].Value = rowData.ReadIcon(false);
}
}

View file

@ -11,6 +11,8 @@ namespace SystemTrayMenu.Utilities
internal static class DataGridViewExtensions
{
private const float WidthMin = 100f;
/// <summary>
/// dgv.AutoResizeColumns() was too slow ~45ms.
/// </summary>
@ -22,13 +24,12 @@ namespace SystemTrayMenu.Utilities
using Graphics gfx = dgv.CreateGraphics();
int i = 1;
gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
float widthMax = dgv.Columns[i].HeaderCell.Size.Width;
float widthMax = WidthMin;
foreach (DataGridViewRow row in rows)
{
float checkWidth = gfx.MeasureString(
row.Cells[i].Value.ToString() + "___",
dgv.RowTemplate.DefaultCellStyle.Font)
.Width;
dgv.RowTemplate.DefaultCellStyle.Font).Width;
if (checkWidth > widthMax)
{
widthMax = checkWidth;

View file

@ -9,7 +9,7 @@ namespace SystemTrayMenu.Utilities
public static class DirectoryBySearchPattern
{
public static string[] GetFiles(string path, string searchPatternCombined)
public static List<string> GetFiles(string path, string searchPatternCombined)
{
string[] searchPatterns = searchPatternCombined.Split('|');
List<string> files = new();
@ -18,8 +18,7 @@ namespace SystemTrayMenu.Utilities
files.AddRange(System.IO.Directory.GetFiles(path, searchPattern));
}
files.Sort();
return files.ToArray();
return files;
}
}
}

View file

@ -69,9 +69,17 @@ namespace SystemTrayMenu.Utilities
return cleared;
}
public static void RemoveIconFromCache(string path)
{
if (DictIconCacheMainMenu.Remove(path, out Icon iconToRemove))
{
iconToRemove?.Dispose();
}
}
public static Icon GetFileIconWithCache(
string pathOrig,
string path,
string resolvedPath,
bool linkOverlay,
bool updateIconInBackground,
bool isMainMenu,
@ -79,10 +87,10 @@ namespace SystemTrayMenu.Utilities
string keyPath = "")
{
loading = false;
string extension = Path.GetExtension(pathOrig);
string extension = Path.GetExtension(path);
IconSize size = IconSize.Large;
string key = pathOrig;
string key = path;
if (!string.IsNullOrEmpty(keyPath))
{
key = keyPath;
@ -103,7 +111,7 @@ namespace SystemTrayMenu.Utilities
new Thread(UpdateIconInBackground).Start();
void UpdateIconInBackground()
{
DictIconCache(isMainMenu).GetOrAdd(key, GetIconSTA(pathOrig, path, linkOverlay, size, null));
DictIconCache(isMainMenu).GetOrAdd(key, GetIconSTA(path, resolvedPath, linkOverlay, size, false));
}
}
}
@ -113,7 +121,6 @@ namespace SystemTrayMenu.Utilities
public static Icon GetFolderIconWithCache(
string path,
FolderType folderType,
bool linkOverlay,
bool updateIconInBackground,
bool isMainMenu,
@ -156,25 +163,25 @@ namespace SystemTrayMenu.Utilities
Icon GetFolder(string keyExtension)
{
return GetIconSTA(path, path, linkOverlay, size, folderType);
return GetIconSTA(path, path, linkOverlay, size, true);
}
return icon;
}
public static Icon GetIconSTA(string pathOrig, string path, bool linkOverlay, IconSize size, FolderType? folderType)
public static Icon GetIconSTA(string path, string resolvedPath, bool linkOverlay, IconSize size, bool isFolder)
{
Icon icon = null;
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
{
icon = GetIcon(pathOrig, path, linkOverlay, size, folderType);
icon = GetIcon(path, resolvedPath, linkOverlay, size, isFolder);
}
else
{
Thread staThread = new(new ParameterizedThreadStart(StaThreadMethod));
void StaThreadMethod(object obj)
{
icon = GetIcon(pathOrig, path, linkOverlay, size, folderType);
icon = GetIcon(path, resolvedPath, linkOverlay, size, isFolder);
}
staThread.SetApartmentState(ApartmentState.STA);
@ -227,17 +234,17 @@ namespace SystemTrayMenu.Utilities
return isExtensionWithSameIcon;
}
private static Icon GetIcon(string path, string pathOrig, bool linkOverlay, IconSize size, FolderType? type)
private static Icon GetIcon(string path, string resolvedPath, bool linkOverlay, IconSize size, bool isFolder)
{
Icon icon;
if (Path.GetExtension(path).Equals(".ico", StringComparison.InvariantCultureIgnoreCase))
{
icon = Icon.ExtractAssociatedIcon(path);
}
else if (Path.GetExtension(pathOrig).Equals(".ico", StringComparison.InvariantCultureIgnoreCase) &&
File.Exists(pathOrig))
else if (Path.GetExtension(resolvedPath).Equals(".ico", StringComparison.InvariantCultureIgnoreCase) &&
File.Exists(resolvedPath))
{
icon = Icon.ExtractAssociatedIcon(pathOrig);
icon = Icon.ExtractAssociatedIcon(resolvedPath);
if (linkOverlay)
{
icon = AddIconOverlay(icon, Properties.Resources.LinkArrow);
@ -246,9 +253,8 @@ namespace SystemTrayMenu.Utilities
else
{
NativeMethods.SHFILEINFO shFileInfo = default;
uint flags = GetFlags(linkOverlay, size, type);
uint attribute = type == null ? NativeMethods.FileAttributeNormal :
NativeMethods.FileAttributeDirectory;
uint flags = GetFlags(linkOverlay, size);
uint attribute = isFolder ? NativeMethods.FileAttributeDirectory : NativeMethods.FileAttributeNormal;
IntPtr imageList = NativeMethods.Shell32SHGetFileInfo(
path, attribute, ref shFileInfo, (uint)Marshal.SizeOf(shFileInfo), flags);
icon = GetIcon(path, linkOverlay, shFileInfo, imageList);
@ -257,7 +263,7 @@ namespace SystemTrayMenu.Utilities
return icon;
}
private static uint GetFlags(bool linkOverlay, IconSize size, FolderType? folderType)
private static uint GetFlags(bool linkOverlay, IconSize size)
{
uint flags = NativeMethods.ShgfiIcon | NativeMethods.ShgfiSYSICONINDEX;
if (linkOverlay)
@ -265,11 +271,6 @@ namespace SystemTrayMenu.Utilities
flags += NativeMethods.ShgfiLINKOVERLAY;
}
if (folderType == FolderType.Open)
{
flags += NativeMethods.ShgfiOPENICON;
}
if (size == IconSize.Small)
{
flags += NativeMethods.ShgfiSMALLICON;

View file

@ -9,6 +9,7 @@ namespace SystemTrayMenu.Utilities
using System.Reflection;
using System.Runtime.InteropServices;
using Shell32;
using SystemTrayMenu.DataClasses;
internal static class FolderOptions
{
@ -55,38 +56,37 @@ namespace SystemTrayMenu.Utilities
}
}
internal static bool IsHidden(string path, ref bool hiddenEntry)
internal static bool IsHidden(RowData rowData)
{
bool isDirectoryToHide = false;
if (path.Length < 260)
if (rowData.Path.Length >= 260)
{
try
Log.Info($"path too long (>=260):'{rowData.Path}'");
return isDirectoryToHide;
}
try
{
FileAttributes attributes = File.GetAttributes(rowData.Path);
rowData.HiddenEntry = attributes.HasFlag(FileAttributes.Hidden);
bool systemEntry = attributes.HasFlag(
FileAttributes.Hidden | FileAttributes.System);
if (Properties.Settings.Default.SystemSettingsShowHiddenFiles)
{
FileAttributes attributes = File.GetAttributes(path);
hiddenEntry = attributes.HasFlag(FileAttributes.Hidden);
bool systemEntry = attributes.HasFlag(
FileAttributes.Hidden | FileAttributes.System);
if (Properties.Settings.Default.SystemSettingsShowHiddenFiles)
{
if ((hideHiddenEntries && hiddenEntry) ||
(hideSystemEntries && systemEntry))
{
isDirectoryToHide = true;
}
}
else if (hiddenEntry && Properties.Settings.Default.NeverShowHiddenFiles)
if ((hideHiddenEntries && rowData.HiddenEntry) ||
(hideSystemEntries && systemEntry))
{
isDirectoryToHide = true;
}
}
catch (Exception ex)
else if (rowData.HiddenEntry && Properties.Settings.Default.NeverShowHiddenFiles)
{
Log.Warn($"path:'{path}'", ex);
isDirectoryToHide = true;
}
}
else
catch (Exception ex)
{
Log.Info($"path too long (>=260):'{path}'");
Log.Warn($"path:'{rowData.Path}'", ex);
}
return isDirectoryToHide;