diff --git a/Business/Menus.cs b/Business/Menus.cs new file mode 100644 index 0000000..a69765f --- /dev/null +++ b/Business/Menus.cs @@ -0,0 +1,742 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Security; +using System.Threading; +using System.Windows.Forms; +using SystemTrayMenu.DataClasses; +using SystemTrayMenu.Handler; +using SystemTrayMenu.Helper; +using SystemTrayMenu.Utilities; +using Menu = SystemTrayMenu.UserInterface.Menu; +using Timer = System.Windows.Forms.Timer; + +namespace SystemTrayMenu.Business +{ + internal class Menus : IDisposable + { + internal event EventHandler LoadStarted; + internal event EventHandlerEmpty LoadStopped; + private enum OpenCloseState { Default, Opening, Closing }; + private OpenCloseState openCloseState = OpenCloseState.Default; + private readonly Menu[] menus = new Menu[MenuDefines.MenusMax]; + private readonly BackgroundWorker worker = new BackgroundWorker(); + + + private readonly Screen screen = Screen.PrimaryScreen; + private readonly KeyboardInput keyboardInput; + private readonly Timer timerStillActiveCheck = new Timer(); + private readonly WaitLeave waitLeave = new WaitLeave(MenuDefines.TimeUntilClose); + private DateTime deactivatedTime = DateTime.MinValue; + + private IEnumerable AsEnumerable => menus.Where(m => m != null && !m.IsDisposed); + private List AsList => AsEnumerable.ToList(); + + public Menus() + { + worker.WorkerSupportsCancellation = true; + worker.DoWork += Load; + void Load(object sender, DoWorkEventArgs e) + { + e.Result = GetData((BackgroundWorker)sender, Config.Path, 0); + } + + worker.RunWorkerCompleted += LoadCompleted; + void LoadCompleted(object sender, RunWorkerCompletedEventArgs e) + { + keyboardInput.ResetSelectedByKey(); + LoadStopped(); + MenuData menuData = (MenuData)e.Result; + if (menuData.Validity == MenuDataValidity.Valid) + { + DisposeMenu(menus[0]); + menus[0] = Create(menuData, Path.GetFileName(Config.Path)); + + menus[0].AdjustLocationAndSize(screen); + AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); }); + } + } + + keyboardInput = new KeyboardInput(menus); + keyboardInput.RegisterHotKey(); + keyboardInput.HotKeyPressed += SwitchOpenClose; + keyboardInput.ClosePressed += MenusFadeOut; + keyboardInput.RowDeselected += CheckMenuOpenerStop; + keyboardInput.RowSelected += KeyboardInputRowSelected; + void KeyboardInputRowSelected(DataGridView dgv, int rowIndex) + { + FadeInIfNeeded(); + CheckMenuOpenerStart(dgv, rowIndex); + } + + waitLeave.LeaveTriggered += LeaveTriggered; + void LeaveTriggered() => FadeHalfOrOutIfNeeded(); + } + + internal void SwitchOpenClose() + { + if ((DateTime.Now - deactivatedTime).TotalMilliseconds < 200) + { + //By click on notifyicon the menu gets deactivated and closed + } + else if (string.IsNullOrEmpty(Config.Path)) + { + //Case when Folder Dialog open + } + else if (openCloseState == OpenCloseState.Opening || + menus[0].Visible && openCloseState == OpenCloseState.Default) + { + openCloseState = OpenCloseState.Closing; + MenusFadeOut(); + StopWorker(); + if (!AsEnumerable.Any(m => m.Visible)) + { + openCloseState = OpenCloseState.Default; + } + } + else + { + openCloseState = OpenCloseState.Opening; + LoadStarted(this, true); + StartWorker(); + } + deactivatedTime = DateTime.MinValue; + } + + public void Dispose() + { + worker.Dispose(); + keyboardInput.Dispose(); + timerStillActiveCheck.Dispose(); + waitLeave.Dispose(); + IconReader.Dispose(); + DisposeMenu(menus[0]); + } + + internal void DisposeMenu(Menu menuToDispose) + { + if (menuToDispose != null) + { + DataGridView dgv = menuToDispose.GetDataGridView(); + foreach (DataGridViewRow row in dgv.Rows) + { + RowData rowData = (RowData)row.Tag; + rowData.Dispose(); + DisposeMenu(rowData.SubMenu); + } + dgv.ClearSelection(); + menuToDispose.Dispose(); + } + } + + internal static MenuData GetData(BackgroundWorker worker, string path, int level) + { + MenuData menuData = new MenuData + { + RowDatas = new List(), + Validity = MenuDataValidity.Invalid, + Level = level + }; + if (!worker.CancellationPending) + { + string[] directories = Array.Empty(); + + try + { + directories = Directory.GetDirectories(path); + Array.Sort(directories, new WindowsExplorerSort()); + } + catch (UnauthorizedAccessException ex) + { + Log.Warn($"path:'{path}'", ex); + menuData.Validity = MenuDataValidity.NoAccess; + } + catch (IOException ex) + { + Log.Warn($"path:'{path}'", ex); + } + + foreach (string directory in directories) + { + if (worker != null && worker.CancellationPending) + { + break; + } + + bool hiddenEntry = false; + if (FolderOptions.IsHidden(directory, ref hiddenEntry)) + { + continue; + } + + RowData rowData = ReadRowData(directory, false); + rowData.ContainsMenu = true; + rowData.HiddenEntry = hiddenEntry; + string resolvedLnkPath = string.Empty; + rowData.ReadIcon(true, ref resolvedLnkPath); + menuData.RowDatas.Add(rowData); + } + } + + if (!worker.CancellationPending) + { + string[] files = Array.Empty(); + + try + { + files = Directory.GetFiles(path); + Array.Sort(files, new WindowsExplorerSort()); + } + catch (UnauthorizedAccessException ex) + { + Log.Warn($"path:'{path}'", ex); + menuData.Validity = MenuDataValidity.NoAccess; + } + catch (IOException ex) + { + Log.Warn($"path:'{path}'", ex); + } + + foreach (string file in files) + { + if (worker != null && worker.CancellationPending) + { + break; + } + + bool hiddenEntry = false; + if (FolderOptions.IsHidden(file, ref hiddenEntry)) + { + continue; + } + + RowData rowData = ReadRowData(file, false); + string resolvedLnkPath = string.Empty; + if (rowData.ReadIcon(false, ref resolvedLnkPath)) + { + rowData = ReadRowData(resolvedLnkPath, true, rowData); + rowData.ContainsMenu = true; + rowData.HiddenEntry = hiddenEntry; + } + + menuData.RowDatas.Add(rowData); + } + } + + if (!worker.CancellationPending) + { + if (menuData.Validity == MenuDataValidity.Invalid) + { + menuData.Validity = MenuDataValidity.Valid; + } + } + + return menuData; + } + + internal void MainPreload() + { + menus[0] = Create(GetData(worker, Config.Path, 0), + Path.GetFileName(Config.Path)); + menus[0].AdjustLocationAndSize(screen); + DisposeMenu(menus[0]); + } + + internal void StartWorker() + { + if (worker.IsBusy) + { + LoadStopped(); + } + else + { + worker.RunWorkerAsync(); + } + } + + internal void StopWorker() + { + if (worker.IsBusy) + { + worker.CancelAsync(); + } + } + + private static RowData ReadRowData(string fileName, + bool isResolvedLnk, RowData rowData = null) + { + if (rowData == null) + { + rowData = new RowData(); + } + rowData.IsResolvedLnk = isResolvedLnk; + + try + { + rowData.FileInfo = new FileInfo(fileName); + rowData.TargetFilePath = rowData.FileInfo.FullName; + if (!isResolvedLnk) + { + rowData.SetText(rowData.FileInfo.Name); + rowData.TargetFilePathOrig = rowData.FileInfo.FullName; + } + } + catch (Exception ex) + { + if (ex is SecurityException || + ex is ArgumentException || + ex is UnauthorizedAccessException || + ex is PathTooLongException || + ex is NotSupportedException) + { + Log.Warn($"fileName:'{fileName}'", ex); + } + else + { + throw; + } + } + + return rowData; + } + + + private Menu Create(MenuData menuData, string title = null) + { + Menu menu = new Menu(); + + if (title != null) + { + if (string.IsNullOrEmpty(title)) + { + title = Path.GetPathRoot(Config.Path); + } + + menu.SetTitle(title); + menu.UserClickedOpenFolder += OpenFolder; + void OpenFolder() + { + Log.ProcessStart("explorer.exe", Config.Path); + } + } + + menu.Level = menuData.Level; + menu.MouseWheel += AdjustSubMenusLocationAndSize; + menu.MouseLeave += waitLeave.Start; + menu.MouseEnter += waitLeave.Stop; + DataGridView dgv = menu.GetDataGridView(); + dgv.CellMouseEnter += Dgv_CellMouseEnter; + dgv.CellMouseLeave += Dgv_CellMouseLeave; + dgv.MouseDown += Dgv_MouseDown; + dgv.MouseDoubleClick += Dgv_MouseDoubleClick; + dgv.SelectionChanged += Dgv_SelectionChanged; + menu.KeyPress += keyboardInput.KeyPress; + menu.CmdKeyProcessed += keyboardInput.CmdKeyProcessed; + menu.Deactivate += Deactivate; + void Deactivate(object sender, EventArgs e) + { + FadeHalfOrOutIfNeeded(); + if (!IsActive()) + { + deactivatedTime = DateTime.Now; + } + } + + menu.Activated += Activated; + void Activated(object sender, EventArgs e) + { + if (IsActive() && + menus[0].IsUsable) + { + menus[0].SetTitleColorActive(); + AsList.ForEach(m => m.ShowWithFade()); + } + + CheckIfWindowsStartStoleFocusNoDeactivateInRareCase(); + void CheckIfWindowsStartStoleFocusNoDeactivateInRareCase() + { + timerStillActiveCheck.Interval = 1000; + timerStillActiveCheck.Tick += StillActiveTick; + void StillActiveTick(object senderTimer, EventArgs eTimer) + { + if (!waitLeave.IsRunning) + { + FadeHalfOrOutIfNeeded(); + if (!IsActive()) + { + timerStillActiveCheck.Stop(); + } + } + } + timerStillActiveCheck.Start(); + } + } + + menu.VisibleChanged += MenuVisibleChanged; + AddItemsToMenu(menuData.RowDatas, menu); + void AddItemsToMenu(List data, Menu m) + { + foreach (RowData rowData in data) + { + CreateMenuRow(rowData, m); + } + } + + return menu; + } + + private void MenuVisibleChanged(object sender, EventArgs e) + { + Menu menu = (Menu)sender; + if (menu.IsUsable) + { + AdjustSubMenusLocationAndSize(); + } + if (!menu.Visible) + { + DisposeMenu(menu); + } + if (!AsEnumerable.Any(m => m.Visible)) + { + openCloseState = OpenCloseState.Default; + } + } + + + private void Dgv_CellMouseEnter(object sender, DataGridViewCellEventArgs e) + { + if (menus[0].IsUsable) + { + if (keyboardInput.InUse) + { + CheckMenuOpenerStop(keyboardInput.iMenuKey, + keyboardInput.iRowKey); + keyboardInput.ClearIsSelectedByKey(); + keyboardInput.InUse = false; + } + + DataGridView dgv = (DataGridView)sender; + keyboardInput.Select(dgv, e.RowIndex); + CheckMenuOpenerStart(dgv, e.RowIndex); + } + } + + private void CheckMenuOpenerStart(DataGridView dgv, int rowIndex) + { + if (rowIndex > -1 && + dgv.Rows.Count > rowIndex) + { + RowData trigger = (RowData)dgv.Rows[rowIndex].Tag; + trigger.IsSelected = true; + dgv.Rows[rowIndex].Selected = true; + Menu menuFromTrigger = (Menu)dgv.FindForm(); + Menu menuTriggered = trigger.SubMenu; + int level = menuFromTrigger.Level + 1; + + if (trigger.ContainsMenu && + level < MenuDefines.MenusMax && + menus[0].IsUsable && + (menus[level] == null || + menus[level] != menuTriggered)) + { + trigger.StopLoadMenuAndStartWaitToOpenIt(); + trigger.StartMenuOpener(); + + if (trigger.Reading.IsBusy) + { + trigger.RestartLoading = true; + } + else + { + LoadStarted(this, false); + trigger.Reading.RunWorkerAsync(level); + } + } + } + } + + private void CheckMenuOpenerStop(int menuIndex, int rowIndex, DataGridView dgv = null) + { + Menu menu = menus[menuIndex]; + if (menu != null && + rowIndex > -1) + { + if (dgv == null) + { + dgv = menu.GetDataGridView(); + } + if (dgv.Rows.Count > rowIndex) + { + RowData trigger = (RowData)dgv.Rows[rowIndex].Tag; + if (trigger.Reading.IsBusy) + { + if (!trigger.IsContextMenuOpen) + { + trigger.IsSelected = false; + dgv.Rows[rowIndex].Selected = false; + } + trigger.Reading.CancelAsync(); + } + else if (trigger.ContainsMenu && !trigger.IsLoading) + { + trigger.IsSelected = true; + dgv.Rows[rowIndex].Selected = true; + } + else + { + if (!trigger.IsContextMenuOpen) + { + trigger.IsSelected = false; + dgv.Rows[rowIndex].Selected = false; + } + } + if (trigger.IsLoading) + { + trigger.StopLoadMenuAndStartWaitToOpenIt(); + trigger.IsLoading = false; + } + } + } + } + + private void Dgv_CellMouseLeave(object sender, DataGridViewCellEventArgs e) + { + if (!keyboardInput.InUse) + { + DataGridView dgv = (DataGridView)sender; + Menu menu = (Menu)dgv.FindForm(); + CheckMenuOpenerStop(menu.Level, e.RowIndex, dgv); + } + } + +#warning [Feature] Double Click to start item not only one click #66 + private void Dgv_MouseDown(object sender, MouseEventArgs e) + { + DataGridView dgv = (DataGridView)sender; + DataGridView.HitTestInfo hitTestInfo; + hitTestInfo = dgv.HitTest(e.X, e.Y); + if (hitTestInfo.RowIndex > -1 && + dgv.Rows.Count > hitTestInfo.RowIndex) + { + RowData trigger = (RowData)dgv.Rows[hitTestInfo.RowIndex].Tag; + trigger.MouseDown(dgv, e); + } + } + + private void Dgv_MouseDoubleClick(object sender, MouseEventArgs e) + { + DataGridView dgv = (DataGridView)sender; + DataGridView.HitTestInfo hitTestInfo; + hitTestInfo = dgv.HitTest(e.X, e.Y); + if (hitTestInfo.RowIndex > -1 && + dgv.Rows.Count > hitTestInfo.RowIndex) + { + RowData trigger = (RowData)dgv.Rows[hitTestInfo.RowIndex].Tag; + trigger.DoubleClick(); + } + } + + private void Dgv_SelectionChanged(object sender, EventArgs e) + { + DataGridView dgv = (DataGridView)sender; + foreach (DataGridViewRow row in dgv.Rows) + { + RowData rowData = (RowData)row.Tag; + + if (!menus[0].IsUsable) + { + row.DefaultCellStyle.SelectionBackColor = Color.White; + } + else if (rowData.IsSelectedByKeyboard) + { + row.DefaultCellStyle.SelectionBackColor = + MenuDefines.ColorSelectedItem; + row.Selected = true; + } + else if (rowData.IsSelected) + { + row.DefaultCellStyle.SelectionBackColor = + MenuDefines.ColorOpenFolder; + row.Selected = true; + } + else + { + rowData.IsSelected = false; + row.Selected = false; + } + } + } + + private void CreateMenuRow(RowData rowData, Menu menu) + { + rowData.SetData(rowData, menu.GetDataGridView()); + rowData.OpenMenu += OpenSubMenu; + rowData.Reading.WorkerSupportsCancellation = true; + rowData.Reading.DoWork += ReadMenu_DoWork; + void ReadMenu_DoWork(object senderDoWork, + DoWorkEventArgs eDoWork) + { + int level = (int)eDoWork.Argument; + BackgroundWorker worker = (BackgroundWorker)senderDoWork; + eDoWork.Result = Business.Menus.GetData(worker, rowData.TargetFilePath, level); + } + + rowData.Reading.RunWorkerCompleted += ReadMenu_RunWorkerCompleted; + void ReadMenu_RunWorkerCompleted(object senderCompleted, + RunWorkerCompletedEventArgs e) + { + MenuData menuData = (MenuData)e.Result; + if (rowData.RestartLoading) + { + rowData.RestartLoading = false; + rowData.Reading.RunWorkerAsync(menuData.Level); + } + else + { + LoadStopped(); + if (menuData.Validity != MenuDataValidity.Invalid) + { + menu = Create(menuData); + if (menuData.RowDatas.Count > 0) + { + menu.SetTypeSub(); + } + else if (menuData.Validity == MenuDataValidity.NoAccess) + { + menu.SetTypeNoAccess(); + } + else + { + menu.SetTypeEmpty(); + } + menu.Tag = rowData; + rowData.SubMenu = menu; + rowData.MenuLoaded(); + } + } + } + } + + private void OpenSubMenu(object sender, RowData trigger) + { + Menu menuTriggered = trigger.SubMenu; + Menu menuFromTrigger = menus[menuTriggered.Level - 1]; + + for (int level = menuTriggered.Level; + level < MenuDefines.MenusMax; level++) + { + if (menus[level] != null) + { + Menu menuToClose = menus[level]; + RowData oldTrigger = (RowData)menuToClose.Tag; + DataGridView dgv = menuFromTrigger.GetDataGridView(); + foreach (DataGridViewRow row in dgv.Rows) + { + RowData rowData = (RowData)row.Tag; + rowData.IsSelected = false; + } + trigger.IsSelected = true; + dgv.ClearSelection(); + dgv.Rows[trigger.RowIndex].Selected = true; + menuToClose.HideWithFade(); + menuToClose.VisibleChanged += MenuVisibleChanged; + menus[level] = null; + } + } + + DisposeMenu(menus[menuTriggered.Level]); + menus[menuTriggered.Level] = menuTriggered; + AdjustSubMenusLocationAndSize(); + menus[menuTriggered.Level].ShowWithFadeOrTransparent(IsActive()); + } + + private void FadeInIfNeeded() + { + if (menus[0].IsUsable) + { + bool active = IsActive(); + AsList.ForEach(menu => menu.ShowWithFadeOrTransparent(active)); + } + } + + internal void FadeHalfOrOutIfNeeded() + { + if (menus[0].IsUsable) + { + if (!(IsActive())) + { + Point position = Control.MousePosition; + if (AsList.Any(m => m.IsMouseOn(position))) + { + if (!keyboardInput.InUse) + { + AsList.ForEach(menu => menu.ShowTransparent()); + } + } + else + { + MenusFadeOut(); + } + } + } + } + + private bool IsActive() + { + return Form.ActiveForm is Menu; + } + + private void MenusFadeOut() + { + openCloseState = OpenCloseState.Closing; + AsList.ForEach(menu => + { + if (menu.Level > 0) + { + menus[menu.Level] = null; + } + menu.HideWithFade(); + }); + } + + private void AdjustSubMenusLocationAndSize() + { + int heightMax = screen.Bounds.Height - + new WindowsTaskbar().Size.Height; + Menu menuPredecessor = menus[0]; + int widthPredecessors = -1; // -1 padding + bool directionToRight = false; + + foreach (Menu menu in AsEnumerable.Where(m => m.Level > 0)) + { + int newWith = (menu.Width - + menu.Padding.Horizontal + menuPredecessor.Width); + if (directionToRight) + { + if (widthPredecessors - menu.Width <= + -menu.Padding.Horizontal) + { + directionToRight = false; + } + else + { + widthPredecessors -= newWith; + } + } + else if (screen.Bounds.Width < + widthPredecessors + menuPredecessor.Width + menu.Width) + { + directionToRight = true; + widthPredecessors -= newWith; + } + + menu.AdjustLocationAndSize(heightMax, menuPredecessor); + widthPredecessors += menu.Width - menu.Padding.Left; + menuPredecessor = menu; + } + } + } +} diff --git a/Business/SystemTrayMenu.cs b/Business/SystemTrayMenu.cs index cfca7b6..7e8c573 100644 --- a/Business/SystemTrayMenu.cs +++ b/Business/SystemTrayMenu.cs @@ -1,111 +1,26 @@ using Microsoft.Win32; using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.IO; -using System.Linq; -using System.Security; using System.Windows.Forms; -using SystemTrayMenu.DataClasses; -using SystemTrayMenu.Handler; -using SystemTrayMenu.Helper; +using SystemTrayMenu.Business; using SystemTrayMenu.UserInterface; using SystemTrayMenu.Utilities; -using Menu = SystemTrayMenu.UserInterface.Menu; namespace SystemTrayMenu { internal class SystemTrayMenu : IDisposable { - private enum OpenCloseState { Default, Opening, Closing }; - private OpenCloseState openCloseState = OpenCloseState.Default; - private readonly MenuNotifyIcon menuNotifyIcon = null; - private readonly Menu[] menus = new Menu[MenuDefines.MenusMax]; - private readonly BackgroundWorker worker = new BackgroundWorker(); - private readonly Screen screen = Screen.PrimaryScreen; - private readonly WaitLeave waitLeave = new WaitLeave(MenuDefines.TimeUntilClose); - private readonly KeyboardInput keyboardInput; - private readonly Timer timerStillActiveCheck = new Timer(); - private DateTime deactivatedTime = DateTime.Now; + private readonly MenuNotifyIcon menuNotifyIcon = new MenuNotifyIcon(); + private Menus menus = new Menus(); public SystemTrayMenu() { AppRestart.BeforeRestarting += Dispose; SystemEvents.DisplaySettingsChanged += AppRestart.ByDisplaySettings; - waitLeave.LeaveTriggered += WaitLeave_LeaveTriggered; - void WaitLeave_LeaveTriggered() - { - FadeHalfOrOutIfNeeded(); - } - keyboardInput = new KeyboardInput(menus); - keyboardInput.RegisterHotKey(); - keyboardInput.HotKeyPressed += SwitchOpenClose; - keyboardInput.ClosePressed += MenusFadeOut; - keyboardInput.RowDeselected += CheckMenuOpenerStop; - keyboardInput.RowSelected += KeyboardInputRowSelected; - void KeyboardInputRowSelected(DataGridView dgv, int rowIndex) - { - keyboardInput.InUse = true; - FadeInIfNeeded(); - CheckMenuOpenerStart(dgv, rowIndex); - } - - menuNotifyIcon = new MenuNotifyIcon(); + menus.LoadStarted += menuNotifyIcon.LoadingStart; + menus.LoadStopped += menuNotifyIcon.LoadingStop; menuNotifyIcon.Exit += Application.Exit; menuNotifyIcon.Restart += AppRestart.ByMenuNotifyIcon; - menuNotifyIcon.HandleClick += SwitchOpenClose; - void SwitchOpenClose() - { - if (string.IsNullOrEmpty(Config.Path) || - (DateTime.Now - deactivatedTime).TotalMilliseconds < 300) - { - //Case when Folder Dialog open or restarts to fast - } - else if (openCloseState == OpenCloseState.Opening || - menus[0].Visible && openCloseState == OpenCloseState.Default) - { - openCloseState = OpenCloseState.Closing; - MenusFadeOut(); - if (worker.IsBusy) - { - worker.CancelAsync(); - } - if (!Menus().Any(m => m.Visible)) - { - openCloseState = OpenCloseState.Default; - } - } - else - { - openCloseState = OpenCloseState.Opening; - menuNotifyIcon.LoadingStart(true); - worker.RunWorkerAsync(); - } - } - - worker.WorkerSupportsCancellation = true; - worker.DoWork += Load; - void Load(object sender, DoWorkEventArgs e) - { - e.Result = ReadMenu((BackgroundWorker)sender, Config.Path, 0); - } - - worker.RunWorkerCompleted += LoadCompleted; - void LoadCompleted(object sender, RunWorkerCompletedEventArgs e) - { - keyboardInput.ResetSelectedByKey(); - menuNotifyIcon.LoadingStop(); - MenuData menuData = (MenuData)e.Result; - if (menuData.Validity == MenuDataValidity.Valid) - { - DisposeMenu(menus[0]); - menus[0] = CreateMenu(menuData, Path.GetFileName(Config.Path)); - menus[0].AdjustLocationAndSize(screen); - Menus().ToList().ForEach(m => { m.ShowWithFade(); }); - } - } - + menuNotifyIcon.Click += menus.SwitchOpenClose; menuNotifyIcon.OpenLog += Log.OpenLogFile; menuNotifyIcon.ChangeFolder += ChangeFolder; void ChangeFolder() @@ -116,617 +31,13 @@ namespace SystemTrayMenu } } - menus[0] = CreateMenu(ReadMenu(worker, Config.Path, 0), - Path.GetFileName(Config.Path)); - menus[0].AdjustLocationAndSize(screen); - DisposeMenu(menus[0]); + menus.MainPreload(); } public void Dispose() { - worker.Dispose(); - keyboardInput.Dispose(); + menus.Dispose(); menuNotifyIcon.Dispose(); - waitLeave.Dispose(); - timerStillActiveCheck.Dispose(); - DisposeMenu(menus[0]); - IconReader.Dispose(); - } - - private void DisposeMenu(Menu menuToDispose) - { - if (menuToDispose != null) - { - DataGridView dgv = menuToDispose.GetDataGridView(); - foreach (DataGridViewRow row in dgv.Rows) - { - RowData rowData = (RowData)row.Tag; - rowData.Dispose(); - DisposeMenu(rowData.SubMenu); - } - dgv.ClearSelection(); - menuToDispose.Dispose(); - } - } - - private static MenuData ReadMenu(BackgroundWorker worker, string path, int level) - { - MenuData menuData = new MenuData - { - RowDatas = new List(), - Validity = MenuDataValidity.Invalid, - Level = level - }; - if (!worker.CancellationPending) - { - string[] directories = Array.Empty(); - - try - { - directories = Directory.GetDirectories(path); - Array.Sort(directories, new WindowsExplorerSort()); - } - catch (UnauthorizedAccessException ex) - { - Log.Warn($"path:'{path}'", ex); - menuData.Validity = MenuDataValidity.NoAccess; - } - catch (IOException ex) - { - Log.Warn($"path:'{path}'", ex); - } - - foreach (string directory in directories) - { - if (worker != null && worker.CancellationPending) - { - break; - } - - bool hiddenEntry = false; - if (FolderOptions.IsHidden(directory, ref hiddenEntry)) - { - continue; - } - - RowData rowData = ReadRowData(directory, false); - rowData.ContainsMenu = true; - rowData.HiddenEntry = hiddenEntry; - string resolvedLnkPath = string.Empty; - rowData.ReadIcon(true, ref resolvedLnkPath); - menuData.RowDatas.Add(rowData); - } - } - - if (!worker.CancellationPending) - { - string[] files = Array.Empty(); - - try - { - files = Directory.GetFiles(path); - Array.Sort(files, new WindowsExplorerSort()); - } - catch (UnauthorizedAccessException ex) - { - Log.Warn($"path:'{path}'", ex); - menuData.Validity = MenuDataValidity.NoAccess; - } - catch (IOException ex) - { - Log.Warn($"path:'{path}'", ex); - } - - foreach (string file in files) - { - if (worker != null && worker.CancellationPending) - { - break; - } - - bool hiddenEntry = false; - if (FolderOptions.IsHidden(file, ref hiddenEntry)) - { - continue; - } - - RowData rowData = ReadRowData(file, false); - string resolvedLnkPath = string.Empty; - if (rowData.ReadIcon(false, ref resolvedLnkPath)) - { - rowData = ReadRowData(resolvedLnkPath, true, rowData); - rowData.ContainsMenu = true; - rowData.HiddenEntry = hiddenEntry; - } - - menuData.RowDatas.Add(rowData); - } - } - - if (!worker.CancellationPending) - { - if (menuData.Validity == MenuDataValidity.Invalid) - { - menuData.Validity = MenuDataValidity.Valid; - } - } - - return menuData; - } - - private static RowData ReadRowData(string fileName, - bool isResolvedLnk, RowData rowData = null) - { - if (rowData == null) - { - rowData = new RowData(); - } - rowData.IsResolvedLnk = isResolvedLnk; - - try - { - rowData.FileInfo = new FileInfo(fileName); - rowData.TargetFilePath = rowData.FileInfo.FullName; - if (!isResolvedLnk) - { - rowData.SetText(rowData.FileInfo.Name); - rowData.TargetFilePathOrig = rowData.FileInfo.FullName; - } - } - catch (Exception ex) - { - if (ex is SecurityException || - ex is ArgumentException || - ex is UnauthorizedAccessException || - ex is PathTooLongException || - ex is NotSupportedException) - { - Log.Warn($"fileName:'{fileName}'", ex); - } - else - { - throw; - } - } - - return rowData; - } - - private void CheckMenuOpenerStart(DataGridView dgv, int rowIndex) - { - if (rowIndex > -1 && - dgv.Rows.Count > rowIndex) - { - RowData trigger = (RowData)dgv.Rows[rowIndex].Tag; - trigger.IsSelected = true; - dgv.Rows[rowIndex].Selected = true; - Menu menuFromTrigger = (Menu)dgv.FindForm(); - Menu menuTriggered = trigger.SubMenu; - int level = menuFromTrigger.Level + 1; - - if (trigger.ContainsMenu && - level < MenuDefines.MenusMax && - menus[0].IsUsable && - (menus[level] == null || - menus[level] != menuTriggered)) - { - trigger.StopLoadMenuAndStartWaitToOpenIt(); - trigger.StartMenuOpener(); - - if (trigger.Reading.IsBusy) - { - trigger.RestartLoading = true; - } - else - { - menuNotifyIcon.LoadingStart(); - trigger.Reading.RunWorkerAsync(level); - } - } - } - } - - private void CheckMenuOpenerStop(int menuIndex, int rowIndex, DataGridView dgv = null) - { - Menu menu = menus[menuIndex]; - if (menu != null && - rowIndex > -1) - { - if (dgv == null) - { - dgv = menu.GetDataGridView(); - } - if (dgv.Rows.Count > rowIndex) - { - RowData trigger = (RowData)dgv.Rows[rowIndex].Tag; - if (trigger.Reading.IsBusy) - { - if (!trigger.IsContextMenuOpen) - { - trigger.IsSelected = false; - dgv.Rows[rowIndex].Selected = false; - } - trigger.Reading.CancelAsync(); - } - else if (trigger.ContainsMenu && !trigger.IsLoading) - { - trigger.IsSelected = true; - dgv.Rows[rowIndex].Selected = true; - } - else - { - if (!trigger.IsContextMenuOpen) - { - trigger.IsSelected = false; - dgv.Rows[rowIndex].Selected = false; - } - } - if (trigger.IsLoading) - { - trigger.StopLoadMenuAndStartWaitToOpenIt(); - trigger.IsLoading = false; - } - } - } - } - - private Menu CreateMenu(MenuData menuData, string title = null) - { - Menu menu = new Menu(); - - if (title != null) - { - if (string.IsNullOrEmpty(title)) - { - title = Path.GetPathRoot(Config.Path); - } - - menu.SetTitle(title); - menu.UserClickedOpenFolder += OpenFolder; - void OpenFolder() - { - Log.ProcessStart("explorer.exe", Config.Path); - } - } - - menu.Level = menuData.Level; - menu.MouseWheel += AdjustSubMenusLocationAndSize; - menu.MouseLeave += waitLeave.Start; - menu.MouseEnter += waitLeave.Stop; - DataGridView dgv = menu.GetDataGridView(); - dgv.CellMouseEnter += Dgv_CellMouseEnter; - dgv.CellMouseLeave += Dgv_CellMouseLeave; - dgv.MouseDown += Dgv_MouseDown; - dgv.MouseDoubleClick += Dgv_MouseDoubleClick; - dgv.SelectionChanged += Dgv_SelectionChanged; - menu.KeyPress += keyboardInput.KeyPress; - menu.CmdKeyProcessed += keyboardInput.CmdKeyProcessed; - menu.Deactivate += Deactivate; - void Deactivate(object sender, EventArgs e) - { - if (!FadeHalfOrOutIfNeeded()) - { - deactivatedTime = DateTime.Now; - } - } - - menu.Activated += Activated; - void Activated(object sender, EventArgs e) - { - if (Form.ActiveForm is Menu && - menus[0].IsUsable) - { - menus[0].SetTitleColorActive(); - Menus().ToList().ForEach(m => m.ShowWithFade()); - } - - CheckIfWindowsStartStoleFocusNoDeactivateInRareCase(); - void CheckIfWindowsStartStoleFocusNoDeactivateInRareCase() - { - timerStillActiveCheck.Interval = 1000; - timerStillActiveCheck.Tick += StillActiveTick; - void StillActiveTick(object senderTimer, EventArgs eTimer) - { - if (!waitLeave.IsRunning && - !FadeHalfOrOutIfNeeded()) - { - timerStillActiveCheck.Stop(); - } - } - timerStillActiveCheck.Start(); - } - } - - menu.VisibleChanged += MenuVisibleChanged; - AddItemsToMenu(menuData.RowDatas, menu); - void AddItemsToMenu(List data, Menu m) - { - foreach (RowData rowData in data) - { - CreateMenuRow(rowData, m); - } - } - - return menu; - } - - private void Dgv_CellMouseEnter(object sender, DataGridViewCellEventArgs e) - { - if (menus[0].IsUsable) - { - if (keyboardInput.InUse) - { - CheckMenuOpenerStop(keyboardInput.iMenuKey, - keyboardInput.iRowKey); - keyboardInput.ClearIsSelectedByKey(); - keyboardInput.InUse = false; - } - - DataGridView dgv = (DataGridView)sender; - keyboardInput.Select(dgv, e.RowIndex); - CheckMenuOpenerStart(dgv, e.RowIndex); - } - } - - private void Dgv_CellMouseLeave(object sender, DataGridViewCellEventArgs e) - { - if (!keyboardInput.InUse) - { - DataGridView dgv = (DataGridView)sender; - Menu menu = (Menu)dgv.FindForm(); - CheckMenuOpenerStop(menu.Level, e.RowIndex, dgv); - } - } - -#warning [Feature] Double Click to start item not only one click #66 - private void Dgv_MouseDown(object sender, MouseEventArgs e) - { - DataGridView dgv = (DataGridView)sender; - DataGridView.HitTestInfo hitTestInfo; - hitTestInfo = dgv.HitTest(e.X, e.Y); - if (hitTestInfo.RowIndex > -1 && - dgv.Rows.Count > hitTestInfo.RowIndex) - { - RowData trigger = (RowData)dgv.Rows[hitTestInfo.RowIndex].Tag; - trigger.MouseDown(dgv, e); - } - } - - private void Dgv_MouseDoubleClick(object sender, MouseEventArgs e) - { - DataGridView dgv = (DataGridView)sender; - DataGridView.HitTestInfo hitTestInfo; - hitTestInfo = dgv.HitTest(e.X, e.Y); - if (hitTestInfo.RowIndex > -1 && - dgv.Rows.Count > hitTestInfo.RowIndex) - { - RowData trigger = (RowData)dgv.Rows[hitTestInfo.RowIndex].Tag; - trigger.DoubleClick(); - } - } - - private void Dgv_SelectionChanged(object sender, EventArgs e) - { - DataGridView dgv = (DataGridView)sender; - foreach (DataGridViewRow row in dgv.Rows) - { - RowData rowData = (RowData)row.Tag; - - if (!menus[0].IsUsable) - { - row.DefaultCellStyle.SelectionBackColor = Color.White; - } - else if (rowData.IsSelectedByKeyboard) - { - row.DefaultCellStyle.SelectionBackColor = - MenuDefines.ColorSelectedItem; - row.Selected = true; - } - else if (rowData.IsSelected) - { - row.DefaultCellStyle.SelectionBackColor = - MenuDefines.ColorOpenFolder; - row.Selected = true; - } - else - { - rowData.IsSelected = false; - row.Selected = false; - } - } - } - - private void CreateMenuRow(RowData rowData, Menu menu) - { - rowData.SetData(rowData, menu.GetDataGridView()); - rowData.OpenMenu += OpenSubMenu; - rowData.Reading.WorkerSupportsCancellation = true; - rowData.Reading.DoWork += ReadMenu_DoWork; - void ReadMenu_DoWork(object senderDoWork, - DoWorkEventArgs eDoWork) - { - int level = (int)eDoWork.Argument; - BackgroundWorker worker = (BackgroundWorker)senderDoWork; - eDoWork.Result = ReadMenu(worker, rowData.TargetFilePath, level); - } - - rowData.Reading.RunWorkerCompleted += ReadMenu_RunWorkerCompleted; - void ReadMenu_RunWorkerCompleted(object senderCompleted, - RunWorkerCompletedEventArgs e) - { - MenuData menuData = (MenuData)e.Result; - if (rowData.RestartLoading) - { - rowData.RestartLoading = false; - rowData.Reading.RunWorkerAsync(menuData.Level); - } - else - { - menuNotifyIcon.LoadingStop(); - if (menuData.Validity != MenuDataValidity.Invalid) - { - menu = CreateMenu(menuData); - if (menuData.RowDatas.Count > 0) - { - menu.SetTypeSub(); - } - else if (menuData.Validity == MenuDataValidity.NoAccess) - { - menu.SetTypeNoAccess(); - } - else - { - menu.SetTypeEmpty(); - } - menu.Tag = rowData; - rowData.SubMenu = menu; - rowData.MenuLoaded(); - } - } - } - } - - private void OpenSubMenu(object sender, RowData trigger) - { - Menu menuTriggered = trigger.SubMenu; - Menu menuFromTrigger = menus[menuTriggered.Level - 1]; - - for (int level = menuTriggered.Level; - level < MenuDefines.MenusMax; level++) - { - if (menus[level] != null) - { - Menu menuToClose = menus[level]; - RowData oldTrigger = (RowData)menuToClose.Tag; - DataGridView dgv = menuFromTrigger.GetDataGridView(); - foreach (DataGridViewRow row in dgv.Rows) - { - RowData rowData = (RowData)row.Tag; - rowData.IsSelected = false; - } - trigger.IsSelected = true; - dgv.ClearSelection(); - dgv.Rows[trigger.RowIndex].Selected = true; - menuToClose.HideWithFade(); - menuToClose.VisibleChanged += MenuVisibleChanged; - menus[level] = null; - } - } - - DisposeMenu(menus[menuTriggered.Level]); - menus[menuTriggered.Level] = menuTriggered; - AdjustSubMenusLocationAndSize(); - menus[menuTriggered.Level].ShowWithFadeOrTransparent( - Form.ActiveForm is Menu); - } - - private void FadeInIfNeeded() - { - if (menus[0].IsUsable) - { - bool isActive = Form.ActiveForm is Menu; - Menus().ToList().ForEach( - menu => menu.ShowWithFadeOrTransparent(isActive)); - } - } - - private bool FadeHalfOrOutIfNeeded() - { - bool menuActive = false; - if (menus[0].IsUsable) - { - menuActive = Form.ActiveForm is Menu; - if (!menuActive) - { - Point position = Control.MousePosition; - if (Menus().Any(m => m.IsMouseOn(position))) - { - if (!keyboardInput.InUse) - { - Menus().ToList().ForEach( - menu => menu.ShowTransparent()); - } - } - else - { - MenusFadeOut(); - } - } - } - - return menuActive; - } - - private void MenusFadeOut() - { - openCloseState = OpenCloseState.Closing; - Menus().ToList().ForEach(menu => - { - if (menu.Level > 0) - { - menus[menu.Level] = null; - } - menu.HideWithFade(); - }); - } - - private void MenuVisibleChanged(object sender, EventArgs e) - { - Menu menu = (Menu)sender; - if (menu.IsUsable) - { - AdjustSubMenusLocationAndSize(); - } - if (!menu.Visible) - { - DisposeMenu(menu); - } - if (!Menus().Any(m => m.Visible)) - { - openCloseState = OpenCloseState.Default; - } - } - - private void AdjustSubMenusLocationAndSize() - { - int heightMax = screen.Bounds.Height - - new WindowsTaskbar().Size.Height; - Menu menuPredecessor = menus[0]; - int widthPredecessors = -1; // -1 padding - bool directionToRight = false; - - foreach (Menu menu in Menus().Where(m => m.Level > 0)) - { - int newWith = (menu.Width - - menu.Padding.Horizontal + menuPredecessor.Width); - if (directionToRight) - { - if (widthPredecessors - menu.Width <= - -menu.Padding.Horizontal) - { - directionToRight = false; - } - else - { - widthPredecessors -= newWith; - } - } - else if (screen.Bounds.Width < - widthPredecessors + menuPredecessor.Width + menu.Width) - { - directionToRight = true; - widthPredecessors -= newWith; - } - - menu.AdjustLocationAndSize(heightMax, menuPredecessor); - widthPredecessors += menu.Width - menu.Padding.Left; - menuPredecessor = menu; - } - } - - private IEnumerable Menus() - { - return menus.Where(m => m != null && !m.IsDisposed); } } } \ No newline at end of file diff --git a/DataClasses/RowData.cs b/DataClasses/RowData.cs index 3008edc..9107fa4 100644 --- a/DataClasses/RowData.cs +++ b/DataClasses/RowData.cs @@ -160,7 +160,7 @@ namespace SystemTrayMenu.DataClasses ref string resolvedLnkPath) { bool handled = false; - resolvedLnkPath = LnkHelper.ResolveShortcut(TargetFilePath); + resolvedLnkPath = LnkHelper.GetResolvedFileName(TargetFilePath); if (LnkHelper.IsDirectory(resolvedLnkPath)) { Icon = IconReader.GetFolderIcon(TargetFilePath, diff --git a/Helpers/Fading.cs b/Helpers/Fading.cs index 68381bf..564b601 100644 --- a/Helpers/Fading.cs +++ b/Helpers/Fading.cs @@ -15,7 +15,7 @@ namespace SystemTrayMenu.UserInterface private const int Interval60FPS = 16; //60fps=>1s/60fps=~16.6ms private const double StepIn = 0.20; - private const double StepOut = 0.05; + private const double StepOut = 0.10; private const double Transparent = 0.80; private const double TransparentMinus = 0.60; //Transparent - StepIn private const double TransparentPlus = 0.85; //Transparent + StepOut diff --git a/Helpers/KeyboardHook.cs b/Helpers/KeyboardHook.cs index 7bb4dd9..4969d6f 100644 --- a/Helpers/KeyboardHook.cs +++ b/Helpers/KeyboardHook.cs @@ -1,5 +1,6 @@ using System; using System.Windows.Forms; +using SystemTrayMenu.Utilities; namespace SystemTrayMenu.Helper { @@ -65,20 +66,35 @@ namespace SystemTrayMenu.Helper }; } + /// + /// Registers a hot key in the system. + /// + /// The key itself that is associated with the hot key. + internal void RegisterHotKey(Keys key) + { + uint keyModifiersNone = 0; + RegisterHotKey(keyModifiersNone, key); + } + /// /// Registers a hot key in the system. /// /// The modifiers that are associated with the hot key. /// The key itself that is associated with the hot key. internal void RegisterHotKey(KeyboardHookModifierKeys modifier, Keys key) + { + RegisterHotKey((uint)modifier, key); + } + + private void RegisterHotKey(uint modifier, Keys key) { _currentId = _currentId + 1; - if (!DllImports.NativeMethods.User32RegisterHotKey(_window.Handle, _currentId, (uint)modifier, (uint)key)) + if (!DllImports.NativeMethods.User32RegisterHotKey( + _window.Handle, _currentId, modifier, (uint)key)) { -#pragma warning disable CA1303 // Do not pass literals as localized parameters - throw new InvalidOperationException("Couldn’t register the hot key."); -#pragma warning restore CA1303 //=> Exceptions not translated in logfile => OK + throw new InvalidOperationException( + Language.Translate("Could not register the hot key.")); } } diff --git a/LICENSE b/LICENSE index 8f235ad..785fe01 100644 --- a/LICENSE +++ b/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. TAMAHO SystemTrayMenu - an improved Windows toolbar - Copyright (C) 2019 Markus Hofknecht + Copyright (C) 2020 Markus Hofknecht This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -652,7 +652,7 @@ You can contact me by mail Markus@Hofknecht.eu If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - TAMAHO SystemTrayMenu Copyright (C) 2019 Markus Hofknecht + TAMAHO SystemTrayMenu Copyright (C) 2020 Markus Hofknecht This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index b4823de..66684f7 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -35,5 +35,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("0.9.2.22")] -[assembly: AssemblyFileVersion("0.9.2.22")] +[assembly: AssemblyVersion("0.9.2.23")] +[assembly: AssemblyFileVersion("0.9.2.23")] diff --git a/Resources/lang.Designer.cs b/Resources/lang.Designer.cs index 0be4556..825a94a 100644 --- a/Resources/lang.Designer.cs +++ b/Resources/lang.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// Dieser Code wurde von einem Tool generiert. -// Laufzeitversion:4.0.30319.42000 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn -// der Code erneut generiert wird. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -13,12 +13,12 @@ namespace SystemTrayMenu.Resources { /// - /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// A strongly-typed resource class, for looking up localized strings, etc. /// - // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert - // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. - // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen - // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] @@ -33,7 +33,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { @@ -47,8 +47,8 @@ namespace SystemTrayMenu.Resources { } /// - /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle - /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { @@ -61,7 +61,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die (e.g. F10) ähnelt. + /// Looks up a localized string similar to (e.g. F10). /// internal static string _e_g__F10_ { get { @@ -70,7 +70,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die About ähnelt. + /// Looks up a localized string similar to About. /// internal static string About { get { @@ -79,7 +79,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Activate autostart ähnelt. + /// Looks up a localized string similar to Activate autostart. /// internal static string Activate_autostart { get { @@ -88,7 +88,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die ALT ähnelt. + /// Looks up a localized string similar to ALT. /// internal static string ALT { get { @@ -97,7 +97,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Autostart ähnelt. + /// Looks up a localized string similar to Autostart. /// internal static string Autostart { get { @@ -106,7 +106,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Details ähnelt. + /// Looks up a localized string similar to Details. /// internal static string buttonDetails { get { @@ -115,7 +115,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die OK ähnelt. + /// Looks up a localized string similar to OK. /// internal static string buttonOk { get { @@ -124,7 +124,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die System Info ähnelt. + /// Looks up a localized string similar to System Info. /// internal static string buttonSystemInfo { get { @@ -133,7 +133,16 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die CTRL ähnelt. + /// Looks up a localized string similar to Couldn’t register the hot key.. + /// + internal static string Could_not_register_the_hot_key_ { + get { + return ResourceManager.GetString("Could not register the hot key.", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CTRL. /// internal static string CTRL { get { @@ -142,7 +151,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die English ähnelt. + /// Looks up a localized string similar to English. /// internal static string English { get { @@ -151,7 +160,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Exit ähnelt. + /// Looks up a localized string similar to Exit. /// internal static string Exit { get { @@ -160,7 +169,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Folder ähnelt. + /// Looks up a localized string similar to Folder. /// internal static string Folder { get { @@ -169,7 +178,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Folder empty ähnelt. + /// Looks up a localized string similar to Folder empty. /// internal static string Folder_empty { get { @@ -178,7 +187,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Folder inaccessible ähnelt. + /// Looks up a localized string similar to Folder inaccessible. /// internal static string Folder_inaccessible { get { @@ -187,7 +196,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Deutsch ähnelt. + /// Looks up a localized string similar to Deutsch. /// internal static string German { get { @@ -196,7 +205,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Move the NotifyIcon per DragDrop from the SystemTray into the Taskbar ähnelt. + /// Looks up a localized string similar to Move the NotifyIcon per DragDrop from the SystemTray into the Taskbar. /// internal static string HintDragDropText { get { @@ -205,7 +214,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die SystemTrayMenu - Hint ähnelt. + /// Looks up a localized string similar to SystemTrayMenu - Hint. /// internal static string HintDragDropTitle { get { @@ -214,7 +223,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Language ähnelt. + /// Looks up a localized string similar to Language. /// internal static string Language { get { @@ -223,7 +232,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Log File ähnelt. + /// Looks up a localized string similar to Log File. /// internal static string Log_File { get { @@ -232,7 +241,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Restart ähnelt. + /// Looks up a localized string similar to Restart. /// internal static string Restart { get { @@ -241,7 +250,7 @@ namespace SystemTrayMenu.Resources { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Shortcut key ähnelt. + /// Looks up a localized string similar to Shortcut key. /// internal static string Shortcut_key { get { diff --git a/Resources/lang.de-DE.resx b/Resources/lang.de-DE.resx index e88a5f7..2893d49 100644 --- a/Resources/lang.de-DE.resx +++ b/Resources/lang.de-DE.resx @@ -180,4 +180,7 @@ Tastenkombination + + Der Tastenkürzel konnte nicht registriert werden. + \ No newline at end of file diff --git a/Resources/lang.resx b/Resources/lang.resx index 7a784ec..d20f883 100644 --- a/Resources/lang.resx +++ b/Resources/lang.resx @@ -180,4 +180,7 @@ Shortcut key + + Couldn’t register the hot key. + \ No newline at end of file diff --git a/SystemTrayMenu.csproj b/SystemTrayMenu.csproj index 27bb5c2..cf7e6d0 100644 --- a/SystemTrayMenu.csproj +++ b/SystemTrayMenu.csproj @@ -138,6 +138,7 @@ + @@ -177,6 +178,7 @@ + @@ -260,10 +262,7 @@ - - - diff --git a/UserInterface/AppNotifyIcon.cs b/UserInterface/AppNotifyIcon.cs index a6c2f30..76d11f9 100644 --- a/UserInterface/AppNotifyIcon.cs +++ b/UserInterface/AppNotifyIcon.cs @@ -12,7 +12,7 @@ namespace SystemTrayMenu.UserInterface { internal class MenuNotifyIcon : IDisposable { - public event EventHandlerEmpty HandleClick; + public event EventHandlerEmpty Click; public event EventHandlerEmpty ChangeFolder; public event EventHandlerEmpty OpenLog; public event EventHandlerEmpty Restart; @@ -69,7 +69,6 @@ namespace SystemTrayMenu.UserInterface { VerifyClick(e); } - notifyIcon.MouseDoubleClick += NotifyIcon_MouseDoubleClick; void NotifyIcon_MouseDoubleClick(object sender, MouseEventArgs e) { @@ -81,7 +80,7 @@ namespace SystemTrayMenu.UserInterface { if (e.Button == MouseButtons.Left) { - HandleClick.Invoke(); + Click?.Invoke(); } } @@ -92,7 +91,7 @@ namespace SystemTrayMenu.UserInterface load.Dispose(); } - public void LoadingStart(bool reset = false) + public void LoadingStart(object sender = null, bool reset = false) { if (reset) { diff --git a/UserInterface/Menu.cs b/UserInterface/Menu.cs index b006f16..45ddb22 100644 --- a/UserInterface/Menu.cs +++ b/UserInterface/Menu.cs @@ -2,6 +2,7 @@ using System.Drawing; using System.Linq; using System.Reflection; +using System.Threading; using System.Windows.Forms; using SystemTrayMenu.DataClasses; using SystemTrayMenu.DllImports; @@ -35,6 +36,7 @@ namespace SystemTrayMenu.UserInterface private readonly Fading fading = new Fading(); private bool autoResizeRowsDone = false; + private bool isShowing = false; internal Menu() { @@ -50,18 +52,22 @@ namespace SystemTrayMenu.UserInterface { try { + isShowing = true; Visible = true; + isShowing = false; } catch (ObjectDisposedException) { Visible = false; - Log.Info($"Could not open menu, menu already closed," + + isShowing = false; + Log.Info($"Could not open menu, old menu was disposing," + $" IsDisposed={IsDisposed}"); } if (Visible) { Activate(); + NativeMethods.User32ShowInactiveTopmost(this); NativeMethods.ForceForegroundWindow(Handle); SetTitleColorActive(); } @@ -201,7 +207,10 @@ namespace SystemTrayMenu.UserInterface internal void HideWithFade() { - fading.Fade(Fading.FadingState.Hide); + if(!isShowing) + { + fading.Fade(Fading.FadingState.Hide); + } } internal void AdjustLocationAndSize(Screen screen) diff --git a/Utilities/File/FileLnk.cs b/Utilities/File/FileLnk.cs index 3c80f13..49433b3 100644 --- a/Utilities/File/FileLnk.cs +++ b/Utilities/File/FileLnk.cs @@ -1,188 +1,329 @@ -using System; +using Shell32; +using System; using System.IO; -using System.Runtime.InteropServices; -using System.Text; +using System.Threading; namespace SystemTrayMenu.Utilities { internal class LnkHelper { - [Flags()] - private enum SLGP_FLAGS + public static string GetResolvedFileName(string shortcutFilename) { - /// Retrieves the standard short (8.3 format) file name - SLGP_SHORTPATH = 0x1, - /// Retrieves the Universal Naming Convention (UNC) path name of the file - SLGP_UNCPRIORITY = 0x2, - /// Retrieves the raw path name. A raw path is something that might not exist and may include environment variables that need to be expanded - SLGP_RAWPATH = 0x4 - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - private struct WIN32_FIND_DATAW - { - public uint dwFileAttributes; - public long ftCreationTime; - public long ftLastAccessTime; - public long ftLastWriteTime; - public uint nFileSizeHigh; - public uint nFileSizeLow; - public uint dwReserved0; - public uint dwReserved1; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] - public string cFileName; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] - public string cAlternateFileName; - } - - [Flags()] - private enum SLR_FLAGS - { - /// - /// Do not display a dialog box if the link cannot be resolved. When SLR_NO_UI is set, - /// the high-order word of fFlags can be set to a time-out value that specifies the - /// maximum amount of time to be spent resolving the link. The function returns if the - /// link cannot be resolved within the time-out duration. If the high-order word is set - /// to zero, the time-out duration will be set to the default value of 3,000 milliseconds - /// (3 seconds). To specify a value, set the high word of fFlags to the desired time-out - /// duration, in milliseconds. - /// - SLR_NO_UI = 0x1, - /// Obsolete and no longer used - SLR_ANY_MATCH = 0x2, - /// If the link object has changed, update its path and list of identifiers. - /// If SLR_UPDATE is set, you do not need to call IPersistFile::IsDirty to determine - /// whether or not the link object has changed. - SLR_UPDATE = 0x4, - /// Do not update the link information - SLR_NOUPDATE = 0x8, - /// Do not execute the search heuristics - SLR_NOSEARCH = 0x10, - /// Do not use distributed link tracking - SLR_NOTRACK = 0x20, - /// Disable distributed link tracking. By default, distributed link tracking tracks - /// removable media across multiple devices based on the volume name. It also uses the - /// Universal Naming Convention (UNC) path to track remote file systems whose drive letter - /// has changed. Setting SLR_NOLINKINFO disables both types of tracking. - SLR_NOLINKINFO = 0x40, - /// Call the Microsoft Windows Installer - SLR_INVOKE_MSI = 0x80 - } - - /// The IShellLink interface allows Shell links to be created, modified, and resolved - [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")] - private interface IShellLinkW - { - /// Retrieves the path and file name of a Shell link object - void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags); - /// Retrieves the list of item identifiers for a Shell link object - void GetIDList(out IntPtr ppidl); - /// Sets the pointer to an item identifier list (PIDL) for a Shell link object. - void SetIDList(IntPtr pidl); - /// Retrieves the description string for a Shell link object - void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); - /// Sets the description for a Shell link object. The description can be any application-defined string - void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); - /// Retrieves the name of the working directory for a Shell link object - void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); - /// Sets the name of the working directory for a Shell link object - void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); - /// Retrieves the command-line arguments associated with a Shell link object - void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); - /// Sets the command-line arguments for a Shell link object - void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); - /// Retrieves the hot key for a Shell link object - void GetHotkey(out short pwHotkey); - /// Sets a hot key for a Shell link object - void SetHotkey(short wHotkey); - /// Retrieves the show command for a Shell link object - void GetShowCmd(out int piShowCmd); - /// Sets the show command for a Shell link object. The show command sets the initial show state of the window. - void SetShowCmd(int iShowCmd); - /// Retrieves the location (path and index) of the icon for a Shell link object - void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, - int cchIconPath, out int piIcon); - /// Sets the location (path and index) of the icon for a Shell link object - void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); - /// Sets the relative path to the Shell link object - void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); - /// Attempts to find the target of a Shell link, even if it has been moved or renamed - void Resolve(IntPtr hwnd, SLR_FLAGS fFlags); - /// Sets the path and file name of a Shell link object - void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); - - } - - [ComImport, Guid("0000010c-0000-0000-c000-000000000046"), - InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface IPersist - { - [PreserveSig] - void GetClassID(out Guid pClassID); - } - - [ComImport, Guid("0000010b-0000-0000-C000-000000000046"), - InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface IPersistFile : IPersist - { - new void GetClassID(out Guid pClassID); - - [PreserveSig] - int IsDirty(); - - [PreserveSig] - void Load([In, MarshalAs(UnmanagedType.LPWStr)] - string pszFileName, uint dwMode); - - [PreserveSig] - void Save([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName, - [In, MarshalAs(UnmanagedType.Bool)] bool fRemember); - - [PreserveSig] - void SaveCompleted([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName); - - [PreserveSig] - void GetCurFile([In, MarshalAs(UnmanagedType.LPWStr)] string ppszFileName); - } - - private const uint STGM_READ = 0; - private const int MAX_PATH = 260; - - // CLSID_ShellLink from ShlGuid.h - [ - ComImport(), - Guid("00021401-0000-0000-C000-000000000046") - ] - public class ShellLink - { - } - - public static string ResolveShortcut(string filename) - { - ShellLink link = new ShellLink(); -#pragma warning disable CA2010 // Always consume the value returned by methods marked with PreserveSigAttribute - ((IPersistFile)link).Load(filename, STGM_READ); -#pragma warning restore CA2010 // => Has no returned value => OK - StringBuilder sb = new StringBuilder(MAX_PATH); - WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); - ((IShellLinkW)link).GetPath(sb, sb.Capacity, out data, 0); - string resolvedPath = sb.ToString(); - if (!IsDirectory(resolvedPath) && - !File.Exists(resolvedPath)) + string resolvedFilename = string.Empty; + if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) { - //For some lnk e.g. WinRar SkypeForBuisness - //resolved path wrong to Program Files (x86) - resolvedPath = ReplaceFirst(resolvedPath, - @"\Program Files (x86)\", - @"\Program Files\"); - if (!File.Exists(resolvedPath)) + resolvedFilename = GetShortcutFileNamePath(shortcutFilename); + } + else + { + Thread staThread = new Thread(new ParameterizedThreadStart(StaThreadMethod)); + void StaThreadMethod(object obj) { - resolvedPath = string.Empty; + resolvedFilename = GetShortcutFileNamePath(shortcutFilename); + } + staThread.SetApartmentState(ApartmentState.STA); + staThread.Start(shortcutFilename); + staThread.Join(); + } + + return resolvedFilename; + } + + + private static string GetShortcutFileNamePath(object shortcutFilename) + { + string resolvedFilename = string.Empty; + string pathOnly = Path.GetDirectoryName((string)shortcutFilename); + string filenameOnly = Path.GetFileName((string)shortcutFilename); + + Shell shell = new Shell(); + Folder folder = shell.NameSpace(pathOnly); + FolderItem folderItem = folder.ParseName(filenameOnly); + if (folderItem != null) + { + ShellLinkObject link = (ShellLinkObject)folderItem.GetLink; + if (string.IsNullOrEmpty(link.Path)) + { + resolvedFilename = link.Target.Path; + } + else + { + resolvedFilename = link.Path; } } - return resolvedPath; + + return resolvedFilename; } + // [Flags()] + // private enum SLGP_FLAGS + // { + // /// Retrieves the standard short (8.3 format) file name + // SLGP_SHORTPATH = 0x1, + // /// Retrieves the Universal Naming Convention (UNC) path name of the file + // SLGP_UNCPRIORITY = 0x2, + // /// Retrieves the raw path name. A raw path is something that might not exist and may include environment variables that need to be expanded + // SLGP_RAWPATH = 0x4 + // } + + // [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + // private struct WIN32_FIND_DATAW + // { + // public uint dwFileAttributes; + // public long ftCreationTime; + // public long ftLastAccessTime; + // public long ftLastWriteTime; + // public uint nFileSizeHigh; + // public uint nFileSizeLow; + // public uint dwReserved0; + // public uint dwReserved1; + // [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + // public string cFileName; + // [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] + // public string cAlternateFileName; + // } + + // [Flags()] + // private enum SLR_FLAGS + // { + // /// + // /// Do not display a dialog box if the link cannot be resolved. When SLR_NO_UI is set, + // /// the high-order word of fFlags can be set to a time-out value that specifies the + // /// maximum amount of time to be spent resolving the link. The function returns if the + // /// link cannot be resolved within the time-out duration. If the high-order word is set + // /// to zero, the time-out duration will be set to the default value of 3,000 milliseconds + // /// (3 seconds). To specify a value, set the high word of fFlags to the desired time-out + // /// duration, in milliseconds. + // /// + // SLR_NO_UI = 0x1, + // /// Obsolete and no longer used + // SLR_ANY_MATCH = 0x2, + // /// If the link object has changed, update its path and list of identifiers. + // /// If SLR_UPDATE is set, you do not need to call IPersistFile::IsDirty to determine + // /// whether or not the link object has changed. + // SLR_UPDATE = 0x4, + // /// Do not update the link information + // SLR_NOUPDATE = 0x8, + // /// Do not execute the search heuristics + // SLR_NOSEARCH = 0x10, + // /// Do not use distributed link tracking + // SLR_NOTRACK = 0x20, + // /// Disable distributed link tracking. By default, distributed link tracking tracks + // /// removable media across multiple devices based on the volume name. It also uses the + // /// Universal Naming Convention (UNC) path to track remote file systems whose drive letter + // /// has changed. Setting SLR_NOLINKINFO disables both types of tracking. + // SLR_NOLINKINFO = 0x40, + // /// Call the Microsoft Windows Installer + // SLR_INVOKE_MSI = 0x80 + // } + + // /// The IShellLink interface allows Shell links to be created, modified, and resolved + // [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")] + // private interface IShellLinkW + // { + // /// Retrieves the path and file name of a Shell link object + // void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags); + // /// Retrieves the list of item identifiers for a Shell link object + // void GetIDList(out IntPtr ppidl); + // /// Sets the pointer to an item identifier list (PIDL) for a Shell link object. + // void SetIDList(IntPtr pidl); + // /// Retrieves the description string for a Shell link object + // void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); + // /// Sets the description for a Shell link object. The description can be any application-defined string + // void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + // /// Retrieves the name of the working directory for a Shell link object + // void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); + // /// Sets the name of the working directory for a Shell link object + // void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + // /// Retrieves the command-line arguments associated with a Shell link object + // void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); + // /// Sets the command-line arguments for a Shell link object + // void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + // /// Retrieves the hot key for a Shell link object + // void GetHotkey(out short pwHotkey); + // /// Sets a hot key for a Shell link object + // void SetHotkey(short wHotkey); + // /// Retrieves the show command for a Shell link object + // void GetShowCmd(out int piShowCmd); + // /// Sets the show command for a Shell link object. The show command sets the initial show state of the window. + // void SetShowCmd(int iShowCmd); + // /// Retrieves the location (path and index) of the icon for a Shell link object + // void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + // int cchIconPath, out int piIcon); + // /// Sets the location (path and index) of the icon for a Shell link object + // void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + // /// Sets the relative path to the Shell link object + // void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); + // /// Attempts to find the target of a Shell link, even if it has been moved or renamed + // void Resolve(IntPtr hwnd, SLR_FLAGS fFlags); + // /// Sets the path and file name of a Shell link object + // void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + + // } + + // [ComImport, Guid("0000010c-0000-0000-c000-000000000046"), + // InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + // public interface IPersist + // { + // [PreserveSig] + // void GetClassID(out Guid pClassID); + // } + + // [ComImport, Guid("0000010b-0000-0000-C000-000000000046"), + // InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + // public interface IPersistFile : IPersist + // { + // new void GetClassID(out Guid pClassID); + + // [PreserveSig] + // int IsDirty(); + + // [PreserveSig] + // void Load([In, MarshalAs(UnmanagedType.LPWStr)] + // string pszFileName, uint dwMode); + + // [PreserveSig] + // void Save([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName, + // [In, MarshalAs(UnmanagedType.Bool)] bool fRemember); + + // [PreserveSig] + // void SaveCompleted([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName); + + // [PreserveSig] + // void GetCurFile([In, MarshalAs(UnmanagedType.LPWStr)] string ppszFileName); + // } + + // private const uint STGM_READ = 0; + // private const int MAX_PATH = 260; + + // // CLSID_ShellLink from ShlGuid.h + // [ + // ComImport(), + // Guid("00021401-0000-0000-C000-000000000046") + // ] + // public class ShellLink + // { + // } + + // public static string ResolveShortcut(string filename) + // { + // ShellLink link = new ShellLink(); + //#pragma warning disable CA2010 // Always consume the value returned by methods marked with PreserveSigAttribute + // ((IPersistFile)link).Load(filename, STGM_READ); + //#pragma warning restore CA2010 // => Has no returned value => OK + // StringBuilder sb = new StringBuilder(MAX_PATH); + // WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); + // ((IShellLinkW)link).GetPath(sb, sb.Capacity, out data, 0); + // string resolvedPath = sb.ToString(); + // if (!IsDirectory(resolvedPath) && + // !File.Exists(resolvedPath)) + // { + // //For some lnk e.g. WinRar SkypeForBuisness + // //resolved path wrong to Program Files (x86) + //#warning Using Shell32 with this method only allow in single thread application. You have to include [STAThread] in main entry point. + // resolvedPath = ReplaceFirst(resolvedPath, + // @"\Program Files (x86)\", + // @"\Program Files\"); + // if (!File.Exists(resolvedPath)) + // { + // resolvedPath = string.Empty; + + // //Shell32.Folder folder = GetShell32NameSpaceFolder(filename as object); + + // //string name1 = string.Empty; + // //string path1 = string.Empty; + // //string description1 = string.Empty; + // //string working_dir1 = string.Empty; + // //string args1 = string.Empty; + + // ////string test2 = name1 + path1 + description1 + working_dir1 + args1 + test1; + + // //object[] args = new object[] { filename }; + // //if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) + // //{ + // // //string test1 = + // // GetShortcutInfo(filename); + // //} + // //else + // //{ + // // Thread staThread = new Thread(new ParameterizedThreadStart(GetShortcutInfo)); + // // staThread.SetApartmentState(ApartmentState.STA); + // // staThread.Start(args); + // // staThread.Join(); + // //} + // } + // } + // return resolvedPath; + // } + + // //public static Shell32.Folder GetShell32NameSpaceFolder(Object folder) + // //{ + // // Type shellAppType = Type.GetTypeFromProgID("Shell.Application"); + + // // Object shell = Activator.CreateInstance(shellAppType); + // // return (Shell32.Folder)shellAppType.InvokeMember("NameSpace", + // // System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { folder }, CultureInfo.InvariantCulture); + // //} + + // //TESTING + // // Get information about this link. + // // Return an error message if there's a problem. + // //static void GetShortcutInfo(object parameters) + // //{ + // // object[] args = (object[])parameters; + // // string full_name = (string)args[0]; + // // string name = ""; + // // string path = ""; + // // string descr = ""; + // // string working_dir = ""; + // // //string args = ""; + + // // try + // // { + // // // Make a Shell object. + // // Shell32.Shell shell = new Shell32.Shell(); + + // // // Get the shortcut's folder and name. + // // string shortcut_path = + // // full_name.Substring(0, full_name.LastIndexOf("\\")); + // // string shortcut_name = + // // full_name.Substring(full_name.LastIndexOf("\\") + 1); + // // if (!shortcut_name.EndsWith(".lnk")) + // // shortcut_name += ".lnk"; + + // // // Get the shortcut's folder. + // // Shell32.Folder shortcut_folder = + // // shell.NameSpace(shortcut_path); + + // // // Get the shortcut's file. + // // Shell32.FolderItem folder_item = + // // shortcut_folder.Items().Item(shortcut_name); + + // // if (folder_item == null) + // // { + // // //return "Cannot find shortcut file '" + full_name + "'"; + // // } + // // if (!folder_item.IsLink) + // // { + // // //return "File '" + full_name + "' isn't a shortcut."; + // // } + + // // // Display the shortcut's information. + // // Shell32.ShellLinkObject lnk = + // // (Shell32.ShellLinkObject)folder_item.GetLink; + // // name = folder_item.Name; + // // descr = lnk.Description; + // // path = lnk.Path; + // // working_dir = lnk.WorkingDirectory; + // // //args = lnk.Arguments; + // // //return ""; + // // } + // // catch (Exception ex) + // // { + // // //return ex.Message; + // // } + // //} + public static bool IsDirectory(string filePath) { bool isDirectory = false; @@ -198,15 +339,15 @@ namespace SystemTrayMenu.Utilities return isDirectory; } - public static string ReplaceFirst(string text, string search, string replace) - { - int pos = text.IndexOf(search, StringComparison.InvariantCulture); - if (pos < 0) - { - return text; - } - return text.Substring(0, pos) + replace + - text.Substring(pos + search.Length); - } + // public static string ReplaceFirst(string text, string search, string replace) + // { + // int pos = text.IndexOf(search, StringComparison.InvariantCulture); + // if (pos < 0) + // { + // return text; + // } + // return text.Substring(0, pos) + replace + + // text.Substring(pos + search.Length); + // } } } \ No newline at end of file