//
// Copyright (c) PlaceholderCompany. All rights reserved.
//
namespace SystemTrayMenu.Business
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using Microsoft.Win32;
using SystemTrayMenu.DataClasses;
using SystemTrayMenu.DllImports;
using SystemTrayMenu.Handler;
using SystemTrayMenu.Helpers;
using SystemTrayMenu.Properties;
using SystemTrayMenu.Utilities;
using static SystemTrayMenu.UserInterface.Menu;
using Menu = SystemTrayMenu.UserInterface.Menu;
internal class Menus : IDisposable
{
private readonly Dispatcher dispatchter = Dispatcher.CurrentDispatcher;
private readonly Menu?[] menus = new Menu?[MenuDefines.MenusMax];
private readonly BackgroundWorker workerMainMenu = new();
private readonly List workersSubMenu = new();
private readonly DgvMouseRow dgvMouseRow = new();
private readonly WaitToLoadMenu waitToOpenMenu = new();
private readonly KeyboardInput keyboardInput;
private readonly JoystickHelper joystickHelper;
private readonly List watchers = new();
private readonly List watcherHistory = new();
private readonly DispatcherTimer timerShowProcessStartedAsLoadingIcon = new();
private readonly DispatcherTimer timerStillActiveCheck = new();
private readonly DispatcherTimer waitLeave = new();
private DateTime deactivatedTime = DateTime.MinValue;
private OpenCloseState openCloseState = OpenCloseState.Default;
private TaskbarPosition taskbarPosition = new WindowsTaskbar().Position;
private bool searchTextChanging;
private int lastMouseDownRowIndex = -1;
private bool showMenuAfterMainPreload;
private bool hideSubmenuDuringRefreshSearch;
public Menus()
{
keyboardInput = new(menus);
keyboardInput.RegisterHotKey();
keyboardInput.HotKeyPressed += () => SwitchOpenClose(false);
keyboardInput.ClosePressed += MenusFadeOut;
keyboardInput.RowDeselected += waitToOpenMenu.RowDeselected;
keyboardInput.EnterPressed += waitToOpenMenu.EnterOpensInstantly;
keyboardInput.RowSelected += waitToOpenMenu.RowSelected;
workerMainMenu.WorkerSupportsCancellation = true;
workerMainMenu.DoWork += LoadMenu;
workerMainMenu.RunWorkerCompleted += LoadMainMenuCompleted;
waitToOpenMenu.StopLoadMenu += WaitToOpenMenu_StopLoadMenu;
void WaitToOpenMenu_StopLoadMenu()
{
foreach (BackgroundWorker workerSubMenu in workersSubMenu.
Where(w => w.IsBusy))
{
workerSubMenu.CancelAsync();
}
LoadStopped?.Invoke();
}
waitToOpenMenu.StartLoadMenu += StartLoadMenu;
void StartLoadMenu(RowData rowData)
{
if (IsMainUsable && rowData.Path != null &&
(menus[rowData.Level + 1] == null ||
menus[rowData.Level + 1]?.RowDataParent != rowData))
{
Create(new(rowData.Level + 1, rowData), rowData.Path); // Level 1+ Sub Menu (loading)
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)
{
if (e.Result == null)
{
return;
}
MenuData menuData = (MenuData)e.Result;
Menu? menu = menus[menuData.Level];
if (menu == null)
{
return;
}
if (IsMainUsable)
{
if (menuData.DirectoryState != MenuDataDirectoryState.Undefined)
{
// Sub Menu (completed)
menu.AddItemsToMenu(menuData.RowDatas);
menu.SetSubMenuState(menuData.DirectoryState);
AdjustMenusSizeAndLocation(menu.Level);
menu.TimerUpdateIconsStart();
}
else
{
menu.HideWithFade();
menus[menu.Level] = null;
if (menuData.RowDataParent != null)
{
menuData.RowDataParent.IsMenuOpen = false;
menuData.RowDataParent.IsClicking = false;
menuData.RowDataParent.IsSelected = false;
}
ListView? lv = menus[menuData.Level - 1]?.GetDataGridView();
if (lv != null)
{
RefreshSelection(lv);
}
}
}
}
}
waitToOpenMenu.CloseMenu += CloseMenu;
void CloseMenu(int level)
{
if (level < menus.Length)
{
Menu? menu = menus[level];
if (menu != null)
{
HideOldMenu(menu);
}
}
if (level - 1 < menus.Length && menus[level - 1] != null)
{
menus[level - 1]?.FocusTextBox();
}
}
waitToOpenMenu.MouseEnterOk += MouseEnterOk;
dgvMouseRow.RowMouseEnter += waitToOpenMenu.MouseEnter;
dgvMouseRow.RowMouseLeave += waitToOpenMenu.MouseLeave;
#if TODO // Misc MouseEvents
dgvMouseRow.RowMouseLeave += Dgv_RowMouseLeave;
#endif
joystickHelper = new();
joystickHelper.KeyPressed += (key, modifiers) => menus[0]?.Dispatcher.Invoke(keyboardInput.CmdKeyProcessed, new object[] { null!, key, modifiers });
timerStillActiveCheck.Interval = TimeSpan.FromMilliseconds(Settings.Default.TimeUntilClosesAfterEnterPressed + 20);
timerStillActiveCheck.Tick += (sender, e) => StillActiveTick();
void StillActiveTick()
{
if (!IsActive())
{
FadeHalfOrOutIfNeeded();
}
timerStillActiveCheck.Stop();
}
waitLeave.Interval = TimeSpan.FromMilliseconds(Settings.Default.TimeUntilCloses);
waitLeave.Tick += (_, _) =>
{
waitLeave.Stop();
FadeHalfOrOutIfNeeded();
};
CreateWatcher(Config.Path, false);
foreach (var pathAndFlags in DirectoryHelpers.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);
}
}
SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged;
}
internal event Action? LoadStarted;
internal event Action? LoadStopped;
private enum OpenCloseState
{
Default,
Opening,
Closing,
}
private bool IsMainUsable => menus[0]?.IsUsable ?? false;
private IEnumerable