mirror of
https://github.com/Hofknecht/SystemTrayMenu.git
synced 2024-06-16 09:25:22 +12:00
parent
53d8505a8e
commit
43755ae648
742
Business/Menus.cs
Normal file
742
Business/Menus.cs
Normal file
|
@ -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<bool> 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<Menu> AsEnumerable => menus.Where(m => m != null && !m.IsDisposed);
|
||||||
|
private List<Menu> 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<RowData>(),
|
||||||
|
Validity = MenuDataValidity.Invalid,
|
||||||
|
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);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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<RowData> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,111 +1,26 @@
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using System;
|
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 System.Windows.Forms;
|
||||||
using SystemTrayMenu.DataClasses;
|
using SystemTrayMenu.Business;
|
||||||
using SystemTrayMenu.Handler;
|
|
||||||
using SystemTrayMenu.Helper;
|
|
||||||
using SystemTrayMenu.UserInterface;
|
using SystemTrayMenu.UserInterface;
|
||||||
using SystemTrayMenu.Utilities;
|
using SystemTrayMenu.Utilities;
|
||||||
using Menu = SystemTrayMenu.UserInterface.Menu;
|
|
||||||
|
|
||||||
namespace SystemTrayMenu
|
namespace SystemTrayMenu
|
||||||
{
|
{
|
||||||
internal class SystemTrayMenu : IDisposable
|
internal class SystemTrayMenu : IDisposable
|
||||||
{
|
{
|
||||||
private enum OpenCloseState { Default, Opening, Closing };
|
private readonly MenuNotifyIcon menuNotifyIcon = new MenuNotifyIcon();
|
||||||
private OpenCloseState openCloseState = OpenCloseState.Default;
|
private Menus menus = new Menus();
|
||||||
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;
|
|
||||||
|
|
||||||
public SystemTrayMenu()
|
public SystemTrayMenu()
|
||||||
{
|
{
|
||||||
AppRestart.BeforeRestarting += Dispose;
|
AppRestart.BeforeRestarting += Dispose;
|
||||||
SystemEvents.DisplaySettingsChanged += AppRestart.ByDisplaySettings;
|
SystemEvents.DisplaySettingsChanged += AppRestart.ByDisplaySettings;
|
||||||
waitLeave.LeaveTriggered += WaitLeave_LeaveTriggered;
|
menus.LoadStarted += menuNotifyIcon.LoadingStart;
|
||||||
void WaitLeave_LeaveTriggered()
|
menus.LoadStopped += menuNotifyIcon.LoadingStop;
|
||||||
{
|
|
||||||
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();
|
|
||||||
menuNotifyIcon.Exit += Application.Exit;
|
menuNotifyIcon.Exit += Application.Exit;
|
||||||
menuNotifyIcon.Restart += AppRestart.ByMenuNotifyIcon;
|
menuNotifyIcon.Restart += AppRestart.ByMenuNotifyIcon;
|
||||||
menuNotifyIcon.HandleClick += SwitchOpenClose;
|
menuNotifyIcon.Click += menus.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.OpenLog += Log.OpenLogFile;
|
menuNotifyIcon.OpenLog += Log.OpenLogFile;
|
||||||
menuNotifyIcon.ChangeFolder += ChangeFolder;
|
menuNotifyIcon.ChangeFolder += ChangeFolder;
|
||||||
void ChangeFolder()
|
void ChangeFolder()
|
||||||
|
@ -116,617 +31,13 @@ namespace SystemTrayMenu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
menus[0] = CreateMenu(ReadMenu(worker, Config.Path, 0),
|
menus.MainPreload();
|
||||||
Path.GetFileName(Config.Path));
|
|
||||||
menus[0].AdjustLocationAndSize(screen);
|
|
||||||
DisposeMenu(menus[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
worker.Dispose();
|
menus.Dispose();
|
||||||
keyboardInput.Dispose();
|
|
||||||
menuNotifyIcon.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<RowData>(),
|
|
||||||
Validity = MenuDataValidity.Invalid,
|
|
||||||
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);
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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<RowData> 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<Menu> Menus()
|
|
||||||
{
|
|
||||||
return menus.Where(m => m != null && !m.IsDisposed);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -160,7 +160,7 @@ namespace SystemTrayMenu.DataClasses
|
||||||
ref string resolvedLnkPath)
|
ref string resolvedLnkPath)
|
||||||
{
|
{
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
resolvedLnkPath = LnkHelper.ResolveShortcut(TargetFilePath);
|
resolvedLnkPath = LnkHelper.GetResolvedFileName(TargetFilePath);
|
||||||
if (LnkHelper.IsDirectory(resolvedLnkPath))
|
if (LnkHelper.IsDirectory(resolvedLnkPath))
|
||||||
{
|
{
|
||||||
Icon = IconReader.GetFolderIcon(TargetFilePath,
|
Icon = IconReader.GetFolderIcon(TargetFilePath,
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace SystemTrayMenu.UserInterface
|
||||||
private const int Interval60FPS = 16; //60fps=>1s/60fps=~16.6ms
|
private const int Interval60FPS = 16; //60fps=>1s/60fps=~16.6ms
|
||||||
|
|
||||||
private const double StepIn = 0.20;
|
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 Transparent = 0.80;
|
||||||
private const double TransparentMinus = 0.60; //Transparent - StepIn
|
private const double TransparentMinus = 0.60; //Transparent - StepIn
|
||||||
private const double TransparentPlus = 0.85; //Transparent + StepOut
|
private const double TransparentPlus = 0.85; //Transparent + StepOut
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using SystemTrayMenu.Utilities;
|
||||||
|
|
||||||
namespace SystemTrayMenu.Helper
|
namespace SystemTrayMenu.Helper
|
||||||
{
|
{
|
||||||
|
@ -65,20 +66,35 @@ namespace SystemTrayMenu.Helper
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a hot key in the system.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key itself that is associated with the hot key.</param>
|
||||||
|
internal void RegisterHotKey(Keys key)
|
||||||
|
{
|
||||||
|
uint keyModifiersNone = 0;
|
||||||
|
RegisterHotKey(keyModifiersNone, key);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers a hot key in the system.
|
/// Registers a hot key in the system.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="modifier">The modifiers that are associated with the hot key.</param>
|
/// <param name="modifier">The modifiers that are associated with the hot key.</param>
|
||||||
/// <param name="key">The key itself that is associated with the hot key.</param>
|
/// <param name="key">The key itself that is associated with the hot key.</param>
|
||||||
internal void RegisterHotKey(KeyboardHookModifierKeys modifier, Keys key)
|
internal void RegisterHotKey(KeyboardHookModifierKeys modifier, Keys key)
|
||||||
|
{
|
||||||
|
RegisterHotKey((uint)modifier, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterHotKey(uint modifier, Keys key)
|
||||||
{
|
{
|
||||||
_currentId = _currentId + 1;
|
_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(
|
||||||
throw new InvalidOperationException("Couldn’t register the hot key.");
|
Language.Translate("Could not register the hot key."));
|
||||||
#pragma warning restore CA1303 //=> Exceptions not translated in logfile => OK
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
4
LICENSE
4
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.
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
TAMAHO SystemTrayMenu - an improved Windows toolbar
|
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
|
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
|
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
|
If the program does terminal interaction, make it output a short
|
||||||
notice like this when it starts in an interactive mode:
|
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 program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
This is free software, and you are welcome to redistribute it
|
This is free software, and you are welcome to redistribute it
|
||||||
under certain conditions; type `show c' for details.
|
under certain conditions; type `show c' for details.
|
||||||
|
|
|
@ -35,5 +35,5 @@ using System.Runtime.InteropServices;
|
||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("0.9.2.22")]
|
[assembly: AssemblyVersion("0.9.2.23")]
|
||||||
[assembly: AssemblyFileVersion("0.9.2.22")]
|
[assembly: AssemblyFileVersion("0.9.2.23")]
|
||||||
|
|
75
Resources/lang.Designer.cs
generated
75
Resources/lang.Designer.cs
generated
|
@ -1,10 +1,10 @@
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// <auto-generated>
|
// <auto-generated>
|
||||||
// Dieser Code wurde von einem Tool generiert.
|
// This code was generated by a tool.
|
||||||
// Laufzeitversion:4.0.30319.42000
|
// Runtime Version:4.0.30319.42000
|
||||||
//
|
//
|
||||||
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
// der Code erneut generiert wird.
|
// the code is regenerated.
|
||||||
// </auto-generated>
|
// </auto-generated>
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -13,12 +13,12 @@ namespace SystemTrayMenu.Resources {
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
|
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
|
// class via a tool like ResGen or Visual Studio.
|
||||||
// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
|
// with the /str option, or rebuild your VS project.
|
||||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
@ -33,7 +33,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
|
/// Returns the cached ResourceManager instance used by this class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||||
|
@ -47,8 +47,8 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
/// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
|
/// resource lookups using this strongly typed resource class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
internal static global::System.Globalization.CultureInfo Culture {
|
internal static global::System.Globalization.CultureInfo Culture {
|
||||||
|
@ -61,7 +61,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die (e.g. F10) ähnelt.
|
/// Looks up a localized string similar to (e.g. F10).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string _e_g__F10_ {
|
internal static string _e_g__F10_ {
|
||||||
get {
|
get {
|
||||||
|
@ -70,7 +70,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die About ähnelt.
|
/// Looks up a localized string similar to About.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string About {
|
internal static string About {
|
||||||
get {
|
get {
|
||||||
|
@ -79,7 +79,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Activate autostart ähnelt.
|
/// Looks up a localized string similar to Activate autostart.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string Activate_autostart {
|
internal static string Activate_autostart {
|
||||||
get {
|
get {
|
||||||
|
@ -88,7 +88,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die ALT ähnelt.
|
/// Looks up a localized string similar to ALT.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string ALT {
|
internal static string ALT {
|
||||||
get {
|
get {
|
||||||
|
@ -97,7 +97,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Autostart ähnelt.
|
/// Looks up a localized string similar to Autostart.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string Autostart {
|
internal static string Autostart {
|
||||||
get {
|
get {
|
||||||
|
@ -106,7 +106,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Details ähnelt.
|
/// Looks up a localized string similar to Details.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string buttonDetails {
|
internal static string buttonDetails {
|
||||||
get {
|
get {
|
||||||
|
@ -115,7 +115,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die OK ähnelt.
|
/// Looks up a localized string similar to OK.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string buttonOk {
|
internal static string buttonOk {
|
||||||
get {
|
get {
|
||||||
|
@ -124,7 +124,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die System Info ähnelt.
|
/// Looks up a localized string similar to System Info.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string buttonSystemInfo {
|
internal static string buttonSystemInfo {
|
||||||
get {
|
get {
|
||||||
|
@ -133,7 +133,16 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die CTRL ähnelt.
|
/// Looks up a localized string similar to Couldn’t register the hot key..
|
||||||
|
/// </summary>
|
||||||
|
internal static string Could_not_register_the_hot_key_ {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Could not register the hot key.", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to CTRL.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string CTRL {
|
internal static string CTRL {
|
||||||
get {
|
get {
|
||||||
|
@ -142,7 +151,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die English ähnelt.
|
/// Looks up a localized string similar to English.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string English {
|
internal static string English {
|
||||||
get {
|
get {
|
||||||
|
@ -151,7 +160,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Exit ähnelt.
|
/// Looks up a localized string similar to Exit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string Exit {
|
internal static string Exit {
|
||||||
get {
|
get {
|
||||||
|
@ -160,7 +169,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Folder ähnelt.
|
/// Looks up a localized string similar to Folder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string Folder {
|
internal static string Folder {
|
||||||
get {
|
get {
|
||||||
|
@ -169,7 +178,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Folder empty ähnelt.
|
/// Looks up a localized string similar to Folder empty.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string Folder_empty {
|
internal static string Folder_empty {
|
||||||
get {
|
get {
|
||||||
|
@ -178,7 +187,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Folder inaccessible ähnelt.
|
/// Looks up a localized string similar to Folder inaccessible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string Folder_inaccessible {
|
internal static string Folder_inaccessible {
|
||||||
get {
|
get {
|
||||||
|
@ -187,7 +196,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Deutsch ähnelt.
|
/// Looks up a localized string similar to Deutsch.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string German {
|
internal static string German {
|
||||||
get {
|
get {
|
||||||
|
@ -196,7 +205,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string HintDragDropText {
|
internal static string HintDragDropText {
|
||||||
get {
|
get {
|
||||||
|
@ -205,7 +214,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die SystemTrayMenu - Hint ähnelt.
|
/// Looks up a localized string similar to SystemTrayMenu - Hint.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string HintDragDropTitle {
|
internal static string HintDragDropTitle {
|
||||||
get {
|
get {
|
||||||
|
@ -214,7 +223,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Language ähnelt.
|
/// Looks up a localized string similar to Language.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string Language {
|
internal static string Language {
|
||||||
get {
|
get {
|
||||||
|
@ -223,7 +232,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Log File ähnelt.
|
/// Looks up a localized string similar to Log File.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string Log_File {
|
internal static string Log_File {
|
||||||
get {
|
get {
|
||||||
|
@ -232,7 +241,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Restart ähnelt.
|
/// Looks up a localized string similar to Restart.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string Restart {
|
internal static string Restart {
|
||||||
get {
|
get {
|
||||||
|
@ -241,7 +250,7 @@ namespace SystemTrayMenu.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sucht eine lokalisierte Zeichenfolge, die Shortcut key ähnelt.
|
/// Looks up a localized string similar to Shortcut key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string Shortcut_key {
|
internal static string Shortcut_key {
|
||||||
get {
|
get {
|
||||||
|
|
|
@ -180,4 +180,7 @@
|
||||||
<data name="Shortcut key" xml:space="preserve">
|
<data name="Shortcut key" xml:space="preserve">
|
||||||
<value>Tastenkombination</value>
|
<value>Tastenkombination</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Could not register the hot key." xml:space="preserve">
|
||||||
|
<value>Der Tastenkürzel konnte nicht registriert werden.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -180,4 +180,7 @@
|
||||||
<data name="Shortcut key" xml:space="preserve">
|
<data name="Shortcut key" xml:space="preserve">
|
||||||
<value>Shortcut key</value>
|
<value>Shortcut key</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Could not register the hot key." xml:space="preserve">
|
||||||
|
<value>Couldn’t register the hot key.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -138,6 +138,7 @@
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Business\Menus.cs" />
|
||||||
<Compile Include="Config\Config.cs" />
|
<Compile Include="Config\Config.cs" />
|
||||||
<Compile Include="NativeDllImport\DestroyMenu.cs" />
|
<Compile Include="NativeDllImport\DestroyMenu.cs" />
|
||||||
<Compile Include="NativeDllImport\CreatePopupMenu.cs" />
|
<Compile Include="NativeDllImport\CreatePopupMenu.cs" />
|
||||||
|
@ -177,6 +178,7 @@
|
||||||
<Compile Include="Utilities\FolderOptions.cs" />
|
<Compile Include="Utilities\FolderOptions.cs" />
|
||||||
<Compile Include="Helpers\KeyboardHook.cs" />
|
<Compile Include="Helpers\KeyboardHook.cs" />
|
||||||
<Compile Include="Utilities\Log.cs" />
|
<Compile Include="Utilities\Log.cs" />
|
||||||
|
<Compile Include="Utilities\Shares.cs" />
|
||||||
<Compile Include="Utilities\SingleAppInstance.cs" />
|
<Compile Include="Utilities\SingleAppInstance.cs" />
|
||||||
<Compile Include="Utilities\Language.cs" />
|
<Compile Include="Utilities\Language.cs" />
|
||||||
<Compile Include="Utilities\Scaling.cs" />
|
<Compile Include="Utilities\Scaling.cs" />
|
||||||
|
@ -260,10 +262,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="Resources\White50Percentage.ico" />
|
<None Include="Resources\White50Percentage.ico" />
|
||||||
<None Include="Resources\STM.ico" />
|
|
||||||
<None Include="Resources\STM_black.ico" />
|
|
||||||
<None Include="Resources\Selected.png" />
|
<None Include="Resources\Selected.png" />
|
||||||
<None Include="Resources\hintDragDrop.png" />
|
|
||||||
<None Include="Resources\L010.ico" />
|
<None Include="Resources\L010.ico" />
|
||||||
<None Include="Resources\L020.ico" />
|
<None Include="Resources\L020.ico" />
|
||||||
<None Include="Resources\L030.ico" />
|
<None Include="Resources\L030.ico" />
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace SystemTrayMenu.UserInterface
|
||||||
{
|
{
|
||||||
internal class MenuNotifyIcon : IDisposable
|
internal class MenuNotifyIcon : IDisposable
|
||||||
{
|
{
|
||||||
public event EventHandlerEmpty HandleClick;
|
public event EventHandlerEmpty Click;
|
||||||
public event EventHandlerEmpty ChangeFolder;
|
public event EventHandlerEmpty ChangeFolder;
|
||||||
public event EventHandlerEmpty OpenLog;
|
public event EventHandlerEmpty OpenLog;
|
||||||
public event EventHandlerEmpty Restart;
|
public event EventHandlerEmpty Restart;
|
||||||
|
@ -69,7 +69,6 @@ namespace SystemTrayMenu.UserInterface
|
||||||
{
|
{
|
||||||
VerifyClick(e);
|
VerifyClick(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyIcon.MouseDoubleClick += NotifyIcon_MouseDoubleClick;
|
notifyIcon.MouseDoubleClick += NotifyIcon_MouseDoubleClick;
|
||||||
void NotifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
|
void NotifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
|
@ -81,7 +80,7 @@ namespace SystemTrayMenu.UserInterface
|
||||||
{
|
{
|
||||||
if (e.Button == MouseButtons.Left)
|
if (e.Button == MouseButtons.Left)
|
||||||
{
|
{
|
||||||
HandleClick.Invoke();
|
Click?.Invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +91,7 @@ namespace SystemTrayMenu.UserInterface
|
||||||
load.Dispose();
|
load.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadingStart(bool reset = false)
|
public void LoadingStart(object sender = null, bool reset = false)
|
||||||
{
|
{
|
||||||
if (reset)
|
if (reset)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using SystemTrayMenu.DataClasses;
|
using SystemTrayMenu.DataClasses;
|
||||||
using SystemTrayMenu.DllImports;
|
using SystemTrayMenu.DllImports;
|
||||||
|
@ -35,6 +36,7 @@ namespace SystemTrayMenu.UserInterface
|
||||||
|
|
||||||
private readonly Fading fading = new Fading();
|
private readonly Fading fading = new Fading();
|
||||||
private bool autoResizeRowsDone = false;
|
private bool autoResizeRowsDone = false;
|
||||||
|
private bool isShowing = false;
|
||||||
|
|
||||||
internal Menu()
|
internal Menu()
|
||||||
{
|
{
|
||||||
|
@ -50,18 +52,22 @@ namespace SystemTrayMenu.UserInterface
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
isShowing = true;
|
||||||
Visible = true;
|
Visible = true;
|
||||||
|
isShowing = false;
|
||||||
}
|
}
|
||||||
catch (ObjectDisposedException)
|
catch (ObjectDisposedException)
|
||||||
{
|
{
|
||||||
Visible = false;
|
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}");
|
$" IsDisposed={IsDisposed}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Visible)
|
if (Visible)
|
||||||
{
|
{
|
||||||
Activate();
|
Activate();
|
||||||
|
NativeMethods.User32ShowInactiveTopmost(this);
|
||||||
NativeMethods.ForceForegroundWindow(Handle);
|
NativeMethods.ForceForegroundWindow(Handle);
|
||||||
SetTitleColorActive();
|
SetTitleColorActive();
|
||||||
}
|
}
|
||||||
|
@ -201,7 +207,10 @@ namespace SystemTrayMenu.UserInterface
|
||||||
|
|
||||||
internal void HideWithFade()
|
internal void HideWithFade()
|
||||||
{
|
{
|
||||||
fading.Fade(Fading.FadingState.Hide);
|
if(!isShowing)
|
||||||
|
{
|
||||||
|
fading.Fade(Fading.FadingState.Hide);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AdjustLocationAndSize(Screen screen)
|
internal void AdjustLocationAndSize(Screen screen)
|
||||||
|
|
|
@ -1,188 +1,329 @@
|
||||||
using System;
|
using Shell32;
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Threading;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace SystemTrayMenu.Utilities
|
namespace SystemTrayMenu.Utilities
|
||||||
{
|
{
|
||||||
internal class LnkHelper
|
internal class LnkHelper
|
||||||
{
|
{
|
||||||
[Flags()]
|
public static string GetResolvedFileName(string shortcutFilename)
|
||||||
private enum SLGP_FLAGS
|
|
||||||
{
|
{
|
||||||
/// <summary>Retrieves the standard short (8.3 format) file name</summary>
|
string resolvedFilename = string.Empty;
|
||||||
SLGP_SHORTPATH = 0x1,
|
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
|
||||||
/// <summary>Retrieves the Universal Naming Convention (UNC) path name of the file</summary>
|
|
||||||
SLGP_UNCPRIORITY = 0x2,
|
|
||||||
/// <summary>Retrieves the raw path name. A raw path is something that might not exist and may include environment variables that need to be expanded</summary>
|
|
||||||
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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
SLR_NO_UI = 0x1,
|
|
||||||
/// <summary>Obsolete and no longer used</summary>
|
|
||||||
SLR_ANY_MATCH = 0x2,
|
|
||||||
/// <summary>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.</summary>
|
|
||||||
SLR_UPDATE = 0x4,
|
|
||||||
/// <summary>Do not update the link information</summary>
|
|
||||||
SLR_NOUPDATE = 0x8,
|
|
||||||
/// <summary>Do not execute the search heuristics</summary>
|
|
||||||
SLR_NOSEARCH = 0x10,
|
|
||||||
/// <summary>Do not use distributed link tracking</summary>
|
|
||||||
SLR_NOTRACK = 0x20,
|
|
||||||
/// <summary>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.</summary>
|
|
||||||
SLR_NOLINKINFO = 0x40,
|
|
||||||
/// <summary>Call the Microsoft Windows Installer</summary>
|
|
||||||
SLR_INVOKE_MSI = 0x80
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>The IShellLink interface allows Shell links to be created, modified, and resolved</summary>
|
|
||||||
[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")]
|
|
||||||
private interface IShellLinkW
|
|
||||||
{
|
|
||||||
/// <summary>Retrieves the path and file name of a Shell link object</summary>
|
|
||||||
void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags);
|
|
||||||
/// <summary>Retrieves the list of item identifiers for a Shell link object</summary>
|
|
||||||
void GetIDList(out IntPtr ppidl);
|
|
||||||
/// <summary>Sets the pointer to an item identifier list (PIDL) for a Shell link object.</summary>
|
|
||||||
void SetIDList(IntPtr pidl);
|
|
||||||
/// <summary>Retrieves the description string for a Shell link object</summary>
|
|
||||||
void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
|
|
||||||
/// <summary>Sets the description for a Shell link object. The description can be any application-defined string</summary>
|
|
||||||
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
|
|
||||||
/// <summary>Retrieves the name of the working directory for a Shell link object</summary>
|
|
||||||
void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
|
|
||||||
/// <summary>Sets the name of the working directory for a Shell link object</summary>
|
|
||||||
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
|
|
||||||
/// <summary>Retrieves the command-line arguments associated with a Shell link object</summary>
|
|
||||||
void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
|
|
||||||
/// <summary>Sets the command-line arguments for a Shell link object</summary>
|
|
||||||
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
|
|
||||||
/// <summary>Retrieves the hot key for a Shell link object</summary>
|
|
||||||
void GetHotkey(out short pwHotkey);
|
|
||||||
/// <summary>Sets a hot key for a Shell link object</summary>
|
|
||||||
void SetHotkey(short wHotkey);
|
|
||||||
/// <summary>Retrieves the show command for a Shell link object</summary>
|
|
||||||
void GetShowCmd(out int piShowCmd);
|
|
||||||
/// <summary>Sets the show command for a Shell link object. The show command sets the initial show state of the window.</summary>
|
|
||||||
void SetShowCmd(int iShowCmd);
|
|
||||||
/// <summary>Retrieves the location (path and index) of the icon for a Shell link object</summary>
|
|
||||||
void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath,
|
|
||||||
int cchIconPath, out int piIcon);
|
|
||||||
/// <summary>Sets the location (path and index) of the icon for a Shell link object</summary>
|
|
||||||
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
|
|
||||||
/// <summary>Sets the relative path to the Shell link object</summary>
|
|
||||||
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
|
|
||||||
/// <summary>Attempts to find the target of a Shell link, even if it has been moved or renamed</summary>
|
|
||||||
void Resolve(IntPtr hwnd, SLR_FLAGS fFlags);
|
|
||||||
/// <summary>Sets the path and file name of a Shell link object</summary>
|
|
||||||
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
|
resolvedFilename = GetShortcutFileNamePath(shortcutFilename);
|
||||||
//resolved path wrong to Program Files (x86)
|
}
|
||||||
resolvedPath = ReplaceFirst(resolvedPath,
|
else
|
||||||
@"\Program Files (x86)\",
|
{
|
||||||
@"\Program Files\");
|
Thread staThread = new Thread(new ParameterizedThreadStart(StaThreadMethod));
|
||||||
if (!File.Exists(resolvedPath))
|
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
|
||||||
|
// {
|
||||||
|
// /// <summary>Retrieves the standard short (8.3 format) file name</summary>
|
||||||
|
// SLGP_SHORTPATH = 0x1,
|
||||||
|
// /// <summary>Retrieves the Universal Naming Convention (UNC) path name of the file</summary>
|
||||||
|
// SLGP_UNCPRIORITY = 0x2,
|
||||||
|
// /// <summary>Retrieves the raw path name. A raw path is something that might not exist and may include environment variables that need to be expanded</summary>
|
||||||
|
// 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
|
||||||
|
// {
|
||||||
|
// /// <summary>
|
||||||
|
// /// 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.
|
||||||
|
// /// </summary>
|
||||||
|
// SLR_NO_UI = 0x1,
|
||||||
|
// /// <summary>Obsolete and no longer used</summary>
|
||||||
|
// SLR_ANY_MATCH = 0x2,
|
||||||
|
// /// <summary>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.</summary>
|
||||||
|
// SLR_UPDATE = 0x4,
|
||||||
|
// /// <summary>Do not update the link information</summary>
|
||||||
|
// SLR_NOUPDATE = 0x8,
|
||||||
|
// /// <summary>Do not execute the search heuristics</summary>
|
||||||
|
// SLR_NOSEARCH = 0x10,
|
||||||
|
// /// <summary>Do not use distributed link tracking</summary>
|
||||||
|
// SLR_NOTRACK = 0x20,
|
||||||
|
// /// <summary>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.</summary>
|
||||||
|
// SLR_NOLINKINFO = 0x40,
|
||||||
|
// /// <summary>Call the Microsoft Windows Installer</summary>
|
||||||
|
// SLR_INVOKE_MSI = 0x80
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// <summary>The IShellLink interface allows Shell links to be created, modified, and resolved</summary>
|
||||||
|
// [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")]
|
||||||
|
// private interface IShellLinkW
|
||||||
|
// {
|
||||||
|
// /// <summary>Retrieves the path and file name of a Shell link object</summary>
|
||||||
|
// void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags);
|
||||||
|
// /// <summary>Retrieves the list of item identifiers for a Shell link object</summary>
|
||||||
|
// void GetIDList(out IntPtr ppidl);
|
||||||
|
// /// <summary>Sets the pointer to an item identifier list (PIDL) for a Shell link object.</summary>
|
||||||
|
// void SetIDList(IntPtr pidl);
|
||||||
|
// /// <summary>Retrieves the description string for a Shell link object</summary>
|
||||||
|
// void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
|
||||||
|
// /// <summary>Sets the description for a Shell link object. The description can be any application-defined string</summary>
|
||||||
|
// void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
|
||||||
|
// /// <summary>Retrieves the name of the working directory for a Shell link object</summary>
|
||||||
|
// void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
|
||||||
|
// /// <summary>Sets the name of the working directory for a Shell link object</summary>
|
||||||
|
// void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
|
||||||
|
// /// <summary>Retrieves the command-line arguments associated with a Shell link object</summary>
|
||||||
|
// void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
|
||||||
|
// /// <summary>Sets the command-line arguments for a Shell link object</summary>
|
||||||
|
// void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
|
||||||
|
// /// <summary>Retrieves the hot key for a Shell link object</summary>
|
||||||
|
// void GetHotkey(out short pwHotkey);
|
||||||
|
// /// <summary>Sets a hot key for a Shell link object</summary>
|
||||||
|
// void SetHotkey(short wHotkey);
|
||||||
|
// /// <summary>Retrieves the show command for a Shell link object</summary>
|
||||||
|
// void GetShowCmd(out int piShowCmd);
|
||||||
|
// /// <summary>Sets the show command for a Shell link object. The show command sets the initial show state of the window.</summary>
|
||||||
|
// void SetShowCmd(int iShowCmd);
|
||||||
|
// /// <summary>Retrieves the location (path and index) of the icon for a Shell link object</summary>
|
||||||
|
// void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath,
|
||||||
|
// int cchIconPath, out int piIcon);
|
||||||
|
// /// <summary>Sets the location (path and index) of the icon for a Shell link object</summary>
|
||||||
|
// void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
|
||||||
|
// /// <summary>Sets the relative path to the Shell link object</summary>
|
||||||
|
// void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
|
||||||
|
// /// <summary>Attempts to find the target of a Shell link, even if it has been moved or renamed</summary>
|
||||||
|
// void Resolve(IntPtr hwnd, SLR_FLAGS fFlags);
|
||||||
|
// /// <summary>Sets the path and file name of a Shell link object</summary>
|
||||||
|
// 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)
|
public static bool IsDirectory(string filePath)
|
||||||
{
|
{
|
||||||
bool isDirectory = false;
|
bool isDirectory = false;
|
||||||
|
@ -198,15 +339,15 @@ namespace SystemTrayMenu.Utilities
|
||||||
return isDirectory;
|
return isDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ReplaceFirst(string text, string search, string replace)
|
// public static string ReplaceFirst(string text, string search, string replace)
|
||||||
{
|
// {
|
||||||
int pos = text.IndexOf(search, StringComparison.InvariantCulture);
|
// int pos = text.IndexOf(search, StringComparison.InvariantCulture);
|
||||||
if (pos < 0)
|
// if (pos < 0)
|
||||||
{
|
// {
|
||||||
return text;
|
// return text;
|
||||||
}
|
// }
|
||||||
return text.Substring(0, pos) + replace +
|
// return text.Substring(0, pos) + replace +
|
||||||
text.Substring(pos + search.Length);
|
// text.Substring(pos + search.Length);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue