From 1d4820c946a965ea32294a46ab3de2c3d307e8df Mon Sep 17 00:00:00 2001 From: Markus Hofknecht Date: Thu, 20 Oct 2022 17:55:54 +0200 Subject: [PATCH] Not enough memory resources are available to process this command. (#442), version 1.3.1.9 --- Business/App.cs | 55 +- Business/KeyboardInput.cs | 60 +- Business/Menus.cs | 700 ++++++++++-------- Business/Program.cs | 54 +- Business/WaitLeave.cs | 3 +- Business/WaitToLoadMenu.cs | 5 +- Helpers/DgvMouseRow.cs | 21 +- Helpers/Fading.cs | 39 +- Helpers/JoystickHelper.cs | 6 + Helpers/KeyboardHook.cs | 10 +- Properties/AssemblyInfo.cs | 4 +- UserInterface/AppContextMenu.cs | 2 +- UserInterface/AppNotifyIcon.cs | 4 +- .../CustomScrollbar/CustomScrollbar.cs | 5 + .../FolderBrowseDialog/FolderDialog.cs | 5 + UserInterface/Menu.Designer.cs | 41 +- UserInterface/Menu.cs | 279 +++---- Utilities/AppRestart.cs | 3 +- Utilities/EventHandlerEmpty.cs | 8 - 19 files changed, 735 insertions(+), 569 deletions(-) delete mode 100644 Utilities/EventHandlerEmpty.cs diff --git a/Business/App.cs b/Business/App.cs index dfedba3..a07201f 100644 --- a/Business/App.cs +++ b/Business/App.cs @@ -24,27 +24,20 @@ namespace SystemTrayMenu public App() { AppRestart.BeforeRestarting += Dispose; - SystemEvents.DisplaySettingsChanged += (s, e) => SystemEvents_DisplaySettingsChanged(); + SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged; menus.LoadStarted += menuNotifyIcon.LoadingStart; menus.LoadStopped += menuNotifyIcon.LoadingStop; - menuNotifyIcon.Click += () => menus.SwitchOpenClose(true); + menuNotifyIcon.Click += MenuNotifyIcon_Click; menuNotifyIcon.OpenLog += Log.OpenLogFile; menus.MainPreload(); if (Properties.Settings.Default.ShowInTaskbar) { taskbarForm = new TaskbarForm(); - taskbarForm.FormClosed += (s, e) => Application.Exit(); - taskbarForm.Deactivate += (s, e) => SetStateNormal(); - taskbarForm.Resize += (s, e) => SetStateNormal(); + taskbarForm.FormClosed += TaskbarForm_FormClosed; + taskbarForm.Deactivate += SetStateNormal; + taskbarForm.Resize += SetStateNormal; taskbarForm.Activated += TasbkarItemActivated; - void TasbkarItemActivated(object sender, EventArgs e) - { - SetStateNormal(); - taskbarForm.Activate(); - taskbarForm.Focus(); - menus.SwitchOpenCloseByTaskbarItem(); - } } DllImports.NativeMethods.User32ShowInactiveTopmost(taskbarForm); @@ -65,27 +58,57 @@ namespace SystemTrayMenu } else { - taskbarForm?.Dispose(); - SystemEvents.DisplaySettingsChanged -= (s, e) => SystemEvents_DisplaySettingsChanged(); + AppRestart.BeforeRestarting -= Dispose; + SystemEvents.DisplaySettingsChanged -= SystemEvents_DisplaySettingsChanged; + menus.LoadStarted -= menuNotifyIcon.LoadingStart; + menus.LoadStopped -= menuNotifyIcon.LoadingStop; menus.Dispose(); + menuNotifyIcon.Click -= MenuNotifyIcon_Click; + menuNotifyIcon.OpenLog -= Log.OpenLogFile; menuNotifyIcon.Dispose(); + if (taskbarForm != null) + { + taskbarForm.FormClosed -= TaskbarForm_FormClosed; + taskbarForm.Deactivate -= SetStateNormal; + taskbarForm.Resize -= SetStateNormal; + taskbarForm.Activated -= TasbkarItemActivated; + taskbarForm.Dispose(); + } } } - private void SystemEvents_DisplaySettingsChanged() + private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e) { menus.ReAdjustSizeAndLocation(); } + private void MenuNotifyIcon_Click() + { + menus.SwitchOpenClose(true); + } + + private void TaskbarForm_FormClosed(object sender, FormClosedEventArgs e) + { + Application.Exit(); + } + /// /// This ensures that next click on taskbaritem works as activate event/click event. /// - private void SetStateNormal() + private void SetStateNormal(object sender, EventArgs e) { if (Form.ActiveForm == taskbarForm) { taskbarForm.WindowState = FormWindowState.Normal; } } + + private void TasbkarItemActivated(object sender, EventArgs e) + { + SetStateNormal(sender, e); + taskbarForm.Activate(); + taskbarForm.Focus(); + menus.SwitchOpenCloseByTaskbarItem(); + } } } \ No newline at end of file diff --git a/Business/KeyboardInput.cs b/Business/KeyboardInput.cs index 6c52848..bbc56ab 100644 --- a/Business/KeyboardInput.cs +++ b/Business/KeyboardInput.cs @@ -27,33 +27,34 @@ namespace SystemTrayMenu.Handler this.menus = menus; } - internal event EventHandlerEmpty HotKeyPressed; + public event Action HotKeyPressed; - internal event EventHandlerEmpty ClosePressed; + public event Action ClosePressed; - internal event Action RowSelected; + public event Action RowSelected; - internal event Action RowDeselected; + public event Action RowDeselected; - internal event Action EnterPressed; + public event Action EnterPressed; - internal event EventHandlerEmpty Cleared; + public event Action Cleared; - internal bool InUse { get; set; } + public bool InUse { get; set; } public void Dispose() { + hook.KeyPressed -= Hook_KeyPressed; hook.Dispose(); } - internal void RegisterHotKey() + public void RegisterHotKey() { if (!string.IsNullOrEmpty(Properties.Settings.Default.HotKey)) { try { hook.RegisterHotKey(); - hook.KeyPressed += (sender, e) => HotKeyPressed?.Invoke(); + hook.KeyPressed += Hook_KeyPressed; } catch (InvalidOperationException ex) { @@ -64,13 +65,13 @@ namespace SystemTrayMenu.Handler } } - internal void ResetSelectedByKey() + public void ResetSelectedByKey() { iRowKey = -1; iMenuKey = 0; } - internal void CmdKeyProcessed(object sender, Keys keys) + public void CmdKeyProcessed(object sender, Keys keys) { sender ??= menus[iMenuKey]; @@ -171,12 +172,12 @@ namespace SystemTrayMenu.Handler } } - internal void SearchTextChanging() + public void SearchTextChanging() { ClearIsSelectedByKey(); } - internal void SearchTextChanged(Menu menu, bool isSearchStringEmpty) + public void SearchTextChanged(Menu menu, bool isSearchStringEmpty) { DataGridView dgv = menu.GetDataGridView(); if (isSearchStringEmpty) @@ -189,12 +190,12 @@ namespace SystemTrayMenu.Handler } } - internal void ClearIsSelectedByKey() + public void ClearIsSelectedByKey() { ClearIsSelectedByKey(iMenuKey, iRowKey); } - internal void Select(DataGridView dgv, int i, bool refreshview) + public void Select(DataGridView dgv, int i, bool refreshview) { int newiMenuKey = ((Menu)dgv.TopLevelControl).Level; if (i != iRowKey || newiMenuKey != iMenuKey) @@ -222,6 +223,11 @@ namespace SystemTrayMenu.Handler } } + private void Hook_KeyPressed(object sender, KeyPressedEventArgs e) + { + HotKeyPressed?.Invoke(); + } + private bool IsAnyMenuSelectedByKey( ref DataGridView dgv, ref Menu menuFromSelected, @@ -307,7 +313,7 @@ namespace SystemTrayMenu.Handler } else { - RowDeselected(iRowBefore, dgvBefore); + RowDeselected(dgvBefore, iRowBefore); SelectRow(dgv, iRowKey); EnterPressed.Invoke(dgv, iRowKey); } @@ -318,7 +324,7 @@ namespace SystemTrayMenu.Handler if (SelectMatchedReverse(dgv, iRowKey) || SelectMatchedReverse(dgv, dgv.Rows.Count - 1)) { - RowDeselected(iRowBefore, dgvBefore); + RowDeselected(dgvBefore, iRowBefore); SelectRow(dgv, iRowKey); toClear = true; } @@ -328,7 +334,7 @@ namespace SystemTrayMenu.Handler if (SelectMatched(dgv, iRowKey) || SelectMatched(dgv, 0)) { - RowDeselected(iRowBefore, dgvBefore); + RowDeselected(dgvBefore, iRowBefore); SelectRow(dgv, iRowKey); toClear = true; } @@ -337,7 +343,7 @@ namespace SystemTrayMenu.Handler case Keys.Home: if (SelectMatched(dgv, 0)) { - RowDeselected(iRowBefore, dgvBefore); + RowDeselected(dgvBefore, iRowBefore); SelectRow(dgv, iRowKey); toClear = true; } @@ -346,7 +352,7 @@ namespace SystemTrayMenu.Handler case Keys.End: if (SelectMatchedReverse(dgv, dgv.Rows.Count - 1)) { - RowDeselected(iRowBefore, dgvBefore); + RowDeselected(dgvBefore, iRowBefore); SelectRow(dgv, iRowKey); toClear = true; } @@ -380,7 +386,7 @@ namespace SystemTrayMenu.Handler break; case Keys.Escape: case Keys.Alt | Keys.F4: - RowDeselected(iRowBefore, dgvBefore); + RowDeselected(dgvBefore, iRowBefore); iMenuKey = 0; iRowKey = -1; toClear = true; @@ -392,7 +398,7 @@ namespace SystemTrayMenu.Handler if (SelectMatched(dgv, iRowKey, keyInput) || SelectMatched(dgv, 0, keyInput)) { - RowDeselected(iRowBefore, null); + RowDeselected(null, iRowBefore); SelectRow(dgv, iRowKey); toClear = true; } @@ -402,7 +408,7 @@ namespace SystemTrayMenu.Handler if (SelectMatched(dgv, iRowKey, keyInput) || SelectMatched(dgv, 0, keyInput)) { - RowDeselected(iRowBefore, null); + RowDeselected(null, iRowBefore); SelectRow(dgv, iRowKey); } else @@ -434,7 +440,7 @@ namespace SystemTrayMenu.Handler if (SelectMatched(dgv, dgv.SelectedRows[0].Index) || SelectMatched(dgv, 0)) { - RowDeselected(iRowBefore, dgvBefore); + RowDeselected(dgvBefore, iRowBefore); SelectRow(dgv, iRowKey); toClear = true; } @@ -442,7 +448,7 @@ namespace SystemTrayMenu.Handler } else { - RowDeselected(iRowBefore, dgvBefore); + RowDeselected(dgvBefore, iRowBefore); iMenuKey = 0; iRowKey = -1; toClear = true; @@ -466,7 +472,7 @@ namespace SystemTrayMenu.Handler if (SelectMatched(dgv, iRowKey) || SelectMatched(dgv, 0)) { - RowDeselected(iRowBefore, dgvBefore); + RowDeselected(dgvBefore, iRowBefore); SelectRow(dgv, iRowKey); toClear = true; } @@ -483,7 +489,7 @@ namespace SystemTrayMenu.Handler if (SelectMatched(dgv, iRowKey) || SelectMatched(dgv, 0)) { - RowDeselected(iRowBefore, dgvBefore); + RowDeselected(dgvBefore, iRowBefore); SelectRow(dgv, iRowKey); toClear = true; } diff --git a/Business/Menus.cs b/Business/Menus.cs index bbb49e9..f92080a 100644 --- a/Business/Menus.cs +++ b/Business/Menus.cs @@ -12,6 +12,7 @@ namespace SystemTrayMenu.Business using System.IO; using System.Linq; using System.Windows.Forms; + using SharpDX.DirectInput; using SystemTrayMenu.DataClasses; using SystemTrayMenu.DllImports; using SystemTrayMenu.Handler; @@ -19,6 +20,7 @@ namespace SystemTrayMenu.Business using SystemTrayMenu.Helpers; using SystemTrayMenu.UserInterface; using SystemTrayMenu.Utilities; + using static System.Windows.Forms.VisualStyles.VisualStyleElement; using Menu = SystemTrayMenu.UserInterface.Menu; using Timer = System.Windows.Forms.Timer; @@ -53,275 +55,33 @@ namespace SystemTrayMenu.Business workerMainMenu.WorkerSupportsCancellation = true; workerMainMenu.DoWork += LoadMenu; workerMainMenu.RunWorkerCompleted += LoadMainMenuCompleted; - void LoadMainMenuCompleted(object sender, RunWorkerCompletedEventArgs e) - { - keyboardInput.ResetSelectedByKey(); - LoadStopped(); - - if (e.Result == null) - { - // Clean up menu status IsMenuOpen for previous one - DataGridView dgvMainMenu = menus[0].GetDataGridView(); - foreach (DataRow row in ((DataTable)dgvMainMenu.DataSource).Rows) - { - RowData rowDataToClear = (RowData)row[2]; - rowDataToClear.IsMenuOpen = false; - rowDataToClear.IsClicking = false; - rowDataToClear.IsSelected = false; - rowDataToClear.IsContextMenuOpen = false; - } - - RefreshSelection(dgvMainMenu); - - if (Properties.Settings.Default.AppearAtMouseLocation) - { - menus[0].Tag = null; - } - - AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); }); - } - else - { - MenuData menuData = (MenuData)e.Result; - switch (menuData.Validity) - { - case MenuDataValidity.Valid: - if (IconReader.MainPreload) - { - workerMainMenu.DoWork -= LoadMenu; - menus[0] = Create(menuData, new DirectoryInfo(Config.Path).Name); - menus[0].HandleCreated += (s, e) => ExecuteWatcherHistory(); - Scaling.CalculateFactorByDpi(menus[0].GetDataGridView().CreateGraphics()); - IconReader.MainPreload = false; - if (showMenuAfterMainPreload) - { - AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); }); - } - } - else - { - AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); }); - } - - break; - case MenuDataValidity.Empty: - MessageBox.Show(Translator.GetText("Your root directory for the app does not exist or is empty! Change the root directory or put some files, directories or shortcuts into the root directory.")); - OpenFolder(); - Config.SetFolderByUser(); - AppRestart.ByConfigChange(); - break; - case MenuDataValidity.NoAccess: - MessageBox.Show(Translator.GetText("You have no access to the root directory of the app. Grant access to the directory or change the root directory.")); - OpenFolder(); - Config.SetFolderByUser(); - AppRestart.ByConfigChange(); - break; - case MenuDataValidity.Undefined: - Log.Info($"{nameof(MenuDataValidity)}.{nameof(MenuDataValidity.Undefined)}"); - break; - default: - break; - } - } - - openCloseState = OpenCloseState.Default; - } - waitToOpenMenu.StopLoadMenu += WaitToOpenMenu_StopLoadMenu; - void WaitToOpenMenu_StopLoadMenu() - { - foreach (BackgroundWorker workerSubMenu in workersSubMenu. - Where(w => w.IsBusy)) - { - workerSubMenu.CancelAsync(); - } - - LoadStopped(); - } - waitToOpenMenu.StartLoadMenu += StartLoadMenu; - void StartLoadMenu(RowData rowData) - { - if (menus[0].IsUsable && - (menus[rowData.Level + 1] == null || - menus[rowData.Level + 1].Tag as RowData != rowData)) - { - CreateAndShowLoadingMenu(rowData); - void CreateAndShowLoadingMenu(RowData rowData) - { - MenuData menuDataLoading = new(rowData.Level + 1) - { - Validity = MenuDataValidity.Valid, - }; - - Menu menuLoading = Create(menuDataLoading, new DirectoryInfo(rowData.Path).Name); - menuLoading.IsLoadingMenu = true; - AdjustMenusSizeAndLocation(); - menus[rowData.Level + 1] = menuLoading; - menuLoading.Tag = menuDataLoading.RowDataParent = rowData; - menuDataLoading.RowDataParent.SubMenu = menuLoading; - menuLoading.SetTypeLoading(); - ShowSubMenu(menuLoading); - } - - BackgroundWorker workerSubMenu = workersSubMenu. - Where(w => !w.IsBusy).FirstOrDefault(); - if (workerSubMenu == null) - { - workerSubMenu = new BackgroundWorker - { - WorkerSupportsCancellation = true, - }; - workerSubMenu.DoWork += LoadMenu; - workerSubMenu.RunWorkerCompleted += LoadSubMenuCompleted; - workersSubMenu.Add(workerSubMenu); - } - - workerSubMenu.RunWorkerAsync(rowData); - } - - void LoadSubMenuCompleted(object senderCompleted, RunWorkerCompletedEventArgs e) - { - MenuData menuData = (MenuData)e.Result; - - Menu menuLoading = menus[menuData.Level]; - string userSearchText = string.Empty; - bool closedLoadingMenu = false; - if (menuLoading != null && menuLoading.IsLoadingMenu) - { - menuLoading.HideWithFade(); - userSearchText = menuLoading.GetSearchText(); - menus[menuLoading.Level] = null; - closedLoadingMenu = true; - } - - if (menuData.Validity != MenuDataValidity.Undefined && - menus[0].IsUsable) - { - Menu menu = Create(menuData); - switch (menuData.Validity) - { - case MenuDataValidity.Valid: - menu.SetTypeSub(); - break; - case MenuDataValidity.Empty: - menu.SetTypeEmpty(); - break; - case MenuDataValidity.NoAccess: - menu.SetTypeNoAccess(); - break; - } - - menu.Tag = menuData.RowDataParent; - menuData.RowDataParent.SubMenu = menu; - if (menus[0].IsUsable) - { - ShowSubMenu(menu); - menu.SetSearchText(userSearchText); - } - } - else if (closedLoadingMenu && menus[0].IsUsable) - { - menuData.RowDataParent.IsMenuOpen = false; - menuData.RowDataParent.IsClicking = false; - menuData.RowDataParent.IsSelected = false; - Menu menuPrevious = menus[menuData.Level - 1]; - if (menuPrevious != null) - { - RefreshSelection(menuPrevious.GetDataGridView()); - } - } - } - } - waitToOpenMenu.CloseMenu += CloseMenu; - void CloseMenu(int level) - { - if (level < menus.Length && menus[level] != null) - { - HideOldMenu(menus[level]); - } - - if (level - 1 < menus.Length && menus[level - 1] != null) - { - menus[level - 1].FocusTextBox(); - } - } - waitToOpenMenu.MouseEnterOk += MouseEnterOk; dgvMouseRow.RowMouseEnter += waitToOpenMenu.MouseEnter; dgvMouseRow.RowMouseLeave += waitToOpenMenu.MouseLeave; dgvMouseRow.RowMouseLeave += Dgv_RowMouseLeave; - keyboardInput = new(menus); keyboardInput.RegisterHotKey(); - keyboardInput.HotKeyPressed += () => SwitchOpenClose(false); + keyboardInput.HotKeyPressed += KeyboardInput_HotKeyPressed; keyboardInput.ClosePressed += MenusFadeOut; keyboardInput.RowDeselected += waitToOpenMenu.RowDeselected; keyboardInput.EnterPressed += waitToOpenMenu.EnterOpensInstantly; keyboardInput.RowSelected += waitToOpenMenu.RowSelected; keyboardInput.RowSelected += AdjustScrollbarToDisplayedRow; - void AdjustScrollbarToDisplayedRow(DataGridView dgv, int index) - { - Menu menu = (Menu)dgv.FindForm(); - menu.AdjustScrollbar(); - } - joystickHelper = new(); - joystickHelper.KeyPressed += (key) => menus[0].Invoke(keyboardInput.CmdKeyProcessed, null, key); - + joystickHelper.KeyPressed += JoystickHelper_KeyPressed; timerShowProcessStartedAsLoadingIcon.Interval = Properties.Settings.Default.TimeUntilClosesAfterEnterPressed; timerStillActiveCheck.Interval = Properties.Settings.Default.TimeUntilClosesAfterEnterPressed + 20; - timerStillActiveCheck.Tick += (sender, e) => StillActiveTick(); - void StillActiveTick() - { - if (!IsActive()) - { - FadeHalfOrOutIfNeeded(); - } - - timerStillActiveCheck.Stop(); - } - + timerStillActiveCheck.Tick += TimerStillActiveCheck_Tick; 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() - { - Path = path, - NotifyFilter = NotifyFilters.Attributes | - NotifyFilters.DirectoryName | - NotifyFilters.FileName | - NotifyFilters.LastWrite, - Filter = "*.*", - }; - watcher.Created += WatcherProcessItem; - watcher.Deleted += WatcherProcessItem; - watcher.Renamed += WatcherProcessItem; - watcher.Changed += WatcherProcessItem; - watcher.IncludeSubdirectories = recursiv; - watcher.EnableRaisingEvents = true; - watchers.Add(watcher); - } - catch (Exception ex) - { - Log.Warn($"Failed to {nameof(CreateWatcher)}: {path}", ex); - } - } + CreateWatchers(); } - internal event EventHandlerEmpty LoadStarted; + public event Action LoadStarted; - internal event EventHandlerEmpty LoadStopped; + public event Action LoadStopped; private enum OpenCloseState { @@ -334,34 +94,7 @@ namespace SystemTrayMenu.Business private List AsList => AsEnumerable.ToList(); - public void Dispose() - { - workerMainMenu.Dispose(); - foreach (BackgroundWorker worker in workersSubMenu) - { - worker.Dispose(); - } - - waitToOpenMenu.Dispose(); - keyboardInput.Dispose(); - joystickHelper.Dispose(); - timerShowProcessStartedAsLoadingIcon.Dispose(); - timerStillActiveCheck.Dispose(); - waitLeave.Dispose(); - IconReader.Dispose(); - DisposeMenu(menus[0]); - dgvMouseRow.Dispose(); - foreach (FileSystemWatcher watcher in watchers) - { - watcher.Created -= WatcherProcessItem; - watcher.Deleted -= WatcherProcessItem; - watcher.Renamed -= WatcherProcessItem; - watcher.Changed -= WatcherProcessItem; - watcher.Dispose(); - } - } - - internal static MenuData GetData(BackgroundWorker worker, string path, int level) + public static MenuData GetData(BackgroundWorker worker, string path, int level) { MenuData menuData = new(level); if (worker?.CancellationPending == true || string.IsNullOrEmpty(path)) @@ -392,18 +125,65 @@ namespace SystemTrayMenu.Business return menuData; } - internal void SwitchOpenCloseByTaskbarItem() + public void Dispose() + { + workerMainMenu.DoWork -= LoadMenu; + workerMainMenu.RunWorkerCompleted -= LoadMainMenuCompleted; + workerMainMenu.Dispose(); + dgvMouseRow.RowMouseEnter -= waitToOpenMenu.MouseEnter; + dgvMouseRow.RowMouseLeave -= waitToOpenMenu.MouseLeave; + dgvMouseRow.RowMouseLeave -= Dgv_RowMouseLeave; + keyboardInput.HotKeyPressed -= KeyboardInput_HotKeyPressed; + keyboardInput.ClosePressed -= MenusFadeOut; + keyboardInput.RowDeselected -= waitToOpenMenu.RowDeselected; + keyboardInput.EnterPressed -= waitToOpenMenu.EnterOpensInstantly; + keyboardInput.RowSelected -= waitToOpenMenu.RowSelected; + keyboardInput.RowSelected -= AdjustScrollbarToDisplayedRow; + keyboardInput.Dispose(); + waitToOpenMenu.StopLoadMenu -= WaitToOpenMenu_StopLoadMenu; + waitToOpenMenu.StartLoadMenu -= StartLoadMenu; + waitToOpenMenu.CloseMenu -= CloseMenu; + waitToOpenMenu.MouseEnterOk -= MouseEnterOk; + waitToOpenMenu.Dispose(); + joystickHelper.KeyPressed -= JoystickHelper_KeyPressed; + joystickHelper.Dispose(); + timerShowProcessStartedAsLoadingIcon.Dispose(); + timerStillActiveCheck.Tick -= TimerStillActiveCheck_Tick; + timerStillActiveCheck.Dispose(); + waitLeave.LeaveTriggered -= FadeHalfOrOutIfNeeded; + waitLeave.Dispose(); + foreach (BackgroundWorker workerSubMenu in workersSubMenu) + { + workerSubMenu.DoWork -= LoadMenu; + workerSubMenu.RunWorkerCompleted -= LoadSubMenuCompleted; + workerSubMenu.Dispose(); + } + + IconReader.Dispose(); + dgvMouseRow.Dispose(); + DisposeMenu(menus[0]); + foreach (FileSystemWatcher watcher in watchers) + { + watcher.Created -= WatcherProcessItem; + watcher.Deleted -= WatcherProcessItem; + watcher.Renamed -= WatcherProcessItem; + watcher.Changed -= WatcherProcessItem; + watcher.Dispose(); + } + } + + public void SwitchOpenCloseByTaskbarItem() { SwitchOpenClose(true); timerStillActiveCheck.Start(); } - internal bool IsOpenCloseStateOpening() + public bool IsOpenCloseStateOpening() { return openCloseState == OpenCloseState.Opening; } - internal void SwitchOpenClose(bool byClick, bool isMainPreload = false) + public void SwitchOpenClose(bool byClick, bool isMainPreload = false) { // Ignore open close events during main preload #248 if (IconReader.MainPreload && !isMainPreload) @@ -448,13 +228,18 @@ namespace SystemTrayMenu.Business { if (menuToDispose != null) { + menuToDispose.UserClickedOpenFolder -= OpenFolder; menuToDispose.MouseWheel -= AdjustMenusSizeAndLocation; menuToDispose.MouseLeave -= waitLeave.Start; menuToDispose.MouseEnter -= waitLeave.Stop; menuToDispose.CmdKeyProcessed -= keyboardInput.CmdKeyProcessed; - menuToDispose.SearchTextChanging -= keyboardInput.SearchTextChanging; menuToDispose.KeyPressCheck -= Menu_KeyPressCheck; + menuToDispose.SearchTextChanging -= Menu_SearchTextChanging; menuToDispose.SearchTextChanged -= Menu_SearchTextChanged; + menuToDispose.UserDragsMenu -= Menu_UserDragsMenu; + menuToDispose.Activated -= Menu_Activated; + menuToDispose.Deactivate -= Menu_Deactivate; + menuToDispose.VisibleChanged -= MenuVisibleChanged; DataGridView dgv = menuToDispose.GetDataGridView(); if (dgv != null) { @@ -470,6 +255,7 @@ namespace SystemTrayMenu.Business dgv.MouseDoubleClick -= Dgv_MouseDoubleClick; dgv.SelectionChanged -= Dgv_SelectionChanged; dgv.RowPostPaint -= Dgv_RowPostPaint; + dgv.DataError -= Dgv_DataError; dgv.ClearSelection(); foreach (DataGridViewRow row in dgv.Rows) @@ -477,6 +263,9 @@ namespace SystemTrayMenu.Business RowData rowData = (RowData)row.Cells[2].Value; DisposeMenu(rowData.SubMenu); } + + DataTable dataTable = (DataTable)dgv.DataSource; + dataTable.Dispose(); } menuToDispose.Dispose(); @@ -620,6 +409,197 @@ namespace SystemTrayMenu.Business } } + private void LoadMainMenuCompleted(object sender, RunWorkerCompletedEventArgs e) + { + keyboardInput.ResetSelectedByKey(); + LoadStopped(); + + if (e.Result == null) + { + // Clean up menu status IsMenuOpen for previous one + DataGridView dgvMainMenu = menus[0].GetDataGridView(); + foreach (DataRow row in ((DataTable)dgvMainMenu.DataSource).Rows) + { + RowData rowDataToClear = (RowData)row[2]; + rowDataToClear.IsMenuOpen = false; + rowDataToClear.IsClicking = false; + rowDataToClear.IsSelected = false; + rowDataToClear.IsContextMenuOpen = false; + } + + RefreshSelection(dgvMainMenu); + + if (Properties.Settings.Default.AppearAtMouseLocation) + { + menus[0].Tag = null; + } + + AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); }); + } + else + { + MenuData menuData = (MenuData)e.Result; + switch (menuData.Validity) + { + case MenuDataValidity.Valid: + if (IconReader.MainPreload) + { + workerMainMenu.DoWork -= LoadMenu; + menus[0] = Create(menuData, new DirectoryInfo(Config.Path).Name); + menus[0].HandleCreated += (s, e) => ExecuteWatcherHistory(); + Scaling.CalculateFactorByDpi(menus[0].GetDataGridView().CreateGraphics()); + IconReader.MainPreload = false; + if (showMenuAfterMainPreload) + { + AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); }); + } + } + else + { + AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); }); + } + + break; + case MenuDataValidity.Empty: + MessageBox.Show(Translator.GetText("Your root directory for the app does not exist or is empty! Change the root directory or put some files, directories or shortcuts into the root directory.")); + OpenFolder(); + Config.SetFolderByUser(); + AppRestart.ByConfigChange(); + break; + case MenuDataValidity.NoAccess: + MessageBox.Show(Translator.GetText("You have no access to the root directory of the app. Grant access to the directory or change the root directory.")); + OpenFolder(); + Config.SetFolderByUser(); + AppRestart.ByConfigChange(); + break; + case MenuDataValidity.Undefined: + Log.Info($"{nameof(MenuDataValidity)}.{nameof(MenuDataValidity.Undefined)}"); + break; + default: + break; + } + } + + openCloseState = OpenCloseState.Default; + } + + private void WaitToOpenMenu_StopLoadMenu() + { + foreach (BackgroundWorker workerSubMenu in workersSubMenu.Where(w => w.IsBusy)) + { + workerSubMenu.CancelAsync(); + } + + LoadStopped(); + } + + private void StartLoadMenu(RowData rowData) + { + if (menus[0].IsUsable && + (menus[rowData.Level + 1] == null || + menus[rowData.Level + 1].Tag as RowData != rowData)) + { + CreateAndShowLoadingMenu(rowData); + void CreateAndShowLoadingMenu(RowData rowData) + { + MenuData menuDataLoading = new(rowData.Level + 1) + { + Validity = MenuDataValidity.Valid, + }; + + Menu menuLoading = Create(menuDataLoading, new DirectoryInfo(rowData.Path).Name); + menuLoading.IsLoadingMenu = true; + AdjustMenusSizeAndLocation(); + menus[rowData.Level + 1] = menuLoading; + menuLoading.Tag = menuDataLoading.RowDataParent = rowData; + menuDataLoading.RowDataParent.SubMenu = menuLoading; + menuLoading.SetTypeLoading(); + ShowSubMenu(menuLoading); + } + + BackgroundWorker workerSubMenu = workersSubMenu. + Where(w => !w.IsBusy).FirstOrDefault(); + if (workerSubMenu == null) + { + workerSubMenu = new BackgroundWorker() + { + WorkerSupportsCancellation = true, + }; + workerSubMenu.DoWork += LoadMenu; + workerSubMenu.RunWorkerCompleted += LoadSubMenuCompleted; + workersSubMenu.Add(workerSubMenu); + } + + workerSubMenu.RunWorkerAsync(rowData); + } + } + + private void CloseMenu(int level) + { + if (level < menus.Length && menus[level] != null) + { + HideOldMenu(menus[level]); + } + + if (level - 1 < menus.Length && menus[level - 1] != null) + { + menus[level - 1].FocusTextBox(); + } + } + + private void LoadSubMenuCompleted(object senderCompleted, RunWorkerCompletedEventArgs e) + { + MenuData menuData = (MenuData)e.Result; + + Menu menuLoading = menus[menuData.Level]; + string userSearchText = string.Empty; + bool closedLoadingMenu = false; + if (menuLoading != null && menuLoading.IsLoadingMenu) + { + menuLoading.HideWithFade(); + userSearchText = menuLoading.GetSearchText(); + menus[menuLoading.Level] = null; + closedLoadingMenu = true; + } + + if (menuData.Validity != MenuDataValidity.Undefined && + menus[0].IsUsable) + { + Menu menu = Create(menuData); + switch (menuData.Validity) + { + case MenuDataValidity.Valid: + menu.SetTypeSub(); + break; + case MenuDataValidity.Empty: + menu.SetTypeEmpty(); + break; + case MenuDataValidity.NoAccess: + menu.SetTypeNoAccess(); + break; + } + + menu.Tag = menuData.RowDataParent; + menuData.RowDataParent.SubMenu = menu; + if (menus[0].IsUsable) + { + ShowSubMenu(menu); + menu.SetSearchText(userSearchText); + } + } + else if (closedLoadingMenu && menus[0].IsUsable) + { + menuData.RowDataParent.IsMenuOpen = false; + menuData.RowDataParent.IsClicking = false; + menuData.RowDataParent.IsSelected = false; + Menu menuPrevious = menus[menuData.Level - 1]; + if (menuPrevious != null) + { + RefreshSelection(menuPrevious.GetDataGridView()); + } + } + } + private bool IsActive() { bool IsShellContextMenuOpen() @@ -653,22 +633,21 @@ namespace SystemTrayMenu.Business private Menu Create(MenuData menuData, string title = null) { Menu menu = new(); - - string path = Config.Path; + menu.Level = menuData.Level; + menu.Path = Config.Path; if (title == null) { title = new DirectoryInfo(menuData.RowDataParent.ResolvedPath).Name; - path = menuData.RowDataParent.ResolvedPath; + menu.Path = menuData.RowDataParent.ResolvedPath; } if (string.IsNullOrEmpty(title)) { - title = Path.GetPathRoot(path); + title = Path.GetPathRoot(menu.Path); } menu.AdjustControls(title, menuData.Validity); - menu.UserClickedOpenFolder += () => OpenFolder(path); - menu.Level = menuData.Level; + menu.UserClickedOpenFolder += OpenFolder; menu.MouseWheel += AdjustMenusSizeAndLocation; menu.MouseLeave += waitLeave.Start; menu.MouseEnter += waitLeave.Stop; @@ -677,43 +656,8 @@ namespace SystemTrayMenu.Business menu.SearchTextChanging += Menu_SearchTextChanging; menu.SearchTextChanged += Menu_SearchTextChanged; menu.UserDragsMenu += Menu_UserDragsMenu; - void Menu_UserDragsMenu() - { - if (menus[1] != null) - { - HideOldMenu(menus[1]); - } - } - - menu.Deactivate += Deactivate; - void Deactivate(object sender, EventArgs e) - { - if (IsOpenCloseStateOpening()) - { - Log.Info("Ignored Deactivate, because openCloseState == OpenCloseState.Opening"); - } - else if (!Properties.Settings.Default.StaysOpenWhenFocusLostAfterEnterPressed || - !waitingForReactivate) - { - FadeHalfOrOutIfNeeded(); - if (!IsActive()) - { - deactivatedTime = DateTime.Now; - } - } - } - - menu.Activated += (sender, e) => Activated(); - void Activated() - { - if (IsActive() && menus[0].IsUsable) - { - AsList.ForEach(m => m.ShowWithFade()); - timerStillActiveCheck.Stop(); - timerStillActiveCheck.Start(); - } - } - + menu.Activated += Menu_Activated; + menu.Deactivate += Menu_Deactivate; menu.VisibleChanged += MenuVisibleChanged; AddItemsToMenu(menuData.RowDatas, menu, out int foldersCount, out int filesCount); @@ -731,18 +675,47 @@ namespace SystemTrayMenu.Business dgv.SelectionChanged += Dgv_SelectionChanged; dgv.RowPostPaint += Dgv_RowPostPaint; dgv.DataError += Dgv_DataError; - void Dgv_DataError(object sender, DataGridViewDataErrorEventArgs e) - { - // WARN Dgv_DataError occured System.ObjectDisposedException: Cannot access a disposed object. Object name: 'Icon' - // => Rare times occured (e.g. when focused an close other application => closed and activated at same time) - Log.Warn("Dgv_DataError occured", e.Exception); - } menu.SetCounts(foldersCount, filesCount); return menu; } + private void Menu_UserDragsMenu() + { + if (menus[1] != null) + { + HideOldMenu(menus[1]); + } + } + + private void Menu_Activated(object sender, EventArgs e) + { + if (IsActive() && menus[0].IsUsable) + { + AsList.ForEach(m => m.ShowWithFade()); + timerStillActiveCheck.Stop(); + timerStillActiveCheck.Start(); + } + } + + private void Menu_Deactivate(object sender, EventArgs e) + { + if (IsOpenCloseStateOpening()) + { + Log.Info("Ignored Deactivate, because openCloseState == OpenCloseState.Opening"); + } + else if (!Properties.Settings.Default.StaysOpenWhenFocusLostAfterEnterPressed || + !waitingForReactivate) + { + FadeHalfOrOutIfNeeded(); + if (!IsActive()) + { + deactivatedTime = DateTime.Now; + } + } + } + private void MenuVisibleChanged(object sender, EventArgs e) { Menu menu = (Menu)sender; @@ -1058,6 +1031,13 @@ namespace SystemTrayMenu.Business } } + private void Dgv_DataError(object sender, DataGridViewDataErrorEventArgs e) + { + // WARN Dgv_DataError occured System.ObjectDisposedException: Cannot access a disposed object. Object name: 'Icon' + // => Rare times occured (e.g. when focused an close other application => closed and activated at same time) + Log.Warn("Dgv_DataError occured", e.Exception); + } + private void ShowSubMenu(Menu menuToShow) { HideOldMenu(menuToShow, true); @@ -1142,6 +1122,68 @@ namespace SystemTrayMenu.Business joystickHelper.Disable(); } + private void CreateWatchers() + { + 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() + { + Path = path, + NotifyFilter = NotifyFilters.Attributes | + NotifyFilters.DirectoryName | + NotifyFilters.FileName | + NotifyFilters.LastWrite, + Filter = "*.*", + }; + watcher.Created += WatcherProcessItem; + watcher.Deleted += WatcherProcessItem; + watcher.Renamed += WatcherProcessItem; + watcher.Changed += WatcherProcessItem; + watcher.IncludeSubdirectories = recursiv; + watcher.EnableRaisingEvents = true; + watchers.Add(watcher); + } + catch (Exception ex) + { + Log.Warn($"Failed to {nameof(CreateWatcher)}: {path}", ex); + } + } + } + + private void AdjustScrollbarToDisplayedRow(DataGridView dgv, int index) + { + Menu menu = (Menu)dgv.FindForm(); + menu.AdjustScrollbar(); + } + + private void JoystickHelper_KeyPressed(Keys keys) + { + menus[0].Invoke(keyboardInput.CmdKeyProcessed, null, keys); + } + + private void TimerStillActiveCheck_Tick(object sender, EventArgs e) + { + if (!IsActive()) + { + FadeHalfOrOutIfNeeded(); + } + + timerStillActiveCheck.Stop(); + } + + private void KeyboardInput_HotKeyPressed() + { + SwitchOpenClose(false); + } + private void AdjustMenusSizeAndLocation() { Rectangle screenBounds; diff --git a/Business/Program.cs b/Business/Program.cs index dd128f9..1a40dc1 100644 --- a/Business/Program.cs +++ b/Business/Program.cs @@ -28,7 +28,7 @@ namespace SystemTrayMenu { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - Application.ThreadException += (sender, e) => AskUserSendError(e.Exception); + Application.ThreadException += Application_ThreadException; Scaling.Initialize(); FolderOptions.Initialize(); @@ -40,6 +40,7 @@ namespace SystemTrayMenu } } + Application.ThreadException -= Application_ThreadException; Config.Dispose(); } catch (Exception ex) @@ -50,32 +51,37 @@ namespace SystemTrayMenu { Log.Close(); } + } - static void AskUserSendError(Exception ex) + private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) + { + AskUserSendError(e.Exception); + } + + private static void AskUserSendError(Exception ex) + { + Log.Error("Application Crashed", ex); + + DialogResult dialogResult = MessageBox.Show( + "A problem has been encountered and the application needs to restart. " + + "Reporting this error will help us make our product better. " + + "Press 'Yes' to open your standard email app (emailto: Markus@Hofknecht.eu). " + Environment.NewLine + + @"You can also create an issue manually here https://github.com/Hofknecht/SystemTrayMenu/issues" + Environment.NewLine + + "Press 'Cancel' to quit SystemTrayMenu.", + "SystemTrayMenu Crashed", + MessageBoxButtons.YesNoCancel); + + if (dialogResult == DialogResult.Yes) { - Log.Error("Application Crashed", ex); + Log.ProcessStart("mailto:" + "markus@hofknecht.eu" + + "?subject=SystemTrayMenu Bug reported " + + Assembly.GetEntryAssembly().GetName().Version + + "&body=" + ex.ToString()); + } - DialogResult dialogResult = MessageBox.Show( - "A problem has been encountered and the application needs to restart. " + - "Reporting this error will help us make our product better. " + - "Press 'Yes' to open your standard email app (emailto: Markus@Hofknecht.eu). " + Environment.NewLine + - @"You can also create an issue manually here https://github.com/Hofknecht/SystemTrayMenu/issues" + Environment.NewLine + - "Press 'Cancel' to quit SystemTrayMenu.", - "SystemTrayMenu Crashed", - MessageBoxButtons.YesNoCancel); - - if (dialogResult == DialogResult.Yes) - { - Log.ProcessStart("mailto:" + "markus@hofknecht.eu" + - "?subject=SystemTrayMenu Bug reported " + - Assembly.GetEntryAssembly().GetName().Version + - "&body=" + ex.ToString()); - } - - if (!isStartup && dialogResult != DialogResult.Cancel) - { - AppRestart.ByThreadException(); - } + if (!isStartup && dialogResult != DialogResult.Cancel) + { + AppRestart.ByThreadException(); } } } diff --git a/Business/WaitLeave.cs b/Business/WaitLeave.cs index 42641c2..82259e1 100644 --- a/Business/WaitLeave.cs +++ b/Business/WaitLeave.cs @@ -18,10 +18,11 @@ namespace SystemTrayMenu.Handler timerLeaveCheck.Tick += TimerLeaveCheckTick; } - public event EventHandlerEmpty LeaveTriggered; + public event Action LeaveTriggered; public void Dispose() { + timerLeaveCheck.Tick -= TimerLeaveCheckTick; timerLeaveCheck.Dispose(); } diff --git a/Business/WaitToLoadMenu.cs b/Business/WaitToLoadMenu.cs index 4c10df8..ef4c3c2 100644 --- a/Business/WaitToLoadMenu.cs +++ b/Business/WaitToLoadMenu.cs @@ -33,7 +33,7 @@ namespace SystemTrayMenu.Handler internal event Action CloseMenu; - internal event EventHandlerEmpty StopLoadMenu; + internal event Action StopLoadMenu; internal event Action MouseEnterOk; @@ -41,6 +41,7 @@ namespace SystemTrayMenu.Handler public void Dispose() { + timerStartLoad.Tick -= WaitStartLoad_Tick; timerStartLoad.Stop(); timerStartLoad.Dispose(); dgv?.Dispose(); @@ -92,7 +93,7 @@ namespace SystemTrayMenu.Handler } } - internal void RowDeselected(int rowIndex, DataGridView dgv) + internal void RowDeselected(DataGridView dgv, int rowIndex) { timerStartLoad.Stop(); StopLoadMenu?.Invoke(); diff --git a/Helpers/DgvMouseRow.cs b/Helpers/DgvMouseRow.cs index cf8f8e5..856ccfd 100644 --- a/Helpers/DgvMouseRow.cs +++ b/Helpers/DgvMouseRow.cs @@ -16,12 +16,12 @@ namespace SystemTrayMenu.Helper internal DgvMouseRow() { timerRaiseRowMouseLeave.Interval = 200; - timerRaiseRowMouseLeave.Tick += Elapsed; - void Elapsed(object sender, EventArgs e) - { - timerRaiseRowMouseLeave.Stop(); - TriggerRowMouseLeave(); - } + timerRaiseRowMouseLeave.Tick += TimerRaiseRowMouseLeave_Tick; + } + + ~DgvMouseRow() // the finalizer + { + Dispose(false); } internal event Action RowMouseEnter; @@ -75,11 +75,18 @@ namespace SystemTrayMenu.Helper { if (disposing) { + timerRaiseRowMouseLeave.Tick -= TimerRaiseRowMouseLeave_Tick; timerRaiseRowMouseLeave.Dispose(); - dgv?.Dispose(); + dgv = null; } } + private void TimerRaiseRowMouseLeave_Tick(object sender, EventArgs e) + { + timerRaiseRowMouseLeave.Stop(); + TriggerRowMouseLeave(); + } + private void TriggerRowMouseLeave() { if (dgv != null) diff --git a/Helpers/Fading.cs b/Helpers/Fading.cs index f34ec6d..2c8109f 100644 --- a/Helpers/Fading.cs +++ b/Helpers/Fading.cs @@ -28,14 +28,19 @@ namespace SystemTrayMenu.Helper internal Fading() { timer.Interval = Interval100FPS; - timer.Tick += (sender, e) => FadeStep(); + timer.Tick += Timer_Tick; } - internal event EventHandlerEmpty Hide; + ~Fading() // the finalizer + { + Dispose(false); + } - internal event EventHandlerEmpty Show; + internal event Action Hide; - internal event EventHandler ChangeOpacity; + internal event Action Show; + + internal event Action ChangeOpacity; internal enum FadingState { @@ -62,6 +67,7 @@ namespace SystemTrayMenu.Helper { if (disposing) { + timer.Tick -= Timer_Tick; timer.Dispose(); } } @@ -80,6 +86,11 @@ namespace SystemTrayMenu.Helper } } + private void Timer_Tick(object sender, EventArgs e) + { + FadeStep(); + } + private void FadeStep() { switch (state) @@ -90,13 +101,13 @@ namespace SystemTrayMenu.Helper visible = true; Show?.Invoke(); opacity = 0; - ChangeOpacity?.Invoke(this, opacity); + ChangeOpacity?.Invoke(opacity); } else if (Properties.Settings.Default.UseFading && opacity < ShownMinus) { opacity += StepIn; - ChangeOpacity?.Invoke(this, opacity); + ChangeOpacity?.Invoke(opacity); } else { @@ -104,11 +115,11 @@ namespace SystemTrayMenu.Helper { // #393 provoke a redraw for the CS_DROPSHADOW to work opacity = ShownMinus; - ChangeOpacity?.Invoke(this, opacity); + ChangeOpacity?.Invoke(opacity); } opacity = Shown; - ChangeOpacity?.Invoke(this, opacity); + ChangeOpacity?.Invoke(opacity); StartStopTimer(FadingState.Idle); } @@ -119,24 +130,24 @@ namespace SystemTrayMenu.Helper visible = true; Show?.Invoke(); opacity = 0; - ChangeOpacity?.Invoke(this, opacity); + ChangeOpacity?.Invoke(opacity); } else if (Properties.Settings.Default.UseFading && opacity < TransparentMinus) { opacity += StepIn; - ChangeOpacity?.Invoke(this, opacity); + ChangeOpacity?.Invoke(opacity); } else if (Properties.Settings.Default.UseFading && opacity > TransparentPlus) { opacity -= StepOut; - ChangeOpacity?.Invoke(this, opacity); + ChangeOpacity?.Invoke(opacity); } else { opacity = Transparent; - ChangeOpacity?.Invoke(this, opacity); + ChangeOpacity?.Invoke(opacity); StartStopTimer(FadingState.Idle); } @@ -146,12 +157,12 @@ namespace SystemTrayMenu.Helper opacity > StepOut) { opacity -= StepOut; - ChangeOpacity?.Invoke(this, opacity); + ChangeOpacity?.Invoke(opacity); } else if (visible) { opacity = 0; - ChangeOpacity?.Invoke(this, opacity); + ChangeOpacity?.Invoke(opacity); visible = false; Hide?.Invoke(); StartStopTimer(FadingState.Idle); diff --git a/Helpers/JoystickHelper.cs b/Helpers/JoystickHelper.cs index 5f82b84..5866cca 100644 --- a/Helpers/JoystickHelper.cs +++ b/Helpers/JoystickHelper.cs @@ -32,6 +32,11 @@ namespace SystemTrayMenu.Helpers } } + ~JoystickHelper() // the finalizer + { + Dispose(false); + } + public event Action KeyPressed; public void Enable() @@ -54,6 +59,7 @@ namespace SystemTrayMenu.Helpers { if (disposing) { + timerReadJoystick.Elapsed -= ReadJoystickLoop; timerReadJoystick?.Dispose(); joystick?.Dispose(); } diff --git a/Helpers/KeyboardHook.cs b/Helpers/KeyboardHook.cs index 3d7d87a..b27a340 100644 --- a/Helpers/KeyboardHook.cs +++ b/Helpers/KeyboardHook.cs @@ -13,7 +13,7 @@ namespace SystemTrayMenu.Helper /// The enumeration of possible modifiers. /// [Flags] - internal enum KeyboardHookModifierKeys : uint + public enum KeyboardHookModifierKeys : uint { None = 0, Alt = 1, @@ -30,7 +30,7 @@ namespace SystemTrayMenu.Helper public KeyboardHook() { // register the event of the inner native window. - window.KeyPressed += (sender, e) => KeyPressed?.Invoke(this, e); + window.KeyPressed += Window_KeyPressed; } /// @@ -47,6 +47,7 @@ namespace SystemTrayMenu.Helper } // dispose the inner native window. + window.KeyPressed -= Window_KeyPressed; window.Dispose(); } @@ -104,6 +105,11 @@ namespace SystemTrayMenu.Helper RegisterHotKey((uint)modifier, key); } + private void Window_KeyPressed(object sender, KeyPressedEventArgs e) + { + KeyPressed?.Invoke(this, e); + } + private void RegisterHotKey(uint modifier, Keys key) { currentId += 1; diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index d06a0c6..2677da3 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -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.3.1.7")] -[assembly: AssemblyFileVersion("1.3.1.7")] +[assembly: AssemblyVersion("1.3.1.9")] +[assembly: AssemblyFileVersion("1.3.1.9")] diff --git a/UserInterface/AppContextMenu.cs b/UserInterface/AppContextMenu.cs index 8aba5af..3c2db6d 100644 --- a/UserInterface/AppContextMenu.cs +++ b/UserInterface/AppContextMenu.cs @@ -15,7 +15,7 @@ namespace SystemTrayMenu.Helper internal class AppContextMenu { - public event EventHandlerEmpty ClickedOpenLog; + public event Action ClickedOpenLog; public ContextMenuStrip Create() { diff --git a/UserInterface/AppNotifyIcon.cs b/UserInterface/AppNotifyIcon.cs index 1f7d50e..93ddead 100644 --- a/UserInterface/AppNotifyIcon.cs +++ b/UserInterface/AppNotifyIcon.cs @@ -41,9 +41,9 @@ namespace SystemTrayMenu.UserInterface } } - public event EventHandlerEmpty Click; + public event Action Click; - public event EventHandlerEmpty OpenLog; + public event Action OpenLog; public void Dispose() { diff --git a/UserInterface/CustomScrollbar/CustomScrollbar.cs b/UserInterface/CustomScrollbar/CustomScrollbar.cs index 3e6c9cf..4e40b6c 100644 --- a/UserInterface/CustomScrollbar/CustomScrollbar.cs +++ b/UserInterface/CustomScrollbar/CustomScrollbar.cs @@ -204,6 +204,11 @@ namespace SystemTrayMenu.UserInterface protected override void Dispose(bool disposing) { + MouseDown -= CustomScrollbar_MouseDown; + MouseMove -= CustomScrollbar_MouseMove; + MouseUp -= CustomScrollbar_MouseUp; + MouseLeave -= CustomScrollbar_MouseLeave; + timerMouseStillClicked.Tick -= TimerMouseStillClicked_Tick; timerMouseStillClicked.Dispose(); base.Dispose(disposing); } diff --git a/UserInterface/FolderBrowseDialog/FolderDialog.cs b/UserInterface/FolderBrowseDialog/FolderDialog.cs index 7b62898..a8afdf8 100644 --- a/UserInterface/FolderBrowseDialog/FolderDialog.cs +++ b/UserInterface/FolderBrowseDialog/FolderDialog.cs @@ -14,6 +14,11 @@ namespace SystemTrayMenu.UserInterface.FolderBrowseDialog { private bool isDisposed; + ~FolderDialog() // the finalizer + { + Dispose(false); + } + /// /// Gets or sets /sets folder in which dialog will be open. /// diff --git a/UserInterface/Menu.Designer.cs b/UserInterface/Menu.Designer.cs index 3fb5e34..ddbe304 100644 --- a/UserInterface/Menu.Designer.cs +++ b/UserInterface/Menu.Designer.cs @@ -1,4 +1,7 @@ -namespace SystemTrayMenu.UserInterface +using System.Windows.Forms; +using SystemTrayMenu.Helper; + +namespace SystemTrayMenu.UserInterface { partial class Menu { @@ -19,9 +22,45 @@ } timerUpdateIcons.Stop(); + timerUpdateIcons.Tick -= TimerUpdateIcons_Tick; + timerUpdateIcons.Tick += TimerUpdateIcons_Tick_Loading; timerUpdateIcons.Dispose(); + fading.ChangeOpacity -= Fading_ChangeOpacity; + fading.Show -= Fading_Show; + fading.Hide -= Hide; fading.Dispose(); + dgv.GotFocus -= Dgv_GotFocus; + dgv.MouseEnter -= ControlsMouseEnter; + dgv.MouseLeave -= ControlsMouseLeave; + customScrollbar.GotFocus -= CustomScrollbar_GotFocus; + customScrollbar.Scroll -= CustomScrollbar_Scroll; + customScrollbar.MouseEnter -= ControlsMouseEnter; + customScrollbar.MouseLeave -= ControlsMouseLeave; customScrollbar.Dispose(); + labelTitle.MouseEnter -= ControlsMouseEnter; + labelTitle.MouseLeave -= ControlsMouseLeave; + textBoxSearch.MouseEnter -= ControlsMouseEnter; + textBoxSearch.MouseLeave -= ControlsMouseLeave; + pictureBoxOpenFolder.MouseEnter -= ControlsMouseEnter; + pictureBoxOpenFolder.MouseLeave -= ControlsMouseLeave; + pictureBoxMenuAlwaysOpen.MouseEnter -= ControlsMouseEnter; + pictureBoxMenuAlwaysOpen.MouseLeave -= ControlsMouseLeave; + pictureBoxMenuAlwaysOpen.Paint -= PictureBoxMenuAlwaysOpen_Paint; + pictureBoxMenuAlwaysOpen.Paint -= LoadingMenu_Paint; + pictureBoxSettings.MouseEnter -= ControlsMouseEnter; + pictureBoxSettings.MouseLeave -= ControlsMouseLeave; + pictureBoxRestart.MouseEnter -= ControlsMouseEnter; + pictureBoxRestart.MouseLeave -= ControlsMouseLeave; + pictureBoxSearch.MouseEnter -= ControlsMouseEnter; + pictureBoxSearch.MouseLeave -= ControlsMouseLeave; + tableLayoutPanelMenu.MouseEnter -= ControlsMouseEnter; + tableLayoutPanelMenu.MouseLeave -= ControlsMouseLeave; + tableLayoutPanelDgvAndScrollbar.MouseEnter -= ControlsMouseEnter; + tableLayoutPanelDgvAndScrollbar.MouseLeave -= ControlsMouseLeave; + tableLayoutPanelBottom.MouseEnter -= ControlsMouseEnter; + tableLayoutPanelBottom.MouseLeave -= ControlsMouseLeave; + labelItems.MouseEnter -= ControlsMouseEnter; + labelItems.MouseLeave -= ControlsMouseLeave; base.Dispose(disposing); } diff --git a/UserInterface/Menu.cs b/UserInterface/Menu.cs index b02bab3..34609ac 100644 --- a/UserInterface/Menu.cs +++ b/UserInterface/Menu.cs @@ -32,144 +32,41 @@ namespace SystemTrayMenu.UserInterface internal Menu() { fading.ChangeOpacity += Fading_ChangeOpacity; - void Fading_ChangeOpacity(object sender, double newOpacity) - { - if (newOpacity != Opacity && !IsDisposed && !Disposing) - { - Opacity = newOpacity; - } - } - fading.Show += Fading_Show; - void Fading_Show() - { - try - { - isShowing = true; - Visible = true; - isShowing = false; - timerUpdateIcons.Start(); - } - catch (ObjectDisposedException) - { - Visible = false; - isShowing = false; - Log.Info($"Could not open menu, old menu was disposing," + - $" IsDisposed={IsDisposed}"); - } - - if (Visible) - { - if (Level == 0) - { - Activate(); - NativeMethods.User32ShowInactiveTopmost(this); - NativeMethods.ForceForegroundWindow(Handle); - } - else - { - NativeMethods.User32ShowInactiveTopmost(this); - } - } - } - fading.Hide += Hide; - InitializeComponent(); - SetDoubleBuffer(dgv, true); - - Color foreColor = Color.Black; - Color backColor = AppColors.Background; - Color backColorSearch = AppColors.SearchField; - Color backgroundBorder = AppColors.BackgroundBorder; - - if (Config.IsDarkMode()) - { - foreColor = Color.White; - labelTitle.ForeColor = foreColor; - textBoxSearch.ForeColor = foreColor; - backColor = AppColors.DarkModeBackground; - backColorSearch = AppColors.DarkModeSearchField; - backgroundBorder = AppColors.DarkModeBackgroundBorder; - } - - ColorConverter colorConverter = new(); - labelItems.ForeColor = MenuDefines.ColorIcons; - - if (backColor.R == 0) - { - backColor = Color.White; - } - - BackColor = backgroundBorder; - labelTitle.BackColor = backColor; - tableLayoutPanelDgvAndScrollbar.BackColor = backColor; - tableLayoutPanelBottom.BackColor = backColor; - tableLayoutPanelMenu.BackColor = backColor; - dgv.BackgroundColor = backColor; - textBoxSearch.BackColor = backColorSearch; - panelLine.BackColor = AppColors.Icons; - pictureBoxSearch.BackColor = backColorSearch; - tableLayoutPanelSearch.BackColor = backColorSearch; - dgv.DefaultCellStyle = new DataGridViewCellStyle - { - SelectionForeColor = foreColor, - ForeColor = foreColor, - BackColor = backColor, - }; - - dgv.GotFocus += (sender, e) => FocusTextBox(); - customScrollbar.GotFocus += (sender, e) => FocusTextBox(); - - customScrollbar.Margin = new Padding(0); - customScrollbar.Scroll += CustomScrollbar_Scroll; - void CustomScrollbar_Scroll(object sender, EventArgs e) - { - decimal firstIndex = customScrollbar.Value * dgv.Rows.Count / (decimal)customScrollbar.Maximum; - int firstIndexRounded = (int)Math.Ceiling(firstIndex); - if (firstIndexRounded > -1 && firstIndexRounded < dgv.RowCount) - { - dgv.FirstDisplayedScrollingRowIndex = firstIndexRounded; - } - } - - customScrollbar.MouseEnter += ControlsMouseEnter; + AdjustColors(); + dgv.GotFocus += Dgv_GotFocus; dgv.MouseEnter += ControlsMouseEnter; - labelTitle.MouseEnter += ControlsMouseEnter; - textBoxSearch.MouseEnter += ControlsMouseEnter; - pictureBoxOpenFolder.MouseEnter += ControlsMouseEnter; - pictureBoxMenuAlwaysOpen.MouseEnter += ControlsMouseEnter; - pictureBoxSettings.MouseEnter += ControlsMouseEnter; - pictureBoxRestart.MouseEnter += ControlsMouseEnter; - pictureBoxSearch.MouseEnter += ControlsMouseEnter; - tableLayoutPanelMenu.MouseEnter += ControlsMouseEnter; - tableLayoutPanelDgvAndScrollbar.MouseEnter += ControlsMouseEnter; - tableLayoutPanelBottom.MouseEnter += ControlsMouseEnter; - labelItems.MouseEnter += ControlsMouseEnter; - void ControlsMouseEnter(object sender, EventArgs e) - { - MouseEnter?.Invoke(); - } - - customScrollbar.MouseLeave += ControlsMouseLeave; dgv.MouseLeave += ControlsMouseLeave; + customScrollbar.Margin = new Padding(0); + customScrollbar.GotFocus += CustomScrollbar_GotFocus; + customScrollbar.Scroll += CustomScrollbar_Scroll; + customScrollbar.MouseEnter += ControlsMouseEnter; + customScrollbar.MouseLeave += ControlsMouseLeave; + labelTitle.MouseEnter += ControlsMouseEnter; labelTitle.MouseLeave += ControlsMouseLeave; + textBoxSearch.MouseEnter += ControlsMouseEnter; textBoxSearch.MouseLeave += ControlsMouseLeave; - pictureBoxMenuAlwaysOpen.MouseLeave += ControlsMouseLeave; + pictureBoxOpenFolder.MouseEnter += ControlsMouseEnter; pictureBoxOpenFolder.MouseLeave += ControlsMouseLeave; + pictureBoxMenuAlwaysOpen.MouseEnter += ControlsMouseEnter; + pictureBoxMenuAlwaysOpen.MouseLeave += ControlsMouseLeave; + pictureBoxSettings.MouseEnter += ControlsMouseEnter; pictureBoxSettings.MouseLeave += ControlsMouseLeave; + pictureBoxRestart.MouseEnter += ControlsMouseEnter; pictureBoxRestart.MouseLeave += ControlsMouseLeave; + pictureBoxSearch.MouseEnter += ControlsMouseEnter; pictureBoxSearch.MouseLeave += ControlsMouseLeave; + tableLayoutPanelMenu.MouseEnter += ControlsMouseEnter; tableLayoutPanelMenu.MouseLeave += ControlsMouseLeave; + tableLayoutPanelDgvAndScrollbar.MouseEnter += ControlsMouseEnter; tableLayoutPanelDgvAndScrollbar.MouseLeave += ControlsMouseLeave; + tableLayoutPanelBottom.MouseEnter += ControlsMouseEnter; tableLayoutPanelBottom.MouseLeave += ControlsMouseLeave; + labelItems.MouseEnter += ControlsMouseEnter; labelItems.MouseLeave += ControlsMouseLeave; - void ControlsMouseLeave(object sender, EventArgs e) - { - MouseLeave?.Invoke(); - } - bool isTouchEnabled = NativeMethods.IsTouchEnabled(); if ((isTouchEnabled && Properties.Settings.Default.DragDropItemsEnabledTouch) || (!isTouchEnabled && Properties.Settings.Default.DragDropItemsEnabled)) @@ -180,23 +77,23 @@ namespace SystemTrayMenu.UserInterface } } - internal new event EventHandlerEmpty MouseWheel; + internal new event Action MouseWheel; - internal new event EventHandlerEmpty MouseEnter; + internal new event Action MouseEnter; - internal new event EventHandlerEmpty MouseLeave; + internal new event Action MouseLeave; - internal event EventHandlerEmpty UserClickedOpenFolder; + internal event Action UserClickedOpenFolder; internal event EventHandler CmdKeyProcessed; internal event EventHandler KeyPressCheck; - internal event EventHandlerEmpty SearchTextChanging; + internal event Action SearchTextChanging; internal event EventHandler SearchTextChanged; - internal event EventHandlerEmpty UserDragsMenu; + internal event Action UserDragsMenu; internal enum MenuType { @@ -218,6 +115,8 @@ namespace SystemTrayMenu.UserInterface internal int Level { get; set; } + internal string Path { get; set; } + internal bool IsUsable => Visible && !fading.IsHiding && !IsDisposed && !Disposing; internal bool ScrollbarVisible { get; private set; } @@ -324,7 +223,7 @@ namespace SystemTrayMenu.UserInterface pictureBoxMenuAlwaysOpen.Paint -= PictureBoxMenuAlwaysOpen_Paint; pictureBoxMenuAlwaysOpen.Paint += LoadingMenu_Paint; timerUpdateIcons.Tick -= TimerUpdateIcons_Tick; - timerUpdateIcons.Tick += (sender, e) => pictureBoxMenuAlwaysOpen.Invalidate(); + timerUpdateIcons.Tick += TimerUpdateIcons_Tick_Loading; timerUpdateIcons.Interval = 15; break; default: @@ -718,6 +617,117 @@ namespace SystemTrayMenu.UserInterface CultureInfo.InvariantCulture); } + private void Fading_ChangeOpacity(double newOpacity) + { + if (newOpacity != Opacity && !IsDisposed && !Disposing) + { + Opacity = newOpacity; + } + } + + private void Fading_Show() + { + try + { + isShowing = true; + Visible = true; + isShowing = false; + timerUpdateIcons.Start(); + } + catch (ObjectDisposedException) + { + Visible = false; + isShowing = false; + Log.Info($"Could not open menu, old menu was disposing," + + $" IsDisposed={IsDisposed}"); + } + + if (Visible) + { + if (Level == 0) + { + Activate(); + NativeMethods.User32ShowInactiveTopmost(this); + NativeMethods.ForceForegroundWindow(Handle); + } + else + { + NativeMethods.User32ShowInactiveTopmost(this); + } + } + } + + private void AdjustColors() + { + Color foreColor = Color.Black; + Color backColor = AppColors.Background; + Color backColorSearch = AppColors.SearchField; + Color backgroundBorder = AppColors.BackgroundBorder; + if (Config.IsDarkMode()) + { + foreColor = Color.White; + labelTitle.ForeColor = foreColor; + textBoxSearch.ForeColor = foreColor; + backColor = AppColors.DarkModeBackground; + backColorSearch = AppColors.DarkModeSearchField; + backgroundBorder = AppColors.DarkModeBackgroundBorder; + } + + ColorConverter colorConverter = new(); + labelItems.ForeColor = MenuDefines.ColorIcons; + if (backColor.R == 0) + { + backColor = Color.White; + } + + BackColor = backgroundBorder; + labelTitle.BackColor = backColor; + tableLayoutPanelDgvAndScrollbar.BackColor = backColor; + tableLayoutPanelBottom.BackColor = backColor; + tableLayoutPanelMenu.BackColor = backColor; + dgv.BackgroundColor = backColor; + textBoxSearch.BackColor = backColorSearch; + panelLine.BackColor = AppColors.Icons; + pictureBoxSearch.BackColor = backColorSearch; + tableLayoutPanelSearch.BackColor = backColorSearch; + dgv.DefaultCellStyle = new DataGridViewCellStyle + { + SelectionForeColor = foreColor, + ForeColor = foreColor, + BackColor = backColor, + }; + } + + private void Dgv_GotFocus(object sender, EventArgs e) + { + FocusTextBox(); + } + + private void CustomScrollbar_GotFocus(object sender, EventArgs e) + { + FocusTextBox(); + } + + private void CustomScrollbar_Scroll(object sender, EventArgs e) + { + decimal firstIndex = customScrollbar.Value * dgv.Rows.Count / (decimal)customScrollbar.Maximum; + int firstIndexRounded = (int)Math.Ceiling(firstIndex); + if (firstIndexRounded > -1 && firstIndexRounded < dgv.RowCount) + { + dgv.FirstDisplayedScrollingRowIndex = firstIndexRounded; + } + } + + private void ControlsMouseEnter(object sender, EventArgs e) + { + MouseEnter?.Invoke(); + } + + private void ControlsMouseLeave(object sender, EventArgs e) + { + MouseLeave?.Invoke(); + } + private void AdjustDataGridViewHeight(Menu menuPredecessor, int screenHeightMax) { double factor = Properties.Settings.Default.RowHeighteInPercentage / 100f; @@ -753,12 +763,12 @@ namespace SystemTrayMenu.UserInterface row.Height = dgv.RowTemplate.Height; } - int dgvHeightByItems = dgv.Rows.GetRowsHeight(DataGridViewElementStates.None); + int dgvHeightByItems = Math.Max(dgv.Rows.GetRowsHeight(DataGridViewElementStates.None), 1); int dgvHeightMaxByScreen = screenHeightMax - (Height - dgv.Height); int dgvHeightMaxByOptions = (int)(Scaling.Factor * Scaling.FactorByDpi * 450f * (Properties.Settings.Default.HeightMaxInPercent / 100f)); int dgvHeightMax = Math.Min(dgvHeightMaxByScreen, dgvHeightMaxByOptions); - if (!dgvHeightSet && dgvHeightByItems > 0 && dgvHeightMax > 0) + if (!dgvHeightSet) { dgv.Height = Math.Min(dgvHeightByItems, dgvHeightMax); dgvHeightSet = true; @@ -1035,7 +1045,7 @@ namespace SystemTrayMenu.UserInterface { if (e.Button == MouseButtons.Left) { - UserClickedOpenFolder?.Invoke(); + UserClickedOpenFolder?.Invoke(Path); } } @@ -1145,6 +1155,11 @@ namespace SystemTrayMenu.UserInterface } } + private void TimerUpdateIcons_Tick_Loading(object sender, EventArgs e) + { + pictureBoxMenuAlwaysOpen.Invalidate(); + } + private void Menu_MouseDown(object sender, MouseEventArgs e) { if (Level == 0) diff --git a/Utilities/AppRestart.cs b/Utilities/AppRestart.cs index 221266b..d2a8b3e 100644 --- a/Utilities/AppRestart.cs +++ b/Utilities/AppRestart.cs @@ -4,6 +4,7 @@ namespace SystemTrayMenu.Utilities { + using System; using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -11,7 +12,7 @@ namespace SystemTrayMenu.Utilities internal class AppRestart { - public static event EventHandlerEmpty BeforeRestarting; + public static event Action BeforeRestarting; internal static void ByThreadException() { diff --git a/Utilities/EventHandlerEmpty.cs b/Utilities/EventHandlerEmpty.cs deleted file mode 100644 index 97ccb75..0000000 --- a/Utilities/EventHandlerEmpty.cs +++ /dev/null @@ -1,8 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace SystemTrayMenu.Utilities -{ - public delegate void EventHandlerEmpty(); -}