mirror of
https://github.com/Hofknecht/SystemTrayMenu.git
synced 2024-05-28 08:11:39 +12:00
Fix lots of nullable warnings
This commit is contained in:
parent
1d29fea766
commit
6993aef3ac
|
@ -5,6 +5,7 @@
|
|||
namespace SystemTrayMenu.Handler
|
||||
{
|
||||
using System;
|
||||
using System.Data.Common;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows.Input;
|
||||
|
@ -33,7 +34,7 @@ namespace SystemTrayMenu.Handler
|
|||
|
||||
internal event Action<ListView, int>? RowSelected;
|
||||
|
||||
internal event Action<int, ListView>? RowDeselected;
|
||||
internal event Action<int, ListView?>? RowDeselected;
|
||||
|
||||
internal event Action<ListView, int>? EnterPressed;
|
||||
|
||||
|
@ -169,10 +170,10 @@ namespace SystemTrayMenu.Handler
|
|||
break;
|
||||
}
|
||||
|
||||
int GetMenuIndex(in Menu currentMenu)
|
||||
int GetMenuIndex(in Menu? currentMenu)
|
||||
{
|
||||
int index = 0;
|
||||
foreach (Menu menuFindIndex in menus.Where(m => m != null))
|
||||
foreach (Menu? menuFindIndex in menus.Where(m => m != null))
|
||||
{
|
||||
if (currentMenu == menuFindIndex)
|
||||
{
|
||||
|
@ -252,7 +253,7 @@ namespace SystemTrayMenu.Handler
|
|||
ref Menu? menuFromSelected,
|
||||
ref string textselected)
|
||||
{
|
||||
Menu menu = menus[iMenuKey];
|
||||
Menu? menu = menus[iMenuKey];
|
||||
bool isStillSelected = false;
|
||||
if (menu != null &&
|
||||
iRowKey > -1)
|
||||
|
@ -282,7 +283,7 @@ namespace SystemTrayMenu.Handler
|
|||
int iRowBefore = iRowKey;
|
||||
int iMenuBefore = iMenuKey;
|
||||
|
||||
Menu menu = menus[iMenuKey];
|
||||
Menu? menu = menus[iMenuKey];
|
||||
ListView? dgv = null;
|
||||
ListView? dgvBefore = null;
|
||||
Menu? menuFromSelected = null;
|
||||
|
@ -305,7 +306,7 @@ namespace SystemTrayMenu.Handler
|
|||
{
|
||||
ResetSelectedByKey();
|
||||
menu = menus[iMenuKey];
|
||||
dgv = menu.GetDataGridView();
|
||||
dgv = menu?.GetDataGridView();
|
||||
}
|
||||
|
||||
bool toClear = false;
|
||||
|
@ -313,7 +314,7 @@ namespace SystemTrayMenu.Handler
|
|||
switch (key)
|
||||
{
|
||||
case Key.Enter:
|
||||
if ((modifiers == ModifierKeys.None) && (iRowKey > -1 && dgv.Items.Count > iRowKey))
|
||||
if ((modifiers == ModifierKeys.None) && (iRowKey > -1 && dgv != null && dgv.Items.Count > iRowKey))
|
||||
{
|
||||
RowData trigger = ((Menu.ListViewItemData)dgv.Items[iRowKey]).data;
|
||||
if (trigger.IsMenuOpen || !trigger.ContainsMenu)
|
||||
|
@ -340,9 +341,9 @@ namespace SystemTrayMenu.Handler
|
|||
}
|
||||
else
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
RowDeselected?.Invoke(iRowBefore, dgvBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
EnterPressed.Invoke(dgv, iRowKey);
|
||||
EnterPressed?.Invoke(dgv, iRowKey);
|
||||
}
|
||||
|
||||
handled = true;
|
||||
|
@ -351,10 +352,11 @@ namespace SystemTrayMenu.Handler
|
|||
break;
|
||||
case Key.Up:
|
||||
if ((modifiers == ModifierKeys.None) &&
|
||||
dgv != null &&
|
||||
(SelectMatchedReverse(dgv, iRowKey) ||
|
||||
SelectMatchedReverse(dgv, dgv.Items.Count - 1)))
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
RowDeselected?.Invoke(iRowBefore, dgvBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
handled = true;
|
||||
|
@ -366,7 +368,7 @@ namespace SystemTrayMenu.Handler
|
|||
(SelectMatched(dgv, iRowKey) ||
|
||||
SelectMatched(dgv, 0)))
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
RowDeselected?.Invoke(iRowBefore, dgvBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
handled = true;
|
||||
|
@ -376,7 +378,7 @@ namespace SystemTrayMenu.Handler
|
|||
case Key.Home:
|
||||
if ((modifiers == ModifierKeys.None) && SelectMatched(dgv, 0))
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
RowDeselected?.Invoke(iRowBefore, dgvBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
handled = true;
|
||||
|
@ -384,9 +386,11 @@ namespace SystemTrayMenu.Handler
|
|||
|
||||
break;
|
||||
case Key.End:
|
||||
if ((modifiers == ModifierKeys.None) && SelectMatchedReverse(dgv, dgv.Items.Count - 1))
|
||||
if ((modifiers == ModifierKeys.None) &&
|
||||
dgv != null &&
|
||||
SelectMatchedReverse(dgv, dgv.Items.Count - 1))
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
RowDeselected?.Invoke(iRowBefore, dgvBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
handled = true;
|
||||
|
@ -394,10 +398,14 @@ namespace SystemTrayMenu.Handler
|
|||
|
||||
break;
|
||||
case Key.Left:
|
||||
if (modifiers == ModifierKeys.None)
|
||||
if (modifiers == ModifierKeys.None &&
|
||||
dgv != null &&
|
||||
dgvBefore != null)
|
||||
{
|
||||
bool nextMenuLocationIsLeft = menus[iMenuKey + 1] != null && menus[iMenuKey + 1].Location.X < menus[iMenuKey].Location.X;
|
||||
bool previousMenuLocationIsRight = iMenuKey > 0 && menus[iMenuKey]?.Location.X < menus[iMenuKey - 1]?.Location.X;
|
||||
Menu? nextMenu = menus[iMenuKey + 1];
|
||||
bool nextMenuLocationIsLeft = nextMenu != null && menu != null && nextMenu.Location.X < menu.Location.X;
|
||||
Menu? previousMenu = menus[iMenuKey - 1];
|
||||
bool previousMenuLocationIsRight = iMenuKey > 0 && previousMenu != null && menu != null && menu.Location.X < previousMenu.Location.X;
|
||||
if (nextMenuLocationIsLeft || previousMenuLocationIsRight)
|
||||
{
|
||||
SelectNextMenu(iRowBefore, ref dgv, dgvBefore, menuFromSelected, isStillSelected, ref toClear);
|
||||
|
@ -412,7 +420,9 @@ namespace SystemTrayMenu.Handler
|
|||
|
||||
break;
|
||||
case Key.Right:
|
||||
if (modifiers == ModifierKeys.None)
|
||||
if (modifiers == ModifierKeys.None &&
|
||||
dgv != null &&
|
||||
dgvBefore != null)
|
||||
{
|
||||
bool nextMenuLocationIsRight = menus[iMenuKey + 1]?.Location.X > menus[iMenuKey]?.Location.X;
|
||||
bool previousMenuLocationIsLeft = iMenuKey > 0 && menus[iMenuKey]?.Location.X > menus[iMenuKey - 1]?.Location.X;
|
||||
|
@ -434,7 +444,7 @@ namespace SystemTrayMenu.Handler
|
|||
if ((key == Key.Escape && modifiers == ModifierKeys.None) ||
|
||||
(key == Key.F4 && modifiers == ModifierKeys.Alt))
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
RowDeselected?.Invoke(iRowBefore, dgvBefore);
|
||||
iMenuKey = 0;
|
||||
iRowKey = -1;
|
||||
toClear = true;
|
||||
|
@ -455,7 +465,7 @@ namespace SystemTrayMenu.Handler
|
|||
if (SelectMatched(dgv, iRowKey, keyInput) ||
|
||||
SelectMatched(dgv, 0, keyInput))
|
||||
{
|
||||
RowDeselected(iRowBefore, null);
|
||||
RowDeselected?.Invoke(iRowBefore, null);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
}
|
||||
|
@ -465,7 +475,7 @@ namespace SystemTrayMenu.Handler
|
|||
if (SelectMatched(dgv, iRowKey, keyInput) ||
|
||||
SelectMatched(dgv, 0, keyInput))
|
||||
{
|
||||
RowDeselected(iRowBefore, null);
|
||||
RowDeselected?.Invoke(iRowBefore, null);
|
||||
SelectRow(dgv, iRowKey);
|
||||
}
|
||||
else
|
||||
|
@ -482,7 +492,7 @@ namespace SystemTrayMenu.Handler
|
|||
}
|
||||
}
|
||||
|
||||
private void SelectPreviousMenu(int iRowBefore, ref Menu menu, ref ListView dgv, ListView dgvBefore, ref bool toClear)
|
||||
private void SelectPreviousMenu(int iRowBefore, ref Menu? menu, ref ListView? dgv, ListView? dgvBefore, ref bool toClear)
|
||||
{
|
||||
if (iMenuKey > 0)
|
||||
{
|
||||
|
@ -491,20 +501,22 @@ namespace SystemTrayMenu.Handler
|
|||
iMenuKey -= 1;
|
||||
iRowKey = -1;
|
||||
menu = menus[iMenuKey];
|
||||
dgv = menu.GetDataGridView();
|
||||
|
||||
if (SelectMatched(dgv, dgv.Items.IndexOf(dgv.SelectedItems.Count > 0 ? dgv.SelectedItems[0] : null)) ||
|
||||
SelectMatched(dgv, 0))
|
||||
dgv = menu?.GetDataGridView();
|
||||
if (dgv != null)
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
if (SelectMatched(dgv, dgv.Items.IndexOf(dgv.SelectedItems.Count > 0 ? dgv.SelectedItems[0] : null)) ||
|
||||
SelectMatched(dgv, 0))
|
||||
{
|
||||
RowDeselected?.Invoke(iRowBefore, dgvBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
RowDeselected?.Invoke(iRowBefore, dgvBefore);
|
||||
iMenuKey = 0;
|
||||
iRowKey = -1;
|
||||
toClear = true;
|
||||
|
@ -512,7 +524,7 @@ namespace SystemTrayMenu.Handler
|
|||
}
|
||||
}
|
||||
|
||||
private void SelectNextMenu(int iRowBefore, ref ListView dgv, ListView dgvBefore, Menu menuFromSelected, bool isStillSelected, ref bool toClear)
|
||||
private void SelectNextMenu(int iRowBefore, ref ListView? dgv, ListView dgvBefore, Menu? menuFromSelected, bool isStillSelected, ref bool toClear)
|
||||
{
|
||||
int iMenuKeyNext = iMenuKey + 1;
|
||||
if (isStillSelected)
|
||||
|
@ -520,15 +532,15 @@ namespace SystemTrayMenu.Handler
|
|||
if (menuFromSelected != null &&
|
||||
menuFromSelected == menus[iMenuKeyNext])
|
||||
{
|
||||
dgv = menuFromSelected.GetDataGridView();
|
||||
if (dgv.Items.Count > 0)
|
||||
dgv = menuFromSelected?.GetDataGridView();
|
||||
if (dgv != null && dgv.Items.Count > 0)
|
||||
{
|
||||
iMenuKey += 1;
|
||||
iRowKey = -1;
|
||||
if (SelectMatched(dgv, iRowKey) ||
|
||||
SelectMatched(dgv, 0))
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
RowDeselected?.Invoke(iRowBefore, dgvBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
}
|
||||
|
@ -539,13 +551,14 @@ namespace SystemTrayMenu.Handler
|
|||
{
|
||||
iRowKey = -1;
|
||||
iMenuKey = menus.Where(m => m != null).Count() - 1;
|
||||
if (menus[iMenuKey] != null)
|
||||
Menu? lastMenu = menus[iMenuKey];
|
||||
if (lastMenu != null)
|
||||
{
|
||||
dgv = menus[iMenuKey].GetDataGridView();
|
||||
dgv = lastMenu?.GetDataGridView();
|
||||
if (SelectMatched(dgv, iRowKey) ||
|
||||
SelectMatched(dgv, 0))
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
RowDeselected?.Invoke(iRowBefore, dgvBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
}
|
||||
|
@ -553,28 +566,34 @@ namespace SystemTrayMenu.Handler
|
|||
}
|
||||
}
|
||||
|
||||
private void SelectRow(ListView dgv, int iRowKey)
|
||||
private void SelectRow(ListView? dgv, int iRowKey)
|
||||
{
|
||||
InUse = true;
|
||||
RowSelected(dgv, iRowKey);
|
||||
if (dgv != null)
|
||||
{
|
||||
InUse = true;
|
||||
RowSelected?.Invoke(dgv, iRowKey);
|
||||
}
|
||||
}
|
||||
|
||||
private bool SelectMatched(ListView dgv, int indexStart, string keyInput = "")
|
||||
private bool SelectMatched(ListView? dgv, int indexStart, string keyInput = "")
|
||||
{
|
||||
bool found = false;
|
||||
for (int i = indexStart; i < dgv.Items.Count; i++)
|
||||
if (dgv != null)
|
||||
{
|
||||
if (Select(dgv, i, keyInput))
|
||||
for (int i = indexStart; i < dgv.Items.Count; i++)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
if (Select(dgv, i, keyInput))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
private bool SelectMatchedReverse(ListView dgv, int indexStart, string keyInput = "")
|
||||
private bool SelectMatchedReverse(ListView? dgv, int indexStart, string keyInput = "")
|
||||
{
|
||||
bool found = false;
|
||||
for (int i = indexStart; i > -1; i--)
|
||||
|
@ -589,11 +608,12 @@ namespace SystemTrayMenu.Handler
|
|||
return found;
|
||||
}
|
||||
|
||||
private bool Select(ListView dgv, int i, string keyInput = "")
|
||||
private bool Select(ListView? dgv, int i, string keyInput = "")
|
||||
{
|
||||
bool found = false;
|
||||
if (i > -1 &&
|
||||
i != iRowKey &&
|
||||
dgv != null &&
|
||||
dgv.Items.Count > i)
|
||||
{
|
||||
Menu.ListViewItemData itemData = (Menu.ListViewItemData)dgv.Items[i];
|
||||
|
@ -619,11 +639,11 @@ namespace SystemTrayMenu.Handler
|
|||
|
||||
private void ClearIsSelectedByKey(int menuIndex, int rowIndex)
|
||||
{
|
||||
Menu menu = menus[menuIndex];
|
||||
Menu? menu = menus[menuIndex];
|
||||
if (menu != null && rowIndex > -1)
|
||||
{
|
||||
ListView dgv = menu.GetDataGridView();
|
||||
if (dgv.Items.Count > rowIndex)
|
||||
ListView? dgv = menu?.GetDataGridView();
|
||||
if (dgv != null && dgv.Items.Count > rowIndex)
|
||||
{
|
||||
Menu.ListViewItemData itemData = (Menu.ListViewItemData)dgv.Items[rowIndex];
|
||||
RowData rowData = itemData.data;
|
||||
|
|
|
@ -326,7 +326,7 @@ namespace SystemTrayMenu.Business
|
|||
|
||||
private bool IsMainUsable => menus[0]?.IsUsable ?? false;
|
||||
|
||||
private IEnumerable<Menu> AsEnumerable => menus.Where(m => m != null && !m.IsDisposed) !;
|
||||
private IEnumerable<Menu> AsEnumerable => menus.Where(m => m != null && !m.IsDisposed)!;
|
||||
|
||||
private List<Menu> AsList => AsEnumerable.ToList();
|
||||
|
||||
|
|
|
@ -16,9 +16,9 @@ namespace SystemTrayMenu.Handler
|
|||
internal class WaitToLoadMenu : IDisposable
|
||||
{
|
||||
private readonly DispatcherTimer timerStartLoad = new();
|
||||
private ListView dgv;
|
||||
private ListView? dgv;
|
||||
private int rowIndex;
|
||||
private ListView dgvTmp;
|
||||
private ListView? dgvTmp;
|
||||
private int rowIndexTmp;
|
||||
private bool alreadyOpened;
|
||||
|
||||
|
@ -32,13 +32,13 @@ namespace SystemTrayMenu.Handler
|
|||
timerStartLoad.Tick += WaitStartLoad_Tick;
|
||||
}
|
||||
|
||||
internal event Action<RowData> StartLoadMenu;
|
||||
internal event Action<RowData>? StartLoadMenu;
|
||||
|
||||
internal event Action<int> CloseMenu;
|
||||
internal event Action<int>? CloseMenu;
|
||||
|
||||
internal event Action StopLoadMenu;
|
||||
internal event Action? StopLoadMenu;
|
||||
|
||||
internal event Action<ListView, int> MouseEnterOk;
|
||||
internal event Action<ListView, int>? MouseEnterOk;
|
||||
|
||||
internal bool MouseActive { get; set; }
|
||||
|
||||
|
@ -58,7 +58,7 @@ namespace SystemTrayMenu.Handler
|
|||
{
|
||||
if (dgv.Items.Count > rowIndex)
|
||||
{
|
||||
MouseEnterOk(dgv, rowIndex);
|
||||
MouseEnterOk?.Invoke(dgv, rowIndex);
|
||||
timerStartLoad.Stop();
|
||||
StopLoadMenu?.Invoke();
|
||||
checkForMouseActive = true;
|
||||
|
@ -96,7 +96,7 @@ namespace SystemTrayMenu.Handler
|
|||
}
|
||||
}
|
||||
|
||||
internal void RowDeselected(int rowIndex, ListView dgv)
|
||||
internal void RowDeselected(int rowIndex, ListView? dgv)
|
||||
{
|
||||
timerStartLoad.Stop();
|
||||
StopLoadMenu?.Invoke();
|
||||
|
@ -136,11 +136,11 @@ namespace SystemTrayMenu.Handler
|
|||
if (mouseMoveEvents > 6)
|
||||
{
|
||||
MouseActive = true;
|
||||
if (dgvTmp != null
|
||||
#if TODO // WPF: Can be optimized away?
|
||||
&& !dgvTmp.IsDisposed
|
||||
if (dgvTmp != null && !dgvTmp.IsDisposed)
|
||||
#else
|
||||
if (dgvTmp != null)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
MouseEnter(dgvTmp, rowIndexTmp);
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ namespace SystemTrayMenu.Handler
|
|||
}
|
||||
}
|
||||
|
||||
private void WaitStartLoad_Tick(object sender, EventArgs e)
|
||||
private void WaitStartLoad_Tick(object? sender, EventArgs e)
|
||||
{
|
||||
timerStartLoad.Stop();
|
||||
if (!checkForMouseActive || MouseActive)
|
||||
|
@ -171,7 +171,7 @@ namespace SystemTrayMenu.Handler
|
|||
|
||||
private void CallOpenMenuNow()
|
||||
{
|
||||
if (dgv.Items.Count > rowIndex && !alreadyOpened)
|
||||
if (!alreadyOpened && dgv != null && dgv.Items.Count > rowIndex)
|
||||
{
|
||||
alreadyOpened = true;
|
||||
|
||||
|
@ -180,16 +180,16 @@ namespace SystemTrayMenu.Handler
|
|||
rowData.Level = menu.Level;
|
||||
if (rowData.ContainsMenu)
|
||||
{
|
||||
CloseMenu.Invoke(rowData.Level + 2);
|
||||
CloseMenu?.Invoke(rowData.Level + 2);
|
||||
}
|
||||
|
||||
CloseMenu.Invoke(rowData.Level + 1);
|
||||
CloseMenu?.Invoke(rowData.Level + 1);
|
||||
|
||||
if (!rowData.IsContextMenuOpen &&
|
||||
rowData.ContainsMenu &&
|
||||
rowData.Level + 1 < MenuDefines.MenusMax)
|
||||
{
|
||||
StartLoadMenu.Invoke(rowData);
|
||||
StartLoadMenu?.Invoke(rowData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +215,7 @@ namespace SystemTrayMenu.Handler
|
|||
dgv.SelectedIndex = rowIndex;
|
||||
}
|
||||
|
||||
private void ResetData(ListView dgv, int rowIndex)
|
||||
private void ResetData(ListView? dgv, int rowIndex)
|
||||
{
|
||||
if (dgv != null && dgv.Items.Count > rowIndex)
|
||||
{
|
||||
|
|
|
@ -227,7 +227,7 @@ namespace SystemTrayMenu.DataClasses
|
|||
}
|
||||
}
|
||||
|
||||
internal void MouseClick(MouseEventArgs e, out bool toCloseByDoubleClick)
|
||||
internal void MouseClick(MouseEventArgs? e, out bool toCloseByDoubleClick)
|
||||
{
|
||||
IsClicking = false;
|
||||
toCloseByDoubleClick = false;
|
||||
|
@ -267,7 +267,7 @@ namespace SystemTrayMenu.DataClasses
|
|||
}
|
||||
}
|
||||
|
||||
private void OpenItem(MouseEventArgs e, ref bool toCloseByOpenItem)
|
||||
private void OpenItem(MouseEventArgs? e, ref bool toCloseByOpenItem)
|
||||
{
|
||||
if (!ContainsMenu && Path != null && ResolvedPath != null &&
|
||||
(e == null || e.LeftButton == MouseButtonState.Pressed))
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
// <copyright file="GlobalSuppressions.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA0001:XML comment analysis is disabled due to project configuration", Justification = "no idea what this is")]
|
||||
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "we need to document")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1601:Partial elements should be documented", Justification = "we need to document")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "we need to document")]
|
||||
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1000:Keywords should be spaced correctly", Justification = "new() should not be replaced by new() ")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:Prefix local calls with this", Justification = "Standard codecleanup removes the this")]
|
||||
|
||||
[assembly: SuppressMessage("Interoperability", "CA1416:Check platform compatibility", Justification = "this is a long way to get platform compatibility")]
|
||||
// <copyright file="GlobalSuppressions.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA0001:XML comment analysis is disabled due to project configuration", Justification = "no idea what this is")]
|
||||
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "we need to document")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1601:Partial elements should be documented", Justification = "we need to document")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "we need to document")]
|
||||
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1000:Keywords should be spaced correctly", Justification = "new() should not be replaced by new() ")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1009:Closing parenthesis should be spaced correctly", Justification = "Weird look when used by null-forgiving operator")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1011:Closing square brackets should be spaced correctly", Justification = "Conflicts with SA1018:A nullable type symbol within a C# element is not spaced correctly.")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:Prefix local calls with this", Justification = "Standard codecleanup removes the this")]
|
||||
|
||||
[assembly: SuppressMessage("Interoperability", "CA1416:Check platform compatibility", Justification = "this is a long way to get platform compatibility")]
|
||||
|
|
|
@ -38,10 +38,16 @@ namespace SystemTrayMenu.Helpers
|
|||
string path;
|
||||
if (menu != null)
|
||||
{
|
||||
RowData rowData = menu.RowDataParent;
|
||||
RowData? rowData = menu.RowDataParent;
|
||||
if (rowData != null)
|
||||
{
|
||||
path = rowData.ResolvedPath;
|
||||
string? resolvedPath = rowData.ResolvedPath;
|
||||
if (string.IsNullOrEmpty(resolvedPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
path = resolvedPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -54,7 +60,11 @@ namespace SystemTrayMenu.Helpers
|
|||
}
|
||||
|
||||
object data = e.Data.GetData("UniformResourceLocator");
|
||||
MemoryStream ms = data as MemoryStream;
|
||||
if (data is not MemoryStream ms)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] bytes = ms.ToArray();
|
||||
Encoding encod = Encoding.ASCII;
|
||||
string url = encod.GetString(bytes);
|
||||
|
@ -107,7 +117,7 @@ namespace SystemTrayMenu.Helpers
|
|||
return value;
|
||||
}
|
||||
|
||||
private static void WriteShortcut(string url, string pathIcon, string fileNamePathShortcut)
|
||||
private static void WriteShortcut(string url, string? pathIcon, string fileNamePathShortcut)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace SystemTrayMenu.Helpers
|
|||
/// <summary>
|
||||
/// A hot key has been pressed.
|
||||
/// </summary>
|
||||
internal event EventHandler<KeyPressedEventArgs> KeyPressed;
|
||||
internal event EventHandler<KeyPressedEventArgs>? KeyPressed;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
@ -52,16 +52,6 @@ namespace SystemTrayMenu.Helpers
|
|||
window.Dispose();
|
||||
}
|
||||
|
||||
/// <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(Key key)
|
||||
{
|
||||
uint keyModifiersNone = 0;
|
||||
RegisterHotKey(keyModifiersNone, key);
|
||||
}
|
||||
|
||||
internal void RegisterHotKey()
|
||||
{
|
||||
KeyboardHookModifierKeys modifiers = KeyboardHookModifierKeys.None;
|
||||
|
@ -97,6 +87,16 @@ namespace SystemTrayMenu.Helpers
|
|||
#endif
|
||||
}
|
||||
|
||||
/// <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(Key key)
|
||||
{
|
||||
uint keyModifiersNone = 0;
|
||||
RegisterHotKey(keyModifiersNone, key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a hot key in the system.
|
||||
/// </summary>
|
||||
|
@ -107,7 +107,7 @@ namespace SystemTrayMenu.Helpers
|
|||
RegisterHotKey((uint)modifier, key);
|
||||
}
|
||||
|
||||
private void Window_KeyPressed(object sender, KeyPressedEventArgs e)
|
||||
private void Window_KeyPressed(object? sender, KeyPressedEventArgs e)
|
||||
{
|
||||
KeyPressed?.Invoke(this, e);
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ namespace SystemTrayMenu.Helpers
|
|||
{
|
||||
private const int WmHotkey = 0x0312;
|
||||
|
||||
public event EventHandler<KeyPressedEventArgs> KeyPressed;
|
||||
public event EventHandler<KeyPressedEventArgs>? KeyPressed;
|
||||
|
||||
/// <summary>
|
||||
/// Overridden to get the notifications.
|
||||
|
|
|
@ -14,8 +14,8 @@ namespace SystemTrayMenu.Helpers.Updater
|
|||
|
||||
public class GitHubUpdate
|
||||
{
|
||||
private static List<Dictionary<string, object>> releases;
|
||||
private static UpdateWindow newVersionForm;
|
||||
private static List<Dictionary<string, object>>? releases;
|
||||
private static UpdateWindow? newVersionWindow;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the latest release version name .
|
||||
|
@ -33,7 +33,7 @@ namespace SystemTrayMenu.Helpers.Updater
|
|||
|
||||
try
|
||||
{
|
||||
result = releases[0]["tag_name"].ToString() !.Replace("v", string.Empty); // 0 = latest
|
||||
result = releases[0]["tag_name"].ToString()!.Replace("v", string.Empty); // 0 = latest
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -51,9 +51,9 @@ namespace SystemTrayMenu.Helpers.Updater
|
|||
|
||||
public static void ActivateNewVersionFormOrCheckForUpdates(bool showWhenUpToDate)
|
||||
{
|
||||
if (newVersionForm != null)
|
||||
if (newVersionWindow != null)
|
||||
{
|
||||
newVersionForm!.HandleInvoke(() => newVersionForm?.Activate());
|
||||
newVersionWindow!.HandleInvoke(() => newVersionWindow?.Activate());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -67,7 +67,7 @@ namespace SystemTrayMenu.Helpers.Updater
|
|||
HttpClient client = new();
|
||||
|
||||
// https://developer.github.com/v3/#user-agent-required
|
||||
client.DefaultRequestHeaders.Add("User-Agent", "SystemTrayMenu/" + Assembly.GetExecutingAssembly().GetName().Version.ToString());
|
||||
client.DefaultRequestHeaders.Add("User-Agent", "SystemTrayMenu/" + Assembly.GetExecutingAssembly().GetName().Version!.ToString());
|
||||
|
||||
// https://developer.github.com/v3/media/#request-specific-version
|
||||
client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3.text+json");
|
||||
|
@ -97,32 +97,43 @@ namespace SystemTrayMenu.Helpers.Updater
|
|||
|
||||
private static void RemoveCurrentAndOlderVersions()
|
||||
{
|
||||
int releasesCount = releases.Count;
|
||||
Version versionCurrent = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
for (int i = 0; i < releasesCount; i++)
|
||||
if (releases != null)
|
||||
{
|
||||
string tagName = releases[i]["tag_name"].ToString();
|
||||
Version versionGitHub = new(tagName.Replace("v", string.Empty));
|
||||
if (versionGitHub.CompareTo(versionCurrent) < 1)
|
||||
int releasesCount = releases.Count;
|
||||
Version versionCurrent = Assembly.GetExecutingAssembly().GetName().Version!;
|
||||
for (int i = 0; i < releasesCount; i++)
|
||||
{
|
||||
releases.RemoveRange(i, releasesCount - i);
|
||||
break;
|
||||
string? tagName = releases[i]["tag_name"].ToString();
|
||||
if (tagName == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Version versionGitHub = new(tagName.Replace("v", string.Empty));
|
||||
if (versionGitHub.CompareTo(versionCurrent) < 1)
|
||||
{
|
||||
releases.RemoveRange(i, releasesCount - i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ShowNewVersionOrUpToDateDialog(bool showWhenUpToDate)
|
||||
{
|
||||
if (releases.Count > 0)
|
||||
if (releases != null)
|
||||
{
|
||||
newVersionForm = new();
|
||||
newVersionForm.textBox.Text = GetChangelog();
|
||||
newVersionForm.Closed += (_, _) => newVersionForm = null;
|
||||
newVersionForm.ShowDialog();
|
||||
}
|
||||
else if (showWhenUpToDate)
|
||||
{
|
||||
MessageBox.Show(Translator.GetText("You have the latest version of SystemTrayMenu!"));
|
||||
if (releases.Count > 0)
|
||||
{
|
||||
newVersionWindow = new();
|
||||
newVersionWindow.textBox.Text = GetChangelog();
|
||||
newVersionWindow.Closed += (_, _) => newVersionWindow = null;
|
||||
newVersionWindow.ShowDialog();
|
||||
}
|
||||
else if (showWhenUpToDate)
|
||||
{
|
||||
MessageBox.Show(Translator.GetText("You have the latest version of SystemTrayMenu!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,10 +156,15 @@ namespace SystemTrayMenu.Helpers.Updater
|
|||
for (int i = 0; i < releases.Count; i++)
|
||||
{
|
||||
Dictionary<string, object> release = releases[i];
|
||||
string? bodyText = release["body_text"].ToString();
|
||||
if (bodyText == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result += release["name"].ToString()
|
||||
+ Environment.NewLine
|
||||
+ release["body_text"].ToString()
|
||||
+ bodyText
|
||||
.Replace("\n\n", Environment.NewLine)
|
||||
.Replace("\n \n", Environment.NewLine)
|
||||
+ Environment.NewLine + Environment.NewLine;
|
||||
|
|
|
@ -172,7 +172,7 @@ namespace SystemTrayMenu.Helpers.Updater
|
|||
|
||||
if (type.IsArray)
|
||||
{
|
||||
Type arrayType = type.GetElementType() !;
|
||||
Type arrayType = type.GetElementType()!;
|
||||
if (json[0] != '[' || json[^1] != ']')
|
||||
{
|
||||
return null;
|
||||
|
|
|
@ -1,48 +1,50 @@
|
|||
// <copyright file="CustomSettingsProvider.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.Properties
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
// <copyright file="CustomSettingsProvider.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.Properties
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Xml.Linq;
|
||||
using SystemTrayMenu.Utilities;
|
||||
|
||||
internal class CustomSettingsProvider : SettingsProvider
|
||||
{
|
||||
private const string NameOf = "name";
|
||||
private const string SerializeAs = "serializeAs";
|
||||
private const string Config = "configuration";
|
||||
private const string UserSettings = "userSettings";
|
||||
private const string Setting = "setting";
|
||||
|
||||
private bool loaded;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CustomSettingsProvider"/> class.
|
||||
/// Loads the file into memory.
|
||||
/// </summary>
|
||||
public CustomSettingsProvider()
|
||||
{
|
||||
SettingsDictionary = new Dictionary<string, SettingStruct>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the setting key this is returning must set before the settings are used.
|
||||
/// e.g. <c>Properties.Settings.Default.SettingsKey = @"C:\temp\user.config";</c>.
|
||||
/// </summary>
|
||||
public static string UserConfigPath => Path.Combine(
|
||||
Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
$"SystemTrayMenu"),
|
||||
$"user-{Environment.MachineName}.config");
|
||||
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Xml.Linq;
|
||||
using SystemTrayMenu.Utilities;
|
||||
|
||||
internal class CustomSettingsProvider : SettingsProvider
|
||||
{
|
||||
private const string NameOf = "name";
|
||||
private const string SerializeAs = "serializeAs";
|
||||
private const string Config = "configuration";
|
||||
private const string UserSettings = "userSettings";
|
||||
private const string Setting = "setting";
|
||||
|
||||
private static readonly string? SettingsFullTypeName = typeof(Settings).FullName;
|
||||
|
||||
private bool loaded;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CustomSettingsProvider"/> class.
|
||||
/// Loads the file into memory.
|
||||
/// </summary>
|
||||
public CustomSettingsProvider()
|
||||
{
|
||||
SettingsDictionary = new Dictionary<string, SettingStruct>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the setting key this is returning must set before the settings are used.
|
||||
/// e.g. <c>Properties.Settings.Default.SettingsKey = @"C:\temp\user.config";</c>.
|
||||
/// </summary>
|
||||
public static string UserConfigPath => Path.Combine(
|
||||
Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
$"SystemTrayMenu"),
|
||||
$"user-{Environment.MachineName}.config");
|
||||
|
||||
public static string? ConfigPathAssembly
|
||||
{
|
||||
get
|
||||
|
@ -59,87 +61,87 @@ namespace SystemTrayMenu.Properties
|
|||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets override.
|
||||
/// </summary>
|
||||
public override string ApplicationName
|
||||
{
|
||||
get => Assembly.GetExecutingAssembly().ManifestModule.Name;
|
||||
|
||||
set
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets in memory storage of the settings values.
|
||||
/// </summary>
|
||||
private Dictionary<string, SettingStruct> SettingsDictionary { get; set; }
|
||||
|
||||
public static bool IsActivatedConfigPathAssembly()
|
||||
{
|
||||
return IsConfigPathAssembly();
|
||||
}
|
||||
|
||||
public static void ActivateConfigPathAssembly()
|
||||
{
|
||||
CreateEmptyConfigIfNotExists(ConfigPathAssembly);
|
||||
}
|
||||
|
||||
public static void DeactivateConfigPathAssembly()
|
||||
{
|
||||
if (IsConfigPathAssembly())
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(ConfigPathAssembly!);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"Could not delete {ConfigPathAssembly}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override.
|
||||
/// </summary>
|
||||
/// <param name="name">name.</param>
|
||||
/// <param name="config">config.</param>
|
||||
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
|
||||
{
|
||||
base.Initialize(ApplicationName, config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Must override this, this is the bit that matches up the designer properties to the dictionary values.
|
||||
/// </summary>
|
||||
/// <param name="context">context.</param>
|
||||
/// <param name="collection">collection.</param>
|
||||
/// <returns>SettingsPropertyValueCollection.</returns>
|
||||
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
|
||||
{
|
||||
// load the file
|
||||
if (!loaded)
|
||||
{
|
||||
loaded = true;
|
||||
LoadValuesFromFile();
|
||||
}
|
||||
|
||||
// collection that will be returned.
|
||||
SettingsPropertyValueCollection values = new();
|
||||
|
||||
// itterate thought the properties we get from the designer, checking to see if the setting is in the dictionary
|
||||
foreach (SettingsProperty setting in collection)
|
||||
{
|
||||
SettingsPropertyValue value = new(setting)
|
||||
{
|
||||
IsDirty = false,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets override.
|
||||
/// </summary>
|
||||
public override string ApplicationName
|
||||
{
|
||||
get => Assembly.GetExecutingAssembly().ManifestModule.Name;
|
||||
|
||||
set
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets in memory storage of the settings values.
|
||||
/// </summary>
|
||||
private Dictionary<string, SettingStruct> SettingsDictionary { get; set; }
|
||||
|
||||
public static bool IsActivatedConfigPathAssembly()
|
||||
{
|
||||
return IsConfigPathAssembly();
|
||||
}
|
||||
|
||||
public static void ActivateConfigPathAssembly()
|
||||
{
|
||||
CreateEmptyConfigIfNotExists(ConfigPathAssembly);
|
||||
}
|
||||
|
||||
public static void DeactivateConfigPathAssembly()
|
||||
{
|
||||
if (IsConfigPathAssembly())
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(ConfigPathAssembly!);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"Could not delete {ConfigPathAssembly}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override.
|
||||
/// </summary>
|
||||
/// <param name="name">name.</param>
|
||||
/// <param name="config">config.</param>
|
||||
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
|
||||
{
|
||||
base.Initialize(ApplicationName, config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Must override this, this is the bit that matches up the designer properties to the dictionary values.
|
||||
/// </summary>
|
||||
/// <param name="context">context.</param>
|
||||
/// <param name="collection">collection.</param>
|
||||
/// <returns>SettingsPropertyValueCollection.</returns>
|
||||
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
|
||||
{
|
||||
// load the file
|
||||
if (!loaded)
|
||||
{
|
||||
loaded = true;
|
||||
LoadValuesFromFile();
|
||||
}
|
||||
|
||||
// collection that will be returned.
|
||||
SettingsPropertyValueCollection values = new();
|
||||
|
||||
// itterate thought the properties we get from the designer, checking to see if the setting is in the dictionary
|
||||
foreach (SettingsProperty setting in collection)
|
||||
{
|
||||
SettingsPropertyValue value = new(setting)
|
||||
{
|
||||
IsDirty = false,
|
||||
};
|
||||
|
||||
// need the type of the value for the strong typing
|
||||
string? typename = setting.PropertyType.FullName;
|
||||
if (typename != null)
|
||||
|
@ -161,143 +163,155 @@ namespace SystemTrayMenu.Properties
|
|||
|
||||
values.Add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Must override this, this is the bit that does the saving to file. Called when Settings.Save() is called.
|
||||
/// </summary>
|
||||
/// <param name="context">context.</param>
|
||||
/// <param name="collection">collection.</param>
|
||||
public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
|
||||
{
|
||||
// grab the values from the collection parameter and update the values in our dictionary.
|
||||
foreach (SettingsPropertyValue value in collection)
|
||||
{
|
||||
SettingStruct setting = new()
|
||||
{
|
||||
Value = value.PropertyValue == null ? string.Empty : value.PropertyValue.ToString() ?? string.Empty,
|
||||
Name = value.Name,
|
||||
SerializeAs = value.Property.SerializeAs.ToString(),
|
||||
};
|
||||
|
||||
if (!SettingsDictionary.ContainsKey(value.Name))
|
||||
{
|
||||
SettingsDictionary.Add(value.Name, setting);
|
||||
}
|
||||
else
|
||||
{
|
||||
SettingsDictionary[value.Name] = setting;
|
||||
}
|
||||
}
|
||||
|
||||
// now that our local dictionary is up-to-date, save it to disk.
|
||||
SaveValuesToFile();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an empty user.config file...looks like the one MS creates.
|
||||
/// This could be overkill a simple key/value pairing would probably do.
|
||||
/// </summary>
|
||||
private static void CreateEmptyConfigIfNotExists(string? path)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
// if the config file is not where it's supposed to be create a new one.
|
||||
XDocument doc = new();
|
||||
XDeclaration declaration = new("1.0", "utf-8", "true");
|
||||
XElement config = new(Config);
|
||||
XElement userSettings = new(UserSettings);
|
||||
XElement group = new(typeof(Settings).FullName);
|
||||
userSettings.Add(group);
|
||||
config.Add(userSettings);
|
||||
doc.Add(config);
|
||||
doc.Declaration = declaration;
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Must override this, this is the bit that does the saving to file. Called when Settings.Save() is called.
|
||||
/// </summary>
|
||||
/// <param name="context">context.</param>
|
||||
/// <param name="collection">collection.</param>
|
||||
public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
|
||||
{
|
||||
// grab the values from the collection parameter and update the values in our dictionary.
|
||||
foreach (SettingsPropertyValue value in collection)
|
||||
{
|
||||
SettingStruct setting = new()
|
||||
{
|
||||
Value = value.PropertyValue == null ? string.Empty : value.PropertyValue.ToString() ?? string.Empty,
|
||||
Name = value.Name,
|
||||
SerializeAs = value.Property.SerializeAs.ToString(),
|
||||
};
|
||||
|
||||
if (!SettingsDictionary.ContainsKey(value.Name))
|
||||
{
|
||||
SettingsDictionary.Add(value.Name, setting);
|
||||
}
|
||||
else
|
||||
{
|
||||
SettingsDictionary[value.Name] = setting;
|
||||
}
|
||||
}
|
||||
|
||||
// now that our local dictionary is up-to-date, save it to disk.
|
||||
SaveValuesToFile();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an empty user.config file...looks like the one MS creates.
|
||||
/// This could be overkill a simple key/value pairing would probably do.
|
||||
/// </summary>
|
||||
private static void CreateEmptyConfigIfNotExists(string? path)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
if (string.IsNullOrEmpty(SettingsFullTypeName))
|
||||
{
|
||||
Log.Warn($"Failed to store config for group {SettingsFullTypeName ?? "<null>"}", new());
|
||||
return;
|
||||
}
|
||||
|
||||
string? dir = Path.GetDirectoryName(path);
|
||||
if (string.IsNullOrEmpty(dir) || string.IsNullOrEmpty(path))
|
||||
{
|
||||
Log.Warn($"Failed to store config in directory {path ?? "<null>"}", new());
|
||||
return;
|
||||
}
|
||||
|
||||
// if the config file is not where it's supposed to be create a new one.
|
||||
XDocument doc = new();
|
||||
XDeclaration declaration = new("1.0", "utf-8", "true");
|
||||
XElement config = new(Config);
|
||||
XElement userSettings = new(UserSettings);
|
||||
XElement group = new(SettingsFullTypeName);
|
||||
userSettings.Add(group);
|
||||
config.Add(userSettings);
|
||||
doc.Add(config);
|
||||
doc.Declaration = declaration;
|
||||
|
||||
if (!Directory.Exists(dir))
|
||||
{
|
||||
Directory.CreateDirectory(dir!);
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
doc.Save(path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"Failed to store config at assembly location {path}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
doc.Save(path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"Failed to store config at assembly location {path}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsConfigPathAssembly()
|
||||
{
|
||||
bool isconfigPathAssembly = false;
|
||||
try
|
||||
{
|
||||
isconfigPathAssembly = File.Exists(ConfigPathAssembly);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn("IsConfigPathAssembly failed", ex);
|
||||
}
|
||||
|
||||
return isconfigPathAssembly;
|
||||
}
|
||||
|
||||
private static XDocument? LoadOrGetNew(string path)
|
||||
{
|
||||
XDocument? xDocument = null;
|
||||
try
|
||||
{
|
||||
xDocument = XDocument.Load(path);
|
||||
}
|
||||
catch (Exception exceptionWarning)
|
||||
{
|
||||
Log.Warn($"Could not load {path}", exceptionWarning);
|
||||
try
|
||||
{
|
||||
File.Delete(path);
|
||||
CreateEmptyConfigIfNotExists(path);
|
||||
xDocument = XDocument.Load(path);
|
||||
}
|
||||
catch (Exception exceptionError)
|
||||
{
|
||||
Log.Error($"Could not delete and create {path}", exceptionError);
|
||||
}
|
||||
}
|
||||
|
||||
return xDocument;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the values of the file into memory.
|
||||
/// </summary>
|
||||
private void LoadValuesFromFile()
|
||||
{
|
||||
CreateEmptyConfigIfNotExists(UserConfigPath);
|
||||
|
||||
// load the xml
|
||||
XDocument? configXml;
|
||||
if (IsConfigPathAssembly())
|
||||
{
|
||||
configXml = LoadOrGetNew(ConfigPathAssembly!);
|
||||
}
|
||||
else
|
||||
{
|
||||
configXml = LoadOrGetNew(UserConfigPath);
|
||||
}
|
||||
|
||||
if (configXml != null)
|
||||
{
|
||||
// get all of the <setting name="..." serializeAs="..."> elements.
|
||||
IEnumerable<XElement>? settingElements = configXml.Element(Config)?.Element(UserSettings)?.Element(typeof(Settings).FullName)?.Elements(Setting);
|
||||
|
||||
// iterate through, adding them to the dictionary, (checking for nulls, xml no likey nulls)
|
||||
|
||||
private static bool IsConfigPathAssembly()
|
||||
{
|
||||
bool isconfigPathAssembly = false;
|
||||
try
|
||||
{
|
||||
isconfigPathAssembly = File.Exists(ConfigPathAssembly);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn("IsConfigPathAssembly failed", ex);
|
||||
}
|
||||
|
||||
return isconfigPathAssembly;
|
||||
}
|
||||
|
||||
private static XDocument? LoadOrGetNew(string path)
|
||||
{
|
||||
XDocument? xDocument = null;
|
||||
try
|
||||
{
|
||||
xDocument = XDocument.Load(path);
|
||||
}
|
||||
catch (Exception exceptionWarning)
|
||||
{
|
||||
Log.Warn($"Could not load {path}", exceptionWarning);
|
||||
try
|
||||
{
|
||||
File.Delete(path);
|
||||
CreateEmptyConfigIfNotExists(path);
|
||||
xDocument = XDocument.Load(path);
|
||||
}
|
||||
catch (Exception exceptionError)
|
||||
{
|
||||
Log.Error($"Could not delete and create {path}", exceptionError);
|
||||
}
|
||||
}
|
||||
|
||||
return xDocument;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the values of the file into memory.
|
||||
/// </summary>
|
||||
private void LoadValuesFromFile()
|
||||
{
|
||||
CreateEmptyConfigIfNotExists(UserConfigPath);
|
||||
|
||||
// load the xml
|
||||
XDocument? configXml;
|
||||
if (IsConfigPathAssembly())
|
||||
{
|
||||
configXml = LoadOrGetNew(ConfigPathAssembly!);
|
||||
}
|
||||
else
|
||||
{
|
||||
configXml = LoadOrGetNew(UserConfigPath);
|
||||
}
|
||||
|
||||
if (configXml != null && !string.IsNullOrEmpty(SettingsFullTypeName))
|
||||
{
|
||||
// get all of the <setting name="..." serializeAs="..."> elements.
|
||||
IEnumerable<XElement>? settingElements = configXml.Element(Config)?.Element(UserSettings)?.Element(SettingsFullTypeName)?.Elements(Setting);
|
||||
|
||||
// iterate through, adding them to the dictionary, (checking for nulls, xml no likey nulls)
|
||||
// using "String" as default serializeAs...just in case, no real good reason.
|
||||
if (settingElements != null)
|
||||
{
|
||||
|
@ -310,74 +324,74 @@ namespace SystemTrayMenu.Properties
|
|||
SettingStruct newSetting = new()
|
||||
{
|
||||
Name = element.Attribute(NameOf) == null ? string.Empty : name,
|
||||
SerializeAs = serializeAs == null ? "String" : serializeAs,
|
||||
SerializeAs = serializeAs ?? "String",
|
||||
Value = element.Value ?? string.Empty,
|
||||
};
|
||||
SettingsDictionary.Add(name, newSetting);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the in memory dictionary to the user config file.
|
||||
/// </summary>
|
||||
private void SaveValuesToFile()
|
||||
{
|
||||
// load the current xml from the file.
|
||||
XDocument? configXml;
|
||||
if (IsConfigPathAssembly())
|
||||
{
|
||||
configXml = LoadOrGetNew(ConfigPathAssembly!);
|
||||
}
|
||||
else
|
||||
{
|
||||
configXml = LoadOrGetNew(UserConfigPath);
|
||||
}
|
||||
|
||||
if (configXml != null)
|
||||
{
|
||||
// get the settings group (e.g. <Company.Project.Desktop.Settings>)
|
||||
XElement? settingsSection = configXml.Element(Config)?.Element(UserSettings)?.Element(typeof(Settings).FullName);
|
||||
|
||||
// iterate though the dictionary, either updating the value or adding the new setting.
|
||||
foreach (KeyValuePair<string, SettingStruct> entry in SettingsDictionary)
|
||||
{
|
||||
XElement? setting = settingsSection?.Elements().FirstOrDefault(e => e.Attribute(NameOf)?.Value == entry.Key);
|
||||
if (setting == null)
|
||||
{
|
||||
// this can happen if a new setting is added via the .settings designer.
|
||||
XElement newSetting = new(Setting);
|
||||
newSetting.Add(new XAttribute(NameOf, entry.Value.Name));
|
||||
newSetting.Add(new XAttribute(SerializeAs, entry.Value.SerializeAs));
|
||||
newSetting.Value = entry.Value.Value ?? string.Empty;
|
||||
settingsSection?.Add(newSetting);
|
||||
}
|
||||
else
|
||||
{
|
||||
// update the value if it exists.
|
||||
setting.Value = entry.Value.Value ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsConfigPathAssembly())
|
||||
{
|
||||
configXml.Save(ConfigPathAssembly!);
|
||||
}
|
||||
|
||||
configXml.Save(UserConfigPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper struct.
|
||||
/// </summary>
|
||||
internal struct SettingStruct
|
||||
{
|
||||
internal string Name;
|
||||
internal string SerializeAs;
|
||||
internal string Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the in memory dictionary to the user config file.
|
||||
/// </summary>
|
||||
private void SaveValuesToFile()
|
||||
{
|
||||
// load the current xml from the file.
|
||||
XDocument? configXml;
|
||||
if (IsConfigPathAssembly())
|
||||
{
|
||||
configXml = LoadOrGetNew(ConfigPathAssembly!);
|
||||
}
|
||||
else
|
||||
{
|
||||
configXml = LoadOrGetNew(UserConfigPath);
|
||||
}
|
||||
|
||||
if (configXml != null && !string.IsNullOrEmpty(SettingsFullTypeName))
|
||||
{
|
||||
// get the settings group (e.g. <Company.Project.Desktop.Settings>)
|
||||
XElement? settingsSection = configXml.Element(Config)?.Element(UserSettings)?.Element(SettingsFullTypeName);
|
||||
|
||||
// iterate though the dictionary, either updating the value or adding the new setting.
|
||||
foreach (KeyValuePair<string, SettingStruct> entry in SettingsDictionary)
|
||||
{
|
||||
XElement? setting = settingsSection?.Elements().FirstOrDefault(e => e.Attribute(NameOf)?.Value == entry.Key);
|
||||
if (setting == null)
|
||||
{
|
||||
// this can happen if a new setting is added via the .settings designer.
|
||||
XElement newSetting = new(Setting);
|
||||
newSetting.Add(new XAttribute(NameOf, entry.Value.Name));
|
||||
newSetting.Add(new XAttribute(SerializeAs, entry.Value.SerializeAs));
|
||||
newSetting.Value = entry.Value.Value ?? string.Empty;
|
||||
settingsSection?.Add(newSetting);
|
||||
}
|
||||
else
|
||||
{
|
||||
// update the value if it exists.
|
||||
setting.Value = entry.Value.Value ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsConfigPathAssembly())
|
||||
{
|
||||
configXml.Save(ConfigPathAssembly!);
|
||||
}
|
||||
|
||||
configXml.Save(UserConfigPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper struct.
|
||||
/// </summary>
|
||||
internal struct SettingStruct
|
||||
{
|
||||
internal string Name;
|
||||
internal string SerializeAs;
|
||||
internal string Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// Copyright (c) 2022-2022 Peter Kirmeier
|
||||
// Copyright (c) 2022-2023 Peter Kirmeier
|
||||
|
||||
namespace SystemTrayMenu.UserInterface
|
||||
{
|
||||
|
@ -11,6 +11,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
|
@ -45,7 +46,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
{
|
||||
if (imgstream != null)
|
||||
{
|
||||
BitmapImage imageSource = new BitmapImage();
|
||||
BitmapImage imageSource = new ();
|
||||
imageSource.BeginInit();
|
||||
imageSource.StreamSource = imgstream;
|
||||
imageSource.EndInit();
|
||||
|
@ -170,16 +171,16 @@ namespace SystemTrayMenu.UserInterface
|
|||
MoreRichTextBox.Visibility = Visibility.Visible;
|
||||
MoreRichTextBox.Document.Blocks.Clear();
|
||||
|
||||
Paragraph para = new Paragraph();
|
||||
Paragraph para = new ();
|
||||
|
||||
// Parse string to detect hyperlinks and add handlers to them
|
||||
// See: https://mycsharp.de/forum/threads/97560/erledigt-dynamische-hyperlinks-in-wpf-flowdocument?page=1
|
||||
int lastPos = 0;
|
||||
foreach (Match match in RegexUrl.Matches(value))
|
||||
foreach (Match match in RegexUrl.Matches(value).Cast<Match>())
|
||||
{
|
||||
if (match.Index != lastPos)
|
||||
{
|
||||
para.Inlines.Add(value.Substring(lastPos, match.Index - lastPos));
|
||||
para.Inlines.Add(value[lastPos..match.Index]);
|
||||
}
|
||||
|
||||
var link = new Hyperlink(new Run(match.Value))
|
||||
|
@ -195,7 +196,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
|
||||
if (lastPos < value.Length)
|
||||
{
|
||||
para.Inlines.Add(value.Substring(lastPos));
|
||||
para.Inlines.Add(value[lastPos..]);
|
||||
}
|
||||
|
||||
MoreRichTextBox.Document.Blocks.Add(para);
|
||||
|
@ -432,7 +433,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
RegistryKey? rk = Registry.LocalMachine.OpenSubKey(keyName);
|
||||
if (rk != null)
|
||||
{
|
||||
strSysInfoPath = (string)rk.GetValue(subKeyRef, string.Empty) !;
|
||||
strSysInfoPath = (string)rk.GetValue(subKeyRef, string.Empty)!;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -662,7 +663,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
private void AboutBox_Load(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// if the user didn't provide an assembly, try to guess which one is the entry assembly
|
||||
AppEntryAssembly ??= Assembly.GetEntryAssembly() !;
|
||||
AppEntryAssembly ??= Assembly.GetEntryAssembly()!;
|
||||
AppEntryAssembly ??= Assembly.GetExecutingAssembly();
|
||||
|
||||
executingAssemblyName = Assembly.GetExecutingAssembly().GetName().Name;
|
||||
|
|
|
@ -23,18 +23,18 @@ namespace SystemTrayMenu.UserInterface.FolderBrowseDialog
|
|||
/// <summary>
|
||||
/// Gets or sets /sets folder in which dialog will be open.
|
||||
/// </summary>
|
||||
public string InitialFolder { get; set; }
|
||||
public string? InitialFolder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets /sets directory in which dialog will be open
|
||||
/// if there is no recent directory available.
|
||||
/// </summary>
|
||||
public string DefaultFolder { get; set; }
|
||||
public string? DefaultFolder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets selected folder.
|
||||
/// </summary>
|
||||
public string Folder { get; set; }
|
||||
public string? Folder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Shows the file dialog and requests user interaction.
|
||||
|
|
|
@ -8,11 +8,11 @@ namespace SystemTrayMenu.UserInterface.FolderBrowseDialog
|
|||
|
||||
public interface IFolderDialog
|
||||
{
|
||||
string InitialFolder { get; set; }
|
||||
string? InitialFolder { get; set; }
|
||||
|
||||
string DefaultFolder { get; set; }
|
||||
string? DefaultFolder { get; set; }
|
||||
|
||||
string Folder { get; set; }
|
||||
string? Folder { get; set; }
|
||||
|
||||
bool ShowDialog(Window owner);
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
private static readonly RoutedEvent FadeOutEvent = EventManager.RegisterRoutedEvent(
|
||||
nameof(FadeOut), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Menu));
|
||||
|
||||
private readonly DispatcherTimer timerUpdateIcons = new (DispatcherPriority.Render, Dispatcher.CurrentDispatcher);
|
||||
private readonly string folderPath;
|
||||
#if TODO // SEARCH
|
||||
public const string RowFilterShowAll = "[SortIndex] LIKE '%0%'";
|
||||
|
@ -46,7 +47,6 @@ namespace SystemTrayMenu.UserInterface
|
|||
private bool isSetSearchText;
|
||||
#endif
|
||||
private bool isClosed = false; // TODO WPF Replace Forms wrapper
|
||||
private DispatcherTimer timerUpdateIcons = new DispatcherTimer(DispatcherPriority.Render, Dispatcher.CurrentDispatcher);
|
||||
|
||||
internal Menu(MenuData menuData, string path)
|
||||
{
|
||||
|
@ -334,7 +334,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
|
||||
public bool Disposing => isClosed; // TODO WPF Replace Forms wrapper
|
||||
|
||||
public System.Drawing.Point Location => new System.Drawing.Point((int)Left, (int)Top); // TODO WPF Replace Forms wrapper)
|
||||
public System.Drawing.Point Location => new ((int)Left, (int)Top); // TODO WPF Replace Forms wrapper)
|
||||
|
||||
internal int Level { get; set; }
|
||||
|
||||
|
@ -716,7 +716,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
case StartLocation.Predecessor:
|
||||
|
||||
RowData? trigger = RowDataParent;
|
||||
ListView dgv = menuPredecessor!.GetDataGridView() !;
|
||||
ListView dgv = menuPredecessor!.GetDataGridView()!;
|
||||
|
||||
// Set position on same height as the selected row from predecessor
|
||||
y = menuPredecessor.Location.Y;
|
||||
|
@ -730,8 +730,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
// When scrolled, we have to reduce the index number as we calculate based on visual tree
|
||||
int startIndex = 0;
|
||||
double offset = 0D;
|
||||
ScrollViewer? scrollViewer = (VisualTreeHelper.GetChild(dgv, 0) as Decorator)?.Child as ScrollViewer;
|
||||
if (scrollViewer != null)
|
||||
if (VisualTreeHelper.GetChild(dgv, 0) is Decorator { Child: ScrollViewer scrollViewer })
|
||||
{
|
||||
startIndex = (int)scrollViewer.VerticalOffset;
|
||||
if (trigger.RowIndex < startIndex)
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
private const string Command = @"Software\Classes\directory\shell\SystemTrayMenu_SetAsRootFolder\command";
|
||||
|
||||
private static SettingsWindow? settingsForm;
|
||||
|
||||
#if TODO // HOTKEY
|
||||
private bool inHotkey;
|
||||
#endif
|
||||
|
@ -41,7 +42,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
{
|
||||
if (imgstream != null)
|
||||
{
|
||||
BitmapImage imageSource = new BitmapImage();
|
||||
BitmapImage imageSource = new();
|
||||
imageSource.BeginInit();
|
||||
imageSource.StreamSource = imgstream;
|
||||
imageSource.EndInit();
|
||||
|
@ -629,12 +630,9 @@ namespace SystemTrayMenu.UserInterface
|
|||
{
|
||||
RegistryKey? key = Registry.CurrentUser.OpenSubKey(
|
||||
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);
|
||||
if (key != null)
|
||||
{
|
||||
key.SetValue(
|
||||
key?.SetValue(
|
||||
Assembly.GetExecutingAssembly().GetName().Name,
|
||||
Environment.ProcessPath!);
|
||||
}
|
||||
|
||||
Settings.Default.IsAutostartActivated = true;
|
||||
}
|
||||
|
@ -642,10 +640,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
{
|
||||
RegistryKey? key = Registry.CurrentUser.OpenSubKey(
|
||||
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);
|
||||
if (key != null)
|
||||
{
|
||||
key.DeleteValue("SystemTrayMenu", false);
|
||||
}
|
||||
key?.DeleteValue("SystemTrayMenu", false);
|
||||
|
||||
Settings.Default.IsAutostartActivated = false;
|
||||
}
|
||||
|
@ -945,8 +940,11 @@ namespace SystemTrayMenu.UserInterface
|
|||
|
||||
if (dialog.ShowDialog(this))
|
||||
{
|
||||
dataGridViewFolders.Items.Add(new ListViewItemData(dialog.Folder, false, true));
|
||||
EnableButtonAddStartMenu();
|
||||
if (!string.IsNullOrEmpty(dialog.Folder))
|
||||
{
|
||||
dataGridViewFolders.Items.Add(new ListViewItemData(dialog.Folder, false, true));
|
||||
EnableButtonAddStartMenu();
|
||||
}
|
||||
}
|
||||
|
||||
dataGridViewFolders.SelectedItem = null;
|
||||
|
|
|
@ -735,7 +735,7 @@ namespace SystemTrayMenu.Utilities
|
|||
|
||||
try
|
||||
{
|
||||
if (arrPIDLs == null)
|
||||
if (arrPIDLs == null || oParentFolder == null || oContextMenu == null)
|
||||
{
|
||||
ReleaseAll();
|
||||
return;
|
||||
|
@ -832,6 +832,14 @@ namespace SystemTrayMenu.Utilities
|
|||
/// by calling HandleMenuMsg and HandleMenuMsg2. It will also call the OnContextMenuMouseHover
|
||||
/// method of Browser when hovering over a ContextMenu item.
|
||||
/// </summary>
|
||||
/// <param name="hwnd">The window handle.</param>
|
||||
/// <param name="msg">The message ID.</param>
|
||||
/// <param name="wParam">The message's wParam value.</param>
|
||||
/// <param name="lParam">The message's lParam value.</param>
|
||||
/// <param name="handled">A value that indicates whether the message was handled.
|
||||
/// Set the value to true if the message was handled; otherwise, false.</param>
|
||||
/// <returns>The appropriate return value depends on the particular message.
|
||||
/// See the message documentation details for the Win32 message being handled.</returns>
|
||||
protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
||||
{
|
||||
if (oContextMenu2 != null &&
|
||||
|
@ -867,7 +875,7 @@ namespace SystemTrayMenu.Utilities
|
|||
/// </summary>
|
||||
/// <param name="arrFI">Array of FileInfo.</param>
|
||||
/// <returns>Array of PIDLs.</returns>
|
||||
protected IntPtr[] GetPIDLs(FileInfo[] arrFI)
|
||||
protected IntPtr[]? GetPIDLs(FileInfo[] arrFI)
|
||||
{
|
||||
if (arrFI == null || arrFI.Length == 0)
|
||||
{
|
||||
|
@ -907,14 +915,14 @@ namespace SystemTrayMenu.Utilities
|
|||
/// </summary>
|
||||
/// <param name="arrFI">Array of DirectoryInfo.</param>
|
||||
/// <returns>Array of PIDLs.</returns>
|
||||
protected IntPtr[] GetPIDLs(DirectoryInfo[] arrFI)
|
||||
protected IntPtr[]? GetPIDLs(DirectoryInfo[] arrFI)
|
||||
{
|
||||
if (arrFI == null || arrFI.Length == 0 || arrFI[0].Parent == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
IShellFolder? oParentFolder = GetParentFolder(arrFI[0].Parent.FullName);
|
||||
IShellFolder? oParentFolder = GetParentFolder(arrFI[0].Parent?.FullName);
|
||||
if (oParentFolder == null)
|
||||
{
|
||||
return null;
|
||||
|
@ -942,7 +950,7 @@ namespace SystemTrayMenu.Utilities
|
|||
return arrPIDLs;
|
||||
}
|
||||
|
||||
private static void InvokeCommand(IContextMenu contextMenu, uint nCmd, string strFolder, Point pointInvoke)
|
||||
private static void InvokeCommand(IContextMenu contextMenu, uint nCmd, string? strFolder, Point pointInvoke)
|
||||
{
|
||||
CMINVOKECOMMANDINFOEX invoke = new()
|
||||
{
|
||||
|
@ -1067,12 +1075,12 @@ namespace SystemTrayMenu.Utilities
|
|||
/// </summary>
|
||||
/// <param name="folderName">Folder path.</param>
|
||||
/// <returns>IShellFolder for the folder (relative from the desktop).</returns>
|
||||
private IShellFolder? GetParentFolder(string folderName)
|
||||
private IShellFolder? GetParentFolder(string? folderName)
|
||||
{
|
||||
if (oParentFolder == null)
|
||||
{
|
||||
IShellFolder oDesktopFolder = GetDesktopFolder();
|
||||
if (oDesktopFolder == null)
|
||||
if (oDesktopFolder == null || folderName == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
@ -1088,7 +1096,7 @@ namespace SystemTrayMenu.Utilities
|
|||
|
||||
IntPtr pStrRet = Marshal.AllocCoTaskMem((MaxPath * 2) + 4);
|
||||
Marshal.WriteInt32(pStrRet, 0, 0);
|
||||
_ = this.oDesktopFolder.GetDisplayNameOf(pPIDL, SHGNO.FORPARSING, pStrRet);
|
||||
_ = oDesktopFolder.GetDisplayNameOf(pPIDL, SHGNO.FORPARSING, pStrRet);
|
||||
StringBuilder strFolder = new(MaxPath);
|
||||
_ = DllImports.NativeMethods.ShlwapiStrRetToBuf(pStrRet, pPIDL, strFolder, MaxPath);
|
||||
Marshal.FreeCoTaskMem(pStrRet);
|
||||
|
@ -1131,7 +1139,7 @@ namespace SystemTrayMenu.Utilities
|
|||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
public string LpParameters;
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
public string LpDirectory;
|
||||
public string? LpDirectory;
|
||||
public SW NShow;
|
||||
public int DwHotKey;
|
||||
public IntPtr HIcon;
|
||||
|
@ -1141,7 +1149,7 @@ namespace SystemTrayMenu.Utilities
|
|||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string LpParametersW;
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string LpDirectoryW;
|
||||
public string? LpDirectoryW;
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string LpTitleW;
|
||||
public POINT PtInvoke;
|
||||
|
@ -1150,7 +1158,7 @@ namespace SystemTrayMenu.Utilities
|
|||
// Contains information about a menu item
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
|
||||
private struct MENUITEMINFO
|
||||
private readonly struct MENUITEMINFO
|
||||
{
|
||||
private readonly int cbSize;
|
||||
private readonly MIIM fMask;
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace SystemTrayMenu.Utilities
|
|||
|
||||
internal class AppRestart
|
||||
{
|
||||
public static event Action BeforeRestarting;
|
||||
public static event Action? BeforeRestarting;
|
||||
|
||||
internal static void ByThreadException()
|
||||
{
|
||||
|
@ -34,24 +34,31 @@ namespace SystemTrayMenu.Utilities
|
|||
Restart(GetCurrentMethod());
|
||||
}
|
||||
|
||||
private static void Restart(string reason)
|
||||
private static void Restart(string? reason)
|
||||
{
|
||||
BeforeRestarting?.Invoke();
|
||||
Log.Info($"Restart by '{reason}'");
|
||||
Log.Info($"Restart by '{reason ?? "unkown"}'");
|
||||
Log.Close();
|
||||
|
||||
using (Process p = new())
|
||||
{
|
||||
string fileName = System.Environment.ProcessPath;
|
||||
p.StartInfo = new ProcessStartInfo(fileName);
|
||||
|
||||
try
|
||||
string? fileName = System.Environment.ProcessPath;
|
||||
if (string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
p.Start();
|
||||
Log.Warn("Restart failed", new());
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
else
|
||||
{
|
||||
Log.Warn("Restart failed", ex);
|
||||
p.StartInfo = new ProcessStartInfo(fileName);
|
||||
|
||||
try
|
||||
{
|
||||
p.Start();
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
Log.Warn("Restart failed", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,12 +66,12 @@ namespace SystemTrayMenu.Utilities
|
|||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static string GetCurrentMethod()
|
||||
private static string? GetCurrentMethod()
|
||||
{
|
||||
StackTrace st = new();
|
||||
StackFrame sf = st.GetFrame(1);
|
||||
StackFrame? sf = st.GetFrame(1);
|
||||
|
||||
return sf.GetMethod().Name;
|
||||
return sf?.GetMethod()?.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace SystemTrayMenu.Utilities
|
|||
else
|
||||
{
|
||||
Thread staThread = new(new ParameterizedThreadStart(StaThreadMethod));
|
||||
void StaThreadMethod(object obj)
|
||||
void StaThreadMethod(object? obj)
|
||||
{
|
||||
resolvedFilename = GetShortcutFileNamePath(shortcutFilename, out isFolderByShell);
|
||||
}
|
||||
|
@ -48,8 +48,8 @@ namespace SystemTrayMenu.Utilities
|
|||
isFolder = false;
|
||||
try
|
||||
{
|
||||
string pathOnly = Path.GetDirectoryName((string)shortcutFilename);
|
||||
string filenameOnly = Path.GetFileName((string)shortcutFilename);
|
||||
string? pathOnly = Path.GetDirectoryName((string)shortcutFilename);
|
||||
string? filenameOnly = Path.GetFileName((string)shortcutFilename);
|
||||
|
||||
Shell shell = new();
|
||||
Folder folder = shell.NameSpace(pathOnly);
|
||||
|
|
|
@ -1,90 +1,90 @@
|
|||
// <copyright file="IconReader.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
// see also: https://www.codeproject.com/Articles/2532/Obtaining-and-managing-file-and-folder-icons-using.
|
||||
|
||||
namespace SystemTrayMenu.Utilities
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using SystemTrayMenu.DllImports;
|
||||
|
||||
/// <summary>
|
||||
/// Provides static methods to read system icons for folders and files.
|
||||
/// </summary>
|
||||
public static class IconReader
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, Icon> DictIconCacheMainMenu = new();
|
||||
private static readonly ConcurrentDictionary<string, Icon> DictIconCacheSubMenus = new();
|
||||
|
||||
public enum IconSize
|
||||
{
|
||||
Large = 0, // 32x32 pixels
|
||||
Small = 1, // 16x16 pixels
|
||||
}
|
||||
|
||||
public enum FolderType
|
||||
{
|
||||
Open = 0,
|
||||
Closed = 1,
|
||||
}
|
||||
|
||||
// see https://github.com/Hofknecht/SystemTrayMenu/issues/209.
|
||||
public static bool MainPreload { get; set; }
|
||||
|
||||
public static void Dispose(bool includingMainMenu = true)
|
||||
{
|
||||
if (includingMainMenu)
|
||||
{
|
||||
foreach (Icon icon in DictIconCacheMainMenu.Values)
|
||||
{
|
||||
icon?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Icon icon in DictIconCacheSubMenus.Values)
|
||||
{
|
||||
icon?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ClearIfCacheTooBig()
|
||||
{
|
||||
bool cleared = false;
|
||||
if (DictIconCacheSubMenus.Count > Properties.Settings.Default.ClearCacheIfMoreThanThisNumberOfItems)
|
||||
{
|
||||
Dispose(false);
|
||||
DictIconCacheSubMenus.Clear();
|
||||
cleared = true;
|
||||
}
|
||||
|
||||
return cleared;
|
||||
}
|
||||
|
||||
public static void RemoveIconFromCache(string path)
|
||||
{
|
||||
if (DictIconCacheMainMenu.Remove(path, out Icon? iconToRemove))
|
||||
{
|
||||
iconToRemove?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static Icon? GetFileIconWithCache(
|
||||
string? path,
|
||||
string? resolvedPath,
|
||||
bool linkOverlay,
|
||||
bool updateIconInBackground,
|
||||
bool isMainMenu,
|
||||
out bool loading,
|
||||
string keyPath = "")
|
||||
{
|
||||
loading = false;
|
||||
// <copyright file="IconReader.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
// see also: https://www.codeproject.com/Articles/2532/Obtaining-and-managing-file-and-folder-icons-using.
|
||||
|
||||
namespace SystemTrayMenu.Utilities
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using SystemTrayMenu.DllImports;
|
||||
|
||||
/// <summary>
|
||||
/// Provides static methods to read system icons for folders and files.
|
||||
/// </summary>
|
||||
public static class IconReader
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, Icon?> DictIconCacheMainMenu = new();
|
||||
private static readonly ConcurrentDictionary<string, Icon?> DictIconCacheSubMenus = new();
|
||||
|
||||
public enum IconSize
|
||||
{
|
||||
Large = 0, // 32x32 pixels
|
||||
Small = 1, // 16x16 pixels
|
||||
}
|
||||
|
||||
public enum FolderType
|
||||
{
|
||||
Open = 0,
|
||||
Closed = 1,
|
||||
}
|
||||
|
||||
// see https://github.com/Hofknecht/SystemTrayMenu/issues/209.
|
||||
public static bool MainPreload { get; set; }
|
||||
|
||||
public static void Dispose(bool includingMainMenu = true)
|
||||
{
|
||||
if (includingMainMenu)
|
||||
{
|
||||
foreach (Icon? icon in DictIconCacheMainMenu.Values)
|
||||
{
|
||||
icon?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Icon? icon in DictIconCacheSubMenus.Values)
|
||||
{
|
||||
icon?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ClearIfCacheTooBig()
|
||||
{
|
||||
bool cleared = false;
|
||||
if (DictIconCacheSubMenus.Count > Properties.Settings.Default.ClearCacheIfMoreThanThisNumberOfItems)
|
||||
{
|
||||
Dispose(false);
|
||||
DictIconCacheSubMenus.Clear();
|
||||
cleared = true;
|
||||
}
|
||||
|
||||
return cleared;
|
||||
}
|
||||
|
||||
public static void RemoveIconFromCache(string path)
|
||||
{
|
||||
if (DictIconCacheMainMenu.Remove(path, out Icon? iconToRemove))
|
||||
{
|
||||
iconToRemove?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static Icon? GetFileIconWithCache(
|
||||
string? path,
|
||||
string? resolvedPath,
|
||||
bool linkOverlay,
|
||||
bool updateIconInBackground,
|
||||
bool isMainMenu,
|
||||
out bool loading,
|
||||
string keyPath = "")
|
||||
{
|
||||
loading = false;
|
||||
|
||||
Icon? icon = null;
|
||||
if (path != null)
|
||||
|
@ -107,7 +107,7 @@ namespace SystemTrayMenu.Utilities
|
|||
if (IsExtensionWithSameIcon(extension))
|
||||
{
|
||||
key = extension + linkOverlay;
|
||||
}
|
||||
}
|
||||
|
||||
if (!DictIconCache(isMainMenu).TryGetValue(key, out icon) &&
|
||||
!DictIconCache(!isMainMenu).TryGetValue(key, out icon))
|
||||
|
@ -123,20 +123,20 @@ namespace SystemTrayMenu.Utilities
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
public static Icon? GetFolderIconWithCache(
|
||||
string? path,
|
||||
bool linkOverlay,
|
||||
bool updateIconInBackground,
|
||||
bool isMainMenu,
|
||||
out bool loading)
|
||||
{
|
||||
loading = false;
|
||||
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
public static Icon? GetFolderIconWithCache(
|
||||
string? path,
|
||||
bool linkOverlay,
|
||||
bool updateIconInBackground,
|
||||
bool isMainMenu,
|
||||
out bool loading)
|
||||
{
|
||||
loading = false;
|
||||
|
||||
Icon? icon = null;
|
||||
if (path != null)
|
||||
{
|
||||
|
@ -171,166 +171,166 @@ namespace SystemTrayMenu.Utilities
|
|||
}
|
||||
}
|
||||
|
||||
Icon GetFolder(string keyExtension)
|
||||
Icon? GetFolder(string keyExtension)
|
||||
{
|
||||
return GetIconSTA(path, path, linkOverlay, size, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
public static Icon? GetIconSTA(string path, string resolvedPath, bool linkOverlay, IconSize size, bool isFolder)
|
||||
{
|
||||
Icon? icon = null;
|
||||
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
|
||||
{
|
||||
icon = GetIcon(path, resolvedPath, linkOverlay, size, isFolder);
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread staThread = new(new ParameterizedThreadStart(StaThreadMethod));
|
||||
void StaThreadMethod(object? obj)
|
||||
{
|
||||
icon = GetIcon(path, resolvedPath, linkOverlay, size, isFolder);
|
||||
}
|
||||
|
||||
staThread.SetApartmentState(ApartmentState.STA);
|
||||
staThread.Start(icon);
|
||||
staThread.Join();
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
public static Icon? AddIconOverlay(Icon? originalIcon, Icon overlay)
|
||||
{
|
||||
Icon? icon = originalIcon;
|
||||
if (originalIcon != null)
|
||||
{
|
||||
using Bitmap target = new(originalIcon.Width, originalIcon.Height, PixelFormat.Format32bppArgb);
|
||||
using Graphics graphics = Graphics.FromImage(target);
|
||||
graphics.DrawIcon(originalIcon, 0, 0);
|
||||
graphics.DrawIcon(overlay, new(0, 0, originalIcon.Width + 2, originalIcon.Height + 2));
|
||||
target.MakeTransparent(target.GetPixel(1, 1));
|
||||
IntPtr hIcon = target.GetHicon();
|
||||
icon = (Icon)Icon.FromHandle(hIcon).Clone();
|
||||
NativeMethods.User32DestroyIcon(hIcon);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
private static ConcurrentDictionary<string, Icon> DictIconCache(bool isMainMenu)
|
||||
{
|
||||
if (isMainMenu)
|
||||
{
|
||||
return DictIconCacheMainMenu;
|
||||
}
|
||||
else
|
||||
{
|
||||
return DictIconCacheSubMenus;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsExtensionWithSameIcon(string fileExtension)
|
||||
{
|
||||
bool isExtensionWithSameIcon = true;
|
||||
List<string> extensionsWithDiffIcons = new() { string.Empty, ".EXE", ".LNK", ".ICO", ".URL" };
|
||||
if (extensionsWithDiffIcons.Contains(fileExtension.ToUpperInvariant()))
|
||||
{
|
||||
isExtensionWithSameIcon = false;
|
||||
}
|
||||
|
||||
return isExtensionWithSameIcon;
|
||||
}
|
||||
|
||||
private static Icon? GetIcon(string path, string? resolvedPath, bool linkOverlay, IconSize size, bool isFolder)
|
||||
{
|
||||
Icon? icon;
|
||||
if (Path.GetExtension(path).Equals(".ico", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
icon = Icon.ExtractAssociatedIcon(path);
|
||||
}
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
public static Icon? GetIconSTA(string path, string? resolvedPath, bool linkOverlay, IconSize size, bool isFolder)
|
||||
{
|
||||
Icon? icon = null;
|
||||
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
|
||||
{
|
||||
icon = GetIcon(path, resolvedPath, linkOverlay, size, isFolder);
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread staThread = new(new ParameterizedThreadStart(StaThreadMethod));
|
||||
void StaThreadMethod(object? obj)
|
||||
{
|
||||
icon = GetIcon(path, resolvedPath, linkOverlay, size, isFolder);
|
||||
}
|
||||
|
||||
staThread.SetApartmentState(ApartmentState.STA);
|
||||
staThread.Start(icon);
|
||||
staThread.Join();
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
public static Icon? AddIconOverlay(Icon? originalIcon, Icon overlay)
|
||||
{
|
||||
Icon? icon = originalIcon;
|
||||
if (originalIcon != null)
|
||||
{
|
||||
using Bitmap target = new(originalIcon.Width, originalIcon.Height, PixelFormat.Format32bppArgb);
|
||||
using Graphics graphics = Graphics.FromImage(target);
|
||||
graphics.DrawIcon(originalIcon, 0, 0);
|
||||
graphics.DrawIcon(overlay, new(0, 0, originalIcon.Width + 2, originalIcon.Height + 2));
|
||||
target.MakeTransparent(target.GetPixel(1, 1));
|
||||
IntPtr hIcon = target.GetHicon();
|
||||
icon = (Icon)Icon.FromHandle(hIcon).Clone();
|
||||
NativeMethods.User32DestroyIcon(hIcon);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
private static ConcurrentDictionary<string, Icon?> DictIconCache(bool isMainMenu)
|
||||
{
|
||||
if (isMainMenu)
|
||||
{
|
||||
return DictIconCacheMainMenu;
|
||||
}
|
||||
else
|
||||
{
|
||||
return DictIconCacheSubMenus;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsExtensionWithSameIcon(string fileExtension)
|
||||
{
|
||||
bool isExtensionWithSameIcon = true;
|
||||
List<string> extensionsWithDiffIcons = new() { string.Empty, ".EXE", ".LNK", ".ICO", ".URL" };
|
||||
if (extensionsWithDiffIcons.Contains(fileExtension.ToUpperInvariant()))
|
||||
{
|
||||
isExtensionWithSameIcon = false;
|
||||
}
|
||||
|
||||
return isExtensionWithSameIcon;
|
||||
}
|
||||
|
||||
private static Icon? GetIcon(string path, string? resolvedPath, bool linkOverlay, IconSize size, bool isFolder)
|
||||
{
|
||||
Icon? icon;
|
||||
if (Path.GetExtension(path).Equals(".ico", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
icon = Icon.ExtractAssociatedIcon(path);
|
||||
}
|
||||
else if (File.Exists(resolvedPath) &&
|
||||
Path.GetExtension(resolvedPath).Equals(".ico", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
icon = Icon.ExtractAssociatedIcon(resolvedPath);
|
||||
if (linkOverlay)
|
||||
{
|
||||
icon = AddIconOverlay(icon, Properties.Resources.LinkArrow);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NativeMethods.SHFILEINFO shFileInfo = default;
|
||||
uint flags = GetFlags(linkOverlay, size);
|
||||
uint attribute = isFolder ? NativeMethods.FileAttributeDirectory : NativeMethods.FileAttributeNormal;
|
||||
IntPtr imageList = NativeMethods.Shell32SHGetFileInfo(
|
||||
path, attribute, ref shFileInfo, (uint)Marshal.SizeOf(shFileInfo), flags);
|
||||
icon = GetIcon(path, linkOverlay, shFileInfo, imageList);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
private static uint GetFlags(bool linkOverlay, IconSize size)
|
||||
{
|
||||
uint flags = NativeMethods.ShgfiIcon | NativeMethods.ShgfiSYSICONINDEX;
|
||||
if (linkOverlay)
|
||||
{
|
||||
flags += NativeMethods.ShgfiLINKOVERLAY;
|
||||
}
|
||||
|
||||
if (size == IconSize.Small)
|
||||
{
|
||||
flags += NativeMethods.ShgfiSMALLICON;
|
||||
}
|
||||
else
|
||||
{
|
||||
flags += NativeMethods.ShgfiLARGEICON;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
private static Icon? GetIcon(
|
||||
string path, bool linkOverlay, NativeMethods.SHFILEINFO shFileInfo, IntPtr imageList)
|
||||
{
|
||||
Icon? icon = null;
|
||||
if (imageList != IntPtr.Zero)
|
||||
{
|
||||
IntPtr hIcon;
|
||||
if (linkOverlay)
|
||||
{
|
||||
hIcon = shFileInfo.hIcon;
|
||||
}
|
||||
else
|
||||
{
|
||||
hIcon = NativeMethods.ImageList_GetIcon(
|
||||
imageList, shFileInfo.iIcon, NativeMethods.IldTransparent);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
icon = (Icon)Icon.FromHandle(hIcon).Clone();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"path:'{path}'", ex);
|
||||
}
|
||||
|
||||
if (!linkOverlay)
|
||||
{
|
||||
NativeMethods.User32DestroyIcon(hIcon);
|
||||
}
|
||||
|
||||
NativeMethods.User32DestroyIcon(shFileInfo.hIcon);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
Path.GetExtension(resolvedPath).Equals(".ico", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
icon = Icon.ExtractAssociatedIcon(resolvedPath);
|
||||
if (linkOverlay)
|
||||
{
|
||||
icon = AddIconOverlay(icon, Properties.Resources.LinkArrow);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NativeMethods.SHFILEINFO shFileInfo = default;
|
||||
uint flags = GetFlags(linkOverlay, size);
|
||||
uint attribute = isFolder ? NativeMethods.FileAttributeDirectory : NativeMethods.FileAttributeNormal;
|
||||
IntPtr imageList = NativeMethods.Shell32SHGetFileInfo(
|
||||
path, attribute, ref shFileInfo, (uint)Marshal.SizeOf(shFileInfo), flags);
|
||||
icon = GetIcon(path, linkOverlay, shFileInfo, imageList);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
private static uint GetFlags(bool linkOverlay, IconSize size)
|
||||
{
|
||||
uint flags = NativeMethods.ShgfiIcon | NativeMethods.ShgfiSYSICONINDEX;
|
||||
if (linkOverlay)
|
||||
{
|
||||
flags += NativeMethods.ShgfiLINKOVERLAY;
|
||||
}
|
||||
|
||||
if (size == IconSize.Small)
|
||||
{
|
||||
flags += NativeMethods.ShgfiSMALLICON;
|
||||
}
|
||||
else
|
||||
{
|
||||
flags += NativeMethods.ShgfiLARGEICON;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
private static Icon? GetIcon(
|
||||
string path, bool linkOverlay, NativeMethods.SHFILEINFO shFileInfo, IntPtr imageList)
|
||||
{
|
||||
Icon? icon = null;
|
||||
if (imageList != IntPtr.Zero)
|
||||
{
|
||||
IntPtr hIcon;
|
||||
if (linkOverlay)
|
||||
{
|
||||
hIcon = shFileInfo.hIcon;
|
||||
}
|
||||
else
|
||||
{
|
||||
hIcon = NativeMethods.ImageList_GetIcon(
|
||||
imageList, shFileInfo.iIcon, NativeMethods.IldTransparent);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
icon = (Icon)Icon.FromHandle(hIcon).Clone();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"path:'{path}'", ex);
|
||||
}
|
||||
|
||||
if (!linkOverlay)
|
||||
{
|
||||
NativeMethods.User32DestroyIcon(hIcon);
|
||||
}
|
||||
|
||||
NativeMethods.User32DestroyIcon(shFileInfo.hIcon);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,25 +15,23 @@ namespace SystemTrayMenu.Utilities
|
|||
{
|
||||
private static bool hideHiddenEntries;
|
||||
private static bool hideSystemEntries;
|
||||
private static IShellDispatch4 iShellDispatch4;
|
||||
private static IShellDispatch4? iShellDispatch4;
|
||||
|
||||
internal static void Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
iShellDispatch4 = (IShellDispatch4)Activator.CreateInstance(
|
||||
Type.GetTypeFromProgID("Shell.Application"));
|
||||
iShellDispatch4 = (IShellDispatch4?)Activator.CreateInstance(
|
||||
Type.GetTypeFromProgID("Shell.Application")!);
|
||||
|
||||
// Using SHGetSetSettings would be much better in performance but the results are not accurate.
|
||||
// We have to go for the shell interface in order to receive the correct settings:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/shell/ishelldispatch4-getsetting
|
||||
const int SSF_SHOWALLOBJECTS = 0x00000001;
|
||||
hideHiddenEntries = !iShellDispatch4.GetSetting(
|
||||
SSF_SHOWALLOBJECTS);
|
||||
hideHiddenEntries = !(iShellDispatch4?.GetSetting(SSF_SHOWALLOBJECTS) ?? false);
|
||||
|
||||
const int SSF_SHOWSUPERHIDDEN = 0x00040000;
|
||||
hideSystemEntries = !iShellDispatch4.GetSetting(
|
||||
SSF_SHOWSUPERHIDDEN);
|
||||
hideSystemEntries = !(iShellDispatch4?.GetSetting(SSF_SHOWSUPERHIDDEN) ?? false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -59,7 +57,7 @@ namespace SystemTrayMenu.Utilities
|
|||
internal static bool IsHidden(RowData rowData)
|
||||
{
|
||||
bool isDirectoryToHide = false;
|
||||
if (rowData.Path.Length >= 260)
|
||||
if (rowData.Path == null || rowData.Path.Length >= 260)
|
||||
{
|
||||
Log.Info($"path too long (>=260):'{rowData.Path}'");
|
||||
return isDirectoryToHide;
|
||||
|
|
|
@ -5,13 +5,12 @@
|
|||
namespace SystemTrayMenu.Utilities
|
||||
{
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
internal static class Scaling
|
||||
{
|
||||
private static FontSizeConverter fontConverter = new FontSizeConverter();
|
||||
private static readonly FontSizeConverter FontConverter = new ();
|
||||
|
||||
public static float Factor { get; private set; } = 1;
|
||||
|
||||
|
@ -34,7 +33,7 @@ namespace SystemTrayMenu.Utilities
|
|||
|
||||
public static double ScaleFontByPoints(float points)
|
||||
{
|
||||
return (double)fontConverter.ConvertFrom((points * Factor).ToString() + "pt") !;
|
||||
return (double)FontConverter.ConvertFrom((points * Factor).ToString() + "pt")!;
|
||||
}
|
||||
|
||||
public static double ScaleFontByPixels(float pixels)
|
||||
|
|
Loading…
Reference in a new issue