2020-04-30 23:22:47 +12:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.ComponentModel;
|
2020-05-14 05:14:51 +12:00
|
|
|
|
using System.Data;
|
2020-04-30 23:22:47 +12:00
|
|
|
|
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.Utilities;
|
|
|
|
|
using Menu = SystemTrayMenu.UserInterface.Menu;
|
|
|
|
|
using Timer = System.Windows.Forms.Timer;
|
|
|
|
|
|
|
|
|
|
namespace SystemTrayMenu.Business
|
|
|
|
|
{
|
|
|
|
|
internal class Menus : IDisposable
|
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
internal event EventHandlerEmpty LoadStarted;
|
2020-04-30 23:22:47 +12:00
|
|
|
|
internal event EventHandlerEmpty LoadStopped;
|
|
|
|
|
private enum OpenCloseState { Default, Opening, Closing };
|
|
|
|
|
private OpenCloseState openCloseState = OpenCloseState.Default;
|
|
|
|
|
private readonly Menu[] menus = new Menu[MenuDefines.MenusMax];
|
2020-06-21 03:38:21 +12:00
|
|
|
|
private readonly BackgroundWorker workerMainMenu = new BackgroundWorker();
|
|
|
|
|
private readonly List<BackgroundWorker> workersSubMenu = new List<BackgroundWorker>();
|
2020-04-30 23:22:47 +12:00
|
|
|
|
|
2020-06-21 03:38:21 +12:00
|
|
|
|
private readonly WaitToLoadMenu waitToOpenMenu = new WaitToLoadMenu();
|
2020-04-30 23:22:47 +12:00
|
|
|
|
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<Menu> AsEnumerable => menus.Where(m => m != null && !m.IsDisposed);
|
|
|
|
|
private List<Menu> AsList => AsEnumerable.ToList();
|
|
|
|
|
|
|
|
|
|
public Menus()
|
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
workerMainMenu.WorkerSupportsCancellation = true;
|
|
|
|
|
workerMainMenu.DoWork += LoadMenu;
|
|
|
|
|
void LoadMenu(object senderDoWork, DoWorkEventArgs eDoWork)
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
string path = Config.Path;
|
|
|
|
|
int level = 0;
|
|
|
|
|
RowData rowData = eDoWork.Argument as RowData;
|
|
|
|
|
if (rowData != null)
|
|
|
|
|
{
|
|
|
|
|
path = rowData.TargetFilePath;
|
|
|
|
|
level = rowData.MenuLevel + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MenuData menuData = GetData((BackgroundWorker)senderDoWork, path, level);
|
|
|
|
|
menuData.RowDataParent = rowData;
|
|
|
|
|
eDoWork.Result = menuData;
|
2020-04-30 23:22:47 +12:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 03:38:21 +12:00
|
|
|
|
workerMainMenu.RunWorkerCompleted += LoadMainMenuCompleted;
|
|
|
|
|
void LoadMainMenuCompleted(object sender, RunWorkerCompletedEventArgs e)
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
|
|
|
|
keyboardInput.ResetSelectedByKey();
|
|
|
|
|
LoadStopped();
|
|
|
|
|
MenuData menuData = (MenuData)e.Result;
|
|
|
|
|
if (menuData.Validity == MenuDataValidity.Valid)
|
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
DisposeMenu(menus[menuData.Level]);
|
2020-04-30 23:22:47 +12:00
|
|
|
|
menus[0] = Create(menuData, Path.GetFileName(Config.Path));
|
|
|
|
|
AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 03:38:21 +12:00
|
|
|
|
waitToOpenMenu.StopLoadMenu += WaitToOpenMenu_StopLoadMenu;
|
|
|
|
|
void WaitToOpenMenu_StopLoadMenu()
|
|
|
|
|
{
|
|
|
|
|
foreach (BackgroundWorker workerSubMenu in workersSubMenu.
|
|
|
|
|
Where(w => w.IsBusy))
|
|
|
|
|
{
|
|
|
|
|
workerSubMenu.CancelAsync();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
waitToOpenMenu.StartLoadMenu += StartLoadMenu;
|
|
|
|
|
void StartLoadMenu(RowData rowData)
|
|
|
|
|
{
|
|
|
|
|
if (menus[0].IsUsable &&
|
|
|
|
|
menus[rowData.MenuLevel] == null ||
|
|
|
|
|
menus[rowData.MenuLevel].Tag as RowData != rowData)
|
|
|
|
|
{
|
|
|
|
|
LoadStarted();
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
LoadStopped();
|
|
|
|
|
MenuData menuData = (MenuData)e.Result;
|
|
|
|
|
if (menus[0].IsUsable &&
|
|
|
|
|
menuData.Validity != MenuDataValidity.AbortedOrUnknown)
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-30 23:22:47 +12:00
|
|
|
|
keyboardInput = new KeyboardInput(menus);
|
|
|
|
|
keyboardInput.RegisterHotKey();
|
2020-05-01 22:41:35 +12:00
|
|
|
|
keyboardInput.HotKeyPressed += KeyboardInput_HotKeyPressed;
|
2020-05-14 05:14:51 +12:00
|
|
|
|
void KeyboardInput_HotKeyPressed()
|
|
|
|
|
{
|
|
|
|
|
SwitchOpenClose(false);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-30 23:22:47 +12:00
|
|
|
|
keyboardInput.ClosePressed += MenusFadeOut;
|
2020-06-21 03:38:21 +12:00
|
|
|
|
keyboardInput.RowDeselected += waitToOpenMenu.RowDeselected;
|
|
|
|
|
keyboardInput.RowSelected += waitToOpenMenu.RowSelected;
|
|
|
|
|
|
|
|
|
|
timerStillActiveCheck.Interval = 1000;
|
|
|
|
|
timerStillActiveCheck.Tick += StillActiveTick;
|
|
|
|
|
void StillActiveTick(object senderTimer, EventArgs eTimer)
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
if (!IsActive())
|
|
|
|
|
{
|
|
|
|
|
FadeHalfOrOutIfNeeded();
|
|
|
|
|
timerStillActiveCheck.Stop();
|
|
|
|
|
}
|
2020-04-30 23:22:47 +12:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
waitLeave.LeaveTriggered += LeaveTriggered;
|
2020-05-14 05:14:51 +12:00
|
|
|
|
void LeaveTriggered()
|
|
|
|
|
{
|
|
|
|
|
FadeHalfOrOutIfNeeded();
|
|
|
|
|
}
|
2020-04-30 23:22:47 +12:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-01 22:41:35 +12:00
|
|
|
|
internal void SwitchOpenClose(bool byClick)
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
2020-05-01 22:41:35 +12:00
|
|
|
|
if (byClick && (DateTime.Now - deactivatedTime).TotalMilliseconds < 200)
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
|
|
|
|
//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;
|
|
|
|
|
StartWorker();
|
|
|
|
|
}
|
|
|
|
|
deactivatedTime = DateTime.MinValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
workerMainMenu.Dispose();
|
2020-06-21 10:30:43 +12:00
|
|
|
|
foreach (BackgroundWorker worker in workersSubMenu)
|
|
|
|
|
{
|
|
|
|
|
worker.Dispose();
|
|
|
|
|
}
|
|
|
|
|
waitLeave.Dispose();
|
2020-04-30 23:22:47 +12:00
|
|
|
|
keyboardInput.Dispose();
|
|
|
|
|
timerStillActiveCheck.Dispose();
|
|
|
|
|
IconReader.Dispose();
|
|
|
|
|
DisposeMenu(menus[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void DisposeMenu(Menu menuToDispose)
|
|
|
|
|
{
|
|
|
|
|
if (menuToDispose != null)
|
|
|
|
|
{
|
|
|
|
|
DataGridView dgv = menuToDispose.GetDataGridView();
|
|
|
|
|
foreach (DataGridViewRow row in dgv.Rows)
|
|
|
|
|
{
|
2020-05-14 05:14:51 +12:00
|
|
|
|
RowData rowData = (RowData)row.Cells[2].Value;
|
2020-04-30 23:22:47 +12:00
|
|
|
|
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<RowData>(),
|
2020-06-21 03:38:21 +12:00
|
|
|
|
Validity = MenuDataValidity.AbortedOrUnknown,
|
2020-04-30 23:22:47 +12:00
|
|
|
|
Level = level
|
|
|
|
|
};
|
|
|
|
|
if (!worker.CancellationPending)
|
|
|
|
|
{
|
|
|
|
|
string[] directories = Array.Empty<string>();
|
|
|
|
|
|
|
|
|
|
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);
|
2020-06-21 03:38:21 +12:00
|
|
|
|
rowData.MenuLevel = level;
|
2020-04-30 23:22:47 +12:00
|
|
|
|
menuData.RowDatas.Add(rowData);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!worker.CancellationPending)
|
|
|
|
|
{
|
|
|
|
|
string[] files = Array.Empty<string>();
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
if (menuData.Validity == MenuDataValidity.AbortedOrUnknown)
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
if (menuData.RowDatas.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
menuData.Validity = MenuDataValidity.Empty;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
menuData.Validity = MenuDataValidity.Valid;
|
|
|
|
|
}
|
2020-04-30 23:22:47 +12:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return menuData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void MainPreload()
|
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
menus[0] = Create(GetData(workerMainMenu, Config.Path, 0),
|
2020-04-30 23:22:47 +12:00
|
|
|
|
Path.GetFileName(Config.Path));
|
2020-05-06 12:39:38 +12:00
|
|
|
|
menus[0].AdjustSizeAndLocation();
|
2020-04-30 23:22:47 +12:00
|
|
|
|
DisposeMenu(menus[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void StartWorker()
|
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
if (!workerMainMenu.IsBusy)
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
LoadStarted();
|
|
|
|
|
workerMainMenu.RunWorkerAsync(
|
|
|
|
|
new object[] { Config.Path, 0 });
|
2020-04-30 23:22:47 +12:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void StopWorker()
|
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
if (workerMainMenu.IsBusy)
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
workerMainMenu.CancelAsync();
|
2020-04-30 23:22:47 +12:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
2020-05-06 12:39:38 +12:00
|
|
|
|
menu.MouseWheel += AdjustMenusSizeAndLocation;
|
2020-04-30 23:22:47 +12:00
|
|
|
|
menu.MouseLeave += waitLeave.Start;
|
|
|
|
|
menu.MouseEnter += waitLeave.Stop;
|
|
|
|
|
menu.KeyPress += keyboardInput.KeyPress;
|
|
|
|
|
menu.CmdKeyProcessed += keyboardInput.CmdKeyProcessed;
|
2020-06-21 03:38:21 +12:00
|
|
|
|
keyboardInput.EnterPressed += waitToOpenMenu.EnterOpensInstantly;
|
2020-05-14 05:14:51 +12:00
|
|
|
|
menu.SearchTextChanging += keyboardInput.SearchTextChanging;
|
|
|
|
|
menu.SearchTextChanged += Menu_SearchTextChanged;
|
|
|
|
|
void Menu_SearchTextChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
keyboardInput.SearchTextChanged(sender, e);
|
|
|
|
|
AdjustMenusSizeAndLocation();
|
|
|
|
|
}
|
2020-04-30 23:22:47 +12:00
|
|
|
|
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());
|
|
|
|
|
timerStillActiveCheck.Start();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
menu.VisibleChanged += MenuVisibleChanged;
|
|
|
|
|
AddItemsToMenu(menuData.RowDatas, menu);
|
2020-06-21 03:38:21 +12:00
|
|
|
|
void AddItemsToMenu(List<RowData> data, Menu menu)
|
|
|
|
|
{
|
|
|
|
|
DataGridView dgv = menu.GetDataGridView();
|
|
|
|
|
DataTable dataTable = new DataTable();
|
|
|
|
|
dataTable.Columns.Add(dgv.Columns[0].Name, typeof(Icon));
|
|
|
|
|
dataTable.Columns.Add(dgv.Columns[1].Name, typeof(string));
|
|
|
|
|
dataTable.Columns.Add("data", typeof(RowData));
|
|
|
|
|
foreach (RowData rowData in data)
|
|
|
|
|
{
|
|
|
|
|
rowData.SetData(rowData, dataTable);
|
|
|
|
|
}
|
|
|
|
|
dgv.DataSource = dataTable;
|
|
|
|
|
}
|
2020-05-06 12:39:38 +12:00
|
|
|
|
DataGridView dgv = menu.GetDataGridView();
|
2020-06-21 03:38:21 +12:00
|
|
|
|
dgv.CellMouseEnter += waitToOpenMenu.MouseEnter;
|
2020-05-06 12:39:38 +12:00
|
|
|
|
dgv.CellMouseEnter += Dgv_CellMouseEnter;
|
2020-06-21 03:38:21 +12:00
|
|
|
|
void Dgv_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
|
|
|
|
|
{
|
2020-06-21 06:11:25 +12:00
|
|
|
|
if (menus[0].IsUsable && waitToOpenMenu.MouseActive)
|
2020-06-21 03:38:21 +12:00
|
|
|
|
{
|
|
|
|
|
if (keyboardInput.InUse)
|
|
|
|
|
{
|
|
|
|
|
keyboardInput.ClearIsSelectedByKey();
|
|
|
|
|
keyboardInput.InUse = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keyboardInput.Select(dgv, e.RowIndex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dgv.CellMouseLeave += waitToOpenMenu.MouseLeave;
|
|
|
|
|
dgv.MouseMove += waitToOpenMenu.MouseMove;
|
2020-05-06 12:39:38 +12:00
|
|
|
|
dgv.MouseDown += Dgv_MouseDown;
|
|
|
|
|
dgv.MouseDoubleClick += Dgv_MouseDoubleClick;
|
|
|
|
|
dgv.SelectionChanged += Dgv_SelectionChanged;
|
|
|
|
|
|
2020-04-30 23:22:47 +12:00
|
|
|
|
return menu;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void MenuVisibleChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
Menu menu = (Menu)sender;
|
|
|
|
|
if (menu.IsUsable)
|
|
|
|
|
{
|
2020-05-06 12:39:38 +12:00
|
|
|
|
AdjustMenusSizeAndLocation();
|
2020-04-30 23:22:47 +12:00
|
|
|
|
}
|
|
|
|
|
if (!menu.Visible)
|
|
|
|
|
{
|
|
|
|
|
DisposeMenu(menu);
|
|
|
|
|
}
|
|
|
|
|
if (!AsEnumerable.Any(m => m.Visible))
|
|
|
|
|
{
|
|
|
|
|
openCloseState = OpenCloseState.Default;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
RowData rowData = (RowData)dgv.Rows[hitTestInfo.RowIndex].Cells[2].Value;
|
|
|
|
|
rowData.MouseDown(dgv, e);
|
|
|
|
|
waitToOpenMenu.ClickOpensInstantly(dgv, hitTestInfo.RowIndex);
|
2020-04-30 23:22:47 +12:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2020-05-14 05:14:51 +12:00
|
|
|
|
RowData trigger = (RowData)dgv.Rows[hitTestInfo.RowIndex].Cells[2].Value;
|
2020-05-09 01:57:30 +12:00
|
|
|
|
trigger.DoubleClick(e);
|
2020-04-30 23:22:47 +12:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Dgv_SelectionChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
RefreshSelection((DataGridView)sender);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void RefreshSelection(DataGridView dgv)
|
|
|
|
|
{
|
2020-04-30 23:22:47 +12:00
|
|
|
|
foreach (DataGridViewRow row in dgv.Rows)
|
|
|
|
|
{
|
2020-05-14 05:14:51 +12:00
|
|
|
|
RowData rowData = (RowData)row.Cells[2].Value;
|
2020-04-30 23:22:47 +12:00
|
|
|
|
|
2020-05-14 05:14:51 +12:00
|
|
|
|
if (rowData == null)
|
|
|
|
|
{
|
|
|
|
|
#warning evalute the case again, should we prevent it somewhere else?
|
|
|
|
|
}
|
|
|
|
|
else if (!menus[0].IsUsable)
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
|
|
|
|
row.DefaultCellStyle.SelectionBackColor = Color.White;
|
2020-06-21 03:38:21 +12:00
|
|
|
|
row.Selected = false;
|
2020-04-30 23:22:47 +12:00
|
|
|
|
}
|
2020-06-21 03:38:21 +12:00
|
|
|
|
else if (rowData.IsSelected)
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
|
|
|
|
row.DefaultCellStyle.SelectionBackColor =
|
|
|
|
|
MenuDefines.ColorSelectedItem;
|
|
|
|
|
row.Selected = true;
|
|
|
|
|
}
|
2020-06-21 03:38:21 +12:00
|
|
|
|
else if (rowData.IsMenuOpen)
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
|
|
|
|
row.DefaultCellStyle.SelectionBackColor =
|
|
|
|
|
MenuDefines.ColorOpenFolder;
|
|
|
|
|
row.Selected = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
row.DefaultCellStyle.SelectionBackColor = Color.White;
|
2020-04-30 23:22:47 +12:00
|
|
|
|
row.Selected = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 03:38:21 +12:00
|
|
|
|
private void ShowSubMenu(Menu menuToShow)
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
//Clean up menu status IsMenuOpen for previous one
|
|
|
|
|
Menu menuPrevious = menus[menuToShow.Level - 1];
|
|
|
|
|
DataGridView dgvPrevious = menuPrevious.GetDataGridView();
|
|
|
|
|
foreach (DataRow row in ((DataTable)dgvPrevious.DataSource).Rows)
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
RowData rowDataToClear = (RowData)row[2];
|
|
|
|
|
if (rowDataToClear == (RowData)menuToShow.Tag)
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
rowDataToClear.IsMenuOpen = true;
|
2020-04-30 23:22:47 +12:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
rowDataToClear.IsMenuOpen = false;
|
2020-04-30 23:22:47 +12:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-21 03:38:21 +12:00
|
|
|
|
RefreshSelection(dgvPrevious);
|
2020-04-30 23:22:47 +12:00
|
|
|
|
|
2020-06-21 03:38:21 +12:00
|
|
|
|
foreach (Menu menuToClose in menus.Where(
|
|
|
|
|
m => m != null && m.Level > menuPrevious.Level))
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
2020-06-21 03:38:21 +12:00
|
|
|
|
menuToClose.VisibleChanged += MenuVisibleChanged;
|
|
|
|
|
menuToClose.HideWithFade();
|
|
|
|
|
menus[menuToClose.Level] = null;
|
2020-04-30 23:22:47 +12:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 03:38:21 +12:00
|
|
|
|
menus[menuToShow.Level] = menuToShow;
|
2020-05-06 12:39:38 +12:00
|
|
|
|
AdjustMenusSizeAndLocation();
|
2020-06-21 03:38:21 +12:00
|
|
|
|
menus[menuToShow.Level].ShowWithFadeOrTransparent(IsActive());
|
2020-04-30 23:22:47 +12:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-06 12:39:38 +12:00
|
|
|
|
private void AdjustMenusSizeAndLocation()
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
|
|
|
|
Menu menuPredecessor = menus[0];
|
|
|
|
|
int widthPredecessors = -1; // -1 padding
|
|
|
|
|
bool directionToRight = false;
|
|
|
|
|
|
2020-05-06 12:39:38 +12:00
|
|
|
|
menus[0].AdjustSizeAndLocation();
|
|
|
|
|
|
2020-04-30 23:22:47 +12:00
|
|
|
|
foreach (Menu menu in AsEnumerable.Where(m => m.Level > 0))
|
|
|
|
|
{
|
|
|
|
|
int newWith = (menu.Width -
|
|
|
|
|
menu.Padding.Horizontal + menuPredecessor.Width);
|
|
|
|
|
if (directionToRight)
|
|
|
|
|
{
|
2020-06-21 07:02:03 +12:00
|
|
|
|
if (widthPredecessors - menus[0].Width - menu.Width < 0)
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
|
|
|
|
directionToRight = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
widthPredecessors -= newWith;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-06 12:39:38 +12:00
|
|
|
|
else if (Statics.ScreenWidth <
|
2020-06-21 07:02:03 +12:00
|
|
|
|
widthPredecessors + menus[0].Width + menu.Width)
|
2020-04-30 23:22:47 +12:00
|
|
|
|
{
|
|
|
|
|
directionToRight = true;
|
|
|
|
|
widthPredecessors -= newWith;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-07 05:50:01 +12:00
|
|
|
|
menu.AdjustSizeAndLocation(menuPredecessor, directionToRight);
|
2020-04-30 23:22:47 +12:00
|
|
|
|
widthPredecessors += menu.Width - menu.Padding.Left;
|
|
|
|
|
menuPredecessor = menu;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|