mirror of
https://github.com/Hofknecht/SystemTrayMenu.git
synced 2024-06-16 01:14:54 +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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.DataClasses;
|
||||
using SystemTrayMenu.Handler;
|
||||
using SystemTrayMenu.Helper;
|
||||
using SystemTrayMenu.Business;
|
||||
using SystemTrayMenu.UserInterface;
|
||||
using SystemTrayMenu.Utilities;
|
||||
using Menu = SystemTrayMenu.UserInterface.Menu;
|
||||
|
||||
namespace SystemTrayMenu
|
||||
{
|
||||
internal class SystemTrayMenu : IDisposable
|
||||
{
|
||||
private enum OpenCloseState { Default, Opening, Closing };
|
||||
private OpenCloseState openCloseState = OpenCloseState.Default;
|
||||
private readonly MenuNotifyIcon menuNotifyIcon = null;
|
||||
private readonly Menu[] menus = new Menu[MenuDefines.MenusMax];
|
||||
private readonly BackgroundWorker worker = new BackgroundWorker();
|
||||
private readonly Screen screen = Screen.PrimaryScreen;
|
||||
private readonly WaitLeave waitLeave = new WaitLeave(MenuDefines.TimeUntilClose);
|
||||
private readonly KeyboardInput keyboardInput;
|
||||
private readonly Timer timerStillActiveCheck = new Timer();
|
||||
private DateTime deactivatedTime = DateTime.Now;
|
||||
private readonly MenuNotifyIcon menuNotifyIcon = new MenuNotifyIcon();
|
||||
private Menus menus = new Menus();
|
||||
|
||||
public SystemTrayMenu()
|
||||
{
|
||||
AppRestart.BeforeRestarting += Dispose;
|
||||
SystemEvents.DisplaySettingsChanged += AppRestart.ByDisplaySettings;
|
||||
waitLeave.LeaveTriggered += WaitLeave_LeaveTriggered;
|
||||
void WaitLeave_LeaveTriggered()
|
||||
{
|
||||
FadeHalfOrOutIfNeeded();
|
||||
}
|
||||
keyboardInput = new KeyboardInput(menus);
|
||||
keyboardInput.RegisterHotKey();
|
||||
keyboardInput.HotKeyPressed += SwitchOpenClose;
|
||||
keyboardInput.ClosePressed += MenusFadeOut;
|
||||
keyboardInput.RowDeselected += CheckMenuOpenerStop;
|
||||
keyboardInput.RowSelected += KeyboardInputRowSelected;
|
||||
void KeyboardInputRowSelected(DataGridView dgv, int rowIndex)
|
||||
{
|
||||
keyboardInput.InUse = true;
|
||||
FadeInIfNeeded();
|
||||
CheckMenuOpenerStart(dgv, rowIndex);
|
||||
}
|
||||
|
||||
menuNotifyIcon = new MenuNotifyIcon();
|
||||
menus.LoadStarted += menuNotifyIcon.LoadingStart;
|
||||
menus.LoadStopped += menuNotifyIcon.LoadingStop;
|
||||
menuNotifyIcon.Exit += Application.Exit;
|
||||
menuNotifyIcon.Restart += AppRestart.ByMenuNotifyIcon;
|
||||
menuNotifyIcon.HandleClick += SwitchOpenClose;
|
||||
void SwitchOpenClose()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Config.Path) ||
|
||||
(DateTime.Now - deactivatedTime).TotalMilliseconds < 300)
|
||||
{
|
||||
//Case when Folder Dialog open or restarts to fast
|
||||
}
|
||||
else if (openCloseState == OpenCloseState.Opening ||
|
||||
menus[0].Visible && openCloseState == OpenCloseState.Default)
|
||||
{
|
||||
openCloseState = OpenCloseState.Closing;
|
||||
MenusFadeOut();
|
||||
if (worker.IsBusy)
|
||||
{
|
||||
worker.CancelAsync();
|
||||
}
|
||||
if (!Menus().Any(m => m.Visible))
|
||||
{
|
||||
openCloseState = OpenCloseState.Default;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
openCloseState = OpenCloseState.Opening;
|
||||
menuNotifyIcon.LoadingStart(true);
|
||||
worker.RunWorkerAsync();
|
||||
}
|
||||
}
|
||||
|
||||
worker.WorkerSupportsCancellation = true;
|
||||
worker.DoWork += Load;
|
||||
void Load(object sender, DoWorkEventArgs e)
|
||||
{
|
||||
e.Result = ReadMenu((BackgroundWorker)sender, Config.Path, 0);
|
||||
}
|
||||
|
||||
worker.RunWorkerCompleted += LoadCompleted;
|
||||
void LoadCompleted(object sender, RunWorkerCompletedEventArgs e)
|
||||
{
|
||||
keyboardInput.ResetSelectedByKey();
|
||||
menuNotifyIcon.LoadingStop();
|
||||
MenuData menuData = (MenuData)e.Result;
|
||||
if (menuData.Validity == MenuDataValidity.Valid)
|
||||
{
|
||||
DisposeMenu(menus[0]);
|
||||
menus[0] = CreateMenu(menuData, Path.GetFileName(Config.Path));
|
||||
menus[0].AdjustLocationAndSize(screen);
|
||||
Menus().ToList().ForEach(m => { m.ShowWithFade(); });
|
||||
}
|
||||
}
|
||||
|
||||
menuNotifyIcon.Click += menus.SwitchOpenClose;
|
||||
menuNotifyIcon.OpenLog += Log.OpenLogFile;
|
||||
menuNotifyIcon.ChangeFolder += ChangeFolder;
|
||||
void ChangeFolder()
|
||||
|
@ -116,617 +31,13 @@ namespace SystemTrayMenu
|
|||
}
|
||||
}
|
||||
|
||||
menus[0] = CreateMenu(ReadMenu(worker, Config.Path, 0),
|
||||
Path.GetFileName(Config.Path));
|
||||
menus[0].AdjustLocationAndSize(screen);
|
||||
DisposeMenu(menus[0]);
|
||||
menus.MainPreload();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
worker.Dispose();
|
||||
keyboardInput.Dispose();
|
||||
menus.Dispose();
|
||||
menuNotifyIcon.Dispose();
|
||||
waitLeave.Dispose();
|
||||
timerStillActiveCheck.Dispose();
|
||||
DisposeMenu(menus[0]);
|
||||
IconReader.Dispose();
|
||||
}
|
||||
|
||||
private void DisposeMenu(Menu menuToDispose)
|
||||
{
|
||||
if (menuToDispose != null)
|
||||
{
|
||||
DataGridView dgv = menuToDispose.GetDataGridView();
|
||||
foreach (DataGridViewRow row in dgv.Rows)
|
||||
{
|
||||
RowData rowData = (RowData)row.Tag;
|
||||
rowData.Dispose();
|
||||
DisposeMenu(rowData.SubMenu);
|
||||
}
|
||||
dgv.ClearSelection();
|
||||
menuToDispose.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static MenuData ReadMenu(BackgroundWorker worker, string path, int level)
|
||||
{
|
||||
MenuData menuData = new MenuData
|
||||
{
|
||||
RowDatas = new List<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)
|
||||
{
|
||||
bool handled = false;
|
||||
resolvedLnkPath = LnkHelper.ResolveShortcut(TargetFilePath);
|
||||
resolvedLnkPath = LnkHelper.GetResolvedFileName(TargetFilePath);
|
||||
if (LnkHelper.IsDirectory(resolvedLnkPath))
|
||||
{
|
||||
Icon = IconReader.GetFolderIcon(TargetFilePath,
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
private const int Interval60FPS = 16; //60fps=>1s/60fps=~16.6ms
|
||||
|
||||
private const double StepIn = 0.20;
|
||||
private const double StepOut = 0.05;
|
||||
private const double StepOut = 0.10;
|
||||
private const double Transparent = 0.80;
|
||||
private const double TransparentMinus = 0.60; //Transparent - StepIn
|
||||
private const double TransparentPlus = 0.85; //Transparent + StepOut
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.Utilities;
|
||||
|
||||
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>
|
||||
/// Registers a hot key in the system.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
internal void RegisterHotKey(KeyboardHookModifierKeys modifier, Keys key)
|
||||
{
|
||||
RegisterHotKey((uint)modifier, key);
|
||||
}
|
||||
|
||||
private void RegisterHotKey(uint modifier, Keys key)
|
||||
{
|
||||
_currentId = _currentId + 1;
|
||||
|
||||
if (!DllImports.NativeMethods.User32RegisterHotKey(_window.Handle, _currentId, (uint)modifier, (uint)key))
|
||||
if (!DllImports.NativeMethods.User32RegisterHotKey(
|
||||
_window.Handle, _currentId, modifier, (uint)key))
|
||||
{
|
||||
#pragma warning disable CA1303 // Do not pass literals as localized parameters
|
||||
throw new InvalidOperationException("Couldn’t register the hot key.");
|
||||
#pragma warning restore CA1303 //=> Exceptions not translated in logfile => OK
|
||||
throw new InvalidOperationException(
|
||||
Language.Translate("Could not register the hot key."));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
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.
|
||||
|
||||
TAMAHO SystemTrayMenu - an improved Windows toolbar
|
||||
Copyright (C) 2019 Markus Hofknecht
|
||||
Copyright (C) 2020 Markus Hofknecht
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -652,7 +652,7 @@ You can contact me by mail Markus@Hofknecht.eu
|
|||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
TAMAHO SystemTrayMenu Copyright (C) 2019 Markus Hofknecht
|
||||
TAMAHO SystemTrayMenu Copyright (C) 2020 Markus Hofknecht
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
|
|
@ -35,5 +35,5 @@ using System.Runtime.InteropServices;
|
|||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("0.9.2.22")]
|
||||
[assembly: AssemblyFileVersion("0.9.2.22")]
|
||||
[assembly: AssemblyVersion("0.9.2.23")]
|
||||
[assembly: AssemblyFileVersion("0.9.2.23")]
|
||||
|
|
75
Resources/lang.Designer.cs
generated
75
Resources/lang.Designer.cs
generated
|
@ -1,10 +1,10 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// Dieser Code wurde von einem Tool generiert.
|
||||
// Laufzeitversion:4.0.30319.42000
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
|
||||
// der Code erneut generiert wird.
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
@ -13,12 +13,12 @@ namespace SystemTrayMenu.Resources {
|
|||
|
||||
|
||||
/// <summary>
|
||||
/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
|
||||
// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
|
||||
// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
|
||||
// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
|
@ -33,7 +33,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
|
@ -47,8 +47,8 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
|
||||
/// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
|
@ -61,7 +61,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die (e.g. F10) ähnelt.
|
||||
/// Looks up a localized string similar to (e.g. F10).
|
||||
/// </summary>
|
||||
internal static string _e_g__F10_ {
|
||||
get {
|
||||
|
@ -70,7 +70,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die About ähnelt.
|
||||
/// Looks up a localized string similar to About.
|
||||
/// </summary>
|
||||
internal static string About {
|
||||
get {
|
||||
|
@ -79,7 +79,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Activate autostart ähnelt.
|
||||
/// Looks up a localized string similar to Activate autostart.
|
||||
/// </summary>
|
||||
internal static string Activate_autostart {
|
||||
get {
|
||||
|
@ -88,7 +88,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die ALT ähnelt.
|
||||
/// Looks up a localized string similar to ALT.
|
||||
/// </summary>
|
||||
internal static string ALT {
|
||||
get {
|
||||
|
@ -97,7 +97,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Autostart ähnelt.
|
||||
/// Looks up a localized string similar to Autostart.
|
||||
/// </summary>
|
||||
internal static string Autostart {
|
||||
get {
|
||||
|
@ -106,7 +106,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Details ähnelt.
|
||||
/// Looks up a localized string similar to Details.
|
||||
/// </summary>
|
||||
internal static string buttonDetails {
|
||||
get {
|
||||
|
@ -115,7 +115,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die OK ähnelt.
|
||||
/// Looks up a localized string similar to OK.
|
||||
/// </summary>
|
||||
internal static string buttonOk {
|
||||
get {
|
||||
|
@ -124,7 +124,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die System Info ähnelt.
|
||||
/// Looks up a localized string similar to System Info.
|
||||
/// </summary>
|
||||
internal static string buttonSystemInfo {
|
||||
get {
|
||||
|
@ -133,7 +133,16 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <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>
|
||||
internal static string CTRL {
|
||||
get {
|
||||
|
@ -142,7 +151,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die English ähnelt.
|
||||
/// Looks up a localized string similar to English.
|
||||
/// </summary>
|
||||
internal static string English {
|
||||
get {
|
||||
|
@ -151,7 +160,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Exit ähnelt.
|
||||
/// Looks up a localized string similar to Exit.
|
||||
/// </summary>
|
||||
internal static string Exit {
|
||||
get {
|
||||
|
@ -160,7 +169,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Folder ähnelt.
|
||||
/// Looks up a localized string similar to Folder.
|
||||
/// </summary>
|
||||
internal static string Folder {
|
||||
get {
|
||||
|
@ -169,7 +178,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Folder empty ähnelt.
|
||||
/// Looks up a localized string similar to Folder empty.
|
||||
/// </summary>
|
||||
internal static string Folder_empty {
|
||||
get {
|
||||
|
@ -178,7 +187,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Folder inaccessible ähnelt.
|
||||
/// Looks up a localized string similar to Folder inaccessible.
|
||||
/// </summary>
|
||||
internal static string Folder_inaccessible {
|
||||
get {
|
||||
|
@ -187,7 +196,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Deutsch ähnelt.
|
||||
/// Looks up a localized string similar to Deutsch.
|
||||
/// </summary>
|
||||
internal static string German {
|
||||
get {
|
||||
|
@ -196,7 +205,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <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>
|
||||
internal static string HintDragDropText {
|
||||
get {
|
||||
|
@ -205,7 +214,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die SystemTrayMenu - Hint ähnelt.
|
||||
/// Looks up a localized string similar to SystemTrayMenu - Hint.
|
||||
/// </summary>
|
||||
internal static string HintDragDropTitle {
|
||||
get {
|
||||
|
@ -214,7 +223,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Language ähnelt.
|
||||
/// Looks up a localized string similar to Language.
|
||||
/// </summary>
|
||||
internal static string Language {
|
||||
get {
|
||||
|
@ -223,7 +232,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Log File ähnelt.
|
||||
/// Looks up a localized string similar to Log File.
|
||||
/// </summary>
|
||||
internal static string Log_File {
|
||||
get {
|
||||
|
@ -232,7 +241,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Restart ähnelt.
|
||||
/// Looks up a localized string similar to Restart.
|
||||
/// </summary>
|
||||
internal static string Restart {
|
||||
get {
|
||||
|
@ -241,7 +250,7 @@ namespace SystemTrayMenu.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sucht eine lokalisierte Zeichenfolge, die Shortcut key ähnelt.
|
||||
/// Looks up a localized string similar to Shortcut key.
|
||||
/// </summary>
|
||||
internal static string Shortcut_key {
|
||||
get {
|
||||
|
|
|
@ -180,4 +180,7 @@
|
|||
<data name="Shortcut key" xml:space="preserve">
|
||||
<value>Tastenkombination</value>
|
||||
</data>
|
||||
<data name="Could not register the hot key." xml:space="preserve">
|
||||
<value>Der Tastenkürzel konnte nicht registriert werden.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -180,4 +180,7 @@
|
|||
<data name="Shortcut key" xml:space="preserve">
|
||||
<value>Shortcut key</value>
|
||||
</data>
|
||||
<data name="Could not register the hot key." xml:space="preserve">
|
||||
<value>Couldn’t register the hot key.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -138,6 +138,7 @@
|
|||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Business\Menus.cs" />
|
||||
<Compile Include="Config\Config.cs" />
|
||||
<Compile Include="NativeDllImport\DestroyMenu.cs" />
|
||||
<Compile Include="NativeDllImport\CreatePopupMenu.cs" />
|
||||
|
@ -177,6 +178,7 @@
|
|||
<Compile Include="Utilities\FolderOptions.cs" />
|
||||
<Compile Include="Helpers\KeyboardHook.cs" />
|
||||
<Compile Include="Utilities\Log.cs" />
|
||||
<Compile Include="Utilities\Shares.cs" />
|
||||
<Compile Include="Utilities\SingleAppInstance.cs" />
|
||||
<Compile Include="Utilities\Language.cs" />
|
||||
<Compile Include="Utilities\Scaling.cs" />
|
||||
|
@ -260,10 +262,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources\White50Percentage.ico" />
|
||||
<None Include="Resources\STM.ico" />
|
||||
<None Include="Resources\STM_black.ico" />
|
||||
<None Include="Resources\Selected.png" />
|
||||
<None Include="Resources\hintDragDrop.png" />
|
||||
<None Include="Resources\L010.ico" />
|
||||
<None Include="Resources\L020.ico" />
|
||||
<None Include="Resources\L030.ico" />
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
{
|
||||
internal class MenuNotifyIcon : IDisposable
|
||||
{
|
||||
public event EventHandlerEmpty HandleClick;
|
||||
public event EventHandlerEmpty Click;
|
||||
public event EventHandlerEmpty ChangeFolder;
|
||||
public event EventHandlerEmpty OpenLog;
|
||||
public event EventHandlerEmpty Restart;
|
||||
|
@ -69,7 +69,6 @@ namespace SystemTrayMenu.UserInterface
|
|||
{
|
||||
VerifyClick(e);
|
||||
}
|
||||
|
||||
notifyIcon.MouseDoubleClick += NotifyIcon_MouseDoubleClick;
|
||||
void NotifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
|
||||
{
|
||||
|
@ -81,7 +80,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
{
|
||||
if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
HandleClick.Invoke();
|
||||
Click?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,7 +91,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
load.Dispose();
|
||||
}
|
||||
|
||||
public void LoadingStart(bool reset = false)
|
||||
public void LoadingStart(object sender = null, bool reset = false)
|
||||
{
|
||||
if (reset)
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.DataClasses;
|
||||
using SystemTrayMenu.DllImports;
|
||||
|
@ -35,6 +36,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
|
||||
private readonly Fading fading = new Fading();
|
||||
private bool autoResizeRowsDone = false;
|
||||
private bool isShowing = false;
|
||||
|
||||
internal Menu()
|
||||
{
|
||||
|
@ -50,18 +52,22 @@ namespace SystemTrayMenu.UserInterface
|
|||
{
|
||||
try
|
||||
{
|
||||
isShowing = true;
|
||||
Visible = true;
|
||||
isShowing = false;
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
Visible = false;
|
||||
Log.Info($"Could not open menu, menu already closed," +
|
||||
isShowing = false;
|
||||
Log.Info($"Could not open menu, old menu was disposing," +
|
||||
$" IsDisposed={IsDisposed}");
|
||||
}
|
||||
|
||||
if (Visible)
|
||||
{
|
||||
Activate();
|
||||
NativeMethods.User32ShowInactiveTopmost(this);
|
||||
NativeMethods.ForceForegroundWindow(Handle);
|
||||
SetTitleColorActive();
|
||||
}
|
||||
|
@ -201,7 +207,10 @@ namespace SystemTrayMenu.UserInterface
|
|||
|
||||
internal void HideWithFade()
|
||||
{
|
||||
fading.Fade(Fading.FadingState.Hide);
|
||||
if(!isShowing)
|
||||
{
|
||||
fading.Fade(Fading.FadingState.Hide);
|
||||
}
|
||||
}
|
||||
|
||||
internal void AdjustLocationAndSize(Screen screen)
|
||||
|
|
|
@ -1,188 +1,329 @@
|
|||
using System;
|
||||
using Shell32;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace SystemTrayMenu.Utilities
|
||||
{
|
||||
internal class LnkHelper
|
||||
{
|
||||
[Flags()]
|
||||
private enum SLGP_FLAGS
|
||||
public static string GetResolvedFileName(string shortcutFilename)
|
||||
{
|
||||
/// <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))
|
||||
string resolvedFilename = string.Empty;
|
||||
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
|
||||
{
|
||||
//For some lnk e.g. WinRar SkypeForBuisness
|
||||
//resolved path wrong to Program Files (x86)
|
||||
resolvedPath = ReplaceFirst(resolvedPath,
|
||||
@"\Program Files (x86)\",
|
||||
@"\Program Files\");
|
||||
if (!File.Exists(resolvedPath))
|
||||
resolvedFilename = GetShortcutFileNamePath(shortcutFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread staThread = new Thread(new ParameterizedThreadStart(StaThreadMethod));
|
||||
void StaThreadMethod(object obj)
|
||||
{
|
||||
resolvedPath = string.Empty;
|
||||
resolvedFilename = GetShortcutFileNamePath(shortcutFilename);
|
||||
}
|
||||
staThread.SetApartmentState(ApartmentState.STA);
|
||||
staThread.Start(shortcutFilename);
|
||||
staThread.Join();
|
||||
}
|
||||
|
||||
return resolvedFilename;
|
||||
}
|
||||
|
||||
|
||||
private static string GetShortcutFileNamePath(object shortcutFilename)
|
||||
{
|
||||
string resolvedFilename = string.Empty;
|
||||
string pathOnly = Path.GetDirectoryName((string)shortcutFilename);
|
||||
string filenameOnly = Path.GetFileName((string)shortcutFilename);
|
||||
|
||||
Shell shell = new Shell();
|
||||
Folder folder = shell.NameSpace(pathOnly);
|
||||
FolderItem folderItem = folder.ParseName(filenameOnly);
|
||||
if (folderItem != null)
|
||||
{
|
||||
ShellLinkObject link = (ShellLinkObject)folderItem.GetLink;
|
||||
if (string.IsNullOrEmpty(link.Path))
|
||||
{
|
||||
resolvedFilename = link.Target.Path;
|
||||
}
|
||||
else
|
||||
{
|
||||
resolvedFilename = link.Path;
|
||||
}
|
||||
}
|
||||
return resolvedPath;
|
||||
|
||||
return resolvedFilename;
|
||||
}
|
||||
|
||||
// [Flags()]
|
||||
// private enum SLGP_FLAGS
|
||||
// {
|
||||
// /// <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)
|
||||
{
|
||||
bool isDirectory = false;
|
||||
|
@ -198,15 +339,15 @@ namespace SystemTrayMenu.Utilities
|
|||
return isDirectory;
|
||||
}
|
||||
|
||||
public static string ReplaceFirst(string text, string search, string replace)
|
||||
{
|
||||
int pos = text.IndexOf(search, StringComparison.InvariantCulture);
|
||||
if (pos < 0)
|
||||
{
|
||||
return text;
|
||||
}
|
||||
return text.Substring(0, pos) + replace +
|
||||
text.Substring(pos + search.Length);
|
||||
}
|
||||
// public static string ReplaceFirst(string text, string search, string replace)
|
||||
// {
|
||||
// int pos = text.IndexOf(search, StringComparison.InvariantCulture);
|
||||
// if (pos < 0)
|
||||
// {
|
||||
// return text;
|
||||
// }
|
||||
// return text.Substring(0, pos) + replace +
|
||||
// text.Substring(pos + search.Length);
|
||||
// }
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue