Compare commits
468 commits
v1.0.17.32
...
master
Author | SHA1 | Date | |
---|---|---|---|
ccbfd22db0 | |||
23d846c691 | |||
a16f686b14 | |||
3c9f345db3 | |||
bd71f6fa3b | |||
5c043cea17 | |||
dbd1e4e0da | |||
effaa6a79e | |||
88d0abc548 | |||
851c0917ec | |||
72a46d9d5b | |||
7305a74a1b | |||
ea1b7eb9bb | |||
b80802bd6e | |||
c1a821d03c | |||
1cde65f3da | |||
20d7f05edd | |||
5fbba28fa6 | |||
1b09ee8c9b | |||
784c671a81 | |||
cdb86df907 | |||
945114317f | |||
b30b9193b9 | |||
b2f3a938f1 | |||
432ca5f0e5 | |||
30d9dfe786 | |||
f4290f4905 | |||
9bc8847f3c | |||
7ea5b40248 | |||
68258eb104 | |||
9683e5c461 | |||
00b4c5ddcc | |||
e76790a5e1 | |||
2add7288ea | |||
49acc2ae27 | |||
a296df3da4 | |||
4cf4b980ea | |||
1f9a2483f4 | |||
12b0a99e76 | |||
6dd343e5f4 | |||
83944572da | |||
a131b2a3dd | |||
aa3dcbc38c | |||
1d4820c946 | |||
87dbd64c73 | |||
ad4942aad6 | |||
d01948563c | |||
52bff9dbf9 | |||
56122d04d5 | |||
3415a92820 | |||
b50b122d8c | |||
18f1570d99 | |||
5069e00eee | |||
0254018210 | |||
9fa3b70e39 | |||
729afdf67f | |||
fbbd0f1f54 | |||
3a37b16dfd | |||
e28c2724b1 | |||
fb0f07c19a | |||
effa675eac | |||
123bece791 | |||
2662b7540d | |||
1a9da04f78 | |||
8f1d04a66c | |||
14a83d7672 | |||
275b707bd2 | |||
41d0c76e5a | |||
a14d0296a3 | |||
480ad7b13b | |||
b2ce80f376 | |||
a14ea6fb32 | |||
663baa3f69 | |||
88bdabf5f2 | |||
9c43fb181f | |||
271a55d0ff | |||
5007203094 | |||
02b80946ce | |||
5cc40a1739 | |||
05c381bb70 | |||
2ef5922801 | |||
b30fdee093 | |||
22bfc56146 | |||
1fab03d91e | |||
919d88c7ed | |||
f2b34ea33f | |||
70a70acd02 | |||
d9ad6bdd7b | |||
49766341c1 | |||
3f23f0ebc2 | |||
7c8926a29a | |||
2c9dc941de | |||
75b0648983 | |||
aa91a4088f | |||
5677320a54 | |||
25ee3d2043 | |||
c0e962dd19 | |||
7211e4dff1 | |||
1ec1842bb0 | |||
e4c297e333 | |||
7c253fa28a | |||
bcb537d1ee | |||
d6093e46b6 | |||
7afcb95f5c | |||
f5b29b43cd | |||
48fcc40acd | |||
532716ea12 | |||
e5e1f848e4 | |||
c79cefd1c9 | |||
9dbbaf1b02 | |||
81dd170b62 | |||
76cde235c9 | |||
c614cdf71b | |||
d3c61273d3 | |||
faa32f96a2 | |||
ac70e4369a | |||
10b4c820ea | |||
a4836d10b9 | |||
4973212f15 | |||
94dd3ddd0c | |||
23f69c5fa8 | |||
62c7883ed6 | |||
c4debe75a8 | |||
d61dffbbd4 | |||
0988b5c211 | |||
07ed93d29e | |||
e12842085e | |||
1172bdd114 | |||
587ebcfc90 | |||
756838ae85 | |||
a16e8eb7c5 | |||
137a792431 | |||
cc502be893 | |||
9411780c48 | |||
e6d805145a | |||
4427f2e9e5 | |||
7c20b6088d | |||
73a5ea9cae | |||
1bbfe4354b | |||
ca8523752e | |||
37ec270c25 | |||
9234515fd9 | |||
f4b540f339 | |||
3c0ed17988 | |||
a9f3308b27 | |||
aef79c431a | |||
49eac15a87 | |||
a8643b00b1 | |||
eaf755ee81 | |||
7256910367 | |||
e6bd977f5c | |||
5eb471b077 | |||
afef0fa13f | |||
dfa24aadb1 | |||
74970ad7b7 | |||
38cca98da5 | |||
57c634149e | |||
ab4c30bb15 | |||
f8f50d6c27 | |||
44db922628 | |||
cd508b7584 | |||
411fc5ab07 | |||
c62670901e | |||
3a11885d72 | |||
39a0e750bd | |||
36ceb2b9af | |||
3ba03b8f9e | |||
ba69fe4df8 | |||
21b79ed0d4 | |||
3def2f40c0 | |||
37355bdfe1 | |||
021d1b2d85 | |||
b32f8aaffd | |||
168c67e992 | |||
461b4a9d17 | |||
4ddd229797 | |||
fc55672814 | |||
1ff825f598 | |||
fffa4b62f5 | |||
206dc1c3df | |||
fe2afeeb92 | |||
e214d71862 | |||
23848e8014 | |||
4c927a4225 | |||
2c4e9c656c | |||
86c0771c3e | |||
95181d34a5 | |||
f683797258 | |||
4ad0e3c7a2 | |||
754830eda4 | |||
16c25df2b0 | |||
282eede778 | |||
d6fc6941db | |||
7968ccf4a3 | |||
9e65ec3143 | |||
968cf08a36 | |||
9725c092e9 | |||
c219720685 | |||
101df84e73 | |||
27f2fed2f8 | |||
27a77d5b31 | |||
6b972b52cd | |||
a43c6e0d9c | |||
ea1d6a0d19 | |||
2ec51aa3e2 | |||
4bd9abe3f7 | |||
a98461a2c7 | |||
80a13f67e1 | |||
07c5a8a585 | |||
04fd61fade | |||
72b9f56e74 | |||
8354e9fece | |||
2242f2bc34 | |||
5533a2d749 | |||
0511468757 | |||
09a6b8927f | |||
50d0dc09d1 | |||
a4a13ecde5 | |||
fe4e172b9b | |||
4c600a8c66 | |||
2659d19283 | |||
6d28822851 | |||
d2705342c0 | |||
1c571291de | |||
7e1f646783 | |||
057e2281a6 | |||
18443a1682 | |||
555996a3cb | |||
c5d9b5aeda | |||
543ff7bc29 | |||
3ce1407477 | |||
a102c968f3 | |||
0d393684a0 | |||
8394704fec | |||
db46db002d | |||
401e1329f1 | |||
6d3832521f | |||
d0dfc22a37 | |||
dbc7d83eed | |||
1da1f489f4 | |||
94cf09f2b1 | |||
e8ca06e005 | |||
1aa6e7171d | |||
3dbafe4075 | |||
961131e1c1 | |||
1e9d94e70f | |||
6092273d52 | |||
6078d6cf8e | |||
b224692eea | |||
f80fbf6583 | |||
088412ddb4 | |||
31bae17632 | |||
76b04a0979 | |||
424bd195eb | |||
b4ad6b4ec1 | |||
1840afdd29 | |||
f2ac3fa66a | |||
e81e955da9 | |||
af7591617e | |||
adea63e089 | |||
a94bbb8bb5 | |||
466151311d | |||
5c2a4c5bc2 | |||
ddb8241528 | |||
c0351a534d | |||
a846f1b5e4 | |||
4fad5b17aa | |||
8233667fd5 | |||
2c04a00307 | |||
a5e68548d2 | |||
7ebdb01934 | |||
ab91572424 | |||
5df5fd77cd | |||
b1226d15a1 | |||
b7a7364b40 | |||
976ad23fc3 | |||
6611d4a7e8 | |||
58ee1f86ec | |||
8db78f8131 | |||
c2d5ca5062 | |||
7a0bfef122 | |||
79f0bbe740 | |||
92d7e058a9 | |||
898768ce71 | |||
08c6b09b2f | |||
bba1bedede | |||
83fa626b08 | |||
63a8300a68 | |||
a2accaa6e6 | |||
3dc0b487f0 | |||
cc9ebd4ecd | |||
8d0c0bb307 | |||
8021cbf02c | |||
c833817ab0 | |||
ac5a24dd3a | |||
b4f3f4f0a1 | |||
e5bb901a62 | |||
ff2ee98393 | |||
775500c5e4 | |||
9da079eea5 | |||
c073204193 | |||
c647cfef86 | |||
69e8cf4e6b | |||
7f4266fffb | |||
3dce420521 | |||
fd3cbd1f69 | |||
8c32dcbeb1 | |||
78325f1c61 | |||
5830f530c2 | |||
675facc773 | |||
e45c97b4f8 | |||
1f84df7300 | |||
53b2a5082d | |||
9333577a4f | |||
b70f966371 | |||
f2303853a1 | |||
a2507973db | |||
7f65ef0e7f | |||
fd554ec7ea | |||
ec7a2467f1 | |||
e4f3d6b67a | |||
6d8cf4da39 | |||
fc353ce760 | |||
a677a7a9b4 | |||
eee8b6b2f0 | |||
47f5c32655 | |||
467afe57d1 | |||
c84b7b3996 | |||
bc2899708a | |||
3ec663361f | |||
a140f13c23 | |||
ceb128b702 | |||
bf1629eb4c | |||
9189b0629d | |||
ae34aded47 | |||
cba17dd877 | |||
f2366c0512 | |||
680c093659 | |||
ed362a8f9d | |||
d3e4c655ae | |||
7c234efad6 | |||
f5234bcec3 | |||
8a6101d329 | |||
8cff4365fc | |||
15cf58c28c | |||
56ba365909 | |||
a30778401e | |||
a35ebda9d4 | |||
155d0679a8 | |||
49ea3cd7fe | |||
1e330d40a8 | |||
a16a3687fa | |||
e9efc8cd0b | |||
ee828466b2 | |||
f999504b9b | |||
5382f9f5af | |||
b217c55bc1 | |||
4919aa9afd | |||
30312cc025 | |||
b4f3e4e6b5 | |||
a8eec0d626 | |||
dc02aab5ab | |||
d669b15bde | |||
de57fce8f3 | |||
450fbfddf3 | |||
6d563f63ec | |||
64e7e93167 | |||
693f196225 | |||
8e83943044 | |||
0e7c59af0c | |||
8d5690928f | |||
b5d35ea0d5 | |||
a0cbd533cc | |||
f323abca72 | |||
520e056f6f | |||
76492e11dd | |||
bec9212d1b | |||
e921e1dc7e | |||
99fb5a0c08 | |||
b1dd42518c | |||
bf7a92f6b9 | |||
cc70299972 | |||
7c77c235c4 | |||
ee6e7b65f4 | |||
e55ed9870d | |||
2683a032c8 | |||
2c16ade92f | |||
6ca1cbaf5a | |||
edd66fb68c | |||
48a896ada6 | |||
4b13cd7129 | |||
0783a12727 | |||
4f38568896 | |||
577b9b7d5d | |||
35422f9ba5 | |||
8dd90406dc | |||
0a310aadb0 | |||
f430782d23 | |||
d5af7893ce | |||
97fda95805 | |||
0f745cd586 | |||
74c68b6d46 | |||
99f4c6e46c | |||
134536dbde | |||
39fb41a9b9 | |||
bb89755da6 | |||
6e17a69cf9 | |||
70f95a0991 | |||
61fdad66d9 | |||
7ee83dd8c9 | |||
7afcf103bf | |||
17079c28e6 | |||
fa12339659 | |||
7310f4c3a5 | |||
9ef27fd14e | |||
d644154c6d | |||
95059eaab2 | |||
0fa8b4cf84 | |||
c5f4add68b | |||
47ccdd7d21 | |||
e6f496a72e | |||
81b4bd037b | |||
e10620f717 | |||
0fe947e4bf | |||
3d22f41b2a | |||
fd6c3d5be2 | |||
f2cb823c54 | |||
58d99bf793 | |||
fae8fec3c3 | |||
b12ca85489 | |||
642b3a731e | |||
bdaebea71a | |||
27a018e5d0 | |||
366acc27ad | |||
0241f9c93c | |||
c0d71c6948 | |||
7b526db87e | |||
f4338c1c50 | |||
bc89f2403a | |||
3e205d8479 | |||
0044b88a79 | |||
d3318b65da | |||
30fe44980b | |||
2ee8779652 | |||
1aa3dec4d5 | |||
027e51407c | |||
a972ae3b70 | |||
bae4d72585 | |||
bd95ee3cd5 | |||
85587370f8 | |||
7bac914f51 | |||
4fefc4f253 | |||
d4fb5afdc7 | |||
cae6aa7922 | |||
0374261774 | |||
600d73d40e | |||
313d786555 | |||
5dd6828a31 | |||
5db49aebd0 | |||
b7038b5780 | |||
94bc94b626 | |||
ed8232b606 | |||
65bf8e55e9 | |||
b7c1c4fe6e | |||
b871d422e4 | |||
5ae2ca2e26 | |||
70f98c97d8 | |||
519c9c8fe4 |
79
.editorconfig
Normal file
|
@ -0,0 +1,79 @@
|
|||
[*.cs]
|
||||
|
||||
# WFAC010: Unsupported high DPI configuration
|
||||
dotnet_diagnostic.WFAC010.severity = silent
|
||||
csharp_indent_labels = one_less_than_current
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
csharp_prefer_simple_using_statement = true:suggestion
|
||||
csharp_prefer_braces = true:silent
|
||||
csharp_style_namespace_declarations = block_scoped:silent
|
||||
csharp_style_prefer_method_group_conversion = true:silent
|
||||
csharp_style_prefer_top_level_statements = true:silent
|
||||
csharp_style_expression_bodied_methods = false:silent
|
||||
csharp_style_expression_bodied_constructors = false:silent
|
||||
csharp_style_expression_bodied_operators = false:silent
|
||||
csharp_style_expression_bodied_properties = true:silent
|
||||
csharp_style_expression_bodied_indexers = true:silent
|
||||
csharp_style_expression_bodied_accessors = true:silent
|
||||
csharp_style_expression_bodied_lambdas = true:silent
|
||||
csharp_style_expression_bodied_local_functions = false:silent
|
||||
dotnet_diagnostic.SX1101.severity = warning
|
||||
dotnet_diagnostic.SA1101.severity = silent
|
||||
|
||||
[*.{cs,vb}]
|
||||
#### Naming styles ####
|
||||
|
||||
# Naming rules
|
||||
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||
|
||||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||
|
||||
# Symbol specifications
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.types.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
|
||||
# Naming styles
|
||||
|
||||
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||
dotnet_naming_style.begins_with_i.required_suffix =
|
||||
dotnet_naming_style.begins_with_i.word_separator =
|
||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
tab_width = 4
|
||||
indent_size = 4
|
||||
end_of_line = crlf
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
|
||||
dotnet_style_prefer_auto_properties = true:silent
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
|
@ -4,9 +4,11 @@
|
|||
namespace SystemTrayMenu
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Win32;
|
||||
using SystemTrayMenu.Business;
|
||||
using SystemTrayMenu.Helper.Updater;
|
||||
using SystemTrayMenu.UserInterface;
|
||||
using SystemTrayMenu.Utilities;
|
||||
|
||||
|
@ -15,32 +17,98 @@ namespace SystemTrayMenu
|
|||
/// </summary>
|
||||
internal class App : IDisposable
|
||||
{
|
||||
private readonly AppNotifyIcon menuNotifyIcon = new AppNotifyIcon();
|
||||
private readonly Menus menus = new Menus();
|
||||
private readonly AppNotifyIcon menuNotifyIcon = new();
|
||||
private readonly Menus menus = new();
|
||||
private readonly TaskbarForm taskbarForm = null;
|
||||
|
||||
public App()
|
||||
{
|
||||
AppRestart.BeforeRestarting += Dispose;
|
||||
SystemEvents.DisplaySettingsChanged += AppRestart.ByDisplaySettings;
|
||||
SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged;
|
||||
menus.LoadStarted += menuNotifyIcon.LoadingStart;
|
||||
menus.LoadStopped += menuNotifyIcon.LoadingStop;
|
||||
menuNotifyIcon.Exit += Application.Exit;
|
||||
menuNotifyIcon.Restart += AppRestart.ByMenuNotifyIcon;
|
||||
menuNotifyIcon.Click += MenuNotifyIcon_Click;
|
||||
void MenuNotifyIcon_Click()
|
||||
{
|
||||
menus.SwitchOpenClose(true);
|
||||
}
|
||||
|
||||
menuNotifyIcon.OpenLog += Log.OpenLogFile;
|
||||
menus.MainPreload();
|
||||
|
||||
if (Properties.Settings.Default.ShowInTaskbar)
|
||||
{
|
||||
taskbarForm = new TaskbarForm();
|
||||
taskbarForm.FormClosed += TaskbarForm_FormClosed;
|
||||
taskbarForm.Deactivate += SetStateNormal;
|
||||
taskbarForm.Resize += SetStateNormal;
|
||||
taskbarForm.Activated += TasbkarItemActivated;
|
||||
}
|
||||
|
||||
DllImports.NativeMethods.User32ShowInactiveTopmost(taskbarForm);
|
||||
|
||||
if (Properties.Settings.Default.CheckForUpdates)
|
||||
{
|
||||
new Thread((obj) => GitHubUpdate.ActivateNewVersionFormOrCheckForUpdates(
|
||||
showWhenUpToDate: false))
|
||||
.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
SystemEvents.DisplaySettingsChanged -= AppRestart.ByDisplaySettings;
|
||||
menus.Dispose();
|
||||
menuNotifyIcon.Dispose();
|
||||
if (taskbarForm?.InvokeRequired == true)
|
||||
{
|
||||
taskbarForm.Invoke(Dispose);
|
||||
}
|
||||
else
|
||||
{
|
||||
AppRestart.BeforeRestarting -= Dispose;
|
||||
SystemEvents.DisplaySettingsChanged -= SystemEvents_DisplaySettingsChanged;
|
||||
menus.LoadStarted -= menuNotifyIcon.LoadingStart;
|
||||
menus.LoadStopped -= menuNotifyIcon.LoadingStop;
|
||||
menus.Dispose();
|
||||
menuNotifyIcon.Click -= MenuNotifyIcon_Click;
|
||||
menuNotifyIcon.OpenLog -= Log.OpenLogFile;
|
||||
menuNotifyIcon.Dispose();
|
||||
if (taskbarForm != null)
|
||||
{
|
||||
taskbarForm.FormClosed -= TaskbarForm_FormClosed;
|
||||
taskbarForm.Deactivate -= SetStateNormal;
|
||||
taskbarForm.Resize -= SetStateNormal;
|
||||
taskbarForm.Activated -= TasbkarItemActivated;
|
||||
taskbarForm.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
|
||||
{
|
||||
menus.ReAdjustSizeAndLocation();
|
||||
}
|
||||
|
||||
private void MenuNotifyIcon_Click()
|
||||
{
|
||||
menus.SwitchOpenClose(true);
|
||||
}
|
||||
|
||||
private void TaskbarForm_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This ensures that next click on taskbaritem works as activate event/click event.
|
||||
/// </summary>
|
||||
private void SetStateNormal(object sender, EventArgs e)
|
||||
{
|
||||
if (Form.ActiveForm == taskbarForm)
|
||||
{
|
||||
taskbarForm.WindowState = FormWindowState.Normal;
|
||||
}
|
||||
}
|
||||
|
||||
private void TasbkarItemActivated(object sender, EventArgs e)
|
||||
{
|
||||
SetStateNormal(sender, e);
|
||||
taskbarForm.Activate();
|
||||
taskbarForm.Focus();
|
||||
menus.SwitchOpenCloseByTaskbarItem();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ namespace SystemTrayMenu.Handler
|
|||
internal class KeyboardInput : IDisposable
|
||||
{
|
||||
private readonly Menu[] menus;
|
||||
private readonly KeyboardHook hook = new KeyboardHook();
|
||||
private readonly KeyboardHook hook = new();
|
||||
|
||||
private int iRowKey = -1;
|
||||
private int iMenuKey;
|
||||
|
@ -27,26 +27,27 @@ namespace SystemTrayMenu.Handler
|
|||
this.menus = menus;
|
||||
}
|
||||
|
||||
internal event EventHandlerEmpty HotKeyPressed;
|
||||
public event Action HotKeyPressed;
|
||||
|
||||
internal event EventHandlerEmpty ClosePressed;
|
||||
public event Action ClosePressed;
|
||||
|
||||
internal event Action<DataGridView, int> RowSelected;
|
||||
public event Action<DataGridView, int> RowSelected;
|
||||
|
||||
internal event Action<int, DataGridView> RowDeselected;
|
||||
public event Action<DataGridView, int> RowDeselected;
|
||||
|
||||
internal event Action<DataGridView, int> EnterPressed;
|
||||
public event Action<DataGridView, int> EnterPressed;
|
||||
|
||||
internal event EventHandlerEmpty Cleared;
|
||||
public event Action Cleared;
|
||||
|
||||
internal bool InUse { get; set; }
|
||||
public bool InUse { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
hook.KeyPressed -= Hook_KeyPressed;
|
||||
hook.Dispose();
|
||||
}
|
||||
|
||||
internal void RegisterHotKey()
|
||||
public void RegisterHotKey()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Properties.Settings.Default.HotKey))
|
||||
{
|
||||
|
@ -54,10 +55,6 @@ namespace SystemTrayMenu.Handler
|
|||
{
|
||||
hook.RegisterHotKey();
|
||||
hook.KeyPressed += Hook_KeyPressed;
|
||||
void Hook_KeyPressed(object sender, KeyPressedEventArgs e)
|
||||
{
|
||||
HotKeyPressed?.Invoke();
|
||||
}
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
|
@ -68,27 +65,38 @@ namespace SystemTrayMenu.Handler
|
|||
}
|
||||
}
|
||||
|
||||
internal void ResetSelectedByKey()
|
||||
public void ResetSelectedByKey()
|
||||
{
|
||||
iRowKey = -1;
|
||||
iMenuKey = 0;
|
||||
}
|
||||
|
||||
internal void CmdKeyProcessed(object sender, Keys keys)
|
||||
public void CmdKeyProcessed(object sender, Keys keys)
|
||||
{
|
||||
sender ??= menus[iMenuKey];
|
||||
|
||||
switch (keys)
|
||||
{
|
||||
case Keys.Enter:
|
||||
SelectByKey(keys);
|
||||
menus[iMenuKey]?.FocusTextBox();
|
||||
break;
|
||||
case Keys.Left:
|
||||
SelectByKey(keys);
|
||||
break;
|
||||
case Keys.Right:
|
||||
SelectByKey(keys);
|
||||
break;
|
||||
case Keys.Home:
|
||||
case Keys.End:
|
||||
case Keys.Up:
|
||||
case Keys.Down:
|
||||
case Keys.Left:
|
||||
case Keys.Right:
|
||||
case Keys.Escape:
|
||||
case Keys.Alt | Keys.F4:
|
||||
SelectByKey(keys);
|
||||
break;
|
||||
case Keys.Control | Keys.F:
|
||||
Menu menu = menus[iMenuKey];
|
||||
menu.FocusTextBox();
|
||||
menus[iMenuKey]?.FocusTextBox();
|
||||
break;
|
||||
case Keys.Tab:
|
||||
{
|
||||
|
@ -105,7 +113,7 @@ namespace SystemTrayMenu.Handler
|
|||
indexNew = indexMax;
|
||||
}
|
||||
|
||||
menus[indexNew].FocusTextBox();
|
||||
menus[indexNew]?.FocusTextBox();
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -124,21 +132,21 @@ namespace SystemTrayMenu.Handler
|
|||
indexNew = 0;
|
||||
}
|
||||
|
||||
menus[indexNew].FocusTextBox();
|
||||
menus[indexNew]?.FocusTextBox();
|
||||
}
|
||||
|
||||
break;
|
||||
case Keys.Apps:
|
||||
{
|
||||
DataGridView dgv = menus[iMenuKey].GetDataGridView();
|
||||
DataGridView dgv = menus[iMenuKey]?.GetDataGridView();
|
||||
|
||||
if (iRowKey > -1 &&
|
||||
dgv.Rows.Count > iRowKey)
|
||||
{
|
||||
Point pt = dgv.GetCellDisplayRectangle(2, iRowKey, false).Location;
|
||||
Point point = dgv.GetCellDisplayRectangle(2, iRowKey, false).Location;
|
||||
RowData trigger = (RowData)dgv.Rows[iRowKey].Cells[2].Value;
|
||||
MouseEventArgs mea = new MouseEventArgs(MouseButtons.Right, 1, pt.X, pt.Y, 0);
|
||||
trigger.MouseDown(dgv, mea, out bool toCloseByDoubleClick);
|
||||
MouseEventArgs mouseEventArgs = new(MouseButtons.Right, 1, point.X, point.Y, 0);
|
||||
trigger.MouseDown(dgv, mouseEventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,48 +172,30 @@ namespace SystemTrayMenu.Handler
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// While menu is open user presses a key to search for specific entries.
|
||||
/// </summary>
|
||||
/// <param name="sender">not used.</param>
|
||||
/// <param name="e">Key data of the pressed key.</param>
|
||||
internal void KeyPress(object sender, KeyPressEventArgs e)
|
||||
{
|
||||
if (char.IsLetterOrDigit(e.KeyChar) ||
|
||||
char.IsPunctuation(e.KeyChar) ||
|
||||
char.IsWhiteSpace(e.KeyChar) ||
|
||||
char.IsSeparator(e.KeyChar))
|
||||
{
|
||||
string letter = e.KeyChar.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
Menu menu = menus[iMenuKey];
|
||||
menu.KeyPressedSearch(letter);
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal void SearchTextChanging()
|
||||
public void SearchTextChanging()
|
||||
{
|
||||
ClearIsSelectedByKey();
|
||||
}
|
||||
|
||||
internal void SearchTextChanged(object sender, EventArgs e)
|
||||
public void SearchTextChanged(Menu menu, bool isSearchStringEmpty)
|
||||
{
|
||||
Menu menu = (Menu)sender;
|
||||
DataGridView dgv = menu.GetDataGridView();
|
||||
if (dgv.Rows.Count > 0)
|
||||
if (isSearchStringEmpty)
|
||||
{
|
||||
ClearIsSelectedByKey();
|
||||
}
|
||||
else if (dgv.Rows.Count > 0)
|
||||
{
|
||||
Select(dgv, 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
internal void ClearIsSelectedByKey()
|
||||
public void ClearIsSelectedByKey()
|
||||
{
|
||||
ClearIsSelectedByKey(iMenuKey, iRowKey);
|
||||
}
|
||||
|
||||
internal void Select(DataGridView dgv, int i, bool refreshview)
|
||||
public void Select(DataGridView dgv, int i, bool refreshview)
|
||||
{
|
||||
int newiMenuKey = ((Menu)dgv.TopLevelControl).Level;
|
||||
if (i != iRowKey || newiMenuKey != iMenuKey)
|
||||
|
@ -220,7 +210,11 @@ namespace SystemTrayMenu.Handler
|
|||
{
|
||||
DataGridViewRow row = dgv.Rows[i];
|
||||
RowData rowData = (RowData)row.Cells[2].Value;
|
||||
rowData.IsSelected = true;
|
||||
if (rowData != null)
|
||||
{
|
||||
rowData.IsSelected = true;
|
||||
}
|
||||
|
||||
if (refreshview)
|
||||
{
|
||||
row.Selected = false;
|
||||
|
@ -229,6 +223,11 @@ namespace SystemTrayMenu.Handler
|
|||
}
|
||||
}
|
||||
|
||||
private void Hook_KeyPressed(object sender, KeyPressedEventArgs e)
|
||||
{
|
||||
HotKeyPressed?.Invoke();
|
||||
}
|
||||
|
||||
private bool IsAnyMenuSelectedByKey(
|
||||
ref DataGridView dgv,
|
||||
ref Menu menuFromSelected,
|
||||
|
@ -292,27 +291,29 @@ namespace SystemTrayMenu.Handler
|
|||
switch (keys)
|
||||
{
|
||||
case Keys.Enter:
|
||||
if (iRowKey > -1 &&
|
||||
dgv.Rows.Count > iRowKey)
|
||||
if (iRowKey > -1 && dgv.Rows.Count > iRowKey)
|
||||
{
|
||||
RowData trigger = (RowData)dgv.Rows[iRowKey].Cells[2].Value;
|
||||
if (trigger.IsMenuOpen || !trigger.ContainsMenu)
|
||||
{
|
||||
trigger.MouseDown(
|
||||
dgv,
|
||||
null,
|
||||
out bool toCloseByMouseDown);
|
||||
trigger.MouseClick(null, out bool toCloseByMouseClick);
|
||||
trigger.DoubleClick(
|
||||
new MouseEventArgs(MouseButtons.Left, 0, 0, 0, 0),
|
||||
out bool toCloseByDoubleClick);
|
||||
if (toCloseByMouseDown || toCloseByDoubleClick)
|
||||
if (toCloseByMouseClick || toCloseByDoubleClick)
|
||||
{
|
||||
ClosePressed?.Invoke();
|
||||
}
|
||||
|
||||
if (iRowKey > -1 && dgv.Rows.Count > iRowKey)
|
||||
{
|
||||
// Raise Dgv_RowPostPaint to show ProcessStarted
|
||||
dgv.InvalidateRow(iRowKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
RowDeselected(dgvBefore, iRowBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
EnterPressed.Invoke(dgv, iRowKey);
|
||||
}
|
||||
|
@ -323,7 +324,7 @@ namespace SystemTrayMenu.Handler
|
|||
if (SelectMatchedReverse(dgv, iRowKey) ||
|
||||
SelectMatchedReverse(dgv, dgv.Rows.Count - 1))
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
RowDeselected(dgvBefore, iRowBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
}
|
||||
|
@ -333,82 +334,59 @@ namespace SystemTrayMenu.Handler
|
|||
if (SelectMatched(dgv, iRowKey) ||
|
||||
SelectMatched(dgv, 0))
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
RowDeselected(dgvBefore, iRowBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case Keys.Home:
|
||||
if (SelectMatched(dgv, 0))
|
||||
{
|
||||
RowDeselected(dgvBefore, iRowBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case Keys.End:
|
||||
if (SelectMatchedReverse(dgv, dgv.Rows.Count - 1))
|
||||
{
|
||||
RowDeselected(dgvBefore, iRowBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case Keys.Left:
|
||||
int iMenuKeyNext = iMenuKey + 1;
|
||||
if (isStillSelected)
|
||||
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;
|
||||
if (nextMenuLocationIsLeft || previousMenuLocationIsRight)
|
||||
{
|
||||
if (menuFromSelected != null &&
|
||||
menuFromSelected == menus[iMenuKeyNext])
|
||||
{
|
||||
dgv = menuFromSelected.GetDataGridView();
|
||||
if (dgv.Rows.Count > 0)
|
||||
{
|
||||
iMenuKey += 1;
|
||||
iRowKey = -1;
|
||||
if (SelectMatched(dgv, iRowKey) ||
|
||||
SelectMatched(dgv, 0))
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
SelectNextMenu(iRowBefore, ref dgv, dgvBefore, menuFromSelected, isStillSelected, ref toClear);
|
||||
}
|
||||
else
|
||||
else if (iMenuKey > 0)
|
||||
{
|
||||
iRowKey = -1;
|
||||
iMenuKey = menus.Where(m => m != null).Count() - 1;
|
||||
if (menus[iMenuKey] != null)
|
||||
{
|
||||
dgv = menus[iMenuKey].GetDataGridView();
|
||||
if (SelectMatched(dgv, iRowKey) ||
|
||||
SelectMatched(dgv, 0))
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
}
|
||||
}
|
||||
SelectPreviousMenu(iRowBefore, ref menu, ref dgv, dgvBefore, ref toClear);
|
||||
}
|
||||
|
||||
break;
|
||||
case Keys.Right:
|
||||
if (iMenuKey > 0)
|
||||
bool nextMenuLocationIsRight = menus[iMenuKey + 1]?.Location.X > menus[iMenuKey]?.Location.X;
|
||||
bool previousMenuLocationIsLeft = iMenuKey > 0 && menus[iMenuKey]?.Location.X > menus[iMenuKey - 1]?.Location.X;
|
||||
if (nextMenuLocationIsRight || previousMenuLocationIsLeft)
|
||||
{
|
||||
if (menus[iMenuKey - 1] != null)
|
||||
{
|
||||
iMenuKey -= 1;
|
||||
iRowKey = -1;
|
||||
menu = menus[iMenuKey];
|
||||
dgv = menu.GetDataGridView();
|
||||
if (SelectMatched(dgv, dgv.SelectedRows[0].Index) ||
|
||||
SelectMatched(dgv, 0))
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
}
|
||||
}
|
||||
SelectNextMenu(iRowBefore, ref dgv, dgvBefore, menuFromSelected, isStillSelected, ref toClear);
|
||||
}
|
||||
else
|
||||
else if (iMenuKey > 0)
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
iMenuKey = 0;
|
||||
iRowKey = -1;
|
||||
toClear = true;
|
||||
Cleared?.Invoke();
|
||||
SelectPreviousMenu(iRowBefore, ref menu, ref dgv, dgvBefore, ref toClear);
|
||||
}
|
||||
|
||||
break;
|
||||
case Keys.Escape:
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
case Keys.Alt | Keys.F4:
|
||||
RowDeselected(dgvBefore, iRowBefore);
|
||||
iMenuKey = 0;
|
||||
iRowKey = -1;
|
||||
toClear = true;
|
||||
|
@ -420,7 +398,7 @@ namespace SystemTrayMenu.Handler
|
|||
if (SelectMatched(dgv, iRowKey, keyInput) ||
|
||||
SelectMatched(dgv, 0, keyInput))
|
||||
{
|
||||
RowDeselected(iRowBefore, null);
|
||||
RowDeselected(null, iRowBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
}
|
||||
|
@ -430,7 +408,7 @@ namespace SystemTrayMenu.Handler
|
|||
if (SelectMatched(dgv, iRowKey, keyInput) ||
|
||||
SelectMatched(dgv, 0, keyInput))
|
||||
{
|
||||
RowDeselected(iRowBefore, null);
|
||||
RowDeselected(null, iRowBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
}
|
||||
else
|
||||
|
@ -449,6 +427,77 @@ namespace SystemTrayMenu.Handler
|
|||
}
|
||||
}
|
||||
|
||||
private void SelectPreviousMenu(int iRowBefore, ref Menu menu, ref DataGridView dgv, DataGridView dgvBefore, ref bool toClear)
|
||||
{
|
||||
if (iMenuKey > 0)
|
||||
{
|
||||
if (menus[iMenuKey - 1] != null)
|
||||
{
|
||||
iMenuKey -= 1;
|
||||
iRowKey = -1;
|
||||
menu = menus[iMenuKey];
|
||||
dgv = menu.GetDataGridView();
|
||||
if ((dgv.SelectedRows.Count > 0 &&
|
||||
SelectMatched(dgv, dgv.SelectedRows[0].Index)) ||
|
||||
SelectMatched(dgv, 0))
|
||||
{
|
||||
RowDeselected(dgvBefore, iRowBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RowDeselected(dgvBefore, iRowBefore);
|
||||
iMenuKey = 0;
|
||||
iRowKey = -1;
|
||||
toClear = true;
|
||||
Cleared?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectNextMenu(int iRowBefore, ref DataGridView dgv, DataGridView dgvBefore, Menu menuFromSelected, bool isStillSelected, ref bool toClear)
|
||||
{
|
||||
int iMenuKeyNext = iMenuKey + 1;
|
||||
if (isStillSelected)
|
||||
{
|
||||
if (menuFromSelected != null &&
|
||||
menuFromSelected == menus[iMenuKeyNext])
|
||||
{
|
||||
dgv = menuFromSelected.GetDataGridView();
|
||||
if (dgv.Rows.Count > 0)
|
||||
{
|
||||
iMenuKey += 1;
|
||||
iRowKey = -1;
|
||||
if (SelectMatched(dgv, iRowKey) ||
|
||||
SelectMatched(dgv, 0))
|
||||
{
|
||||
RowDeselected(dgvBefore, iRowBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
iRowKey = -1;
|
||||
iMenuKey = menus.Where(m => m != null).Count() - 1;
|
||||
if (menus[iMenuKey] != null)
|
||||
{
|
||||
dgv = menus[iMenuKey].GetDataGridView();
|
||||
if (SelectMatched(dgv, iRowKey) ||
|
||||
SelectMatched(dgv, 0))
|
||||
{
|
||||
RowDeselected(dgvBefore, iRowBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectRow(DataGridView dgv, int iRowKey)
|
||||
{
|
||||
InUse = true;
|
||||
|
@ -529,9 +578,13 @@ namespace SystemTrayMenu.Handler
|
|||
if (dgv.Rows.Count > rowIndex)
|
||||
{
|
||||
DataGridViewRow row = dgv.Rows[rowIndex];
|
||||
RowData rowData = (RowData)row.Cells[2].Value;
|
||||
rowData.IsSelected = false;
|
||||
row.Selected = false;
|
||||
RowData rowData = (RowData)row.Cells[2].Value;
|
||||
if (rowData != null)
|
||||
{
|
||||
rowData.IsSelected = false;
|
||||
rowData.IsClicking = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
1504
Business/Menus.cs
269
Business/MenusHelpers.cs
Normal file
|
@ -0,0 +1,269 @@
|
|||
// <copyright file="MenusHelpers.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.Business
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SystemTrayMenu.DataClasses;
|
||||
using SystemTrayMenu.Helper;
|
||||
using SystemTrayMenu.Utilities;
|
||||
|
||||
internal static class MenusHelpers
|
||||
{
|
||||
internal static void GetItemsForMainMenu(BackgroundWorker worker, string path, ref MenuData menuData)
|
||||
{
|
||||
menuData.IsNetworkRoot = FileLnk.IsNetworkRoot(path);
|
||||
if (menuData.IsNetworkRoot)
|
||||
{
|
||||
GetNetworkRootDirectories(path, ref menuData);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetDirectories(worker, path, ref menuData);
|
||||
GetFiles(worker, path, ref menuData);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void GetAddionalItemsForMainMenu(ref MenuData menuData)
|
||||
{
|
||||
if (menuData.Level != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var path in GetAddionalPathsForMainMenu())
|
||||
{
|
||||
GetDirectoriesAndFilesRecursive(ref menuData, path.Path, path.OnlyFiles, path.Recursive);
|
||||
}
|
||||
}
|
||||
|
||||
internal static IEnumerable<(string Path, bool Recursive, bool OnlyFiles)> GetAddionalPathsForMainMenu()
|
||||
{
|
||||
foreach (string pathAndRecursivString in Properties.Settings.Default.PathsAddToMainMenu.Split(@"|"))
|
||||
{
|
||||
if (string.IsNullOrEmpty(pathAndRecursivString))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string pathAddForMainMenu = pathAndRecursivString.Split("recursiv:")[0].Trim();
|
||||
bool recursive = pathAndRecursivString.Split("recursiv:")[1].StartsWith("True");
|
||||
bool onlyFiles = pathAndRecursivString.Split("onlyFiles:")[1].StartsWith("True");
|
||||
yield return (Path: pathAddForMainMenu, Recursive: recursive, OnlyFiles: onlyFiles);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ReadHiddenAndReadIcons(BackgroundWorker worker, ref MenuData menuData)
|
||||
{
|
||||
List<RowData> rowDatasToRemove = new();
|
||||
foreach (RowData rowData in menuData.RowDatas)
|
||||
{
|
||||
if (worker?.CancellationPending == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!menuData.IsNetworkRoot && FolderOptions.IsHidden(rowData))
|
||||
{
|
||||
rowDatasToRemove.Add(rowData);
|
||||
continue;
|
||||
}
|
||||
|
||||
rowData.ReadIcon(true);
|
||||
}
|
||||
|
||||
menuData.RowDatas = menuData.RowDatas.Except(rowDatasToRemove).ToList();
|
||||
}
|
||||
|
||||
internal static void CheckIfValid(ref MenuData menuData)
|
||||
{
|
||||
if (menuData.Validity == MenuDataValidity.Undefined)
|
||||
{
|
||||
if (menuData.RowDatas.Count == 0)
|
||||
{
|
||||
menuData.Validity = MenuDataValidity.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
menuData.Validity = MenuDataValidity.Valid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void SortItemsWhenValid(ref MenuData menuData)
|
||||
{
|
||||
if (menuData.Validity != MenuDataValidity.Valid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
menuData.RowDatas = SortItems(menuData.RowDatas);
|
||||
}
|
||||
|
||||
internal static List<RowData> SortItems(List<RowData> rowDatas)
|
||||
{
|
||||
if (Properties.Settings.Default.SortByTypeAndNameWindowsExplorerSort)
|
||||
{
|
||||
rowDatas = rowDatas.OrderByDescending(x => x.IsFolder)
|
||||
.ThenBy(x => x.Text, new WindowsExplorerSort()).ToList();
|
||||
}
|
||||
else if (Properties.Settings.Default.SortByTypeAndDate)
|
||||
{
|
||||
rowDatas = rowDatas.OrderByDescending(x => x.IsFolder)
|
||||
.ThenByDescending(x => x.FileInfo.LastWriteTime).ToList();
|
||||
}
|
||||
else if (Properties.Settings.Default.SortByFileExtensionAndName)
|
||||
{
|
||||
rowDatas = rowDatas.OrderBy(x => x.FileExtension).ThenBy(x => x.Text).ToList();
|
||||
}
|
||||
else if (Properties.Settings.Default.SortByName)
|
||||
{
|
||||
rowDatas = rowDatas.OrderBy(x => x.Text).ToList();
|
||||
}
|
||||
else if (Properties.Settings.Default.SortByDate)
|
||||
{
|
||||
rowDatas = rowDatas.OrderByDescending(x => x.FileInfo.LastWriteTime).ToList();
|
||||
}
|
||||
|
||||
return rowDatas;
|
||||
}
|
||||
|
||||
private static void GetNetworkRootDirectories(string path, ref MenuData menuData)
|
||||
{
|
||||
Process cmd = new();
|
||||
cmd.StartInfo.FileName = "cmd.exe";
|
||||
cmd.StartInfo.RedirectStandardInput = true;
|
||||
cmd.StartInfo.RedirectStandardOutput = true;
|
||||
cmd.StartInfo.CreateNoWindow = true;
|
||||
cmd.StartInfo.UseShellExecute = false;
|
||||
cmd.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
|
||||
try
|
||||
{
|
||||
bool resolvedSomething = false;
|
||||
cmd.Start();
|
||||
cmd.StandardInput.WriteLine($"net view {path}");
|
||||
cmd.StandardInput.Flush();
|
||||
cmd.StandardInput.Close();
|
||||
string output = cmd.StandardOutput.ReadToEnd();
|
||||
cmd.WaitForExit();
|
||||
cmd.Close();
|
||||
List<string> lines = output
|
||||
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
if (lines.Count > 8)
|
||||
{
|
||||
foreach (string line in lines.Skip(6).SkipLast(2))
|
||||
{
|
||||
int indexOfFirstSpace = line.IndexOf(" ", StringComparison.InvariantCulture);
|
||||
if (indexOfFirstSpace > 0)
|
||||
{
|
||||
string directory = Path.Combine(path, line[..indexOfFirstSpace]);
|
||||
menuData.RowDatas.Add(new RowData(true, false, true, menuData.Level, directory));
|
||||
resolvedSomething = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!resolvedSomething)
|
||||
{
|
||||
Log.Info($"Could not resolve network root folder: {path} , output:{output}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"path:'{path}'", ex);
|
||||
if (ex is UnauthorizedAccessException)
|
||||
{
|
||||
menuData.Validity = MenuDataValidity.NoAccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetDirectories(BackgroundWorker worker, string path, ref MenuData menuData)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var directory in Directory.GetDirectories(path))
|
||||
{
|
||||
if (worker?.CancellationPending == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
menuData.RowDatas.Add(new RowData(true, false, false, menuData.Level, directory));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"path:'{path}'", ex);
|
||||
if (ex is UnauthorizedAccessException)
|
||||
{
|
||||
menuData.Validity = MenuDataValidity.NoAccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetFiles(BackgroundWorker worker, string path, ref MenuData menuData)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (string file in DirectoryBySearchPattern.GetFiles(path, Config.SearchPattern))
|
||||
{
|
||||
if (worker?.CancellationPending == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
menuData.RowDatas.Add(new RowData(false, false, false, menuData.Level, file));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"path:'{path}'", ex);
|
||||
if (ex is UnauthorizedAccessException)
|
||||
{
|
||||
menuData.Validity = MenuDataValidity.NoAccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetDirectoriesAndFilesRecursive(
|
||||
ref MenuData menuData,
|
||||
string path,
|
||||
bool onlyFiles,
|
||||
bool recursiv)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (string file in DirectoryBySearchPattern.GetFiles(path, Config.SearchPattern))
|
||||
{
|
||||
menuData.RowDatas.Add(new RowData(false, true, false, menuData.Level, file));
|
||||
}
|
||||
|
||||
foreach (string directory in Directory.GetDirectories(path))
|
||||
{
|
||||
if (!onlyFiles)
|
||||
{
|
||||
menuData.RowDatas.Add(new RowData(true, true, false, menuData.Level, directory));
|
||||
}
|
||||
|
||||
if (recursiv)
|
||||
{
|
||||
GetDirectoriesAndFilesRecursive(ref menuData, directory, onlyFiles, recursiv);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"GetDirectoriesAndFilesRecursive path:'{path}'", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ namespace SystemTrayMenu
|
|||
{
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.Utilities;
|
||||
|
||||
|
@ -15,25 +14,22 @@ namespace SystemTrayMenu
|
|||
private static bool isStartup = true;
|
||||
|
||||
[STAThread]
|
||||
private static void Main()
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.Initialize();
|
||||
SingleAppInstance.Initialize();
|
||||
Translator.Initialize();
|
||||
Config.SetFolderByWindowsContextMenu(args);
|
||||
Config.LoadOrSetByUser();
|
||||
Config.Initialize();
|
||||
PrivilegeChecker.Initialize();
|
||||
|
||||
Config.UpgradeIfNotUpgraded();
|
||||
if (Config.LoadOrSetByUser())
|
||||
if (SingleAppInstance.Initialize())
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.ThreadException += ThreadException;
|
||||
static void ThreadException(object s, ThreadExceptionEventArgs t)
|
||||
{
|
||||
AskUserSendError(t.Exception);
|
||||
}
|
||||
|
||||
Application.ThreadException += Application_ThreadException;
|
||||
Scaling.Initialize();
|
||||
FolderOptions.Initialize();
|
||||
|
||||
|
@ -44,10 +40,11 @@ namespace SystemTrayMenu
|
|||
Application.Run();
|
||||
}
|
||||
}
|
||||
|
||||
Application.ThreadException -= Application_ThreadException;
|
||||
Config.Dispose();
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch (Exception ex)
|
||||
#pragma warning restore CA1031 // => Represents ThreadException during attached to process
|
||||
{
|
||||
AskUserSendError(ex);
|
||||
}
|
||||
|
@ -55,28 +52,37 @@ namespace SystemTrayMenu
|
|||
{
|
||||
Log.Close();
|
||||
}
|
||||
}
|
||||
|
||||
static void AskUserSendError(Exception ex)
|
||||
private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
|
||||
{
|
||||
AskUserSendError(e.Exception);
|
||||
}
|
||||
|
||||
private static void AskUserSendError(Exception ex)
|
||||
{
|
||||
Log.Error("Application Crashed", ex);
|
||||
|
||||
DialogResult dialogResult = MessageBox.Show(
|
||||
"A problem has been encountered and the application needs to restart. " +
|
||||
"Reporting this error will help us make our product better. " +
|
||||
"Press 'Yes' to open your standard email app (emailto: Markus@Hofknecht.eu). " + Environment.NewLine +
|
||||
@"You can also create an issue manually here https://github.com/Hofknecht/SystemTrayMenu/issues" + Environment.NewLine +
|
||||
"Press 'Cancel' to quit SystemTrayMenu.",
|
||||
"SystemTrayMenu Crashed",
|
||||
MessageBoxButtons.YesNoCancel);
|
||||
|
||||
if (dialogResult == DialogResult.Yes)
|
||||
{
|
||||
Log.Error("Application Crashed", ex);
|
||||
Log.ProcessStart("mailto:" + "markus@hofknecht.eu" +
|
||||
"?subject=SystemTrayMenu Bug reported " +
|
||||
Assembly.GetEntryAssembly().GetName().Version +
|
||||
"&body=" + ex.ToString());
|
||||
}
|
||||
|
||||
if (MessageBox.Show(
|
||||
"A problem has been encountered and the application needs to restart. " +
|
||||
"Reporting this error will help us make our product better. " +
|
||||
"Press yes to open your standard email app.",
|
||||
"SystemTrayMenu BugSplat",
|
||||
MessageBoxButtons.YesNo) == DialogResult.Yes)
|
||||
{
|
||||
Log.ProcessStart("mailto:" + "markus@hofknecht.eu" +
|
||||
"?subject=SystemTrayMenu Bug reported " +
|
||||
Assembly.GetEntryAssembly().GetName().Version +
|
||||
"&body=" + ex.ToString());
|
||||
}
|
||||
|
||||
if (!isStartup)
|
||||
{
|
||||
AppRestart.ByThreadException();
|
||||
}
|
||||
if (!isStartup && dialogResult != DialogResult.Cancel)
|
||||
{
|
||||
AppRestart.ByThreadException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace SystemTrayMenu.Handler
|
|||
|
||||
internal class WaitLeave : IDisposable
|
||||
{
|
||||
private readonly Timer timerLeaveCheck = new Timer();
|
||||
private readonly Timer timerLeaveCheck = new();
|
||||
|
||||
public WaitLeave(int timeUntilTriggered)
|
||||
{
|
||||
|
@ -18,10 +18,11 @@ namespace SystemTrayMenu.Handler
|
|||
timerLeaveCheck.Tick += TimerLeaveCheckTick;
|
||||
}
|
||||
|
||||
public event EventHandlerEmpty LeaveTriggered;
|
||||
public event Action LeaveTriggered;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
timerLeaveCheck.Tick -= TimerLeaveCheckTick;
|
||||
timerLeaveCheck.Dispose();
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace SystemTrayMenu.Handler
|
|||
|
||||
internal class WaitToLoadMenu : IDisposable
|
||||
{
|
||||
private readonly Timer timerStartLoad = new Timer();
|
||||
private readonly Timer timerStartLoad = new();
|
||||
private DataGridView dgv;
|
||||
private int rowIndex;
|
||||
private DataGridView dgvTmp;
|
||||
|
@ -33,7 +33,7 @@ namespace SystemTrayMenu.Handler
|
|||
|
||||
internal event Action<int> CloseMenu;
|
||||
|
||||
internal event EventHandlerEmpty StopLoadMenu;
|
||||
internal event Action StopLoadMenu;
|
||||
|
||||
internal event Action<DataGridView, int> MouseEnterOk;
|
||||
|
||||
|
@ -41,6 +41,7 @@ namespace SystemTrayMenu.Handler
|
|||
|
||||
public void Dispose()
|
||||
{
|
||||
timerStartLoad.Tick -= WaitStartLoad_Tick;
|
||||
timerStartLoad.Stop();
|
||||
timerStartLoad.Dispose();
|
||||
dgv?.Dispose();
|
||||
|
@ -92,7 +93,7 @@ namespace SystemTrayMenu.Handler
|
|||
}
|
||||
}
|
||||
|
||||
internal void RowDeselected(int rowIndex, DataGridView dgv)
|
||||
internal void RowDeselected(DataGridView dgv, int rowIndex)
|
||||
{
|
||||
timerStartLoad.Stop();
|
||||
StopLoadMenu?.Invoke();
|
||||
|
@ -129,7 +130,7 @@ namespace SystemTrayMenu.Handler
|
|||
{
|
||||
if (!MouseActive)
|
||||
{
|
||||
if (mouseMoveEvents > 3)
|
||||
if (mouseMoveEvents > 6)
|
||||
{
|
||||
MouseActive = true;
|
||||
if (dgvTmp != null && !dgvTmp.IsDisposed)
|
||||
|
@ -168,19 +169,17 @@ namespace SystemTrayMenu.Handler
|
|||
{
|
||||
RowData rowData = (RowData)dgv.Rows[rowIndex].Cells[2].Value;
|
||||
Menu menu = (Menu)dgv.FindForm();
|
||||
rowData.MenuLevel = menu.Level;
|
||||
rowData.Level = menu.Level;
|
||||
if (rowData.ContainsMenu)
|
||||
{
|
||||
CloseMenu.Invoke(rowData.MenuLevel + 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
CloseMenu.Invoke(rowData.MenuLevel + 1);
|
||||
CloseMenu.Invoke(rowData.Level + 2);
|
||||
}
|
||||
|
||||
CloseMenu.Invoke(rowData.Level + 1);
|
||||
|
||||
if (!rowData.IsContextMenuOpen &&
|
||||
rowData.ContainsMenu &&
|
||||
rowData.MenuLevel + 1 < MenuDefines.MenusMax)
|
||||
rowData.Level + 1 < MenuDefines.MenusMax)
|
||||
{
|
||||
StartLoadMenu.Invoke(rowData);
|
||||
}
|
||||
|
@ -193,7 +192,11 @@ namespace SystemTrayMenu.Handler
|
|||
this.dgv = dgv;
|
||||
this.rowIndex = rowIndex;
|
||||
RowData rowData = (RowData)dgv.Rows[rowIndex].Cells[2].Value;
|
||||
rowData.IsSelected = true;
|
||||
if (rowData != null)
|
||||
{
|
||||
rowData.IsSelected = true;
|
||||
}
|
||||
|
||||
dgv.Rows[rowIndex].Selected = false;
|
||||
dgv.Rows[rowIndex].Selected = true;
|
||||
}
|
||||
|
@ -203,10 +206,14 @@ namespace SystemTrayMenu.Handler
|
|||
if (dgv != null && dgv.Rows.Count > rowIndex)
|
||||
{
|
||||
RowData rowData = (RowData)dgv.Rows[rowIndex].Cells[2].Value;
|
||||
rowData.IsSelected = false;
|
||||
dgv.Rows[rowIndex].Selected = false;
|
||||
this.dgv = null;
|
||||
this.rowIndex = 0;
|
||||
if (rowData != null)
|
||||
{
|
||||
rowData.IsSelected = false;
|
||||
rowData.IsClicking = false;
|
||||
dgv.Rows[rowIndex].Selected = false;
|
||||
this.dgv = null;
|
||||
this.rowIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,21 +8,88 @@ namespace SystemTrayMenu
|
|||
|
||||
internal static class AppColors
|
||||
{
|
||||
internal static readonly Color Blue = Color.FromArgb(204, 232, 255);
|
||||
internal static readonly Color DarkModeBlue = Color.FromArgb(51, 51, 51);
|
||||
internal static readonly Color BlueBorder = Color.FromArgb(153, 209, 255);
|
||||
internal static readonly Color DarkModeBlueBorder = Color.FromArgb(20, 29, 75);
|
||||
internal static readonly Color Green = Color.FromArgb(194, 245, 222);
|
||||
internal static readonly Color DarkModeGreen = Color.FromArgb(20, 65, 42);
|
||||
internal static readonly Color GreenBorder = Color.FromArgb(153, 255, 165);
|
||||
internal static readonly Color DarkModeGreenBorder = Color.FromArgb(20, 75, 85);
|
||||
internal static readonly Color Red = Color.FromArgb(255, 204, 232);
|
||||
internal static readonly Color DarkModeRed = Color.FromArgb(75, 24, 52);
|
||||
internal static readonly Color Azure = Color.Azure;
|
||||
internal static readonly Color DarkModeAzure = DarkModeBackColor1;
|
||||
public static Color Arrow { get; internal set; }
|
||||
|
||||
internal static readonly Color DarkModeBackColor3 = Color.FromArgb(25, 25, 25);
|
||||
internal static readonly Color DarkModeBackColor2 = Color.FromArgb(32, 32, 32);
|
||||
internal static readonly Color DarkModeBackColor1 = Color.FromArgb(43, 43, 43);
|
||||
public static Color ArrowHoverBackground { get; internal set; }
|
||||
|
||||
public static Color ArrowHover { get; internal set; }
|
||||
|
||||
public static Color ArrowClick { get; internal set; }
|
||||
|
||||
public static Color ArrowClickBackground { get; internal set; }
|
||||
|
||||
public static Color SliderArrowsAndTrackHover { get; internal set; }
|
||||
|
||||
public static Color Slider { get; internal set; }
|
||||
|
||||
public static Color SliderHover { get; internal set; }
|
||||
|
||||
public static Color SliderDragging { get; internal set; }
|
||||
|
||||
public static Color ScrollbarBackground { get; internal set; }
|
||||
|
||||
public static Color ArrowDarkMode { get; internal set; }
|
||||
|
||||
public static Color ArrowHoverBackgroundDarkMode { get; internal set; }
|
||||
|
||||
public static Color ArrowHoverDarkMode { get; internal set; }
|
||||
|
||||
public static Color ArrowClickDarkMode { get; internal set; }
|
||||
|
||||
public static Color ArrowClickBackgroundDarkMode { get; internal set; }
|
||||
|
||||
public static Color SliderArrowsAndTrackHoverDarkMode { get; internal set; }
|
||||
|
||||
public static Color SliderDarkMode { get; internal set; }
|
||||
|
||||
public static Color SliderHoverDarkMode { get; internal set; }
|
||||
|
||||
public static Color SliderDraggingDarkMode { get; internal set; }
|
||||
|
||||
public static Color ScrollbarBackgroundDarkMode { get; internal set; }
|
||||
|
||||
public static Color SelectedItem { get; set; }
|
||||
|
||||
public static Color DarkModeSelecetedItem { get; set; }
|
||||
|
||||
public static Color SelectedItemBorder { get; set; }
|
||||
|
||||
public static Color DarkModeSelectedItemBorder { get; set; }
|
||||
|
||||
public static Color OpenFolder { get; set; }
|
||||
|
||||
public static Color DarkModeOpenFolder { get; set; }
|
||||
|
||||
public static Color OpenFolderBorder { get; set; }
|
||||
|
||||
public static Color DarkModeOpenFolderBorder { get; set; }
|
||||
|
||||
public static Color Background { get; set; }
|
||||
|
||||
public static Color DarkModeBackground { get; set; }
|
||||
|
||||
public static Color BackgroundBorder { get; set; }
|
||||
|
||||
public static Color DarkModeBackgroundBorder { get; set; }
|
||||
|
||||
public static Color SearchField { get; set; }
|
||||
|
||||
public static Color DarkModeSearchField { get; set; }
|
||||
|
||||
public static Bitmap BitmapOpenFolder { get; set; }
|
||||
|
||||
public static Bitmap BitmapPin { get; set; }
|
||||
|
||||
public static Bitmap BitmapSettings { get; set; }
|
||||
|
||||
public static Bitmap BitmapRestart { get; set; }
|
||||
|
||||
public static Bitmap BitmapPinActive { get; set; }
|
||||
|
||||
public static Bitmap BitmapSearch { get; set; }
|
||||
|
||||
public static Color Icons { get; set; }
|
||||
|
||||
public static Color DarkModeIcons { get; set; }
|
||||
}
|
||||
}
|
15
Config/ColorAndCode.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
// <copyright file="ColorAndCode.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu
|
||||
{
|
||||
using System.Drawing;
|
||||
|
||||
internal struct ColorAndCode
|
||||
{
|
||||
public Color Color { get; set; }
|
||||
|
||||
public string HtmlColorCode { get; set; }
|
||||
}
|
||||
}
|
470
Config/Config.cs
|
@ -5,117 +5,146 @@
|
|||
namespace SystemTrayMenu
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Win32;
|
||||
using Svg;
|
||||
using SystemTrayMenu.Properties;
|
||||
using SystemTrayMenu.UserInterface.FolderBrowseDialog;
|
||||
using SystemTrayMenu.Utilities;
|
||||
using static SystemTrayMenu.Utilities.IconReader;
|
||||
|
||||
public static class Config
|
||||
{
|
||||
private static readonly Icon SystemTrayMenu = new Icon(Properties.Resources.SystemTrayMenu, SystemInformation.SmallIconSize);
|
||||
private static readonly Icon IconRootFolder = GetIconSTA(Path, Path, false, IconSize.Small, true);
|
||||
|
||||
private static bool readDarkModeDone;
|
||||
private static bool isDarkMode;
|
||||
private static bool readHideFileExtdone;
|
||||
private static bool isHideFileExtension;
|
||||
|
||||
public static bool IsHideFileExtdone => IsHideFileExtension();
|
||||
public static string Path => Settings.Default.PathDirectory;
|
||||
|
||||
public static string Path => Properties.Settings.Default.PathDirectory;
|
||||
public static string SearchPattern => Settings.Default.SearchPattern;
|
||||
|
||||
public static void UpgradeIfNotUpgraded()
|
||||
public static bool ShowDirectoryTitleAtTop => Settings.Default.ShowDirectoryTitleAtTop;
|
||||
|
||||
public static bool ShowSearchBar => Settings.Default.ShowSearchBar;
|
||||
|
||||
public static bool ShowCountOfElementsBelow => Settings.Default.ShowCountOfElementsBelow;
|
||||
|
||||
public static bool ShowFunctionKeyOpenFolder => Settings.Default.ShowFunctionKeyOpenFolder;
|
||||
|
||||
public static bool ShowFunctionKeyPinMenu => Settings.Default.ShowFunctionKeyPinMenu;
|
||||
|
||||
public static bool ShowFunctionKeySettings => Settings.Default.ShowFunctionKeySettings;
|
||||
|
||||
public static bool ShowFunctionKeyRestart => Settings.Default.ShowFunctionKeyRestart;
|
||||
|
||||
public static bool AlwaysOpenByPin { get; internal set; }
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
if (!Properties.Settings.Default.IsUpgraded)
|
||||
UpgradeIfNotUpgraded();
|
||||
InitializeColors();
|
||||
if (string.IsNullOrEmpty(Settings.Default.PathIcoDirectory))
|
||||
{
|
||||
Properties.Settings.Default.Upgrade();
|
||||
Properties.Settings.Default.IsUpgraded = true;
|
||||
Properties.Settings.Default.Save();
|
||||
|
||||
FileVersionInfo versionInfo = FileVersionInfo.
|
||||
GetVersionInfo(Assembly.GetEntryAssembly().Location);
|
||||
string upgradedFromPath = $"%localappdata%\\{versionInfo.CompanyName}\\";
|
||||
try
|
||||
Settings.Default.PathIcoDirectory = System.IO.Path.Combine(
|
||||
System.IO.Path.Combine(
|
||||
Environment.GetFolderPath(
|
||||
Environment.SpecialFolder.ApplicationData), $"SystemTrayMenu"), "ico");
|
||||
if (!Directory.Exists(Settings.Default.PathIcoDirectory))
|
||||
{
|
||||
upgradedFromPath = System.IO.Path.GetFullPath(upgradedFromPath);
|
||||
Directory.CreateDirectory(Settings.Default.PathIcoDirectory);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is ArgumentException ||
|
||||
ex is System.Security.SecurityException ||
|
||||
ex is NotSupportedException ||
|
||||
ex is PathTooLongException)
|
||||
{
|
||||
Log.Warn($"Resolve path {upgradedFromPath} failed", ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
Log.Info($"Settings upgraded from {upgradedFromPath}");
|
||||
}
|
||||
}
|
||||
|
||||
public static bool LoadOrSetByUser()
|
||||
public static void Dispose()
|
||||
{
|
||||
bool pathOK = Directory.Exists(Path);
|
||||
AppColors.BitmapOpenFolder.Dispose();
|
||||
AppColors.BitmapPin.Dispose();
|
||||
AppColors.BitmapPinActive.Dispose();
|
||||
AppColors.BitmapSettings.Dispose();
|
||||
AppColors.BitmapRestart.Dispose();
|
||||
AppColors.BitmapSearch.Dispose();
|
||||
}
|
||||
|
||||
if (!pathOK)
|
||||
public static Icon GetAppIcon()
|
||||
{
|
||||
if (Settings.Default.UseIconFromRootFolder)
|
||||
{
|
||||
string textFirstStart = Translator.GetText("TextFirstStart");
|
||||
return IconRootFolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SystemTrayMenu;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetFolderByWindowsContextMenu(string[] args)
|
||||
{
|
||||
if (args != null && args.Length > 0 && args[0] != "-r")
|
||||
{
|
||||
string path = args[0];
|
||||
Log.Info($"SetFolderByWindowsContextMenu() path: {path}");
|
||||
Settings.Default.PathDirectory = path;
|
||||
Settings.Default.Save();
|
||||
}
|
||||
}
|
||||
|
||||
public static void LoadOrSetByUser()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Path))
|
||||
{
|
||||
string textFirstStart = Translator.GetText(
|
||||
"Read the FAQ and then choose a root directory for SystemTrayMenu.");
|
||||
MessageBox.Show(
|
||||
textFirstStart,
|
||||
Translator.GetText("SystemTrayMenu"),
|
||||
"SystemTrayMenu",
|
||||
MessageBoxButtons.OK);
|
||||
ShowHelpFAQ();
|
||||
pathOK = SetFolderByUser();
|
||||
SetFolderByUser();
|
||||
}
|
||||
|
||||
return pathOK;
|
||||
}
|
||||
|
||||
public static bool SetFolderByUser(bool save = true)
|
||||
public static void SetFolderByUser(bool save = true)
|
||||
{
|
||||
bool pathOK = false;
|
||||
bool userAborted = false;
|
||||
using (FolderDialog dialog = new FolderDialog())
|
||||
using FolderDialog dialog = new();
|
||||
dialog.InitialFolder = Path;
|
||||
|
||||
if (dialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
dialog.InitialFolder = Path;
|
||||
|
||||
do
|
||||
Settings.Default.PathDirectory = dialog.Folder;
|
||||
if (save)
|
||||
{
|
||||
if (dialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
if (Directory.Exists(dialog.Folder))
|
||||
{
|
||||
pathOK = true;
|
||||
Properties.Settings.Default.PathDirectory =
|
||||
dialog.Folder;
|
||||
if (save)
|
||||
{
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
userAborted = true;
|
||||
}
|
||||
Settings.Default.Save();
|
||||
}
|
||||
while (!pathOK && !userAborted);
|
||||
}
|
||||
}
|
||||
|
||||
return pathOK;
|
||||
public static void SetFolderIcoByUser()
|
||||
{
|
||||
using FolderDialog dialog = new();
|
||||
dialog.InitialFolder = Settings.Default.PathIcoDirectory;
|
||||
|
||||
if (dialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
Settings.Default.PathIcoDirectory = dialog.Folder;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ShowHelpFAQ()
|
||||
{
|
||||
if (FileUrl.GetDefaultBrowserPath(out string browserPath))
|
||||
{
|
||||
Process.Start(browserPath, "https://github.com/Hofknecht/SystemTrayMenu#FAQ");
|
||||
}
|
||||
Log.ProcessStart("https://github.com/Hofknecht/SystemTrayMenu#FAQ");
|
||||
}
|
||||
|
||||
internal static void ShowSupportSystemTrayMenu()
|
||||
{
|
||||
Log.ProcessStart("https://github.com/Hofknecht/SystemTrayMenu#donations");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -127,7 +156,7 @@ namespace SystemTrayMenu
|
|||
if (!readDarkModeDone)
|
||||
{
|
||||
// 0 = Dark mode, 1 = Light mode
|
||||
if (Properties.Settings.Default.IsDarkModeAlwaysOn ||
|
||||
if (Settings.Default.IsDarkModeAlwaysOn ||
|
||||
IsRegistryValueThisValue(
|
||||
@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize",
|
||||
"AppsUseLightTheme",
|
||||
|
@ -142,10 +171,16 @@ namespace SystemTrayMenu
|
|||
return isDarkMode;
|
||||
}
|
||||
|
||||
internal static void ResetReadDarkModeDone()
|
||||
{
|
||||
isDarkMode = false;
|
||||
readDarkModeDone = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the OS setting whether HideFileExt enabled.
|
||||
/// </summary>
|
||||
/// <returns>true = Dark mode; false = Light mode.</returns>
|
||||
/// <returns>isHideFileExtension.</returns>
|
||||
internal static bool IsHideFileExtension()
|
||||
{
|
||||
if (!readHideFileExtdone)
|
||||
|
@ -165,6 +200,269 @@ namespace SystemTrayMenu
|
|||
return isHideFileExtension;
|
||||
}
|
||||
|
||||
internal static void InitializeColors(bool save = true)
|
||||
{
|
||||
ColorConverter converter = new();
|
||||
ColorAndCode colorAndCode = default;
|
||||
bool changed = false;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorSelectedItem;
|
||||
colorAndCode.Color = Color.FromArgb(204, 232, 255);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorSelectedItem = colorAndCode.HtmlColorCode;
|
||||
AppColors.SelectedItem = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorDarkModeSelecetedItem;
|
||||
colorAndCode.Color = Color.FromArgb(51, 51, 51);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorDarkModeSelecetedItem = colorAndCode.HtmlColorCode;
|
||||
AppColors.DarkModeSelecetedItem = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorSelectedItemBorder;
|
||||
colorAndCode.Color = Color.FromArgb(153, 209, 255);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorSelectedItemBorder = colorAndCode.HtmlColorCode;
|
||||
AppColors.SelectedItemBorder = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorDarkModeSelectedItemBorder;
|
||||
colorAndCode.Color = Color.FromArgb(20, 29, 75);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorDarkModeSelectedItemBorder = colorAndCode.HtmlColorCode;
|
||||
AppColors.DarkModeSelectedItemBorder = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorOpenFolder;
|
||||
colorAndCode.Color = Color.FromArgb(194, 245, 222);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorOpenFolder = colorAndCode.HtmlColorCode;
|
||||
AppColors.OpenFolder = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorDarkModeOpenFolder;
|
||||
colorAndCode.Color = Color.FromArgb(20, 65, 42);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorDarkModeOpenFolder = colorAndCode.HtmlColorCode;
|
||||
AppColors.DarkModeOpenFolder = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorOpenFolderBorder;
|
||||
colorAndCode.Color = Color.FromArgb(153, 255, 165);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorOpenFolderBorder = colorAndCode.HtmlColorCode;
|
||||
AppColors.OpenFolderBorder = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorDarkModeOpenFolderBorder;
|
||||
colorAndCode.Color = Color.FromArgb(20, 75, 85);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorDarkModeOpenFolderBorder = colorAndCode.HtmlColorCode;
|
||||
AppColors.DarkModeOpenFolderBorder = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorIcons;
|
||||
colorAndCode.Color = Color.FromArgb(149, 160, 166);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorIcons = colorAndCode.HtmlColorCode;
|
||||
AppColors.Icons = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorDarkModeIcons;
|
||||
colorAndCode.Color = Color.FromArgb(149, 160, 166);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorDarkModeIcons = colorAndCode.HtmlColorCode;
|
||||
AppColors.DarkModeIcons = colorAndCode.Color;
|
||||
|
||||
string htmlColorCodeIcons;
|
||||
if (IsDarkMode())
|
||||
{
|
||||
htmlColorCodeIcons = Settings.Default.ColorDarkModeIcons;
|
||||
}
|
||||
else
|
||||
{
|
||||
htmlColorCodeIcons = Settings.Default.ColorIcons;
|
||||
}
|
||||
|
||||
AppColors.BitmapOpenFolder =
|
||||
ReadSvg(Properties.Resources.ic_fluent_folder_arrow_right_48_regular, htmlColorCodeIcons);
|
||||
AppColors.BitmapPin =
|
||||
ReadSvg(Properties.Resources.ic_fluent_pin_48_regular, htmlColorCodeIcons);
|
||||
AppColors.BitmapSettings =
|
||||
ReadSvg(Properties.Resources.ic_fluent_settings_28_regular, htmlColorCodeIcons);
|
||||
AppColors.BitmapRestart =
|
||||
ReadSvg(Properties.Resources.ic_fluent_arrow_sync_24_regular, htmlColorCodeIcons);
|
||||
AppColors.BitmapPinActive =
|
||||
ReadSvg(Properties.Resources.ic_fluent_pin_48_filled, htmlColorCodeIcons);
|
||||
AppColors.BitmapSearch =
|
||||
ReadSvg(Properties.Resources.ic_fluent_search_48_regular, htmlColorCodeIcons);
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorSearchField;
|
||||
colorAndCode.Color = Color.FromArgb(255, 255, 255);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorSearchField = colorAndCode.HtmlColorCode;
|
||||
AppColors.SearchField = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorDarkModeSearchField;
|
||||
colorAndCode.Color = Color.FromArgb(25, 25, 25);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorDarkModeSearchField = colorAndCode.HtmlColorCode;
|
||||
AppColors.DarkModeSearchField = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorBackground;
|
||||
colorAndCode.Color = Color.FromArgb(255, 255, 255);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorBackground = colorAndCode.HtmlColorCode;
|
||||
AppColors.Background = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorDarkModeBackground;
|
||||
colorAndCode.Color = Color.FromArgb(32, 32, 32);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorDarkModeBackground = colorAndCode.HtmlColorCode;
|
||||
AppColors.DarkModeBackground = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorBackgroundBorder;
|
||||
colorAndCode.Color = Color.FromArgb(0, 0, 0);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorBackgroundBorder = colorAndCode.HtmlColorCode;
|
||||
AppColors.BackgroundBorder = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorDarkModeBackgroundBorder;
|
||||
colorAndCode.Color = Color.FromArgb(0, 0, 0);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorDarkModeBackgroundBorder = colorAndCode.HtmlColorCode;
|
||||
AppColors.DarkModeBackgroundBorder = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorArrow;
|
||||
colorAndCode.Color = Color.FromArgb(96, 96, 96);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorArrow = colorAndCode.HtmlColorCode;
|
||||
AppColors.Arrow = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorArrowHoverBackground;
|
||||
colorAndCode.Color = Color.FromArgb(218, 218, 218);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorArrowHoverBackground = colorAndCode.HtmlColorCode;
|
||||
AppColors.ArrowHoverBackground = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorArrowHover;
|
||||
colorAndCode.Color = Color.FromArgb(0, 0, 0);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorArrowHover = colorAndCode.HtmlColorCode;
|
||||
AppColors.ArrowHover = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorArrowClick;
|
||||
colorAndCode.Color = Color.FromArgb(255, 255, 255);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorArrowClick = colorAndCode.HtmlColorCode;
|
||||
AppColors.ArrowClick = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorArrowClickBackground;
|
||||
colorAndCode.Color = Color.FromArgb(96, 96, 96);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorArrowClickBackground = colorAndCode.HtmlColorCode;
|
||||
AppColors.ArrowClickBackground = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorSliderArrowsAndTrackHover;
|
||||
colorAndCode.Color = Color.FromArgb(192, 192, 192);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorSliderArrowsAndTrackHover = colorAndCode.HtmlColorCode;
|
||||
AppColors.SliderArrowsAndTrackHover = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorSlider;
|
||||
colorAndCode.Color = Color.FromArgb(205, 205, 205);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorSlider = colorAndCode.HtmlColorCode;
|
||||
AppColors.Slider = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorSliderHover;
|
||||
colorAndCode.Color = Color.FromArgb(166, 166, 166);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorSliderHover = colorAndCode.HtmlColorCode;
|
||||
AppColors.SliderHover = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorSliderDragging;
|
||||
colorAndCode.Color = Color.FromArgb(96, 96, 96);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorSliderDragging = colorAndCode.HtmlColorCode;
|
||||
AppColors.SliderDragging = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorScrollbarBackground;
|
||||
colorAndCode.Color = Color.FromArgb(240, 240, 240);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorScrollbarBackground = colorAndCode.HtmlColorCode;
|
||||
AppColors.ScrollbarBackground = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorArrowDarkMode;
|
||||
colorAndCode.Color = Color.FromArgb(103, 103, 103);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorArrowDarkMode = colorAndCode.HtmlColorCode;
|
||||
AppColors.ArrowDarkMode = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorArrowHoverBackgroundDarkMode;
|
||||
colorAndCode.Color = Color.FromArgb(55, 55, 55);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorArrowHoverBackgroundDarkMode = colorAndCode.HtmlColorCode;
|
||||
AppColors.ArrowHoverBackgroundDarkMode = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorArrowHoverDarkMode;
|
||||
colorAndCode.Color = Color.FromArgb(103, 103, 103);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorArrowHoverDarkMode = colorAndCode.HtmlColorCode;
|
||||
AppColors.ArrowHoverDarkMode = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorArrowClickDarkMode;
|
||||
colorAndCode.Color = Color.FromArgb(23, 23, 23);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorArrowClickDarkMode = colorAndCode.HtmlColorCode;
|
||||
AppColors.ArrowClickDarkMode = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorArrowClickBackgroundDarkMode;
|
||||
colorAndCode.Color = Color.FromArgb(166, 166, 166);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorArrowClickBackgroundDarkMode = colorAndCode.HtmlColorCode;
|
||||
AppColors.ArrowClickBackgroundDarkMode = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorSliderArrowsAndTrackHoverDarkMode;
|
||||
colorAndCode.Color = Color.FromArgb(77, 77, 77);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorSliderArrowsAndTrackHoverDarkMode = colorAndCode.HtmlColorCode;
|
||||
AppColors.SliderArrowsAndTrackHoverDarkMode = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorSliderDarkMode;
|
||||
colorAndCode.Color = Color.FromArgb(77, 77, 77);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorSliderDarkMode = colorAndCode.HtmlColorCode;
|
||||
AppColors.SliderDarkMode = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorSliderHoverDarkMode;
|
||||
colorAndCode.Color = Color.FromArgb(122, 122, 122);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorSliderHoverDarkMode = colorAndCode.HtmlColorCode;
|
||||
AppColors.SliderHoverDarkMode = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorSliderDraggingDarkMode;
|
||||
colorAndCode.Color = Color.FromArgb(166, 166, 166);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorSliderDraggingDarkMode = colorAndCode.HtmlColorCode;
|
||||
AppColors.SliderDraggingDarkMode = colorAndCode.Color;
|
||||
|
||||
colorAndCode.HtmlColorCode = Settings.Default.ColorScrollbarBackgroundDarkMode;
|
||||
colorAndCode.Color = Color.FromArgb(23, 23, 23);
|
||||
colorAndCode = ProcessColorAndCode(converter, colorAndCode, ref changed);
|
||||
Settings.Default.ColorScrollbarBackgroundDarkMode = colorAndCode.HtmlColorCode;
|
||||
AppColors.ScrollbarBackgroundDarkMode = colorAndCode.Color;
|
||||
|
||||
if (save && changed)
|
||||
{
|
||||
Settings.Default.Save();
|
||||
}
|
||||
}
|
||||
|
||||
private static Bitmap ReadSvg(byte[] byteArray, string htmlColorCode)
|
||||
{
|
||||
string str = Encoding.UTF8.GetString(byteArray);
|
||||
str = str.Replace("#585858", htmlColorCode);
|
||||
byteArray = Encoding.UTF8.GetBytes(str);
|
||||
|
||||
using MemoryStream stream = new(byteArray);
|
||||
SvgDocument svgDocument = SvgDocument.Open<SvgDocument>(stream);
|
||||
svgDocument.Color = new SvgColourServer(Color.Black);
|
||||
return svgDocument.Draw();
|
||||
}
|
||||
|
||||
private static bool IsRegistryValueThisValue(string keyName, string valueName, string value)
|
||||
{
|
||||
bool isRegistryValueThisValue = false;
|
||||
|
@ -197,5 +495,35 @@ namespace SystemTrayMenu
|
|||
|
||||
return isRegistryValueThisValue;
|
||||
}
|
||||
|
||||
private static void UpgradeIfNotUpgraded()
|
||||
{
|
||||
if (!Settings.Default.IsUpgraded)
|
||||
{
|
||||
Settings.Default.Upgrade();
|
||||
Settings.Default.IsUpgraded = true;
|
||||
Settings.Default.Save();
|
||||
Log.Info($"Settings upgraded from {CustomSettingsProvider.UserConfigPath}");
|
||||
}
|
||||
}
|
||||
|
||||
private static ColorAndCode ProcessColorAndCode(
|
||||
ColorConverter colorConverter,
|
||||
ColorAndCode colorAndCode,
|
||||
ref bool changedHtmlColorCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
colorAndCode.Color = (Color)colorConverter.ConvertFromString(colorAndCode.HtmlColorCode);
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
Log.Warn($"HtmlColorCode {colorAndCode.HtmlColorCode}", ex);
|
||||
colorAndCode.HtmlColorCode = ColorTranslator.ToHtml(colorAndCode.Color);
|
||||
changedHtmlColorCode = true;
|
||||
}
|
||||
|
||||
return colorAndCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,9 @@ namespace SystemTrayMenu
|
|||
|
||||
internal static class MenuDefines
|
||||
{
|
||||
internal const int Scrollspeed = 4;
|
||||
internal const int MenusMax = 50;
|
||||
internal const int LengthMax = 37;
|
||||
internal static readonly Color File = Color.White;
|
||||
internal static readonly Color Folder = Color.White;
|
||||
internal const int Scrollspeed = 3;
|
||||
|
||||
public static Color ColorSelectedItem
|
||||
{
|
||||
|
@ -20,11 +18,11 @@ namespace SystemTrayMenu
|
|||
{
|
||||
if (Config.IsDarkMode())
|
||||
{
|
||||
return AppColors.DarkModeBlue;
|
||||
return AppColors.DarkModeSelecetedItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppColors.Blue;
|
||||
return AppColors.SelectedItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,11 +33,11 @@ namespace SystemTrayMenu
|
|||
{
|
||||
if (Config.IsDarkMode())
|
||||
{
|
||||
return AppColors.DarkModeBlueBorder;
|
||||
return AppColors.DarkModeSelectedItemBorder;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppColors.BlueBorder;
|
||||
return AppColors.SelectedItemBorder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,11 +48,11 @@ namespace SystemTrayMenu
|
|||
{
|
||||
if (Config.IsDarkMode())
|
||||
{
|
||||
return AppColors.DarkModeGreen;
|
||||
return AppColors.DarkModeOpenFolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppColors.Green;
|
||||
return AppColors.OpenFolder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,56 +63,26 @@ namespace SystemTrayMenu
|
|||
{
|
||||
if (Config.IsDarkMode())
|
||||
{
|
||||
return AppColors.DarkModeGreenBorder;
|
||||
return AppColors.DarkModeOpenFolderBorder;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppColors.GreenBorder;
|
||||
return AppColors.OpenFolderBorder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Color ColorTitleWarning
|
||||
public static Color ColorIcons
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Config.IsDarkMode())
|
||||
{
|
||||
return AppColors.DarkModeRed;
|
||||
return AppColors.DarkModeIcons;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppColors.Red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Color ColorTitleSelected
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Config.IsDarkMode())
|
||||
{
|
||||
return AppColors.DarkModeBlue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppColors.Blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Color ColorTitleBackground
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Config.IsDarkMode())
|
||||
{
|
||||
return AppColors.DarkModeAzure;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppColors.Azure;
|
||||
return AppColors.Icons;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace SystemTrayMenu.DataClasses
|
|||
|
||||
internal enum MenuDataValidity
|
||||
{
|
||||
AbortedOrUnknown,
|
||||
Undefined,
|
||||
Valid,
|
||||
Empty,
|
||||
NoAccess,
|
||||
|
@ -16,9 +16,23 @@ namespace SystemTrayMenu.DataClasses
|
|||
|
||||
internal struct MenuData
|
||||
{
|
||||
internal List<RowData> RowDatas;
|
||||
internal MenuDataValidity Validity;
|
||||
internal int Level;
|
||||
internal RowData RowDataParent;
|
||||
public MenuData(int level)
|
||||
{
|
||||
RowDatas = new List<RowData>();
|
||||
Validity = MenuDataValidity.Undefined;
|
||||
Level = level;
|
||||
RowDataParent = null;
|
||||
IsNetworkRoot = false;
|
||||
}
|
||||
|
||||
internal List<RowData> RowDatas { get; set; }
|
||||
|
||||
internal MenuDataValidity Validity { get; set; }
|
||||
|
||||
internal int Level { get; }
|
||||
|
||||
internal RowData RowDataParent { get; set; }
|
||||
|
||||
internal bool IsNetworkRoot { get; set; }
|
||||
}
|
||||
}
|
|
@ -5,177 +5,197 @@
|
|||
namespace SystemTrayMenu.DataClasses
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using IWshRuntimeLibrary;
|
||||
using SystemTrayMenu.Utilities;
|
||||
using TAFactory.IconPack;
|
||||
using static SystemTrayMenu.Utilities.IconReader;
|
||||
using Menu = SystemTrayMenu.UserInterface.Menu;
|
||||
|
||||
internal class RowData : IDisposable
|
||||
internal class RowData
|
||||
{
|
||||
private static readonly Icon White50PercentageIcon = Properties.Resources.White50Percentage;
|
||||
private static DateTime contextMenuClosed;
|
||||
private string workingDirectory;
|
||||
private string arguments;
|
||||
private string text;
|
||||
private Icon icon;
|
||||
private bool diposeIcon = true;
|
||||
private bool isDisposed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RowData"/> class.
|
||||
/// empty dummy.
|
||||
/// </summary>
|
||||
internal RowData()
|
||||
{
|
||||
}
|
||||
|
||||
internal FileInfo FileInfo { get; set; }
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RowData"/> class.
|
||||
/// (Related replace "\x00" see #171.)
|
||||
/// </summary>
|
||||
/// <param name="isFolder">Flag if file or folder.</param>
|
||||
/// <param name="isAddionalItem">Flag if addional item, from other folder than root folder.</param>
|
||||
/// <param name="isNetworkRoot">Flag if resolved from network root folder.</param>
|
||||
/// <param name="level">The number of the menu level.</param>
|
||||
/// <param name="path">Path to item.</param>
|
||||
internal RowData(bool isFolder, bool isAddionalItem, bool isNetworkRoot, int level, string path)
|
||||
{
|
||||
IsFolder = isFolder;
|
||||
IsAddionalItem = isAddionalItem;
|
||||
IsNetworkRoot = isNetworkRoot;
|
||||
Level = level;
|
||||
|
||||
try
|
||||
{
|
||||
FileInfo = new FileInfo(path.Replace("\x00", string.Empty));
|
||||
Path = IsFolder ? $@"{FileInfo.FullName}\" : FileInfo.FullName;
|
||||
FileExtension = System.IO.Path.GetExtension(Path);
|
||||
IsLink = FileExtension.Equals(".lnk", StringComparison.InvariantCultureIgnoreCase);
|
||||
if (IsLink)
|
||||
{
|
||||
ResolvedPath = FileLnk.GetResolvedFileName(Path, out bool isLinkToFolder);
|
||||
IsLinkToFolder = isLinkToFolder || FileLnk.IsNetworkRoot(ResolvedPath);
|
||||
ShowOverlay = Properties.Settings.Default.ShowLinkOverlay;
|
||||
Text = System.IO.Path.GetFileNameWithoutExtension(Path);
|
||||
if (string.IsNullOrEmpty(ResolvedPath))
|
||||
{
|
||||
Log.Info($"Resolved path is empty: '{Path}'");
|
||||
ResolvedPath = Path;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ResolvedPath = Path;
|
||||
if (string.IsNullOrEmpty(FileInfo.Name))
|
||||
{
|
||||
int nameBegin = FileInfo.FullName.LastIndexOf(@"\", StringComparison.InvariantCulture) + 1;
|
||||
Text = FileInfo.FullName[nameBegin..];
|
||||
}
|
||||
else if (FileExtension.Equals(".url", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
FileExtension.Equals(".appref-ms", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
ShowOverlay = Properties.Settings.Default.ShowLinkOverlay;
|
||||
Text = System.IO.Path.GetFileNameWithoutExtension(FileInfo.Name);
|
||||
}
|
||||
else if (!IsFolder && Config.IsHideFileExtension())
|
||||
{
|
||||
Text = System.IO.Path.GetFileNameWithoutExtension(FileInfo.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
Text = FileInfo.Name;
|
||||
}
|
||||
}
|
||||
|
||||
ContainsMenu = IsFolder;
|
||||
if (Properties.Settings.Default.ResolveLinksToFolders)
|
||||
{
|
||||
ContainsMenu |= IsLinkToFolder;
|
||||
}
|
||||
|
||||
IsMainMenu = Level == 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"path:'{path}'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
internal FileInfo FileInfo { get; }
|
||||
|
||||
internal string Path { get; }
|
||||
|
||||
internal bool IsFolder { get; }
|
||||
|
||||
internal bool IsAddionalItem { get; }
|
||||
|
||||
internal bool IsNetworkRoot { get; }
|
||||
|
||||
internal int Level { get; set; }
|
||||
|
||||
internal string FileExtension { get; }
|
||||
|
||||
internal bool IsLink { get; }
|
||||
|
||||
internal string ResolvedPath { get; }
|
||||
|
||||
internal bool IsLinkToFolder { get; }
|
||||
|
||||
internal bool ShowOverlay { get; }
|
||||
|
||||
internal string Text { get; }
|
||||
|
||||
internal bool ContainsMenu { get; }
|
||||
|
||||
internal bool IsMainMenu { get; }
|
||||
|
||||
internal Menu SubMenu { get; set; }
|
||||
|
||||
internal bool IsMenuOpen { get; set; }
|
||||
|
||||
internal bool IsSelected { get; set; }
|
||||
internal bool IsClicking { get; set; }
|
||||
|
||||
internal bool ContainsMenu { get; set; }
|
||||
internal bool IsSelected { get; set; }
|
||||
|
||||
internal bool IsContextMenuOpen { get; set; }
|
||||
|
||||
internal bool IsResolvedLnk { get; set; }
|
||||
|
||||
internal bool HiddenEntry { get; set; }
|
||||
|
||||
internal string TargetFilePath { get; set; }
|
||||
|
||||
internal string TargetFilePathOrig { get; set; }
|
||||
|
||||
internal int RowIndex { get; set; }
|
||||
|
||||
internal int MenuLevel { get; set; }
|
||||
internal bool IconLoading { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
internal void SetText(string text)
|
||||
{
|
||||
this.text = text;
|
||||
}
|
||||
internal bool ProcessStarted { get; set; }
|
||||
|
||||
internal void SetData(RowData data, DataTable dataTable)
|
||||
{
|
||||
DataRow row = dataTable.Rows.Add();
|
||||
data.RowIndex = dataTable.Rows.IndexOf(row);
|
||||
|
||||
if (icon == null)
|
||||
{
|
||||
icon = White50PercentageIcon;
|
||||
}
|
||||
|
||||
if (HiddenEntry)
|
||||
{
|
||||
row[0] = IconReader.AddIconOverlay(
|
||||
data.icon,
|
||||
White50PercentageIcon);
|
||||
row[0] = AddIconOverlay(data.icon, Properties.Resources.White50Percentage);
|
||||
}
|
||||
else
|
||||
{
|
||||
row[0] = data.icon;
|
||||
}
|
||||
|
||||
if (!ContainsMenu &&
|
||||
Config.IsHideFileExtension())
|
||||
{
|
||||
row[1] = Path.GetFileNameWithoutExtension(data.text);
|
||||
}
|
||||
else
|
||||
{
|
||||
row[1] = data.text;
|
||||
}
|
||||
|
||||
row[1] = data.Text;
|
||||
row[2] = data;
|
||||
}
|
||||
|
||||
internal bool ReadIcon(bool isDirectory, ref string resolvedLnkPath)
|
||||
internal Icon ReadIcon(bool updateIconInBackground)
|
||||
{
|
||||
bool isLnkDirectory = false;
|
||||
|
||||
if (string.IsNullOrEmpty(TargetFilePath))
|
||||
if (IsFolder || IsLinkToFolder)
|
||||
{
|
||||
Log.Info($"TargetFilePath from {resolvedLnkPath} empty");
|
||||
}
|
||||
else if (isDirectory)
|
||||
{
|
||||
icon = IconReader.GetFolderIconSTA(
|
||||
TargetFilePath,
|
||||
IconReader.FolderType.Closed,
|
||||
false);
|
||||
icon = GetFolderIconWithCache(Path, ShowOverlay, updateIconInBackground, IsMainMenu, out bool loading);
|
||||
IconLoading = loading;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool handled = false;
|
||||
bool showOverlay = false;
|
||||
string fileExtension = Path.GetExtension(TargetFilePath);
|
||||
icon = GetFileIconWithCache(Path, ResolvedPath, ShowOverlay, updateIconInBackground, IsMainMenu, out bool loading);
|
||||
IconLoading = loading;
|
||||
}
|
||||
|
||||
if (fileExtension == ".lnk")
|
||||
if (!IconLoading)
|
||||
{
|
||||
if (icon == null)
|
||||
{
|
||||
handled = SetLnk(
|
||||
ref isLnkDirectory,
|
||||
ref resolvedLnkPath);
|
||||
showOverlay = true;
|
||||
icon = Properties.Resources.NotFound;
|
||||
}
|
||||
else if (fileExtension == ".url")
|
||||
else if (HiddenEntry)
|
||||
{
|
||||
handled = SetUrl();
|
||||
showOverlay = true;
|
||||
}
|
||||
else if (fileExtension == ".sln")
|
||||
{
|
||||
handled = SetSln();
|
||||
}
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
try
|
||||
{
|
||||
icon = IconReader.GetFileIconWithCache(
|
||||
TargetFilePath,
|
||||
showOverlay,
|
||||
out bool toDispose);
|
||||
diposeIcon = toDispose;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is SecurityException ||
|
||||
ex is ArgumentException ||
|
||||
ex is UnauthorizedAccessException ||
|
||||
ex is PathTooLongException ||
|
||||
ex is NotSupportedException)
|
||||
{
|
||||
Log.Warn($"path:'{TargetFilePath}'", ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
icon = AddIconOverlay(icon, Properties.Resources.White50Percentage);
|
||||
}
|
||||
}
|
||||
|
||||
return isLnkDirectory;
|
||||
return icon;
|
||||
}
|
||||
|
||||
internal void MouseDown(DataGridView dgv, MouseEventArgs e, out bool toCloseByDoubleClick)
|
||||
internal void MouseDown(DataGridView dgv, MouseEventArgs e)
|
||||
{
|
||||
toCloseByDoubleClick = false;
|
||||
if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
IsClicking = true;
|
||||
}
|
||||
|
||||
if (e != null &&
|
||||
e.Button == MouseButtons.Right &&
|
||||
|
@ -186,46 +206,57 @@ namespace SystemTrayMenu.DataClasses
|
|||
{
|
||||
IsContextMenuOpen = true;
|
||||
|
||||
ShellContextMenu ctxMnu = new ShellContextMenu();
|
||||
ShellContextMenu ctxMnu = new();
|
||||
Point location = dgv.FindForm().Location;
|
||||
Point point = new Point(
|
||||
Point point = new(
|
||||
e.X + location.X + dgv.Location.X,
|
||||
e.Y + location.Y + dgv.Location.Y);
|
||||
if (ContainsMenu)
|
||||
{
|
||||
DirectoryInfo[] dir = new DirectoryInfo[1];
|
||||
dir[0] = new DirectoryInfo(TargetFilePathOrig);
|
||||
dir[0] = new DirectoryInfo(Path);
|
||||
ctxMnu.ShowContextMenu(dir, point);
|
||||
TriggerFileWatcherChangeWorkaround();
|
||||
}
|
||||
else
|
||||
{
|
||||
FileInfo[] arrFI = new FileInfo[1];
|
||||
arrFI[0] = new FileInfo(TargetFilePathOrig);
|
||||
arrFI[0] = FileInfo;
|
||||
ctxMnu.ShowContextMenu(arrFI, point);
|
||||
TriggerFileWatcherChangeWorkaround();
|
||||
}
|
||||
|
||||
IsContextMenuOpen = false;
|
||||
contextMenuClosed = DateTime.Now;
|
||||
}
|
||||
|
||||
void TriggerFileWatcherChangeWorkaround()
|
||||
{
|
||||
try
|
||||
{
|
||||
string parentFolder = System.IO.Path.GetDirectoryName(Path);
|
||||
Directory.GetFiles(parentFolder);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"{nameof(TriggerFileWatcherChangeWorkaround)} '{Path}'", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void MouseClick(MouseEventArgs e, out bool toCloseByDoubleClick)
|
||||
{
|
||||
IsClicking = false;
|
||||
toCloseByDoubleClick = false;
|
||||
if (Properties.Settings.Default.OpenItemWithOneClick)
|
||||
{
|
||||
OpenItem(e, ref toCloseByDoubleClick);
|
||||
}
|
||||
}
|
||||
|
||||
internal void DoubleClick(MouseEventArgs e, out bool toCloseByDoubleClick)
|
||||
{
|
||||
toCloseByDoubleClick = false;
|
||||
if (!Properties.Settings.Default.OpenItemWithOneClick)
|
||||
if (Properties.Settings.Default.OpenDirectoryWithOneClick &&
|
||||
ContainsMenu && (e == null || e.Button == MouseButtons.Left))
|
||||
{
|
||||
OpenItem(e, ref toCloseByDoubleClick);
|
||||
}
|
||||
|
||||
if (ContainsMenu &&
|
||||
(e == null || e.Button == MouseButtons.Left))
|
||||
{
|
||||
Log.ProcessStart(TargetFilePath);
|
||||
Log.ProcessStart(Path);
|
||||
if (!Properties.Settings.Default.StaysOpenWhenItemClicked)
|
||||
{
|
||||
toCloseByDoubleClick = true;
|
||||
|
@ -233,17 +264,24 @@ namespace SystemTrayMenu.DataClasses
|
|||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
internal void DoubleClick(MouseEventArgs e, out bool toCloseByDoubleClick)
|
||||
{
|
||||
if (!isDisposed)
|
||||
IsClicking = false;
|
||||
toCloseByDoubleClick = false;
|
||||
if (!Properties.Settings.Default.OpenItemWithOneClick)
|
||||
{
|
||||
if (diposeIcon)
|
||||
{
|
||||
icon?.Dispose();
|
||||
}
|
||||
OpenItem(e, ref toCloseByDoubleClick);
|
||||
}
|
||||
|
||||
isDisposed = true;
|
||||
if (!Properties.Settings.Default.OpenDirectoryWithOneClick &&
|
||||
ContainsMenu && (e == null || e.Button == MouseButtons.Left))
|
||||
{
|
||||
Log.ProcessStart(Path);
|
||||
if (!Properties.Settings.Default.StaysOpenWhenItemClicked)
|
||||
{
|
||||
toCloseByDoubleClick = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenItem(MouseEventArgs e, ref bool toCloseByOpenItem)
|
||||
|
@ -251,164 +289,14 @@ namespace SystemTrayMenu.DataClasses
|
|||
if (!ContainsMenu &&
|
||||
(e == null || e.Button == MouseButtons.Left))
|
||||
{
|
||||
Log.ProcessStart(TargetFilePathOrig, arguments, true, workingDirectory, true);
|
||||
ProcessStarted = true;
|
||||
string workingDirectory = System.IO.Path.GetDirectoryName(ResolvedPath);
|
||||
Log.ProcessStart(Path, string.Empty, false, workingDirectory, true, ResolvedPath);
|
||||
if (!Properties.Settings.Default.StaysOpenWhenItemClicked)
|
||||
{
|
||||
toCloseByOpenItem = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool SetLnk(
|
||||
ref bool isLnkDirectory,
|
||||
ref string resolvedLnkPath)
|
||||
{
|
||||
bool handled = false;
|
||||
resolvedLnkPath = FileLnk.GetResolvedFileName(TargetFilePath);
|
||||
if (FileLnk.IsNetworkPath(resolvedLnkPath))
|
||||
{
|
||||
string nameOrAdress = resolvedLnkPath.Split(@"\\")[1].Split(@"\").First();
|
||||
if (!FileLnk.PingHost(nameOrAdress))
|
||||
{
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
|
||||
if (FileLnk.IsDirectory(resolvedLnkPath))
|
||||
{
|
||||
icon = IconReader.GetFolderIconSTA(TargetFilePath, IconReader.FolderType.Open, true);
|
||||
handled = true;
|
||||
isLnkDirectory = true;
|
||||
}
|
||||
else if (FileLnk.IsNetworkRoot(resolvedLnkPath))
|
||||
{
|
||||
isLnkDirectory = true;
|
||||
}
|
||||
else if (string.IsNullOrEmpty(resolvedLnkPath))
|
||||
{
|
||||
Log.Info($"Resolve *.LNK '{TargetFilePath}' has no icon");
|
||||
}
|
||||
else
|
||||
{
|
||||
IWshShell shell = new WshShell();
|
||||
IWshShortcut lnk = shell.CreateShortcut(TargetFilePath)
|
||||
as IWshShortcut;
|
||||
arguments = lnk.Arguments;
|
||||
workingDirectory = lnk.WorkingDirectory;
|
||||
string iconLocation = lnk.IconLocation;
|
||||
if (iconLocation.Length > 2)
|
||||
{
|
||||
iconLocation = iconLocation[0..^2];
|
||||
if (System.IO.File.Exists(iconLocation))
|
||||
{
|
||||
try
|
||||
{
|
||||
icon = IconReader.GetFileIconWithCache(iconLocation, true, out bool toDispose);
|
||||
diposeIcon = toDispose;
|
||||
handled = true;
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
Log.Warn($"iconLocation:'{iconLocation}'", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TargetFilePath = resolvedLnkPath;
|
||||
}
|
||||
|
||||
SetText(Path.GetFileNameWithoutExtension(TargetFilePathOrig));
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
private bool SetUrl()
|
||||
{
|
||||
bool handled = false;
|
||||
string iconFile = string.Empty;
|
||||
try
|
||||
{
|
||||
FileIni file = new FileIni(TargetFilePath);
|
||||
iconFile = file.Value("IconFile", string.Empty);
|
||||
if (string.IsNullOrEmpty(iconFile))
|
||||
{
|
||||
if (FileUrl.GetDefaultBrowserPath(out string browserPath))
|
||||
{
|
||||
icon = IconReader.GetFileIconWithCache(browserPath, true, out bool toDispose);
|
||||
diposeIcon = toDispose;
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if (System.IO.File.Exists(iconFile))
|
||||
{
|
||||
icon = IconReader.GetFileIconWithCache(iconFile, true, out bool toDispose);
|
||||
diposeIcon = toDispose;
|
||||
handled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Info($"Resolve *.URL '{TargetFilePath}' has no icon");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is SecurityException ||
|
||||
ex is ArgumentException ||
|
||||
ex is UnauthorizedAccessException ||
|
||||
ex is PathTooLongException ||
|
||||
ex is NotSupportedException)
|
||||
{
|
||||
Log.Warn(
|
||||
$"path:'{TargetFilePath}', " +
|
||||
$"iconFile:'{iconFile}'",
|
||||
ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
SetText($"{FileInfo.Name[0..^4]}");
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
private bool SetSln()
|
||||
{
|
||||
bool handled = false;
|
||||
StringBuilder executable = new StringBuilder(1024);
|
||||
try
|
||||
{
|
||||
DllImports.NativeMethods.Shell32FindExecutable(TargetFilePath, string.Empty, executable);
|
||||
|
||||
// icon = IconReader.GetFileIcon(executable, false);
|
||||
// e.g. VS 2019 icon, need another icom in imagelist
|
||||
List<Icon> extractedIcons = IconHelper.ExtractAllIcons(
|
||||
executable.ToString());
|
||||
icon = extractedIcons.Last();
|
||||
handled = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is SecurityException ||
|
||||
ex is ArgumentException ||
|
||||
ex is UnauthorizedAccessException ||
|
||||
ex is PathTooLongException ||
|
||||
ex is NotSupportedException)
|
||||
{
|
||||
Log.Warn(
|
||||
$"path:'{TargetFilePath}', " +
|
||||
$"executable:'{executable}'",
|
||||
ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,4 +10,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||
[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")]
|
||||
|
|
|
@ -9,19 +9,19 @@ namespace SystemTrayMenu.Helper
|
|||
|
||||
public class DgvMouseRow : IDisposable
|
||||
{
|
||||
private readonly Timer timerRaiseRowMouseLeave = new Timer();
|
||||
private readonly Timer timerRaiseRowMouseLeave = new();
|
||||
private DataGridView dgv;
|
||||
private DataGridViewCellEventArgs eventArgs;
|
||||
|
||||
internal DgvMouseRow()
|
||||
{
|
||||
timerRaiseRowMouseLeave.Interval = 200;
|
||||
timerRaiseRowMouseLeave.Tick += Elapsed;
|
||||
void Elapsed(object sender, EventArgs e)
|
||||
{
|
||||
timerRaiseRowMouseLeave.Stop();
|
||||
TriggerRowMouseLeave();
|
||||
}
|
||||
timerRaiseRowMouseLeave.Tick += TimerRaiseRowMouseLeave_Tick;
|
||||
}
|
||||
|
||||
~DgvMouseRow() // the finalizer
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
internal event Action<object, DataGridViewCellEventArgs> RowMouseEnter;
|
||||
|
@ -75,11 +75,18 @@ namespace SystemTrayMenu.Helper
|
|||
{
|
||||
if (disposing)
|
||||
{
|
||||
timerRaiseRowMouseLeave.Tick -= TimerRaiseRowMouseLeave_Tick;
|
||||
timerRaiseRowMouseLeave.Dispose();
|
||||
dgv?.Dispose();
|
||||
dgv = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void TimerRaiseRowMouseLeave_Tick(object sender, EventArgs e)
|
||||
{
|
||||
timerRaiseRowMouseLeave.Stop();
|
||||
TriggerRowMouseLeave();
|
||||
}
|
||||
|
||||
private void TriggerRowMouseLeave()
|
||||
{
|
||||
if (dgv != null)
|
||||
|
|
190
Helpers/DragDropHelper.cs
Normal file
|
@ -0,0 +1,190 @@
|
|||
// <copyright file="DragDropHelper.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.Helper
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.DataClasses;
|
||||
using SystemTrayMenu.UserInterface;
|
||||
using SystemTrayMenu.Utilities;
|
||||
|
||||
public static class DragDropHelper
|
||||
{
|
||||
public static void DragEnter(object sender, DragEventArgs e)
|
||||
{
|
||||
object data = e.Data.GetData("UniformResourceLocator");
|
||||
|
||||
if (data is MemoryStream memoryStream)
|
||||
{
|
||||
byte[] bytes = memoryStream.ToArray();
|
||||
Encoding encod = Encoding.ASCII;
|
||||
string url = encod.GetString(bytes);
|
||||
if (!string.IsNullOrEmpty(url))
|
||||
{
|
||||
e.Effect = DragDropEffects.Copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void DragDrop(object sender, DragEventArgs e)
|
||||
{
|
||||
Menu menu = (Menu)sender;
|
||||
string path;
|
||||
if (menu != null)
|
||||
{
|
||||
RowData rowData = (RowData)menu.Tag;
|
||||
if (rowData != null)
|
||||
{
|
||||
path = rowData.ResolvedPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
path = Config.Path;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
path = Config.Path;
|
||||
}
|
||||
|
||||
object data = e.Data.GetData("UniformResourceLocator");
|
||||
MemoryStream ms = data as MemoryStream;
|
||||
byte[] bytes = ms.ToArray();
|
||||
Encoding encod = Encoding.ASCII;
|
||||
string url = encod.GetString(bytes);
|
||||
|
||||
new Thread(CreateShortcutInBackground).Start();
|
||||
void CreateShortcutInBackground()
|
||||
{
|
||||
CreateShortcut(url.Replace("\0", string.Empty), path);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateShortcut(string url, string pathToStoreFile)
|
||||
{
|
||||
string title = GetUrlShortcutTitle(url);
|
||||
string fileNamePathShortcut = pathToStoreFile + "\\" + title.Trim() + ".url";
|
||||
WriteShortcut(url, null, fileNamePathShortcut);
|
||||
string pathIcon = DownloadUrlIcon(url);
|
||||
if (!string.IsNullOrEmpty(pathIcon))
|
||||
{
|
||||
WriteShortcut(url, pathIcon, fileNamePathShortcut);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetUrlShortcutTitle(string url)
|
||||
{
|
||||
string title = url
|
||||
.Replace("/", " ")
|
||||
.Replace("https", string.Empty)
|
||||
.Replace("http", string.Empty);
|
||||
string invalid =
|
||||
new string(Path.GetInvalidFileNameChars()) +
|
||||
new string(Path.GetInvalidPathChars());
|
||||
foreach (char character in invalid)
|
||||
{
|
||||
title = title.Replace(character.ToString(), string.Empty);
|
||||
}
|
||||
|
||||
title = Truncate(title, 128); // max 255
|
||||
return title;
|
||||
}
|
||||
|
||||
private static string Truncate(string value, int maxLength)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(value) &&
|
||||
value.Length > maxLength)
|
||||
{
|
||||
value = value[..maxLength];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static void WriteShortcut(string url, string pathIcon, string fileNamePathShortcut)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(fileNamePathShortcut))
|
||||
{
|
||||
File.Delete(fileNamePathShortcut);
|
||||
}
|
||||
|
||||
StreamWriter writer = new(fileNamePathShortcut);
|
||||
writer.WriteLine("[InternetShortcut]");
|
||||
writer.WriteLine($"URL={url.TrimEnd('\0')}");
|
||||
writer.WriteLine("IconIndex=0");
|
||||
writer.WriteLine($"HotKey=0");
|
||||
writer.WriteLine($"IDList=");
|
||||
if (!string.IsNullOrEmpty(pathIcon))
|
||||
{
|
||||
writer.WriteLine($"IconFile={pathIcon}");
|
||||
}
|
||||
|
||||
writer.Flush();
|
||||
writer.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"{nameof(WriteShortcut)} failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static string DownloadUrlIcon(string url)
|
||||
{
|
||||
string pathIcon = string.Empty;
|
||||
string pathToStoreIcons = Properties.Settings.Default.PathIcoDirectory;
|
||||
Uri uri = new(url);
|
||||
string hostname = uri.Host.ToString();
|
||||
string pathIconPng = Path.Combine(pathToStoreIcons, $"{hostname}.png");
|
||||
string urlGoogleIconDownload = @"http://www.google.com/s2/favicons?sz=32&domain=" + url;
|
||||
HttpClient client = new();
|
||||
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(pathToStoreIcons))
|
||||
{
|
||||
Directory.CreateDirectory(pathToStoreIcons);
|
||||
}
|
||||
|
||||
using HttpResponseMessage response = client.GetAsync(urlGoogleIconDownload).Result;
|
||||
using HttpContent content = response.Content;
|
||||
Stream stream = content.ReadAsStreamAsync().Result;
|
||||
using var fileStream = File.Create(pathIconPng);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
stream.CopyTo(fileStream);
|
||||
fileStream.Close();
|
||||
|
||||
pathIcon = Path.Combine(pathToStoreIcons, $"{hostname}.ico");
|
||||
if (!ImagingHelper.ConvertToIcon(pathIconPng, pathIcon, 32))
|
||||
{
|
||||
Log.Info("Failed to convert icon.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"{nameof(DownloadUrlIcon)} failed", ex);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (File.Exists(pathIconPng))
|
||||
{
|
||||
File.Delete(pathIconPng);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"{nameof(DownloadUrlIcon)} failed to delete {pathIconPng}", ex);
|
||||
}
|
||||
|
||||
return pathIcon;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.UserInterface
|
||||
namespace SystemTrayMenu.Helper
|
||||
{
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
@ -10,7 +10,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
|
||||
public class Fading : IDisposable
|
||||
{
|
||||
private const int Interval60FPS = 16; // 60fps=>1s/60fps=~16.6ms
|
||||
private const int Interval100FPS = 10; // 100fps=>1s/100fps=~10ms
|
||||
|
||||
private const double StepIn = 0.20;
|
||||
private const double StepOut = 0.10;
|
||||
|
@ -20,26 +20,27 @@ namespace SystemTrayMenu.UserInterface
|
|||
private const double Shown = 1.00;
|
||||
private const double ShownMinus = 0.80; // Shown - StepIn
|
||||
|
||||
private readonly Timer timer = new Timer();
|
||||
private readonly Timer timer = new();
|
||||
private FadingState state = FadingState.Idle;
|
||||
private double opacity;
|
||||
private bool visible;
|
||||
|
||||
internal Fading()
|
||||
{
|
||||
timer.Interval = Interval60FPS;
|
||||
timer.Tick += Tick;
|
||||
void Tick(object sender, EventArgs e)
|
||||
{
|
||||
FadeStep();
|
||||
}
|
||||
timer.Interval = Interval100FPS;
|
||||
timer.Tick += Timer_Tick;
|
||||
}
|
||||
|
||||
internal event EventHandlerEmpty Hide;
|
||||
~Fading() // the finalizer
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
internal event EventHandlerEmpty Show;
|
||||
internal event Action Hide;
|
||||
|
||||
internal event EventHandler<double> ChangeOpacity;
|
||||
internal event Action Show;
|
||||
|
||||
internal event Action<double> ChangeOpacity;
|
||||
|
||||
internal enum FadingState
|
||||
{
|
||||
|
@ -66,6 +67,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
{
|
||||
if (disposing)
|
||||
{
|
||||
timer.Tick -= Timer_Tick;
|
||||
timer.Dispose();
|
||||
}
|
||||
}
|
||||
|
@ -81,10 +83,14 @@ namespace SystemTrayMenu.UserInterface
|
|||
{
|
||||
state = newState;
|
||||
timer.Start();
|
||||
FadeStep();
|
||||
}
|
||||
}
|
||||
|
||||
private void Timer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
FadeStep();
|
||||
}
|
||||
|
||||
private void FadeStep()
|
||||
{
|
||||
switch (state)
|
||||
|
@ -95,17 +101,25 @@ namespace SystemTrayMenu.UserInterface
|
|||
visible = true;
|
||||
Show?.Invoke();
|
||||
opacity = 0;
|
||||
ChangeOpacity?.Invoke(this, opacity);
|
||||
ChangeOpacity?.Invoke(opacity);
|
||||
}
|
||||
else if (opacity < ShownMinus)
|
||||
else if (Properties.Settings.Default.UseFading &&
|
||||
opacity < ShownMinus)
|
||||
{
|
||||
opacity += StepIn;
|
||||
ChangeOpacity?.Invoke(this, opacity);
|
||||
ChangeOpacity?.Invoke(opacity);
|
||||
}
|
||||
else if (opacity != Shown)
|
||||
else
|
||||
{
|
||||
if (!Properties.Settings.Default.UseFading)
|
||||
{
|
||||
// #393 provoke a redraw for the CS_DROPSHADOW to work
|
||||
opacity = ShownMinus;
|
||||
ChangeOpacity?.Invoke(opacity);
|
||||
}
|
||||
|
||||
opacity = Shown;
|
||||
ChangeOpacity?.Invoke(this, Shown);
|
||||
ChangeOpacity?.Invoke(opacity);
|
||||
StartStopTimer(FadingState.Idle);
|
||||
}
|
||||
|
||||
|
@ -116,39 +130,47 @@ namespace SystemTrayMenu.UserInterface
|
|||
visible = true;
|
||||
Show?.Invoke();
|
||||
opacity = 0;
|
||||
ChangeOpacity?.Invoke(this, opacity);
|
||||
ChangeOpacity?.Invoke(opacity);
|
||||
}
|
||||
else if (opacity < TransparentMinus)
|
||||
else if (Properties.Settings.Default.UseFading &&
|
||||
opacity < TransparentMinus)
|
||||
{
|
||||
opacity += StepIn;
|
||||
ChangeOpacity?.Invoke(this, opacity);
|
||||
ChangeOpacity?.Invoke(opacity);
|
||||
}
|
||||
else if (opacity > TransparentPlus)
|
||||
else if (Properties.Settings.Default.UseFading &&
|
||||
opacity > TransparentPlus)
|
||||
{
|
||||
opacity -= StepOut;
|
||||
ChangeOpacity?.Invoke(this, opacity);
|
||||
ChangeOpacity?.Invoke(opacity);
|
||||
}
|
||||
else if (opacity != Transparent)
|
||||
else
|
||||
{
|
||||
ChangeOpacity?.Invoke(this, Transparent);
|
||||
opacity = Transparent;
|
||||
ChangeOpacity?.Invoke(opacity);
|
||||
StartStopTimer(FadingState.Idle);
|
||||
}
|
||||
|
||||
break;
|
||||
case FadingState.Hide:
|
||||
if (opacity > StepOut)
|
||||
if (Properties.Settings.Default.UseFading &&
|
||||
opacity > StepOut)
|
||||
{
|
||||
opacity -= StepOut;
|
||||
ChangeOpacity?.Invoke(this, opacity);
|
||||
ChangeOpacity?.Invoke(opacity);
|
||||
}
|
||||
else if (visible)
|
||||
{
|
||||
opacity = 0;
|
||||
ChangeOpacity?.Invoke(this, opacity);
|
||||
ChangeOpacity?.Invoke(opacity);
|
||||
visible = false;
|
||||
Hide?.Invoke();
|
||||
StartStopTimer(FadingState.Idle);
|
||||
}
|
||||
else
|
||||
{
|
||||
StartStopTimer(FadingState.Idle);
|
||||
}
|
||||
|
||||
break;
|
||||
case FadingState.Idle:
|
||||
|
|
147
Helpers/ImagingHelper.cs
Normal file
|
@ -0,0 +1,147 @@
|
|||
// <copyright file="ImagingHelper.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.Helper
|
||||
{
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Provides helper methods for imaging.
|
||||
/// </summary>
|
||||
public static class ImagingHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a PNG image to a icon (ico).
|
||||
/// </summary>
|
||||
/// <param name="input">The input stream.</param>
|
||||
/// <param name="output">The output stream.</param>
|
||||
/// <param name="size">The size (16x16 px by default).</param>
|
||||
/// <param name="preserveAspectRatio">Preserve the aspect ratio.</param>
|
||||
/// <returns>Wether or not the icon was succesfully generated.</returns>
|
||||
public static bool ConvertToIcon(Stream input, Stream output, int size = 16, bool preserveAspectRatio = false)
|
||||
{
|
||||
Bitmap inputBitmap = (Bitmap)Image.FromStream(input);
|
||||
if (inputBitmap != null)
|
||||
{
|
||||
int width, height;
|
||||
if (preserveAspectRatio)
|
||||
{
|
||||
width = size;
|
||||
height = inputBitmap.Height / inputBitmap.Width * size;
|
||||
}
|
||||
else
|
||||
{
|
||||
width = height = size;
|
||||
}
|
||||
|
||||
Bitmap newBitmap = new(inputBitmap, new Size(width, height));
|
||||
if (newBitmap != null)
|
||||
{
|
||||
// save the resized png into a memory stream for future use
|
||||
using MemoryStream memoryStream = new();
|
||||
newBitmap.Save(memoryStream, ImageFormat.Png);
|
||||
|
||||
BinaryWriter iconWriter = new(output);
|
||||
if (output != null && iconWriter != null)
|
||||
{
|
||||
// 0-1 reserved, 0
|
||||
iconWriter.Write((byte)0);
|
||||
iconWriter.Write((byte)0);
|
||||
|
||||
// 2-3 image type, 1 = icon, 2 = cursor
|
||||
iconWriter.Write((short)1);
|
||||
|
||||
// 4-5 number of images
|
||||
iconWriter.Write((short)1);
|
||||
|
||||
// image entry 1
|
||||
// 0 image width
|
||||
iconWriter.Write((byte)width);
|
||||
|
||||
// 1 image height
|
||||
iconWriter.Write((byte)height);
|
||||
|
||||
// 2 number of colors
|
||||
iconWriter.Write((byte)0);
|
||||
|
||||
// 3 reserved
|
||||
iconWriter.Write((byte)0);
|
||||
|
||||
// 4-5 color planes
|
||||
iconWriter.Write((short)0);
|
||||
|
||||
// 6-7 bits per pixel
|
||||
iconWriter.Write((short)32);
|
||||
|
||||
// 8-11 size of image data
|
||||
iconWriter.Write((int)memoryStream.Length);
|
||||
|
||||
// 12-15 offset of image data
|
||||
iconWriter.Write(6 + 16);
|
||||
|
||||
// write image data
|
||||
// png data must contain the whole png data file
|
||||
iconWriter.Write(memoryStream.ToArray());
|
||||
|
||||
iconWriter.Flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a PNG image to a icon (ico).
|
||||
/// </summary>
|
||||
/// <param name="inputPath">The input path.</param>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
/// <param name="size">The size (16x16 px by default).</param>
|
||||
/// <param name="preserveAspectRatio">Preserve the aspect ratio.</param>
|
||||
/// <returns>Wether or not the icon was succesfully generated.</returns>
|
||||
public static bool ConvertToIcon(string inputPath, string outputPath, int size = 16, bool preserveAspectRatio = false)
|
||||
{
|
||||
using FileStream inputStream = new(inputPath, FileMode.Open);
|
||||
using FileStream outputStream = new(outputPath, FileMode.OpenOrCreate);
|
||||
return ConvertToIcon(inputStream, outputStream, size, preserveAspectRatio);
|
||||
}
|
||||
|
||||
public static Image RotateImage(Image img, float rotationAngle)
|
||||
{
|
||||
// create an empty Bitmap image
|
||||
Bitmap bmp = new(img.Width, img.Height);
|
||||
|
||||
// turn the Bitmap into a Graphics object
|
||||
Graphics gfx = Graphics.FromImage(bmp);
|
||||
|
||||
// now we set the rotation point to the center of our image
|
||||
gfx.TranslateTransform(0.5f + ((float)bmp.Width / 2), 0.5f + ((float)bmp.Height / 2));
|
||||
|
||||
// now rotate the image
|
||||
gfx.RotateTransform(rotationAngle);
|
||||
|
||||
gfx.TranslateTransform(0.5f - ((float)bmp.Width / 2), 0.5f - ((float)bmp.Height / 2));
|
||||
|
||||
// set the InterpolationMode to HighQualityBicubic so to ensure a high
|
||||
// quality image once it is transformed to the specified size
|
||||
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
|
||||
// now draw our new image onto the graphics object
|
||||
gfx.DrawImage(img, new Point(0, 0));
|
||||
|
||||
// dispose of our Graphics object
|
||||
gfx.Dispose();
|
||||
|
||||
// return the image
|
||||
return bmp;
|
||||
}
|
||||
}
|
||||
}
|
215
Helpers/JoystickHelper.cs
Normal file
|
@ -0,0 +1,215 @@
|
|||
// <copyright file="JoystickHelper.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.Helpers
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using SharpDX.DirectInput;
|
||||
|
||||
public class JoystickHelper : IDisposable
|
||||
{
|
||||
private readonly System.Timers.Timer timerReadJoystick = new();
|
||||
private readonly object lockRead = new();
|
||||
private Joystick joystick;
|
||||
private Keys pressingKey;
|
||||
private int pressingKeyCounter;
|
||||
private bool joystickHelperEnabled;
|
||||
|
||||
public JoystickHelper()
|
||||
{
|
||||
timerReadJoystick.Interval = 80;
|
||||
timerReadJoystick.Elapsed += ReadJoystickLoop;
|
||||
timerReadJoystick.Enabled = false;
|
||||
if (Properties.Settings.Default.SupportGamepad)
|
||||
{
|
||||
timerReadJoystick.Start();
|
||||
}
|
||||
}
|
||||
|
||||
~JoystickHelper() // the finalizer
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public event Action<Keys> KeyPressed;
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
joystickHelperEnabled = true;
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
joystickHelperEnabled = false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
timerReadJoystick.Elapsed -= ReadJoystickLoop;
|
||||
timerReadJoystick?.Dispose();
|
||||
joystick?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static Keys ReadKeyFromState(JoystickUpdate state)
|
||||
{
|
||||
Keys keys = Keys.None;
|
||||
switch (state.Offset)
|
||||
{
|
||||
case JoystickOffset.PointOfViewControllers0:
|
||||
switch (state.Value)
|
||||
{
|
||||
case 0:
|
||||
keys = Keys.Up;
|
||||
break;
|
||||
case 9000:
|
||||
keys = Keys.Right;
|
||||
break;
|
||||
case 18000:
|
||||
keys = Keys.Down;
|
||||
break;
|
||||
case 27000:
|
||||
keys = Keys.Left;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case JoystickOffset.Buttons0:
|
||||
if (state.Value == 128)
|
||||
{
|
||||
keys = Keys.Enter;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
private void ReadJoystickLoop(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
if (joystickHelperEnabled)
|
||||
{
|
||||
lock (lockRead)
|
||||
{
|
||||
timerReadJoystick.Stop();
|
||||
if (joystick == null)
|
||||
{
|
||||
Thread.Sleep(3000);
|
||||
InitializeJoystick();
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadJoystick();
|
||||
}
|
||||
|
||||
timerReadJoystick.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadJoystick()
|
||||
{
|
||||
try
|
||||
{
|
||||
joystick.Poll();
|
||||
JoystickUpdate[] datas = joystick.GetBufferedData();
|
||||
foreach (JoystickUpdate state in datas)
|
||||
{
|
||||
if (state.Value < 0)
|
||||
{
|
||||
pressingKey = Keys.None;
|
||||
pressingKeyCounter = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
Keys key = ReadKeyFromState(state);
|
||||
if (key != Keys.None)
|
||||
{
|
||||
KeyPressed?.Invoke(key);
|
||||
if (state.Offset == JoystickOffset.PointOfViewControllers0)
|
||||
{
|
||||
pressingKeyCounter = 0;
|
||||
pressingKey = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pressingKey != Keys.None)
|
||||
{
|
||||
pressingKeyCounter += 1;
|
||||
if (pressingKeyCounter > 1)
|
||||
{
|
||||
KeyPressed?.Invoke(pressingKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
joystick?.Dispose();
|
||||
joystick = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeJoystick()
|
||||
{
|
||||
// Initialize DirectInput
|
||||
DirectInput directInput = new();
|
||||
|
||||
// Find a Joystick Guid
|
||||
Guid joystickGuid = Guid.Empty;
|
||||
|
||||
foreach (DeviceInstance deviceInstance in directInput.GetDevices(
|
||||
DeviceType.Gamepad,
|
||||
DeviceEnumerationFlags.AllDevices))
|
||||
{
|
||||
joystickGuid = deviceInstance.InstanceGuid;
|
||||
}
|
||||
|
||||
// If Gamepad not found, look for a Joystick
|
||||
if (joystickGuid == Guid.Empty)
|
||||
{
|
||||
foreach (DeviceInstance deviceInstance in directInput.GetDevices(
|
||||
DeviceType.Joystick,
|
||||
DeviceEnumerationFlags.AllDevices))
|
||||
{
|
||||
joystickGuid = deviceInstance.InstanceGuid;
|
||||
}
|
||||
}
|
||||
|
||||
// If Joystick found
|
||||
if (joystickGuid != Guid.Empty)
|
||||
{
|
||||
// Instantiate the joystick
|
||||
joystick = new Joystick(directInput, joystickGuid);
|
||||
|
||||
// Set BufferSize in order to use buffered data.
|
||||
joystick.Properties.BufferSize = 128;
|
||||
|
||||
var handle = Process.GetCurrentProcess().MainWindowHandle;
|
||||
joystick.SetCooperativeLevel(handle, CooperativeLevel.NonExclusive | CooperativeLevel.Background);
|
||||
|
||||
// Acquire the joystick
|
||||
joystick.Acquire();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,16 +12,15 @@ namespace SystemTrayMenu.Helper
|
|||
/// </summary>
|
||||
internal class KeyPressedEventArgs : EventArgs
|
||||
{
|
||||
private readonly KeyboardHookModifierKeys modifier;
|
||||
private readonly Keys key;
|
||||
|
||||
internal KeyPressedEventArgs(KeyboardHookModifierKeys modifier, Keys key)
|
||||
{
|
||||
this.modifier = modifier;
|
||||
Modifier = modifier;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
internal KeyboardHookModifierKeys Modifier => modifier;
|
||||
internal KeyboardHookModifierKeys Modifier { get; }
|
||||
|
||||
internal Keys Key => key;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace SystemTrayMenu.Helper
|
|||
/// The enumeration of possible modifiers.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
internal enum KeyboardHookModifierKeys : uint
|
||||
public enum KeyboardHookModifierKeys : uint
|
||||
{
|
||||
None = 0,
|
||||
Alt = 1,
|
||||
|
@ -24,16 +24,13 @@ namespace SystemTrayMenu.Helper
|
|||
|
||||
public sealed class KeyboardHook : IDisposable
|
||||
{
|
||||
private readonly Window window = new Window();
|
||||
private readonly Window window = new();
|
||||
private int currentId;
|
||||
|
||||
public KeyboardHook()
|
||||
{
|
||||
// register the event of the inner native window.
|
||||
window.KeyPressed += (sender, args) =>
|
||||
{
|
||||
KeyPressed?.Invoke(this, args);
|
||||
};
|
||||
window.KeyPressed += Window_KeyPressed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -50,6 +47,7 @@ namespace SystemTrayMenu.Helper
|
|||
}
|
||||
|
||||
// dispose the inner native window.
|
||||
window.KeyPressed -= Window_KeyPressed;
|
||||
window.Dispose();
|
||||
}
|
||||
|
||||
|
@ -107,6 +105,11 @@ namespace SystemTrayMenu.Helper
|
|||
RegisterHotKey((uint)modifier, key);
|
||||
}
|
||||
|
||||
private void Window_KeyPressed(object sender, KeyPressedEventArgs e)
|
||||
{
|
||||
KeyPressed?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void RegisterHotKey(uint modifier, Keys key)
|
||||
{
|
||||
currentId += 1;
|
||||
|
|
251
Helpers/Updater/GitHubUpdate.cs
Normal file
|
@ -0,0 +1,251 @@
|
|||
// <copyright file="GitHubUpdate.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.Helper.Updater
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.Utilities;
|
||||
|
||||
public class GitHubUpdate
|
||||
{
|
||||
private static List<Dictionary<string, object>> releases;
|
||||
private static Form newVersionForm;
|
||||
|
||||
public static void ActivateNewVersionFormOrCheckForUpdates(bool showWhenUpToDate)
|
||||
{
|
||||
if (newVersionForm != null)
|
||||
{
|
||||
newVersionForm.HandleInvoke(newVersionForm.Activate);
|
||||
}
|
||||
else
|
||||
{
|
||||
CheckForUpdates(showWhenUpToDate);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckForUpdates(bool showWhenUpToDate)
|
||||
{
|
||||
string urlGithubReleases = @"http://api.github.com/repos/Hofknecht/SystemTrayMenu/releases";
|
||||
HttpClient client = new();
|
||||
|
||||
// https://developer.github.com/v3/#user-agent-required
|
||||
client.DefaultRequestHeaders.Add("User-Agent", "SystemTrayMenu/" + Application.ProductVersion.ToString());
|
||||
|
||||
// https://developer.github.com/v3/media/#request-specific-version
|
||||
client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3.text+json");
|
||||
|
||||
try
|
||||
{
|
||||
using HttpResponseMessage response = client.GetAsync(urlGithubReleases).Result;
|
||||
using HttpContent content = response.Content;
|
||||
string responseString = content.ReadAsStringAsync().Result;
|
||||
releases = responseString.FromJson<List<Dictionary<string, object>>>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"{nameof(CheckForUpdates)} failed", ex);
|
||||
}
|
||||
|
||||
if (releases == null)
|
||||
{
|
||||
Log.Info($"{nameof(CheckForUpdates)} failed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveCurrentAndOlderVersions();
|
||||
ShowNewVersionOrUpToDateDialog(showWhenUpToDate);
|
||||
}
|
||||
|
||||
newVersionForm?.Dispose();
|
||||
newVersionForm = null;
|
||||
}
|
||||
|
||||
private static void RemoveCurrentAndOlderVersions()
|
||||
{
|
||||
int releasesCount = releases.Count;
|
||||
Version versionCurrent = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
for (int i = 0; i < releasesCount; i++)
|
||||
{
|
||||
string tagName = releases[i]["tag_name"].ToString();
|
||||
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 (NewVersionDialog() == DialogResult.Yes)
|
||||
{
|
||||
Log.ProcessStart("https://github.com/Hofknecht/SystemTrayMenu/releases");
|
||||
}
|
||||
}
|
||||
else if (showWhenUpToDate)
|
||||
{
|
||||
MessageBox.Show(Translator.GetText("You have the latest version of SystemTrayMenu!"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a window to show changelog of new available versions.
|
||||
/// </summary>
|
||||
/// <param name="LatestVersionTitle">Name of latest release.</param>
|
||||
/// <param name="Changelog">Pathnotes.</param>
|
||||
/// <returns>OK = OK, Yes = Website, else = Cancel.</returns>
|
||||
private static DialogResult NewVersionDialog()
|
||||
{
|
||||
const int ClientPad = 15;
|
||||
newVersionForm = new()
|
||||
{
|
||||
StartPosition = FormStartPosition.CenterScreen,
|
||||
FormBorderStyle = FormBorderStyle.FixedDialog,
|
||||
Icon = Config.GetAppIcon(),
|
||||
ShowInTaskbar = false,
|
||||
};
|
||||
newVersionForm.FormBorderStyle = FormBorderStyle.Sizable;
|
||||
newVersionForm.MaximizeBox = true;
|
||||
newVersionForm.MinimizeBox = false;
|
||||
newVersionForm.ClientSize = new Size(600, 400);
|
||||
newVersionForm.MinimumSize = newVersionForm.ClientSize;
|
||||
newVersionForm.Text = Translator.GetText("New version available!");
|
||||
|
||||
Label label = new()
|
||||
{
|
||||
Size = new Size(newVersionForm.ClientSize.Width - ClientPad, 20),
|
||||
Location = new Point(ClientPad, ClientPad),
|
||||
Text = $"{Translator.GetText("Latest available version:")} {GetLatestVersionName()}",
|
||||
};
|
||||
newVersionForm.Controls.Add(label);
|
||||
|
||||
Button buttonOK = new()
|
||||
{
|
||||
DialogResult = DialogResult.OK,
|
||||
Name = "buttonOK",
|
||||
};
|
||||
buttonOK.Location = new Point(
|
||||
newVersionForm.ClientSize.Width - buttonOK.Size.Width - ClientPad,
|
||||
newVersionForm.ClientSize.Height - buttonOK.Size.Height - ClientPad);
|
||||
buttonOK.MinimumSize = new Size(75, 23);
|
||||
buttonOK.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
buttonOK.Text = Translator.GetText("OK");
|
||||
buttonOK.AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
||||
buttonOK.AutoSize = true;
|
||||
newVersionForm.Controls.Add(buttonOK);
|
||||
|
||||
Button buttonGoToDownloadPage = new()
|
||||
{
|
||||
DialogResult = DialogResult.Yes,
|
||||
Name = "buttonGoToDownloadPage",
|
||||
};
|
||||
buttonGoToDownloadPage.Location = new Point(
|
||||
newVersionForm.ClientSize.Width - buttonGoToDownloadPage.Size.Width - ClientPad - buttonOK.Size.Width - ClientPad,
|
||||
newVersionForm.ClientSize.Height - buttonGoToDownloadPage.Size.Height - ClientPad);
|
||||
buttonGoToDownloadPage.MinimumSize = new Size(75, 23);
|
||||
buttonGoToDownloadPage.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
buttonGoToDownloadPage.Text = Translator.GetText("Go to download page");
|
||||
buttonGoToDownloadPage.AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
||||
buttonGoToDownloadPage.AutoSize = true;
|
||||
newVersionForm.Controls.Add(buttonGoToDownloadPage);
|
||||
|
||||
TextBox textBox = new()
|
||||
{
|
||||
Location = new Point(ClientPad, label.Location.Y + label.Size.Height + 5),
|
||||
};
|
||||
textBox.Size = new Size(
|
||||
newVersionForm.ClientSize.Width - (ClientPad * 2),
|
||||
buttonOK.Location.Y - ClientPad - textBox.Location.Y);
|
||||
textBox.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
|
||||
textBox.Multiline = true;
|
||||
textBox.Text = GetChangelog();
|
||||
textBox.ReadOnly = true;
|
||||
textBox.ScrollBars = ScrollBars.Both;
|
||||
textBox.BackColor = Color.FromKnownColor(KnownColor.Window);
|
||||
textBox.ForeColor = Color.FromKnownColor(KnownColor.ControlText);
|
||||
newVersionForm.Controls.Add(textBox);
|
||||
|
||||
newVersionForm.AcceptButton = buttonOK;
|
||||
return newVersionForm.ShowDialog();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the latest release version name.
|
||||
/// </summary>
|
||||
/// <returns>Version name.</returns>
|
||||
private static string GetLatestVersionName()
|
||||
{
|
||||
string result = "Unknown";
|
||||
|
||||
if (releases == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
result = releases[0]["tag_name"].ToString().Replace("v", string.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"{nameof(GetLatestVersionName)} failed", ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the change log from current version up to the latest release version.
|
||||
/// </summary>
|
||||
/// <returns>Change log summary or error text.</returns>
|
||||
private static string GetChangelog()
|
||||
{
|
||||
string result = string.Empty;
|
||||
string errorstr = "An error occurred during update check!" + Environment.NewLine;
|
||||
|
||||
if (releases == null)
|
||||
{
|
||||
return errorstr + "Could not receive changelog!";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < releases.Count; i++)
|
||||
{
|
||||
Dictionary<string, object> release = releases[i];
|
||||
|
||||
result += release["name"].ToString()
|
||||
+ Environment.NewLine
|
||||
+ release["body_text"].ToString()
|
||||
.Replace("\n\n", Environment.NewLine)
|
||||
.Replace("\n \n", Environment.NewLine)
|
||||
+ Environment.NewLine + Environment.NewLine;
|
||||
if (i < releases.Count)
|
||||
{
|
||||
result += "--------------------------------------------------" +
|
||||
"-------------------------------------------------------"
|
||||
+ Environment.NewLine;
|
||||
}
|
||||
}
|
||||
|
||||
result = result.Replace("\n", Environment.NewLine);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"{nameof(GetChangelog)}", ex);
|
||||
result = errorstr + ex.Message.ToString();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
480
Helpers/Updater/JsonParser.cs
Normal file
|
@ -0,0 +1,480 @@
|
|||
// <copyright file="JsonParser.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.Helper.Updater
|
||||
{
|
||||
// Copyright (c) 2018 Alex Parker
|
||||
// Copyright (c) 2018-2019 Peter Kirmeier
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
|
||||
// Really simple JSON parser in ~300 lines
|
||||
// - Attempts to parse JSON files with minimal GC allocation
|
||||
// - Nice and simple "[1,2,3]".FromJson<List<int>>() API
|
||||
// - Classes and structs can be parsed too!
|
||||
// class Foo { public int Value; }
|
||||
// "{\"Value\":10}".FromJson<Foo>()
|
||||
// - Can parse JSON without type information into Dictionary<string,object> and List<object> e.g.
|
||||
// "[1,2,3]".FromJson<object>().GetType() == typeof(List<object>)
|
||||
// "{\"Value\":10}".FromJson<object>().GetType() == typeof(Dictionary<string,object>)
|
||||
// - No JIT Emit support to support AOT compilation on iOS
|
||||
// - Attempts are made to NOT throw an exception if the JSON is corrupted or invalid: returns null instead.
|
||||
// - Only public fields and property setters on classes/structs will be written to
|
||||
//
|
||||
// Limitations:
|
||||
// - No JIT Emit support to parse structures quickly
|
||||
// - Limited to parsing <2GB JSON files (due to int.MaxValue)
|
||||
// - Parsing of abstract classes or interfaces is NOT supported and will throw an exception.
|
||||
public static class JSONParser
|
||||
{
|
||||
[ThreadStatic]
|
||||
private static Stack<List<string>> splitArrayPool;
|
||||
[ThreadStatic]
|
||||
private static StringBuilder stringBuilder;
|
||||
[ThreadStatic]
|
||||
private static Dictionary<Type, Dictionary<string, FieldInfo>> fieldInfoCache;
|
||||
[ThreadStatic]
|
||||
private static Dictionary<Type, Dictionary<string, PropertyInfo>> propertyInfoCache;
|
||||
|
||||
public static T FromJson<T>(this string json)
|
||||
{
|
||||
// Initialize, if needed, the ThreadStatic variables
|
||||
propertyInfoCache ??= new Dictionary<Type, Dictionary<string, PropertyInfo>>();
|
||||
|
||||
fieldInfoCache ??= new Dictionary<Type, Dictionary<string, FieldInfo>>();
|
||||
|
||||
stringBuilder ??= new StringBuilder();
|
||||
|
||||
splitArrayPool ??= new Stack<List<string>>();
|
||||
|
||||
// Remove all whitespace not within strings to make parsing simpler
|
||||
stringBuilder.Length = 0;
|
||||
for (int i = 0; i < json.Length; i++)
|
||||
{
|
||||
char c = json[i];
|
||||
if (c == '"')
|
||||
{
|
||||
i = AppendUntilStringEnd(true, i, json);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char.IsWhiteSpace(c))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
stringBuilder.Append(c);
|
||||
}
|
||||
|
||||
// Parse the thing!
|
||||
return (T)ParseValue(typeof(T), stringBuilder.ToString());
|
||||
}
|
||||
|
||||
internal static object ParseValue(Type type, string json)
|
||||
{
|
||||
if (type == typeof(string))
|
||||
{
|
||||
if (json.Length <= 2)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
StringBuilder parseStringBuilder = new(json.Length);
|
||||
for (int i = 1; i < json.Length - 1; ++i)
|
||||
{
|
||||
if (json[i] == '\\' && i + 1 < json.Length - 1)
|
||||
{
|
||||
int j = "\"\\nrtbf/".IndexOf(json[i + 1]);
|
||||
if (j >= 0)
|
||||
{
|
||||
parseStringBuilder.Append("\"\\\n\r\t\b\f/"[j]);
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (json[i + 1] == 'u' && i + 5 < json.Length - 1)
|
||||
{
|
||||
if (uint.TryParse(json.AsSpan(i + 2, 4), System.Globalization.NumberStyles.AllowHexSpecifier, null, out uint c))
|
||||
{
|
||||
parseStringBuilder.Append((char)c);
|
||||
i += 5;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseStringBuilder.Append(json[i]);
|
||||
}
|
||||
|
||||
return parseStringBuilder.ToString();
|
||||
}
|
||||
|
||||
if (type.IsPrimitive)
|
||||
{
|
||||
var result = Convert.ChangeType(json, type, System.Globalization.CultureInfo.InvariantCulture);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (type == typeof(decimal))
|
||||
{
|
||||
decimal.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out decimal result);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (json == "null")
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (type.IsEnum)
|
||||
{
|
||||
if (json[0] == '"')
|
||||
{
|
||||
json = json[1..^1];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return Enum.Parse(type, json, false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (type.IsArray)
|
||||
{
|
||||
Type arrayType = type.GetElementType();
|
||||
if (json[0] != '[' || json[^1] != ']')
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
List<string> elems = Split(json);
|
||||
Array newArray = Array.CreateInstance(arrayType, elems.Count);
|
||||
for (int i = 0; i < elems.Count; i++)
|
||||
{
|
||||
newArray.SetValue(ParseValue(arrayType, elems[i]), i);
|
||||
}
|
||||
|
||||
splitArrayPool.Push(elems);
|
||||
return newArray;
|
||||
}
|
||||
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
|
||||
{
|
||||
Type listType = type.GetGenericArguments()[0];
|
||||
if (json[0] != '[' || json[^1] != ']')
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
List<string> elems = Split(json);
|
||||
var list = (IList)type.GetConstructor(new Type[] { typeof(int) }).Invoke(new object[] { elems.Count });
|
||||
for (int i = 0; i < elems.Count; i++)
|
||||
{
|
||||
list.Add(ParseValue(listType, elems[i]));
|
||||
}
|
||||
|
||||
splitArrayPool.Push(elems);
|
||||
return list;
|
||||
}
|
||||
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
|
||||
{
|
||||
Type keyType, valueType;
|
||||
{
|
||||
Type[] args = type.GetGenericArguments();
|
||||
keyType = args[0];
|
||||
valueType = args[1];
|
||||
}
|
||||
|
||||
// Refuse to parse dictionary keys that aren't of type string
|
||||
if (keyType != typeof(string))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Must be a valid dictionary element
|
||||
if (json[0] != '{' || json[^1] != '}')
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON
|
||||
List<string> elems = Split(json);
|
||||
if (elems.Count % 2 != 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var dictionary = (IDictionary)type.GetConstructor(new Type[] { typeof(int) }).Invoke(new object[] { elems.Count / 2 });
|
||||
for (int i = 0; i < elems.Count; i += 2)
|
||||
{
|
||||
if (elems[i].Length <= 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string keyValue = elems[i][1..^1];
|
||||
object val = ParseValue(valueType, elems[i + 1]);
|
||||
dictionary.Add(keyValue, val);
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
if (type == typeof(object))
|
||||
{
|
||||
return ParseAnonymousValue(json);
|
||||
}
|
||||
|
||||
if (json[0] == '{' && json[^1] == '}')
|
||||
{
|
||||
return ParseObject(type, json);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int AppendUntilStringEnd(bool appendEscapeCharacter, int startIdx, string json)
|
||||
{
|
||||
stringBuilder.Append(json[startIdx]);
|
||||
for (int i = startIdx + 1; i < json.Length; i++)
|
||||
{
|
||||
if (json[i] == '\\')
|
||||
{
|
||||
if (appendEscapeCharacter)
|
||||
{
|
||||
stringBuilder.Append(json[i]);
|
||||
}
|
||||
|
||||
stringBuilder.Append(json[i + 1]);
|
||||
i++; // Skip next character as it is escaped
|
||||
}
|
||||
else if (json[i] == '"')
|
||||
{
|
||||
stringBuilder.Append(json[i]);
|
||||
return i;
|
||||
}
|
||||
else
|
||||
{
|
||||
stringBuilder.Append(json[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return json.Length - 1;
|
||||
}
|
||||
|
||||
// Splits { <value>:<value>, <value>:<value> } and [ <value>, <value> ] into a list of <value> strings
|
||||
private static List<string> Split(string json)
|
||||
{
|
||||
List<string> splitArray = splitArrayPool.Count > 0 ? splitArrayPool.Pop() : new List<string>();
|
||||
splitArray.Clear();
|
||||
if (json.Length == 2)
|
||||
{
|
||||
return splitArray;
|
||||
}
|
||||
|
||||
int parseDepth = 0;
|
||||
stringBuilder.Length = 0;
|
||||
for (int i = 1; i < json.Length - 1; i++)
|
||||
{
|
||||
switch (json[i])
|
||||
{
|
||||
case '[':
|
||||
case '{':
|
||||
parseDepth++;
|
||||
break;
|
||||
case ']':
|
||||
case '}':
|
||||
parseDepth--;
|
||||
break;
|
||||
case '"':
|
||||
i = AppendUntilStringEnd(true, i, json);
|
||||
continue;
|
||||
case ',':
|
||||
case ':':
|
||||
if (parseDepth == 0)
|
||||
{
|
||||
splitArray.Add(stringBuilder.ToString());
|
||||
stringBuilder.Length = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
stringBuilder.Append(json[i]);
|
||||
}
|
||||
|
||||
splitArray.Add(stringBuilder.ToString());
|
||||
|
||||
return splitArray;
|
||||
}
|
||||
|
||||
private static object ParseAnonymousValue(string json)
|
||||
{
|
||||
if (json.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (json[0] == '{' && json[^1] == '}')
|
||||
{
|
||||
List<string> elems = Split(json);
|
||||
if (elems.Count % 2 != 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var dict = new Dictionary<string, object>(elems.Count / 2);
|
||||
for (int i = 0; i < elems.Count; i += 2)
|
||||
{
|
||||
dict.Add(elems[i][1..^1], ParseAnonymousValue(elems[i + 1]));
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
if (json[0] == '[' && json[^1] == ']')
|
||||
{
|
||||
List<string> items = Split(json);
|
||||
var finalList = new List<object>(items.Count);
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
finalList.Add(ParseAnonymousValue(items[i]));
|
||||
}
|
||||
|
||||
return finalList;
|
||||
}
|
||||
|
||||
if (json[0] == '"' && json[^1] == '"')
|
||||
{
|
||||
return ParseValue(typeof(string), json); // fix https://github.com/zanders3/json/issues/29
|
||||
}
|
||||
|
||||
if (char.IsDigit(json[0]) || json[0] == '-')
|
||||
{
|
||||
if (json.Contains('.'))
|
||||
{
|
||||
double.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out double result);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = int.TryParse(json, out int result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (json == "true")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (json == "false")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// handles json == "null" as well as invalid JSON
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Dictionary<string, T> CreateMemberNameDictionary<T>(T[] members)
|
||||
where T : MemberInfo
|
||||
{
|
||||
Dictionary<string, T> nameToMember = new(StringComparer.OrdinalIgnoreCase);
|
||||
for (int i = 0; i < members.Length; i++)
|
||||
{
|
||||
/*T member = members[i];
|
||||
if (member.IsDefined(typeof(IgnoreDataMemberAttribute), true))
|
||||
continue;
|
||||
|
||||
string name = member.Name;
|
||||
if (member.IsDefined(typeof(DataMemberAttribute), true))
|
||||
{
|
||||
DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute), true);
|
||||
if (!string.IsNullOrEmpty(dataMemberAttribute.Name))
|
||||
name = dataMemberAttribute.Name;
|
||||
}
|
||||
|
||||
nameToMember.Add(name, member);*/
|
||||
// The above code is not working with .Net framework 2.0, so we ignore these attributes for compatibility reasons:
|
||||
nameToMember.Add(members[i].Name, members[i]);
|
||||
}
|
||||
|
||||
return nameToMember;
|
||||
}
|
||||
|
||||
private static object ParseObject(Type type, string json)
|
||||
{
|
||||
object instance = FormatterServices.GetUninitializedObject(type);
|
||||
|
||||
// The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON
|
||||
List<string> elems = Split(json);
|
||||
if (elems.Count % 2 != 0)
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
if (!fieldInfoCache.TryGetValue(type, out Dictionary<string, FieldInfo> nameToField))
|
||||
{
|
||||
nameToField = CreateMemberNameDictionary(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
|
||||
fieldInfoCache.Add(type, nameToField);
|
||||
}
|
||||
|
||||
if (!propertyInfoCache.TryGetValue(type, out Dictionary<string, PropertyInfo> nameToProperty))
|
||||
{
|
||||
nameToProperty = CreateMemberNameDictionary(type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
|
||||
propertyInfoCache.Add(type, nameToProperty);
|
||||
}
|
||||
|
||||
for (int i = 0; i < elems.Count; i += 2)
|
||||
{
|
||||
if (elems[i].Length <= 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string key = elems[i][1..^1];
|
||||
string value = elems[i + 1];
|
||||
|
||||
if (nameToField.TryGetValue(key, out FieldInfo fieldInfo))
|
||||
{
|
||||
fieldInfo.SetValue(instance, ParseValue(fieldInfo.FieldType, value));
|
||||
}
|
||||
else if (nameToProperty.TryGetValue(key, out PropertyInfo propertyInfo))
|
||||
{
|
||||
propertyInfo.SetValue(instance, ParseValue(propertyInfo.PropertyType, value), null);
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ namespace SystemTrayMenu.Helper
|
|||
{
|
||||
IntPtr taskbarHandle = User32FindWindow(ClassName, null);
|
||||
|
||||
APPBARDATA data = new APPBARDATA
|
||||
APPBARDATA data = new()
|
||||
{
|
||||
cbSize = (uint)Marshal.SizeOf(typeof(APPBARDATA)),
|
||||
hWnd = taskbarHandle,
|
||||
|
|
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 - browse and open your files easily
|
||||
Copyright (C) 2021 Markus Hofknecht
|
||||
Copyright (C) 2022 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) 2021 Markus Hofknecht
|
||||
TAMAHO SystemTrayMenu Copyright (C) 2022 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.
|
||||
|
|
40
NativeDllImport/CreateRoundRectRgn.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
// <copyright file="CreateRoundRectRgn.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/// <summary>
|
||||
/// wraps the methodcalls to native windows dll's.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
public static bool GetRegionRoundCorners(int width, int height, int widthEllipse, int heightEllipse, out System.Drawing.Region region)
|
||||
{
|
||||
bool success = false;
|
||||
region = null;
|
||||
|
||||
IntPtr handle = CreateRoundRectRgn(0, 0, width, height, widthEllipse, heightEllipse);
|
||||
if (handle != IntPtr.Zero)
|
||||
{
|
||||
region = System.Drawing.Region.FromHrgn(handle);
|
||||
_ = DeleteObject(handle);
|
||||
success = true;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
[DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr CreateRoundRectRgn(
|
||||
int nLeftRect, // x-coordinate of upper-left corner
|
||||
int nTopRect, // y-coordinate of upper-left corner
|
||||
int nRightRect, // x-coordinate of lower-right corner
|
||||
int nBottomRect, // y-coordinate of lower-right corner
|
||||
int nWidthEllipse, // width of ellipse
|
||||
int nHeightEllipse); // height of ellipse
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// <copyright file="GetDeviceCaps.cs" company="PlaceholderCompany">
|
||||
// <copyright file="DeleteObject.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
|
@ -12,13 +12,8 @@ namespace SystemTrayMenu.DllImports
|
|||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
public static int Gdi32GetDeviceCaps(IntPtr hdc, int nIndex)
|
||||
{
|
||||
return GetDeviceCaps(hdc, nIndex);
|
||||
}
|
||||
|
||||
[DllImport("gdi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
|
||||
private static extern int DeleteObject(IntPtr hIcon);
|
||||
}
|
||||
}
|
|
@ -12,7 +12,6 @@ namespace SystemTrayMenu.DllImports
|
|||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
#pragma warning disable SA1600 // Elements should be documented
|
||||
public const uint ShgfiIcon = 0x000000100; // get icon
|
||||
public const uint ShgfiSYSICONINDEX = 0x000004000; // get system icon index
|
||||
public const uint ShgfiLINKOVERLAY = 0x000008000; // put a link overlay on icon
|
||||
|
@ -22,16 +21,6 @@ namespace SystemTrayMenu.DllImports
|
|||
public const uint FileAttributeDirectory = 0x00000010;
|
||||
public const uint FileAttributeNormal = 0x00000080;
|
||||
public const int IldTransparent = 0x00000001;
|
||||
#pragma warning restore SA1600 // Elements should be documented
|
||||
|
||||
/// <summary>
|
||||
/// comctl32 ImageList_GetIcon(IntPtr hIcon).
|
||||
/// </summary>
|
||||
/// <param name="hIcon">hIcon.</param>
|
||||
public static void Comctl32ImageListGetIcon(IntPtr hIcon)
|
||||
{
|
||||
_ = DestroyIcon(hIcon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// comctl32 ImageList_GetIcon(IntPtr himl, int i, int flags).
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
// <copyright file="GetMenuDefaultItem.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/// <summary>
|
||||
/// wraps the methodcalls to native windows dll's.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
public static int User32GetMenuDefaultItem(IntPtr hMenu, bool fByPos, uint gmdiFlags)
|
||||
{
|
||||
return GetMenuDefaultItem(hMenu, fByPos, gmdiFlags);
|
||||
}
|
||||
|
||||
// Determines the default menu item on the specified menu
|
||||
[DllImport("user32", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern int GetMenuDefaultItem(IntPtr hMenu, bool fByPos, uint gmdiFlags);
|
||||
}
|
||||
}
|
|
@ -18,7 +18,6 @@ namespace SystemTrayMenu.DllImports
|
|||
[Flags]
|
||||
internal enum TPM : uint
|
||||
{
|
||||
#pragma warning disable SA1602 // Enumeration items should be documented
|
||||
LEFTBUTTON = 0x0000, // LEFTALIGN = 0x0000, // TOPALIGN = 0x0000, // HORIZONTAL = 0x0000,
|
||||
RIGHTBUTTON = 0x0002,
|
||||
CENTERALIGN = 0x0004,
|
||||
|
@ -35,7 +34,6 @@ namespace SystemTrayMenu.DllImports
|
|||
VERNEGANIMATION = 0x2000,
|
||||
NOANIMATION = 0x4000,
|
||||
LAYOUTRTL = 0x8000,
|
||||
#pragma warning restore SA1602 // Enumeration items should be documented
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
Before Width: | Height: | Size: 241 B After Width: | Height: | Size: 170 B |
Before Width: | Height: | Size: 305 B After Width: | Height: | Size: 182 B |
Before Width: | Height: | Size: 377 B After Width: | Height: | Size: 213 B |
Before Width: | Height: | Size: 488 B After Width: | Height: | Size: 444 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 860 B |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 472 KiB After Width: | Height: | Size: 267 KiB |
BIN
Packaging/Images/SplashScreen.scale-400.xcf
Normal file
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 8 KiB After Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 884 B After Width: | Height: | Size: 732 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 4 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 884 B After Width: | Height: | Size: 732 B |
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 4 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 637 B After Width: | Height: | Size: 517 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 883 B |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 17 KiB |
|
@ -3,13 +3,14 @@
|
|||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
IgnorableNamespaces="uap rescap">
|
||||
|
||||
<Identity
|
||||
Name="49543SystemTrayMenu.SystemTrayMenu"
|
||||
Publisher="CN=5884501C-92ED-45DE-9508-9D987C314243"
|
||||
Version="1.0.17.0" />
|
||||
Version="1.3.5.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>SystemTrayMenu</DisplayName>
|
||||
|
@ -40,6 +41,15 @@
|
|||
<uap:SplashScreen Image="Images\SplashScreen.png" />
|
||||
<uap:LockScreen BadgeLogo="Images\BadgeLogo.png" Notification="badge"/>
|
||||
</uap:VisualElements>
|
||||
<Extensions>
|
||||
<uap5:Extension
|
||||
Category="windows.startupTask">
|
||||
<uap5:StartupTask
|
||||
TaskId="MyStartupId"
|
||||
Enabled="false"
|
||||
DisplayName="SystemTrayMenu" />
|
||||
</uap5:Extension>
|
||||
</Extensions>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
|
|
|
@ -47,17 +47,18 @@
|
|||
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.props" />
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>01d77f37-786a-4dc4-a1ad-bc1eec54eae3</ProjectGuid>
|
||||
<TargetPlatformVersion>10.0.19041.0</TargetPlatformVersion>
|
||||
<TargetPlatformVersion>10.0.22000.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<AppxPackageSigningEnabled>false</AppxPackageSigningEnabled>
|
||||
<EntryPointProjectUniqueName>..\SystemTrayMenu.csproj</EntryPointProjectUniqueName>
|
||||
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
|
||||
<AppxAutoIncrementPackageRevision>True</AppxAutoIncrementPackageRevision>
|
||||
<GenerateTestArtifacts>True</GenerateTestArtifacts>
|
||||
<AppxBundlePlatforms>x86|x64</AppxBundlePlatforms>
|
||||
<AppInstallerUri>https://github.com/Hofknecht/SystemTrayMenu/releases</AppInstallerUri>
|
||||
<AppxSymbolPackageEnabled>True</AppxSymbolPackageEnabled>
|
||||
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
|
||||
<AppxPackageDir>C:\Users\Marku\Desktop\</AppxPackageDir>
|
||||
<GenerateTestArtifacts>True</GenerateTestArtifacts>
|
||||
<HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks>
|
||||
<EntryPointProjectUniqueName>..\SystemTrayMenu.csproj</EntryPointProjectUniqueName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<AppxBundle>Always</AppxBundle>
|
||||
|
@ -91,6 +92,12 @@
|
|||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Package.StoreAssociation.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Images\BadgeLogo.scale-100.png" />
|
||||
<Content Include="Images\BadgeLogo.scale-125.png" />
|
||||
|
@ -111,7 +118,6 @@
|
|||
<Content Include="Images\SplashScreen.scale-125.png" />
|
||||
<Content Include="Images\SplashScreen.scale-150.png" />
|
||||
<Content Include="Images\SplashScreen.scale-200.png" />
|
||||
<Content Include="Images\LockScreenLogo.scale-200.png" />
|
||||
<Content Include="Images\SplashScreen.scale-400.png" />
|
||||
<Content Include="Images\Square150x150Logo.scale-100.png" />
|
||||
<Content Include="Images\Square150x150Logo.scale-125.png" />
|
||||
|
@ -124,6 +130,7 @@
|
|||
<Content Include="Images\Square44x44Logo.altform-lightunplated_targetsize-32.png" />
|
||||
<Content Include="Images\Square44x44Logo.altform-lightunplated_targetsize-48.png" />
|
||||
<Content Include="Images\Square44x44Logo.altform-unplated_targetsize-16.png" />
|
||||
<Content Include="Images\Square44x44Logo.altform-unplated_targetsize-24.png" />
|
||||
<Content Include="Images\Square44x44Logo.altform-unplated_targetsize-256.png" />
|
||||
<Content Include="Images\Square44x44Logo.altform-unplated_targetsize-32.png" />
|
||||
<Content Include="Images\Square44x44Logo.altform-unplated_targetsize-48.png" />
|
||||
|
@ -134,7 +141,6 @@
|
|||
<Content Include="Images\Square44x44Logo.scale-400.png" />
|
||||
<Content Include="Images\Square44x44Logo.targetsize-16.png" />
|
||||
<Content Include="Images\Square44x44Logo.targetsize-24.png" />
|
||||
<Content Include="Images\Square44x44Logo.targetsize-24_altform-unplated.png" />
|
||||
<Content Include="Images\Square44x44Logo.targetsize-256.png" />
|
||||
<Content Include="Images\Square44x44Logo.targetsize-32.png" />
|
||||
<Content Include="Images\Square44x44Logo.targetsize-48.png" />
|
||||
|
@ -148,12 +154,9 @@
|
|||
<Content Include="Images\Wide310x150Logo.scale-150.png" />
|
||||
<Content Include="Images\Wide310x150Logo.scale-200.png" />
|
||||
<Content Include="Images\Wide310x150Logo.scale-400.png" />
|
||||
<None Include="Package.StoreAssociation.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SystemTrayMenu.csproj">
|
||||
<SkipGetTargetFrameworkProperties>True</SkipGetTargetFrameworkProperties>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SystemTrayMenu.csproj" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.targets" />
|
||||
</Project>
|
|
@ -14,7 +14,7 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("TAMAHO")]
|
||||
[assembly: AssemblyProduct("TAMAHO SystemTrayMenu")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2021, TAMAHO SystemTrayMenu")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2022, TAMAHO SystemTrayMenu")]
|
||||
[assembly: AssemblyTrademark("TAMAHO")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
|
@ -39,5 +39,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("1.0.17.32")]
|
||||
[assembly: AssemblyFileVersion("1.0.17.32")]
|
||||
[assembly: AssemblyVersion("1.3.5.0")]
|
||||
[assembly: AssemblyFileVersion("1.3.5.0")]
|
||||
|
|
|
@ -10,7 +10,9 @@ namespace SystemTrayMenu.Properties
|
|||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Xml.Linq;
|
||||
using SystemTrayMenu.Utilities;
|
||||
|
||||
internal class CustomSettingsProvider : SettingsProvider
|
||||
{
|
||||
|
@ -31,12 +33,26 @@ namespace SystemTrayMenu.Properties
|
|||
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 => Path.Combine(
|
||||
Directory.GetParent(Assembly.GetEntryAssembly().Location).FullName,
|
||||
$"user.config");
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets override.
|
||||
/// </summary>
|
||||
public override string ApplicationName
|
||||
{
|
||||
get => System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name;
|
||||
get => Assembly.GetExecutingAssembly().ManifestModule.Name;
|
||||
|
||||
set
|
||||
{
|
||||
|
@ -44,21 +60,36 @@ namespace SystemTrayMenu.Properties
|
|||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
private static string UserConfigPath => Path.Combine(
|
||||
Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
$"SystemTrayMenu"),
|
||||
$"user-{Environment.MachineName}.config");
|
||||
|
||||
/// <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>
|
||||
|
@ -85,12 +116,12 @@ namespace SystemTrayMenu.Properties
|
|||
}
|
||||
|
||||
// collection that will be returned.
|
||||
SettingsPropertyValueCollection values = new SettingsPropertyValueCollection();
|
||||
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 SettingsPropertyValue(setting)
|
||||
SettingsPropertyValue value = new(setting)
|
||||
{
|
||||
IsDirty = false,
|
||||
};
|
||||
|
@ -126,7 +157,7 @@ namespace SystemTrayMenu.Properties
|
|||
// grab the values from the collection parameter and update the values in our dictionary.
|
||||
foreach (SettingsPropertyValue value in collection)
|
||||
{
|
||||
SettingStruct setting = new SettingStruct()
|
||||
SettingStruct setting = new()
|
||||
{
|
||||
Value = value.PropertyValue == null ? string.Empty : value.PropertyValue.ToString(),
|
||||
Name = value.Name,
|
||||
|
@ -151,18 +182,69 @@ namespace SystemTrayMenu.Properties
|
|||
/// 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 CreateEmptyConfig()
|
||||
private static void CreateEmptyConfigIfNotExists(string path)
|
||||
{
|
||||
XDocument doc = new XDocument();
|
||||
XDeclaration declaration = new XDeclaration("1.0", "utf-8", "true");
|
||||
XElement config = new XElement(Config);
|
||||
XElement userSettings = new XElement(UserSettings);
|
||||
XElement group = new XElement(typeof(Properties.Settings).FullName);
|
||||
userSettings.Add(group);
|
||||
config.Add(userSettings);
|
||||
doc.Add(config);
|
||||
doc.Declaration = declaration;
|
||||
doc.Save(UserConfigPath);
|
||||
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;
|
||||
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>
|
||||
|
@ -170,29 +252,36 @@ namespace SystemTrayMenu.Properties
|
|||
/// </summary>
|
||||
private void LoadValuesFromFile()
|
||||
{
|
||||
if (!File.Exists(UserConfigPath))
|
||||
{
|
||||
// if the config file is not where it's supposed to be create a new one.
|
||||
CreateEmptyConfig();
|
||||
}
|
||||
CreateEmptyConfigIfNotExists(UserConfigPath);
|
||||
|
||||
// load the xml
|
||||
XDocument configXml = XDocument.Load(UserConfigPath);
|
||||
|
||||
// get all of the <setting name="..." serializeAs="..."> elements.
|
||||
IEnumerable<XElement> settingElements = configXml.Element(Config).Element(UserSettings).Element(typeof(Properties.Settings).FullName).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.
|
||||
foreach (XElement element in settingElements)
|
||||
XDocument configXml;
|
||||
if (IsConfigPathAssembly())
|
||||
{
|
||||
SettingStruct newSetting = new SettingStruct()
|
||||
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)
|
||||
// using "String" as default serializeAs...just in case, no real good reason.
|
||||
foreach (XElement element in settingElements)
|
||||
{
|
||||
Name = element.Attribute(NameOf) == null ? string.Empty : element.Attribute(NameOf).Value,
|
||||
SerializeAs = element.Attribute(SerializeAs) == null ? "String" : element.Attribute(SerializeAs).Value,
|
||||
Value = element.Value ?? string.Empty,
|
||||
};
|
||||
SettingsDictionary.Add(element.Attribute(NameOf).Value, newSetting);
|
||||
SettingStruct newSetting = new()
|
||||
{
|
||||
Name = element.Attribute(NameOf) == null ? string.Empty : element.Attribute(NameOf).Value,
|
||||
SerializeAs = element.Attribute(SerializeAs) == null ? "String" : element.Attribute(SerializeAs).Value,
|
||||
Value = element.Value ?? string.Empty,
|
||||
};
|
||||
SettingsDictionary.Add(element.Attribute(NameOf).Value, newSetting);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -202,32 +291,48 @@ namespace SystemTrayMenu.Properties
|
|||
private void SaveValuesToFile()
|
||||
{
|
||||
// load the current xml from the file.
|
||||
XDocument import = XDocument.Load(UserConfigPath);
|
||||
|
||||
// get the settings group (e.g. <Company.Project.Desktop.Settings>)
|
||||
XElement settingsSection = import.Element(Config).Element(UserSettings).Element(typeof(Properties.Settings).FullName);
|
||||
|
||||
// iterate though the dictionary, either updating the value or adding the new setting.
|
||||
foreach (KeyValuePair<string, SettingStruct> entry in SettingsDictionary)
|
||||
XDocument configXml;
|
||||
if (IsConfigPathAssembly())
|
||||
{
|
||||
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 XElement(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;
|
||||
}
|
||||
configXml = LoadOrGetNew(ConfigPathAssembly);
|
||||
}
|
||||
else
|
||||
{
|
||||
configXml = LoadOrGetNew(UserConfigPath);
|
||||
}
|
||||
|
||||
import.Save(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>
|
||||
|
|
234
Properties/Resources.Designer.cs
generated
|
@ -19,7 +19,7 @@ namespace SystemTrayMenu.Properties {
|
|||
// 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.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Resources {
|
||||
|
@ -60,12 +60,72 @@ namespace SystemTrayMenu.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
public static byte[] ic_fluent_arrow_sync_24_regular {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("ic_fluent_arrow_sync_24_regular", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
public static byte[] ic_fluent_folder_arrow_right_48_regular {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("ic_fluent_folder_arrow_right_48_regular", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
public static byte[] ic_fluent_pin_48_filled {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("ic_fluent_pin_48_filled", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
public static byte[] ic_fluent_pin_48_regular {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("ic_fluent_pin_48_regular", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
public static byte[] ic_fluent_search_48_regular {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("ic_fluent_search_48_regular", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
public static byte[] ic_fluent_settings_28_regular {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("ic_fluent_settings_28_regular", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L010 {
|
||||
public static System.Drawing.Icon LinkArrow {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L010", resourceCulture);
|
||||
object obj = ResourceManager.GetObject("LinkArrow", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
@ -73,9 +133,9 @@ namespace SystemTrayMenu.Properties {
|
|||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L020 {
|
||||
public static System.Drawing.Icon Loading {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L020", resourceCulture);
|
||||
object obj = ResourceManager.GetObject("Loading", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
@ -83,169 +143,9 @@ namespace SystemTrayMenu.Properties {
|
|||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L030 {
|
||||
public static System.Drawing.Icon NotFound {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L030", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L040 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L040", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L050 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L050", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L060 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L060", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L070 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L070", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L080 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L080", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L090 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L090", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L100 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L100", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L110 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L110", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L120 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L120", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L130 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L130", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L140 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L140", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L150 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L150", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L160 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L160", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L170 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L170", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon L180 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("L180", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
public static System.Drawing.Icon search {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("search", resourceCulture);
|
||||
object obj = ResourceManager.GetObject("NotFound", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,67 +118,37 @@
|
|||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="L080" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L080.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="L020" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L020.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="L070" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L070.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="L140" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L140.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="L160" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L160.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="L100" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L100.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="L060" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L060.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="L180" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L180.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="White50Percentage" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\White50Percentage.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="L150" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L150.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="L120" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L120.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="L170" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L170.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="SystemTrayMenu" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\SystemTrayMenu.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="L090" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L090.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
<data name="ic_fluent_pin_48_filled" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\ic_fluent_pin_48_filled.svg;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="L040" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L040.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
<data name="ic_fluent_pin_48_regular" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\ic_fluent_pin_48_regular.svg;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="L010" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L010.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
<data name="ic_fluent_search_48_regular" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\ic_fluent_search_48_regular.svg;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="L030" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L030.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
<data name="ic_fluent_folder_arrow_right_48_regular" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\ic_fluent_folder_arrow_right_48_regular.svg;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="L050" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L050.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
<data name="NotFound" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\NotFound.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="L130" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L130.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
<data name="Loading" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Loading.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="L110" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LoadingIcon\L110.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
<data name="ic_fluent_arrow_sync_24_regular" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\ic_fluent_arrow_sync_24_regular.svg;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="search" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\search.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
<data name="ic_fluent_settings_28_regular" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\ic_fluent_settings_28_regular.svg;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="LinkArrow" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\LinkArrow.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
</root>
|
1447
Properties/Settings.Designer.cs
generated
|
@ -1,27 +1,5 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="SystemTrayMenu.Properties" GeneratedClassName="Settings">
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||
<Profiles />
|
||||
<Settings>
|
||||
<Setting Name="CurrentCultureInfoName" Provider="CustomSettingsProvider" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="PathDirectory" Provider="CustomSettingsProvider" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="IsAutostartActivated" Provider="CustomSettingsProvider" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="HotKey" Provider="CustomSettingsProvider" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)">Ctrl+LWin</Value>
|
||||
</Setting>
|
||||
<Setting Name="IsUpgraded" Provider="CustomSettingsProvider" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="OpenItemWithOneClick" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="IsDarkModeAlwaysOn" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Settings />
|
||||
</SettingsFile>
|
327
README.md
|
@ -1,65 +1,201 @@
|
|||
SystemTrayMenu<img src="https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/master/Resources/SystemTrayMenu.ico" alt="Trulli" width="24" height="24">
|
||||
------------------
|
||||
|
||||
[![Build Status](https://dev.azure.com/MarkusHofknecht/SystemTrayMenu/_apis/build/status/Hofknecht.SystemTrayMenu?branchName=master)](https://dev.azure.com/MarkusHofknecht/SystemTrayMenu/_build)
|
||||
[![Build Version](https://img.shields.io/github/v/release/hofknecht/systemtraymenu)](https://github.com/Hofknecht/SystemTrayMenu/releases)
|
||||
|
||||
[![GitHub](https://github.com/favicon.ico)](https://github.com/Hofknecht/SystemTrayMenu/releases) [![Downloads GitHub](https://img.shields.io/github/downloads/Hofknecht/SystemTrayMenu/total.svg)](https://github.com/Hofknecht/SystemTrayMenu/releases) [![SourceForge](https://user-images.githubusercontent.com/52528841/89990756-1aff8000-dc83-11ea-828a-a70a4d567399.png)](https://sourceforge.net/projects/systemtraymenu/files/latest/download) [![Downloads SourceForge](https://img.shields.io/sourceforge/dt/systemtraymenu.svg)](https://sourceforge.net/projects/systemtraymenu/files/latest/download) [![MicrosoftStore](https://user-images.githubusercontent.com/52528841/88452959-371db780-ce63-11ea-9076-11920156456a.png)](https://www.microsoft.com/store/apps/9N24F8ZBJMT1) [![Downloads MicrosoftStore](https://img.shields.io/badge/0.6k-green)](https://www.microsoft.com/store/apps/9N24F8ZBJMT1) [![CHIP](https://user-images.githubusercontent.com/52528841/88452975-5583b300-ce63-11ea-8256-6e69a2bb3e2d.png)](https://www.chip.de/downloads/SystemTrayMenu_182854219.html) [![Downloads CHIP](https://img.shields.io/badge/1.4k-green)](https://www.chip.de/downloads/SystemTrayMenu_182854219.html) [![Computerbild](https://user-images.githubusercontent.com/52528841/89651200-d9a65380-d8c3-11ea-9dab-e5563eb7c4f6.png)](https://www.computerbild.de/download/SystemTrayMenu-26748523.html) [![Downloads Computerbild](https://img.shields.io/badge/0.2k-green)](https://www.computerbild.de/download/SystemTrayMenu-26748523.html) [![Majorgeeks](https://user-images.githubusercontent.com/52528841/116281616-2d2a3b80-a78a-11eb-948c-bde9a8ccb1ed.png)](https://www.majorgeeks.com/files/details/systemtraymenu.html)[![Downloads Majorgeeks](https://img.shields.io/badge/0.7k-green)](https://www.majorgeeks.com/files/details/systemtraymenu.html) [![Downloaddrivers](https://user-images.githubusercontent.com/52528841/116524789-0f6aec80-a8d8-11eb-9037-06b2c101fa72.png)](https://www.downloaddrivers.info/download-systemtraymenu-1-0-17-24/)[![Downloads Downloaddrivers](https://img.shields.io/badge/0.7k-green)](https://www.downloaddrivers.info/download-systemtraymenu-1-0-17-24/)
|
||||
|
||||
<!-- [![Gitter](https://badges.gitter.im/SystemTrayMenu/community.svg)](https://gitter.im/SystemTrayMenu/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) -->
|
||||
<!--
|
||||
[![Windows 11 neues Startmenü hinzufügen! - SystemTrayMenu _ PathTM - YouTube](https://user-images.githubusercontent.com/52528841/145684486-44e98d8f-c2ba-42fa-837d-91a6a397549a.png)](https://www.youtube.com/watch?v=xsi4Uv3-ZLg)
|
||||
-->
|
||||
|
||||
[![Release](https://img.shields.io/github/v/release/hofknecht/systemtraymenu?label=Download%20release%20(click%20here))](https://github.com/Hofknecht/SystemTrayMenu/releases)
|
||||
[![Tweet](https://img.shields.io/badge/Tweet-blue&logo=twitter?&labelColor=white&logo=twitter&color=white)](https://twitter.com/intent/tweet?text=SystemTrayMenu%20is%20an%20open-source%20%27Desktop%20Toolbar%27%20or%20%27Start%20Menu%27%20alternative.%20It%20offers%20a%20clear%2C%20personalized%20menu%20which%20can%20be%20opened%20via%20keyboard%20or%20mouse.%20Files%2C%20links%20and%20folders%20are%20organized%20in%20several%20levels%20as%20drop-down%20menus.%0A%0Ahttps%3A%2F%2Fgithub.com%2FHofknecht%2FSystemTrayMenu%23systemtraymenu%0A%0A)
|
||||
[![Facebook](https://img.shields.io/badge/Share-blue&logo=facebook?&labelColor=white&logo=facebook&color=white)](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fgithub.com%2FHofknecht%2FSystemTrayMenu%23systemtraymenu%0A%0ASystemTrayMenu)
|
||||
|
||||
SystemTrayMenu <img src="https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/master/Resources/SystemTrayMenu.ico" alt="Trulli" width="32" height="32" align="left">
|
||||
------------------
|
||||
**Browse and open your files easily**
|
||||
|
||||
SystemTrayMenu is a free, open-source start menu alternative for Microsoft Windows. It offers a clear, personalized menu in the system tray. Files, links and folders are organized in several levels as drop-down menus.
|
||||
SystemTrayMenu is an open source 'Desktop Toolbar' or 'Start Menu' alternative. It offers a clear, personalized menu which can be controlled via keyboard, mouse or touchscreen. Files, links and folders are organized in several levels as dropdown menus. Various options like appearance, hotkey, autostart and behavior can be adjusted.
|
||||
|
||||
We are using C# and .Net Core 3.1
|
||||
* Shortcuts to folders or to network paths are resolved. Their content is displayed like a folder.
|
||||
* The search function allows you to define hotkey sequences to find and open items.
|
||||
* The menu stays open, if starting several applications and mouse still on the menu or if a script steals the focus for a short time.
|
||||
* Menu appears transparent in case it has no focus and closes automatically when you leave the menu.
|
||||
* A scrollbar appears when there are too many items, which can also be controlled via mouse wheel.
|
||||
* Supports drag and drop to open an item with an application or to copy it to the desktop.
|
||||
* There is a possiblity to create URL shortcuts including the browser icon via drag and drop the URL from the browser into the menu.
|
||||
|
||||
![20200812_125923](https://user-images.githubusercontent.com/52528841/90009201-ee0c9680-dc9d-11ea-9b8a-b34108152f9b.gif)
|
||||
![20200812_130823](https://user-images.githubusercontent.com/52528841/90009212-f1078700-dc9d-11ea-943a-d5fde4d6f2dc.gif)
|
||||
**Controlled via keyboard and like the 'Start Menu' - Demo**
|
||||
|
||||
Download the latest official version:
|
||||
https://github.com/Hofknecht/SystemTrayMenu/releases
|
||||
|
||||
![20220311_182732 (2)](https://user-images.githubusercontent.com/52528841/157929743-2ba4d11b-e0b1-4ab1-8eee-a2a7ff631adf.gif)
|
||||
|
||||
**Controlled via mouse and like the 'Desktop Toolbar' - Demo**
|
||||
|
||||
![20220311_183519 (1)](https://user-images.githubusercontent.com/52528841/157929241-1906b180-3839-425a-bc71-5486a1d54156.gif)
|
||||
|
||||
Keyboard shortcuts
|
||||
------------------
|
||||
|
||||
| Shortcut | Function |
|
||||
|-------------------------------------------------------|---------------------------------------|
|
||||
| <kbd>Ctrl</kbd>+<kbd>Win</kbd> | Open SystemTrayMenu (Shortcut can be configured) |
|
||||
| <kbd>↑</kbd>/<kbd>↓</kbd> | Navigate items within a menu |
|
||||
| <kbd>←</kbd>/<kbd>→</kbd> | Navigate through menus |
|
||||
| <kbd>Return</kbd> | Open selected item |
|
||||
| <kbd>Apps</kbd> | Open Explorer Context Menu |
|
||||
| <kbd>Tab</kbd> | Switch to next/previous menu (also via <kbd>Shift</kbd>+<kbd>Tab</kbd>) |
|
||||
| <kbd>Esc</kbd> | Close SystemTrayMenu |
|
||||
|
||||
Sources
|
||||
------------------
|
||||
|
||||
[![GitHub all releases](https://img.shields.io/github/downloads/Hofknecht/SystemTrayMenu/total?label=GitHub)](https://github.com/Hofknecht/SystemTrayMenu/releases)
|
||||
[![Downloads SourceForge](https://img.shields.io/sourceforge/dt/systemtraymenu.svg?label=Sourceforge)](https://sourceforge.net/projects/systemtraymenu/)
|
||||
[![Downloads Chocolatey](https://img.shields.io/chocolatey/dt/SystemTrayMenu?label=Chocolatey)](https://community.chocolatey.org/packages/systemtraymenu)
|
||||
[![GitHub](https://github.com/favicon.ico)](https://github.com/Hofknecht/SystemTrayMenu/releases)
|
||||
[![MicrosoftStore](https://user-images.githubusercontent.com/52528841/88452959-371db780-ce63-11ea-9076-11920156456a.png)](https://www.microsoft.com/store/apps/9N24F8ZBJMT1)
|
||||
[![SourceForge](https://user-images.githubusercontent.com/52528841/89990756-1aff8000-dc83-11ea-828a-a70a4d567399.png)](https://sourceforge.net/projects/systemtraymenu/)
|
||||
[![majorgeeks](https://user-images.githubusercontent.com/52528841/116281616-2d2a3b80-a78a-11eb-948c-bde9a8ccb1ed.png)](https://www.majorgeeks.com/files/details/systemtraymenu.html)
|
||||
[![heise](https://user-images.githubusercontent.com/52528841/125174840-53c41400-e1c8-11eb-8992-2de3e078c7b2.png)](https://www.heise.de/download/product/systemtraymenu)
|
||||
[![chocolatey](https://user-images.githubusercontent.com/52528841/153746103-47e7edd7-1bc3-4c29-b712-bab052b473eb.png)](https://community.chocolatey.org/packages/systemtraymenu)
|
||||
[![scoop](https://user-images.githubusercontent.com/52528841/153939000-a8fd62b7-068b-48c3-8ad2-a09b1552f3a8.png)](https://github.com/Hofknecht/SystemTrayMenu/issues/325)
|
||||
[![computerbild](https://user-images.githubusercontent.com/52528841/121583122-2735a480-ca30-11eb-82a4-b4f6054e4d8f.png)](https://www.computerbild.de/download/SystemTrayMenu-26748523.html)
|
||||
[![CHIP](https://user-images.githubusercontent.com/52528841/88452975-5583b300-ce63-11ea-8256-6e69a2bb3e2d.png)](https://www.chip.de/downloads/SystemTrayMenu_182854219.html)
|
||||
[![softpedia](https://user-images.githubusercontent.com/52528841/121581852-a033fc80-ca2e-11eb-8aee-4a8cd3043bd1.png)](https://www.softpedia.com/get/System/Launchers-Shutdown-Tools/SystemTrayMenu.shtml)
|
||||
[![freeware-base](https://user-images.githubusercontent.com/52528841/136707080-a64c8eeb-d563-44e6-aee3-1fb2921221e0.png)](https://www.freeware-base.de/freeware-zeige-details-31811-SystemTrayMenu.html)
|
||||
[![netzwelt](https://user-images.githubusercontent.com/52528841/121585548-015dcf00-ca33-11eb-8436-b1f5231d5c2d.png)](https://www.netzwelt.de/download/25705-systemtraymenu.html) [![aiiguide](https://user-images.githubusercontent.com/52528841/136708010-1f7a7d04-0b0b-4fe8-b70c-83bd48733d9c.png)](https://aiiguide.com/download-systemtraymenu.html)[![taiwebs](https://user-images.githubusercontent.com/52528841/136708061-6384d536-88a7-4c5d-a13a-a1b0cff00e68.png)](https://de.taiwebs.com/windows/download-systemtraymenu-6812.html)
|
||||
[![rjno1](https://user-images.githubusercontent.com/52528841/121582195-04ef5700-ca2f-11eb-9c22-cf8239c6e99b.png)](https://www.rjno1.com/systemtraymenu/)
|
||||
[![softaro](https://user-images.githubusercontent.com/52528841/121581997-c9548d00-ca2e-11eb-9145-fab017925475.png)](https://softaro.net/systemtraymenu/)
|
||||
[![bytesin](https://user-images.githubusercontent.com/52528841/136706178-0bd2e812-f087-40f4-8c30-301ba645f10d.png)](https://www.bytesin.com/software/SystemTrayMenu/)
|
||||
[![softonic](https://user-images.githubusercontent.com/52528841/121586781-5a7a3280-ca34-11eb-8a03-618b4661f859.png)](https://systemtraymenu.en.softonic.com/)
|
||||
[![downloaddrivers](https://user-images.githubusercontent.com/52528841/116524789-0f6aec80-a8d8-11eb-9037-06b2c101fa72.png)](https://www.downloaddrivers.info/download-systemtraymenu-1-2-4-0/)
|
||||
[![baominh](https://user-images.githubusercontent.com/52528841/120082388-0c277400-c0c3-11eb-97c8-ee35e692b38d.png)](https://baominh.tech/systemtraymenu-tao-menu-start-tuy-chinh-duoi-khai-he-thong.html)
|
||||
[![softodrom](https://user-images.githubusercontent.com/52528841/142266835-80248737-60d6-4e80-9ce3-e9b68bb217c0.png)](https://soft.softodrom.ru/%D0%A1%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C/28225)
|
||||
[![lo4d](https://github.com/Hofknecht/SystemTrayMenu/assets/52528841/3ba7d63a-5882-42e6-9cc7-0a7fa167f2c6)](https://systemtraymenu.en.lo4d.com/windows)
|
||||
|
||||
Reviews
|
||||
------------------
|
||||
[![2022-02-15 00_04_52-Window](https://user-images.githubusercontent.com/52528841/153961695-be5f50ab-9ce5-423e-871a-cf1062023d31.png)](https://www.youtube.com/watch?v=xsi4Uv3-ZLg)
|
||||
[![Youtube](https://img.shields.io/youtube/views/xsi4Uv3-ZLg?style=social)](https://www.youtube.com/watch?v=xsi4Uv3-ZLg)
|
||||
|
||||
[deskmodder.de 2020/07/18](https://www.deskmodder.de/blog/2020/07/18/systemtraymenu-nun-auch-als-windows-10-app-neben-der-portablen-version/) |
|
||||
[deskmodder.de 2021/11/14](https://www.deskmodder.de/blog/2021/11/14/systemtraymenu-als-ersatz-fuer-die-symbolleiste-unter-windows-11/) |
|
||||
[trishtech.com](https://www.trishtech.com/2020/07/systemtraymenu-is-simple-and-lightweight-start-menu-alternative/) |
|
||||
[ghacks.net](https://www.ghacks.net/2021/04/10/create-a-custom-start-menu-that-you-can-access-with-a-hotkey-using-systemtraymenu/) |
|
||||
[shrayas.com](http://www.shrayas.com/posts/quick-access-everything-with-systemtraymenu/) |
|
||||
[reviewsapp.org](https://reviewsapp.org/systemtraymenu-better-organization-of-windows) |
|
||||
[thewindowsclub.com](https://www.thewindowsclub.com/systemtraymenu-for-windows) |
|
||||
[br.atsit.in](https://br.atsit.in/id/?p=100192) |
|
||||
[nesabamedia.com](https://www.nesabamedia.com/systemtraymenu-alternatif-start-menu-untuk-windows/) |
|
||||
[soluciones.link](https://soluciones.link/windows/systemtraymenu-es-una-alternativa-gratuita-del-menu-de-inicio-para-windows-11-10/) |
|
||||
[de.moyens.net](https://de.moyens.net/windows/systemtraymenu-ist-eine-kostenlose-startmenue-alternative-fuer-windows-11-10/) |
|
||||
[windowsreport.com](https://windowsreport.com/windows-11-free-start-menu-replacement/) |
|
||||
[onmsft.com](https://www.onmsft.com/how-to/how-to-edit-windows-11-start-menu-layout) |
|
||||
[starchart.cc](https://starchart.cc/hofknecht/systemtraymenu.svg)
|
||||
|
||||
FAQ
|
||||
------------------
|
||||
|
||||
**What do I have to do now as first steps?**
|
||||
**What do I have to do as first steps?**
|
||||
|
||||
SystemTrayMenu is portable, so it does not need to be installed. After downloading e.g. SystemTrayMenu-1.2.6.0.zip, **unzip the folder**, then **start SystemTrayMenu.exe**. If you prefer an installation there is the possibility to install SystemTrayMenu via the [Microsoft Store](https://www.microsoft.com/store/apps/9N24F8ZBJMT1).
|
||||
|
||||
SystemTrayMenu is portable, so it does not need to be installed. After downloading e.g. SystemTrayMenu-1.0.17.24.zip, **unzip the folder**, then **start SystemTrayMenu.exe**.
|
||||
If you prefer an installation there is the possibility to install SystemTrayMenu via the Windows Store.
|
||||
|
||||
1. Step: After starting the application the first time you have to **choose the root directory**.
|
||||
In this directory you should put shortcuts, files and folders (App, Game, Script, URL, Network),
|
||||
which you are often using and especially when you can not find them over the windows start menu search.
|
||||
You can also consider to put there all files from your desktop.
|
||||
|
||||
2. Step: **Move the SystemTrayMenu icon** by drag and drop from the system tray **into the taskbar** below.
|
||||
![group icon out](https://user-images.githubusercontent.com/52528841/83349567-1ab74000-a336-11ea-8676-3db33615a57a.gif)
|
||||
|
||||
3. Step: Now it is ready to start - just **click on the icon** in the taskbar to open the SystemTrayMenu.
|
||||
![group systemtray](https://user-images.githubusercontent.com/52528841/157921582-ac6350c0-4c6d-4a02-9bd2-224a264e4329.gif)
|
||||
|
||||
|
||||
3. Step: 'Right click' on taskbar item -> '**Pin to taskbar**'
|
||||
|
||||
![pin to taskbar](https://user-images.githubusercontent.com/52528841/157921722-123a7694-e3f4-4cce-951a-94ddb3d4b60f.png)
|
||||
|
||||
Now it is ready to start - just **click on the icon** in the system tray or the taskbar or press the hotkey (Ctrl + LWin) to open the SystemTrayMenu.
|
||||
|
||||
---
|
||||
|
||||
**How can I change the root directory?**
|
||||
|
||||
You can change the root directory in the settings menu, that you can open by right clicking on the SystemTrayMenu icon.
|
||||
You can change the root directory in the settings menu, which can be opened by right clicking on the icon in the system tray.
|
||||
|
||||
---
|
||||
|
||||
**What does the hotkey do?**
|
||||
|
||||
In the settings menu you can choose a hotkey to open and close the SystemTrayMenu.
|
||||
|
||||
---
|
||||
|
||||
**Can the SystemTrayMenu launch on windows startup?**
|
||||
|
||||
Yes, you can select this option in the settings menu, that you can open by right clicking on the SystemTrayMenu icon.
|
||||
Yes, you can select this option in the settings menu, which can be opened by right clicking on the icon in the systen tray.
|
||||
|
||||
**What can I do if I have a problem, idea or question?**
|
||||
---
|
||||
|
||||
If a problem has occured or you have any ideas or questions, you are welcome to contact us.
|
||||
Our contact information you can find in the About window, that can be opened by right clicking on the SystemTrayMenu icon.
|
||||
**How can I add Windows 'Shutdown' or Windows 'Restart'?**
|
||||
|
||||
Create a file 'Shutdown.bat' : `Shutdown.exe -s -t 00`. Create a file 'Restart.bat'. `Shutdown.exe -r -t 00`.
|
||||
Make shortcuts of the files and maybe also set an icon to the shortcut. Rename the link to e.g. 'xx Shutdown'. Either put it directly into the root folder or include the files via option 'Folders'. There you could also hide the scripts by default and only show it when searching e.g. 'xx'. Then you can shutdown your PC with: hotkey + xx + Enter
|
||||
|
||||
![2022-02-06 11_09_12-Window](https://user-images.githubusercontent.com/52528841/152676102-30eccd6a-2ae6-4964-9631-c06497f491e7.png)
|
||||
|
||||
---
|
||||
|
||||
**How can I switch sound between 'Speaker' and 'Headset'?**
|
||||
|
||||
Download NirCmd, put nircmd.exe to your preferred location.
|
||||
<br>
|
||||
Create a file 'Speaker - Audio.bat':
|
||||
`"D:\<preffered location>\nircmd\nircmd.exe" setdefaultsounddevice "VSX-521" 1`
|
||||
<br>
|
||||
Create a file 'Headset - Audio.bat':
|
||||
`"D:\<preffered location>\nircmd\nircmd.exe" setdefaultsounddevice "Speakers" 1`.
|
||||
|
||||
Then you can switch between speaker and headset with: hotkey + headset + Enter
|
||||
|
||||
![2022-02-06 11_16_30-Window](https://user-images.githubusercontent.com/52528841/152676337-349666dd-da7d-4b1e-ae02-e297935fd5b7.png)
|
||||
|
||||
---
|
||||
|
||||
**How can I move the 'Recycle Bin' from the Desktop into the STM?**
|
||||
|
||||
Create a shortcut of 'Recycle Bin' and put it into STM. Then hide it from desktop:
|
||||
To find the option, right-click on the desktop, choose “Personalize,” and then click “Desktop Icon Settings” in the left sidebar. Uncheck the “Recycle Bin” box and click “OK” to save your changes.
|
||||
Then you can e.g. empty your 'Recycle Bin' with: hotkey + recycle + Keys.App (to open context menu) + b (context menu shortcut for 'Empty Recycle Bin') + Enter
|
||||
|
||||
![2022-02-06 12_37_51-Window](https://user-images.githubusercontent.com/52528841/152679061-ae7c30c5-92c5-4b4c-abb3-5316aabf9c6a.png)
|
||||
|
||||
---
|
||||
|
||||
**Which options are there to control the behavior via taskbar item?**
|
||||
|
||||
By default, the 'Show in Taskbar' option is activated, which means there is shown an active form in the taskbar which can be used to open and close the menu via activate and deactivate event, e.g. also via Alt + Tab.
|
||||
|
||||
![2022-02-06 15_35_02-Window](https://user-images.githubusercontent.com/52528841/152685888-1b746858-dd45-41ab-a082-da7710298c74.png)
|
||||
|
||||
|
||||
When you switch off the option, there is no active form in the taskbar. When you start the application, then other instances will be killed and a new one started.
|
||||
This behavior can be changed via
|
||||
|
||||
![2022-03-11 13_02_28-Window](https://user-images.githubusercontent.com/52528841/157863638-663c4792-1c4c-4468-a731-acd79a23906e.png)
|
||||
|
||||
If the shortcut is pressed then via the taskbar item, we need more time to show the menu (because we have to start an extra process which tells the first to open).
|
||||
|
||||
---
|
||||
|
||||
What can I do if I have a problem, idea or question?
|
||||
------------------
|
||||
|
||||
If a problem has occured or you have ideas or questions, you are welcome to contact us:
|
||||
* [Create an issue](https://github.com/Hofknecht/SystemTrayMenu/issues/new/choose)
|
||||
* [Suggest your idea](https://github.com/Hofknecht/SystemTrayMenu/discussions/categories/ideas)
|
||||
* [Start a discussion](https://github.com/Hofknecht/SystemTrayMenu/discussions/new)
|
||||
* [Write us an email](mailto:Markus@Hofknecht.eu)
|
||||
|
||||
**Find more FAQ topics here:**
|
||||
[SystemTrayMenu FAQ](https://github.com/Hofknecht/SystemTrayMenu/issues?q=is%3Aissue+is%3Aclosed+label%3AFAQ)
|
||||
|
||||
|
||||
Security
|
||||
------------------
|
||||
|
||||
|
@ -69,18 +205,19 @@ Some antiviruses might flag this program as malicious, but it is not! The source
|
|||
Build
|
||||
------------------
|
||||
|
||||
Install Visual Studio 2019.
|
||||
Install .NET Core 3.1 SDK [https://dotnet.microsoft.com](https://dotnet.microsoft.com/download/dotnet-core/thank-you/sdk-3.1.300-windows-x64-installer).
|
||||
[![Build Status](https://dev.azure.com/MarkusHofknecht/SystemTrayMenu/_apis/build/status/Hofknecht.SystemTrayMenu?branchName=master)](https://dev.azure.com/MarkusHofknecht/SystemTrayMenu/_build)
|
||||
[![IDE](https://img.shields.io/badge/framework-2022-darkturquoise?label=Downlaod%20Visual%20Studio%202022%20(click%20here))](https://visualstudio.microsoft.com/de/downloads/)
|
||||
[![Framework](https://img.shields.io/badge/framework-.NET%207%20SDK%20(Win%2010/11)-darkturquoise?label=Download%20framework%20(click%20here))](https://dotnet.microsoft.com/download/dotnet/7.0)
|
||||
|
||||
Contributing
|
||||
------------------
|
||||
|
||||
If you would like to contribute, everyone is welcome to.
|
||||
If you are considering a feature, need guidance, or want to talk about an idea, don't hesitate to create an issue here.
|
||||
If you would like to contribute, you are very welcome.
|
||||
If you are considering a feature, need guidance, or want to talk about an idea, don't hesitate to create an issue or a discussion here.
|
||||
When contributing please respect the style used by the codebase and consider the following rules:
|
||||
* Run FixCop.
|
||||
* Fix warnings shown by code analyzers.
|
||||
* Increase the version in the assembly file.
|
||||
* Add a commit message like:
|
||||
* Add a commit message like:
|
||||
```
|
||||
[Feature] Show icon in taskbar when application is running (#115), version 0.11.1.9
|
||||
```
|
||||
|
@ -93,23 +230,125 @@ Contributors
|
|||
------------------
|
||||
|
||||
Special thanks to our productive contibutors!
|
||||
* [Tanja Kauth](https://github.com/Tanjalibertatis)
|
||||
* [Tanja Hofknecht](https://github.com/Tanjalibertatis)
|
||||
* [Peter Kirmeier](https://github.com/topeterk)
|
||||
|
||||
Thanks for translations!
|
||||
* French by [Pascal Aloy](mailto:paloy@wanadoo.fr)
|
||||
* Dutch by [HansieNL](https://github.com/HansieNL)
|
||||
* Portuguese (Brazil) by [igorruckert](https://github.com/igorruckert)
|
||||
Thanks for ideas, reporting issues and contributing!
|
||||
|
||||
#123 [Mordecai00](https://github.com/Mordecai00),
|
||||
#125 [Holgermh](https://github.com/Holgermh),
|
||||
#135 #153 #154 #164 [jakkaas](https://github.com/jakkaas),
|
||||
#145 [Pascal Aloy](mailto:paloy@wanadoo.fr),
|
||||
#153 #158 #160 [blackcrack](https://github.com/blackcrack),
|
||||
#162 [HansieNL](https://github.com/HansieNL),
|
||||
#163 [igorruckert](https://github.com/igorruckert),
|
||||
#171 [kehoen](https://github.com/kehoen),
|
||||
#186 [Dtrieb](https://github.com/Dtrieb),
|
||||
#188 #189 #191 #195 [iJahangard](https://github.com/iJahangard),
|
||||
#195 #197 #225 #238 [the-phuctran](https://github.com/the-phuctran),
|
||||
#205 [kristofzerbe](https://github.com/kristofzerbe),
|
||||
#209 [jonaskohl](https://github.com/jonaskohl),
|
||||
#211 [blacksparrow15](https://github.com/blacksparrow15),
|
||||
#220 #403 [Yavuz E.](mailto:yavuzelektronik@gmail.com),
|
||||
#229 #230 #239 [Peter O.](pohle@htp-tel.de),
|
||||
#231 [Ryonez](https://www.youtube.com/user/Ryonez),
|
||||
#235 #242 243 #247, #271 Tom,
|
||||
#237 Torsten S.,
|
||||
#240 [video](https://www.youtube.com/watch?v=xsi4Uv3-ZLg) [Patrick](https://www.youtube.com/user/DyRexLP),
|
||||
#244 Gunter D.,
|
||||
#246 #329 [MACE4GITHUB](https://github.com/MACE4GITHUB),
|
||||
#259 #310 [vanjac](https://github.com/vanjac),
|
||||
#262 [terencemcdonnell](https://github.com/terencemcdonnell),
|
||||
#269 [petersnows25](https://github.com/petersnows25),
|
||||
#272 Peter M.,
|
||||
#273 #274 [ParasiteDelta](https://github.com/ParasiteDelta),
|
||||
#275 #276 #278 [donaldaken](https://github.com/donaldaken),
|
||||
#277 Jan S.,
|
||||
#282 [akuznets](https://github.com/akuznets),
|
||||
#283 #284 #289 [RuSieg](https://github.com/RuSieg),
|
||||
#285 #286 [dao-net](https://github.com/dao-net),
|
||||
#288 William P.,
|
||||
#294 #295 #296 Stefan M.,
|
||||
#225 #297 #299 #317 #321 #324 #330 #386 #390 #401 #402 #407 #409 #414 #416 #418 #428 #430 #443 [chip33](https://github.com/chip33),
|
||||
#298 [phanirithvij](https://github.com/phanirithvij),
|
||||
#306 [wini2](https://github.com/wini2),
|
||||
#370 [dna5589](https://github.com/dna5589),
|
||||
#372 [not-nef](https://github.com/not-nef),
|
||||
#376 Michelle H.,
|
||||
#377 [SoenkeHob](https://github.com/SoenkeHob),
|
||||
#380 #394 [TransLucida](https://github.com/TransLucida),
|
||||
#384 #434 #435 [boydfields](https://github.com/boydfields),
|
||||
#386 [visusys](https://github.com/visusys),
|
||||
#387 #411 #444 [yrctw](https://github.com/yrctw),
|
||||
#446 [timinformatica](https://github.com/timinformatica),
|
||||
#450 [ppt-oldoerp](https://github.com/ppt-oldoerp),
|
||||
#453 [fubaWoW](https://github.com/fubaWoW),
|
||||
#454 [WouterVanGoey](https://github.com/WouterVanGoey),
|
||||
#462 [verdammt89x](https://github.com/verdammt89x),
|
||||
#463 #494 Dirk S.,
|
||||
#466 [Dean-Corso](https://github.com/Dean-Corso),
|
||||
#488 [DailenG](https://github.com/DailenG),
|
||||
#490 [TrampiPW](https://github.com/TrampiPW),
|
||||
#497 Aziz,
|
||||
#499 [spitzlbergerj](https://github.com/spitzlbergerj),
|
||||
|
||||
|
||||
|
||||
Donations
|
||||
------------------
|
||||
|
||||
We would be delighted if you could help us with the following:
|
||||
* a star on this github project
|
||||
* a like on [Facebook](https://www.facebook.com/Systemtraymenu-114069060335483)
|
||||
* a review or rating on [Sourceforge](https://sourceforge.net/projects/systemtraymenu/)
|
||||
* your ideas either as issues here in github or directly per mail
|
||||
* Give a star, follow, watch, fork [![stars](https://img.shields.io/github/stars/hofknecht/systemtraymenu?style=social)](https://github.com/Hofknecht/SystemTrayMenu/stargazers) [![followers](https://img.shields.io/github/followers/hofknecht?style=social)](https://github.com/Hofknecht?tab=followers) [![watchers](https://img.shields.io/github/watchers/hofknecht/systemtraymenu?style=social)](https://github.com/Hofknecht/SystemTrayMenu/watchers) [![forks](https://img.shields.io/github/forks/hofknecht/systemtraymenu?style=social)](https://github.com/Hofknecht/SystemTrayMenu/network/members)
|
||||
* Help us to improve SystemTrayMenu via [Feedback](https://github.com/Hofknecht/SystemTrayMenu/edit/master/README.md#what-can-i-do-if-i-have-a-problem-idea-or-question) or [Show and tell](https://github.com/Hofknecht/SystemTrayMenu/discussions/categories/show-and-tell) how you use it
|
||||
* Don't hesitate to donate via PayPal if you appreciate SystemTrayMenu and would like to support our work:
|
||||
[![PayPal](https://www.paypalobjects.com/webstatic/de_DE/i/de-pp-logo-200px.png)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Y9W6H5HXQPPUQ&source=url)
|
||||
* Become a [![Downloads Releases](https://img.shields.io/badge/-GitHub%20Sponsor-fafbfc?logo=GitHub%20Sponsors)](https://github.com/sponsors/Hofknecht)
|
||||
* Like, rate, share, comment, watch, tweet, join SystemTrayMenu (see also list in the top of readme)
|
||||
[![Twitter](https://img.shields.io/twitter/follow/markushofknecht?style=social)](https://twitter.com/MarkusHofknecht)
|
||||
[![Reddit](https://img.shields.io/reddit/subreddit-subscribers/systemtraymenu?style=social)](https://www.reddit.com/r/SystemTrayMenu/)
|
||||
[![Facebook](https://img.shields.io/badge/Like-blue&logo=facebook?&labelColor=white&logo=facebook&color=white)](https://www.facebook.com/Systemtraymenu-114069060335483)
|
||||
<!--* We are trying to apply for the amazon affiliate program with an amazon link, in the future we might get ~1.5% per purchase, for you the price stays the same. All you have to do is doing your purchases via the link. As an Amazon partner, we earn from qualified sales. This only works with certain products and we can e.g. create these category links (please tell us if you need other special categories):
|
||||
Popular offers: -->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/0c0ca0e6?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=2563e96525666067f86f307dc0dbb3ce&ref_=ihub_rc_td_c_deals-promotions_0c0ca0e6">External and internal hard drives & SSDs</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/00e8adff?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=3f230f3b20a20179d13865a261eebcbb&ref_=ihub_rc_td_c_deals-promotions_00e8adff">USB sticks Micro SD cards & RAM memory</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/6e1b3fdd?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=a564059235d38cc1f7cacb21a9b55d4d&ref_=ihub_rc_td_c_deals-promotions_6e1b3fdd">Gaming Mice Keyboards Headsets and Accessories</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/6be9721b?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=4f277b38dfc3bb1bd7f03ede8ccc2fdc&ref_=ihub_rc_td_c_deals-promotions_6be9721b">Top sellers for your home</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/92352ca0?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=42616447ce672851de0e49131fde37a7&ref_=ihub_rc_td_c_deals-promotions_92352ca0">ASUS gaming motherboard socket</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/28f2bc0f?showVariations=true&_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=ad1215bb0830e568b5c7d859b2c90d37&ref_=ihub_rc_td_c_deals-promotions_28f2bc0f">Gaming monitors from different manufacturers</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/634e16ae?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=044709e2c9f34d3949aad05c5dad578d&ref_=ihub_rc_td_c_deals-promotions_634e16ae">Headphones from Sony Denon and much more</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/2a2abb33?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=b25df70dec6da6986aa18eb1d3baa6cd&ref_=ihub_rc_td_c_deals-promotions_2a2abb33">Bluetooth Headphones & Speakers by Soundcore</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/5e70f674?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=612481a6d72e08029a0091e9fcc07f82&ref_=ihub_rc_td_c_deals-promotions_5e70f674">Lighting</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/8b218d21?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=8d433699b562dd9c7e6873194811bb4e&ref_=ihub_rc_td_c_deals-promotions_8b218d21">Gaming Laptops</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/e45ea2b7?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=788ad1176bd69dc2e6980317ff4e2b67&ref_=ihub_rc_td_c_deals-promotions_e45ea2b7">Headphones from Bose Sennheiser Philips Audio and much more</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/e4af7b7a?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=4c808d1c3bcbb720b52bdd3d0622fa74&ref_=ihub_rc_td_c_deals-promotions_e4af7b7a">Coffee machines and accessories</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/4666dc4a?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=588b33399f734e20c04d82ff41ba45e3&ref_=ihub_rc_td_c_deals-promotions_4666dc4a">Computer accessories</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/d7117d73?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=62aff0fd7572235fde50c13413f8fd28&ref_=ihub_rc_td_c_deals-promotions_d7117d73">HyperX Gaming</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/9644446b?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=8dd2b17705164603a6b55a3f8e461c2e&ref_=ihub_rc_td_c_deals-promotions_9644446b">Netatmo smart home products</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/e693926e?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=e27d8ac861d8f33041872ff21fdff02c&ref_=ihub_rc_td_c_deals-promotions_e693926e">Smartphones Smartwatches and accessories</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/f9a5261d?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=8a12373dc9b5ceaa67a1a2a2e1dfc606&ref_=ihub_rc_td_c_deals-promotions_f9a5261d">Audio cable</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/8b13c2f5?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=1302ff8212f930791293b411851a6bdc&ref_=ihub_rc_td_c_deals-promotions_8b13c2f5">WLAN and mesh systems, switches and surveillance cameras</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/fc813eed?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=ff134e03141d366eac0518528d1c999e&ref_=ihub_rc_td_c_deals-promotions_fc813eed">Smarthome and Home Security</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/deal/0f10758d?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=3325789289a03d743fe5b46981c82edc&ref_=ihub_rc_td_c_deals-promotions_0f10758d">Soundbars & speakers from Denon Polk Philips Audio and much more</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/dp/B07ZNGT8K4?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=764abbfc6ac54f3a5abd298af0105b28&ref_=ihub_rc_td_c_deals-promotions_b77be80b">Monitor mount</a-->
|
||||
<!--a target="_blank" href="https://www.amazon.de/dp/B00L2442H0?_encoding=UTF8&linkCode=r02&tag=systemtraymen-21&linkId=1c3e9143da0adfd2c667d6451ebaca6b&ref_=ihub_rc_td_c_deals-promotions_8333037a">PCs & accessories</a-->
|
||||
|
||||
Don't hesitate to donate if you appreciate SystemTrayMenu and would like to support our work.
|
||||
[![PayPal](https://www.paypalobjects.com/webstatic/de_DE/i/de-pp-logo-100px.png)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Y9W6H5HXQPPUQ&source=url)
|
||||
<!--a target="_blank" href="https://www.amazon.de/b?_encoding=UTF8&tag=systemtraymen-21&linkCode=ur2&linkId=6151ac8258f44adda610cc05e57967a2&camp=1638&creative=6742&node=340843031">Computers & Accessories</a>, <a target="_blank" href="https://www.amazon.de/b?_encoding=UTF8&tag=systemtraymen-21&linkCode=ur2&linkId=ca8480de9bc1ef726d5a1280f593ac9f&camp=1638&creative=6742&node=562066">Electronics & Photography</a>, <a target="_blank" href="https://www.amazon.de/b?_encoding=UTF8&tag=systemtraymen-21&linkCode=ur2&linkId=7ff79addfbdbfcaae04818d279a7e973&camp=1638&creative=6742&node=301927">Software</a>, <a target="_blank" href="https://www.amazon.de/b?_encoding=UTF8&tag=systemtraymen-21&linkCode=ur2&linkId=100c22e7db187d7cce9ae20f8a4e024a&camp=1638&creative=6742&node=300992">Video Games</a-->
|
||||
|
||||
PayPal/GitHub Sponsors - Thank you!
|
||||
------------------
|
||||
(Sponsors are listed here in the ReadMe and in the application about menu)
|
||||
* Stefan Mahrer
|
||||
* [boydfields](https://github.com/boydfields)
|
||||
* [RuSieg](https://github.com/RuSieg)
|
||||
* [igor-davidov](https://github.com/igor-davidov)
|
||||
* Ralf K.
|
||||
* Tim K.
|
||||
* Georg W.
|
||||
* [donaldaken](https://github.com/donaldaken)
|
||||
* Marc Speer
|
||||
* [Cito](https://github.com/Cito)
|
||||
* Peter G.
|
||||
* [Traditional_Tap3954](https://www.reddit.com/user/Traditional_Tap3954/)
|
||||
* Maximilian H.
|
||||
* Jens B.
|
||||
* [spitzlbergerj](https://github.com/spitzlbergerj)
|
||||
* Udo N.
|
||||
|
|