Compare commits
591 commits
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 | |||
ceb55191f2 | |||
ecebddb0cb | |||
010af66b42 | |||
a5e1ff4c25 | |||
83a9d29da2 | |||
c1beb9ffa9 | |||
85fd0371b5 | |||
2932ddf1b6 | |||
9aa75c72b8 | |||
cdb5cb56f1 | |||
c00c02356a | |||
6162364577 | |||
f48306c27d | |||
7ff2768f94 | |||
bddf4ccea1 | |||
754c4a2218 | |||
e85dcc39d6 | |||
4f74970e68 | |||
480b2636aa | |||
a72defa020 | |||
a45d0d8017 | |||
47a66d7bce | |||
45fe4084ee | |||
4ab7755ca7 | |||
5bb592c066 | |||
808ab532c9 | |||
a2f697a49d | |||
da00b14494 | |||
6b5772bbd8 | |||
49c6ef05f0 | |||
cbdef123ba | |||
684babe9db | |||
7426a44409 | |||
09e9751ae8 | |||
d4bf10f174 | |||
f2c20c5a5e | |||
713ae57f70 | |||
bec1b3ff28 | |||
6eea1d9b4c | |||
9a349a3848 | |||
0978b4c25d | |||
4638557984 | |||
2e29fc0a3e | |||
a3c1956fea | |||
5f050c86e8 | |||
d732fea62e | |||
bc8f0c8069 | |||
512c747a71 | |||
15c7ca0803 | |||
16ee650f8a | |||
660212720c | |||
8d3a80a5ea | |||
274682d26e | |||
de4fb95df3 | |||
3e5486ab66 | |||
138a1a3830 | |||
72abbf68a6 | |||
c7d9f4cfc5 | |||
3846c09677 | |||
fed20a59d7 | |||
3f7bacd93a | |||
ae440e4389 | |||
26f46f1038 | |||
6c63a4a6fe | |||
e0720a1555 | |||
55324c8a58 | |||
c3fa2cf404 | |||
506e573cd5 | |||
35011aeb57 | |||
6b0fb14ec6 | |||
6038d2ae20 | |||
bcc16a9dfd | |||
b8a305e257 | |||
b0cdfe1ab2 | |||
b595ecbe8a | |||
af05e3ba2d | |||
14cc6af244 | |||
d38750c0b9 | |||
2686adf0df | |||
2d2eb88339 | |||
48805cc0f2 | |||
0ecb70c878 | |||
72f1a3f5b3 | |||
b178e606ef | |||
b092f138e2 | |||
4e9636ba94 |
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
.github/workflows/dotnetframework.yml
vendored
|
@ -7,10 +7,10 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup Nuget.exe
|
||||
uses: warrenbuckley/Setup-Nuget@v1
|
||||
uses: nuget/setup-nuget@v1
|
||||
- name: Nuget Restore
|
||||
run: nuget restore SystemTrayMenu.sln
|
||||
- name: Setup MSBuild.exe
|
||||
uses: warrenbuckley/Setup-MSBuild@v1
|
||||
uses: microsoft/setup-msbuild@v1
|
||||
- name: Build with MSBuild
|
||||
run: msbuild SystemTrayMenu.sln -p:Configuration=Release
|
||||
|
|
129
Business/App.cs
|
@ -1,41 +1,90 @@
|
|||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.Business;
|
||||
using SystemTrayMenu.DataClasses;
|
||||
using SystemTrayMenu.Helper;
|
||||
using SystemTrayMenu.UserInterface;
|
||||
using SystemTrayMenu.Utilities;
|
||||
|
||||
// <copyright file="App.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// App contains the notifyicon, the taskbarform and the menus.
|
||||
/// </summary>
|
||||
internal class App : IDisposable
|
||||
{
|
||||
private readonly MenuNotifyIcon menuNotifyIcon = new MenuNotifyIcon();
|
||||
private readonly Menus menus = new Menus();
|
||||
private TaskbarForm taskbarForm = new TaskbarForm();
|
||||
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();
|
||||
taskbarForm.Activated += TasbkarItemActivated;
|
||||
taskbarForm.Resize += TaskbarForm_Resize;
|
||||
taskbarForm.FormClosed += TaskbarForm_FormClosed;
|
||||
taskbarForm.Deactivate += TaskbarForm_Deactivate;
|
||||
|
||||
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()
|
||||
{
|
||||
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)
|
||||
|
@ -43,39 +92,23 @@ namespace SystemTrayMenu
|
|||
Application.Exit();
|
||||
}
|
||||
|
||||
private void TaskbarForm_Resize(object sender, EventArgs e)
|
||||
{
|
||||
SetStateNormal();
|
||||
}
|
||||
|
||||
internal void TasbkarItemActivated(object sender, EventArgs e)
|
||||
{
|
||||
SetStateNormal();
|
||||
taskbarForm.Activate();
|
||||
taskbarForm.Focus();
|
||||
menus.SwitchOpenCloseByTaskbarItem();
|
||||
}
|
||||
|
||||
private void TaskbarForm_Deactivate(object sender, EventArgs e)
|
||||
{
|
||||
SetStateNormal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This ensures that next click on taskbaritem works as activate event/click event
|
||||
/// This ensures that next click on taskbaritem works as activate event/click event.
|
||||
/// </summary>
|
||||
private void SetStateNormal()
|
||||
private void SetStateNormal(object sender, EventArgs e)
|
||||
{
|
||||
if (Form.ActiveForm == taskbarForm)
|
||||
{
|
||||
taskbarForm.WindowState = FormWindowState.Normal;
|
||||
}
|
||||
}
|
||||
public void Dispose()
|
||||
|
||||
private void TasbkarItemActivated(object sender, EventArgs e)
|
||||
{
|
||||
taskbarForm.Dispose();
|
||||
menus.Dispose();
|
||||
menuNotifyIcon.Dispose();
|
||||
SetStateNormal(sender, e);
|
||||
taskbarForm.Activate();
|
||||
taskbarForm.Focus();
|
||||
menus.SwitchOpenCloseByTaskbarItem();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,67 +1,60 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.DataClasses;
|
||||
using SystemTrayMenu.Helper;
|
||||
using SystemTrayMenu.Utilities;
|
||||
using Menu = SystemTrayMenu.UserInterface.Menu;
|
||||
// <copyright file="KeyboardInput.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.Handler
|
||||
{
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.DataClasses;
|
||||
using SystemTrayMenu.Helper;
|
||||
using SystemTrayMenu.Utilities;
|
||||
using Menu = SystemTrayMenu.UserInterface.Menu;
|
||||
|
||||
internal class KeyboardInput : IDisposable
|
||||
{
|
||||
internal event EventHandlerEmpty HotKeyPressed;
|
||||
internal event EventHandlerEmpty ClosePressed;
|
||||
internal event Action<DataGridView, int> RowSelected;
|
||||
internal event Action<int, DataGridView> RowDeselected;
|
||||
internal Action<DataGridView, int> EnterPressed;
|
||||
internal event EventHandlerEmpty Cleared;
|
||||
|
||||
internal bool InUse = false;
|
||||
internal int iRowKey = -1;
|
||||
internal int iMenuKey = 0;
|
||||
|
||||
private readonly Menu[] menus;
|
||||
private readonly KeyboardHook hook = new KeyboardHook();
|
||||
private readonly KeyboardHook hook = new();
|
||||
|
||||
private int iRowKey = -1;
|
||||
private int iMenuKey;
|
||||
|
||||
public KeyboardInput(Menu[] menus)
|
||||
{
|
||||
this.menus = menus;
|
||||
}
|
||||
|
||||
public event Action HotKeyPressed;
|
||||
|
||||
public event Action ClosePressed;
|
||||
|
||||
public event Action<DataGridView, int> RowSelected;
|
||||
|
||||
public event Action<DataGridView, int> RowDeselected;
|
||||
|
||||
public event Action<DataGridView, int> EnterPressed;
|
||||
|
||||
public event Action Cleared;
|
||||
|
||||
public bool InUse { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
hook.KeyPressed -= Hook_KeyPressed;
|
||||
hook.Dispose();
|
||||
}
|
||||
|
||||
private int GetMenuIndex(in Menu currentMenu)
|
||||
{
|
||||
int index = 0;
|
||||
foreach (Menu menuFindIndex in menus.Where(m => m != null))
|
||||
{
|
||||
if (currentMenu == menuFindIndex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
internal void RegisterHotKey()
|
||||
public void RegisterHotKey()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Properties.Settings.Default.HotKey))
|
||||
{
|
||||
try
|
||||
{
|
||||
hook.RegisterHotKey();
|
||||
hook.KeyPressed += hook_KeyPressed;
|
||||
void hook_KeyPressed(object sender, KeyPressedEventArgs e)
|
||||
{
|
||||
HotKeyPressed?.Invoke();
|
||||
}
|
||||
hook.KeyPressed += Hook_KeyPressed;
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
|
@ -72,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:
|
||||
{
|
||||
|
@ -109,8 +113,9 @@ namespace SystemTrayMenu.Handler
|
|||
indexNew = indexMax;
|
||||
}
|
||||
|
||||
menus[indexNew].FocusTextBox();
|
||||
menus[indexNew]?.FocusTextBox();
|
||||
}
|
||||
|
||||
break;
|
||||
case Keys.Tab | Keys.Shift:
|
||||
{
|
||||
|
@ -127,64 +132,102 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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))
|
||||
int GetMenuIndex(in Menu currentMenu)
|
||||
{
|
||||
string letter = e.KeyChar.ToString(CultureInfo.InvariantCulture);
|
||||
int index = 0;
|
||||
foreach (Menu menuFindIndex in menus.Where(m => m != null))
|
||||
{
|
||||
if (currentMenu == menuFindIndex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Menu menu = menus[iMenuKey];
|
||||
menu.KeyPressedSearch(letter);
|
||||
index++;
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearIsSelectedByKey()
|
||||
{
|
||||
ClearIsSelectedByKey(iMenuKey, iRowKey);
|
||||
}
|
||||
|
||||
public void Select(DataGridView dgv, int i, bool refreshview)
|
||||
{
|
||||
int newiMenuKey = ((Menu)dgv.TopLevelControl).Level;
|
||||
if (i != iRowKey || newiMenuKey != iMenuKey)
|
||||
{
|
||||
ClearIsSelectedByKey();
|
||||
}
|
||||
|
||||
iRowKey = i;
|
||||
iMenuKey = newiMenuKey;
|
||||
|
||||
if (dgv.Rows.Count > i)
|
||||
{
|
||||
DataGridViewRow row = dgv.Rows[i];
|
||||
RowData rowData = (RowData)row.Cells[2].Value;
|
||||
if (rowData != null)
|
||||
{
|
||||
rowData.IsSelected = true;
|
||||
}
|
||||
|
||||
if (refreshview)
|
||||
{
|
||||
row.Selected = false;
|
||||
row.Selected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Hook_KeyPressed(object sender, KeyPressedEventArgs e)
|
||||
{
|
||||
HotKeyPressed?.Invoke();
|
||||
}
|
||||
|
||||
private bool IsAnyMenuSelectedByKey(
|
||||
ref DataGridView dgv,
|
||||
ref Menu menuFromSelected,
|
||||
|
@ -213,7 +256,7 @@ namespace SystemTrayMenu.Handler
|
|||
return isStillSelected;
|
||||
}
|
||||
|
||||
private void SelectByKey(Keys keys, string keyInput = "", bool KeepSelection = false)
|
||||
private void SelectByKey(Keys keys, string keyInput = "", bool keepSelection = false)
|
||||
{
|
||||
int iRowBefore = iRowKey;
|
||||
int iMenuBefore = iMenuKey;
|
||||
|
@ -226,7 +269,7 @@ namespace SystemTrayMenu.Handler
|
|||
bool isStillSelected = IsAnyMenuSelectedByKey(ref dgv, ref menuFromSelected, ref textselected);
|
||||
if (isStillSelected)
|
||||
{
|
||||
if (KeepSelection)
|
||||
if (keepSelection)
|
||||
{
|
||||
// If current selection is still valid for this search then skip selecting different item
|
||||
if (textselected.StartsWith(keyInput, true, CultureInfo.InvariantCulture))
|
||||
|
@ -248,110 +291,102 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case Keys.Up:
|
||||
if (SelectMatchedReverse(dgv, iRowKey) ||
|
||||
SelectMatchedReverse(dgv, dgv.Rows.Count - 1))
|
||||
{
|
||||
RowDeselected(iRowBefore, dgvBefore);
|
||||
RowDeselected(dgvBefore, iRowBefore);
|
||||
SelectRow(dgv, iRowKey);
|
||||
toClear = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case Keys.Down:
|
||||
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;
|
||||
|
@ -363,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;
|
||||
}
|
||||
|
@ -373,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
|
||||
|
@ -382,22 +417,94 @@ namespace SystemTrayMenu.Handler
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (isStillSelected && toClear)
|
||||
{
|
||||
ClearIsSelectedByKey(iMenuBefore, iRowBefore);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
RowSelected(dgv, iRowKey);
|
||||
}
|
||||
|
||||
private bool SelectMatched(DataGridView dgv,
|
||||
int indexStart, string keyInput = "")
|
||||
private bool SelectMatched(DataGridView dgv, int indexStart, string keyInput = "")
|
||||
{
|
||||
bool found = false;
|
||||
for (int i = indexStart; i < dgv.Rows.Count; i++)
|
||||
|
@ -408,11 +515,11 @@ namespace SystemTrayMenu.Handler
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
private bool SelectMatchedReverse(DataGridView dgv,
|
||||
int indexStart, string keyInput = "")
|
||||
private bool SelectMatchedReverse(DataGridView dgv, int indexStart, string keyInput = "")
|
||||
{
|
||||
bool found = false;
|
||||
for (int i = indexStart; i > -1; i--)
|
||||
|
@ -423,30 +530,11 @@ namespace SystemTrayMenu.Handler
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
public void Select(DataGridView dgv, int i, bool refreshview)
|
||||
{
|
||||
int newiMenuKey = ((Menu)dgv.TopLevelControl).Level;
|
||||
if (i != iRowKey || newiMenuKey != iMenuKey)
|
||||
{
|
||||
ClearIsSelectedByKey();
|
||||
}
|
||||
iRowKey = i;
|
||||
iMenuKey = newiMenuKey;
|
||||
DataGridViewRow row = dgv.Rows[i];
|
||||
RowData rowData = (RowData)row.Cells[2].Value;
|
||||
rowData.IsSelected = true;
|
||||
if (refreshview)
|
||||
{
|
||||
row.Selected = false; //event trigger
|
||||
row.Selected = true; //event trigger
|
||||
}
|
||||
}
|
||||
|
||||
private bool Select(DataGridView dgv, int i,
|
||||
string keyInput = "")
|
||||
private bool Select(DataGridView dgv, int i, string keyInput = "")
|
||||
{
|
||||
bool found = false;
|
||||
if (i > -1 &&
|
||||
|
@ -460,8 +548,8 @@ namespace SystemTrayMenu.Handler
|
|||
{
|
||||
iRowKey = rowData.RowIndex;
|
||||
rowData.IsSelected = true;
|
||||
row.Selected = false; //event trigger
|
||||
row.Selected = true; //event trigger
|
||||
row.Selected = false;
|
||||
row.Selected = true;
|
||||
if (row.Index < dgv.FirstDisplayedScrollingRowIndex)
|
||||
{
|
||||
dgv.FirstDisplayedScrollingRowIndex = row.Index;
|
||||
|
@ -477,12 +565,8 @@ namespace SystemTrayMenu.Handler
|
|||
found = true;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
internal void ClearIsSelectedByKey()
|
||||
{
|
||||
ClearIsSelectedByKey(iMenuKey, iRowKey);
|
||||
return found;
|
||||
}
|
||||
|
||||
private void ClearIsSelectedByKey(int menuIndex, int rowIndex)
|
||||
|
@ -494,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
2262
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,46 +1,50 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.Utilities;
|
||||
// <copyright file="Program.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu
|
||||
{
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.Utilities;
|
||||
|
||||
internal static class Program
|
||||
{
|
||||
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;
|
||||
void ThreadException(object s, ThreadExceptionEventArgs t)
|
||||
{
|
||||
AskUserSendError(t.Exception);
|
||||
}
|
||||
|
||||
Application.ThreadException += Application_ThreadException;
|
||||
Scaling.Initialize();
|
||||
FolderOptions.Initialize();
|
||||
|
||||
using (new App())
|
||||
{
|
||||
isStartup = false;
|
||||
Log.WriteApplicationRuns();
|
||||
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);
|
||||
}
|
||||
|
@ -48,22 +52,36 @@ namespace SystemTrayMenu
|
|||
{
|
||||
Log.Close();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
#warning [Feature] When Error ask user to send us #47, todo own dialog, lines here too long
|
||||
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());
|
||||
}
|
||||
Log.ProcessStart("mailto:" + "markus@hofknecht.eu" +
|
||||
"?subject=SystemTrayMenu Bug reported " +
|
||||
Assembly.GetEntryAssembly().GetName().Version +
|
||||
"&body=" + ex.ToString());
|
||||
}
|
||||
|
||||
if (!isStartup && dialogResult != DialogResult.Cancel)
|
||||
{
|
||||
AppRestart.ByThreadException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
using System;
|
||||
using SystemTrayMenu.Utilities;
|
||||
using Timer = System.Windows.Forms.Timer;
|
||||
// <copyright file="WaitLeave.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.Handler
|
||||
{
|
||||
using System;
|
||||
using SystemTrayMenu.Utilities;
|
||||
using Timer = System.Windows.Forms.Timer;
|
||||
|
||||
internal class WaitLeave : IDisposable
|
||||
{
|
||||
public event EventHandlerEmpty LeaveTriggered;
|
||||
|
||||
private readonly Timer timerLeaveCheck = new Timer();
|
||||
|
||||
public bool IsRunning => timerLeaveCheck.Enabled;
|
||||
private readonly Timer timerLeaveCheck = new();
|
||||
|
||||
public WaitLeave(int timeUntilTriggered)
|
||||
{
|
||||
|
@ -18,26 +18,29 @@ namespace SystemTrayMenu.Handler
|
|||
timerLeaveCheck.Tick += TimerLeaveCheckTick;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
public event Action LeaveTriggered;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
timerLeaveCheck.Tick -= TimerLeaveCheckTick;
|
||||
timerLeaveCheck.Dispose();
|
||||
}
|
||||
|
||||
internal void Start()
|
||||
{
|
||||
timerLeaveCheck.Stop();
|
||||
timerLeaveCheck.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
internal void Stop()
|
||||
{
|
||||
timerLeaveCheck.Stop();
|
||||
}
|
||||
|
||||
private void TimerLeaveCheckTick(object sender, EventArgs e)
|
||||
internal void TimerLeaveCheckTick(object sender, EventArgs e)
|
||||
{
|
||||
timerLeaveCheck.Stop();
|
||||
LeaveTriggered?.Invoke();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
timerLeaveCheck.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,63 +1,86 @@
|
|||
using System;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.DataClasses;
|
||||
using SystemTrayMenu.UserInterface;
|
||||
using SystemTrayMenu.Utilities;
|
||||
using Timer = System.Windows.Forms.Timer;
|
||||
// <copyright file="WaitToLoadMenu.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.Handler
|
||||
{
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.DataClasses;
|
||||
using SystemTrayMenu.UserInterface;
|
||||
using SystemTrayMenu.Utilities;
|
||||
using Timer = System.Windows.Forms.Timer;
|
||||
|
||||
internal class WaitToLoadMenu : IDisposable
|
||||
{
|
||||
internal event Action<RowData> StartLoadMenu;
|
||||
internal event Action<int> CloseMenu;
|
||||
internal event EventHandlerEmpty StopLoadMenu;
|
||||
internal event Action<DataGridView, int> MouseEnterOk;
|
||||
private readonly Timer timerStartLoad = new();
|
||||
private DataGridView dgv;
|
||||
private int rowIndex;
|
||||
private DataGridView dgvTmp;
|
||||
private int rowIndexTmp;
|
||||
|
||||
private readonly Timer timerStartLoad = new Timer();
|
||||
private DataGridView dgv = null;
|
||||
private int rowIndex = 0;
|
||||
private DataGridView dgvTmp = null;
|
||||
private int rowIndexTmp = 0;
|
||||
|
||||
internal bool MouseActive = false;
|
||||
private int mouseMoveEvents = 0;
|
||||
private int mouseMoveEvents;
|
||||
private DateTime dateTimeLastMouseMoveEvent = DateTime.Now;
|
||||
private bool checkForMouseActive = true;
|
||||
|
||||
internal WaitToLoadMenu()
|
||||
{
|
||||
timerStartLoad.Interval = 200;
|
||||
timerStartLoad.Interval = Properties.Settings.Default.TimeUntilOpens;
|
||||
timerStartLoad.Tick += WaitStartLoad_Tick;
|
||||
}
|
||||
|
||||
internal event Action<RowData> StartLoadMenu;
|
||||
|
||||
internal event Action<int> CloseMenu;
|
||||
|
||||
internal event Action StopLoadMenu;
|
||||
|
||||
internal event Action<DataGridView, int> MouseEnterOk;
|
||||
|
||||
internal bool MouseActive { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
timerStartLoad.Tick -= WaitStartLoad_Tick;
|
||||
timerStartLoad.Stop();
|
||||
timerStartLoad.Dispose();
|
||||
dgv?.Dispose();
|
||||
dgvTmp?.Dispose();
|
||||
}
|
||||
|
||||
internal void MouseEnter(object sender, DataGridViewCellEventArgs e)
|
||||
{
|
||||
if (MouseActive)
|
||||
{
|
||||
DataGridView dgv = (DataGridView)sender;
|
||||
MouseEnterOk(dgv, e.RowIndex);
|
||||
timerStartLoad.Stop();
|
||||
StopLoadMenu?.Invoke();
|
||||
checkForMouseActive = true;
|
||||
SetData(dgv, e.RowIndex);
|
||||
timerStartLoad.Start();
|
||||
if (dgv.Rows.Count > e.RowIndex)
|
||||
{
|
||||
MouseEnterOk(dgv, e.RowIndex);
|
||||
timerStartLoad.Stop();
|
||||
StopLoadMenu?.Invoke();
|
||||
checkForMouseActive = true;
|
||||
SetData(dgv, e.RowIndex);
|
||||
timerStartLoad.Start();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.dgvTmp = (DataGridView)sender;
|
||||
this.rowIndexTmp = e.RowIndex;
|
||||
dgvTmp = (DataGridView)sender;
|
||||
rowIndexTmp = e.RowIndex;
|
||||
}
|
||||
}
|
||||
|
||||
internal void RowSelected(DataGridView dgv, int rowIndex)
|
||||
{
|
||||
timerStartLoad.Stop();
|
||||
StopLoadMenu?.Invoke();
|
||||
SetData(dgv, rowIndex);
|
||||
MouseActive = false;
|
||||
checkForMouseActive = false;
|
||||
timerStartLoad.Start();
|
||||
if (dgv.Rows.Count > rowIndex)
|
||||
{
|
||||
timerStartLoad.Stop();
|
||||
StopLoadMenu?.Invoke();
|
||||
SetData(dgv, rowIndex);
|
||||
MouseActive = false;
|
||||
checkForMouseActive = false;
|
||||
timerStartLoad.Start();
|
||||
}
|
||||
}
|
||||
|
||||
internal void MouseLeave(object sender, DataGridViewCellEventArgs e)
|
||||
|
@ -70,7 +93,7 @@ namespace SystemTrayMenu.Handler
|
|||
}
|
||||
}
|
||||
|
||||
internal void RowDeselected(int rowIndex, DataGridView dgv) //iMenuBefore not needed
|
||||
internal void RowDeselected(DataGridView dgv, int rowIndex)
|
||||
{
|
||||
timerStartLoad.Stop();
|
||||
StopLoadMenu?.Invoke();
|
||||
|
@ -80,12 +103,14 @@ namespace SystemTrayMenu.Handler
|
|||
|
||||
internal void ClickOpensInstantly(DataGridView dgv, int rowIndex)
|
||||
{
|
||||
timerStartLoad.Stop();
|
||||
StopLoadMenu?.Invoke();
|
||||
SetData(dgv, rowIndex);
|
||||
MouseActive = true;
|
||||
checkForMouseActive = false;
|
||||
CallOpenMenuNow();
|
||||
if (dgv.Rows.Count > rowIndex)
|
||||
{
|
||||
timerStartLoad.Stop();
|
||||
SetData(dgv, rowIndex);
|
||||
MouseActive = true;
|
||||
checkForMouseActive = false;
|
||||
CallOpenMenuNow();
|
||||
}
|
||||
}
|
||||
|
||||
internal void EnterOpensInstantly(DataGridView dgv, int rowIndex)
|
||||
|
@ -105,7 +130,7 @@ namespace SystemTrayMenu.Handler
|
|||
{
|
||||
if (!MouseActive)
|
||||
{
|
||||
if (mouseMoveEvents > 3)
|
||||
if (mouseMoveEvents > 6)
|
||||
{
|
||||
MouseActive = true;
|
||||
if (dgvTmp != null && !dgvTmp.IsDisposed)
|
||||
|
@ -113,6 +138,7 @@ namespace SystemTrayMenu.Handler
|
|||
MouseEnter(dgvTmp, new DataGridViewCellEventArgs(
|
||||
0, rowIndexTmp));
|
||||
}
|
||||
|
||||
mouseMoveEvents = 0;
|
||||
}
|
||||
else if (DateTime.Now - dateTimeLastMouseMoveEvent <
|
||||
|
@ -143,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);
|
||||
}
|
||||
|
@ -168,29 +192,29 @@ 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;
|
||||
}
|
||||
|
||||
private void ResetData(DataGridView dgv, int rowIndex)
|
||||
{
|
||||
if (dgv != null)
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
timerStartLoad.Stop();
|
||||
timerStartLoad.Dispose();
|
||||
dgv?.Dispose();
|
||||
dgvTmp?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
95
Config/AppColors.cs
Normal file
|
@ -0,0 +1,95 @@
|
|||
// <copyright file="AppColors.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu
|
||||
{
|
||||
using System.Drawing;
|
||||
|
||||
internal static class AppColors
|
||||
{
|
||||
public static Color Arrow { get; internal set; }
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
553
Config/Config.cs
|
@ -1,92 +1,529 @@
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.UserInterface.Dialogs;
|
||||
using SystemTrayMenu.Utilities;
|
||||
// <copyright file="Config.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu
|
||||
{
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
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
|
||||
{
|
||||
public const string Language = "en";
|
||||
private static readonly Icon SystemTrayMenu = new Icon(Properties.Resources.SystemTrayMenu, SystemInformation.SmallIconSize);
|
||||
private static readonly Icon IconRootFolder = GetIconSTA(Path, Path, false, IconSize.Small, true);
|
||||
|
||||
public static string Path => Properties.Settings.Default.PathDirectory;
|
||||
private static bool readDarkModeDone;
|
||||
private static bool isDarkMode;
|
||||
private static bool readHideFileExtdone;
|
||||
private static bool isHideFileExtension;
|
||||
|
||||
public static void UpgradeIfNotUpgraded()
|
||||
public static string Path => Settings.Default.PathDirectory;
|
||||
|
||||
public static string SearchPattern => Settings.Default.SearchPattern;
|
||||
|
||||
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))
|
||||
{
|
||||
// configs located at "%localappdata%\<AssemblyCompany>\"
|
||||
Properties.Settings.Default.Upgrade();
|
||||
|
||||
Properties.Settings.Default.IsUpgraded = true;
|
||||
Properties.Settings.Default.Save();
|
||||
|
||||
FileVersionInfo versionInfo = FileVersionInfo.
|
||||
GetVersionInfo(Assembly.GetEntryAssembly().Location);
|
||||
Log.Info($"Settings upgraded from " +
|
||||
$"%localappdata%\\{versionInfo.CompanyName}\\");
|
||||
Settings.Default.PathIcoDirectory = System.IO.Path.Combine(
|
||||
System.IO.Path.Combine(
|
||||
Environment.GetFolderPath(
|
||||
Environment.SpecialFolder.ApplicationData), $"SystemTrayMenu"), "ico");
|
||||
if (!Directory.Exists(Settings.Default.PathIcoDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(Settings.Default.PathIcoDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool LoadOrSetByUser()
|
||||
public static void Dispose()
|
||||
{
|
||||
bool pathOK = Directory.Exists(
|
||||
Properties.Settings.Default.PathDirectory);
|
||||
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");
|
||||
MessageBox.Show(textFirstStart, Translator.GetText("SystemTrayMenu"),
|
||||
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,
|
||||
"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))
|
||||
Log.ProcessStart("https://github.com/Hofknecht/SystemTrayMenu#FAQ");
|
||||
}
|
||||
|
||||
internal static void ShowSupportSystemTrayMenu()
|
||||
{
|
||||
Log.ProcessStart("https://github.com/Hofknecht/SystemTrayMenu#donations");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the OS setting whether dark mode is enabled.
|
||||
/// </summary>
|
||||
/// <returns>true = Dark mode; false = Light mode.</returns>
|
||||
internal static bool IsDarkMode()
|
||||
{
|
||||
if (!readDarkModeDone)
|
||||
{
|
||||
Process.Start(browserPath, "https://github.com/Hofknecht/SystemTrayMenu#FAQ");
|
||||
// 0 = Dark mode, 1 = Light mode
|
||||
if (Settings.Default.IsDarkModeAlwaysOn ||
|
||||
IsRegistryValueThisValue(
|
||||
@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize",
|
||||
"AppsUseLightTheme",
|
||||
"0"))
|
||||
{
|
||||
isDarkMode = true;
|
||||
}
|
||||
|
||||
readDarkModeDone = true;
|
||||
}
|
||||
|
||||
return isDarkMode;
|
||||
}
|
||||
|
||||
internal static void ResetReadDarkModeDone()
|
||||
{
|
||||
isDarkMode = false;
|
||||
readDarkModeDone = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the OS setting whether HideFileExt enabled.
|
||||
/// </summary>
|
||||
/// <returns>isHideFileExtension.</returns>
|
||||
internal static bool IsHideFileExtension()
|
||||
{
|
||||
if (!readHideFileExtdone)
|
||||
{
|
||||
// 0 = To show extensions, 1 = To hide extensions
|
||||
if (IsRegistryValueThisValue(
|
||||
@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced",
|
||||
"HideFileExt",
|
||||
"1"))
|
||||
{
|
||||
isHideFileExtension = true;
|
||||
}
|
||||
|
||||
readHideFileExtdone = true;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
try
|
||||
{
|
||||
object registryHideFileExt = Registry.GetValue(keyName, valueName, 1);
|
||||
|
||||
if (registryHideFileExt == null)
|
||||
{
|
||||
Log.Info($"Could not read registry keyName:{keyName} valueName:{valueName}");
|
||||
}
|
||||
else if (registryHideFileExt.ToString() == value)
|
||||
{
|
||||
isRegistryValueThisValue = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is System.Security.SecurityException ||
|
||||
ex is IOException)
|
||||
{
|
||||
Log.Warn($"Could not read registry keyName:{keyName} valueName:{valueName}", ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +1,90 @@
|
|||
using System.Drawing;
|
||||
using System.Reflection;
|
||||
// <copyright file="MenuDefines.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu
|
||||
{
|
||||
using System.Drawing;
|
||||
|
||||
internal static class MenuDefines
|
||||
{
|
||||
internal static string NotifyIconText = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyTitleAttribute>().Title;
|
||||
internal static readonly Color File = Color.White;
|
||||
internal static readonly Color Folder = Color.White;
|
||||
internal static readonly Color ColorSelectedItem = AppColors.Blue;
|
||||
internal static readonly Color ColorSelectedItemBorder = AppColors.BlueBorder;
|
||||
internal static readonly Color ColorOpenFolder = AppColors.Green;
|
||||
internal static readonly Color ColorOpenFolderBorder = AppColors.GreenBorder;
|
||||
internal static readonly Color ColorTitleWarning = AppColors.Red;
|
||||
internal static readonly Color ColorTitleSelected = AppColors.Yellow;
|
||||
internal static readonly Color ColorTitleBackground = AppColors.Azure;
|
||||
internal const int Scrollspeed = 4;
|
||||
internal const int TimeUntilClose = 1000;
|
||||
internal const int MenusMax = 50;
|
||||
internal const int LengthMax = 37;
|
||||
internal static float MaxMenuWidth = 300;
|
||||
}
|
||||
internal const int Scrollspeed = 3;
|
||||
|
||||
internal static class AppColors
|
||||
{
|
||||
internal static readonly Color Blue = Color.FromArgb(204, 232, 255);
|
||||
internal static readonly Color BlueBorder = Color.FromArgb(153, 209, 255);
|
||||
internal static readonly Color Green = Color.FromArgb(194, 245, 222);
|
||||
internal static readonly Color GreenBorder = Color.FromArgb(153, 255, 165);
|
||||
internal static readonly Color Red = Color.FromArgb(255, 204, 232);
|
||||
internal static readonly Color Yellow = Color.LightYellow;
|
||||
internal static readonly Color Azure = Color.Azure;
|
||||
public static Color ColorSelectedItem
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Config.IsDarkMode())
|
||||
{
|
||||
return AppColors.DarkModeSelecetedItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppColors.SelectedItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Color ColorSelectedItemBorder
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Config.IsDarkMode())
|
||||
{
|
||||
return AppColors.DarkModeSelectedItemBorder;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppColors.SelectedItemBorder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Color ColorOpenFolder
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Config.IsDarkMode())
|
||||
{
|
||||
return AppColors.DarkModeOpenFolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppColors.OpenFolder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Color ColorOpenFolderBorder
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Config.IsDarkMode())
|
||||
{
|
||||
return AppColors.DarkModeOpenFolderBorder;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppColors.OpenFolderBorder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Color ColorIcons
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Config.IsDarkMode())
|
||||
{
|
||||
return AppColors.DarkModeIcons;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppColors.Icons;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,38 @@
|
|||
using System.Collections.Generic;
|
||||
// <copyright file="MenuData.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.DataClasses
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
|
||||
internal enum MenuDataValidity
|
||||
{
|
||||
AbortedOrUnknown,
|
||||
Undefined,
|
||||
Valid,
|
||||
Empty,
|
||||
NoAccess
|
||||
NoAccess,
|
||||
}
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
|
@ -1,381 +1,302 @@
|
|||
using IWshRuntimeLibrary;
|
||||
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 SystemTrayMenu.Utilities;
|
||||
using TAFactory.IconPack;
|
||||
using Menu = SystemTrayMenu.UserInterface.Menu;
|
||||
// <copyright file="RowData.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.DataClasses
|
||||
{
|
||||
internal class RowData : IDisposable
|
||||
{
|
||||
internal FileInfo FileInfo;
|
||||
internal Menu SubMenu;
|
||||
internal bool IsMenuOpen;
|
||||
internal bool IsSelected;
|
||||
internal bool ContainsMenu;
|
||||
internal bool IsContextMenuOpen;
|
||||
private static DateTime ContextMenuClosed;
|
||||
internal bool IsResolvedLnk;
|
||||
internal bool HiddenEntry;
|
||||
internal string TargetFilePath;
|
||||
internal string TargetFilePathOrig;
|
||||
internal int RowIndex;
|
||||
private string WorkingDirectory;
|
||||
private string Arguments;
|
||||
private string Text;
|
||||
private Icon Icon = null;
|
||||
private bool diposeIcon = true;
|
||||
private bool isDisposed = false;
|
||||
internal int MenuLevel;
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.Utilities;
|
||||
using static SystemTrayMenu.Utilities.IconReader;
|
||||
using Menu = SystemTrayMenu.UserInterface.Menu;
|
||||
|
||||
internal class RowData
|
||||
{
|
||||
private static DateTime contextMenuClosed;
|
||||
private Icon icon;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RowData"/> class.
|
||||
/// empty dummy.
|
||||
/// </summary>
|
||||
internal RowData()
|
||||
{
|
||||
}
|
||||
|
||||
internal void SetText(string text)
|
||||
/// <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)
|
||||
{
|
||||
Text = text;
|
||||
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 IsClicking { get; set; }
|
||||
|
||||
internal bool IsSelected { get; set; }
|
||||
|
||||
internal bool IsContextMenuOpen { get; set; }
|
||||
|
||||
internal bool HiddenEntry { get; set; }
|
||||
|
||||
internal int RowIndex { get; set; }
|
||||
|
||||
internal bool IconLoading { get; set; }
|
||||
|
||||
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;
|
||||
row[0] = data.icon;
|
||||
}
|
||||
|
||||
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.GetFolderIcon(TargetFilePath,
|
||||
IconReader.FolderType.Closed, false);
|
||||
icon = GetFolderIconWithCache(Path, ShowOverlay, updateIconInBackground, IsMainMenu, out bool loading);
|
||||
IconLoading = loading;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool handled = false;
|
||||
string fileExtension = Path.GetExtension(TargetFilePath);
|
||||
|
||||
if (fileExtension == ".lnk")
|
||||
{
|
||||
handled = SetLnk(ref isLnkDirectory,
|
||||
ref resolvedLnkPath);
|
||||
}
|
||||
else if (fileExtension == ".url")
|
||||
{
|
||||
handled = SetUrl();
|
||||
}
|
||||
else if (fileExtension == ".sln")
|
||||
{
|
||||
handled = SetSln();
|
||||
}
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
try
|
||||
{
|
||||
Icon = IconReader.GetFileIconWithCache(TargetFilePath, false);
|
||||
diposeIcon = false;
|
||||
|
||||
// other project -> fails sometimes
|
||||
//icon = IconHelper.ExtractIcon(TargetFilePath, 0);
|
||||
|
||||
// standard way -> fails sometimes
|
||||
//icon = Icon.ExtractAssociatedIcon(filePath);
|
||||
|
||||
// API Code Pack -> fails sometimes
|
||||
//ShellFile shellFile = ShellFile.FromFilePath(filePath);
|
||||
//Bitmap shellThumb = shellFile.Thumbnail.ExtraLargeBitmap;
|
||||
}
|
||||
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 = GetFileIconWithCache(Path, ResolvedPath, ShowOverlay, updateIconInBackground, IsMainMenu, out bool loading);
|
||||
IconLoading = loading;
|
||||
}
|
||||
|
||||
return isLnkDirectory;
|
||||
}
|
||||
|
||||
|
||||
private bool SetLnk(ref bool isLnkDirectory,
|
||||
ref string resolvedLnkPath)
|
||||
{
|
||||
bool handled = false;
|
||||
resolvedLnkPath = LnkHelper.GetResolvedFileName(TargetFilePath);
|
||||
if (LnkHelper.IsDirectory(resolvedLnkPath))
|
||||
if (!IconLoading)
|
||||
{
|
||||
Icon = IconReader.GetFolderIcon(TargetFilePath,
|
||||
IconReader.FolderType.Open, true);
|
||||
handled = true;
|
||||
isLnkDirectory = true;
|
||||
}
|
||||
else if (string.IsNullOrEmpty(resolvedLnkPath))
|
||||
{
|
||||
Log.Info($"Resolve *.LNK '{TargetFilePath}' has no icon");
|
||||
#warning [Feature] Resolve network root #48, start here
|
||||
}
|
||||
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)
|
||||
if (icon == null)
|
||||
{
|
||||
iconLocation = iconLocation.Substring(0,
|
||||
iconLocation.Length - 2);
|
||||
if (System.IO.File.Exists(iconLocation))
|
||||
{
|
||||
try
|
||||
{
|
||||
Icon = Icon.ExtractAssociatedIcon(iconLocation);
|
||||
handled = true;
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
Log.Warn($"iconLocation:'{iconLocation}'", ex);
|
||||
}
|
||||
}
|
||||
icon = Properties.Resources.NotFound;
|
||||
}
|
||||
|
||||
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))
|
||||
else if (HiddenEntry)
|
||||
{
|
||||
string browserPath = FileUrl.GetDefaultBrowserPath();
|
||||
if (string.IsNullOrEmpty(browserPath))
|
||||
{
|
||||
Log.Info($"Resolve *.URL '{TargetFilePath}'" +
|
||||
$"No default browser found!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Icon = IconReader.GetFileIconWithCache(browserPath, false);
|
||||
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;
|
||||
icon = AddIconOverlay(icon, Properties.Resources.White50Percentage);
|
||||
}
|
||||
}
|
||||
|
||||
SetText($"{FileInfo.Name.Substring(0, FileInfo.Name.Length - 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;
|
||||
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 &&
|
||||
dgv != null &&
|
||||
dgv.Rows.Count > RowIndex &&
|
||||
(DateTime.Now - ContextMenuClosed).TotalMilliseconds > 200)
|
||||
(DateTime.Now - contextMenuClosed).TotalMilliseconds > 200)
|
||||
{
|
||||
IsContextMenuOpen = true;
|
||||
Color colorbefore = dgv.Rows[RowIndex].DefaultCellStyle.SelectionBackColor;
|
||||
dgv.Rows[RowIndex].DefaultCellStyle.SelectionBackColor =
|
||||
MenuDefines.ColorSelectedItem;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (!dgv.IsDisposed)
|
||||
{
|
||||
dgv.Rows[RowIndex].DefaultCellStyle.SelectionBackColor = colorbefore;
|
||||
TriggerFileWatcherChangeWorkaround();
|
||||
}
|
||||
|
||||
IsContextMenuOpen = false;
|
||||
ContextMenuClosed = DateTime.Now;
|
||||
contextMenuClosed = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
internal void DoubleClick(MouseEventArgs e)
|
||||
{
|
||||
if (e == null ||
|
||||
e.Button == MouseButtons.Left &&
|
||||
!ContainsMenu)
|
||||
void TriggerFileWatcherChangeWorkaround()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (Process p = new Process())
|
||||
{
|
||||
p.StartInfo = new ProcessStartInfo(TargetFilePath)
|
||||
{
|
||||
FileName = TargetFilePathOrig,
|
||||
Arguments = Arguments,
|
||||
WorkingDirectory = WorkingDirectory,
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = true
|
||||
};
|
||||
p.Start();
|
||||
};
|
||||
string parentFolder = System.IO.Path.GetDirectoryName(Path);
|
||||
Directory.GetFiles(parentFolder);
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warn($"path:'{TargetFilePath}'", ex);
|
||||
MessageBox.Show(ex.Message);
|
||||
Log.Warn($"{nameof(TriggerFileWatcherChangeWorkaround)} '{Path}'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (e == null ||
|
||||
e.Button == MouseButtons.Left &&
|
||||
ContainsMenu)
|
||||
{
|
||||
Log.ProcessStart("explorer.exe", TargetFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
internal void MouseClick(MouseEventArgs e, out bool toCloseByDoubleClick)
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!isDisposed)
|
||||
IsClicking = false;
|
||||
toCloseByDoubleClick = false;
|
||||
if (Properties.Settings.Default.OpenItemWithOneClick)
|
||||
{
|
||||
if (diposeIcon)
|
||||
OpenItem(e, ref toCloseByDoubleClick);
|
||||
}
|
||||
|
||||
if (Properties.Settings.Default.OpenDirectoryWithOneClick &&
|
||||
ContainsMenu && (e == null || e.Button == MouseButtons.Left))
|
||||
{
|
||||
Log.ProcessStart(Path);
|
||||
if (!Properties.Settings.Default.StaysOpenWhenItemClicked)
|
||||
{
|
||||
Icon?.Dispose();
|
||||
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))
|
||||
{
|
||||
ProcessStarted = true;
|
||||
string workingDirectory = System.IO.Path.GetDirectoryName(ResolvedPath);
|
||||
Log.ProcessStart(Path, string.Empty, false, workingDirectory, true, ResolvedPath);
|
||||
if (!Properties.Settings.Default.StaysOpenWhenItemClicked)
|
||||
{
|
||||
toCloseByOpenItem = true;
|
||||
}
|
||||
}
|
||||
isDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
16
GlobalSuppressions.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
// <copyright file="GlobalSuppressions.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA0001:XML comment analysis is disabled due to project configuration", Justification = "no idea what this is")]
|
||||
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "we need to document")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1601:Partial elements should be documented", Justification = "we need to document")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "we need to document")]
|
||||
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1000:Keywords should be spaced correctly", Justification = "new() should not be replaced by new() ")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:Prefix local calls with this", Justification = "Standard codecleanup removes the this")]
|
||||
|
||||
[assembly: SuppressMessage("Interoperability", "CA1416:Check platform compatibility", Justification = "this is a long way to get platform compatibility")]
|
106
Helpers/DgvMouseRow.cs
Normal file
|
@ -0,0 +1,106 @@
|
|||
// <copyright file="DgvMouseRow.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.Helper
|
||||
{
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
public class DgvMouseRow : IDisposable
|
||||
{
|
||||
private readonly Timer timerRaiseRowMouseLeave = new();
|
||||
private DataGridView dgv;
|
||||
private DataGridViewCellEventArgs eventArgs;
|
||||
|
||||
internal DgvMouseRow()
|
||||
{
|
||||
timerRaiseRowMouseLeave.Interval = 200;
|
||||
timerRaiseRowMouseLeave.Tick += TimerRaiseRowMouseLeave_Tick;
|
||||
}
|
||||
|
||||
~DgvMouseRow() // the finalizer
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
internal event Action<object, DataGridViewCellEventArgs> RowMouseEnter;
|
||||
|
||||
internal event Action<object, DataGridViewCellEventArgs> RowMouseLeave;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
internal void CellMouseEnter(object sender, DataGridViewCellEventArgs newEventArgs)
|
||||
{
|
||||
DataGridView newDgv = (DataGridView)sender;
|
||||
|
||||
if (dgv != newDgv || newEventArgs.RowIndex != eventArgs.RowIndex)
|
||||
{
|
||||
if (timerRaiseRowMouseLeave.Enabled)
|
||||
{
|
||||
timerRaiseRowMouseLeave.Stop();
|
||||
TriggerRowMouseLeave();
|
||||
}
|
||||
|
||||
TriggerRowMouseEnter(newDgv, newEventArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
timerRaiseRowMouseLeave.Stop();
|
||||
}
|
||||
|
||||
dgv = newDgv;
|
||||
eventArgs = newEventArgs;
|
||||
}
|
||||
|
||||
internal void CellMouseLeave(object sender, DataGridViewCellEventArgs e)
|
||||
{
|
||||
timerRaiseRowMouseLeave.Start();
|
||||
}
|
||||
|
||||
internal void MouseLeave(object sender, EventArgs e)
|
||||
{
|
||||
if (timerRaiseRowMouseLeave.Enabled)
|
||||
{
|
||||
timerRaiseRowMouseLeave.Stop();
|
||||
TriggerRowMouseLeave();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
timerRaiseRowMouseLeave.Tick -= TimerRaiseRowMouseLeave_Tick;
|
||||
timerRaiseRowMouseLeave.Dispose();
|
||||
dgv = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void TimerRaiseRowMouseLeave_Tick(object sender, EventArgs e)
|
||||
{
|
||||
timerRaiseRowMouseLeave.Stop();
|
||||
TriggerRowMouseLeave();
|
||||
}
|
||||
|
||||
private void TriggerRowMouseLeave()
|
||||
{
|
||||
if (dgv != null)
|
||||
{
|
||||
RowMouseLeave?.Invoke(dgv, eventArgs);
|
||||
}
|
||||
|
||||
dgv = null;
|
||||
eventArgs = null;
|
||||
}
|
||||
|
||||
private void TriggerRowMouseEnter(DataGridView dgv, DataGridViewCellEventArgs e)
|
||||
{
|
||||
RowMouseEnter?.Invoke(dgv, e);
|
||||
}
|
||||
}
|
||||
}
|
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,40 +1,61 @@
|
|||
using System;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.Utilities;
|
||||
// <copyright file="Fading.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.UserInterface
|
||||
namespace SystemTrayMenu.Helper
|
||||
{
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.Utilities;
|
||||
|
||||
public class Fading : IDisposable
|
||||
{
|
||||
internal event EventHandlerEmpty Hide;
|
||||
internal event EventHandlerEmpty Show;
|
||||
internal event EventHandler<double> ChangeOpacity;
|
||||
internal enum FadingState { Idle, Show, ShowTransparent, Hide };
|
||||
internal bool IsHiding => state == FadingState.Hide;
|
||||
|
||||
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;
|
||||
private const double Transparent = 0.80;
|
||||
private const double TransparentMinus = 0.60; //Transparent - StepIn
|
||||
private const double TransparentPlus = 0.85; //Transparent + StepOut
|
||||
private const double TransparentMinus = 0.60; // Transparent - StepIn
|
||||
private const double TransparentPlus = 0.85; // Transparent + StepOut
|
||||
private const double Shown = 1.00;
|
||||
private const double ShownMinus = 0.80; //Shown - StepIn
|
||||
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 = 0.00;
|
||||
private bool visible = false;
|
||||
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;
|
||||
}
|
||||
|
||||
~Fading() // the finalizer
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
internal event Action Hide;
|
||||
|
||||
internal event Action Show;
|
||||
|
||||
internal event Action<double> ChangeOpacity;
|
||||
|
||||
internal enum FadingState
|
||||
{
|
||||
Idle,
|
||||
Show,
|
||||
ShowTransparent,
|
||||
Hide,
|
||||
}
|
||||
|
||||
internal bool IsHiding => state == FadingState.Hide;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
internal void Fade(FadingState state)
|
||||
|
@ -42,6 +63,15 @@ namespace SystemTrayMenu.UserInterface
|
|||
StartStopTimer(state);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
timer.Tick -= Timer_Tick;
|
||||
timer.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void StartStopTimer(FadingState newState)
|
||||
{
|
||||
if (newState == FadingState.Idle)
|
||||
|
@ -53,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)
|
||||
|
@ -67,19 +101,28 @@ 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);
|
||||
}
|
||||
|
||||
break;
|
||||
case FadingState.ShowTransparent:
|
||||
if (!visible)
|
||||
|
@ -87,38 +130,48 @@ 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:
|
||||
default:
|
||||
|
@ -126,19 +179,5 @@ namespace SystemTrayMenu.UserInterface
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
timer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
Helpers/KeyPressedEventArgs.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
// <copyright file="KeyPressedEventArgs.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.Helper
|
||||
{
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
/// <summary>
|
||||
/// Event Args for the event that is fired after the hot key has been pressed.
|
||||
/// </summary>
|
||||
internal class KeyPressedEventArgs : EventArgs
|
||||
{
|
||||
private readonly Keys key;
|
||||
|
||||
internal KeyPressedEventArgs(KeyboardHookModifierKeys modifier, Keys key)
|
||||
{
|
||||
Modifier = modifier;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
internal KeyboardHookModifierKeys Modifier { get; }
|
||||
|
||||
internal Keys Key => key;
|
||||
}
|
||||
}
|
|
@ -1,70 +1,54 @@
|
|||
using System;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.UserInterface.Controls;
|
||||
using SystemTrayMenu.Utilities;
|
||||
// <copyright file="KeyboardHook.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.Helper
|
||||
{
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using SystemTrayMenu.UserInterface.HotkeyTextboxControl;
|
||||
using SystemTrayMenu.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// The enumeration of possible modifiers.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum KeyboardHookModifierKeys : uint
|
||||
{
|
||||
None = 0,
|
||||
Alt = 1,
|
||||
Control = 2,
|
||||
Shift = 4,
|
||||
Win = 8,
|
||||
}
|
||||
|
||||
public sealed class KeyboardHook : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the window that is used internally to get the messages.
|
||||
/// </summary>
|
||||
private class Window : NativeWindow, IDisposable
|
||||
{
|
||||
private const int WM_HOTKEY = 0x0312;
|
||||
|
||||
public Window()
|
||||
{
|
||||
// create the handle for the window.
|
||||
CreateHandle(new CreateParams());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden to get the notifications.
|
||||
/// </summary>
|
||||
/// <param name="m"></param>
|
||||
protected override void WndProc(ref Message m)
|
||||
{
|
||||
base.WndProc(ref m);
|
||||
|
||||
// check if we got a hot key pressed.
|
||||
if (m.Msg == WM_HOTKEY)
|
||||
{
|
||||
// get the keys.
|
||||
Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF);
|
||||
KeyboardHookModifierKeys modifier = (KeyboardHookModifierKeys)((int)m.LParam & 0xFFFF);
|
||||
|
||||
// invoke the event to notify the parent.
|
||||
if (KeyPressed != null)
|
||||
{
|
||||
KeyPressed(this, new KeyPressedEventArgs(modifier, key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<KeyPressedEventArgs> KeyPressed;
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DestroyHandle();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
private readonly Window _window = new Window();
|
||||
private int _currentId;
|
||||
private readonly Window window = new();
|
||||
private int currentId;
|
||||
|
||||
public KeyboardHook()
|
||||
{
|
||||
// register the event of the inner native window.
|
||||
_window.KeyPressed += delegate (object sender, KeyPressedEventArgs args)
|
||||
window.KeyPressed += Window_KeyPressed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A hot key has been pressed.
|
||||
/// </summary>
|
||||
internal event EventHandler<KeyPressedEventArgs> KeyPressed;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// unregister all the registered hot keys.
|
||||
for (int i = currentId; i > 0; i--)
|
||||
{
|
||||
KeyPressed?.Invoke(this, args);
|
||||
};
|
||||
DllImports.NativeMethods.User32UnregisterHotKey(window.Handle, i);
|
||||
}
|
||||
|
||||
// dispose the inner native window.
|
||||
window.KeyPressed -= Window_KeyPressed;
|
||||
window.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -87,21 +71,26 @@ namespace SystemTrayMenu.Helper
|
|||
{
|
||||
modifiers |= KeyboardHookModifierKeys.Alt;
|
||||
}
|
||||
if (modifiersString.ToUpperInvariant().Contains("CTRL", StringComparison.InvariantCulture))
|
||||
|
||||
if (modifiersString.ToUpperInvariant().Contains("CTRL", StringComparison.InvariantCulture) ||
|
||||
modifiersString.ToUpperInvariant().Contains("STRG", StringComparison.InvariantCulture))
|
||||
{
|
||||
modifiers |= KeyboardHookModifierKeys.Control;
|
||||
}
|
||||
|
||||
if (modifiersString.ToUpperInvariant().Contains("SHIFT", StringComparison.InvariantCulture))
|
||||
{
|
||||
modifiers |= KeyboardHookModifierKeys.Shift;
|
||||
}
|
||||
|
||||
if (modifiersString.ToUpperInvariant().Contains("WIN", StringComparison.InvariantCulture))
|
||||
{
|
||||
modifiers |= KeyboardHookModifierKeys.Win;
|
||||
}
|
||||
}
|
||||
|
||||
RegisterHotKey(modifiers,
|
||||
RegisterHotKey(
|
||||
modifiers,
|
||||
HotkeyControl.HotkeyFromString(
|
||||
Properties.Settings.Default.HotKey));
|
||||
}
|
||||
|
@ -111,18 +100,22 @@ namespace SystemTrayMenu.Helper
|
|||
/// </summary>
|
||||
/// <param name="modifier">The modifiers that are associated with the hot key.</param>
|
||||
/// <param name="key">The key itself that is associated with the hot key.</param>
|
||||
//internal void RegisterHotKey(KeyboardHookModifierKeys modifier, Keys key)
|
||||
internal void RegisterHotKey(KeyboardHookModifierKeys modifier, Keys key)
|
||||
{
|
||||
RegisterHotKey((uint)modifier, key);
|
||||
}
|
||||
|
||||
private void Window_KeyPressed(object sender, KeyPressedEventArgs e)
|
||||
{
|
||||
KeyPressed?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void RegisterHotKey(uint modifier, Keys key)
|
||||
{
|
||||
_currentId = _currentId + 1;
|
||||
currentId += 1;
|
||||
|
||||
if (!DllImports.NativeMethods.User32RegisterHotKey(
|
||||
_window.Handle, _currentId, modifier, (uint)key))
|
||||
window.Handle, currentId, modifier, (uint)key))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
Translator.GetText("Could not register the hot key."));
|
||||
|
@ -130,56 +123,44 @@ namespace SystemTrayMenu.Helper
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// A hot key has been pressed.
|
||||
/// Represents the window that is used internally to get the messages.
|
||||
/// </summary>
|
||||
internal event EventHandler<KeyPressedEventArgs> KeyPressed;
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
public void Dispose()
|
||||
private class Window : NativeWindow, IDisposable
|
||||
{
|
||||
// unregister all the registered hot keys.
|
||||
for (int i = _currentId; i > 0; i--)
|
||||
private const int WmHotkey = 0x0312;
|
||||
|
||||
public Window()
|
||||
{
|
||||
DllImports.NativeMethods.User32UnregisterHotKey(_window.Handle, i);
|
||||
// create the handle for the window.
|
||||
CreateHandle(new CreateParams());
|
||||
}
|
||||
|
||||
// dispose the inner native window.
|
||||
_window.Dispose();
|
||||
public event EventHandler<KeyPressedEventArgs> KeyPressed;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DestroyHandle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden to get the notifications.
|
||||
/// </summary>
|
||||
/// <param name="m">m.</param>
|
||||
protected override void WndProc(ref Message m)
|
||||
{
|
||||
base.WndProc(ref m);
|
||||
|
||||
// check if we got a hot key pressed.
|
||||
if (m.Msg == WmHotkey)
|
||||
{
|
||||
// get the keys.
|
||||
Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF);
|
||||
KeyboardHookModifierKeys modifier = (KeyboardHookModifierKeys)((int)m.LParam & 0xFFFF);
|
||||
|
||||
// invoke the event to notify the parent.
|
||||
KeyPressed?.Invoke(this, new KeyPressedEventArgs(modifier, key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event Args for the event that is fired after the hot key has been pressed.
|
||||
/// </summary>
|
||||
internal class KeyPressedEventArgs : EventArgs
|
||||
{
|
||||
private readonly KeyboardHookModifierKeys _modifier;
|
||||
private readonly Keys _key;
|
||||
|
||||
internal KeyPressedEventArgs(KeyboardHookModifierKeys modifier, Keys key)
|
||||
{
|
||||
_modifier = modifier;
|
||||
_key = key;
|
||||
}
|
||||
|
||||
internal KeyboardHookModifierKeys Modifier => _modifier;
|
||||
|
||||
internal Keys Key => _key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The enumeration of possible modifiers.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
internal enum KeyboardHookModifierKeys : uint
|
||||
{
|
||||
None = 0,
|
||||
Alt = 1,
|
||||
Control = 2,
|
||||
Shift = 4,
|
||||
Win = 8
|
||||
}
|
||||
}
|
||||
|
|
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
// <copyright file="WindowsExplorerSort.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.Helper
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
|
||||
internal class WindowsExplorerSort : IComparer<string>
|
||||
{
|
||||
public int Compare(string x, string y)
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using static SystemTrayMenu.DllImports.NativeMethods;
|
||||
// <copyright file="WindowsTaskbar.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
//Microsoft.WindowsAPICodePack.Taskbar.TaskbarManager do not have the bounds implemented?
|
||||
namespace SystemTrayMenu.Helper
|
||||
{
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using static SystemTrayMenu.DllImports.NativeMethods;
|
||||
|
||||
public enum TaskbarPosition
|
||||
{
|
||||
Unknown = -1,
|
||||
|
@ -19,43 +22,18 @@ namespace SystemTrayMenu.Helper
|
|||
{
|
||||
private const string ClassName = "Shell_TrayWnd";
|
||||
|
||||
public Rectangle Bounds
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
public TaskbarPosition Position
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
public Point Location => Bounds.Location;
|
||||
public Size Size => Bounds.Size;
|
||||
//Always returns false under Windows 7
|
||||
public bool AlwaysOnTop
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
public bool AutoHide
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public WindowsTaskbar()
|
||||
{
|
||||
IntPtr taskbarHandle = User32FindWindow(ClassName, null);
|
||||
|
||||
APPBARDATA data = new APPBARDATA
|
||||
APPBARDATA data = new()
|
||||
{
|
||||
cbSize = (uint)Marshal.SizeOf(typeof(APPBARDATA)),
|
||||
hWnd = taskbarHandle
|
||||
hWnd = taskbarHandle,
|
||||
};
|
||||
IntPtr result = Shell32SHAppBarMessage(ABM.GetTaskbarPos, ref data);
|
||||
if (result == IntPtr.Zero)
|
||||
{
|
||||
//throw new InvalidOperationException();
|
||||
Bounds = new Rectangle(20, 20, 20, 20);
|
||||
}
|
||||
else
|
||||
|
@ -70,5 +48,33 @@ namespace SystemTrayMenu.Helper
|
|||
AutoHide = (state & ABS.Autohide) == ABS.Autohide;
|
||||
}
|
||||
}
|
||||
|
||||
public Rectangle Bounds
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public TaskbarPosition Position
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Point Location => Bounds.Location;
|
||||
|
||||
public Size Size => Bounds.Size;
|
||||
|
||||
public bool AlwaysOnTop
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool AutoHide
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
6
LICENSE
|
@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively
|
|||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
TAMAHO SystemTrayMenu - an improved Windows toolbar
|
||||
Copyright (C) 2020 Markus Hofknecht
|
||||
TAMAHO SystemTrayMenu - browse and open your files easily
|
||||
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) 2020 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.
|
||||
|
|
|
@ -1,29 +1,18 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
// <copyright file="BringWindowToTop.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("user32.dll")]
|
||||
private static extern bool IsIconic(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
private static extern IntPtr GetForegroundWindow();
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern uint GetCurrentThreadId();
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern bool BringWindowToTop(IntPtr hWnd);
|
||||
|
||||
private const int SW_RESTORE = 9;
|
||||
private const int SwRestore = 9;
|
||||
|
||||
public static void ForceForegroundWindow(IntPtr hWnd)
|
||||
{
|
||||
|
@ -35,7 +24,7 @@ namespace SystemTrayMenu.DllImports
|
|||
|
||||
if (IsIconic(hWnd))
|
||||
{
|
||||
cmdShow = SW_RESTORE;
|
||||
cmdShow = SwRestore;
|
||||
}
|
||||
|
||||
if (foreThread != appThread)
|
||||
|
@ -51,5 +40,29 @@ namespace SystemTrayMenu.DllImports
|
|||
ShowWindow(hWnd, cmdShow);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern bool IsIconic(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern IntPtr GetForegroundWindow();
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern uint GetCurrentThreadId();
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern bool BringWindowToTop(IntPtr hWnd);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,25 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
// <copyright file="CreatePopupMenu.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
|
||||
{
|
||||
// The CreatePopupMenu function creates a drop-down menu, submenu, or shortcut menu. The menu is initially empty. You can insert or append menu items by using the InsertMenuItem function. You can also use the InsertMenu function to insert menu items and the AppendMenu function to append menu items.
|
||||
[DllImport("user32", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr CreatePopupMenu();
|
||||
|
||||
public static IntPtr User32CreatePopupMenu()
|
||||
{
|
||||
return CreatePopupMenu();
|
||||
}
|
||||
|
||||
// The CreatePopupMenu function creates a drop-down menu, submenu, or shortcut menu. The menu is initially empty. You can insert or append menu items by using the InsertMenuItem function. You can also use the InsertMenu function to insert menu items and the AppendMenu function to append menu items.
|
||||
[DllImport("user32", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern IntPtr CreatePopupMenu();
|
||||
}
|
||||
}
|
||||
|
|
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
|
||||
}
|
||||
}
|
19
NativeDllImport/DeleteObject.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
// <copyright file="DeleteObject.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("gdi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern int DeleteObject(IntPtr hIcon);
|
||||
}
|
||||
}
|
|
@ -1,16 +1,24 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
// <copyright file="DestroyIcon.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("User32.dll")]
|
||||
private static extern int DestroyIcon(IntPtr hIcon);
|
||||
|
||||
public static void User32DestroyIcon(IntPtr hIcon)
|
||||
{
|
||||
_ = DestroyIcon(hIcon);
|
||||
}
|
||||
|
||||
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern int DestroyIcon(IntPtr hIcon);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,25 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
// <copyright file="DestroyMenu.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
|
||||
{
|
||||
// The DestroyMenu function destroys the specified menu and frees any memory that the menu occupies.
|
||||
[DllImport("user32", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern bool DestroyMenu(IntPtr hMenu);
|
||||
|
||||
public static bool User32DestroyMenu(IntPtr hMenu)
|
||||
{
|
||||
return DestroyMenu(hMenu);
|
||||
}
|
||||
|
||||
// The DestroyMenu function destroys the specified menu and frees any memory that the menu occupies.
|
||||
[DllImport("user32", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern bool DestroyMenu(IntPtr hMenu);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
// <copyright file="FindExecuteable.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// wraps the methodcalls to native windows dll's.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern int FindExecutable(string lpFile, string lpDirectory, [Out] StringBuilder lpResult);
|
||||
|
||||
public static void Shell32FindExecutable(string lpFile, string lpDirectory, [Out] StringBuilder lpResult)
|
||||
{
|
||||
_ = FindExecutable(lpFile, lpDirectory, lpResult);
|
||||
}
|
||||
|
||||
[DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern int FindExecutable(string lpFile, string lpDirectory, [Out] StringBuilder lpResult);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
// <copyright file="FindWindow.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("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
||||
|
||||
public static IntPtr User32FindWindow(string lpClassName, string lpWindowName)
|
||||
{
|
||||
return FindWindow(lpClassName, lpWindowName);
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
[DllImport("gdi32.dll")]
|
||||
private static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
|
||||
|
||||
public static int Gdi32GetDeviceCaps(IntPtr hdc, int nIndex)
|
||||
{
|
||||
return GetDeviceCaps(hdc, nIndex);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,89 +1,39 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
// <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
|
||||
{
|
||||
//[StructLayout(LayoutKind.Sequential)]
|
||||
//[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "<Pending>")]
|
||||
//internal struct SHITEMID
|
||||
//{
|
||||
// public ushort cb;
|
||||
// [MarshalAs(UnmanagedType.LPArray)]
|
||||
// public byte[] abID;
|
||||
//}
|
||||
|
||||
//[StructLayout(LayoutKind.Sequential)]
|
||||
//[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "<Pending>")]
|
||||
//internal struct ITEMIDLIST
|
||||
//{
|
||||
// public SHITEMID mkid;
|
||||
//}
|
||||
|
||||
//[StructLayout(LayoutKind.Sequential)]
|
||||
//[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "<Pending>")]
|
||||
//internal struct BROWSEINFO
|
||||
//{
|
||||
// public IntPtr hwndOwner;
|
||||
// public IntPtr pidlRoot;
|
||||
// public IntPtr pszDisplayName;
|
||||
// [MarshalAs(UnmanagedType.LPTStr)]
|
||||
// public string lpszTitle;
|
||||
// public uint ulFlags;
|
||||
// public IntPtr lpfn;
|
||||
// public int lParam;
|
||||
// public IntPtr iImage;
|
||||
//}
|
||||
|
||||
// Browsing for directory.
|
||||
//public const uint BIF_RETURNONLYFSDIRS = 0x0001;
|
||||
//public const uint BIF_DONTGOBELOWDOMAIN = 0x0002;
|
||||
//public const uint BIF_STATUSTEXT = 0x0004;
|
||||
//public const uint BIF_RETURNFSANCESTORS = 0x0008;
|
||||
//public const uint BIF_EDITBOX = 0x0010;
|
||||
//public const uint BIF_VALIDATE = 0x0020;
|
||||
//public const uint BIF_NEWDIALOGSTYLE = 0x0040;
|
||||
//public const uint BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX);
|
||||
//public const uint BIF_BROWSEINCLUDEURLS = 0x0080;
|
||||
//public const uint BIF_BROWSEFORCOMPUTER = 0x1000;
|
||||
//public const uint BIF_BROWSEFORPRINTER = 0x2000;
|
||||
//public const uint BIF_BROWSEINCLUDEFILES = 0x4000;
|
||||
//public const uint BIF_SHAREABLE = 0x8000;
|
||||
|
||||
public const uint ShgfiIcon = 0x000000100; // get icon
|
||||
//public const uint SHGFI_DISPLAYNAME = 0x000000200; // get display name
|
||||
//public const uint SHGFI_TYPENAME = 0x000000400; // get type name
|
||||
//public const uint SHGFI_ATTRIBUTES = 0x000000800; // get attributes
|
||||
//public const uint SHGFI_ICONLOCATION = 0x000001000; // get icon location
|
||||
//public const uint SHGFI_EXETYPE = 0x000002000; // return exe type
|
||||
public const uint ShgfiSYSICONINDEX = 0x000004000; // get system icon index
|
||||
public const uint ShgfiLINKOVERLAY = 0x000008000; // put a link overlay on icon
|
||||
//public const uint SHGFI_SELECTED = 0x000010000; // show icon in selected state
|
||||
//public const uint SHGFI_ATTR_SPECIFIED = 0x000020000; // get only specified attributes
|
||||
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 SHGFI_SHELLICONSIZE = 0x000000004; // get shell size icon
|
||||
//public const uint SHGFI_PIDL = 0x000000008; // pszPath is a pidl
|
||||
//public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010; // use passed dwFileAttribute
|
||||
//public const uint SHGFI_ADDOVERLAYS = 0x000000020; // apply the appropriate overlays
|
||||
//public const uint SHGFI_OVERLAYINDEX = 0x000000040; // Get the index of the overlay
|
||||
|
||||
public const uint FileAttributeDirectory = 0x00000010;
|
||||
public const uint FileAttributeNormal = 0x00000080;
|
||||
|
||||
public const int IldTransparent = 0x00000001;
|
||||
|
||||
[DllImport("comctl32")]
|
||||
/// <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);
|
||||
|
||||
public static void Comctl32ImageListGetIcon(IntPtr hIcon)
|
||||
{
|
||||
_ = DestroyIcon(hIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
// Determines the default menu item on the specified menu
|
||||
[DllImport("user32", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern int GetMenuDefaultItem(IntPtr hMenu, bool fByPos, uint gmdiFlags);
|
||||
|
||||
public static int User32GetMenuDefaultItem(IntPtr hMenu, bool fByPos, uint gmdiFlags)
|
||||
{
|
||||
return GetMenuDefaultItem(hMenu, fByPos, gmdiFlags);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,16 @@
|
|||
using System.Runtime.InteropServices;
|
||||
// <copyright file="IsTouchEnabled.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/// <summary>
|
||||
/// wraps the methodcalls to native windows dll's.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int GetSystemMetrics(int nIndex);
|
||||
|
||||
public static bool IsTouchEnabled()
|
||||
{
|
||||
const int MAXTOUCHES_INDEX = 95;
|
||||
|
@ -14,5 +18,9 @@ namespace SystemTrayMenu.DllImports
|
|||
|
||||
return maxTouches > 0;
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern int GetSystemMetrics(int nIndex);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
// <copyright file="RegisterHotKey.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// wraps the methodcalls to native windows dll's.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
||||
|
||||
public static bool User32RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk)
|
||||
{
|
||||
return RegisterHotKey(hWnd, id, fsModifiers, vk);
|
||||
|
@ -20,5 +22,33 @@ namespace SystemTrayMenu.DllImports
|
|||
{
|
||||
return UnregisterHotKey(hWnd, id);
|
||||
}
|
||||
|
||||
public static uint User32MapVirtualKey(uint uCode, uint uMapType)
|
||||
{
|
||||
return MapVirtualKey(uCode, uMapType);
|
||||
}
|
||||
|
||||
public static int User32GetKeyNameText(uint lParam, [Out] StringBuilder lpString, int nSize)
|
||||
{
|
||||
return GetKeyNameText(lParam, lpString, nSize);
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint virtualKeyCode);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern uint MapVirtualKey(uint uCode, uint uMapType);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern int GetKeyNameText(uint lParam, [Out] StringBuilder lpString, int nSize);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
// <copyright file="SHAppBarMessage.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", SetLastError = true)]
|
||||
private static extern IntPtr SHAppBarMessage(ABM dwMessage, [In] ref APPBARDATA pData);
|
||||
|
||||
internal static IntPtr Shell32SHAppBarMessage(ABM dwMessage, [In] ref APPBARDATA pData)
|
||||
{
|
||||
return SHAppBarMessage(dwMessage, ref pData);
|
||||
}
|
||||
|
||||
internal enum ABM : uint
|
||||
{
|
||||
New = 0x00000000,
|
||||
|
@ -33,16 +32,19 @@ namespace SystemTrayMenu.DllImports
|
|||
Left = 0,
|
||||
Top = 1,
|
||||
Right = 2,
|
||||
Bottom = 3
|
||||
Bottom = 3,
|
||||
}
|
||||
|
||||
internal static class ABS
|
||||
internal static IntPtr Shell32SHAppBarMessage(ABM dwMessage, [In] ref APPBARDATA pData)
|
||||
{
|
||||
public const int Autohide = 0x0000001;
|
||||
public const int AlwaysOnTop = 0x0000002;
|
||||
return SHAppBarMessage(dwMessage, ref pData);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
[DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern IntPtr SHAppBarMessage(ABM dwMessage, [In] ref APPBARDATA pData);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct APPBARDATA
|
||||
{
|
||||
public uint cbSize;
|
||||
|
@ -53,7 +55,7 @@ namespace SystemTrayMenu.DllImports
|
|||
public int lParam;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct RECT
|
||||
{
|
||||
public int left;
|
||||
|
@ -61,5 +63,11 @@ namespace SystemTrayMenu.DllImports
|
|||
public int right;
|
||||
public int bottom;
|
||||
}
|
||||
|
||||
internal static class ABS
|
||||
{
|
||||
public const int Autohide = 0x0000001;
|
||||
public const int AlwaysOnTop = 0x0000002;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,25 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
// <copyright file="SHGetDesktopFolder.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
|
||||
{
|
||||
// Retrieves the IShellFolder interface for the desktop folder, which is the root of the Shell's namespace.
|
||||
[DllImport("shell32.dll")]
|
||||
private static extern int SHGetDesktopFolder(out IntPtr ppshf);
|
||||
|
||||
public static int Shell32SHGetDesktopFolder(out IntPtr ppshf)
|
||||
{
|
||||
return SHGetDesktopFolder(out ppshf);
|
||||
}
|
||||
|
||||
// Retrieves the IShellFolder interface for the desktop folder, which is the root of the Shell's namespace.
|
||||
[DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern int SHGetDesktopFolder(out IntPtr ppshf);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +1,45 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
// <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;
|
||||
|
||||
[DllImport("Shell32.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr SHGetFileInfo(
|
||||
string pszPath,
|
||||
uint dwFileAttributes,
|
||||
ref SHFILEINFO psfi,
|
||||
uint cbFileInfo,
|
||||
uint uFlags
|
||||
);
|
||||
|
||||
internal static IntPtr Shell32SHGetFileInfo(
|
||||
string pszPath,
|
||||
uint dwFileAttributes,
|
||||
ref SHFILEINFO psfi,
|
||||
uint cbFileInfo,
|
||||
uint uFlags
|
||||
)
|
||||
uint uFlags)
|
||||
{
|
||||
return SHGetFileInfo(pszPath,
|
||||
return SHGetFileInfo(
|
||||
pszPath,
|
||||
dwFileAttributes,
|
||||
ref psfi,
|
||||
cbFileInfo,
|
||||
uFlags);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
[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;
|
||||
|
@ -45,5 +53,3 @@ namespace SystemTrayMenu.DllImports
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,16 +1,25 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
// <copyright file="SHGetFolderPath.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// wraps the methodcalls to native windows dll's.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
[DllImport("shfolder.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern int SHGetFolderPath(IntPtr hwndOwner, int nFolder, IntPtr hToken, int dwFlags, StringBuilder lpszPath);
|
||||
public static int ShfolderSHGetFolderPath(IntPtr hwndOwner, int nFolder, IntPtr hToken, int dwFlags, StringBuilder lpszPath)
|
||||
{
|
||||
return SHGetFolderPath(hwndOwner, nFolder, hToken, dwFlags, lpszPath);
|
||||
}
|
||||
|
||||
[DllImport("shfolder.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern int SHGetFolderPath(IntPtr hwndOwner, int nFolder, IntPtr hToken, int dwFlags, StringBuilder lpszPath);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool SetProcessDPIAware();
|
||||
public static void User32SetProcessDPIAware()
|
||||
{
|
||||
_ = SetProcessDPIAware();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,39 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
// <copyright file="ShowInactiveTopmost.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
/// <summary>
|
||||
/// wraps the methodcalls to native windows dll's.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
private const int SW_SHOWNOACTIVATE = 4;
|
||||
private const int HWND_TOPMOST = -1;
|
||||
private const uint SWP_NOACTIVATE = 0x0010;
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
|
||||
public static void User32ShowInactiveTopmost(Form form)
|
||||
{
|
||||
if (form != null)
|
||||
{
|
||||
_ = ShowWindow(form.Handle, SW_SHOWNOACTIVATE);
|
||||
SetWindowPos(
|
||||
form.Handle.ToInt32(),
|
||||
HWND_TOPMOST,
|
||||
form.Left,
|
||||
form.Top,
|
||||
form.Width,
|
||||
form.Height,
|
||||
SWP_NOACTIVATE);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetWindowPos", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern bool SetWindowPos(
|
||||
int hWnd, // Window handle
|
||||
int hWndInsertAfter, // Placement-order handle
|
||||
|
@ -18,16 +42,5 @@ namespace SystemTrayMenu.DllImports
|
|||
int cx, // Width
|
||||
int cy, // Height
|
||||
uint uFlags); // Window positioning flags
|
||||
|
||||
public static void User32ShowInactiveTopmost(Form form)
|
||||
{
|
||||
if (form != null)
|
||||
{
|
||||
_ = ShowWindow(form.Handle, SW_SHOWNOACTIVATE);
|
||||
SetWindowPos(form.Handle.ToInt32(), HWND_TOPMOST,
|
||||
form.Left, form.Top, form.Width, form.Height,
|
||||
SWP_NOACTIVATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
// <copyright file="ShowWindow.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("user32.dll")]
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,23 @@
|
|||
using System.Runtime.InteropServices;
|
||||
// <copyright file="StrCmpLogicalW.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/// <summary>
|
||||
/// wraps the methodcalls to native windows dll's.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
|
||||
private static extern int StrCmpLogicalW(string x, string y);
|
||||
|
||||
public static int ShlwapiStrCmpLogicalW(string x, string y)
|
||||
{
|
||||
return StrCmpLogicalW(x, y);
|
||||
}
|
||||
|
||||
[DllImport("shlwapi.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern int StrCmpLogicalW(string x, string y);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,26 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
// <copyright file="StrRetToBuf.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace SystemTrayMenu.DllImports
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// wraps the methodcalls to native windows dll's.
|
||||
/// </summary>
|
||||
public static partial class NativeMethods
|
||||
{
|
||||
// Takes a STRRET structure returned by IShellFolder::GetDisplayNameOf, converts it to a string, and places the result in a buffer.
|
||||
[DllImport("shlwapi.dll", EntryPoint = "StrRetToBuf", ExactSpelling = false, CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
private static extern int StrRetToBuf(IntPtr pstr, IntPtr pidl, StringBuilder pszBuf, int cchBuf);
|
||||
|
||||
public static int ShlwapiStrRetToBuf(IntPtr pstr, IntPtr pidl, StringBuilder pszBuf, int cchBuf)
|
||||
{
|
||||
return StrRetToBuf(pstr, pidl, pszBuf, cchBuf);
|
||||
}
|
||||
|
||||
// Takes a STRRET structure returned by IShellFolder::GetDisplayNameOf, converts it to a string, and places the result in a buffer.
|
||||
[DllImport("shlwapi.dll", EntryPoint = "StrRetToBuf", ExactSpelling = false, SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern int StrRetToBuf(IntPtr pstr, IntPtr pidl, StringBuilder pszBuf, int cchBuf);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +1,29 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
// <copyright file="TrackPopupMenuEx.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
|
||||
{
|
||||
// The TrackPopupMenuEx function displays a shortcut menu at the specified location and tracks the selection of items on the shortcut menu. The shortcut menu can appear anywhere on the screen.
|
||||
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Unicode)]
|
||||
private static extern uint TrackPopupMenuEx(IntPtr hmenu, TPM flags, int x, int y, IntPtr hwnd, IntPtr lptpm);
|
||||
|
||||
internal static uint User32TrackPopupMenuEx(IntPtr hmenu, TPM flags, int x, int y, IntPtr hwnd, IntPtr lptpm)
|
||||
{
|
||||
return TrackPopupMenuEx(hmenu, flags, x, y, hwnd, lptpm);
|
||||
}
|
||||
|
||||
// Specifies how TrackPopupMenuEx positions the shortcut menu horizontally
|
||||
/// <summary>
|
||||
/// Specifies how TrackPopupMenuEx positions the shortcut menu horizontally.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
internal enum TPM : uint
|
||||
{
|
||||
LEFTBUTTON = 0x0000,
|
||||
LEFTBUTTON = 0x0000, // LEFTALIGN = 0x0000, // TOPALIGN = 0x0000, // HORIZONTAL = 0x0000,
|
||||
RIGHTBUTTON = 0x0002,
|
||||
//LEFTALIGN = 0x0000,
|
||||
CENTERALIGN = 0x0004,
|
||||
RIGHTALIGN = 0x0008,
|
||||
//TOPALIGN = 0x0000,
|
||||
VCENTERALIGN = 0x0010,
|
||||
BOTTOMALIGN = 0x0020,
|
||||
//HORIZONTAL = 0x0000,
|
||||
VERTICAL = 0x0040,
|
||||
NONOTIFY = 0x0080,
|
||||
RETURNCMD = 0x0100,
|
||||
|
@ -36,7 +33,27 @@ namespace SystemTrayMenu.DllImports
|
|||
VERPOSANIMATION = 0x1000,
|
||||
VERNEGANIMATION = 0x2000,
|
||||
NOANIMATION = 0x4000,
|
||||
LAYOUTRTL = 0x8000
|
||||
LAYOUTRTL = 0x8000,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// user32 TrackPopupMenuEx.
|
||||
/// </summary>
|
||||
/// <param name="hmenu">hmenu.</param>
|
||||
/// <param name="flags">flags.</param>
|
||||
/// <param name="x">x.</param>
|
||||
/// <param name="y">y.</param>
|
||||
/// <param name="hwnd">hwnd.</param>
|
||||
/// <param name="lptpm">lptpm.</param>
|
||||
/// <returns>uint.</returns>
|
||||
internal static uint User32TrackPopupMenuEx(IntPtr hmenu, TPM flags, int x, int y, IntPtr hwnd, IntPtr lptpm)
|
||||
{
|
||||
return TrackPopupMenuEx(hmenu, flags, x, y, hwnd, lptpm);
|
||||
}
|
||||
|
||||
// The TrackPopupMenuEx function displays a shortcut menu at the specified location and tracks the selection of items on the shortcut menu. The shortcut menu can appear anywhere on the screen.
|
||||
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
|
||||
private static extern uint TrackPopupMenuEx(IntPtr hmenu, TPM flags, int x, int y, IntPtr hwnd, IntPtr lptpm);
|
||||
}
|
||||
}
|
||||
|
|
BIN
Packaging/Images/BadgeLogo.scale-100.png
Normal file
After Width: | Height: | Size: 170 B |
BIN
Packaging/Images/BadgeLogo.scale-125.png
Normal file
After Width: | Height: | Size: 182 B |
BIN
Packaging/Images/BadgeLogo.scale-150.png
Normal file
After Width: | Height: | Size: 213 B |
BIN
Packaging/Images/BadgeLogo.scale-200.png
Normal file
After Width: | Height: | Size: 444 B |
BIN
Packaging/Images/BadgeLogo.scale-400.png
Normal file
After Width: | Height: | Size: 860 B |
BIN
Packaging/Images/LargeTile.scale-100.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
Packaging/Images/LargeTile.scale-125.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
Packaging/Images/LargeTile.scale-150.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
Packaging/Images/LargeTile.scale-200.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
Packaging/Images/LargeTile.scale-400.png
Normal file
After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 15 KiB |
BIN
Packaging/Images/SmallTile.scale-100.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Packaging/Images/SmallTile.scale-125.png
Normal file
After Width: | Height: | Size: 2 KiB |
BIN
Packaging/Images/SmallTile.scale-150.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
Packaging/Images/SmallTile.scale-200.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
Packaging/Images/SmallTile.scale-400.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
Packaging/Images/SplashScreen.scale-100.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
Packaging/Images/SplashScreen.scale-125.png
Normal file
After Width: | Height: | Size: 87 KiB |
BIN
Packaging/Images/SplashScreen.scale-150.png
Normal file
After Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 546 KiB After Width: | Height: | Size: 124 KiB |
BIN
Packaging/Images/SplashScreen.scale-400.png
Normal file
After Width: | Height: | Size: 267 KiB |
BIN
Packaging/Images/SplashScreen.scale-400.xcf
Normal file
BIN
Packaging/Images/Square150x150Logo.scale-100.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
Packaging/Images/Square150x150Logo.scale-125.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
Packaging/Images/Square150x150Logo.scale-150.png
Normal file
After Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 5.7 KiB |
BIN
Packaging/Images/Square150x150Logo.scale-400.png
Normal file
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 732 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 732 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.8 KiB |
BIN
Packaging/Images/Square44x44Logo.scale-100.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Packaging/Images/Square44x44Logo.scale-125.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
Packaging/Images/Square44x44Logo.scale-150.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 2.7 KiB |
BIN
Packaging/Images/Square44x44Logo.scale-400.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
Packaging/Images/Square44x44Logo.targetsize-16.png
Normal file
After Width: | Height: | Size: 517 B |
BIN
Packaging/Images/Square44x44Logo.targetsize-24.png
Normal file
After Width: | Height: | Size: 883 B |
Before Width: | Height: | Size: 11 KiB |
BIN
Packaging/Images/Square44x44Logo.targetsize-256.png
Normal file
After Width: | Height: | Size: 9.3 KiB |
BIN
Packaging/Images/Square44x44Logo.targetsize-32.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Packaging/Images/Square44x44Logo.targetsize-48.png
Normal file
After Width: | Height: | Size: 1.6 KiB |