Compare commits
505 commits
v1.0.17.18
...
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 | |||
b9acf24962 | |||
ec8f677593 | |||
c12ed3fc4e | |||
7301b61a42 | |||
88cab02cf0 | |||
15609e7240 | |||
2fd0270a1b | |||
a005582d5e | |||
c824c7a10b | |||
a12096ebfb | |||
b316929135 | |||
989ab5d3cc | |||
2b7f1740c4 | |||
2b4cf855fc | |||
7914950c9f | |||
49907998bb | |||
f17db4ed9f | |||
66b14a5c7f | |||
f5aece4741 | |||
ac28ab2b1c | |||
40ec45960b | |||
6c185f8767 | |||
2c7600661d | |||
d64e3df694 | |||
bfea86fa6a | |||
721ee0579e | |||
1a06c0ebe7 | |||
4200cb9cc0 | |||
8678196326 | |||
f92de22b2b | |||
2cea00c942 | |||
d894277d00 | |||
1edfa657ae | |||
11caf4ed91 | |||
afa153fd9b | |||
a175d6a718 | |||
903322eaa1 |
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
|
12
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: Hofknecht # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
|
@ -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);
|
||||
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,19 +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);
|
||||
trigger.MouseClick(null, out bool toCloseByMouseClick);
|
||||
trigger.DoubleClick(
|
||||
new MouseEventArgs(MouseButtons.Left, 0, 0, 0, 0));
|
||||
new MouseEventArgs(MouseButtons.Left, 0, 0, 0, 0),
|
||||
out bool 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);
|
||||
}
|
||||
|
@ -315,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;
|
||||
}
|
||||
|
@ -325,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;
|
||||
|
@ -412,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;
|
||||
}
|
||||
|
@ -422,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
|
||||
|
@ -441,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;
|
||||
|
@ -521,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
1533
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;
|
||||
|
@ -25,7 +25,7 @@ namespace SystemTrayMenu.Handler
|
|||
|
||||
internal WaitToLoadMenu()
|
||||
{
|
||||
timerStartLoad.Interval = 200;
|
||||
timerStartLoad.Interval = Properties.Settings.Default.TimeUntilOpens;
|
||||
timerStartLoad.Tick += WaitStartLoad_Tick;
|
||||
}
|
||||
|
||||
|
@ -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; }
|
||||
}
|
||||
}
|
471
Config/Config.cs
|
@ -5,118 +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()
|
||||
{
|
||||
string browserPath = FileUrl.GetDefaultBrowserPath();
|
||||
if (!string.IsNullOrEmpty(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>
|
||||
|
@ -128,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",
|
||||
|
@ -143,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)
|
||||
|
@ -166,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;
|
||||
|
@ -198,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,13 +8,9 @@ namespace SystemTrayMenu
|
|||
|
||||
internal static class MenuDefines
|
||||
{
|
||||
internal const int Scrollspeed = 4;
|
||||
internal const int TimeUntilClose = 1000;
|
||||
internal const int MenusMax = 50;
|
||||
internal const int LengthMax = 37;
|
||||
internal const float MaxMenuWidth = 300;
|
||||
internal static readonly Color File = Color.White;
|
||||
internal static readonly Color Folder = Color.White;
|
||||
internal const int Scrollspeed = 3;
|
||||
|
||||
public static Color ColorSelectedItem
|
||||
{
|
||||
|
@ -22,11 +18,11 @@ namespace SystemTrayMenu
|
|||
{
|
||||
if (Config.IsDarkMode())
|
||||
{
|
||||
return AppColors.DarkModeBlue;
|
||||
return AppColors.DarkModeSelecetedItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppColors.Blue;
|
||||
return AppColors.SelectedItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,11 +33,11 @@ namespace SystemTrayMenu
|
|||
{
|
||||
if (Config.IsDarkMode())
|
||||
{
|
||||
return AppColors.DarkModeBlueBorder;
|
||||
return AppColors.DarkModeSelectedItemBorder;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppColors.BlueBorder;
|
||||
return AppColors.SelectedItemBorder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,11 +48,11 @@ namespace SystemTrayMenu
|
|||
{
|
||||
if (Config.IsDarkMode())
|
||||
{
|
||||
return AppColors.DarkModeGreen;
|
||||
return AppColors.DarkModeOpenFolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppColors.Green;
|
||||
return AppColors.OpenFolder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,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,166 +5,198 @@
|
|||
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 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 = Properties.Resources.WhiteTransparency;
|
||||
}
|
||||
|
||||
if (HiddenEntry)
|
||||
{
|
||||
row[0] = IconReader.AddIconOverlay(
|
||||
data.icon,
|
||||
Properties.Resources.WhiteTransparency);
|
||||
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);
|
||||
icon = GetFolderIconWithCache(Path, ShowOverlay, updateIconInBackground, IsMainMenu, out bool loading);
|
||||
IconLoading = loading;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool handled = 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);
|
||||
icon = Properties.Resources.NotFound;
|
||||
}
|
||||
else if (fileExtension == ".url")
|
||||
else if (HiddenEntry)
|
||||
{
|
||||
handled = SetUrl();
|
||||
}
|
||||
else if (fileExtension == ".sln")
|
||||
{
|
||||
handled = SetSln();
|
||||
}
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
try
|
||||
{
|
||||
icon = IconReader.GetFileIconWithCache(TargetFilePath);
|
||||
diposeIcon = false;
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
IsClicking = true;
|
||||
}
|
||||
|
||||
if (e != null &&
|
||||
e.Button == MouseButtons.Right &&
|
||||
FileInfo != null &&
|
||||
|
@ -174,234 +206,97 @@ 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;
|
||||
}
|
||||
|
||||
if (Properties.Settings.Default.OpenItemWithOneClick)
|
||||
void TriggerFileWatcherChangeWorkaround()
|
||||
{
|
||||
OpenItem(e);
|
||||
}
|
||||
}
|
||||
|
||||
internal void DoubleClick(MouseEventArgs e)
|
||||
{
|
||||
if (!Properties.Settings.Default.OpenItemWithOneClick)
|
||||
{
|
||||
OpenItem(e);
|
||||
}
|
||||
|
||||
if (ContainsMenu &&
|
||||
(e == null || e.Button == MouseButtons.Left))
|
||||
{
|
||||
Log.ProcessStart(TargetFilePath, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!isDisposed)
|
||||
{
|
||||
if (diposeIcon)
|
||||
try
|
||||
{
|
||||
icon?.Dispose();
|
||||
string parentFolder = System.IO.Path.GetDirectoryName(Path);
|
||||
Directory.GetFiles(parentFolder);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"{nameof(TriggerFileWatcherChangeWorkaround)} '{Path}'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
isDisposed = true;
|
||||
}
|
||||
|
||||
private void OpenItem(MouseEventArgs e)
|
||||
internal void MouseClick(MouseEventArgs e, out bool toCloseByDoubleClick)
|
||||
{
|
||||
IsClicking = false;
|
||||
toCloseByDoubleClick = false;
|
||||
if (Properties.Settings.Default.OpenItemWithOneClick)
|
||||
{
|
||||
OpenItem(e, ref toCloseByDoubleClick);
|
||||
}
|
||||
|
||||
if (Properties.Settings.Default.OpenDirectoryWithOneClick &&
|
||||
ContainsMenu && (e == null || e.Button == MouseButtons.Left))
|
||||
{
|
||||
Log.ProcessStart(Path);
|
||||
if (!Properties.Settings.Default.StaysOpenWhenItemClicked)
|
||||
{
|
||||
toCloseByDoubleClick = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void DoubleClick(MouseEventArgs e, out bool toCloseByDoubleClick)
|
||||
{
|
||||
IsClicking = false;
|
||||
toCloseByDoubleClick = false;
|
||||
if (!Properties.Settings.Default.OpenItemWithOneClick)
|
||||
{
|
||||
OpenItem(e, ref toCloseByDoubleClick);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!ContainsMenu &&
|
||||
(e == null || e.Button == MouseButtons.Left))
|
||||
{
|
||||
try
|
||||
ProcessStarted = true;
|
||||
string workingDirectory = System.IO.Path.GetDirectoryName(ResolvedPath);
|
||||
Log.ProcessStart(Path, string.Empty, false, workingDirectory, true, ResolvedPath);
|
||||
if (!Properties.Settings.Default.StaysOpenWhenItemClicked)
|
||||
{
|
||||
using Process p = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo(TargetFilePath)
|
||||
{
|
||||
FileName = TargetFilePathOrig,
|
||||
Arguments = arguments,
|
||||
WorkingDirectory = workingDirectory,
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = true,
|
||||
},
|
||||
};
|
||||
p.Start();
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
Log.Warn($"path:'{TargetFilePath}'", ex);
|
||||
MessageBox.Show(ex.Message);
|
||||
toCloseByOpenItem = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool SetLnk(
|
||||
ref bool isLnkDirectory,
|
||||
ref string resolvedLnkPath)
|
||||
{
|
||||
bool handled = false;
|
||||
resolvedLnkPath = FileLnk.GetResolvedFileName(TargetFilePath);
|
||||
if (FileLnk.IsDirectory(resolvedLnkPath))
|
||||
{
|
||||
icon = IconReader.GetFolderIconSTA(TargetFilePath);
|
||||
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 = Icon.ExtractAssociatedIcon(iconLocation);
|
||||
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))
|
||||
{
|
||||
string browserPath = FileUrl.GetDefaultBrowserPath();
|
||||
if (string.IsNullOrEmpty(browserPath))
|
||||
{
|
||||
Log.Info($"Resolve *.URL '{TargetFilePath}'" +
|
||||
$"No default browser found!");
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = IconReader.GetFileIconWithCache(browserPath);
|
||||
diposeIcon = false;
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if (System.IO.File.Exists(iconFile))
|
||||
{
|
||||
icon = Icon.ExtractAssociatedIcon(iconFile);
|
||||
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
|
||||
}
|
||||
}
|
|
@ -12,12 +12,8 @@ namespace SystemTrayMenu.DllImports
|
|||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
internal static bool Gdi32DeleteObject(IntPtr hObject)
|
||||
{
|
||||
return DeleteObject(hObject);
|
||||
}
|
||||
|
||||
[DllImport("gdi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern bool DeleteObject(IntPtr hObject);
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern int DeleteObject(IntPtr hIcon);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
// <copyright file="GetDeviceCaps.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 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);
|
||||
}
|
||||
}
|
39
NativeDllImport/GetIcon.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
// <copyright file="GetIcon.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 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
|
||||
public const uint ShgfiLARGEICON = 0x000000000; // get large icon
|
||||
public const uint ShgfiSMALLICON = 0x000000001; // get small icon
|
||||
public const uint ShgfiOPENICON = 0x000000002; // get open icon
|
||||
public const uint FileAttributeDirectory = 0x00000010;
|
||||
public const uint FileAttributeNormal = 0x00000080;
|
||||
public const int IldTransparent = 0x00000001;
|
||||
|
||||
/// <summary>
|
||||
/// comctl32 ImageList_GetIcon(IntPtr himl, int i, int flags).
|
||||
/// </summary>
|
||||
/// <param name="himl">himl.</param>
|
||||
/// <param name="i">i.</param>
|
||||
/// <param name="flags">flags.</param>
|
||||
/// <returns>IntPtr.</returns>
|
||||
[DllImport("comctl32", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
internal static extern IntPtr ImageList_GetIcon(
|
||||
IntPtr himl,
|
||||
int i,
|
||||
int flags);
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
// <copyright file="IShellItem.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[ComImport]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
|
||||
public interface IShellItem
|
||||
{
|
||||
void BindToHandler(
|
||||
IntPtr pbc,
|
||||
[MarshalAs(UnmanagedType.LPStruct)] Guid bhid,
|
||||
[MarshalAs(UnmanagedType.LPStruct)] Guid riid,
|
||||
out IntPtr ppv);
|
||||
|
||||
void GetParent(out IShellItem ppsi);
|
||||
|
||||
void GetDisplayName(SIGDN sigdnName, out IntPtr ppszName);
|
||||
|
||||
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
|
||||
|
||||
void Compare(IShellItem psi, uint hint, out int piOrder);
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
// <copyright file="IShellItemImageFactory.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[ComImport]
|
||||
[Guid("bcc18b79-ba16-442f-80c4-8a59c30c463b")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IShellItemImageFactory
|
||||
{
|
||||
void GetImage(
|
||||
[In, MarshalAs(UnmanagedType.Struct)] SIZE size,
|
||||
[In] SIIGBF flags,
|
||||
[Out] out IntPtr phbm);
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
// <copyright file="SHCreateItemFromParsingName.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
|
||||
{
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
|
||||
internal static extern void SHCreateItemFromParsingName(
|
||||
[In][MarshalAs(UnmanagedType.LPWStr)] string pszPath,
|
||||
[In] IntPtr pbc,
|
||||
[In][MarshalAs(UnmanagedType.LPStruct)] Guid riid,
|
||||
[Out][MarshalAs(UnmanagedType.Interface, IidParameterIndex = 2)] out IShellItem ppv);
|
||||
}
|
||||
}
|
55
NativeDllImport/SHGetFileInfo.cs
Normal file
|
@ -0,0 +1,55 @@
|
|||
// <copyright file="SHGetFileInfo.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
|
||||
{
|
||||
private const int maxPath = 256;
|
||||
|
||||
internal static IntPtr Shell32SHGetFileInfo(
|
||||
string pszPath,
|
||||
uint dwFileAttributes,
|
||||
ref SHFILEINFO psfi,
|
||||
uint cbFileInfo,
|
||||
uint uFlags)
|
||||
{
|
||||
return SHGetFileInfo(
|
||||
pszPath,
|
||||
dwFileAttributes,
|
||||
ref psfi,
|
||||
cbFileInfo,
|
||||
uFlags);
|
||||
}
|
||||
|
||||
[DllImport("Shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern IntPtr SHGetFileInfo(
|
||||
string pszPath,
|
||||
uint dwFileAttributes,
|
||||
ref SHFILEINFO psfi,
|
||||
uint cbFileInfo,
|
||||
uint uFlags);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
|
||||
internal struct SHFILEINFO
|
||||
{
|
||||
public const int NAMESIZE = 80;
|
||||
public IntPtr hIcon;
|
||||
public int iIcon;
|
||||
public uint dwAttributes;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = maxPath)]
|
||||
public string szDisplayName;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAMESIZE)]
|
||||
public string szTypeName;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
// <copyright file="SIGDN.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
public enum SIGDN : uint
|
||||
{
|
||||
NORMALDISPLAY = 0,
|
||||
PARENTRELATIVEPARSING = 0x80018001,
|
||||
PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
|
||||
DESKTOPABSOLUTEPARSING = 0x80028000,
|
||||
PARENTRELATIVEEDITING = 0x80031001,
|
||||
DESKTOPABSOLUTEEDITING = 0x8004c000,
|
||||
FILESYSPATH = 0x80058000,
|
||||
URL = 0x80068000,
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
// <copyright file="SIIGBF.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
using System;
|
||||
|
||||
[Flags]
|
||||
public enum SIIGBF
|
||||
{
|
||||
SIIGBF_RESIZETOFIT = 0x00,
|
||||
SIIGBF_BIGGERSIZEOK = 0x01,
|
||||
SIIGBF_MEMORYONLY = 0x02,
|
||||
SIIGBF_ICONONLY = 0x04,
|
||||
SIIGBF_THUMBNAILONLY = 0x08,
|
||||
SIIGBF_INCACHEONLY = 0x10,
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
// <copyright file="SIZE.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
public struct SIZE
|
||||
{
|
||||
public int Cx;
|
||||
public int Cy;
|
||||
|
||||
public SIZE(int cx, int cy)
|
||||
{
|
||||
Cx = cx;
|
||||
Cy = cy;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
||||
|
|